原帖地址: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

[cpp] view plain copy print?
  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在网络层接收中分析过,这里会再一次看到在网络层被跳过的关于路由相关的代码,下面的代码片段就是上面统计信息被赋值的一个地方:

[cpp] view plain copy print?
  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

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

[cpp] view plain copy print?
  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

策略路由使用的结构。

[cpp] view plain copy print?
  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

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

[cpp] view plain copy print?
  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文件。

[cpp] view plain copy print?
  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文件中;

[cpp] view plain copy print?
  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

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

[cpp] view plain copy print?
  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
};

路由项别名:

[cpp] view plain copy print?
  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 可以添加。

[cpp] view plain copy print?
  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  路由表

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

路由子系统初始化:

[cpp] view plain copy print?
  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地址时的路由信息:

[cpp] view plain copy print?
  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. Pointers: 0
  11. Null ptrs: 0
  12. Total size: 0  kB
  13. Main:
  14. Aver depth:     0.00
  15. Max depth:      0
  16. Leaves:         0
  17. Prefixes:       0
  18. Internal nodes: 0
  19. Pointers: 0
  20. Null ptrs: 0
  21. 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.00Max depth:      0Leaves:         0Prefixes:       0Internal nodes: 0Pointers: 0
Null ptrs: 0
Total size: 0  kB
Main: Aver depth:     0.00Max depth:      0Leaves:         0Prefixes:       0Internal nodes: 0Pointers: 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操作就是依赖这些统计信息对树进行均衡的。

[cpp] view plain copy print?
  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# cat /proc/net/fib_triestat
  15. 16Basic info: size of leaf: 20 bytes, size of tnode: 28 bytes.
  16. 17Local:
  17. 18  Aver depth:     1.66
  18. 19  Max depth:      2
  19. 20  Leaves:         3
  20. 21  Prefixes:       3
  21. 22  Internal nodes: 2
  22. 23    1: 2
  23. 24  Pointers: 4
  24. 25Null ptrs: 0
  25. 26Total size: 1  kB
  26. 27Main:
  27. 28  Aver depth:     0.00
  28. 29  Max depth:      0
  29. 30  Leaves:         1
  30. 31  Prefixes:       1
  31. 32  Internal nodes: 0
  32. 33
  33. 34  Pointers: 0
  34. 35Null ptrs: 0
  35. 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 UNICAST15# 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文件中,并且在初始化时会被调用。

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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     else716         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     else726         cfg.fc_scope = RT_SCOPE_HOST;727 728     if (cmd == RTM_NEWROUTE)729         fib_table_insert(tb, &cfg);730     else731         fib_table_delete(tb, &cfg);732 }

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

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

include/net/ip_fib.h

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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);813814        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    } else826        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;848849        nla_for_each_attr(nla,cfg->fc_mx, cfg->fc_mx_len, remaining) {850            inttype = nla_type(nla);851852            if(type) {853                u32 val;854855                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_MULTIPATH869        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_CLASSID877        if(cfg->fc_flow && fi->fib_nh->nh_tclassid !=cfg->fc_flow)878            gotoerr_inval;879#endif880#else881        gotoerr_inval;882#endif883     } else{
//对下一跳的赋值,见图12.3.2。赋值的来源同样是用户空间。884        structfib_nh *nh = fi->fib_nh;885886        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_CLASSID890        nh->nh_tclassid= cfg->fc_flow;891        if(nh->nh_tclassid)892            fi->fib_net->ipv4.fib_num_tclassid_users++;893#endif894#ifdefCONFIG_IP_ROUTE_MULTIPATH895        nh->nh_weight= 1;896#endif897    }
//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;921922        /*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;972973        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没有必要在创建了,直接重复利用就好了。

[cpp] view plain copy print?
  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,这个过程还是很容易理解的。

[cpp] view plain copy print?
  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. //如果这个if语句满足,说明我们要插入的项已经存在这个路由表了,这是一次重复插入操作。
  37. 981    if (n != NULL && IS_LEAF(n) && tkey_equals(key,n->key))
  38. 982        return (struct leaf *)n;
  39. 983
  40. 984    return NULL;
  41. 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;961962    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;967968        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        } else977            break;978    }//如果这个if语句满足,说明我们要插入的项已经存在这个路由表了,这是一次重复插入操作。981    if (n != NULL && IS_LEAF(n) && tkey_equals(key,n->key))982        return (struct leaf *)n;983984    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。

[cpp] view plain copy print?
  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行,其它的读者自行完成。

[cpp] view plain copy print?
  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
//对于添加路由项,传递进来的值都是-1509     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方法完成网关的设置。

[cpp] view plain copy print?
  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. memcpy(&v4_rt.rtmsg_gateway, gateway, sizeof(struct in_addr));
  5. /*添加路由*/
  6. if (ioctl(sockfd, SIOCADDRT, &v4_rt) < 0)
  7. {
  8. SAFE_CLOSE(sockfd);
  9. BASEFUN_DBG(RT_ERROR, "add route ioctl error and errno=%d\n", errno);
  10. return -1;
  11. }
  12. 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开始的程序片段。

[cpp] view plain copy print?
  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                 else505                     err = -ESRCH;506             } else {507                 tb = fib_new_table(net, cfg.fc_table);508                 if (tb)509                     err = fib_table_insert(tb, &cfg);510                 else511                     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

[cpp] view plain copy print?
  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用于获得一个和用户匹配类型的路由表,如果在现有的路由表中没有要找的类型,则会创建这个类型的路由表。

[cpp] view plain copy print?
  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文件中:

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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初始化该路由表的各个字段。

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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_MULTIPATHint         fib_power;
#endifstruct 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结构体。

[cpp] view plain copy print?
  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、BROADCASTu8            fa_state;struct rcu_head        rcu;
};

fib_flowi4

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

[cpp] view plain copy print?
  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. /* (saddr,daddr) must be grouped, same order as in IP header */
  12. __be32          saddr; //源地址
  13. __be32          daddr;//目的地址
  14. union flowi_uli     uli;
  15. #define fl4_sport       uli.ports.sport //tcp层源端口号
  16. #define fl4_dport       uli.ports.dport//tcp层目的端口号
  17. #define fl4_icmp_type       uli.icmpt.type
  18. #define fl4_icmp_code       uli.icmpt.code
  19. #define fl4_ipsec_spi       uli.spi
  20. #define fl4_mh_type     uli.mht.type
  21. #define fl4_gre_key     uli.gre_key
  22. } __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

[cpp] view plain copy print?
  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

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

[cpp] view plain copy print?
  1. struct rtable {
  2. struct dst_entry    dst;
  3. int         rt_genid;
  4. unsigned int        rt_flags;
  5. __u16           rt_type;
  6. __u8            rt_is_input;
  7. __u8            rt_uses_gateway;
  8. int         rt_iif;
  9. /* Info on neighbour */
  10. __be32          rt_gateway;
  11. /* Miscellaneous cached information */
  12. u32         rt_pmtu;
  13. struct list_head    rt_uncached;
  14. };
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

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

[cpp] view plain copy print?
  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接收数据包进行路由寻址。

[cpp] view plain copy print?
  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。

[cpp] view plain copy print?
  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), false, false);
  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)。为了使函数的脉络看起来更为清晰,这里省去函数变量的定义、路由地址的合法性检查以及一些错误处理代码,只保留了正常情况下路由处理相关代码。

[cpp] view plain copy print?
  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行路由查找函数

[cpp] view plain copy print?
  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,查询的结果

[cpp] view plain copy print?
  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

[cpp] view plain copy print?
  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 }

第十二章 trie路由--基于Linux3.10相关推荐

  1. C++Primer第五版 第十二章习题答案(1~10)

    1:知识点1:对象生命周期:全局对象在程序启动时分配,在程序结束时销毁.局部自动对象,当我们进入其定义所在程序块时被创建,在离开块时被销毁.局部static对象在第一次使用前分配,在程序结束时销毁 知 ...

  2. 数字图像处理:第二十二章 基于模型的编码

    第二十二章基于模型的编码 目录 引言 基于对象的编码 基于模型的编码 作业 1. 引言 为了获得更高的压缩效率并保持一定的视频质量以支持超低码率(大约10kbps)双向视频应用,不考虑内容特点的仅仅针 ...

  3. 【正点原子FPGA连载】 第三十二章基于lwip的TCP服务器性能测试实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南

    第三十二章基于lwip的TCP服务器性能测试实验 上一章的lwip Echo Server实验让我们对lwip有一个基本的了解,而Echo Server是基于TCP协议的.TCP协议是为了在不可靠的互 ...

  4. 系统架构师学习笔记_第十二章_连载

    第十二章  系统安全架构设计 12.1  信息系统安全架构的简单描述 信息安全的特征 是为了保证信息的 机密性.完整性.可用性.可控性.不可抵赖性. 以风险策略为基础. 12.1.1  信息安全的现状 ...

  5. 《深入理解 Spring Cloud 与微服务构建》第十二章 服务注册和发现 Consul

    <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发现 Consul 文章目录 <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发 ...

  6. 高级shell编程笔记(第十二章 外部过滤器,程序和命令)

    第十二章 外部过滤器,程序和命令 标准的UNIX命令使得脚本更加灵活.通过简单的编程结构把shell指令和系统命令结合起来,这才是脚本能力的所在. 12.1 基本命令 新手必须掌握的初级命令 ls 基 ...

  7. 第十二章 Django框架

    第十二章 Django框架 tcp/ip五层模型 应用层 传输层 网络层 数据链路层 物理层 socket : 套接字,位于应用层和传输层之间的虚拟层,是一组接口 c/s架构 ------> b ...

  8. 【译】 WebSocket 协议第十二章——使用其他规范中的WebSocket协议

    概述 本文为 WebSocket 协议的第十二章,本文翻译的主要内容为如何使用其他规范中的 WebSocket 协议. 使用其他规范中的WebSocket协议(协议正文) WebSocket协议旨在由 ...

  9. 鸟哥的Linux私房菜(服务器)- 第十二章、网络参数控管者: DHCP 服务器

    第十二章.网络参数控管者: DHCP 服务器 最近更新日期:2011/07/27 想象两种情况:(1)如果你在工作单位使用的是笔记本电脑,而且常常要带着你的笔记本电脑到处跑, 那么由第四章.连上 In ...

  10. 数字图像处理:第十二章 小波变换

    第十二章 小波变换 目录 1         引言 2         连续小波变换 3         二进小波变换 3.1      Haar变换 4         离散小波变换 4.1     ...

最新文章

  1. solr搭建分布式搜索引擎
  2. C#基础知识整理:基础知识(1) Main方法
  3. Chapter 4, FAQ about Master Theorm, exercises and problems
  4. 水晶报表中对某一栏位值进行处理_【节能学院】能耗管理系统在某超市嘉兴店二期工程的设计与应用...
  5. 监控系统之weblogic,apache,tuxedo的监控项的整理
  6. ArrayList源码解析
  7. PHP+MYSQL图书管理系统(课设)
  8. HTML注册页面代码
  9. 北京租房住起来舒服的地方(小区)有哪些?
  10. ubuntu20.04截图快捷键
  11. QlikView 学习资料
  12. 红米手机4开启root超级权限的步骤
  13. 【PIL处理图片】小技巧之画虚线、加粗字体、长文本自动分行(符号处理)
  14. oracle 考勤记录表,基于C#+Oracle的考勤管理系统的设计与开发_.doc
  15. 阿里云code登录,阿里云code使用教程
  16. MSTP:多生成树协议
  17. 图解:手机控制电脑的软件的使用教程
  18. 常用的清除浮动的方法
  19. 北方工业大学计算机专硕分数线,北方工业大学法硕:近三年分数线汇总,一分钟速了解详细...
  20. 物联网系统的搭建(五)

热门文章

  1. php_curl模拟登录有验证码实例
  2. 400,404,500报错页面总结
  3. adb.exe可能被其他程序关闭_苹果说关闭后台 反而会缩短电池寿命?!
  4. C#操作十六进制数据以及十进制与十六进制互相转换
  5. Kotlin 1.3带来稳定的协程、合约及其他
  6. 渗透场景篇--当XSS遇上CSRF
  7. javascript的window.open()具体解释
  8. 360换机 v2.12.5.9 官方安卓版
  9. 2015-2016-2 《Java程序设计》项目小组博客
  10. 十一月份英语学习总结—积累