第十二章 trie路由--基于Linux3.10

原帖地址:http://blog.csdn.net/shichaog/article/details/44658205#t1

下载地址《http://download.csdn.net/detail/shichaog/8620701》

路由表的构建途径:

 通过用户命令[route(ioctl) 、ip route(netlink)]静态配置

通过路由协议动态配置,这些协议是BGP(Border Gateway Protocol)、EGP(Exterior Gateway Protocol)以及OSPF(Open Shortest Path First)

这一章的内容基于route方法,其它的配置路由的方法不在这章中,但是上面的方法区别在于配置方法,而对应调用的路由核心函数以及操作的核心路由数据结构是一样的,这章的主要内容就是关于这些和核心函数和核心数据结构的。

路由相关数据结构在include/net/route.h

  1. struct ip_rt_acct {  
  2. __u32 o_bytes;  //发送数据的字节数  
  3. __u32 o_packets;  
  4. __u32 i_bytes;  
  5. __u32 i_packets;  
  6. };  
struct ip_rt_acct {
__u32 o_bytes;  //发送数据的字节数
__u32 o_packets;
__u32 i_bytes;
__u32 i_packets;
};

这个结构体在ip_rcv_finish中被使用到,由于网络数据包的统计,分别按照byte和packet两种方法计数,ip_rcv_finish在网络层接收中分析过,这里会再一次看到在网络层被跳过的关于路由相关的代码,下面的代码片段就是上面统计信息被赋值的一个地方:

  1. static int ip_rcv_finish(struct sk_buff *skb)   
  2. {  
  3. #ifdef CONFIG_IP_ROUTE_CLASSID  
  4. if (unlikely(skb_dst(skb)->tclassid)) {  
  5. struct ip_rt_acct *st = this_cpu_ptr(ip_rt_acct);  
  6. u32 idx = skb_dst(skb)->tclassid;  
  7. st[idx&0xFF].o_packets++;  
  8. st[idx&0xFF].o_bytes += skb->len;  
  9. st[(idx>>16)&0xFF].i_packets++;  
  10. st[(idx>>16)&0xFF].i_bytes += skb->len;  
  11. }  
  12. #endif  
  13. }  
static int ip_rcv_finish(struct sk_buff *skb) 
{
#ifdef CONFIG_IP_ROUTE_CLASSID
if (unlikely(skb_dst(skb)->tclassid)) {
struct ip_rt_acct *st = this_cpu_ptr(ip_rt_acct);
u32 idx = skb_dst(skb)->tclassid;
st[idx&0xFF].o_packets++;
st[idx&0xFF].o_bytes += skb->len;
st[(idx>>16)&0xFF].i_packets++;
st[(idx>>16)&0xFF].i_bytes += skb->len;
}
#endif
}

由上面的使用可以知道,定义了基于路由的分类器就会使用该字段。该字段根据idx索引可构成具有256个成员的数组。其初始化在ip_rt_init中完成。

rt_cache_stat

路由表缓存的统计信息,除了输入输出路由信息统计,还有垃圾回收信息。

fib_result

查找路由表会得到此结构。

  1. struct fib_result {  
  2. unsigned char  prefixlen;  
  3. unsigned char  nh_sel;  
  4. unsigned char  type;  
  5. unsigned char  scope;  
  6. u32 tclassid;  
  7. struct fib_info *fi;  
  8. struct fib_table *table;  
  9. struct list_head *fa_head;  
  10. };  
struct fib_result {
unsigned char  prefixlen;
unsigned char  nh_sel;
unsigned char  type;
unsigned char  scope;
u32 tclassid;
struct fib_info *fi;
struct fib_table *table;
struct list_head *fa_head;
};

struct fib_rule

策略路由使用的结构。

  1. struct fib_rule {  
  2. struct list_headlist;  
  3. atomic_t  refcnt;  
  4. int iifindex;  
  5. int oifindex;  
  6. u32 mark;  
  7. u32 mark_mask;  
  8. u32 pref;  
  9. u32 flags;  
  10. u32 table;  
  11. u8 action;  
  12. u32 target;  
  13. struct fib_rule __rcu*ctarget;  
  14. char iifname[IFNAMSIZ];  
  15. char oifname[IFNAMSIZ];  
  16. struct rcu_headrcu;  
  17. struct net *  fr_net;  
  18. };  
struct fib_rule {
struct list_headlist;
atomic_t  refcnt;
int iifindex;
int oifindex;
u32 mark;
u32 mark_mask;
u32 pref;
u32 flags;
u32 table;
u8 action;
u32 target;
struct fib_rule __rcu*ctarget;
char iifname[IFNAMSIZ];
char oifname[IFNAMSIZ];
struct rcu_headrcu;
struct net *  fr_net;
};

struct flowi

流量控制,作为路由表查找的键值。

  1. struct flowi {  
  2. union {  
  3. struct flowi_common__fl_common;  
  4. struct flowi4  ip4;  
  5. struct flowi6  ip6;  
  6. struct flowidndn;  
  7. } u;  
  8. #define flowi_oif u.__fl_common.flowic_oif  
  9. #define flowi_iif u.__fl_common.flowic_iif  
  10. #define flowi_mark u.__fl_common.flowic_mark  
  11. #define flowi_tos u.__fl_common.flowic_tos  
  12. #define flowi_scope u.__fl_common.flowic_scope  
  13. #define flowi_proto u.__fl_common.flowic_proto  
  14. #define flowi_flags u.__fl_common.flowic_flags  
  15. #define flowi_secid u.__fl_common.flowic_secid  
  16. } __attribute__((__aligned__(BITS_PER_LONG/8)));  
struct flowi {
union {
struct flowi_common__fl_common;
struct flowi4  ip4;
struct flowi6  ip6;
struct flowidndn;
} u;
#define flowi_oif u.__fl_common.flowic_oif
#define flowi_iif u.__fl_common.flowic_iif
#define flowi_mark u.__fl_common.flowic_mark
#define flowi_tos u.__fl_common.flowic_tos
#define flowi_scope u.__fl_common.flowic_scope
#define flowi_proto u.__fl_common.flowic_proto
#define flowi_flags u.__fl_common.flowic_flags
#define flowi_secid u.__fl_common.flowic_secid
} __attribute__((__aligned__(BITS_PER_LONG/8)));

fib_table

路由表在内核中的表示为fib_table的一个结构体,其定义位于include/net/ip_fib.h文件。

  1. struct fib_table {  
  2. struct hlist_nodetb_hlist;  
  3. u32 tb_id;  
  4. int tb_default;  
  5. int tb_num_default;  
  6. unsigned long  tb_data[0];  
  7. };  
struct fib_table {
struct hlist_nodetb_hlist;
u32 tb_id;
int tb_default;
int tb_num_default;
unsigned long  tb_data[0];
};

tb_id用于标识路由表所属,其可选字段在include/uapi/linux/rtnetlink.h文件中;

  1. enum rt_class_t {  
  2.     RT_TABLE_UNSPEC=0,   
  3. /* User defined values */  
  4.     RT_TABLE_COMPAT=252,   
  5.     RT_TABLE_DEFAULT=253,   
  6.     RT_TABLE_MAIN=254,   
  7.     RT_TABLE_LOCAL=255,   
  8.     RT_TABLE_MAX=0xFFFFFFFF  
  9. };  
enum rt_class_t {
	RT_TABLE_UNSPEC=0, 
/* User defined values */
	RT_TABLE_COMPAT=252, 
	RT_TABLE_DEFAULT=253, 
	RT_TABLE_MAIN=254, 
	RT_TABLE_LOCAL=255, 
	RT_TABLE_MAX=0xFFFFFFFF
};

从枚举类型名称,如果没有使用策略路由,那么只有RT_TABLE_MAIN和RT_TABLE_LOCAL两种类型的路由表存在。

struct fib_info

多个路由项共享该一些字段:

  1. struct fib_info {  
  2. struct hlist_nodefib_hash;  
  3. struct hlist_nodefib_lhash;  
  4. struct net  *fib_net;  
  5. int fib_treeref;  
  6. atomic_t  fib_clntref;  
  7. unsigned int  fib_flags;  
  8. unsigned char  fib_dead;  
  9. unsigned char  fib_protocol;  
  10. unsigned char  fib_scope;  
  11. unsigned char  fib_type;  
  12. __be32  fib_prefsrc;  
  13. u32 fib_priority;  
  14. u32 *fib_metrics;  
  15. #define fib_mtu fib_metrics[RTAX_MTU-1]  
  16. #define fib_window fib_metrics[RTAX_WINDOW-1]  
  17. #define fib_rtt fib_metrics[RTAX_RTT-1]  
  18. #define fib_advmss fib_metrics[RTAX_ADVMSS-1]  
  19. int fib_nhs;  
  20. #ifdef CONFIG_IP_ROUTE_MULTIPATH  
  21. int fib_power;  
  22. #endif  
  23. struct rcu_headrcu;  
  24. struct fib_nh  fib_nh[0];  
  25. #define fib_dev fib_nh[0].nh_dev  
  26. };  
struct fib_info {
struct hlist_nodefib_hash;
struct hlist_nodefib_lhash;
struct net  *fib_net;
int fib_treeref;
atomic_t  fib_clntref;
unsigned int  fib_flags;
unsigned char  fib_dead;
unsigned char  fib_protocol;
unsigned char  fib_scope;
unsigned char  fib_type;
__be32  fib_prefsrc;
u32 fib_priority;
u32 *fib_metrics;
#define fib_mtu fib_metrics[RTAX_MTU-1]
#define fib_window fib_metrics[RTAX_WINDOW-1]
#define fib_rtt fib_metrics[RTAX_RTT-1]
#define fib_advmss fib_metrics[RTAX_ADVMSS-1]
int fib_nhs;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
int fib_power;
#endif
struct rcu_headrcu;
struct fib_nh  fib_nh[0];
#define fib_dev fib_nh[0].nh_dev
};

路由项别名:

  1. struct fib_alias {  
  2. struct list_headfa_list;  
  3. struct fib_info*fa_info;  
  4. u8 fa_tos;  
  5. u8 fa_type;  
  6. u8 fa_state;  
  7. struct rcu_headrcu;  
  8. };  
struct fib_alias {
struct list_headfa_list;
struct fib_info*fa_info;
u8 fa_tos;
u8 fa_type;
u8 fa_state;
struct rcu_headrcu;
};

下一跳,使用route或者ip route 可以添加。

  1. struct fib_nh {  
  2. struct net_device*nh_dev;  
  3. struct hlist_nodenh_hash;  
  4. struct fib_info*nh_parent;  
  5. unsigned int  nh_flags;  
  6. unsigned char  nh_scope;  
  7. #ifdef CONFIG_IP_ROUTE_MULTIPATH  
  8. int nh_weight;  
  9. int nh_power;  
  10. #endif  
  11. #ifdef CONFIG_IP_ROUTE_CLASSID  
  12. __u32 nh_tclassid;  
  13. #endif  
  14. int nh_oif;  
  15. __be32  nh_gw;  
  16. __be32  nh_saddr;  
  17. int nh_saddr_genid;  
  18. struct rtable __rcu * __percpu *nh_pcpu_rth_output;  
  19. struct rtable __rcu*nh_rth_input;  
  20. struct fnhe_hash_bucket*nh_exceptions;  
  21. };  
struct fib_nh {
struct net_device*nh_dev;
struct hlist_nodenh_hash;
struct fib_info*nh_parent;
unsigned int  nh_flags;
unsigned char  nh_scope;
#ifdef CONFIG_IP_ROUTE_MULTIPATH
int nh_weight;
int nh_power;
#endif
#ifdef CONFIG_IP_ROUTE_CLASSID
__u32 nh_tclassid;
#endif
int nh_oif;
__be32  nh_gw;
__be32  nh_saddr;
int nh_saddr_genid;
struct rtable __rcu * __percpu *nh_pcpu_rth_output;
struct rtable __rcu*nh_rth_input;
struct fnhe_hash_bucket*nh_exceptions;
};

struct dst_entry路由表入口项

struct dst_ops 路由入口项的操作函数集,如垃圾回收函数就在这里。

struct rtable  路由表

路由三大块:路由缓存、路由表、路由信息查找,这三大块依赖的重要数据数据结构在路由子系统初始化时完成。

路由子系统初始化:

  1. 2655 int __init ip_rt_init(void)  
  2. 2656 {  
  3. /*ip_rt_acct 字段的意义在前面*/  
  4. 2659 #ifdef CONFIG_IP_ROUTE_CLASSID  
  5. 2660     ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct));  
  6. 2661     if (!ip_rt_acct)  
  7. 2662         panic("IP: failed to allocate ip_rt_acct\n");  
  8. 2663 #endif  
  9. //创建rtalble大小的路由表缓存,这是上述路由三大块中的路由缓存使用的,没有使用malloc的原因是加速网络数据包的传递  
  10. 2665     ipv4_dst_ops.kmem_cachep =  
  11. 2666         kmem_cache_create("ip_dst_cache"sizeof(struct rtable), 0,  
  12. 2667                   SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);  
  13. 2668   
  14. 2669     ipv4_dst_blackhole_ops.kmem_cachep = ipv4_dst_ops.kmem_cachep;  
  15. /*路由项垃圾回收门限,对无效的路由项回收其占用内存的门限,初始设置门限为最大值*/  
  16. 2677     ipv4_dst_ops.gc_thresh = ~0;   
  17. /*路由表膨胀的最大尺寸,#define INT_MAX ((int)(~0U>>1))*/  
  18. 2678     ip_rt_max_size = INT_MAX;  
  19. /***************************** 
  20. 注册两种类型的内核通知链,netdev_chain和inetaddr_chain,这两种通知链对应的回调函数是inetdev_event和fib_inetaddr_event,设备的状态的改变(up、down、register和unregistere)会调用回调函数: 
  21. devinet_init---向--netdev_chain---添加--inetdev_event回调函数;  回调函数完成设备的初始化、删除等操作  
  22. ip_fib_init---向--netdev_chain---添加--fib_netdev_event回调函数;回调函数完成该设备相关路由表的使能、禁止、刷新等操作。 
  23.                 -向--inetaddr_chain---添加--fib_inetaddr_event回调函数;回调函数完成该设备相关路由表路由项的添加和删除操作。 
  24. ***************************/  
  25. 2680     devinet_init();  
  26. 2681     ip_fib_init();  
  27. 2696     return rc;  
  28. 2697 }  
2655 int __init ip_rt_init(void)
2656 {
/*ip_rt_acct 字段的意义在前面*/
2659 #ifdef CONFIG_IP_ROUTE_CLASSID
2660     ip_rt_acct = __alloc_percpu(256 * sizeof(struct ip_rt_acct), __alignof__(struct ip_rt_acct));
2661     if (!ip_rt_acct)
2662         panic("IP: failed to allocate ip_rt_acct\n");
2663 #endif
//创建rtalble大小的路由表缓存,这是上述路由三大块中的路由缓存使用的,没有使用malloc的原因是加速网络数据包的传递
2665     ipv4_dst_ops.kmem_cachep =
2666         kmem_cache_create("ip_dst_cache", sizeof(struct rtable), 0,
2667                   SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
2668 
2669     ipv4_dst_blackhole_ops.kmem_cachep = ipv4_dst_ops.kmem_cachep;
/*路由项垃圾回收门限,对无效的路由项回收其占用内存的门限,初始设置门限为最大值*/
2677     ipv4_dst_ops.gc_thresh = ~0; 
/*路由表膨胀的最大尺寸,#define INT_MAX ((int)(~0U>>1))*/
2678     ip_rt_max_size = INT_MAX;
/*****************************
注册两种类型的内核通知链,netdev_chain和inetaddr_chain,这两种通知链对应的回调函数是inetdev_event和fib_inetaddr_event,设备的状态的改变(up、down、register和unregistere)会调用回调函数:
devinet_init---向--netdev_chain---添加--inetdev_event回调函数;  回调函数完成设备的初始化、删除等操作 
ip_fib_init---向--netdev_chain---添加--fib_netdev_event回调函数;回调函数完成该设备相关路由表的使能、禁止、刷新等操作。
                -向--inetaddr_chain---添加--fib_inetaddr_event回调函数;回调函数完成该设备相关路由表路由项的添加和删除操作。
***************************/
2680     devinet_init();
2681     ip_fib_init();
2696     return rc;
2697 }

12.2 LC-trie(字典树、单词查找树)

现在内核采用的是trie算法组织路由表,这里的trie其实就是tree的意思。本章会以ifconfig和route两个命令作为引子,以这两个例子详细看一下trie路由算法在Linux下的实现,本节是现在内核默认trie路由算法的一个简单的算法简介,并将trie路由算法的节点、叶子和Linux内核下具体的数据结构对应起来。至于早期的哈希算法,这里就丝毫没有涉及了。

先从一个引子说起,如果让你使用百度词典查找apple这个单词,会发现在你输入一个字母后,其下拉栏会显示若干的备选单词。如图12.2.1显示的,会有appliance、apple等提示单词,问题来了,百度是按照什么规则给出的提示的呢?

1、首先这些单词必须遵循字母顺序,不能用户输入a,下来栏中出现个b打头的单词。

2、首先这些词在字典里必须是存在的,或者是一些组织机构的,总而言之就是这个词目前是存在的。

3、这些词并没有按照26个字母表的顺序给出,图中很明显,ace比and要排在前面,这里加入了词频(概率)权重因子。


图12.2.1 apple字典提示

路由的算法就有点类似上面下拉栏的实现算法。但是上述频率的概念没有在路由算法本身体现,所以这里也就略过。下面还是以单词为例来看看LC-trie是如何组织的。图12.2.1中蓝色一栏就是我们要找的单词。

图12.1.2是对要查找的单词构建的一颗字典树,虚线左右两边都对应这个树,它们的不同在于深度。我们以左边的示例来说明字典树是如何组织的,对于apple这个单词,

1、树的根为空,NULL

2、最后一个字符是e,e被称为叶子

3、中间的字符,如a、p、l等,被称为节点。

4、尽量利用前缀节点,比如approve和apple有相同的前缀app,黄色那个支路就是用来表示approve的。


图12.2.2 apple字典树拓扑

图12.2.2中左右两幅图是用来说明rebalance这个概念的,这棵树不能太瘦也不能太胖,也即其广度和深度要在一个合理的比率上。所以当我们觉得这棵树太瘦时,可以进行压缩,比如将app压缩成一个节点,这就意味着先前复用a、ap的节点会被创建,因为这时没有a、ap节点了。如果太胖,那就拉长,就是上述过程的逆过程。

上述的过程虽然和内核管理路由表的方法有点差别,但思想是一样的,图12.1.3是具有10.12.39.0和192.168.0.10两项的路由表。这个10.12.39.0并不是使用route add 192.168.0.10 eth0 命令分配的,而是使用ifconfig eth0 10.12.39.221 netmask255.255.255.0 配置本机IP地址时设置的。至于为什么配置本机IP地址的ifconfig会设置这么一个路由项,主要原因是规范中要求:主机号全零的用于标识一个网段,这个地址会被用作路由项,不能被分配给主机使用,Linux默认在设置主机IP时,都会配置一个主机号全零的IP作为路由项。这个内容在12.2节中会看到代码的实现。

图12.2.4是在图12.2.3的基础之上使用route命令添加一项组成的路由表拓扑图,需要说明的是这两幅图是路由项组织的核心结构,这里略去了其和路由缓存、arp缓存、网络命名空间等之间的交互。图中黄色部分是数据结构,淡蓝色部分是对应数据结构的成员,成员的等号右边是其具有的值。

Struct fib_table:代表一个路由表,

tb_hlist,对于ipv4是&net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]指向的哈希表,用于索引该路由项,对于本机IP地址由&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]哈希表来指向。

tb_id用于标识表的ID号,对于路由出去的表id是254,本地IP表则id是255。

tb_data[0]零长数组,这个数组用于指向struct trie结构体,这个结构体就是对

struct rt_trie_node结构体的封装,该结构体只有parent和key两个成员,红色框已经表明它们的所属关系。

struct tnode结构体的前两项也是parent 和key,这就意味着可以使用强制类型转换方法和struct rt_tire_node互换使用。内核经常使用这个技巧。tnode自身是trie node的意思,就是字典树的节点的意义,对应图12.1.1的蓝色节点,tnode的最后一个成员是*child[0],这是又是一个零长数组,图中画出了0和1两个成员,实际上可能还有2…,full_children与empty_children之和等于零长数组成员的个数。

Pos:比较起始位置

Bits:比较的位数。

这两成员算是trie路由算法的核心成员,pos指定了一个32位ipv4地址比较的起始位置,bits指定了比较的比特数,因为IPV4的地址是32位的,pos和bits是0~31之间的数值。

由于10.12.39.0路由项先于192.168.0.10路由项存在这张路由表中,当插入192.168.0.10时,它会和根节点开始查找,192和10的二进制比较结果就是,它们的第一个bit不同,192的第一个bit是1,而10的四位二进制的第一个bit是0,23bit的IPv4地址使用0~31标记,这就是tnode中pos等于0,bits等于1的由来。

leaf对应图12.2.3中的黄色节点,用于标记叶子节点。


图12.2.3 具有10.12.39.0和192.168.0.10两项的路由表

在插入192.168.0.100时,很明显,首先遍历tnode,找到pos是0,bits1是1的一个节点(实际上只有这么一个节点),tnode的children,发现有相同的前缀192.168.0项,即192.168.0.100和192.168.0.10从第25个bit不同,并且不同的那个bit的值是1,这里会创建一个tnode,并将children赋值成适当的叶子。这一过程参看图12.1.4,和图12.1.3对比可以使这一过程更加明晰。当这棵树添加完成了以后,最后会去判断这棵树是否需要rebalance。


图12.2.4 10.12.39.0/192.168.0.10/192.168.0.100三项路由表项

对路由项的核心算法以及路由表项的组织有了一个了解以后,下面就正式进入源码及的剖析,由于代码的分支情况及其繁多,所以会以一个主线分析代码,即使主线不包括的代码也会注释它们的功能。

最后为了加深对pos和bits的奥妙了理解,这里给出3个IP地址分别是10.12.39.0,192.168.0.10和192.168.0.100这三项,这三项和图12.1.4是对应的。插入的顺序是10.12.39.0、192.168.0.10和192.169.0.100,插入192.168.0.10时,其发现和10.12.39.0的第0个比特不同,所以这时会将先前的10.12.39.0作为一个tnode,tnode的pos设置为0,bits设置成1,黄色的那里只有一列,在插入192.168.0.100时,其和192.168.0.10有相同的前缀,所以192.168.0.10赋值到一个tnode,其pos设置为25,bits设置为1。查找时先遍历tnode,在遍历leaf,这个过程会在后面结合具体代码来看。图中虚线右边是rebalance的一个示例。


图12.2.5 10.12.39.0/192.168.0.10/192.168.0.100三项路由表的pos和bits

 12.3 ifconfig下trie路由跟新

12.3.1 路由下路由信息

在/proc/net/目录下fib_trie和fib_triestat这两个文件,这两文件包含了一些trie路由的信息。fib_trie用于显示路由表的树状图,fib_ritestat是trie树的一些统计信息。

没有分配IP地址时的路由信息:

  1. # cat /proc/net/fib_trie   
  2. # cat /proc/net/fib_triestat   
  3. Basic info: size of leaf: 20 bytes, size of tnode: 28 bytes.   
  4. Local:   
  5.     Aver depth:     0.00  
  6.     Max depth:      0  
  7.     Leaves:         0  
  8.     Prefixes:       0  
  9.     Internal nodes: 0  
  10.       
  11.     Pointers: 0  
  12. Null ptrs: 0  
  13. Total size: 0  kB  
  14. Main:   
  15.     Aver depth:     0.00  
  16.     Max depth:      0  
  17.     Leaves:         0  
  18.     Prefixes:       0  
  19.     Internal nodes: 0  
  20.       
  21.     Pointers: 0  
  22. Null ptrs: 0  
  23. Total size: 0  kB  
# cat /proc/net/fib_trie 
# cat /proc/net/fib_triestat 
Basic info: size of leaf: 20 bytes, size of tnode: 28 bytes. 
Local: 
	Aver depth:     0.00
	Max depth:      0
	Leaves:         0
	Prefixes:       0
	Internal nodes: 0
	
	Pointers: 0
Null ptrs: 0
Total size: 0  kB
Main: 
	Aver depth:     0.00
	Max depth:      0
	Leaves:         0
	Prefixes:       0
	Internal nodes: 0
	
	Pointers: 0
Null ptrs: 0
Total size: 0  kB

没有分配IP地址时的路由信息都是空的,这很合逻辑, 在使用ifconfig eth0 10.12.39.221 netmask255.255.255.0配置本机IP时,路由信息的内容非常的多。在fib_trie中可以看到有Local和Main这两个字段,这两个字段分别对应于两张表,Local用于表示自己的IP地址,而Main用于路由出去的IP地址。

在配置10.12.39.221时,LOCAL表中包含了主机号全零和主机号全一的IP地址,这都是协议上规定的特殊地址。

第3行10.12.39.0/24,这里的24对应于12.2节中trie字段的pos,这里10.12.39.0和10.12.192的第一个不同的比特就是第24个(从0位置计)比特。同样第6行中的26也是根据10.12.39.221和10.12.39.255之间的差别。有“+”的行,表明其是一个tnode(节点),“|”则表明了其是一个leaf(叶子)。每个叶子下面“/”的标记了该叶子的属性,host、LOCAL以及BROADCAST是表的类型,每对应这么一项,意味着插入了一次,比如这里的叶子10.12.39.0既属于link类型又属于BROADCAST类型,这两种类型的表项会对应于两次插入路由表的动作。

fib_triestat统计了树的一些统计信息,在12.2节中所述的rebalance操作就是依赖这些统计信息对树进行均衡的。

  1. 1# cat /proc/net/fib_trie   
  2. 2Local:   
  3. 3  +-- 10.12.39.0/24 1 0 0  
  4. 4    |-- 10.12.39.0  
  5. 5        /32 link BROADCAST  
  6. 6     +-- 10.12.39.192/26 1 0 0  
  7. 7        |-- 10.12.39.221  
  8. 8           /32 host LOCAL  
  9. 9        |-- 10.12.39.255  
  10. 10           /32 link BROADCAST  
  11. 11Main:   
  12. 12  |-- 10.12.39.0  
  13. 13     /24 link UNICAST  
  14.   
  15. 15# cat /proc/net/fib_triestat   
  16. 16Basic info: size of leaf: 20 bytes, size of tnode: 28 bytes.   
  17. 17Local:   
  18. 18  Aver depth:     1.66  
  19. 19  Max depth:      2  
  20. 20  Leaves:         3  
  21. 21  Prefixes:       3  
  22. 22  Internal nodes: 2  
  23. 23    1: 2  
  24. 24  Pointers: 4  
  25. 25Null ptrs: 0  
  26. 26Total size: 1  kB  
  27. 27Main:   
  28. 28  Aver depth:     0.00  
  29. 29  Max depth:      0  
  30. 30  Leaves:         1  
  31. 31  Prefixes:       1  
  32. 32  Internal nodes: 0  
  33. 33    
  34. 34  Pointers: 0  
  35. 35Null ptrs: 0  
  36. 36Total size: 1  kB  
1# cat /proc/net/fib_trie 
2Local: 
3  +-- 10.12.39.0/24 1 0 0
4    |-- 10.12.39.0
5        /32 link BROADCAST
6     +-- 10.12.39.192/26 1 0 0
7        |-- 10.12.39.221
8           /32 host LOCAL
9        |-- 10.12.39.255
10           /32 link BROADCAST
11Main: 
12  |-- 10.12.39.0
13     /24 link UNICAST

15# cat /proc/net/fib_triestat 
16Basic info: size of leaf: 20 bytes, size of tnode: 28 bytes. 
17Local: 
18	Aver depth:     1.66
19	Max depth:      2
20	Leaves:         3
21	Prefixes:       3
22	Internal nodes: 2
23	  1: 2
24	Pointers: 4
25Null ptrs: 0
26Total size: 1  kB
27Main: 
28	Aver depth:     0.00
29	Max depth:      0
30	Leaves:         1
31	Prefixes:       1
32	Internal nodes: 0
33	
34	Pointers: 0
35Null ptrs: 0
36Total size: 1  kB

12.3.2  路由通知链函数的注册

前面已经见到过ip_fib_init函数,这个函数在net/ipv4/fib_frontend.c文件中,并且在初始化时会被调用。

  1. 1075 static struct notifier_block fib_inetaddr_notifier = {  
  2. 1076     .notifier_call = fib_inetaddr_event,   
  3. 1077 };  
  4. 1168 void __init ip_fib_init(void)   
  5. 1169 {  
  6. 1170     rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);   
  7. 1171     rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);   
  8. 1172     rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);   
  9. 1173   
  10. 1174     register_pernet_subsys(&fib_net_ops);   
  11. 1175     register_netdevice_notifier(&fib_netdev_notifier);   
  12. 1176     register_inetaddr_notifier(&fib_inetaddr_notifier);   
  13. 1177   
  14. 1178     fib_trie_init();  
  15. 1179 }  
1075 static struct notifier_block fib_inetaddr_notifier = {
1076     .notifier_call = fib_inetaddr_event, 
1077 };
1168 void __init ip_fib_init(void) 
1169 {
1170     rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL); 
1171     rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL); 
1172     rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL); 
1173 
1174     register_pernet_subsys(&fib_net_ops); 
1175     register_netdevice_notifier(&fib_netdev_notifier); 
1176     register_inetaddr_notifier(&fib_inetaddr_notifier); 
1177 
1178     fib_trie_init();
1179 }

其1176行注册的结构体在1075行,这节就以注册的fib_inetaddr_event函数开始,当使用ifconfig eth0 10.12.39.221 netmask255.255.255.0 设置设备IP地址时,这个函数就会被触发调用。1175行的fib_netdev_notifier函数也会在该设备获得地址的时候出发调用,进而会配置12.3.1节中那些未指定IP地址。

12.3.3  ifconfig调用流程

ifconfig配置IP地址的函数调用,第一个内核里被调用到的函数是inet_ioctl(),经过devinet_ioctl函数会调用fib_inetaddr_event():

net/ipv4/af_inet.c inet_ioctl()

net/ipv4/devinet.c devinet_ioctl()

fib_inetaddr_event()这个函数是在上一节注册的通知链函数,从这个函数的命名(fib forward information base)就可以看出其和路由是有直接关系关系的,这节就从这个函数开始。

好了从现在开始就要正式接触Linux内核网络路由代码细节了。由12.3.1节可知,一个ifconfig命令会在Local表中配置三项路由项,在Main表中配置一项路由项,本节所述内容是ifconfig在Local表创建路由项所经历的代码片段,当然在操作Main表时用的都是同一套代码。

图12.3.1给出是的ifconfig在创建一个本地IPv4地址时所调用的函数,在后面继续创建路由项时同样调用这些函数,只是执行的逻辑分支会不一样,为了让脉络更加清晰,图12.3.1是添加10.12.39.221这一项路由项的函数调用流程。

首先在fib_inetaddr_event检测导师是NETDEV_UP事件发生,其会调用fib_add_ifaddr函数处理设备UP事件,fib_add_ifaddr的源码如下:

net/ipv4/fib_frontend.c

  1. 734 void fib_add_ifaddr(struct in_ifaddr *ifa)  
  2. 735 {  
  3. 736     struct in_device *in_dev = ifa->ifa_dev;  
  4. 737     struct net_device *dev = in_dev->dev;  
  5. 738     struct in_ifaddr *prim = ifa;  
  6. 739     __be32 mask = ifa->ifa_mask;  
  7. 740     __be32 addr = ifa->ifa_local;  
  8. 741     __be32 prefix = ifa->ifa_address & mask;  
  9. 742   
  10. 743     if (ifa->ifa_flags & IFA_F_SECONDARY) {  
  11. 744         prim = inet_ifa_byprefix(in_dev, prefix, mask);  
  12. 745         if (prim == NULL) {  
  13. 746             pr_warn("%s: bug: prim == NULL\n", __func__);  
  14. 747             return;  
  15. 748         }  
  16. 749     }  
  17. 750   
  18. 751     fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);  
  19. 752   
  20. 753     if (!(dev->flags & IFF_UP))  
  21. 754         return;  
  22. 755   
  23. 756     /* Add broadcast address, if it is explicitly assigned. */  
  24. 757     if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))  
  25. 758         fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);  
  26. 759   
  27. 760     if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&  
  28. 761         (prefix != addr || ifa->ifa_prefixlen < 32)) {  
  29. 762         fib_magic(RTM_NEWROUTE,  
  30. 763               dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,  
  31. 764               prefix, ifa->ifa_prefixlen, prim);  
  32. 765   
  33. 766         /* Add network specific broadcasts, when it takes a sense */  
  34. 767         if (ifa->ifa_prefixlen < 31) {  
  35. 768             fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);  
  36. 769             fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask,  
  37. 770                   32, prim);  
  38. 771         }  
  39. 772     }  
  40. 773 }  
 734 void fib_add_ifaddr(struct in_ifaddr *ifa)
 735 {
 736     struct in_device *in_dev = ifa->ifa_dev;
 737     struct net_device *dev = in_dev->dev;
 738     struct in_ifaddr *prim = ifa;
 739     __be32 mask = ifa->ifa_mask;
 740     __be32 addr = ifa->ifa_local;
 741     __be32 prefix = ifa->ifa_address & mask;
 742 
 743     if (ifa->ifa_flags & IFA_F_SECONDARY) {
 744         prim = inet_ifa_byprefix(in_dev, prefix, mask);
 745         if (prim == NULL) {
 746             pr_warn("%s: bug: prim == NULL\n", __func__);
 747             return;
 748         }
 749     }
 750 
 751     fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
 752 
 753     if (!(dev->flags & IFF_UP))
 754         return;
 755 
 756     /* Add broadcast address, if it is explicitly assigned. */
 757     if (ifa->ifa_broadcast && ifa->ifa_broadcast != htonl(0xFFFFFFFF))
 758         fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
 759 
 760     if (!ipv4_is_zeronet(prefix) && !(ifa->ifa_flags & IFA_F_SECONDARY) &&
 761         (prefix != addr || ifa->ifa_prefixlen < 32)) {
 762         fib_magic(RTM_NEWROUTE,
 763               dev->flags & IFF_LOOPBACK ? RTN_LOCAL : RTN_UNICAST,
 764               prefix, ifa->ifa_prefixlen, prim);
 765 
 766         /* Add network specific broadcasts, when it takes a sense */
 767         if (ifa->ifa_prefixlen < 31) {
 768             fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
 769             fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix | ~mask,
 770                   32, prim);
 771         }
 772     }
 773 }

743~749 如果flag参数指定了配置IP对象为从属设备或者临时设备,但是根据索引找不到该设备,返回错误。

751行,在ID等于255的表中,插入IP地址类型为2的IP地址dd270c0a。ID等于255的表示是LOCAL表,即该表中的所有IP项的目的地址均是本机。类型见IP地址类型。

760~770处理广播包的路由项。主机号全为1和主机号全为0,实际的过程比较复杂,这里就不展开了,这里以751行添加的过程来说明路由表项是如何添加的。

IP地址的类型如下

include/uapi/linux/ rtnetlink.h

  1. 191 enum {  
  2. 192     RTN_UNSPEC,//未定义  
  3. 193     RTN_UNICAST, //单播,网关或者直接路由         
  4. 194     RTN_LOCAL, //host地址,目的地址是本机  
  5. 195     RTN_BROADCAST,    广播地址,广播收广播发  
  6. 197     RTN_ANYCAST,   //广播接收数据,单播发送数据,IPV6协议规定的地址类型  
  7. 199     RTN_MULTICAST,   多播  
  8. 200     RTN_BLACKHOLE,   丢弃  
  9. 201     RTN_UNREACHABLE,  目的不可达  
  10. 202     RTN_PROHIBIT,   管理员禁止IP地址  
  11. 203     RTN_THROW,     /* Not in this table        */  
  12. 204     RTN_NAT,      需要进行网络地址转换  
  13. 205     RTN_XRESOLVE,    外部解析  
  14. 206     __RTN_MAX  
  15. 207 };  
191 enum {
192     RTN_UNSPEC,//未定义
193     RTN_UNICAST, //单播,网关或者直接路由       
194     RTN_LOCAL, //host地址,目的地址是本机
195     RTN_BROADCAST,    广播地址,广播收广播发
197     RTN_ANYCAST,   //广播接收数据,单播发送数据,IPV6协议规定的地址类型
199     RTN_MULTICAST,   多播
200     RTN_BLACKHOLE,   丢弃
201     RTN_UNREACHABLE,  目的不可达
202     RTN_PROHIBIT,   管理员禁止IP地址
203     RTN_THROW,     /* Not in this table        */
204     RTN_NAT,      需要进行网络地址转换
205     RTN_XRESOLVE,    外部解析
206     __RTN_MAX
207 };

图12.3.1 ifconfig 配置IP地址调用流程

路由项的管理集中在fib_frontend.c、fib_semantics.c和fib_trie.c这三个文件,后面的代码也集中在这三个文件中。接着fib_magic函数的调用。这个函数的三个参数:

Cmd:ioctl传递的命令参数,这里是RTM_NEWROUTE,对应添加路由项,此外前面提到的netfilter也是在这个magic函数里处理的。

Type:这个参数是路由项的类型,前面已经介绍过了,这里传递的参数是RTN_LOCAL,配置的IP地址的目的端就是本机。

Dst:是一个32bit的IP地址,是要添加到路由表项,ifa是一些附属配置信息。

net/ipv4/fib_frontend.c

  1. 696 static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)  
  2. 697 {  
  3. 698     struct net *net = dev_net(ifa->ifa_dev->dev);  
  4. 699     struct fib_table *tb;  
  5. 700     struct fib_config cfg = {  
  6. 701         .fc_protocol = RTPROT_KERNEL,  
  7. 702         .fc_type = type,  
  8. 703         .fc_dst = dst,  
  9. 704         .fc_dst_len = dst_len,  
  10. 705         .fc_prefsrc = ifa->ifa_local,  
  11. 706         .fc_oif = ifa->ifa_dev->dev->ifindex,  
  12. 707         .fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,  
  13. 708         .fc_nlinfo = {  
  14. 709             .nl_net = net,  
  15. 710         },  
  16. 711     };  
  17. 712   
  18. 713     if (type == RTN_UNICAST)  
  19. 714         tb = fib_new_table(net, RT_TABLE_MAIN);  
  20. 715     else  
  21. 716         tb = fib_new_table(net, RT_TABLE_LOCAL);  
  22. 717   
  23. 718     if (tb == NULL)  
  24. 719         return;  
  25. 720   
  26. 721     cfg.fc_table = tb->tb_id;  
  27. 722   
  28. 723     if (type != RTN_LOCAL)  
  29. 724         cfg.fc_scope = RT_SCOPE_LINK;  
  30. 725     else  
  31. 726         cfg.fc_scope = RT_SCOPE_HOST;  
  32. 727   
  33. 728     if (cmd == RTM_NEWROUTE)  
  34. 729         fib_table_insert(tb, &cfg);  
  35. 730     else  
  36. 731         fib_table_delete(tb, &cfg);  
  37. 732 }  
 696 static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifaddr *ifa)
 697 {
 698     struct net *net = dev_net(ifa->ifa_dev->dev);
 699     struct fib_table *tb;
 700     struct fib_config cfg = {
 701         .fc_protocol = RTPROT_KERNEL,
 702         .fc_type = type,
 703         .fc_dst = dst,
 704         .fc_dst_len = dst_len,
 705         .fc_prefsrc = ifa->ifa_local,
 706         .fc_oif = ifa->ifa_dev->dev->ifindex,
 707         .fc_nlflags = NLM_F_CREATE | NLM_F_APPEND,
 708         .fc_nlinfo = {
 709             .nl_net = net,
 710         },
 711     };
 712 
 713     if (type == RTN_UNICAST)
 714         tb = fib_new_table(net, RT_TABLE_MAIN);
 715     else
 716         tb = fib_new_table(net, RT_TABLE_LOCAL);
 717 
 718     if (tb == NULL)
 719         return;
 720 
 721     cfg.fc_table = tb->tb_id;
 722 
 723     if (type != RTN_LOCAL)
 724         cfg.fc_scope = RT_SCOPE_LINK;
 725     else
 726         cfg.fc_scope = RT_SCOPE_HOST;
 727 
 728     if (cmd == RTM_NEWROUTE)
 729         fib_table_insert(tb, &cfg);
 730     else
 731         fib_table_delete(tb, &cfg);
 732 }

700~710行,将用户传递的配置参数使用内核下的数据结构保护起来。

713~716通过类型,索引要添加到的表。

include/net/ip_fib.h

  1. 204 staticinline struct fib_table *fib_get_table(struct net *net, u32 id)  
  2. 205 {  
  3. 206     struct hlist_head *ptr;   
  4. 207  
  5. 208     ptr = id == RT_TABLE_LOCAL ?   
  6. 209        &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] :   
  7. 210        &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX];   
  8. 211     return hlist_entry(ptr->first, structfib_table, tb_hlist);   
  9. 212 }  
  10. 213  
  11. 214 staticinline struct fib_table *fib_new_table(struct net *net, u32 id)   
  12. 215 {  
  13. 216     return fib_get_table(net, id);   
  14. 217 }  
204 staticinline struct fib_table *fib_get_table(struct net *net, u32 id)
205 {
206     struct hlist_head *ptr; 
207
208     ptr = id == RT_TABLE_LOCAL ? 
209        &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX] : 
210        &net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]; 
211     return hlist_entry(ptr->first, structfib_table, tb_hlist); 
212 }
213
214 staticinline struct fib_table *fib_new_table(struct net *net, u32 id) 
215 {
216     return fib_get_table(net, id); 
217 }

这个函数的第一个参数是一个net,代表了一个网络,其附属于一个网络命名空间,第二个参数是716行的RT_TABLE_LOCAL。索引返回的类型是fib_table,在路由表核心数据结构时,介绍过fib_table代表的是路由表的大类,这些路由表可以多打256张,这么多张的表使用哈希法索引。

fib_get_table的209行就是返回ipv4的路由项,&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]。

718~719判断是否得到了该表项,依据就是这个表的地址非NULL,实际上在初始化时这个表已经得到了存储表项的空间。

721~726将参数保存在cfg结构体内。

728~729将cfg中保存的配置路由项的信息,插入到由fib_get_table获得的表中。

路由项的插入工作就是在fib_table_insert这个函数中完成的,这个函数大体分为四个部分:

1、路由信息获取,每一个路由项都会对应一个路由信息,并且一个路由信息可以对应多个路由项。获取含义是先查找,如果找不到路由信息则根据传递的参数创建路由信息。

2、Trie树节点tnode。存在多个路由项,并且他们有共同的前缀时,共同的前缀会被设定为tnode。

3、Trie树叶子leaf,如果一个路由项不在路由表中,但是其和其它已经在路由表中的项具有相同的前缀,则会创建对应的叶子,在rebalance操作时,tnode和leaf因深度和广度原因都可能被调整。

4、插入操作,就是将新创建的叶子插入到trie树上去,也就对应创建了一个路由项。

net/ipv4/fib_frontend.c
  1. 1172 int fib_table_insert(struct fib_table*tb, struct fib_config *cfg)   
  2. 1173 {  
  3. 1174    struct trie *t = (struct trie *) tb->tb_data;   
  4. 1175    struct fib_alias *fa, *new_fa;   
  5. 1176    struct list_head *fa_head = NULL;  
  6. 1177    struct fib_info *fi;  
  7. 1178    int plen = cfg->fc_dst_len;  
  8. 1179    u8 tos = cfg->fc_tos;  
  9. 1180    u32 key, mask;  
  10. 1181    int err;  
  11. 1182    struct leaf *l;  
  12. //图12.1.2中plen值等于32。下面的判断条件满足。  
  13. 1184    if (plen > 32)   
  14. 1185        return -EINVAL;  
  15. //图12.3.1中,fc_dst是0a0c27dd,即10.12.39.221,用户空间配置的路由地址  
  16. 1187    key = ntohl(cfg->fc_dst);  
  17. //mask 等于~0  
  18. 1191    mask = ntohl(inet_make_mask(plen));  
  19. //验证key(目的地址)和目的地址掩码的正确性。  
  20. 1193    if (key & ~mask)  
  21. 1194        return -EINVAL;  
  22. //经过掩码匹配,得到真正的键值key等于10.12.39.221  
  23. 1196    key = key & mask;  
  24. //对应于四个部分中的第一个部分,路由信息获取,见后文  
  25. 1198    fi = fib_create_info(cfg);  
  26. 1199    if (IS_ERR(fi)) {  
  27. 1200        err = PTR_ERR(fi);  
  28. 1201        goto err;  
  29. 1202    }  
  30. //对应于四个部分的第二、三两个部分,遍历tnode获取,获取叶子leaf,见后文。  
  31. 1204    l = fib_find_node(t, key);  
  32. 1205    fa = NULL;  
  33. //在使用ifconfig向LOCAL表插入10.12.39.221前,路由表时空的,所以这里不可能找到对应的fib_table 和leaf信息。所以这里fi返回值是新创建的路由信息记录项,I==NULL,fa==NULL,直接跳至1293行,对于如果有相同前缀的路由IP项,fib_find_node返回的是一个tnode。  
  34. 1207    if (l) {  
  35. //get_fa_head根据leaf信息,获得fib_alias链表的头指针。  
  36. 1208        fa_head = get_fa_head(l, plen);  
  37. //遍历上述链表,找到合适的服务类型和优先级,合适的意义是优先级要高于这里传递的参数  
  38. 1209        fa = fib_find_alias(fa_head, tos, fi->fib_priority);  
  39. 1210    }  
  40. //fa在首次配置IP地址时为空,第一遍先跳过下面对这个if语句的注释吧,第二遍再来看。   
  41. 1223    if (fa && fa->fa_tos == tos &&  
  42. 1224        fa->fa_info->fib_priority == fi->fib_priority) {  
  43. 1225        struct fib_alias *fa_first, *fa_match;  
  44. 1226  
  45. 1227        err = -EEXIST;  
  46. 1228        if (cfg->fc_nlflags & NLM_F_EXCL)  
  47. 1229             goto out;  
  48. 1230  
  49. 1231        /* We have 2 goals: 
  50. 1232         * 1. Find exact match for type, scope, fib_info to avoid 
  51. 1233         * duplicate routes 
  52. 1234         * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it 
  53. 1235         */  
  54. 1236        fa_match = NULL;  
  55. 1237        fa_first = fa;  
  56. 1238        fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);  
  57. 1239        list_for_each_entry_continue(fa, fa_head, fa_list) {  
  58. 1240             if (fa->fa_tos != tos)  
  59. 1241                 break;  
  60. 1242             if(fa->fa_info->fib_priority != fi->fib_priority)  
  61. 1243                 break;  
  62. 1244             if (fa->fa_type ==cfg->fc_type &&  
  63. 1245                 fa->fa_info == fi) {  
  64. 1246                 fa_match = fa;  
  65. 1247                 break;  
  66. 1248             }  
  67. 1249        }  
  68. 1250  
  69. 1251        if (cfg->fc_nlflags & NLM_F_REPLACE) {  
  70. 1252             struct fib_info *fi_drop;  
  71. 1253             u8 state;  
  72. 1254  
  73. 1255             fa = fa_first;  
  74. 1256             if (fa_match) {  
  75. 1257                 if (fa == fa_match)  
  76. 1258                     err = 0;  
  77. 1259                goto out;  
  78. 1260             }  
  79. 1261             err = -ENOBUFS;  
  80. //上面的代码已然没有找到fib alias,所以这里创建新的fib alias。并在1266~1273行对其成员进行初始化。  
  81. 1262             new_fa =kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);  
  82. 1263             if (new_fa == NULL)  
  83. 1264                 goto out;  
  84. 1265  
  85. 1266             fi_drop = fa->fa_info;  
  86. 1267             new_fa->fa_tos = fa->fa_tos;  
  87. 1268             new_fa->fa_info = fi;  
  88. 1269             new_fa->fa_type =cfg->fc_type;  
  89. 1270             state = fa->fa_state;  
  90. 1271             new_fa->fa_state = state &~FA_S_ACCESSED;  
  91. 1272  
  92. 1273             list_replace_rcu(&fa->fa_list,&new_fa->fa_list);  
  93. 1274             alias_free_mem_rcu(fa);  
  94. 1275  
  95. 1276             fib_release_info(fi_drop);  
  96. 1277             if (state & FA_S_ACCESSED)  
  97. 1278                rt_cache_flush(cfg->fc_nlinfo.nl_net);  
  98. 1279            rtmsg_fib(RTM_NEWROUTE,htonl(key), new_fa, plen,  
  99. 1280                 tb->tb_id,&cfg->fc_nlinfo, NLM_F_REPLACE);  
  100. 1281  
  101. 1282             goto succeeded;  
  102. 1283        }  
  103. 1284        /* Error if we find a perfect match which 
  104. 1285         * uses the same scope, type, and nexthop 
  105. 1286         * information. 
  106. 1287         */  
  107. 1288        if (fa_match)  
  108. 1289             goto out;  
  109. 1290  
  110. 1291        if (!(cfg->fc_nlflags & NLM_F_APPEND))  
  111. 1292             fa = fa_first;  
  112. 1293    }  
  113. 1294    err = -ENOENT;  
  114. //由图12.3.1可以知道fc_nlflags的值等于1024,就是0x400,正好等于这里的(NLM_F_CREATE),所以还要接着1297行看。  
  115. 1295    if (!(cfg->fc_nlflags & NLM_F_CREATE))  
  116.  1296         goto out;  
  117. 1297  
  118. 1298    err = -ENOBUFS;  
  119. //从fn_alias_kmem上分配一块cache,这个cache用于flash别名系统  
  120. 1299    new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);  
  121. 1300    if (new_fa == NULL)  
  122. 1301        goto out;  
  123. //向fib_alias中插入相关的路由信息,并将其成员fa_info指向前面的fib_ info结构体。  
  124. 1303    new_fa->fa_info = fi;  
  125. 1304    new_fa->fa_tos = tos;  
  126. 1305    new_fa->fa_type = cfg->fc_type;  
  127. 1306    new_fa->fa_state = 0;  
  128. 1307    /* 
  129. 1308     * Insert new entry to the list. 
  130. 1309     */  
  131. //fa_head在1208行没有得到复制,这里fa_head == NULL, fib_insert_node对应于第四个部分,插入操作,见后文  
  132. 1311    if (!fa_head) {  
  133. 1312        fa_head = fib_insert_node(t, key, plen);  
  134. 1313        if (unlikely(!fa_head)) {  
  135. 1314             err = -ENOMEM;  
  136. 1315             goto out_free_new_fa;  
  137. 1316        }  
  138. 1317    }  
  139. 1318  
  140. //跟新tb_num_default字段  
  141. 1319    if (!plen)  
  142. 1320        tb->tb_num_default++;  
  143. //处理fa_list链表  
  144. 1322    list_add_tail_rcu(&new_fa->fa_list,  
  145. 1323               (fa ? &fa->fa_list :fa_head));  
  146. 1324  
  147. 1325    rt_cache_flush(cfg->fc_nlinfo.nl_net);  
  148. 发送netlink消息  
  149. 1326    rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,  
  150. 1327           &cfg->fc_nlinfo, 0);  
  151. 1328 succeeded:  
  152. 1329    return 0;  
  153. //差错处理  
  154. 1331 out_free_new_fa:  
  155. 1332    kmem_cache_free(fn_alias_kmem, new_fa);  
  156. 1333 out:  
  157. 1334    fib_release_info(fi);  
  158. 1335 err:  
  159. 1336    return err;  
  160. 1337 }  
1172 int fib_table_insert(struct fib_table*tb, struct fib_config *cfg) 
1173 {
1174    struct trie *t = (struct trie *) tb->tb_data; 
1175    struct fib_alias *fa, *new_fa; 
1176    struct list_head *fa_head = NULL;
1177    struct fib_info *fi;
1178    int plen = cfg->fc_dst_len;
1179    u8 tos = cfg->fc_tos;
1180    u32 key, mask;
1181    int err;
1182    struct leaf *l;
//图12.1.2中plen值等于32。下面的判断条件满足。
1184    if (plen > 32) 
1185        return -EINVAL;
//图12.3.1中,fc_dst是0a0c27dd,即10.12.39.221,用户空间配置的路由地址
1187    key = ntohl(cfg->fc_dst);
//mask 等于~0
1191    mask = ntohl(inet_make_mask(plen));
//验证key(目的地址)和目的地址掩码的正确性。
1193    if (key & ~mask)
1194        return -EINVAL;
//经过掩码匹配,得到真正的键值key等于10.12.39.221
1196    key = key & mask;
//对应于四个部分中的第一个部分,路由信息获取,见后文
1198    fi = fib_create_info(cfg);
1199    if (IS_ERR(fi)) {
1200        err = PTR_ERR(fi);
1201        goto err;
1202    }
//对应于四个部分的第二、三两个部分,遍历tnode获取,获取叶子leaf,见后文。
1204    l = fib_find_node(t, key);
1205    fa = NULL;
//在使用ifconfig向LOCAL表插入10.12.39.221前,路由表时空的,所以这里不可能找到对应的fib_table 和leaf信息。所以这里fi返回值是新创建的路由信息记录项,I==NULL,fa==NULL,直接跳至1293行,对于如果有相同前缀的路由IP项,fib_find_node返回的是一个tnode。
1207    if (l) {
//get_fa_head根据leaf信息,获得fib_alias链表的头指针。
1208        fa_head = get_fa_head(l, plen);
//遍历上述链表,找到合适的服务类型和优先级,合适的意义是优先级要高于这里传递的参数
1209        fa = fib_find_alias(fa_head, tos, fi->fib_priority);
1210    }
//fa在首次配置IP地址时为空,第一遍先跳过下面对这个if语句的注释吧,第二遍再来看。 
1223    if (fa && fa->fa_tos == tos &&
1224        fa->fa_info->fib_priority == fi->fib_priority) {
1225        struct fib_alias *fa_first, *fa_match;
1226
1227        err = -EEXIST;
1228        if (cfg->fc_nlflags & NLM_F_EXCL)
1229             goto out;
1230
1231        /* We have 2 goals:
1232         * 1. Find exact match for type, scope, fib_info to avoid
1233         * duplicate routes
1234         * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
1235         */
1236        fa_match = NULL;
1237        fa_first = fa;
1238        fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
1239        list_for_each_entry_continue(fa, fa_head, fa_list) {
1240             if (fa->fa_tos != tos)
1241                 break;
1242             if(fa->fa_info->fib_priority != fi->fib_priority)
1243                 break;
1244             if (fa->fa_type ==cfg->fc_type &&
1245                 fa->fa_info == fi) {
1246                 fa_match = fa;
1247                 break;
1248             }
1249        }
1250
1251        if (cfg->fc_nlflags & NLM_F_REPLACE) {
1252             struct fib_info *fi_drop;
1253             u8 state;
1254
1255             fa = fa_first;
1256             if (fa_match) {
1257                 if (fa == fa_match)
1258                     err = 0;
1259                goto out;
1260             }
1261             err = -ENOBUFS;
//上面的代码已然没有找到fib alias,所以这里创建新的fib alias。并在1266~1273行对其成员进行初始化。
1262             new_fa =kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
1263             if (new_fa == NULL)
1264                 goto out;
1265
1266             fi_drop = fa->fa_info;
1267             new_fa->fa_tos = fa->fa_tos;
1268             new_fa->fa_info = fi;
1269             new_fa->fa_type =cfg->fc_type;
1270             state = fa->fa_state;
1271             new_fa->fa_state = state &~FA_S_ACCESSED;
1272
1273             list_replace_rcu(&fa->fa_list,&new_fa->fa_list);
1274             alias_free_mem_rcu(fa);
1275
1276             fib_release_info(fi_drop);
1277             if (state & FA_S_ACCESSED)
1278                rt_cache_flush(cfg->fc_nlinfo.nl_net);
1279            rtmsg_fib(RTM_NEWROUTE,htonl(key), new_fa, plen,
1280                 tb->tb_id,&cfg->fc_nlinfo, NLM_F_REPLACE);
1281
1282             goto succeeded;
1283        }
1284        /* Error if we find a perfect match which
1285         * uses the same scope, type, and nexthop
1286         * information.
1287         */
1288        if (fa_match)
1289             goto out;
1290
1291        if (!(cfg->fc_nlflags & NLM_F_APPEND))
1292             fa = fa_first;
1293    }
1294    err = -ENOENT;
//由图12.3.1可以知道fc_nlflags的值等于1024,就是0x400,正好等于这里的(NLM_F_CREATE),所以还要接着1297行看。
1295    if (!(cfg->fc_nlflags & NLM_F_CREATE))
 1296         goto out;
1297
1298    err = -ENOBUFS;
//从fn_alias_kmem上分配一块cache,这个cache用于flash别名系统
1299    new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
1300    if (new_fa == NULL)
1301        goto out;
//向fib_alias中插入相关的路由信息,并将其成员fa_info指向前面的fib_ info结构体。
1303    new_fa->fa_info = fi;
1304    new_fa->fa_tos = tos;
1305    new_fa->fa_type = cfg->fc_type;
1306    new_fa->fa_state = 0;
1307    /*
1308     * Insert new entry to the list.
1309     */
//fa_head在1208行没有得到复制,这里fa_head == NULL, fib_insert_node对应于第四个部分,插入操作,见后文
1311    if (!fa_head) {
1312        fa_head = fib_insert_node(t, key, plen);
1313        if (unlikely(!fa_head)) {
1314             err = -ENOMEM;
1315             goto out_free_new_fa;
1316        }
1317    }
1318
//跟新tb_num_default字段
1319    if (!plen)
1320        tb->tb_num_default++;
//处理fa_list链表
1322    list_add_tail_rcu(&new_fa->fa_list,
1323               (fa ? &fa->fa_list :fa_head));
1324
1325    rt_cache_flush(cfg->fc_nlinfo.nl_net);
发送netlink消息
1326    rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
1327           &cfg->fc_nlinfo, 0);
1328 succeeded:
1329    return 0;
//差错处理
1331 out_free_new_fa:
1332    kmem_cache_free(fn_alias_kmem, new_fa);
1333 out:
1334    fib_release_info(fi);
1335 err:
1336    return err;
1337 }

1198行fib_create_info获得一个路由信息记录结构体,当要获得的对象不存在时,会创建一个新的路由信息结构体fib_info。

net/ipv4/fib_semantics.c

  1. 773 struct fib_info*fib_create_info(structfib_config *cfg)  
  2.  774 {  
  3.  775    interr;  
  4.  776    structfib_info *fi = NULL;  
  5.  777    structfib_info *ofi;  
  6.  778    int nhs= 1;  
  7.  779    structnet *net = cfg->fc_nlinfo.nl_net;  
  8. //用户空间传递进来的tc_type值是2,所以这里的检查类型的有效性通过。  
  9.  781    if(cfg->fc_type > RTN_MAX)  
  10.  782        gotoerr_inval;  
  11. //本地地址LOCAL的值是RT_SCOPE_HOST,254,用户传递进来的fc_scope等于254(图13.3.1),这里检查也通过。  
  12.  784    /* Fastcheck to catch the most weird cases */  
  13.  785    if(fib_props[cfg->fc_type].scope > cfg->fc_scope)  
  14.  786        gotoerr_inval;  
  15. 796    err =-ENOBUFS;  
  16. // 对于由于前表项为空,所以统计路由信息技术变量fib_info_cnt的值等于0,索引路由信息的哈希数组大小变量//fib_info_hash_size值等于0.  
  17.  797    if(fib_info_cnt >= fib_info_hash_size) {  
  18.  798        unsignedint new_size = fib_info_hash_size << 1;  
  19.  799        structhlist_head *new_info_hash;  
  20.  800        structhlist_head *new_laddrhash;  
  21.  801        unsignedint bytes;  
  22. //由于fib_info_hash_size确实为0,所以这里的new_size将被赋值成16。  
  23.  803        if(!new_size)  
  24.  804            new_size= 16;  
  25.  805        bytes= new_size * sizeof(struct hlist_head *);  
  26. //为哈希信息和哈希地址存储分配内存,如果小于一个页使用kzalloc,大于一个页使用__get_free_pages。  
  27.  806        new_info_hash= fib_info_hash_alloc(bytes);  
  28.  807        new_laddrhash= fib_info_hash_alloc(bytes);  
  29. //分配失败的处理,如果没有失败,调用fib_info_hash_move进行处理。  
  30.  808        if (!new_info_hash || !new_laddrhash){  
  31.  809            fib_info_hash_free(new_info_hash,bytes);  
  32.  810            fib_info_hash_free(new_laddrhash,bytes);  
  33.  811        }else  
  34. //根据先前申请到的内存,设置fib_info_hash和fib_info_laddrhash这两个fib_semantics.c文件内的全局变量。这两变量用于链接所有的fib_info结构体。  
  35.  812            fib_info_hash_move(new_info_hash,new_laddrhash, new_size);  
  36.  813  
  37.  814        if(!fib_info_hash_size)  
  38.  815            gotofailure;  
  39.  816    }  
  40. //申请路由信息空间,注意零长数组指向了下一跳fib_nh(nh—next hop)。  
  41. 818    fi =kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);  
  42.  819    if (fi== NULL)  
  43.  820        gotofailure;  
  44. /*用户空间没有配置metric,metirc用于配置下一跳的个数,不指定的情况下赋值如下。 
  45. *const u32 dst_default_metrics[RTAX_MAX+ 1]= { 
  46. *           [RTAX_MAX]= 0xdeadbeef, 
  47. *}; 
  48. */  
  49.  821    if(cfg->fc_mx) {  
  50.  822        fi->fib_metrics= kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);  
  51.  823        if(!fi->fib_metrics)  
  52.  824            gotofailure;  
  53.  825    } else  
  54.  826        fi->fib_metrics= (u32 *) dst_default_metrics;  
  55. //fib_info_cnt用于计数有意义的fib_info 个数。  
  56.  827    fib_info_cnt++;  
  57. //下面的赋值,见图12.3.2,fib_info结构体赋值,值来源于用户配置。  
  58.  829    fi->fib_net= hold_net(net);  
  59.  830    fi->fib_protocol= cfg->fc_protocol;  
  60.  831    fi->fib_scope= cfg->fc_scope;  
  61.  832    fi->fib_flags= cfg->fc_flags;  
  62.  833    fi->fib_priority= cfg->fc_priority;  
  63.  834    fi->fib_prefsrc= cfg->fc_prefsrc;  
  64.  835    fi->fib_type= cfg->fc_type;  
  65. //初始化下一跳的成员,对于没有配置等价路由的,这个循环只会执行一次,对于等价路由,有几个等价的路由项就会执行几  
  66. //次。将下一跳的nh_parent指向fib_info字段。分配一个percpu变量,这会为每个核创建一个对应的rtable,rtable是路由缓//存部分的,其将加速路由项的查找,路由缓存参考11.5节。  
  67.  837    fi->fib_nhs= nhs;  
  68.  838    change_nexthops(fi){  
  69.  839        nexthop_nh->nh_parent= fi;  
  70.  840        nexthop_nh->nh_pcpu_rth_output= alloc_percpu(struct rtable __rcu *);  
  71.  841        if(!nexthop_nh->nh_pcpu_rth_output)  
  72.  842            gotofailure;  
  73.  843    }endfor_nexthops(fi)  
  74. //用户空间没有传递metric,跳过。  
  75.  845    if(cfg->fc_mx) {  
  76.  846        structnlattr *nla;  
  77.  847        intremaining;  
  78.  848  
  79.  849        nla_for_each_attr(nla,cfg->fc_mx, cfg->fc_mx_len, remaining) {  
  80.  850            inttype = nla_type(nla);  
  81.  851  
  82.  852            if(type) {  
  83.  853                u32 val;  
  84.  854  
  85.  855                if (type > RTAX_MAX)  
  86.  856                    goto err_inval;  
  87.  857                val = nla_get_u32(nla);  
  88.  858                if (type == RTAX_ADVMSS&& val > 65535 - 40)  
  89.  859                    val = 65535 - 40;  
  90.  860                if (type == RTAX_MTU&& val > 65535 - 15)  
  91.  861                    val = 65535 - 15;  
  92.  862                fi->fib_metrics[type - 1] =val;  
  93. 863            }  
  94.  864        }  
  95.  865    }  
  96. //用户空间也没有配置等价路由,执行else语句。  
  97.  867    if(cfg->fc_mp) {  
  98.  868#ifdefCONFIG_IP_ROUTE_MULTIPATH  
  99.  869        err= fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);  
  100.  870        if(err != 0)  
  101.  871            gotofailure;  
  102.  872        if(cfg->fc_oif && fi->fib_nh->nh_oif !=cfg->fc_oif)  
  103.  873            gotoerr_inval;  
  104.  874        if(cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)  
  105.  875            gotoerr_inval;  
  106.  876#ifdef CONFIG_IP_ROUTE_CLASSID  
  107.  877        if(cfg->fc_flow && fi->fib_nh->nh_tclassid !=cfg->fc_flow)  
  108.  878            gotoerr_inval;  
  109.  879#endif  
  110.  880#else  
  111.  881        gotoerr_inval;  
  112.  882#endif  
  113.  883     } else{  
  114. //对下一跳的赋值,见图12.3.2。赋值的来源同样是用户空间。  
  115.  884        structfib_nh *nh = fi->fib_nh;  
  116.  885  
  117.  886        nh->nh_oif= cfg->fc_oif;  
  118.  887        nh->nh_gw= cfg->fc_gw;  
  119.  888        nh->nh_flags= cfg->fc_flags;  
  120.  889#ifdef CONFIG_IP_ROUTE_CLASSID  
  121.  890        nh->nh_tclassid= cfg->fc_flow;  
  122.  891        if(nh->nh_tclassid)  
  123.  892            fi->fib_net->ipv4.fib_num_tclassid_users++;  
  124.  893#endif  
  125.  894#ifdefCONFIG_IP_ROUTE_MULTIPATH  
  126.  895        nh->nh_weight= 1;  
  127.  896#endif  
  128.  897    }  
  129. //fc_type类型是2,即单播,该类型的error成员值是0,执行else语句。else语句判断type字段是否合法。  
  130.  899    if(fib_props[cfg->fc_type].error) {  
  131.  900        if(cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)  
  132.  901            gotoerr_inval;  
  133.  902        gotolink_it;  
  134.  903    } else{  
  135.  904        switch(cfg->fc_type) {  
  136.  905        caseRTN_UNICAST:  
  137.  906        case RTN_LOCAL:  
  138.  907        caseRTN_BROADCAST:  
  139.  908        case RTN_ANYCAST:  
  140.  909        caseRTN_MULTICAST:  
  141.  910            break;  
  142.  911        default:  
  143.  912            gotoerr_inval;  
  144.  913        }  
  145.  914    }  
  146. //范围大于RT_SCOPE_HOST(253)就出错了。用于限定路由的范围。  
  147.  916    if(cfg->fc_scope > RT_SCOPE_HOST)  
  148.  917        gotoerr_inval;  
  149. //fc_scope的值等于254,执行if语句里的内容,当使用route命令配置一个(RT_SCOPE_LINK)范围地址,执行else语句  
  150.  919    if(cfg->fc_scope == RT_SCOPE_HOST) {  
  151. //fi是在818行创建的指向fib_info的结构体,该结构体的部分程员在821~843被赋值,这里是处理其nh(next hop)字段。  
  152.  920        structfib_nh *nh = fi->fib_nh;  
  153.  921  
  154.  922        /*Local address is added. */  
  155.  923        if(nhs != 1 || nh->nh_gw)  
  156.  924            gotoerr_inval;  
  157. //目的地址是本机,所以scope赋值成RT_SCOPE_NOWHERE,该scope范围不会向外发送数据包。  
  158.  925        nh->nh_scope= RT_SCOPE_NOWHERE;  
  159. //根据索引号获得net_device结构体,net_device结构体用于标记网卡,其实就是“eth0”结构体,该网卡用来发送数据。  
  160.  926        nh->nh_dev= dev_get_by_index(net, fi->fib_nh->nh_oif);  
  161.  927        err= -ENODEV;  
  162.  928        if(nh->nh_dev == NULL)  
  163.  929            gotofailure;  
  164.  930    } else{  
  165.  931        change_nexthops(fi){  
  166. //fib_check_nh检查下一跳语义的正确性,  
  167.  932            err= fib_check_nh(cfg, fi, nexthop_nh);  
  168.  933            if(err != 0)  
  169.  934                goto failure;  
  170.  935        }endfor_nexthops(fi)  
  171.  936    }  
  172. // prefsrc是dd270c0a,并且检查也通过。  
  173.  938     if(fi->fib_prefsrc) {  
  174.  939        if(cfg->fc_type != RTN_LOCAL || !cfg->fc_dst ||  
  175.  940            fi->fib_prefsrc!= cfg->fc_dst)  
  176.  941            if(inet_addr_type(net, fi->fib_prefsrc) != RTN_LOCAL)  
  177.  942                goto err_inval;  
  178.  943    }  
  179. //获得对应接口的源地址信息,将这些信息存放在下一跳nexthop_nh中。  
  180.  945    change_nexthops(fi){  
  181.  946        fib_info_update_nh_saddr(net,nexthop_nh);  
  182.  947    }endfor_nexthops(fi)  
  183. //在fib_info_hash所标示的哈希表中,见图12.3.2左上角,紫色部分;看看要插入的路由项是否已经存在了,如果存在,则//955行直接返回,由于用户空间的配置路由项原本内核路由表中没有,所以接着958行继续。  
  184.  949link_it:  
  185.  950    ofi = fib_find_info(fi);  
  186.  951    if(ofi) {  
  187.  952        fi->fib_dead= 1;  
  188. 953        free_fib_info(fi);  
  189.  954        ofi->fib_treeref++;  
  190.  955        returnofi;  
  191.  956    }  
  192. //路由树引用计数++  
  193.  958    fi->fib_treeref++;  
  194.  959    atomic_inc(&fi->fib_clntref);  
  195. //获得保护该路由信息记录项的锁。这个fib_info_hash表是局部全局的。以安全将上面创建的fi添加到fib_info_hash链表上。  
  196.  960    spin_lock_bh(&fib_info_lock);  
  197. //将这个新的fib_info,添加到管理这个结构的全局哈希表上。  
  198.  961    hlist_add_head(&fi->fib_hash,  
  199.  962               &fib_info_hash[fib_info_hashfn(fi)]);  
  200. 969    change_nexthops(fi){  
  201.  970        structhlist_head *head;  
  202.  971        unsignedint hash;  
  203.  972  
  204.  973        if(!nexthop_nh->nh_dev)  
  205.  974            continue;  
  206. //ifindex的值等于2,对应于eth0。  
  207.  975        hash= fib_devindex_hashfn(nexthop_nh->nh_dev->ifindex);  
  208.  976        head= &fib_info_devhash[hash];  
  209. // fib_info_devhash是存储设备的哈希表,将nexthop指向设备的哈希元素添加到fib_info_devhash链表上。  
  210.  977        hlist_add_head(&nexthop_nh->nh_hash,head);  
  211.  978    }endfor_nexthops(fi)  
  212.  979    spin_unlock_bh(&fib_info_lock);  
  213. //最后这里返回已经添加到相应管理链表上的fib_info的结构体信息fi。  
  214.  980    returnfi;  
  215. 992 }  
773 struct fib_info*fib_create_info(structfib_config *cfg)
 774 {
 775    interr;
 776    structfib_info *fi = NULL;
 777    structfib_info *ofi;
 778    int nhs= 1;
 779    structnet *net = cfg->fc_nlinfo.nl_net;
//用户空间传递进来的tc_type值是2,所以这里的检查类型的有效性通过。
 781    if(cfg->fc_type > RTN_MAX)
 782        gotoerr_inval;
//本地地址LOCAL的值是RT_SCOPE_HOST,254,用户传递进来的fc_scope等于254(图13.3.1),这里检查也通过。
 784    /* Fastcheck to catch the most weird cases */
 785    if(fib_props[cfg->fc_type].scope > cfg->fc_scope)
 786        gotoerr_inval;
796    err =-ENOBUFS;
// 对于由于前表项为空,所以统计路由信息技术变量fib_info_cnt的值等于0,索引路由信息的哈希数组大小变量//fib_info_hash_size值等于0.
 797    if(fib_info_cnt >= fib_info_hash_size) {
 798        unsignedint new_size = fib_info_hash_size << 1;
 799        structhlist_head *new_info_hash;
 800        structhlist_head *new_laddrhash;
 801        unsignedint bytes;
//由于fib_info_hash_size确实为0,所以这里的new_size将被赋值成16。
 803        if(!new_size)
 804            new_size= 16;
 805        bytes= new_size * sizeof(struct hlist_head *);
//为哈希信息和哈希地址存储分配内存,如果小于一个页使用kzalloc,大于一个页使用__get_free_pages。
 806        new_info_hash= fib_info_hash_alloc(bytes);
 807        new_laddrhash= fib_info_hash_alloc(bytes);
//分配失败的处理,如果没有失败,调用fib_info_hash_move进行处理。
 808        if (!new_info_hash || !new_laddrhash){
 809            fib_info_hash_free(new_info_hash,bytes);
 810            fib_info_hash_free(new_laddrhash,bytes);
 811        }else
//根据先前申请到的内存,设置fib_info_hash和fib_info_laddrhash这两个fib_semantics.c文件内的全局变量。这两变量用于链接所有的fib_info结构体。
 812            fib_info_hash_move(new_info_hash,new_laddrhash, new_size);
 813
 814        if(!fib_info_hash_size)
 815            gotofailure;
 816    }
//申请路由信息空间,注意零长数组指向了下一跳fib_nh(nh—next hop)。
818    fi =kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
 819    if (fi== NULL)
 820        gotofailure;
/*用户空间没有配置metric,metirc用于配置下一跳的个数,不指定的情况下赋值如下。
*const u32 dst_default_metrics[RTAX_MAX+ 1]= {
*           [RTAX_MAX]= 0xdeadbeef,
*};
*/
 821    if(cfg->fc_mx) {
 822        fi->fib_metrics= kzalloc(sizeof(u32) * RTAX_MAX, GFP_KERNEL);
 823        if(!fi->fib_metrics)
 824            gotofailure;
 825    } else
 826        fi->fib_metrics= (u32 *) dst_default_metrics;
//fib_info_cnt用于计数有意义的fib_info 个数。
 827    fib_info_cnt++;
//下面的赋值,见图12.3.2,fib_info结构体赋值,值来源于用户配置。
 829    fi->fib_net= hold_net(net);
 830    fi->fib_protocol= cfg->fc_protocol;
 831    fi->fib_scope= cfg->fc_scope;
 832    fi->fib_flags= cfg->fc_flags;
 833    fi->fib_priority= cfg->fc_priority;
 834    fi->fib_prefsrc= cfg->fc_prefsrc;
 835    fi->fib_type= cfg->fc_type;
//初始化下一跳的成员,对于没有配置等价路由的,这个循环只会执行一次,对于等价路由,有几个等价的路由项就会执行几
//次。将下一跳的nh_parent指向fib_info字段。分配一个percpu变量,这会为每个核创建一个对应的rtable,rtable是路由缓//存部分的,其将加速路由项的查找,路由缓存参考11.5节。
 837    fi->fib_nhs= nhs;
 838    change_nexthops(fi){
 839        nexthop_nh->nh_parent= fi;
 840        nexthop_nh->nh_pcpu_rth_output= alloc_percpu(struct rtable __rcu *);
 841        if(!nexthop_nh->nh_pcpu_rth_output)
 842            gotofailure;
 843    }endfor_nexthops(fi)
//用户空间没有传递metric,跳过。
 845    if(cfg->fc_mx) {
 846        structnlattr *nla;
 847        intremaining;
 848
 849        nla_for_each_attr(nla,cfg->fc_mx, cfg->fc_mx_len, remaining) {
 850            inttype = nla_type(nla);
 851
 852            if(type) {
 853                u32 val;
 854
 855                if (type > RTAX_MAX)
 856                    goto err_inval;
 857                val = nla_get_u32(nla);
 858                if (type == RTAX_ADVMSS&& val > 65535 - 40)
 859                    val = 65535 - 40;
 860                if (type == RTAX_MTU&& val > 65535 - 15)
 861                    val = 65535 - 15;
 862                fi->fib_metrics[type - 1] =val;
863            }
 864        }
 865    }
//用户空间也没有配置等价路由,执行else语句。
 867    if(cfg->fc_mp) {
 868#ifdefCONFIG_IP_ROUTE_MULTIPATH
 869        err= fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);
 870        if(err != 0)
 871            gotofailure;
 872        if(cfg->fc_oif && fi->fib_nh->nh_oif !=cfg->fc_oif)
 873            gotoerr_inval;
 874        if(cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)
 875            gotoerr_inval;
 876#ifdef CONFIG_IP_ROUTE_CLASSID
 877        if(cfg->fc_flow && fi->fib_nh->nh_tclassid !=cfg->fc_flow)
 878            gotoerr_inval;
 879#endif
 880#else
 881        gotoerr_inval;
 882#endif
 883     } else{
//对下一跳的赋值,见图12.3.2。赋值的来源同样是用户空间。
 884        structfib_nh *nh = fi->fib_nh;
 885
 886        nh->nh_oif= cfg->fc_oif;
 887        nh->nh_gw= cfg->fc_gw;
 888        nh->nh_flags= cfg->fc_flags;
 889#ifdef CONFIG_IP_ROUTE_CLASSID
 890        nh->nh_tclassid= cfg->fc_flow;
 891        if(nh->nh_tclassid)
 892            fi->fib_net->ipv4.fib_num_tclassid_users++;
 893#endif
 894#ifdefCONFIG_IP_ROUTE_MULTIPATH
 895        nh->nh_weight= 1;
 896#endif
 897    }
//fc_type类型是2,即单播,该类型的error成员值是0,执行else语句。else语句判断type字段是否合法。
 899    if(fib_props[cfg->fc_type].error) {
 900        if(cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)
 901            gotoerr_inval;
 902        gotolink_it;
 903    } else{
 904        switch(cfg->fc_type) {
 905        caseRTN_UNICAST:
 906        case RTN_LOCAL:
 907        caseRTN_BROADCAST:
 908        case RTN_ANYCAST:
 909        caseRTN_MULTICAST:
 910            break;
 911        default:
 912            gotoerr_inval;
 913        }
 914    }
//范围大于RT_SCOPE_HOST(253)就出错了。用于限定路由的范围。
 916    if(cfg->fc_scope > RT_SCOPE_HOST)
 917        gotoerr_inval;
//fc_scope的值等于254,执行if语句里的内容,当使用route命令配置一个(RT_SCOPE_LINK)范围地址,执行else语句
 919    if(cfg->fc_scope == RT_SCOPE_HOST) {
//fi是在818行创建的指向fib_info的结构体,该结构体的部分程员在821~843被赋值,这里是处理其nh(next hop)字段。
 920        structfib_nh *nh = fi->fib_nh;
 921
 922        /*Local address is added. */
 923        if(nhs != 1 || nh->nh_gw)
 924            gotoerr_inval;
//目的地址是本机,所以scope赋值成RT_SCOPE_NOWHERE,该scope范围不会向外发送数据包。
 925        nh->nh_scope= RT_SCOPE_NOWHERE;
//根据索引号获得net_device结构体,net_device结构体用于标记网卡,其实就是“eth0”结构体,该网卡用来发送数据。
 926        nh->nh_dev= dev_get_by_index(net, fi->fib_nh->nh_oif);
 927        err= -ENODEV;
 928        if(nh->nh_dev == NULL)
 929            gotofailure;
 930    } else{
 931        change_nexthops(fi){
//fib_check_nh检查下一跳语义的正确性,
 932            err= fib_check_nh(cfg, fi, nexthop_nh);
 933            if(err != 0)
 934                goto failure;
 935        }endfor_nexthops(fi)
 936    }
// prefsrc是dd270c0a,并且检查也通过。
 938     if(fi->fib_prefsrc) {
 939        if(cfg->fc_type != RTN_LOCAL || !cfg->fc_dst ||
 940            fi->fib_prefsrc!= cfg->fc_dst)
 941            if(inet_addr_type(net, fi->fib_prefsrc) != RTN_LOCAL)
 942                goto err_inval;
 943    }
//获得对应接口的源地址信息,将这些信息存放在下一跳nexthop_nh中。
 945    change_nexthops(fi){
 946        fib_info_update_nh_saddr(net,nexthop_nh);
 947    }endfor_nexthops(fi)
//在fib_info_hash所标示的哈希表中,见图12.3.2左上角,紫色部分;看看要插入的路由项是否已经存在了,如果存在,则//955行直接返回,由于用户空间的配置路由项原本内核路由表中没有,所以接着958行继续。
 949link_it:
 950    ofi = fib_find_info(fi);
 951    if(ofi) {
 952        fi->fib_dead= 1;
953        free_fib_info(fi);
 954        ofi->fib_treeref++;
 955        returnofi;
 956    }
//路由树引用计数++
 958    fi->fib_treeref++;
 959    atomic_inc(&fi->fib_clntref);
//获得保护该路由信息记录项的锁。这个fib_info_hash表是局部全局的。以安全将上面创建的fi添加到fib_info_hash链表上。
 960    spin_lock_bh(&fib_info_lock);
//将这个新的fib_info,添加到管理这个结构的全局哈希表上。
 961    hlist_add_head(&fi->fib_hash,
 962               &fib_info_hash[fib_info_hashfn(fi)]);
969    change_nexthops(fi){
 970        structhlist_head *head;
 971        unsignedint hash;
 972
 973        if(!nexthop_nh->nh_dev)
 974            continue;
//ifindex的值等于2,对应于eth0。
 975        hash= fib_devindex_hashfn(nexthop_nh->nh_dev->ifindex);
 976        head= &fib_info_devhash[hash];
// fib_info_devhash是存储设备的哈希表,将nexthop指向设备的哈希元素添加到fib_info_devhash链表上。
 977        hlist_add_head(&nexthop_nh->nh_hash,head);
 978    }endfor_nexthops(fi)
 979    spin_unlock_bh(&fib_info_lock);
//最后这里返回已经添加到相应管理链表上的fib_info的结构体信息fi。
 980    returnfi;
992 }


图12.3.2 ifconfig使用到的数据结构

fib_find_info参数值是前面创建的fib_info结构体,这个函数就是在管理fib_info的哈希链表fib_info_hash查找先前创建的fib_info信息是否已经存在这张链表了,如果存在,那么说明fib_info没有必要在创建了,直接重复利用就好了。

  1. 298 static struct fib_info *fib_find_info(const struct fib_info *nfi)  
  2.  299 {  
  3.  300     struct hlist_head *head;  
  4.  301     struct fib_info *fi;  
  5.  302     unsigned int hash;  
  6. //计算fib_info的哈希索引值,该哈希值依赖于fib_protocol、fib_scope、fib_prefsrc、fib_priority以及设备index。  
  7.  304     hash = fib_info_hashfn(nfi);  
  8. //直接得到传递进来了fib_info所对应的哈希表头,如果在这个表中能够找到就直接返回,如果找不到返回NULL,返回去以后  
  9. //的函数查看返回值,是NULL的话,说明这项并不存在,就会将这个fib_info添加到fib_info_hash链表上。  
  10.  305     head = &fib_info_hash[hash];  
  11. //遍历哈希表,依次对比哈希表的每一个成员的下列字段和传递进来要找的fib_info是否相同。   
  12.  307     hlist_for_each_entry(fi, head, fib_hash) {  
  13.  308         if (!net_eq(fi->fib_net, nfi->fib_net))  
  14.  309             continue;  
  15.  310         if (fi->fib_nhs != nfi->fib_nhs)  
  16.  311             continue;  
  17.  312         if (nfi->fib_protocol == fi->fib_protocol &&  
  18.  313             nfi->fib_scope == fi->fib_scope &&  
  19.  314             nfi->fib_prefsrc == fi->fib_prefsrc &&  
  20.  315             nfi->fib_priority == fi->fib_priority &&  
  21.  316             nfi->fib_type == fi->fib_type &&  
  22.  317             memcmp(nfi->fib_metrics, fi->fib_metrics,  
  23.  318                sizeof(u32) * RTAX_MAX) == 0 &&  
  24.  319             ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 &&  
  25.  320             (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))  
  26.  321             return fi;  
  27.  322     }  
  28.  323   
  29.  324     return NULL;  
  30.  325 }  
298 static struct fib_info *fib_find_info(const struct fib_info *nfi)
 299 {
 300     struct hlist_head *head;
 301     struct fib_info *fi;
 302     unsigned int hash;
//计算fib_info的哈希索引值,该哈希值依赖于fib_protocol、fib_scope、fib_prefsrc、fib_priority以及设备index。
 304     hash = fib_info_hashfn(nfi);
//直接得到传递进来了fib_info所对应的哈希表头,如果在这个表中能够找到就直接返回,如果找不到返回NULL,返回去以后
//的函数查看返回值,是NULL的话,说明这项并不存在,就会将这个fib_info添加到fib_info_hash链表上。
 305     head = &fib_info_hash[hash];
//遍历哈希表,依次对比哈希表的每一个成员的下列字段和传递进来要找的fib_info是否相同。 
 307     hlist_for_each_entry(fi, head, fib_hash) {
 308         if (!net_eq(fi->fib_net, nfi->fib_net))
 309             continue;
 310         if (fi->fib_nhs != nfi->fib_nhs)
 311             continue;
 312         if (nfi->fib_protocol == fi->fib_protocol &&
 313             nfi->fib_scope == fi->fib_scope &&
 314             nfi->fib_prefsrc == fi->fib_prefsrc &&
 315             nfi->fib_priority == fi->fib_priority &&
 316             nfi->fib_type == fi->fib_type &&
 317             memcmp(nfi->fib_metrics, fi->fib_metrics,
 318                sizeof(u32) * RTAX_MAX) == 0 &&
 319             ((nfi->fib_flags ^ fi->fib_flags) & ~RTNH_F_DEAD) == 0 &&
 320             (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0))
 321             return fi;
 322     }
 323 
 324     return NULL;
 325 }

继续回到fib_table_insert 的1204行fib_find_node,该函数用于查找叶子节点。页叶子节点和tnode都是用来表示字典树节点,它们处在这棵树的位置不一样,两者并不完全相同,但是前两个成员是一样的,两者之间的互相转换的技巧前面已经遇到过了。这个函数的参数,key就是要添加的192.168.0.10 IP地址,第一个参数见图12.3.2路由表拓扑的底部。这个函数查找的过程是这样的:首先遍历tnode,然后是遍历leaf,这个过程还是很容易理解的。

  1. 955 static struct leaf *  
  2.  956fib_find_node(struct trie *t, u32 key)  
  3.  957 {  
  4.  958    int pos;  
  5.  959    struct tnode *tn;  
  6.  960    struct rt_trie_node *n;  
  7.  961  
  8.  962    pos = 0;  
  9. //将传递进来的trie节点转换成tnode类型。  
  10.  963    n = rcu_dereference_rtnl(t->trie);  
  11. /************************************************************************************************* 
  12. //在插入唯一一项10.12.39.0时,这一项就是一个leaf。 
  13. //在添加192.168.0.10时,由于n指向10.12.39.0的rt_trie_node类结构体,并不是NULL。 
  14. // n->parent:00000001, n->key:0a0c2700。 
  15. //而在添加192.168.0.10时, 
  16. // n->parent:00000000, n->key:0a0c2700 
  17. n->parent用于存放其父节点的地址,这个父节点是tnode,如果不存在则是NULL(0000000*)。该成员的最低bit用于标识这个节点是tnode还是leaf。如果是tnode则最低一个bit是0,如果是leaf则最低一个bit是1。 
  18. //#define T_TNODE 0 
  19. //#define T_LEAF  1 
  20. 在插入192.168.0.10时,由于10.12.39.0是个leaf,所以这个循环被跳过了。而插入192.168.0.100时,由于有tnode存在,所以这里while循环就不会被跳过了,这个tnode是图12.3.2的上面两行产生了,至于pos为25的则是192.168.0.100插入之后才会存在的tnode。 
  21. ***************************************************************************************************/  
  22.  965    while (n != NULL && NODE_TYPE(n) == T_TNODE) {  
  23.  966        tn = (struct tnode *) n;  
  24.  967  
  25.  968        check_tnode(tn);  
  26. //970~977非常经典的几行代码,就这么短短的几行代码能够处理更正情况下与要查找的key最接近的tnode。  
  27.  970        if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {  
  28.  971            pos = tn->pos + tn->bits;  
  29.  972            n = tnode_get_child_rcu(tn,  
  30.  973                         tkey_extract_bits(key,  
  31.  974                                   tn->pos,  
  32.  975                                  tn->bits));  
  33.  976        } else  
  34.  977            break;  
  35.  978    }  
  36.   
  37. //如果这个if语句满足,说明我们要插入的项已经存在这个路由表了,这是一次重复插入操作。  
  38.  981    if (n != NULL && IS_LEAF(n) && tkey_equals(key,n->key))  
  39.  982        return (struct leaf *)n;  
  40.  983  
  41.  984    return NULL;  
  42.  985 }  
955 static struct leaf *
 956fib_find_node(struct trie *t, u32 key)
 957 {
 958    int pos;
 959    struct tnode *tn;
 960    struct rt_trie_node *n;
 961
 962    pos = 0;
//将传递进来的trie节点转换成tnode类型。
 963    n = rcu_dereference_rtnl(t->trie);
/*************************************************************************************************
//在插入唯一一项10.12.39.0时,这一项就是一个leaf。
//在添加192.168.0.10时,由于n指向10.12.39.0的rt_trie_node类结构体,并不是NULL。
// n->parent:00000001, n->key:0a0c2700。
//而在添加192.168.0.10时,
// n->parent:00000000, n->key:0a0c2700
n->parent用于存放其父节点的地址,这个父节点是tnode,如果不存在则是NULL(0000000*)。该成员的最低bit用于标识这个节点是tnode还是leaf。如果是tnode则最低一个bit是0,如果是leaf则最低一个bit是1。
//#define T_TNODE 0
//#define T_LEAF  1
在插入192.168.0.10时,由于10.12.39.0是个leaf,所以这个循环被跳过了。而插入192.168.0.100时,由于有tnode存在,所以这里while循环就不会被跳过了,这个tnode是图12.3.2的上面两行产生了,至于pos为25的则是192.168.0.100插入之后才会存在的tnode。
***************************************************************************************************/
 965    while (n != NULL && NODE_TYPE(n) == T_TNODE) {
 966        tn = (struct tnode *) n;
 967
 968        check_tnode(tn);
//970~977非常经典的几行代码,就这么短短的几行代码能够处理更正情况下与要查找的key最接近的tnode。
 970        if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
 971            pos = tn->pos + tn->bits;
 972            n = tnode_get_child_rcu(tn,
 973                         tkey_extract_bits(key,
 974                                   tn->pos,
 975                                  tn->bits));
 976        } else
 977            break;
 978    }

//如果这个if语句满足,说明我们要插入的项已经存在这个路由表了,这是一次重复插入操作。
 981    if (n != NULL && IS_LEAF(n) && tkey_equals(key,n->key))
 982        return (struct leaf *)n;
 983
 984    return NULL;
 985 }

为了说明遍历tnode的过程,看图12.3.2,如果根据该图张图不能想象出trie的拓扑结构,参考图12.2.4,它们是一样的拓扑结构。在这个基础上,这里在次插入192.168.0.11这个项。

图12.3.2 路由项查找实例

Key值就是192.168.0.11,t参数指向10.12.39.0这个tnode。

遍历过程如下:

         1、遍历第一个tnode,if语句因tn->pos-pos的等于零成立。遍历trie树第一个tnode。

tkey_extract_bits提取key(192.168.0.11)的pos位置开始的bits位的值,这里就是1。tnode_get_child_rcu转变成tnode_get_child_rcu(tn,1),这个函数获得tnode的child成员,这里就是child[1],

         2、再一次遍历tnode的第二个节点。这个tn->pos是25,tn->bits是1。971行的pos将变成26,这就意味着从第26个bit开始比较。然后比较的bits数是1。这时得到是child[0],参考图12.2.4,该节点挂载的是192.168.0.10,。

         3、该函数返回以后会,外部函数会将这个child指向的leaf节点,变成指向一个tnode节点,tnode的孩子节点分别指向192.168.0.10和192.168.0.11。

1312行是核心的插入操作,为了使这一过程更具代表性,这里结合图12.2.4以插入192.168.0.100为例进行源码分析,在插入192.168.0.100之前的状态见图12.2.3,只存在key等于10.12.39.0的tnode,和child[0]指向key等于10.12.39.0的leaf和child[1]指向key值等于192.168.0.10的leaf节点。在插入192.168.0.100时,fib_inset_node d的参数意义如下:

1、t指向key等于10.12.39.0的tnode;

2、key是需要插入的目的IP 192.168.0.100

3、plen是前缀长度,这里是32。

  1. 1023 static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)  
  2. 1024 {  
  3. 1025     int pos, newpos;  
  4. 1026     struct tnode *tp = NULL, *tn = NULL;  
  5. 1027     struct rt_trie_node *n;  
  6. 1028     struct leaf *l;  
  7. 1029     int missbit;  
  8. 1030     struct list_head *fa_head = NULL;  
  9. 1031     struct leaf_info *li;  
  10. 1032     t_key cindex;  
  11. 1033   
  12. 1034     pos = 0;  
  13. //rcu类型变量引用。  
  14. 1035     n = rtnl_dereference(t->trie);  
  15. //由于t->trie是key等于10.12.39.0的tnode,所以这个while的语句判断第一次是满足的。  
  16. 1055     while (n != NULL &&  NODE_TYPE(n) == T_TNODE) {  
  17. //从类型rt_trie_node到tnode强制类型转换,内核常用技巧  
  18. 1056         tn = (struct tnode *) n;  
  19. //tnode验证,tnode != NULL,并且该tnode的pos和bits成员之和需要小于32。  
  20. 1058         check_tnode(tn);  
  21. //下面这段代码和fib_find_node一样。  
  22. 1060         if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {  
  23. 1061             tp = tn; //图12.2.4中的copy来自这行代码。  
  24. 1062             pos = tn->pos + tn->bits; //获得比较的起始位置  
  25. //返回tnode下合适的孩子,这个孩子可能是tnode也可能是leaf,对于这里情况返回192.168.0.10 leaf。  
  26. 1063             n = tnode_get_child(tn,   
  27. 1064                         tkey_extract_bits(key, //根据传递进来的key,查tnode处不同的bits。  
  28. 1065                                   tn->pos,  
  29. 1066                                   tn->bits));  
  30. 1067   
  31. 1068             BUG_ON(n && node_parent(n) != tn);  
  32. 1069         } else  
  33. 1070             break;  
  34. 1071     }  
  35. 1072   
  36. //这里两个条件满足,但是第三个条件不满足,实际上如果第三个条件满足,这就说明该路由项已经在该路由表中了,没有必  
  37. //要再次添加该路由项。  
  38. 1083     if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) {  
  39. 1084         l = (struct leaf *) n;  
  40. 1085         li = leaf_info_new(plen);  
  41. 1086   
  42. 1087         if (!li)  
  43. 1088             return NULL;  
  44. //将leaf串接成一个链表,leaf_info结构体的falh成员完成这一工作。  
  45. 1090         fa_head = &li->falh;   
  46. 1091         insert_leaf_info(&l->list, li);  
  47. 1092         goto done;  
  48. 1093     }  
  49. //如果1083行if语句不满足,说明路由表中不存在这么一项,创建一个新的leaf,该leaf用于插入项的索引。  
  50. 1094     l = leaf_new();  
  51. //申请内存失败处理  
  52. 1096     if (!l)  
  53. 1097         return NULL;  
  54. //这里将192,168.0.100作为key值传递给leaf,并根据前缀长度创建一个记录leaf信息的li(leaf_info结构体)  
  55. 1099     l->key = key;  
  56. 1100     li = leaf_info_new(plen);  
  57. //记录leaf信息的结构体内存申请失败的处理。  
  58. 1102     if (!li) {  
  59. 1103         free_leaf(l);  
  60. 1104         return NULL;  
  61. 1105     }  
  62. //将leaf_info和leaf建立关系,即将leaf_info的falh成员指向leaf的list。  
  63. 1107     fa_head = &li->falh;  
  64. 1108     insert_leaf_info(&l->list, li);  
  65. //上面已经看到n显然不等于NULL。  
  66. 1110     if (t->trie && n == NULL) {  
  67. 1111         /* Case 2: n is NULL, and will just insert a new leaf */  
  68. 1112   
  69. 1113         node_set_parent((struct rt_trie_node *)l, tp);  
  70. 1114   
  71. 1115         cindex = tkey_extract_bits(key, tp->pos, tp->bits);  
  72. 1116         put_child(tp, cindex, (struct rt_trie_node *)l);  
  73. 1117     } else {  
  74. //tp在前面得到赋值(copy),tp->pos+tp->bits将等于1,这就意味着key等于192.168.0.100的插入项从第一个比特开始比较。  
  75. 1124         if (tp)  
  76. 1125             pos = tp->pos+tp->bits;  
  77. 1126         else  
  78. 1127             pos = 0;  
  79. 1128   
  80. 1129         if (n) {  
  81. /*************************************************************************************************************** 
  82. 265 static inline int tkey_mismatch(t_key a, int offset, t_key b) 
  83.  266 { 
  84.  267     t_key diff = a ^ b; 
  85.  268     int i = offset; 
  86.  269  
  87.  270     if (!diff) 
  88.  271         return 0; 
  89.  272     while ((diff << i) >> (KEYLENGTH-1) == 0) 
  90.  273         i++; 
  91.  274     return i; 
  92.  275 } 
  93. ///key:c0a80064,pos:1,n->key:0a0c2700 
  94. 将这三个参数带入上面的函数,KEYLENGTH值是32,可得25,源于192.168.0.100和192.168.0.10二进制不同的起始比特 
  95. **************************************************************************************************************/  
  96. 1130             newpos = tkey_mismatch(key, pos, n->key);  
  97. //根据n->key和newpos创建一个tnode,这对应于图12.2.4中的key等于192.168.0.10的tnode。  
  98. 1131             tn = tnode_new(n->key, newpos, 1);  
  99. 1132         } else {  
  100. //首次需要创建tnode的情况,这是一个特殊情况,一个路由表,只会调用一次这里的函数。  
  101. 1133             newpos = 0;  
  102. 1134             tn = tnode_new(key, newpos, 1); /* First tnode */  
  103. 1135         }  
  104. //tn创建失败的处理  
  105. 1137         if (!tn) {  
  106. 1138             free_leaf_info(li);  
  107. 1139             free_leaf(l);  
  108. 1140             return NULL;  
  109. 1141         }  
  110. //这个函数就是设置这里创建的tn(tree node)的父节点。注意看图12.2.4中的ADD开始字符串,这里的赋值使用就是ADD  
  111. //冒号后字符串,实际上是地址,注意图中它们的赋值关系。  
  112. 1143         node_set_parent((struct rt_trie_node *)tn, tp);  
  113. //从第25个比特比起,它们不同的第一个比特是1,  
  114. 1145         missbit = tkey_extract_bits(key, newpos, 1);  
  115. //在tnode的child[1],插入key值等于192.168.0.100的leaf。见12.3.4小节。  
  116. //在tnode的child[0],插入key值等于192.168.0.10的leaf。  
  117. 1146         put_child(tn, missbit, (struct rt_trie_node *)l);  
  118. 1147         put_child(tn, 1-missbit, n);  
  119. //1061行,如果有copy,那么需要跟新原来tnode的child,其child[1]变成了一个具有两个leaf的tnode。  
  120. 1149         if (tp) {  
  121. //这里找到tnode插入的起始位置。  
  122. 1150             cindex = tkey_extract_bits(key, tp->pos, tp->bits);  
  123. //put_child为插入操作。  
  124. 1151             put_child(tp, cindex, (struct rt_trie_node *)tn);  
  125. 1152         } else {  
  126. 1153             rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);  
  127. 1154             tp = tn;  
  128. 1155         }  
  129. 1156     }  
  130. //设置后的参数合理性检查,温和的使用了warn。  
  131. 1158     if (tp && tp->pos + tp->bits > 32)  
  132. 1159         pr_warn("fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n",  
  133. 1160             tp, tp->pos, tp->bits, key, plen);  
  134. 1161   
  135. 1162     /* Rebalance the trie */  
  136. //提到过好多次的rebalance操作。图12.3.2中黄色的列,bits值在前面的操作都是一个比特,在rebalance中可能会变成多个  
  137. //比特。  
  138. 1164     trie_rebalance(t, tp);  
  139. 1165 done:  
  140. 1166     return fa_head;  
  141. 1167 }  
1023 static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
1024 {
1025     int pos, newpos;
1026     struct tnode *tp = NULL, *tn = NULL;
1027     struct rt_trie_node *n;
1028     struct leaf *l;
1029     int missbit;
1030     struct list_head *fa_head = NULL;
1031     struct leaf_info *li;
1032     t_key cindex;
1033 
1034     pos = 0;
//rcu类型变量引用。
1035     n = rtnl_dereference(t->trie);
//由于t->trie是key等于10.12.39.0的tnode,所以这个while的语句判断第一次是满足的。
1055     while (n != NULL &&  NODE_TYPE(n) == T_TNODE) {
//从类型rt_trie_node到tnode强制类型转换,内核常用技巧
1056         tn = (struct tnode *) n;
//tnode验证,tnode != NULL,并且该tnode的pos和bits成员之和需要小于32。
1058         check_tnode(tn);
//下面这段代码和fib_find_node一样。
1060         if (tkey_sub_equals(tn->key, pos, tn->pos-pos, key)) {
1061             tp = tn; //图12.2.4中的copy来自这行代码。
1062             pos = tn->pos + tn->bits; //获得比较的起始位置
//返回tnode下合适的孩子,这个孩子可能是tnode也可能是leaf,对于这里情况返回192.168.0.10 leaf。
1063             n = tnode_get_child(tn, 
1064                         tkey_extract_bits(key, //根据传递进来的key,查tnode处不同的bits。
1065                                   tn->pos,
1066                                   tn->bits));
1067 
1068             BUG_ON(n && node_parent(n) != tn);
1069         } else
1070             break;
1071     }
1072 
//这里两个条件满足,但是第三个条件不满足,实际上如果第三个条件满足,这就说明该路由项已经在该路由表中了,没有必
//要再次添加该路由项。
1083     if (n != NULL && IS_LEAF(n) && tkey_equals(key, n->key)) {
1084         l = (struct leaf *) n;
1085         li = leaf_info_new(plen);
1086 
1087         if (!li)
1088             return NULL;
//将leaf串接成一个链表,leaf_info结构体的falh成员完成这一工作。
1090         fa_head = &li->falh; 
1091         insert_leaf_info(&l->list, li);
1092         goto done;
1093     }
//如果1083行if语句不满足,说明路由表中不存在这么一项,创建一个新的leaf,该leaf用于插入项的索引。
1094     l = leaf_new();
//申请内存失败处理
1096     if (!l)
1097         return NULL;
//这里将192,168.0.100作为key值传递给leaf,并根据前缀长度创建一个记录leaf信息的li(leaf_info结构体)
1099     l->key = key;
1100     li = leaf_info_new(plen);
//记录leaf信息的结构体内存申请失败的处理。
1102     if (!li) {
1103         free_leaf(l);
1104         return NULL;
1105     }
//将leaf_info和leaf建立关系,即将leaf_info的falh成员指向leaf的list。
1107     fa_head = &li->falh;
1108     insert_leaf_info(&l->list, li);
//上面已经看到n显然不等于NULL。
1110     if (t->trie && n == NULL) {
1111         /* Case 2: n is NULL, and will just insert a new leaf */
1112 
1113         node_set_parent((struct rt_trie_node *)l, tp);
1114 
1115         cindex = tkey_extract_bits(key, tp->pos, tp->bits);
1116         put_child(tp, cindex, (struct rt_trie_node *)l);
1117     } else {
//tp在前面得到赋值(copy),tp->pos+tp->bits将等于1,这就意味着key等于192.168.0.100的插入项从第一个比特开始比较。
1124         if (tp)
1125             pos = tp->pos+tp->bits;
1126         else
1127             pos = 0;
1128 
1129         if (n) {
/***************************************************************************************************************
265 static inline int tkey_mismatch(t_key a, int offset, t_key b)
 266 {
 267     t_key diff = a ^ b;
 268     int i = offset;
 269 
 270     if (!diff)
 271         return 0;
 272     while ((diff << i) >> (KEYLENGTH-1) == 0)
 273         i++;
 274     return i;
 275 }
///key:c0a80064,pos:1,n->key:0a0c2700
将这三个参数带入上面的函数,KEYLENGTH值是32,可得25,源于192.168.0.100和192.168.0.10二进制不同的起始比特
**************************************************************************************************************/
1130             newpos = tkey_mismatch(key, pos, n->key);
//根据n->key和newpos创建一个tnode,这对应于图12.2.4中的key等于192.168.0.10的tnode。
1131             tn = tnode_new(n->key, newpos, 1);
1132         } else {
//首次需要创建tnode的情况,这是一个特殊情况,一个路由表,只会调用一次这里的函数。
1133             newpos = 0;
1134             tn = tnode_new(key, newpos, 1); /* First tnode */
1135         }
//tn创建失败的处理
1137         if (!tn) {
1138             free_leaf_info(li);
1139             free_leaf(l);
1140             return NULL;
1141         }
//这个函数就是设置这里创建的tn(tree node)的父节点。注意看图12.2.4中的ADD开始字符串,这里的赋值使用就是ADD
//冒号后字符串,实际上是地址,注意图中它们的赋值关系。
1143         node_set_parent((struct rt_trie_node *)tn, tp);
//从第25个比特比起,它们不同的第一个比特是1,
1145         missbit = tkey_extract_bits(key, newpos, 1);
//在tnode的child[1],插入key值等于192.168.0.100的leaf。见12.3.4小节。
//在tnode的child[0],插入key值等于192.168.0.10的leaf。
1146         put_child(tn, missbit, (struct rt_trie_node *)l);
1147         put_child(tn, 1-missbit, n);
//1061行,如果有copy,那么需要跟新原来tnode的child,其child[1]变成了一个具有两个leaf的tnode。
1149         if (tp) {
//这里找到tnode插入的起始位置。
1150             cindex = tkey_extract_bits(key, tp->pos, tp->bits);
//put_child为插入操作。
1151             put_child(tp, cindex, (struct rt_trie_node *)tn);
1152         } else {
1153             rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
1154             tp = tn;
1155         }
1156     }
//设置后的参数合理性检查,温和的使用了warn。
1158     if (tp && tp->pos + tp->bits > 32)
1159         pr_warn("fib_trie tp=%p pos=%d, bits=%d, key=%0x plen=%d\n",
1160             tp, tp->pos, tp->bits, key, plen);
1161 
1162     /* Rebalance the trie */
//提到过好多次的rebalance操作。图12.3.2中黄色的列,bits值在前面的操作都是一个比特,在rebalance中可能会变成多个
//比特。
1164     trie_rebalance(t, tp);
1165 done:
1166     return fa_head;
1167 }

后面是发送一个消息,网卡接收到消息后,执行函数fib_netdev_event()函数,这个函数会再次调用fib_inetaddr_event,这个函数这次会按顺序添加子网号全1、全0以及主机号为全1(10.12.39.255广播地址,10.12.39.221也是要接收发往这个地址的数据的)。其过程和上面的类似,插入的节点hash值也是相同的(图中表ifconfig的数组索引)。

图12.3.3由ifconfig引发的前三次路由表更新过程

12.3.4 put_child

12.3.3小节可以知道核心的插入函数时put_child,所以这里单独一个小节来看一下插入的过程是如何完成的。这节的情况还是继续12.3.3小节。但是只打算分析1164行,其它的读者自行完成。

  1. 475 static inline int tnode_full(const struct tnode *tn, const struct rt_trie_node *n)  
  2.  476 {  
  3. //对于NULL和leaf情况,返回值一定是0,用于更新full_children计数器。  
  4.  477     if (n == NULL || IS_LEAF(n))  
  5.  478         return 0;  
  6. //对于tnode情况的判断方法。  
  7. 480     return ((struct tnode *) n)->pos == tn->pos + tn->bits;  
  8.  481 }  
  9. 483 static inline void put_child(struct tnode *tn, int i,  
  10.  484                  struct rt_trie_node *n)  
  11.  485 {  
  12.  486     tnode_put_child_reorg(tn, i, n, -1);  
  13.  487 }  
  14.  488   
  15.  489  /* 
  16.  490   * Add a child at position i overwriting the old value. 
  17.  491   * Update the value of full_children and empty_children. 
  18.  492   */  
  19.  493   
  20.  494 static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node *n,  
  21.  495                   int wasfull)  
  22.  496 {  
  23.  497     struct rt_trie_node *chi = rtnl_dereference(tn->child[i]);  
  24.  498     int isfull;  
  25.  499   
  26. //传递进来的n非空,chi这是还没有获得有效值,所以会执行505行的else语句。对于插入操作,基本都是对  
  27. //empty_children减一操作  
  28.  502     /* update emptyChildren */  
  29.  503     if (n == NULL && chi != NULL)  
  30.  504         tn->empty_children++;  
  31.  505     else if (n != NULL && chi == NULL)  
  32.  506         tn->empty_children--;  
  33.  507   
  34. //对于添加路由项,传递进来的值都是-1  
  35.  509     if (wasfull == -1)  
  36.  510         wasfull = tnode_full(tn, chi);  
  37.  511   
  38.  512     isfull = tnode_full(tn, n);  
  39.  513     if (wasfull && !isfull)  
  40.  514         tn->full_children--;  
  41.  515     else if (!wasfull && isfull)  
  42.  516         tn->full_children++;  
  43.  517   
  44. /******************************************************************************************************* 
  45. 最低一个bit设置node的类型,要么tnode,要么leaf,高位存放地址(ADD) 
  46. 208 static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr) 
  47.  209 { 
  48.  210     smp_wmb(); 
  49.  211     node->parent = (unsigned long)ptr | NODE_TYPE(node); 
  50.  212 } 
  51. ********************************************************************************************************/  
  52.  518     if (n)  
  53.  519         node_set_parent(n, tn);  
  54.  520   
  55. //添加的操作实际上非常的简单,就是一个rcu变量的赋值,将tnode的child成员赋值成适当的值就好了。  
  56.  521     rcu_assign_pointer(tn->child[i], n);  
  57. 522 }  
475 static inline int tnode_full(const struct tnode *tn, const struct rt_trie_node *n)
 476 {
//对于NULL和leaf情况,返回值一定是0,用于更新full_children计数器。
 477     if (n == NULL || IS_LEAF(n))
 478         return 0;
//对于tnode情况的判断方法。
480     return ((struct tnode *) n)->pos == tn->pos + tn->bits;
 481 }
483 static inline void put_child(struct tnode *tn, int i,
 484                  struct rt_trie_node *n)
 485 {
 486     tnode_put_child_reorg(tn, i, n, -1);
 487 }
 488 
 489  /*
 490   * Add a child at position i overwriting the old value.
 491   * Update the value of full_children and empty_children.
 492   */
 493 
 494 static void tnode_put_child_reorg(struct tnode *tn, int i, struct rt_trie_node *n,
 495                   int wasfull)
 496 {
 497     struct rt_trie_node *chi = rtnl_dereference(tn->child[i]);
 498     int isfull;
 499 
//传递进来的n非空,chi这是还没有获得有效值,所以会执行505行的else语句。对于插入操作,基本都是对
//empty_children减一操作
 502     /* update emptyChildren */
 503     if (n == NULL && chi != NULL)
 504         tn->empty_children++;
 505     else if (n != NULL && chi == NULL)
 506         tn->empty_children--;
 507 
//对于添加路由项,传递进来的值都是-1
 509     if (wasfull == -1)
 510         wasfull = tnode_full(tn, chi);
 511 
 512     isfull = tnode_full(tn, n);
 513     if (wasfull && !isfull)
 514         tn->full_children--;
 515     else if (!wasfull && isfull)
 516         tn->full_children++;
 517 
/*******************************************************************************************************
最低一个bit设置node的类型,要么tnode,要么leaf,高位存放地址(ADD)
208 static inline void node_set_parent(struct rt_trie_node *node, struct tnode *ptr)
 209 {
 210     smp_wmb();
 211     node->parent = (unsigned long)ptr | NODE_TYPE(node);
 212 }
********************************************************************************************************/
 518     if (n)
 519         node_set_parent(n, tn);
 520 
//添加的操作实际上非常的简单,就是一个rcu变量的赋值,将tnode的child成员赋值成适当的值就好了。
 521     rcu_assign_pointer(tn->child[i], n);
522 }

483参数:

tn是key值为0a0c2700的tnode,

i值是1

l是key等于192.168.0.100的leaf,(先前已经创建并初始化好了)。

该函数调用了,486 tnode_put_child_reorg,完成添加工作。

479行原来tnode的child[1]是指向key值等于192.168.0.10的leaf。

         502~506跟新孩子计数器。

         509行,判断chi是否“满”,由于这里的chi是键值为192.168.0.10的leaf,所以其返回值是0,

         512行,判断插入leaf,192.168.0.100是否满,显然返回值是0。

12.4 route添加路由项

路由这章的介绍和前面的章节分开,从工具说起,在Linux下经常使用route工具配置路由项和网关。下面的这段代码来自配置网关的用户空间程序的代码片段,从这个函数可以看出,创建一个套接字,并调用ioctl方法完成网关的设置。

  1. memcpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));   
  2.     (void)ioctl(sockfd, SIOGIFINDEX, &ifr);   
  3.     v4_rt.rtmsg_ifindex = ifr.ifr_ifindex;   
  4.       
  5.     memcpy(&v4_rt.rtmsg_gateway, gateway, sizeof(struct in_addr));   
  6.     /*添加路由*/  
  7.     if (ioctl(sockfd, SIOCADDRT, &v4_rt) < 0)   
  8.     {  
  9.         SAFE_CLOSE(sockfd);   
  10.         BASEFUN_DBG(RT_ERROR, "add route ioctl error and errno=%d\n", errno);   
  11.         return -1;   
  12.     }     
  13.     SAFE_CLOSE(sockfd);  
memcpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name)); 
	(void)ioctl(sockfd, SIOGIFINDEX, &ifr); 
	v4_rt.rtmsg_ifindex = ifr.ifr_ifindex; 
	
	memcpy(&v4_rt.rtmsg_gateway, gateway, sizeof(struct in_addr)); 
	/*添加路由*/
	if (ioctl(sockfd, SIOCADDRT, &v4_rt) < 0) 
	{
		SAFE_CLOSE(sockfd); 
		BASEFUN_DBG(RT_ERROR, "add route ioctl error and errno=%d\n", errno); 
		return -1; 
	}	
	SAFE_CLOSE(sockfd);

这个ioctl函数对应到内核下最终的路由配置函数是ip_rt_ioctl,该函数位于net/ipv4/fib_front.c文件。Linux源码中路由的表示结构体是fib*,FIB(forward information base),所以后面会出现好多以fib开始的程序片段。

  1. 480 int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)  
  2.  481 {  
  3.  482     struct fib_config cfg;  
  4.  483     struct rtentry rt;  
  5.  484     int err;  
  6.  485   
  7.  486     switch (cmd) {  
  8.  487     case SIOCADDRT:     /* Add a route */  
  9.  488     case SIOCDELRT:     /* Delete a route */  
  10.  489         if (!ns_capable(net->user_ns, CAP_NET_ADMIN))  
  11.  490             return -EPERM;  
  12.  491   
  13.  492         if (copy_from_user(&rt, arg, sizeof(rt)))  
  14.  493             return -EFAULT;  
  15.  494   
  16.  495         rtnl_lock();  
  17.  496         err = rtentry_to_fib_config(net, cmd, &rt, &cfg);  
  18.  497         if (err == 0) {  
  19.  498             struct fib_table *tb;  
  20.  499   
  21.  500             if (cmd == SIOCDELRT) {  
  22.  501                 tb = fib_get_table(net, cfg.fc_table);  
  23.  502                 if (tb)  
  24.  503                     err = fib_table_delete(tb, &cfg);  
  25.  504                 else  
  26.  505                     err = -ESRCH;  
  27.  506             } else {  
  28.  507                 tb = fib_new_table(net, cfg.fc_table);  
  29.  508                 if (tb)  
  30.  509                     err = fib_table_insert(tb, &cfg);  
  31.  510                 else  
  32.  511                     err = -ENOBUFS;  
  33.  512             }  
  34.  513   
  35.  514             /* allocated by rtentry_to_fib_config() */  
  36.  515             kfree(cfg.fc_mx);  
  37.  516         }  
  38.  517         rtnl_unlock();  
  39.  518         return err;  
  40.  519     }  
  41.  520     return -EINVAL;  
  42.  521 }  
480 int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
 481 {
 482     struct fib_config cfg;
 483     struct rtentry rt;
 484     int err;
 485 
 486     switch (cmd) {
 487     case SIOCADDRT:     /* Add a route */
 488     case SIOCDELRT:     /* Delete a route */
 489         if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
 490             return -EPERM;
 491 
 492         if (copy_from_user(&rt, arg, sizeof(rt)))
 493             return -EFAULT;
 494 
 495         rtnl_lock();
 496         err = rtentry_to_fib_config(net, cmd, &rt, &cfg);
 497         if (err == 0) {
 498             struct fib_table *tb;
 499 
 500             if (cmd == SIOCDELRT) {
 501                 tb = fib_get_table(net, cfg.fc_table);
 502                 if (tb)
 503                     err = fib_table_delete(tb, &cfg);
 504                 else
 505                     err = -ESRCH;
 506             } else {
 507                 tb = fib_new_table(net, cfg.fc_table);
 508                 if (tb)
 509                     err = fib_table_insert(tb, &cfg);
 510                 else
 511                     err = -ENOBUFS;
 512             }
 513 
 514             /* allocated by rtentry_to_fib_config() */
 515             kfree(cfg.fc_mx);
 516         }
 517         rtnl_unlock();
 518         return err;
 519     }
 520     return -EINVAL;
 521 }

根据cmd参数SIOCADDRT可以跟踪到switch内执行的程序代码段,496行rtentry_to_fib_config用于将用户空间的路由配置信息转变到内核记录配置信息的结构体cfg中,在图12.4.2中等号右边的信息就是来自用户空间的配置信息。

图12.4.1是使用route命令添加了一个路由项、一个网关和导出路由项,内核下保存配置信息的成员cfg是struct fib_config类型的一个结构体,该结构体的成员值在图12.4.1中列了出来。

include/net/ip_fib.h

  1. 26 struct fib_config {  
  2.  27     u8          fc_dst_len;  
  3.  28     u8          fc_tos;  
  4.  29     u8          fc_protocol;  
  5.  30     u8          fc_scope;  
  6.  31     u8          fc_type;  
  7.  32     /* 3 bytes unused */  
  8.  33     u32         fc_table;  
  9.  34     __be32          fc_dst;  
  10.  35     __be32          fc_gw;  
  11.  36     int         fc_oif;  
  12.  37     u32         fc_flags;  
  13.  38     u32         fc_priority;  
  14.  39     __be32          fc_prefsrc;  
  15.  40     struct nlattr       *fc_mx;  
  16.  41     struct rtnexthop    *fc_mp;  
  17.  42     int         fc_mx_len;  
  18.  43     int         fc_mp_len;  
  19.  44     u32         fc_flow;  
  20.  45     u32         fc_nlflags;  
  21.  46     struct nl_info      fc_nlinfo;  
  22.  47  };  
26 struct fib_config {
 27     u8          fc_dst_len;
 28     u8          fc_tos;
 29     u8          fc_protocol;
 30     u8          fc_scope;
 31     u8          fc_type;
 32     /* 3 bytes unused */
 33     u32         fc_table;
 34     __be32          fc_dst;
 35     __be32          fc_gw;
 36     int         fc_oif;
 37     u32         fc_flags;
 38     u32         fc_priority;
 39     __be32          fc_prefsrc;
 40     struct nlattr       *fc_mx;
 41     struct rtnexthop    *fc_mp;
 42     int         fc_mx_len;
 43     int         fc_mp_len;
 44     u32         fc_flow;
 45     u32         fc_nlflags;
 46     struct nl_info      fc_nlinfo;
 47  };

图12.4.1 route命令配置路由信息

根据上面信息,将各项内容表述在如下图形中:

图12.4.2 cfg信息

由于应用空间传递的命令是SIOCADDRT(代码片段一),所以执行507~511行的代码。fib_new_table用于获得一个和用户匹配类型的路由表,如果在现有的路由表中没有要找的类型,则会创建这个类型的路由表。

  1.  75 struct fib_table *fib_new_table(struct net *net, u32 id)  
  2.   76 {  
  3.   77     struct fib_table *tb;  
  4.   78     unsigned int h;  
  5.   79   
  6.   80     if (id == 0)  
  7.   81         id = RT_TABLE_MAIN;  
  8.   82     tb = fib_get_table(net, id);  
  9.   83     if (tb)  
  10.   84         return tb;  
  11.   85   
  12.   86     tb = fib_trie_table(id);  
  13.   87     if (!tb)  
  14.   88         return NULL;  
  15.   89   
  16.   90     switch (id) {  
  17.   91     case RT_TABLE_LOCAL:  
  18.   92         net->ipv4.fib_local = tb;  
  19.   93         break;  
  20.   94   
  21.   95     case RT_TABLE_MAIN:  
  22.   96         net->ipv4.fib_main = tb;  
  23.   97         break;  
  24.   98   
  25.   99     case RT_TABLE_DEFAULT:  
  26.  100         net->ipv4.fib_default = tb;  
  27.  101         break;  
  28.  102   
  29.  103     default:  
  30.  104         break;  
  31.  105     }  
  32.  106   
  33.  107     h = id & (FIB_TABLE_HASHSZ - 1);  
  34.  108     hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);  
  35.  109     return tb;  
  36.  110 }  
  37. 112 struct fib_table *fib_get_table(struct net *net, u32 id)  
  38.  113 {  
  39.  114     struct fib_table *tb;  
  40.  115     struct hlist_head *head;  
  41.  116     unsigned int h;  
  42.  117   
  43.  118     if (id == 0)  
  44.  119         id = RT_TABLE_MAIN;  
  45.  120     h = id & (FIB_TABLE_HASHSZ - 1);  
  46.  121   
  47.  122     rcu_read_lock();  
  48.  123     head = &net->ipv4.fib_table_hash[h];  
  49.  124     hlist_for_each_entry_rcu(tb, head, tb_hlist) {  
  50.  125         if (tb->tb_id == id) {  
  51.  126             rcu_read_unlock();  
  52.  127             return tb;  
  53.  128         }  
  54.  129     }  
  55.  130     rcu_read_unlock();  
  56.  131     return NULL;  
  57.  132 }  
 75 struct fib_table *fib_new_table(struct net *net, u32 id)
  76 {
  77     struct fib_table *tb;
  78     unsigned int h;
  79 
  80     if (id == 0)
  81         id = RT_TABLE_MAIN;
  82     tb = fib_get_table(net, id);
  83     if (tb)
  84         return tb;
  85 
  86     tb = fib_trie_table(id);
  87     if (!tb)
  88         return NULL;
  89 
  90     switch (id) {
  91     case RT_TABLE_LOCAL:
  92         net->ipv4.fib_local = tb;
  93         break;
  94 
  95     case RT_TABLE_MAIN:
  96         net->ipv4.fib_main = tb;
  97         break;
  98 
  99     case RT_TABLE_DEFAULT:
 100         net->ipv4.fib_default = tb;
 101         break;
 102 
 103     default:
 104         break;
 105     }
 106 
 107     h = id & (FIB_TABLE_HASHSZ - 1);
 108     hlist_add_head_rcu(&tb->tb_hlist, &net->ipv4.fib_table_hash[h]);
 109     return tb;
 110 }
112 struct fib_table *fib_get_table(struct net *net, u32 id)
 113 {
 114     struct fib_table *tb;
 115     struct hlist_head *head;
 116     unsigned int h;
 117 
 118     if (id == 0)
 119         id = RT_TABLE_MAIN;
 120     h = id & (FIB_TABLE_HASHSZ - 1);
 121 
 122     rcu_read_lock();
 123     head = &net->ipv4.fib_table_hash[h];
 124     hlist_for_each_entry_rcu(tb, head, tb_hlist) {
 125         if (tb->tb_id == id) {
 126             rcu_read_unlock();
 127             return tb;
 128         }
 129     }
 130     rcu_read_unlock();
 131     return NULL;
 132 }

82行fib_get_table用户查找现有的路由表,路由表的类型定义在include/uapi/linux/rtnetlink.h文件中:

  1. enum rt_class_t {  
  2.     RT_TABLE_UNSPEC=0,  
  3. /* User defined values */  
  4.     RT_TABLE_COMPAT=252,  
  5.     RT_TABLE_DEFAULT=253,  
  6.     RT_TABLE_MAIN=254,  
  7.     RT_TABLE_LOCAL=255,  
  8.     RT_TABLE_MAX=0xFFFFFFFF  
  9. };  
enum rt_class_t {
	RT_TABLE_UNSPEC=0,
/* User defined values */
	RT_TABLE_COMPAT=252,
	RT_TABLE_DEFAULT=253,
	RT_TABLE_MAIN=254,
	RT_TABLE_LOCAL=255,
	RT_TABLE_MAX=0xFFFFFFFF
};

虽然这里最大路由项的定义值是RT_TABLE_MAX,但是其实在不启动等价路由【Equal-cost multi-path routing(ECMP)(CONFIG_IP_ROUTE_MULTIPATH)】时,配置CONFIG_IP_MULTIPLE_TABLES时有256张路由表。启用等价路由时只会有两张路由表,在存在多条网络路径的网络节点会配置使能。

图12.4.3的路由表拓扑结构中, net代表了一个网络命名空间,其指针成员ipv4指向inet协议字段,ipv4的fib_table_hash是一个数组,其指向是哈希链表,该表表映射的就是具体路由表的类型,路由表的类型从0开始一直到255,图中第一行的RT_TABLE_DEFAULT就等于253依次类推,所以在查找路由表时,只需要记得你找的是MAIN表还是LOCAL,而不需要关心是254还是255之类的数值了,这很类似于域名概念。每一个路由表由struct fib_table类型表示。命名空间中的哈希字段指向具体路由表的tb_list字段,并且处在同一个类型的路由表之间也是通过这个字段联系在一起的。

图12.4.3路由表拓扑

fc_table项指示的选择哪一张表插入用户配置的路由项,fib_new_table的82行fib_get_table根据路由表的id(根据图12.1.2其传递进来的值等于0)字段查询指定类型的路由表。81行将id赋值为RT_TABLE_MAIN。

120行得到h等于 RT_TABLE_MAIN。

将123行结合图12.1.3就可以看出其找到main表。

124~128根据路由表的tb_list字段遍历路由表,寻找路由表的tb_id等于RT_TABLE_MAIN的表。找到就返回该表,没找到就返回NULL,让其创建该表项。

83~84,如果fib_get_table找到了指定的表,则就是向这个表插入一个路由项,如果没有找到,说明需要创建一个这个表,接着向下86行即用于创建一个表。创建路由表的函数位于fib_trie.c文件,在2.6.38及以前Linux默认使用的是hash方法管理路由表和路由项,到Linux3.10,内核默认使用LC-trie方法,中文里常把这个算法称为字典或者单词查找树,路由使用的关于单词查找树的代码都在fib_trie.c。这个算法在分析代码时遇到了再说。

net/ipv4/fib_trie.c

  1. 1970 struct fib_table *fib_trie_table(u32 id)  
  2. 1971 {  
  3. 1972     struct fib_table *tb;  
  4. 1973     struct trie *t;  
  5. 1974   
  6. 1975     tb = kmalloc(sizeof(struct fib_table) + sizeof(struct trie),  
  7. 1976              GFP_KERNEL);  
  8. 1977     if (tb == NULL)  
  9. 1978         return NULL;  
  10. 1979   
  11. 1980     tb->tb_id = id;  
  12. 1981     tb->tb_default = -1;  
  13. 1982     tb->tb_num_default = 0;  
  14. 1983   
  15. 1984     t = (struct trie *) tb->tb_data;  
  16. 1985     memset(t, 0, sizeof(*t));  
  17. 1986   
  18. 1987     return tb;  
  19. 1988 }  
1970 struct fib_table *fib_trie_table(u32 id)
1971 {
1972     struct fib_table *tb;
1973     struct trie *t;
1974 
1975     tb = kmalloc(sizeof(struct fib_table) + sizeof(struct trie),
1976              GFP_KERNEL);
1977     if (tb == NULL)
1978         return NULL;
1979 
1980     tb->tb_id = id;
1981     tb->tb_default = -1;
1982     tb->tb_num_default = 0;
1983 
1984     t = (struct trie *) tb->tb_data;
1985     memset(t, 0, sizeof(*t));
1986 
1987     return tb;
1988 }

1975行创建一个路由表,路由表的定义如下,这里的内存申请将零长数组tb_data初始化为struct trie成员。1980~1985初始化该路由表的各个字段。

  1. struct fib_table {  
  2.     struct hlist_node   tb_hlist;  
  3.     u32         tb_id;  
  4.     int         tb_default;  
  5.     int         tb_num_default;  
  6.     unsigned long       tb_data[0];  
  7. };  
struct fib_table {
	struct hlist_node	tb_hlist;
	u32			tb_id;
	int			tb_default;
	int			tb_num_default;
	unsigned long		tb_data[0];
};

fib_new_table的90~105行将网络命名空间的fib_main字段指向这里创建的MAIN表。108行再将MAIN表放到数组fib_table_hash里,以便后续的索引。

再回退到ip_rt_ioctl的509行,fib_table_insert用于向路由表中插入一个路由项了。可以知道插入操作是基于trie方法的。其插入文件位于net/ipv4/fib_trie.c文件。这个函数有点长,但是还是要一行一行的过。

其参数是上面找到的或者创建的路由表,配置项就是前面用户空间配置的信息,该信息见图12.4.2。

12.6 路由查找

在12.2和12.3节,主要关注的是trie树的构建和管理,并不涉及路由查找的内容,由于管理和维护一个trie树本身是一件复杂的事,所以内核没有将查找路由表需要的信息也放在trie树中存储,而是使用了其它的一些数据结构来辅助查找过程,这节先引入这些数据结构,然后剖析查找过程,查找的总体思想是首先检查参数的合法性,查找trie(tnode ,leaf)树,trie树本身存储的是key值,没有其它 过多的信息,这些信息包括tos,也即流控,所以需要查找一些辅助数据结构,查找到了以后,如果对应的路由项没有缓存,会创建一个路由缓存。

12.6.1 相关数据结构

 

本节所述的数据结构指的是辅助数据结构,并没有将使用到的trie树相关数据结构罗列出来。关于这几个数据结构间的关系见图12.3.3。

fib_result

fib_result用于存放路由查找的结果

include/net/ip_fib.h

  1. struct fib_result {  
  2. unsigned char   prefixlen; //前缀长度  
  3. unsigned char   nh_sel; //nexthop 索引  
  4. unsigned char   type; //type和scope是类型和范围  
  5. unsigned char   scope;  
  6. u32     tclassid;   
  7. struct fib_info *fi; //路由项对应的信息  
  8. struct fib_table *table; //该结果源于的表项  
  9. struct list_head *fa_head; //fib alias链表指针。  
  10. };  
struct fib_result {
unsigned char	prefixlen; //前缀长度
unsigned char	nh_sel; //nexthop 索引
unsigned char	type; //type和scope是类型和范围
unsigned char	scope;
u32		tclassid; 
struct fib_info *fi; //路由项对应的信息
struct fib_table *table; //该结果源于的表项
struct list_head *fa_head; //fib alias链表指针。
};
fib_info
  1. struct fib_info {  
  2.     struct hlist_node   fib_hash; //fib_info信息索引,局部全局数组fib_info_hash使用  
  3.     struct hlist_node   fib_lhash;//同样是局部全局数组fib_info_laddrhash索引  
  4.     struct net      *fib_net;//对应设备所在网络命名空间中的一些信息。  
  5.     int         fib_treeref;//trie树引用计数  
  6.     atomic_t        fib_clntref;  
  7.     unsigned int        fib_flags;  
  8. //标记该fib_info是否可用,当值是1时,标志该路由项失效,查找时将不会参考该值。  
  9.     unsigned char       fib_dead;     
  10. unsigned char       fib_protocol; //支持的协议  
  11.     unsigned char       fib_scope; //范围  
  12.     unsigned char       fib_type;//类型  
  13.     __be32          fib_prefsrc; //前缀  
  14.     u32         fib_priority; //优先级  
  15.     u32         *fib_metrics; //metrics相关内容  
  16. #define fib_mtu fib_metrics[RTAX_MTU-1]  
  17. #define fib_window fib_metrics[RTAX_WINDOW-1]  
  18. #define fib_rtt fib_metrics[RTAX_RTT-1]  
  19. #define fib_advmss fib_metrics[RTAX_ADVMSS-1]  
  20.     int         fib_nhs; //记录fib_nh[0]零长数组具有的下一跳的个数。  
  21. #ifdef CONFIG_IP_ROUTE_MULTIPATH  
  22.     int         fib_power;  
  23. #endif  
  24.     struct rcu_head     rcu;  
  25.     struct fib_nh       fib_nh[0];  
  26. #define fib_dev     fib_nh[0].nh_dev  
  27. };  
struct fib_info {
	struct hlist_node	fib_hash; //fib_info信息索引,局部全局数组fib_info_hash使用
	struct hlist_node	fib_lhash;//同样是局部全局数组fib_info_laddrhash索引
	struct net		*fib_net;//对应设备所在网络命名空间中的一些信息。
	int			fib_treeref;//trie树引用计数
	atomic_t		fib_clntref;
	unsigned int		fib_flags;
//标记该fib_info是否可用,当值是1时,标志该路由项失效,查找时将不会参考该值。
	unsigned char		fib_dead;	
unsigned char		fib_protocol; //支持的协议
	unsigned char		fib_scope; //范围
	unsigned char		fib_type;//类型
	__be32			fib_prefsrc; //前缀
	u32			fib_priority; //优先级
	u32			*fib_metrics; //metrics相关内容
#define fib_mtu fib_metrics[RTAX_MTU-1]
#define fib_window fib_metrics[RTAX_WINDOW-1]
#define fib_rtt fib_metrics[RTAX_RTT-1]
#define fib_advmss fib_metrics[RTAX_ADVMSS-1]
	int			fib_nhs; //记录fib_nh[0]零长数组具有的下一跳的个数。
#ifdef CONFIG_IP_ROUTE_MULTIPATH
	int			fib_power;
#endif
	struct rcu_head		rcu;
	struct fib_nh		fib_nh[0];
#define fib_dev		fib_nh[0].nh_dev
};

该数据结构元素的初始化实例可以参考图12.3.3,图中的fi、fi-2、fi-3分别对应于三种路由项情况。

fib_alias

虽然套接字数据包路由项key可能不同,例如路由到192.168.0.10和路由到192.168.0.100的两项,但是不同套接字数据包的type或者tos是相同的,对于一个大规模的路由而言,的类型不同套接字数据包的type和tos相同的概率还是比较大的,为了节约空间和时间,就将这些字段抽象出来了,tos和type组合的类型有限,这样可以使这个有限的组合应对无限多路由项。fa_list用于串接所有的fib_alias类型的结构体,以便于管理fa_alias结构体。

  1. struct fib_alias {  
  2.     struct list_head    fa_list;  
  3.     struct fib_info     *fa_info; //见上  
  4.     u8          fa_tos;  
  5.     u8          fa_type;// UNICAST、LOCAL、BROADCAST  
  6.     u8          fa_state;  
  7.     struct rcu_head     rcu;  
  8. };  
struct fib_alias {
	struct list_head	fa_list;
	struct fib_info		*fa_info; //见上
	u8			fa_tos;
	u8			fa_type;// UNICAST、LOCAL、BROADCAST
	u8			fa_state;
	struct rcu_head		rcu;
};
fib_flowi4

该数据结构在图12.3.3中并未显示出来,路由查找过程中会使用到该数据结构,该数据结构根据输入输出网络设备、ip层和tcp层一些字段对流量进行分类。flowi4(flow inet 4)是Internet 4协议的流控之意。对应的还有flowi6的流控,其实该结构体就是对flowi_common的封装,这么做的好处是不言而喻的,提高代码复用率的同时增加了代码维护的灵活性。

  1. struct flowi4 {  
  2.     struct flowi_common __fl_common;  
  3. #define flowi4_oif      __fl_common.flowic_oif //output Interface   
  4. #define flowi4_iif      __fl_common.flowic_iif //input interface  
  5. #define flowi4_mark     __fl_common.flowic_mark  
  6. #define flowi4_tos      __fl_common.flowic_tos  
  7. #define flowi4_scope        __fl_common.flowic_scope  
  8. #define flowi4_proto        __fl_common.flowic_proto  
  9. #define flowi4_flags        __fl_common.flowic_flags  
  10. #define flowi4_secid        __fl_common.flowic_secid  
  11.   
  12.     /* (saddr,daddr) must be grouped, same order as in IP header */  
  13.     __be32          saddr; //源地址  
  14.     __be32          daddr;//目的地址  
  15.   
  16.     union flowi_uli     uli;  
  17. #define fl4_sport       uli.ports.sport //tcp层源端口号  
  18. #define fl4_dport       uli.ports.dport//tcp层目的端口号  
  19. #define fl4_icmp_type       uli.icmpt.type  
  20. #define fl4_icmp_code       uli.icmpt.code  
  21. #define fl4_ipsec_spi       uli.spi  
  22. #define fl4_mh_type     uli.mht.type  
  23. #define fl4_gre_key     uli.gre_key  
  24. } __attribute__((__aligned__(BITS_PER_LONG/8)));  
struct flowi4 {
	struct flowi_common	__fl_common;
#define flowi4_oif		__fl_common.flowic_oif //output Interface 
#define flowi4_iif		__fl_common.flowic_iif //input interface
#define flowi4_mark		__fl_common.flowic_mark
#define flowi4_tos		__fl_common.flowic_tos
#define flowi4_scope		__fl_common.flowic_scope
#define flowi4_proto		__fl_common.flowic_proto
#define flowi4_flags		__fl_common.flowic_flags
#define flowi4_secid		__fl_common.flowic_secid

	/* (saddr,daddr) must be grouped, same order as in IP header */
	__be32			saddr; //源地址
	__be32			daddr;//目的地址

	union flowi_uli		uli;
#define fl4_sport		uli.ports.sport //tcp层源端口号
#define fl4_dport		uli.ports.dport//tcp层目的端口号
#define fl4_icmp_type		uli.icmpt.type
#define fl4_icmp_code		uli.icmpt.code
#define fl4_ipsec_spi		uli.spi
#define fl4_mh_type		uli.mht.type
#define fl4_gre_key		uli.gre_key
} __attribute__((__aligned__(BITS_PER_LONG/8)));
fib_ common
  1. struct flowi_common {  
  2.     int flowic_oif;  
  3.     int flowic_iif;  
  4.     __u32   flowic_mark;  
  5.     __u8    flowic_tos;  
  6.     __u8    flowic_scope;  
  7.     __u8    flowic_proto;  
  8.     __u8    flowic_flags;  
  9. #define FLOWI_FLAG_ANYSRC       0x01  
  10. #define FLOWI_FLAG_CAN_SLEEP        0x02  
  11. #define FLOWI_FLAG_KNOWN_NH     0x04  
  12.     __u32   flowic_secid;  
  13. };  
struct flowi_common {
	int	flowic_oif;
	int	flowic_iif;
	__u32	flowic_mark;
	__u8	flowic_tos;
	__u8	flowic_scope;
	__u8	flowic_proto;
	__u8	flowic_flags;
#define FLOWI_FLAG_ANYSRC		0x01
#define FLOWI_FLAG_CAN_SLEEP		0x02
#define FLOWI_FLAG_KNOWN_NH		0x04
	__u32	flowic_secid;
};
rtable

套接字将会绑定该结构体。

  1. struct rtable {  
  2.     struct dst_entry    dst;  
  3.   
  4.     int         rt_genid;  
  5.     unsigned int        rt_flags;  
  6.     __u16           rt_type;  
  7.     __u8            rt_is_input;  
  8.     __u8            rt_uses_gateway;  
  9.   
  10.     int         rt_iif;  
  11.   
  12.     /* Info on neighbour */  
  13.     __be32          rt_gateway;  
  14.   
  15.     /* Miscellaneous cached information */  
  16.     u32         rt_pmtu;  
  17.   
  18.     struct list_head    rt_uncached;  
  19. };  
struct rtable {
	struct dst_entry	dst;

	int			rt_genid;
	unsigned int		rt_flags;
	__u16			rt_type;
	__u8			rt_is_input;
	__u8			rt_uses_gateway;

	int			rt_iif;

	/* Info on neighbour */
	__be32			rt_gateway;

	/* Miscellaneous cached information */
	u32			rt_pmtu;

	struct list_head	rt_uncached;
};
dst_entry
  1. struct dst_entry {  
  2.     struct rcu_head     rcu_head;  
  3.     struct dst_entry    *child;  
  4.     struct net_device       *dev;  
  5.     struct  dst_ops         *ops;  
  6.     unsigned long       _metrics;  
  7.     unsigned long           expires;  
  8.     struct dst_entry    *path;  
  9.     struct dst_entry    *from;  
  10. #ifdef CONFIG_XFRM  
  11.     struct xfrm_state   *xfrm;  
  12. #else  
  13.     void            *__pad1;  
  14. #endif  
  15.     int         (*input)(struct sk_buff *);  
  16.     int         (*output)(struct sk_buff *);  
  17.   
  18.     unsigned short      flags;  
  19. #define DST_HOST        0x0001  
  20. #define DST_NOXFRM      0x0002  
  21. #define DST_NOPOLICY        0x0004  
  22. #define DST_NOHASH      0x0008  
  23. #define DST_NOCACHE     0x0010  
  24. #define DST_NOCOUNT     0x0020  
  25. #define DST_NOPEER      0x0040  
  26. #define DST_FAKE_RTABLE     0x0080  
  27. #define DST_XFRM_TUNNEL     0x0100  
  28. #define DST_XFRM_QUEUE      0x0200  
  29.   
  30.     unsigned short      pending_confirm;  
  31.   
  32.     short           error;  
  33.   
  34.     /* A non-zero value of dst->obsolete forces by-hand validation 
  35.      * of the route entry.  Positive values are set by the generic 
  36.      * dst layer to indicate that the entry has been forcefully 
  37.      * destroyed. 
  38.      * 
  39.      * Negative values are used by the implementation layer code to 
  40.      * force invocation of the dst_ops->check() method. 
  41.      */  
  42.     short           obsolete;  
  43. #define DST_OBSOLETE_NONE   0  
  44. #define DST_OBSOLETE_DEAD   2  
  45. #define DST_OBSOLETE_FORCE_CHK  -1  
  46. #define DST_OBSOLETE_KILL   -2  
  47.     unsigned short      header_len; /* more space at head required */  
  48.     unsigned short      trailer_len;    /* space to reserve at tail */  
  49. #ifdef CONFIG_IP_ROUTE_CLASSID  
  50.     __u32           tclassid;  
  51. #else  
  52.     __u32           __pad2;  
  53. #endif  
  54.   
  55.     /* 
  56.      * Align __refcnt to a 64 bytes alignment 
  57.      * (L1_CACHE_SIZE would be too much) 
  58.      */  
  59. #ifdef CONFIG_64BIT  
  60.     long            __pad_to_align_refcnt[2];  
  61. #endif  
  62.     /* 
  63.      * __refcnt wants to be on a different cache line from 
  64.      * input/output/ops or performance tanks badly 
  65.      */  
  66.     atomic_t        __refcnt;   /* client references    */  
  67.     int         __use;  
  68.     unsigned long       lastuse;  
  69.     union {  
  70.         struct dst_entry    *next;  
  71.         struct rtable __rcu *rt_next;  
  72.         struct rt6_info     *rt6_next;  
  73.         struct dn_route __rcu   *dn_next;  
  74.     };  
  75. };  
struct dst_entry {
	struct rcu_head		rcu_head;
	struct dst_entry	*child;
	struct net_device       *dev;
	struct  dst_ops	        *ops;
	unsigned long		_metrics;
	unsigned long           expires;
	struct dst_entry	*path;
	struct dst_entry	*from;
#ifdef CONFIG_XFRM
	struct xfrm_state	*xfrm;
#else
	void			*__pad1;
#endif
	int			(*input)(struct sk_buff *);
	int			(*output)(struct sk_buff *);

	unsigned short		flags;
#define DST_HOST		0x0001
#define DST_NOXFRM		0x0002
#define DST_NOPOLICY		0x0004
#define DST_NOHASH		0x0008
#define DST_NOCACHE		0x0010
#define DST_NOCOUNT		0x0020
#define DST_NOPEER		0x0040
#define DST_FAKE_RTABLE		0x0080
#define DST_XFRM_TUNNEL		0x0100
#define DST_XFRM_QUEUE		0x0200

	unsigned short		pending_confirm;

	short			error;

	/* A non-zero value of dst->obsolete forces by-hand validation
	 * of the route entry.  Positive values are set by the generic
	 * dst layer to indicate that the entry has been forcefully
	 * destroyed.
	 *
	 * Negative values are used by the implementation layer code to
	 * force invocation of the dst_ops->check() method.
	 */
	short			obsolete;
#define DST_OBSOLETE_NONE	0
#define DST_OBSOLETE_DEAD	2
#define DST_OBSOLETE_FORCE_CHK	-1
#define DST_OBSOLETE_KILL	-2
	unsigned short		header_len;	/* more space at head required */
	unsigned short		trailer_len;	/* space to reserve at tail */
#ifdef CONFIG_IP_ROUTE_CLASSID
	__u32			tclassid;
#else
	__u32			__pad2;
#endif

	/*
	 * Align __refcnt to a 64 bytes alignment
	 * (L1_CACHE_SIZE would be too much)
	 */
#ifdef CONFIG_64BIT
	long			__pad_to_align_refcnt[2];
#endif
	/*
	 * __refcnt wants to be on a different cache line from
	 * input/output/ops or performance tanks badly
	 */
	atomic_t		__refcnt;	/* client references	*/
	int			__use;
	unsigned long		lastuse;
	union {
		struct dst_entry	*next;
		struct rtable __rcu	*rt_next;
		struct rt6_info		*rt6_next;
		struct dn_route __rcu	*dn_next;
	};
};

12.6.2 接收包路由项查找

在ip_input.c文件中,ip_rcv_finish()函数用于处理接收到的数据包,这里将重心倾向于路由这块。 skb中没有找到路由项,即缓存中寻找路由项失败,需要调用ip_route_input_noref到路由表中查找,如果是回环包,则skb的路由缓存有路由项,即路由cache命中。

*多播地址寻找路由项函数:ip_route_input_mc

*单播地址寻找路由项函数:ip_route_input_slow

  1. 314 static int ip_rcv_finish(struct sk_buff *skb)  
  2. 315 {  
  3. //根据套接字获得ip头  
  4. 316         const struct iphdr *iph = ip_hdr(skb);  
  5. 317         struct rtable *rt;  
  6. / sysctl_ip_early_demux 是二进制值,该值用于对发往本地数据包的优化。当前仅对建立连接的套接字起作用。  
  7. 319         if (sysctl_ip_early_demux && !skb_dst(skb)) {  
  8. 320                 const struct net_protocol *ipprot;  
  9. 321                 int protocol = iph->protocol;  
  10. 322   
  11. 323                 ipprot = rcu_dereference(inet_protos[protocol]);  
  12. 324                 if (ipprot && ipprot->early_demux) {  
  13. 325                         ipprot->early_demux(skb);  
  14. 326                         /* must reload iph, skb->head might have changed */  
  15. 327                         iph = ip_hdr(skb);  
  16. 328                 }  
  17. 329         }  
  18. 330   
  19. //如果套接字的dst字段没有指向一个路由项,如果没有则调用ip_route_input_noref进行查找。  
  20. 335         if (!skb_dst(skb)) {  
  21. 336                 int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,  
  22. 337                                                iph->tos, skb->dev);  
  23. 338                 if (unlikely(err)) {  
  24. 339                         if (err == -EXDEV)  
  25. //更新基于tcp/ip因特网的MIB(management information base)信息,RFC1213  
  26. 340                                 NET_INC_STATS_BH(dev_net(skb->dev),  
  27. 341                                                  LINUX_MIB_IPRPFILTER);  
  28. 342                         goto drop;  
  29. 343                 }  
  30. 344         }  
  31. 345   
  32. //对套接字可选字段的处理。ip_rcv_options(skb)会调用ip_options_rcv_srr(skb)  
  33. 357         if (iph->ihl > 5 && ip_rcv_options(skb))  
  34. 358                 goto drop;  
  35. //获得路由表  
  36. 360         rt = skb_rtable(skb);  
  37. //多播和广播时的信息传递。  
  38. 361         if (rt->rt_type == RTN_MULTICAST) {  
  39. 362                 IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST,  
  40. 363                                 skb->len);  
  41. 364         } else if (rt->rt_type == RTN_BROADCAST)  
  42. 365                 IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,  
  43. 366                                 skb->len);  
  44. /*向tcp层传递packet*/  
  45. 368         return dst_input(skb);  
  46. 373 }  
314 static int ip_rcv_finish(struct sk_buff *skb)
315 {
//根据套接字获得ip头
316         const struct iphdr *iph = ip_hdr(skb);
317         struct rtable *rt;
/ sysctl_ip_early_demux 是二进制值,该值用于对发往本地数据包的优化。当前仅对建立连接的套接字起作用。
319         if (sysctl_ip_early_demux && !skb_dst(skb)) {
320                 const struct net_protocol *ipprot;
321                 int protocol = iph->protocol;
322 
323                 ipprot = rcu_dereference(inet_protos[protocol]);
324                 if (ipprot && ipprot->early_demux) {
325                         ipprot->early_demux(skb);
326                         /* must reload iph, skb->head might have changed */
327                         iph = ip_hdr(skb);
328                 }
329         }
330 
//如果套接字的dst字段没有指向一个路由项,如果没有则调用ip_route_input_noref进行查找。
335         if (!skb_dst(skb)) {
336                 int err = ip_route_input_noref(skb, iph->daddr, iph->saddr,
337                                                iph->tos, skb->dev);
338                 if (unlikely(err)) {
339                         if (err == -EXDEV)
//更新基于tcp/ip因特网的MIB(management information base)信息,RFC1213
340                                 NET_INC_STATS_BH(dev_net(skb->dev),
341                                                  LINUX_MIB_IPRPFILTER);
342                         goto drop;
343                 }
344         }
345 
//对套接字可选字段的处理。ip_rcv_options(skb)会调用ip_options_rcv_srr(skb)
357         if (iph->ihl > 5 && ip_rcv_options(skb))
358                 goto drop;
//获得路由表
360         rt = skb_rtable(skb);
//多播和广播时的信息传递。
361         if (rt->rt_type == RTN_MULTICAST) {
362                 IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INMCAST,
363                                 skb->len);
364         } else if (rt->rt_type == RTN_BROADCAST)
365                 IP_UPD_PO_STATS_BH(dev_net(rt->dst.dev), IPSTATS_MIB_INBCAST,
366                                 skb->len);
/*向tcp层传递packet*/
368         return dst_input(skb);
373 }

336行ip_route_input_noref对于tcp/ip接收数据包进行路由寻址。

  1. net/ipv4/route.c  
  2. /*参数的意义 
  3. skb:传递进来的skb_buff, 
  4. dst:目的地址 
  5. src: 源地址 
  6. tos:type of service,ip头中的服务类型 
  7. devin:网卡设备 
  8. */  
  9. int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,  
  10. 1763                          u8 tos, struct net_device *dev)  
  11. 1764 {  
  12. 1765         int res;  
  13. 1766   
  14. 1767         rcu_read_lock();  
  15. 1768   
  16. //多播的处理  
  17. 1780         if (ipv4_is_multicast(daddr)) {  
  18. 1781                 struct in_device *in_dev = __in_dev_get_rcu(dev);  
  19. 1782   
  20. 1783                 if (in_dev) {  
  21. 1784                         int our = ip_check_mc_rcu(in_dev, daddr, saddr,  
  22. 1785                                                   ip_hdr(skb)->protocol);  
  23. 1786                         if (our  
  24. 1792                            ) {  
  25. 1793                                 int res = ip_route_input_mc(skb, daddr, saddr,  
  26. 1794                                                             tos, dev, our);  
  27. 1795                                 rcu_read_unlock();  
  28. 1796                                 return res;  
  29. 1797                         }  
  30. 1798                 }  
  31. 1799                 rcu_read_unlock();  
  32. 1800                 return -EINVAL;  
  33. 1801         }  
  34. //除多播以外情况的处理。  
  35. 1802         res = ip_route_input_slow(skb, daddr, saddr, tos, dev);  
  36. 1803         rcu_read_unlock();  
  37. 1804         return res;  
  38. 1805 }   
net/ipv4/route.c
/*参数的意义
skb:传递进来的skb_buff,
dst:目的地址
src: 源地址
tos:type of service,ip头中的服务类型
devin:网卡设备
*/
int ip_route_input_noref(struct sk_buff *skb, __be32 daddr, __be32 saddr,
1763                          u8 tos, struct net_device *dev)
1764 {
1765         int res;
1766 
1767         rcu_read_lock();
1768 
//多播的处理
1780         if (ipv4_is_multicast(daddr)) {
1781                 struct in_device *in_dev = __in_dev_get_rcu(dev);
1782 
1783                 if (in_dev) {
1784                         int our = ip_check_mc_rcu(in_dev, daddr, saddr,
1785                                                   ip_hdr(skb)->protocol);
1786                         if (our
1792                            ) {
1793                                 int res = ip_route_input_mc(skb, daddr, saddr,
1794                                                             tos, dev, our);
1795                                 rcu_read_unlock();
1796                                 return res;
1797                         }
1798                 }
1799                 rcu_read_unlock();
1800                 return -EINVAL;
1801         }
//除多播以外情况的处理。
1802         res = ip_route_input_slow(skb, daddr, saddr, tos, dev);
1803         rcu_read_unlock();
1804         return res;
1805 } 

1793行是多播地址寻找路由项函数ip_route_input_mc。

  1. 1373 static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,  
  2. 1374                                 u8 tos, struct net_device *dev, int our)  
  3. 1375 {  
  4. /申请并初始化dst_entry,由于rtable的第一个成员就是dst_entry,多以这里直接进行赋值,没有使用策略路由  
  5. 1403         rth = rt_dst_alloc(dev_net(dev)->loopback_dev,  
  6. 1404                            IN_DEV_CONF_GET(in_dev, NOPOLICY), falsefalse);  
  7. 1405         if (!rth)  
  8. 1406                 goto e_nobufs;  
  9. 1407   
  10. //初始化rtable字段的其它项。  
  11. 1411         rth->dst.output = ip_rt_bug;  
  12. 1412   
  13. 1413         rth->rt_genid   = rt_genid(dev_net(dev));  
  14. 1414         rth->rt_flags   = RTCF_MULTICAST;  
  15. 1415         rth->rt_type    = RTN_MULTICAST;  
  16. 1416         rth->rt_is_input= 1;  
  17. 1417         rth->rt_iif     = 0;  
  18. 1418         rth->rt_pmtu    = 0;  
  19. 1419         rth->rt_gateway = 0;  
  20. 1420         rth->rt_uses_gateway = 0;  
  21. 1421         INIT_LIST_HEAD(&rth->rt_uncached);  
  22. 1422         if (our) {  
  23. 1423                 rth->dst.input= ip_local_deliver;  
  24. 1424                 rth->rt_flags |= RTCF_LOCAL;  
  25. 1425         }  
  26. //将rtable的dst成员地址赋值给skb。  
  27. 1433         skb_dst_set(skb, &rth->dst);  
  28. 1434         return 0;  
  29. 1442 }  
1373 static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr,
1374                                 u8 tos, struct net_device *dev, int our)
1375 {
/申请并初始化dst_entry,由于rtable的第一个成员就是dst_entry,多以这里直接进行赋值,没有使用策略路由
1403         rth = rt_dst_alloc(dev_net(dev)->loopback_dev,
1404                            IN_DEV_CONF_GET(in_dev, NOPOLICY), false, false);
1405         if (!rth)
1406                 goto e_nobufs;
1407 
//初始化rtable字段的其它项。
1411         rth->dst.output = ip_rt_bug;
1412 
1413         rth->rt_genid   = rt_genid(dev_net(dev));
1414         rth->rt_flags   = RTCF_MULTICAST;
1415         rth->rt_type    = RTN_MULTICAST;
1416         rth->rt_is_input= 1;
1417         rth->rt_iif     = 0;
1418         rth->rt_pmtu    = 0;
1419         rth->rt_gateway = 0;
1420         rth->rt_uses_gateway = 0;
1421         INIT_LIST_HEAD(&rth->rt_uncached);
1422         if (our) {
1423                 rth->dst.input= ip_local_deliver;
1424                 rth->rt_flags |= RTCF_LOCAL;
1425         }
//将rtable的dst成员地址赋值给skb。
1433         skb_dst_set(skb, &rth->dst);
1434         return 0;
1442 }

ip_route_input_noref根据传递进来的目的地址判断是多播还是单播,多播使用ip_route_input_mc(skb, daddr,saddr, tos, dev, our)处理,单播使用 ip_route_input_slow(skb, daddr, saddr,tos, dev)。为了使函数的脉络看起来更为清晰,这里省去函数变量的定义、路由地址的合法性检查以及一些错误处理代码,只保留了正常情况下路由处理相关代码。

  1. 1585 static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,  
  2. 1586                                u8 tos, struct net_device *dev)  
  3. 1587 {  
  4. //上面之所以要检查源和目的地址,是因为路由会使用该信息。  
  5. //流量分类信息初始化,也是流控  
  6. 1637         fl4.flowi4_oif = 0;  
  7. 1638         fl4.flowi4_iif = dev->ifindex;  
  8. 1639         fl4.flowi4_mark = skb->mark;  
  9. 1640         fl4.flowi4_tos = tos;  
  10. 1641         fl4.flowi4_scope = RT_SCOPE_UNIVERSE;  
  11. 1642         fl4.daddr = daddr;  
  12. 1643         fl4.saddr = saddr;  
  13. //路由查找路由查找的结果存放在res(results)里。  
  14. 1644         err = fib_lookup(net, &fl4, &res);  
  15. 1645         if (err != 0)  
  16. 1646                 goto no_route;  
  17. //根据路由查找结果,创建一个路由缓存项。   
  18. 1667         err = ip_mkroute_input(skb, &res, &fl4, in_dev, daddr, saddr, tos);  
  19. 1668 out:    return err;  
  20. 1669   
  21. 1760 }  
1585 static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
1586                                u8 tos, struct net_device *dev)
1587 {
//上面之所以要检查源和目的地址,是因为路由会使用该信息。
//流量分类信息初始化,也是流控
1637         fl4.flowi4_oif = 0;
1638         fl4.flowi4_iif = dev->ifindex;
1639         fl4.flowi4_mark = skb->mark;
1640         fl4.flowi4_tos = tos;
1641         fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
1642         fl4.daddr = daddr;
1643         fl4.saddr = saddr;
//路由查找路由查找的结果存放在res(results)里。
1644         err = fib_lookup(net, &fl4, &res);
1645         if (err != 0)
1646                 goto no_route;
//根据路由查找结果,创建一个路由缓存项。 
1667         err = ip_mkroute_input(skb, &res, &fl4, in_dev, daddr, saddr, tos);
1668 out:    return err;
1669 
1760 }

1644行路由查找函数

  1. include/net/ip_fib.h  
  2. 219 static inline int fib_lookup(struct net *net, const struct flowi4 *flp,  
  3. 220                              struct fib_result *res)  
  4. 221 {  
  5. 222         struct fib_table *table;  
  6. //获得id等于LOCAL的路由,见12.3节。并查找其中的路由项, 查找过程见后面。  
  7. 224         table = fib_get_table(net, RT_TABLE_LOCAL);  
  8. 225         if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))  
  9. 226                 return 0;  
  10. //获得id等于MAIN的路由,见12.3节。并查找其中的路由项, 查找过程见后面。  
  11. 228         table = fib_get_table(net, RT_TABLE_MAIN);  
  12. 229         if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))  
  13. 230                 return 0;  
  14. 231         return -ENETUNREACH;  
  15. 232 }  
include/net/ip_fib.h
219 static inline int fib_lookup(struct net *net, const struct flowi4 *flp,
220                              struct fib_result *res)
221 {
222         struct fib_table *table;
//获得id等于LOCAL的路由,见12.3节。并查找其中的路由项, 查找过程见后面。
224         table = fib_get_table(net, RT_TABLE_LOCAL);
225         if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
226                 return 0;
//获得id等于MAIN的路由,见12.3节。并查找其中的路由项, 查找过程见后面。
228         table = fib_get_table(net, RT_TABLE_MAIN);
229         if (!fib_table_lookup(table, flp, res, FIB_LOOKUP_NOREF))
230                 return 0;
231         return -ENETUNREACH;
232 }

225行和229行的函数和12.3节提到的大多数函数一样,也位于fib_trie.c函数里。它是路由查找的核心函数,由于这个函数有些部分直接或者间接在12.3节有过叙述,该函数看起来也稍微容易些。这个函数先查找tnode然后查找leaf,查询的结果

  1. 1405 int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,  
  2. 1406                      struct fib_result *res, int fib_flags)  
  3. 1407 {  
  4. 1408         struct trie *t = (struct trie *) tb->tb_data;  
  5. 1409         int ret;  
  6. 1410         struct rt_trie_node *n;  
  7. 1411         struct tnode *pn;  
  8. 1412         unsigned int pos, bits;  
  9. 1413         t_key key = ntohl(flp->daddr);  
  10. 1414         unsigned int chopped_off;  
  11. 1415         t_key cindex = 0;  
  12. 1416         unsigned int current_prefix_length = KEYLENGTH;  
  13. 1417         struct tnode *cn;  
  14. 1418         t_key pref_mismatch;  
  15. 1419   
  16. 1420         rcu_read_lock();  
  17. 1421   
  18. 1422         n = rcu_dereference(t->trie);  
  19. 1423         if (!n)  
  20. 1424                 goto failed;  
  21. //首先查看fib_table指向的是否仅仅是leaf,而没有tnode,对于fib_table只有一个leaf的情况下,直接调用check_leaf进行  
  22. //验证  
  23. 1430         /* Just a leaf? */  
  24. 1431         if (IS_LEAF(n)) {  
  25. 1432                 ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags);  
  26. 1433                 goto found;  
  27. 1434         }  
  28. //对于是tnode的情况的处理  
  29. 1436         pn = (struct tnode *) n;  
  30. //该变量用于记录已经匹配到的比特数。  
  31. 1437         chopped_off = 0;  
  32. 1438   
  33. 1439         while (pn) {  
  34. 1440                 pos = pn->pos;  
  35. 1441                 bits = pn->bits;  
  36. 1443                 if (!chopped_off)  
  37. //找到不同的bit,这是为了获得孩子节点。  
  38. 1444                         cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length),  
  39. 1445                                                    pos, bits);  
  40. //获得孩子节点,这个孩子节点可能是tnode也可能是leaf  
  41. 1447                 n = tnode_get_child_rcu(pn, cindex);  
  42. //  
  43. 1449                 if (n == NULL) {  
  44. 1453                         goto backtrace;  
  45. 1454                 }  
  46. //如果孩子节点是一个leaf节点,则调用check_leaf检查是否是需要的路由项,并将结果存放在res中。  
  47. 1456                 if (IS_LEAF(n)) {  
  48. 1457                         ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags);  
  49. 1458                         if (ret > 0)  
  50. 1459                                 goto backtrace;  
  51. 1460                         goto found;  
  52. 1461                 }  
  53. //如果孩子节点是一个tnode,则需要进行迭代到其孩子进行上述查找过程。  
  54. 1463                 cn = (struct tnode *)n;  
  55. //第一次进入该函数这里的current_prefix_length的长度是不会小于pos+bits的。  
  56. 1494                 if (current_prefix_length < pos+bits) {  
  57. 1495                         if (tkey_extract_bits(cn->key, current_prefix_length,  
  58. 1496                                                 cn->pos - current_prefix_length)  
  59. 1497                             || !(cn->child[0]))  
  60. 1498                                 goto backtrace;  
  61. 1499                 }  
  62. 1532   
  63. 1533                 pref_mismatch = mask_pfx(cn->key ^ key, cn->pos);  
  64. //当根据pos和bits值没有找到搜索的key的话,进入前缀匹配模式。  
  65. /************************************************************************************************ 
  66. ***该模式存在意义如下: 
  67. ***对于ipv4路由表有如下两项: 
  68. ***192.168.20.16/28 
  69. ***192.168.0.0/16 
  70. ***如果需要查找192.168.20.19则上面两个都是匹配的,但是取哪个好呢?内核使用最常匹配原则,即认为192.168.20.16 
  71. ***(子网掩码长度是28)这一项是匹配的,通常default 项的前缀是最短的,其作用是在其他路由项均不能匹配时会使用***default项*[摘自维基百科,longest prefix match],这个函数的chopped_off就是忽略prefix的长度,这样匹配成功的概率会***变大。对于图12.2.4的情况的trie树,查找192.168.0.100路由项的情况是不会进入backtrace标号开始的语句的。 
  72. **********************************************************************************************/  
  73. 1540                 if (pref_mismatch) {  
  74. 1541                         /* fls(x) = __fls(x) + 1 */  
  75. 1542                         int mp = KEYLENGTH - __fls(pref_mismatch) - 1;  
  76. 1543   
  77. 1544                         if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0)  
  78. 1545                                 goto backtrace;  
  79. 1546   
  80. 1547                         if (current_prefix_length >= cn->pos)  
  81. 1548                                 current_prefix_length = mp;  
  82. 1549                 }  
  83. 1550   
  84. 1551                 pn = (struct tnode *)n; /* Descend */  
  85. 1552                 chopped_off = 0;  
  86. 1553                 continue;  
  87. 1554   
  88. 1555 backtrace:  
  89. 1556                 chopped_off++;  
  90. 1557   
  91. 1558                 /* As zero don't change the child key (cindex) */  
  92. 1559                 while ((chopped_off <= pn->bits)  
  93. 1560                        && !(cindex & (1<<(chopped_off-1))))  
  94. 1561                         chopped_off++;  
  95. 1562   
  96. 1563                 /* Decrease current_... with bits chopped off */  
  97. 1564                 if (current_prefix_length > pn->pos + pn->bits - chopped_off)  
  98. 1565                         current_prefix_length = pn->pos + pn->bits  
  99. 1566                                 - chopped_off;  
  100. 1567   
  101. 1568                 /* 
  102. 1569                  * Either we do the actual chop off according or if we have 
  103. 1570                  * chopped off all bits in this tnode walk up to our parent. 
  104. 1571                  */  
  105. 1572   
  106. 1573                 if (chopped_off <= pn->bits) {  
  107. 1574                         cindex &= ~(1 << (chopped_off-1));  
  108. 1575                 } else {  
  109. 1576                         struct tnode *parent = node_parent_rcu((struct rt_trie_node *) pn);  
  110. 1577                         if (!parent)  
  111. 1578                                 goto failed;  
  112. 1579   
  113. 1580                         /* Get Child's index */  
  114. 1581                         cindex = tkey_extract_bits(pn->key, parent->pos, parent->bits);  
  115. 1582                         pn = parent;  
  116. 1583                         chopped_off = 0;  
  117. 1593 found:  
  118. 1594         rcu_read_unlock();  
  119. 1595         return ret;  
  120. 1596 }  
1405 int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
1406                      struct fib_result *res, int fib_flags)
1407 {
1408         struct trie *t = (struct trie *) tb->tb_data;
1409         int ret;
1410         struct rt_trie_node *n;
1411         struct tnode *pn;
1412         unsigned int pos, bits;
1413         t_key key = ntohl(flp->daddr);
1414         unsigned int chopped_off;
1415         t_key cindex = 0;
1416         unsigned int current_prefix_length = KEYLENGTH;
1417         struct tnode *cn;
1418         t_key pref_mismatch;
1419 
1420         rcu_read_lock();
1421 
1422         n = rcu_dereference(t->trie);
1423         if (!n)
1424                 goto failed;
//首先查看fib_table指向的是否仅仅是leaf,而没有tnode,对于fib_table只有一个leaf的情况下,直接调用check_leaf进行
//验证
1430         /* Just a leaf? */
1431         if (IS_LEAF(n)) {
1432                 ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags);
1433                 goto found;
1434         }
//对于是tnode的情况的处理
1436         pn = (struct tnode *) n;
//该变量用于记录已经匹配到的比特数。
1437         chopped_off = 0;
1438 
1439         while (pn) {
1440                 pos = pn->pos;
1441                 bits = pn->bits;
1443                 if (!chopped_off)
//找到不同的bit,这是为了获得孩子节点。
1444                         cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length),
1445                                                    pos, bits);
//获得孩子节点,这个孩子节点可能是tnode也可能是leaf
1447                 n = tnode_get_child_rcu(pn, cindex);
//
1449                 if (n == NULL) {
1453                         goto backtrace;
1454                 }
//如果孩子节点是一个leaf节点,则调用check_leaf检查是否是需要的路由项,并将结果存放在res中。
1456                 if (IS_LEAF(n)) {
1457                         ret = check_leaf(tb, t, (struct leaf *)n, key, flp, res, fib_flags);
1458                         if (ret > 0)
1459                                 goto backtrace;
1460                         goto found;
1461                 }
//如果孩子节点是一个tnode,则需要进行迭代到其孩子进行上述查找过程。
1463                 cn = (struct tnode *)n;
//第一次进入该函数这里的current_prefix_length的长度是不会小于pos+bits的。
1494                 if (current_prefix_length < pos+bits) {
1495                         if (tkey_extract_bits(cn->key, current_prefix_length,
1496                                                 cn->pos - current_prefix_length)
1497                             || !(cn->child[0]))
1498                                 goto backtrace;
1499                 }
1532 
1533                 pref_mismatch = mask_pfx(cn->key ^ key, cn->pos);
//当根据pos和bits值没有找到搜索的key的话,进入前缀匹配模式。
/************************************************************************************************
***该模式存在意义如下:
***对于ipv4路由表有如下两项:
***192.168.20.16/28
***192.168.0.0/16
***如果需要查找192.168.20.19则上面两个都是匹配的,但是取哪个好呢?内核使用最常匹配原则,即认为192.168.20.16
***(子网掩码长度是28)这一项是匹配的,通常default 项的前缀是最短的,其作用是在其他路由项均不能匹配时会使用***default项*[摘自维基百科,longest prefix match],这个函数的chopped_off就是忽略prefix的长度,这样匹配成功的概率会***变大。对于图12.2.4的情况的trie树,查找192.168.0.100路由项的情况是不会进入backtrace标号开始的语句的。
**********************************************************************************************/
1540                 if (pref_mismatch) {
1541                         /* fls(x) = __fls(x) + 1 */
1542                         int mp = KEYLENGTH - __fls(pref_mismatch) - 1;
1543 
1544                         if (tkey_extract_bits(cn->key, mp, cn->pos - mp) != 0)
1545                                 goto backtrace;
1546 
1547                         if (current_prefix_length >= cn->pos)
1548                                 current_prefix_length = mp;
1549                 }
1550 
1551                 pn = (struct tnode *)n; /* Descend */
1552                 chopped_off = 0;
1553                 continue;
1554 
1555 backtrace:
1556                 chopped_off++;
1557 
1558                 /* As zero don't change the child key (cindex) */
1559                 while ((chopped_off <= pn->bits)
1560                        && !(cindex & (1<<(chopped_off-1))))
1561                         chopped_off++;
1562 
1563                 /* Decrease current_... with bits chopped off */
1564                 if (current_prefix_length > pn->pos + pn->bits - chopped_off)
1565                         current_prefix_length = pn->pos + pn->bits
1566                                 - chopped_off;
1567 
1568                 /*
1569                  * Either we do the actual chop off according or if we have
1570                  * chopped off all bits in this tnode walk up to our parent.
1571                  */
1572 
1573                 if (chopped_off <= pn->bits) {
1574                         cindex &= ~(1 << (chopped_off-1));
1575                 } else {
1576                         struct tnode *parent = node_parent_rcu((struct rt_trie_node *) pn);
1577                         if (!parent)
1578                                 goto failed;
1579 
1580                         /* Get Child's index */
1581                         cindex = tkey_extract_bits(pn->key, parent->pos, parent->bits);
1582                         pn = parent;
1583                         chopped_off = 0;
1593 found:
1594         rcu_read_unlock();
1595         return ret;
1596 }

回到ip_route_input_slow的1667行,在没有使用多路路由技术的情况下,只是对__mkroute_input()函数的封装。该函数用于为接收到的套接字数据创建路由项缓存。该函数的第二个参数res是前面查找的结果。

net/ipv4/route.c

  1. 1471 static int __mkroute_input(struct sk_buff *skb,  
  2. 1472                            const struct fib_result *res,  
  3. 1473                            struct in_device *in_dev,  
  4. 1474                            __be32 daddr, __be32 saddr, u32 tos)  
  5. 1475 {  
  6. 1476         struct rtable *rth;  
  7. 1477         int err;  
  8. 1478         struct in_device *out_dev;  
  9. 1479         unsigned int flags = 0;  
  10. 1480         bool do_cache;  
  11. 1481         u32 itag;  
  12. 1482   
  13. //该函数给了数据包的源地址、输入Interface以及目的地址、oif、tos;检查源地址的正确性,例如不能是广播地址和local地  
  14. //址,      
  15. 1490         err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res),  
  16. 1491                                   in_dev->dev, in_dev, &itag);  
  17. 1492         if (err < 0) {  
  18. 1493                 ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,  
  19. 1494                                          saddr);  
  20. 1495   
  21. 1496                 goto cleanup;  
  22. 1497         }  
  23. 1498   
  24. //创建一个dst_entry入口项,并将其赋值给rth,rtable的第一个字段就是指向dst_entry。该函数还对dst_entry进行了初始化。  
  25. 1530         rth = rt_dst_alloc(out_dev->dev,  
  26. 1531                            IN_DEV_CONF_GET(in_dev, NOPOLICY),  
  27. 1532                            IN_DEV_CONF_GET(out_dev, NOXFRM), do_cache);  
  28. 1533         if (!rth) {  
  29. 1534                 err = -ENOBUFS;  
  30. 1535                 goto cleanup;  
  31. 1536         }  
  32. //rtable相关字段初始化  
  33. 1538         rth->rt_genid = rt_genid(dev_net(rth->dst.dev));  
  34. 1539         rth->rt_flags = flags;  
  35. 1540         rth->rt_type = res->type;  
  36. 1541         rth->rt_is_input = 1;  
  37. 1542         rth->rt_iif     = 0;  
  38. 1543         rth->rt_pmtu    = 0;  
  39. 1544         rth->rt_gateway = 0;  
  40. 1545         rth->rt_uses_gateway = 0;  
  41. 1546         INIT_LIST_HEAD(&rth->rt_uncached);  
  42. 1547   
  43. 1548         rth->dst.input = ip_forward;  
  44. 1549         rth->dst.output = ip_output;  
  45. //rtable的最后一项是nexthop,这里设置rth的nexthop项,并设置路由缓存。  
  46. 1551         rt_set_nexthop(rth, daddr, res, NULL, res->fi, res->type, itag);  
  47. //将rtable的dst_entry入口项设置成skb的路由项。4.2节的路由内容至此结束。  
  48. 1552         skb_dst_set(skb, &rth->dst);  
  49. 1553 out:  
  50. 1554         err = 0;  
  51. 1555  cleanup:  
  52. 1556         return err;  
  53. 1557 }  
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值