Linux内核网络协议栈TCP/IP分析:ip route路由表

4 篇文章 0 订阅
2 篇文章 0 订阅

一 概述
1.1 概念
(1)路由:跨越从源主机到目标主机的一个互联网络来转发数据包的过程;
(2)路由器:能够将数据包转发到正确的目的地,并在转发过程中选择最佳路径的设备;
(3)路由表:在路由器中维护的路由条目,路由器根据路由表做路径选择;
(4)静态路由:是由管理员手工配置的,是单向的;
(5)默认路由:当路由器在路由表中找不到目标网络的路由条目时,路由器把请求转发到默认路由接口 。
1.2 路由表
Linux系统可以同时存在256(0-255)个路由表,而且每个路由表都各自独立,互不相关。数据包在传输时是根据RPDB(路由策略数据库)内的策略决定数据包应该用哪个路由表传输的。
在默认情况下,系统有三个路由表,这三个路由表的功能如下:
(1)local:路由表local包含本机路由及广播信息。例如,在本机上执行ssh 127.0.0.1时,就会参考这份路由表的内容,在正常情况下,只要配置好网卡的网络设置,就会自动生成local路由表的内容,我们应该也不必修改其内容。
(2)main:使用传统命令route -n所看到的路由表就是main的内容。Linux系统在默认情况下使用这份路由表的内容来传输数据包,因此,其内容极为重要,在正常情况下,只要配置好网卡的网络设置,就会自动生成main路由表的内容。
(3)default:最后是default路由表,这个路由表在默认情况下内容为空;除非有特别的要求,否则保持其内容为空即可。
1.3 路由类型
(1)主机路由
  主机路由是路由选择表中指向单个IP地址或主机名的路由记录。主机路由的Flags字段为H。
(2)网络路由
  网络路由是代表主机可以到达的网络。网络路由的Flags字段为N。
(3)默认路由
  当主机不能在路由表中查找到目标主机的IP地址或网络路由时,数据包就被发送到默认路由(默认网关)上。默认路由的Flags字段为G。
二 Linux路由表
  在Linux内核中存在路由表fib_table和路由缓存表rt_hash。
  路由缓存表主要是为了加速路由的查找,每次路由查询都会先查找路由缓存,再查找路由表。
  在Linux 2.4.9内核中默认的路由查找算法使用的是Hash查找。
  Linux默认有三种策略路由:本地路由,主路由和默认路由,那么与之对应的就是三张路由表:本地路由表,主路由表和默认路由表。
2.1 相关结构体
(1)路由表是由fib_table结构来描述的,该结构是通过函数fib_hash_table()来赋值的,fib_table结构中的tb_data,是一个零长数组,该地址指向fn_hash结构体;

 struct fib_table
    {
    	unsigned char	tb_id;  // 标识符(例如:本地路由,主路由,默认路由);
    	unsigned	tb_stamp;   // 时间戳
    	int		(*tb_lookup)(struct fib_table *tb, const struct rt_key *key, struct fib_result *res);// 查找函数
    	int		(*tb_insert)(struct fib_table *table, struct rtmsg *r,
    				     struct kern_rta *rta, struct nlmsghdr *n,
    				     struct netlink_skb_parms *req);// 插入函数
    	int		(*tb_delete)(struct fib_table *table, struct rtmsg *r,
    				     struct kern_rta *rta, struct nlmsghdr *n,
    				     struct netlink_skb_parms *req);// 删除路由函数
    	int		(*tb_dump)(struct fib_table *table, struct sk_buff *skb,
    				     struct netlink_callback *cb); // 用于路由转发
    	int		(*tb_flush)(struct fib_table *table);// 移除路由信息结构
    	int		(*tb_get_info)(struct fib_table *table, char *buf,
    				       int first, int count);
    	void		(*tb_select_default)(struct fib_table *table,
    					     const struct rt_key *key, struct fib_result *res);// 设置默认路由
    
    	unsigned char	tb_data[0];// 注意这个特殊字段,标识结构的结尾,分配fib_table同时分配fn_hash结构;
    	                         // 也就是fib_table之后就是fn_hash结构
    };

(2)fn_hash包含fn_zone[33]和fn_zone_list,其中fn_zone[33]是由33个fn_zone结构指针构成的向量,与fn_zone_list构成了循环单链表;fz_hash是长度为fz_divisor的HASH表,HASH表中存放的是不同子网的fib_node节点。

// 路由区结构体的数组( 包含所有的额路由区的情况 )
    struct fn_hash {
    	struct fn_zone	*fn_zones[33];  // 路由区分成33份,子网掩码长度是1~32,0长度掩码代表网关,那么加起来就是33,即:fn_zone[0]的掩码是0.0.0.0,fn_zone[1]是10000000.00000000.00000000.0000000这一类  等等;
    	struct fn_zone	*fn_zone_list;  // 指向第一个活动的路由区
    };

(3)fn_zone代表同一掩码长度表项的集合,路由区:fn_zone,子网掩码长度相同的认为是相同的路由区;

// 路由区结构体(所有的子网长度相等的被分在同一个路由区)
struct fn_zone
{
	struct fn_zone	*fz_next;	/* Next not empty zone,指向下一个不为空的路由区结构,那么所有的路由区就能链接起来*/
	struct fib_node	**fz_hash;	/* Hash table pointer,有一个hash数组,用来hash得到一个hlist_head,是很多的fib_node通过自己的字段连接在这个队列中,那么通过这个fz_hahs字段可以找到fib_node所在的队列的头hlist_head,进而找到对应的fib_node ( 注意:上面说的hash数组的长度是fz_divisor长度);*/
	int		fz_nent;	/* Number of entries,包含的路由节总数	*/

	int		fz_divisor;	/* Hash divisor,hash头数量(上面说了)		*/
	u32		fz_hashmask;	/* (1<<fz_divisor) - 1,确定hash头的掩码	*/
#define FZ_HASHMASK(fz)	((fz)->fz_hashmask)

	int		fz_order;	/* Zone order,子网掩码位数		*/
	u32		fz_mask;    /*子网掩码*/
#define FZ_MASK(fz)	((fz)->fz_mask)     /*获取子网掩码的宏定义*/
};

(4)fib_info存储真正重要路由信息,即如何到达目的地。

/*
 * This structure contains data shared by many of routes.
 */
// 具体怎么路由这个数据包的信息
struct fib_info
{
	struct fib_info		*fib_next;
	struct fib_info		*fib_prev;
	int			fib_treeref;    // 路由信息结构使用计数器
	atomic_t		fib_clntref;  // 释放路由信息结构(fib)计数器
	int			fib_dead;   // 标志路由被删除了
	unsigned		fib_flags;   // 标识位
	int			fib_protocol;   // 安装路由协议
	u32			fib_prefsrc;    // 指定源IP,源地址和目的地址组成一个路由
	u32			fib_priority;   // 路由优先级
	unsigned		fib_metrics[RTAX_MAX];  // 保存负载值(例如MTU,MSS)
#define fib_mtu fib_metrics[RTAX_MTU-1]     // MTU值
#define fib_window fib_metrics[RTAX_WINDOW-1]   // 窗口值
#define fib_rtt fib_metrics[RTAX_RTT-1]     // RTT值
#define fib_advmss fib_metrics[RTAX_ADVMSS-1]   // MSS值(对外公开的)
	int			fib_nhs;     // 倒数第二个字段即:跳转结构的数组个数
#ifdef CONFIG_IP_ROUTE_MULTIPATH
	int			fib_power;   // 支持多路径时候使用
#endif
	struct fib_nh		fib_nh[0];  // 跳转结构(就是该怎么路由)
#define fib_dev		fib_nh[0].nh_dev    //fib_nh[0],这样的操作手法在内核中也是常见的。代表会有这个字段的存在,但是具体是几个并不知道,因为可能是动态的,所以需要一个计数表示,也就是fib_power;
};

三 路由查找
进入Linux内核数据包的路由是通过函数ip_route_input来处理的:

/*
*函数名:ip_route_input
*函数功能:在处理从网络上进来的IP包时调用的路由函数,
*		它的结果主要有两个:即如果是本地包则传给上层协议层,
*		如果不是则选则一个出端口再发送出去。
*函数参数:skb--表示ip包的缓冲区;
*			dst--目的地址;
*			src--源地址;
*			tos--IP包服务类型;
*			dev--入端口。
*函数返回值:函数返回值指示错误,如果成功查到路由,
*				函数返回后,skb->dst会被赋值。
*
*/
int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
		   u8 tos, struct net_device *dev);

(1)首先这个函数需要查路由缓存(cache),如果找到了那么它给skb->dst赋值并返回,如是没找到,它会调用ip_route_input_slow去查询路由数据库。
(2)路由查找就是最终找到这个路由条目,得到目的地址(下一跳),然后赋值给skb->dst,然后通过skb->dst->input(skb)就可以进行操作。第三需要注意,这里的操作分成两类:第一类是投到本地,即数据是发到本机的,那么调用ip_local_deliver将数据包发送给上一层进行处理;第二类是转发,调用ip_forward函数进行处理,转发出去。最后注意:当路由缓冲找不到所需要的路由项,那么最终需要再次到fib中去查找,也就是完整的一个查找过程。
2.1 路由缓存
  路由缓存用于加速路由的查找,当收到报文或发送报文时,首先会查询路由缓存,在内核中被组织成hash表,就是rt_hash_table。
  ip-hash-
  流程说明:(1)rt_hash_table是在inet_init()模块初始化的时候调用ip_rt_init创建的路由缓存,之后通过rt_intern_hash函数插入条目rt,以供路由查找的时候进行查找;
  2.2 路由表
  route-table-1
  流程说明:(1)在ip_fib_init()函数初始化的时候,为路由表分配空间;并且声明路由的插入、删除、转发函数;
  (2)向路由表中插入一条新的路由是通过fn_hash_insert函数实现的,事实上,调用此函数的还有处理路由的尾部追加(append)、首部追加(prepend)、改变(change)和替换(replace)操作,这些不同的操作是由传入的NLM_F_XXX标识参数来区分的。路由的删除由fn_hash_delete完成,删除路由要比添加路由简单,因为删除路由只有一种操作;

评论 2 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

JackieRenne169

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值