精华内容
下载资源
问答
  • 迈普学习总结 经过在公司里学习了几个月把大体的工作总结于下 在参与1800-20 3G 路由的开发中我参与了l2tp,gre静态路由,ipsec日志关键信息提取的编写并同时参与了ipsec-tools源码linuxkernel 网络协议栈源码l2tpd...
  • 从现在开始学习路由相关的代码,在分析代码之前,我们还是先分析数据结构,把数据结构之间的关系理解了以后,再理解代码就相对轻松多了。本小节先分析路由相关的数据结构。内核里面大多模块定义的数据结构之间一般都...

    http://blog.csdn.net/lickylin/article/details/38326719

     

    从现在开始学习路由相关的代码,在分析代码之前, 我们还是先分析数据结构,把数据结构之间的关系理解了以后,再理解代码就相对轻松多了。本小节先分析路由相关的数据结构。内核里面大多模块定义的数据结构之间一般都是使用链表或者hash表实现连接操作。

    对于路由表,相关的数据结构有fib_table、fn_hash、fn_zone、fib_node、fib_alias、fib_info、fib_nh等, 下面分别介绍这几个数据结构

     

    路由表结构,该结构为一个路由表的抽象,包括路由表的id、路由添加函数、路由查找函数、路由删除函数等

     

    1. struct fib_table {  
    2. /*使用hash链表将多个路由表变量链接在一起*/  
    3. struct hlist_node tb_hlist;  
    4. /*表id*/  
    5. u32  tb_id;  
    6. unsigned    tb_stamp;  
    7. /*路由查找函数*/  
    8. int  (*tb_lookup)(struct fib_table *tb, const struct flowi *flp, struct fib_result *res);  
    9. /*路由插入函数*/  
    10. int  (*tb_insert)(struct fib_table *, struct fib_config *);  
    11. /*路由项删除函数*/  
    12. int  (*tb_delete)(struct fib_table *, struct fib_config *);  
    13. int  (*tb_dump)(struct fib_table *table, struct sk_buff *skb,  
    14.      struct netlink_callback *cb);  
    15. /*清空路由表的规则*/  
    16. int  (*tb_flush)(struct fib_table *table);  
    17. void     (*tb_select_default)(struct fib_table *table,  
    18.      const struct flowi *flp, struct fib_result *res);  
    19.    
    20. /*可变长数组,主要是用来指向掩码相关的hash数组*/  
    21. unsigned char   tb_data[0];  
    22. };  



     

     

    该结构主要用于描述以掩码划分的区域结构以及掩码区域之间的关系

    对于ipv4来说,掩码可以为0-32共33种可能,因此对于一个fn_hash来说,则

    定义了一个包含33个fn_zone的数组,而链表fn_zone_list,主要是将一个路由表里的

    fn_zone链接在一起(以掩码大小的顺序排列,主要是在路由查找时,先匹配最长掩码对应的路由,以提高路由匹配的精确度)。

     

     

    1. struct fn_hash {  
    2. struct fn_zone  *fn_zones[33];  
    3. struct fn_zone  *fn_zone_list;  
    4. };  



     

     

    以掩码划分的区域结构抽象,将掩码长度相同的所有路由放在同一个fn_zone中hash表中。

     

    1. struct fn_zone {  
    2. /*指向下一个非空的fn_zone*/  
    3. struct fn_zone   *fz_next;  
    4.    
    5. /*指向路由项关联的hash链表,对于掩码相关的目的ip地址,根据目的网络地址 
    6. 的hash值,将新的fn_node节点插入到相应的hash链表中*/  
    7. struct hlist_head   *fz_hash;   /* Hash table pointer   */  
    8.    
    9. /*fn_node结构变量的个数,fn_node并不能说是对一个路由项的抽象,可以看成对一个 
    10. 目的网络地址的抽象,而对于相同的目的网络地址,可以根据tos、priority、mark值创建 
    11. 不同的路由,所以一个fn_node结构中fn_alias才能算作是对一个路由项的抽象*/  
    12. int  fz_nent;   /* Number of entries    */  
    13.    
    14. /*该fn_zone中fz_hash链表的个数*/  
    15. int  fz_divisor;    /* Hash divisor  */  
    16. /*fz_hash的mask值*/  
    17. u32  fz_hashmask;   /* (fz_divisor - 1) */  
    18. #define FZ_HASHMASK(fz)  ((fz)->fz_hashmask)  
    19.    /*以上两个值用于fz_hash数组的容量扩充相关*/  
    20.    
    21. /*该fn_zone对应的掩码长度*/  
    22. int  fz_order;  /* Zone order    */  
    23. /*根据fz_order而得到的掩码值*/  
    24. __be32   fz_mask;  
    25. /*获取该fn_zone对应的掩码值*/  
    26. #define FZ_MASK(fz)  ((fz)->fz_mask)  
    27. };  



     

     

     

     

     

    抽象为一个目的网络地址相同的所有路由项的基础结构,其中的fn_alias,表示该结构所包含的已存在的路由项的链表,fn_key为该结构对应的目的网络地址值,

    用于和掩码长度相同的其他fib_node区分,对于同一个fn_zone里的fib_node,都链接到fn_zone->fz_hash中相应的hash表中

     

     

    1. struct fib_node {  
    2.     /*用于将hash值相同,且目的网络地址值不同,且网络掩 
    3.     码相同的fib_node变量链接在一起*/  
    4. struct hlist_node   fn_hash;  
    5. struct list_head    fn_alias;  
    6. __be32   fn_key;  
    7. };  



     

     

     

    该结构可以理解为一个路由项的抽象。

    当路由项的目的网络地址相同时,可以根据这个结构变量区分不同的路由项。包括tos、type、scope、state以及fib_info来区分一个路由项

     

    方法:

    1.首先根据tos、type、scope等确定一个fib_alias

    2.当fib_alias确定以后,再根据priority等值确定一个fib_info,

    3.根据fib_info确定出口设备与下一跳网关的ip地址。

     

    1. struct fib_alias {  
    2. struct list_head    fa_list;  
    3. struct rcu_head rcu;  
    4. struct fib_info  *fa_info;  
    5. u8   fa_tos;  
    6. u8   fa_type;  
    7. u8   fa_scope;  
    8. u8   fa_state;  
    9. };  



     

    而fa_scope表示路由的scope,取值范围如下:

    RT_SCOPE_UNIVERSE:该选项用于所有通向非直连目的地的路由表项,即

                                  应用层创建的路由中包含via的路由                       

    RT_SCOPE_LINK:该选项用于目的地址为本地网络的路由项

    RT_SCOPE_HOST:该选项用于路由为本机接口,

    RT_SCOPE_NOWHERE:该选项用于路由不可到达                                

     

    1. enum rt_scope_t  
    2. {  
    3. RT_SCOPE_UNIVERSE=0,  
    4. /* User defined values  */  
    5. RT_SCOPE_SITE=200,  
    6. RT_SCOPE_LINK=253,  
    7. RT_SCOPE_HOST=254,  
    8. RT_SCOPE_NOWHERE=255  
    9. };  



     

     

     

     

     

     

    功能:主要是用来获取出口设备以及下一跳网关的数据结构,以及路由项的优先级,路由创建协议fib_protocol(RTPROTO_KERNEL、RTPROTO_BOOT、RTPROTO_STATIC等取值)。而fib_hash与fib_lhash则是将fib_info链接到对应的hash链表fib_info_hash[]与fib_info_laddrhash[]中去的。

     

    1. struct fib_info {  
    2. /*这两个指针用于将该fib_info链接到hash链表中*/  
    3. struct hlist_node   fib_hash;  
    4. struct hlist_node   fib_lhash;  
    5. int  fib_treeref;  
    6. atomic_t     fib_clntref;  
    7. /*标识是否将要释放该fib_info变量*/  
    8. int  fib_dead;  
    9. unsigned     fib_flags;  
    10. int  fib_protocol;  
    11. __be32   fib_prefsrc;  
    12. /*优先级*/  
    13. u32  fib_priority;  
    14. u32  fib_metrics[RTAX_MAX];  
    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. /*fib_nh变量的个数*/  
    20. int  fib_nhs;  
    21. #ifdef CONFIG_IP_ROUTE_MULTIPATH  
    22. int  fib_power;  
    23. #endif  
    24. #ifdef CONFIG_IP_ROUTE_MULTIPATH_CACHED  
    25. u32  fib_mp_alg;  
    26. #endif  
    27.     /*可变长度数组,用于动态申请fib_nh内存空间*/  
    28. struct fib_nh    fib_nh[0];  
    29. #define fib_dev  fib_nh[0].nh_dev  
    30. };  



     

     

     

     

     

    包含下一跳网关及出口设备的结构

     

    1. struct fib_nh {  
    2.     /*数据包输出设备接口*/  
    3. struct net_device   *nh_dev;  
    4. /*将fib_nh变量链接在hash链表中*/  
    5. struct hlist_node   nh_hash;  
    6. /*包含该变量的fib_info变量*/  
    7. struct fib_info  *nh_parent;  
    8. unsigned     nh_flags;  
    9. unsigned char    nh_scope;  
    10. #ifdef CONFIG_IP_ROUTE_MULTIPATH  
    11. int  nh_weight;  
    12. int  nh_power;  
    13. #endif  
    14. #ifdef CONFIG_NET_CLS_ROUTE  
    15. __u32    nh_tclassid;  
    16. #endif  
    17. /*出口设备的index*/  
    18. int  nh_oif;  
    19. /*下一跳网关地址*/  
    20. __be32   nh_gw;  
    21. };  



     

    而nh_hash是用来将fib_nh变量链接到对应的fib_info_devhash[]链表中的。

     

    以上就是相应的数据结构分析,下面是这些数据结构之间的逻辑关系。没有给出fib_info_hash[]、fib_info_laddrhash[]与fib_info之间的,也没有给出fib_nh与fib_info_devhash之间的关系

     

     

    以上就是路由相关的数据结构,下一节开始分析路由的添加与删除等功能。以上分析的数据结构没有包含路由缓存相关的数据结构,等到介绍到路由缓存时再进行分析。

     

    转载于:https://www.cnblogs.com/virusolf/p/4358336.html

    展开全文
  • Linux路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解,如果要想掌握的话估计还得再花点时间阅读代码,先把框架记录下来。路由子系统可以划分为三个子部分,...

    内核版本:3.4.39

    Linux路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解,如果要想掌握的话估计还得再花点时间阅读代码,先把框架记录下来。路由子系统可以划分为三个子部分,路由缓存,路由策略和路由表,前两者已经总结过了,今天再总结下路由表。路由表和其它模块类似,都有初始化、添加、删除、查询等操作,要说区别吧,可能是数据结构组织不一样,不同的数据结构需要不同的算法。

    看《深入理解Linux网络技术内幕》这本书路由子模块这一部分,它介绍的路由表是基于hash表来组织,但是新版本的内核这一块已经改成lpc-trie树来组织,lpc-trie树,网上简称字典树,lpc表示path compression(路径压缩), level compression(平面压缩),路由表的添加、删除、查找都是基于该树实现,具体的实现还是蛮复杂的,先看下它的组织图:

    上图左边部分fib_table_hash就表示路由表hash数组,hash值就是路由表ID,每个路由表都由一个fib_table结构体表示,这个结构体尾部存放一个占位指针,用来指向路由trie树,树种由很多中间节点和叶子节点,中间节点的结构体为tnode,叶子节点为leaf, 无论是中间节点还是叶子节点,都含有一个key值,该值即为ipv4地址,同一条路径上的节点拥有相同的前缀,比如1.1.1.1和1.1.1.2,leaf_info包含了子网掩码长度,fib_alias包含了路由项里面的tos等信息,fib_alias指向fib_info,这里面也包含了路由信息,fib_nh用来保存下一跳网关信息,可以看到,一个路由项由多个数据结构组成,之所以用这么多结构体而不是用一个超大的结构体是因为路由里面很多信息是可以共用的,比如说相同的下一跳等等,考虑到大型骨干路由器路由表项可以达到数万到数以百万,如果每个路由项都要一个大结构体的话,估计内存有点紧张,不如将路由项分割成多个块,相同的块可以共享,有一点需要注意,每个路由项都有一个唯一的fib_alias结构体。

    路由表初始化流程就是申请缓存、注册netlink消息处理函数:

    路由初始化主要函数是ip_fib_init():

    void __init ip_fib_init(void)
    {
    	//注册netlink路由添加、删除和dump命令处理函数
    	rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
    	rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
    	rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
    
    	//初始化路由表和路由缓存
    	register_pernet_subsys(&fib_net_ops);
    
    	//注册通知链处理函数,监听系统其它模块信息
    	register_netdevice_notifier(&fib_netdev_notifier);
    	register_inetaddr_notifier(&fib_inetaddr_notifier);
    
    	//初始化路由用到的缓存池
    	fib_trie_init();
    }
    

    当使用ip route add添加路由时会通过netlink将信息下发下来,然后调用路由系统注册的netlink处理函数,这里是inet_rtm_newroute,该函数即对下发参数进行合理性检查,检查通过则添加到对应的trie路由树中,没指定路由表id的话,默认添加到main表。

    fib_net_ops是个函数集,在子系统启动的过程中会被调用:

    static struct pernet_operations fib_net_ops = {
    	.init = fib_net_init,
    	.exit = fib_net_exit,
    };
    

    fib_net_init是启动过程中的处理函数,主要申请路由表缓存:

    static int __net_init fib_net_init(struct net *net)
    {
    	int error;
    
    	//初始化路由缓存和策略
    	error = ip_fib_net_init(net);
    	if (error < 0)
    		goto out;
    
    	//创建netlink
    	error = nl_fib_lookup_init(net);
    	if (error < 0)
    		goto out_nlfl;
    
    	//初始化proc文件
    	error = fib_proc_init(net);
    	if (error < 0)
    		goto out_proc;
    out:
    	return error;
    
    out_proc:
    	nl_fib_lookup_exit(net);
    out_nlfl:
    	ip_fib_net_exit(net);
    	goto out;
    }

     路由表缓存的申请是ip_fib_net_init函数:

    //创建路由表缓存和默认策略或者默认路由表
    static int __net_init ip_fib_net_init(struct net *net)
    {
    	int err;
    	size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ;
    
    	/* Avoid false sharing : Use at least a full cache line */
    	size = max_t(size_t, size, L1_CACHE_BYTES);
    
    	//创建路由表缓存,
    	net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL);
    	if (net->ipv4.fib_table_hash == NULL)
    		return -ENOMEM;
    
    	//初始化策略路由和路由表
    	err = fib4_rules_init(net);
    	if (err < 0)
    		goto fail;
    	return 0;
    
    fail:
    	kfree(net->ipv4.fib_table_hash);
    	return err;
    }

    上述就是路由表初始化的过程

    看下路由表是怎么添加的,一般情况下应用层添加路由有两种手段,一种是使用ip route添加,另一种是使用route添加,虽然都是添加路由,但是它俩和路由系统通信机制不一样,前者使用netlink,后者使用ioctl。看下ip route的添加,当调用ip route命令的时候,该命令会将参数通过netlink传递给内核的netlink模块,然后调用相应的事件处理函数,添加的时候调用的是inet_rtm_newroute:

    //添加路由
    static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
    {
    	struct net *net = sock_net(skb->sk);
    	struct fib_config cfg;
    	struct fib_table *tb;
    	int err;
    
    	//将用户层配置信息转换成fib_config内核可识别的信息
    	err = rtm_to_fib_config(net, skb, nlh, &cfg);
    	if (err < 0)
    		goto errout;
    
    	//如果指定ID的路由表存在则返回该表,不存在则新建
    	tb = fib_new_table(net, cfg.fc_table);
    	if (tb == NULL) {
    		err = -ENOBUFS;
    		goto errout;
    	}
    
    	//插入路由
    	err = fib_table_insert(tb, &cfg);
    errout:
    	return err;
    }

    该函数首先是将应用层下发的信息转换成一个标准的配置结构体里面,然后检查指定的路由表是否存在,最终调用fib_table_insert来添加路由。

     ioctl添加基本上和netlink相同,除了通信机制的不同,但是对于路由表的操作都是相同的接口:

    int ip_rt_ioctl(struct net *net, unsigned int cmd, void __user *arg)
    {
    	struct fib_config cfg;
    	struct rtentry rt;
    	int err;
    
    	switch (cmd) {
    	//添加路由
    	case SIOCADDRT:		/* Add a route */
    
    	//删除路由
    	case SIOCDELRT:		/* Delete a route */
    		if (!capable(CAP_NET_ADMIN))
    			return -EPERM;
    
    		//复制应用层数据
    		if (copy_from_user(&rt, arg, sizeof(rt)))
    			return -EFAULT;
    
    		rtnl_lock();
    
    		//将应用层数据转换成路由子系统可识别的结构体
    		err = rtentry_to_fib_config(net, cmd, &rt, &cfg);
    		if (err == 0) {
    			struct fib_table *tb;
    
    			if (cmd == SIOCDELRT) {
    				//删除操作
    				tb = fib_get_table(net, cfg.fc_table);
    				if (tb)
    					err = fib_table_delete(tb, &cfg);
    				else
    					err = -ESRCH;
    			} else {
    				//添加操作
    				tb = fib_new_table(net, cfg.fc_table);
    				if (tb)
    					err = fib_table_insert(tb, &cfg);
    				else
    					err = -ENOBUFS;
    			}
    
    			/* allocated by rtentry_to_fib_config() */
    			kfree(cfg.fc_mx);
    		}
    		rtnl_unlock();
    		return err;
    	}
    	return -EINVAL;
    }

     可以看到添加操作都是调用fib_table_insert操作,该操作就是对trie路由树进行添加和删除处理。

    初始化和配置看完了,看下查询是怎么回事。

    系统查询路由通常由两个地方,一个是收到报文的时候,另一个是发送报文的时候。 

    当然查找路由不一定非要查询路由表,首先是查找路由缓存,没有命中的话则查询策略路由,根据策略路由动作再来查询路由表

    从上图可以看到,收发报文最终都是调用fib_table_lookup函数来查找路由表,这个函数就是在trie树中查找匹配的路由项。查找流程还是蛮复杂的,应该说关于trie树的操作都是有点难度,无论是插入还是查询,这一块我目前还没有完全搞清楚,待后续有足够的实力再来讲一下trie树的操作。有兴趣的同学可以去参考下trie树的一篇论文,路由表的实现是参考该论文的,链接放在参考目录里。

     

    参考目录:

    1. 《Linux Kernel Networking -  Implementation and Theory》

    2. 《深入理解Linux网络技术内幕》

    3.  《Implementing a dynamic compressed trie》   https://pdfs.semanticscholar.org/e880/05c8801983758917bf6e647da97f1027c86b.pdf

     

    展开全文
  • 也分析了这么多的内核代码,基本上对一个功能模块的几个主要内容也算是比较熟悉了。对于我们开发而言,一定要重视数据结构,当我们分析新的子功能或者开发新的子功能时,一定要好好的构思数据结构,因为数据结构的...

    前面分析路由查找时,已经捎带提到了策略规则,本节开始就要全面分析策略规则的内容了。也分析了这么多的内核代码,基本上对一个功能模块的几个主要内容也算是比较熟悉了。对于我们开发而言,一定要重视数据结构,当我们分析新的子功能或者开发新的子功能时,一定要好好的构思数据结构,因为数据结构的好坏,某种程度上就决定了代码的好坏。

    因此在开始分析策略规则的子模块时,照例需要先分析策略规则相关的数据结构。

    策略规则起到什么作用呢?

    添加策略规则,主要是用来和路由表一起作用,实现策略路由的功能。简而言之,就是输入或输出的数据包,通过策略规则,通过指定的路由表的路由项而把数据从相应的接口发送出去,从而实现策略路由的功能。

     

    1.数据结构

     

    1.1 fib_rule

    该数据结构即为抽象一个策略规则,下面分析下这个数据结构

    /*

    策略规则对应的数据结构

    */

    struct fib_rule

    {

    /*将策略规则链接在一起*/

    struct list_head list;

    /*引用计数*/

    atomic_t refcnt;

    /*接口的index*/

    int ifindex;

    /*接口的名称*/

    char ifname[IFNAMSIZ];

    /*mark值以及mark的掩码值*/

    u32 mark;

    u32 mark_mask;

    /*优先级,值越小优先级越大*/

    u32 pref;

    u32 flags;

    /*路由表的id*/

    u32 table;

    /*fib ruleaction规则,包括FR_ACT_TO_TBL*/

    u8 action;

    struct rcu_head rcu;

    };

     

    对于一个策略规则来说,肯定需要与其他的策略规则连接在一起,此处则是使用链表来实现的。接着还需要有引用计数以及action,指示匹配该策略规则后的下一步操作是什么;还有接口名称、接口indexmark值、路由表id(这个才是最主要的)等。

     

    1.2 fib_rules_ops

    该数据结构是策略规则中协议相关的操作函数的结构体,对于v4v6,其相应的处理函数会有所不同

     

    /*

    策略规则的操作相关的数据结构

     

    */

    struct fib_rules_ops

    {

    /*对应的协议簇,对于ipv4AF_INET*/

    int family;

    /*主要是将注册到系统的fib_rules_ops链接到链表rules_ops*/

    struct list_head list;

    /*一个策略规则所占用的内存大小*/

    int rule_size;

    /*协议相关的地址的长度*/

    int addr_size;

    /*协议相关的action函数,即是策略规则匹配后,所调用的action函数,执行后续的操作,一般是获取到相应的路由表,查找符合要求的路由项*/

    int (*action)(struct fib_rule *,

      struct flowi *, int,

      struct fib_lookup_arg *);

    /*协议相关的规则匹配函数,对于策略规则的匹配,首先是通用匹配,待通用匹配完成后,则会调用该函数,进行协议相关参数(源、目的地址等)的匹配*/

    int (*match)(struct fib_rule *,

     struct flowi *, int);

    /*协议相关的配置函数*/

    int (*configure)(struct fib_rule *,

         struct sk_buff *,

         struct nlmsghdr *,

         struct fib_rule_hdr *,

         struct nlattr **);

    int (*compare)(struct fib_rule *,

       struct fib_rule_hdr *,

       struct nlattr **);

    int (*fill)(struct fib_rule *, struct sk_buff *,

    struct nlmsghdr *,

    struct fib_rule_hdr *);

    u32 (*default_pref)(void);

    size_t (*nlmsg_payload)(struct fib_rule *);

    /*下面两个是netlink相关的参数*/

    int nlgroup;

    struct nla_policy *policy;

    /*链表用于将该协议簇已添加的所有fib_rule规则链接在一起*/

    struct list_head *rules_list;

     

    struct module *owner;

    };

     

    1.3 策略规则的action类型

     

    /*fib ruleaction类型,FR_ACT_TO_TBL即该fib rule与路由表关联*/

    enum

    {

    FR_ACT_UNSPEC,

    FR_ACT_TO_TBL, /* Pass to fixed table */

    FR_ACT_RES1,

    FR_ACT_RES2,

    FR_ACT_RES3,

    FR_ACT_RES4,

    FR_ACT_BLACKHOLE, /* Drop without notification */

    FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */

    FR_ACT_PROHIBIT, /* Drop with EACCES */

    __FR_ACT_MAX,

    };

    我们一般用到的是FR_ACT_TO_TBL,即匹配后的规则,即会进入到相应的路由表,继续进行路由项的匹配。

    1.4 fib4_rule

     

    ipv4协议相关的fib rule结构,该结构包含了

    struct fib_rule 类型的成员变量,同时增加了源ip地址、目的

    ip地址、tosipv4相关的成员判断

     

    struct fib4_rule

    {

    struct fib_rule common;

    u8 dst_len;

    u8 src_len;

    u8 tos;

    __be32 src;

    __be32 srcmask;

    __be32 dst;

    __be32 dstmask;

    #ifdef CONFIG_NET_CLS_ROUTE

    u32 tclassid;

    #endif

    };

     

    2.策略规则的初始化

    对于策略规则的初始化,主要是包括两个方面

    通用策略规则的初始化

    协议相关的策略规则的初始化

     

    2.1 通用策略规则的初始化

    对于通用策略规则,主要是就是注册通知链而已。

    /*

    注册通知链

    */

    static int __init fib_rules_init(void)

    {

    return register_netdevice_notifier(&fib_rules_notifier);

    }

     

    static struct notifier_block fib_rules_notifier = {

    .notifier_call = fib_rules_event,

    };

     

    该通知链仅处理NETDEV_REGISTERNETDEV_UNREGISTER这两个事件通知,主要是在设备注册时,即是遍历策略规则,对于匹配的规则,则会将该策略规则的ifindex进行赋值;而当设备注销时,则会遍历策略规则,对于匹配的规则,则会将该策略规则的ifindex的值设置为-1

    static int fib_rules_event(struct notifier_block *this, unsigned long event,

        void *ptr)

    {

    struct net_device *dev = ptr;

    struct fib_rules_ops *ops;

     

    ASSERT_RTNL();

    rcu_read_lock();

     

    switch (event) {

    case NETDEV_REGISTER:

    list_for_each_entry(ops, &rules_ops, list)

    attach_rules(ops->rules_list, dev);

    break;

     

    case NETDEV_UNREGISTER:

    list_for_each_entry(ops, &rules_ops, list)

    detach_rules(ops->rules_list, dev);

    break;

    }

     

    rcu_read_unlock();

     

    return NOTIFY_DONE;

    }

     

    2.2 协议相关的策略规则的初始化

    本文以ipv4为主,此处就分析ipv4相关的策略规则的的初始化

    2.2.1 fib4_rules_init

    这个函数主要也就是实现两个功能,增加localmaindefault3个策略规则,并添加到fib4_rules_ops.rules_list中;然后就是将fib4_rules_ops注册到系统中,添加到链表rules_ops

     

    功能:ipv4协议的fib rule的初始化函数

    1.分别将默认的3fib rule规则添加到全局链表fib4_rules的链尾

    2.调用函数fib_rules_registeripv4对应的fib 规则的操作变量fib4_rules_ops添加

       到系统的链表rules_ops

    void __init fib4_rules_init(void)

    {

    list_add_tail(&local_rule.common.list, &fib4_rules);

    list_add_tail(&main_rule.common.list, &fib4_rules);

    list_add_tail(&default_rule.common.list, &fib4_rules);

     

    fib_rules_register(&fib4_rules_ops);

    }

     

    2.2.2 localmaindefault等默认规则的定义

    默认创建3fib_rule规则,而fib rule规则添加到相应协议簇

    fib_rules_opslist链表中时,是根据pref的优先级来进行添加

    到,pref的值越小,而优先级越大。

    而在ipv4fib rule的初始化中,首先建立了default_rulemain_rulelocal_rule

    并分别添加到fib4_rules_ops.list中,因此以后添加的ipv4 fib rule规则,即使

    优先级最大,也是在local_rule规则之后。

     

    因此,在使用策略路由查找时,首先就会匹配local_rule规则,即进入

    local路由表中进行路由匹配,其次才会匹配其他的fib rule规则

     

    以下3个默认ipv4 fib rule是没有设置匹配条件的,即只要遍历到

    下面3ipv4 fib rule规则,即会匹配。

     

    static struct fib4_rule default_rule = {

    .common = {

    .refcnt = ATOMIC_INIT(2),

    .pref = 0x7FFF,

    .table = RT_TABLE_DEFAULT,

    .action = FR_ACT_TO_TBL,

    },

    };

     

    static struct fib4_rule main_rule = {

    .common = {

    .refcnt = ATOMIC_INIT(2),

    .pref = 0x7FFE,

    .table = RT_TABLE_MAIN,

    .action = FR_ACT_TO_TBL,

    },

    };

     

    static struct fib4_rule local_rule = {

    .common = {

    .refcnt = ATOMIC_INIT(2),

    .table = RT_TABLE_LOCAL,

    .action = FR_ACT_TO_TBL,

    .flags = FIB_RULE_PERMANENT,

    },

    };

    2.2.3 ipv4相关的fib_rule_ops的定义以及注册

    ipv4对应的struct fib_rules_ops 变量,其中为ipv4 fib rulematchactionconfigurecompare、等函数指针进行赋值;规定了ipv4fib rule链表为fib4_rules

     

    static struct fib_rules_ops fib4_rules_ops = {

    .family = AF_INET,

    .rule_size = sizeof(struct fib4_rule),

    .addr_size = sizeof(u32),

    .action = fib4_rule_action,

    .match = fib4_rule_match,

    .configure = fib4_rule_configure,

    .compare = fib4_rule_compare,

    .fill = fib4_rule_fill,

    .default_pref = fib4_rule_default_pref,

    .nlmsg_payload = fib4_rule_nlmsg_payload,

    .nlgroup = RTNLGRP_IPV4_RULE,

    .policy = fib4_rule_policy,

    .rules_list = &fib4_rules,

    .owner = THIS_MODULE,

    };

     

     

    既然讲到了fib_rule_ops的注册,那就分析下相应的注销与注册函数吧,这两个函数也是通用的函数,因此其定义是放在通用策略规则相关的fib_rule.c

    2.2.3.1 fib_rules_register

    功能:fib_rules_ops注册到全局链表rules_ops中去

    1.对传入的struct fib_rules_ops变量的成员进行合理性检查包括rule_size是否符合要求,matchconfigurecompare等函数指针是否为NULL

    2.只有符合1中的合理性检查后,才会将传入的struct fib_rules_ops变量添加到

        全局链表rules_ops

    int fib_rules_register(struct fib_rules_ops *ops)

    {

    int err = -EEXIST;

    struct fib_rules_ops *o;

     

    if (ops->rule_size < sizeof(struct fib_rule))

    return -EINVAL;

     

    if (ops->match == NULL || ops->configure == NULL ||

        ops->compare == NULL || ops->fill == NULL ||

        ops->action == NULL)

    return -EINVAL;

     

    spin_lock(&rules_mod_lock);

    /*判断全局链表rules_ops中是否存在相同协议簇的struct fib_rules_ops变量,

    若存在,则不再添加,程序返回*/

    list_for_each_entry(o, &rules_ops, list)

    if (ops->family == o->family)

    goto errout;

     

    list_add_tail_rcu(&ops->list, &rules_ops);

    err = 0;

    errout:

    spin_unlock(&rules_mod_lock);

     

    return err;

    }

     

    2.2.3.2 fib_rules_unregister

    功能:fib_rules_ops从全局链表rules_ops中删除,并删除该fib_rules_opsdrules_list链表中的所有fib rule规则(这主要是通过函数cleanup_ops实现)

     

    int fib_rules_unregister(struct fib_rules_ops *ops)

    {

    int err = 0;

    struct fib_rules_ops *o;

     

    spin_lock(&rules_mod_lock);

    list_for_each_entry(o, &rules_ops, list) {

    if (o == ops) {

    list_del_rcu(&o->list);

    cleanup_ops(ops);

    goto out;

    }

    }

     

    err = -ENOENT;

    out:

    spin_unlock(&rules_mod_lock);

     

    synchronize_rcu();

     

    return err;

    }

     

     

    至此,将策略规则的初始化基本分析完了,后面开始分析策略规则的添加、删除、查找等功能。

    展开全文
  • Linux中ipv6代码阅读(3)

    千次阅读 2009-01-09 16:46:00
    路由模块的处理1. 路由节点结构是fib6_node的结构,通过这个结构来组织成一棵路由树。这个结构主要是用来组织路由结构树的,具体的路由信息是存放在fib6_node->leaf结构中,这是一个rt6_info的结构体。每个fib6_node...

    三.路由模块的处理

    1. 路由节点结构是fib6_node的结构,通过这个结构来组织成一棵路由树。这个结构主要是用来组织路由结构树的,具体的路由信息是存放在fib6_node->leaf结构中,这是一个rt6_info的结构体。每个fib6_node伴随着一个rt6_info。查找路由的时候,遍历整个路由树,根据每个fib6_node节点的rt6_info信息,判断是否是自己需要的节点。如果是,则返回,然后根据这个节点的rt6_info信息进行路由。

    2. 路由表的组织结构如下图所示。

    这里多说两句,定义CONFIG_IPV6_SUBTREES情况,fib6_lookup_1会递归调用,但最多只能递归一次(因为subtree里不会再有subtree)。递归的那次fib6_lookup_1调用只对src进行了匹配,因为args[1]里的addrsrc<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

    下图中的蓝色部分,表示每个fib6_node都伴随着一个rt6_info结构用来携带具体的路由信息。

     

     

    <?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />

     

    3. ipv6的路由表是是一个radix树,根对应默认路由,结点的层次和路由prefix_len对应。在fib6_lookup_1()中下面的循环把fn设为叶子结点,然后从他开始匹配,如果不符就fn = fn->parent。这样就做到了最长匹配原则。

           for (;;) {

                  struct fib6_node *next;

                  dir = addr_bit_set(args->addr, fn->fn_bit);

                  next = dir ? fn->right : fn->left;

                  if (next) {

                         fn = next;

                         continue;

                  }

                  break;

           }

     

    4. 关于radix树的介绍,可以google,这里简单介绍一下,参考了bloghttp://wurong81.spaces.live.com/blog/cns!5EB4A630986C6ECC!393.entry?sa=419936170 。Radix tree 是一种搜索树,采用二进制数据进行查找,如下图所示。但对于路由表,采用的是二叉树的方式,只有一个Leftright两个子节点。(好像fn_bit表示的是prefix_len,就是路由前缀的长度,不确定?)

     

     

     

     

    展开全文
  • 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式。然后,针对每一个重点模块进行分析。1. 整体通信与业务框架:前端与后端通信框架都为NIO/AIO,因为目前...
  • 3G路由DTU方案

    千次阅读 2014-04-24 15:01:35
    嵌入式Linux,C语言代码模块化设计 静态路由,NAT DTU多协议,多通道 维护方便:WEB升级,远程设置,WEB设置参数 多种机制保证稳定可靠,在线率高 硬件看门狗,工作稳定 适合365*24无人值守恶劣室外工作环境...
  • MyCat - 源代码篇(1)

    万次阅读 热门讨论 2016-02-17 14:00:01
    数据库路由中间件MyCat - 源代码篇(1)进入了源代码篇,将按照框架->模块->细节的方式进行。模块的顺序按照从前端接收请求到后端发送给数据库,之后从数据库返回结果到前端的顺序进行。 MyCat整体框架图: 1. ...
  • zebra代码简单分析

    千次阅读 2015-12-17 20:10:14
    1) zebra是很有名的linux下的开源路由软件项目,代码写的非常漂亮,模块化,很清晰的结构。 关于软件的框架部分就不说了,zebra 官方网站上已经有详细的解释了,简单的来说:zebra作为一个守护进程来维护linux路由...
  • Linux高级配置详解

    2011-06-11 06:59:29
    3.6.2调制解调器的用途和分类3.6.3调制解调器的性能与通信标准3.6.4调制...路由5.1.8域名系统5.1.9TCP/IP服务和客户/服务器体系结构5.1.10CP/IP和套接字5.2以太网和LINUX5.2.1以太网基础5.2.2地址解析协议5.2.3作为...
  • 1) zebra是很有名的linux下的开源路由软件项目,代码写的非常漂亮,模块化,很清晰的结构。 关于软件的框架部分就不说了,zebra 官方网站上已经有详细的解释了,简单的来说:zebra作为一个守护进程来维护linux路由...
  • Linux编程从入门到精通

    千次下载 热门讨论 2011-09-21 10:19:24
    第11章 Linux内核源代码 117 11.1 怎样得到Linux内核源码 117 11.2 内核源码的编排 117 11.3 从何处看起 118 第12章 Linux数据结构 120 附录A 有用的Web和FTP站点 138 附录B 词汇表 139 第二部分 Linux内核模块编程...
  • Linux核心编程.pdf

    热门讨论 2010-09-17 18:03:34
    6.6.4 PCI 补丁代码 64 第七章 中断及中断处理 68 7.1 可编程中断控制器 69 7.2 初始化中断处理数据结构 69 7.3 中断处理 70 第八章 设备驱动 72 8.1 轮询与中断 73 8.2 直接内存访问 (DMA) 74 8.3 内存 74 8.4 设备...
  • Linux从入门到精通

    2010-04-25 19:58:09
    E.5.3 Red Hat Linux是否包含源代码? E.6 安装 E.6.1 我有一个空的硬盘, 并想安装DOS或Windows 95和Linux. 最好的方法是什么? E.6.2 我没有CD-ROM, 也不能从网上安装. 是否有其他方法? E.6.3 我想制作新的软盘, ...
  • 1.5.1 linux内核的主要模块 7 1.5.2 linux的文件结构 8 1.6 gnu通用公共许可证 9 1.6.1 gpl许可证的历史 9 1.6.2 gpl的自由理念 10 1.6.3 gpl的基本条款 11 1.6.4 关于gpl许可证的争议 12 1.7 linux软件...
  • 1.6.2 Linux下访问DNS服务 1.6.3 使用tcpdump观察DNS通信过程 1.7 socket和TCPIP协议族的关系 第2章 IP协议详解 2.1 IP服务的特点 2.2 IPv4头部结构 2.2.1 IPv4头部结构 2.2.2 使用tcpdump观察IPv4...
  • Linux从入门到精通》

    热门讨论 2008-09-04 17:05:49
    E.5.3 Red Hat Linux是否包含源代码? E.6 安装 E.6.1 我有一个空的硬盘, 并想安装DOS或Windows 95和Linux. 最好的方法是什么? E.6.2 我没有CD-ROM, 也不能从网上安装. 是否有其他方法? E.6.3 我想制作新的软盘, ...
  • 1.5.1 Linux内核的主要模块............................................ 7 1.5.2 Linux的文件结构................................................ 9 1.6 G N U 通用公共许可证...................................
  • 入门学习Linux常用必会60个命令实例详解doc/txt

    千次下载 热门讨论 2011-06-09 00:08:45
    不过目前大多数较新的Linux发行版本(包括红旗 Linux、中软Linux、Mandrake Linux等)都可以自动挂装文件系统,但Red Hat Linux除外。 umount 1.作用 umount命令的作用是卸载一个文件系统,它的使用权限是超级...
  • 自制迷你路由器过程中的点滴

    千次阅读 2012-06-26 17:31:15
    端午假期搞了一块带有Wifi模块的开发板,本来这... 所谓的一个路由器,使用Linux来做实在太方便了,对照着家里的TP-LINK,无非就是实现以下的模块,甚至都不用写一行C代码:1.实现路由添加,修改,删除操作界面界面可
  • quagga 中 rip运行流程具体分析

    千次阅读 2013-08-03 12:49:20
    1) zebra是很有名的linux下的开源路由软件项目,代码写的非常漂亮,模块化,很清晰的结构。 关于软件的框架部分就不说了,zebra 官方网站上已经有详细的解释了,简单的来说:zebra作为一个守护进程来维护linux路由...

空空如也

空空如也

1 2 3 4
收藏数 61
精华内容 24
关键字:

linux代码路由模块

linux 订阅