当前位置: 移动技术网 > 网络运营>网络>协议 > TCP/IP协议栈初始化

TCP/IP协议栈初始化

2018年04月21日  | 移动技术网网络运营  | 我要评论

声卡 驱动,今天限行尾号,黄山汽车站

这已经是第六篇了。但协议栈的初始化还没有说完。不得不承认协议栈还是很复杂的。越是牛B的东西,就越复杂。就像一门手艺一样,当你可以做到别人都不能达到的复杂度的时候,你就是大师了。还有人说,想要精通一样技术,你必须重复它10万次以上。子曰:“温故而知新”,代码看多了,就能明白其中的奥秘了。当然一些实践还是必不可少的。这个系列一开始,我就说,协议栈很牛,所以它复杂也在情理之中。学习到现在,对协议栈,感觉自己刚入门。继续看代码。
上回说完了ARP协议。回到inet_init函数的第1414行。看到了期待已久的IP协议的初始化函数。
1414 ip_init();
函数定义在net/ipv4/ip_output.c中。
1404 void __init ip_init(void)
1405 {
1406     ip_rt_init();
1407     inet_initpeers();
1409 #if defined(CONFIG_IP_MULTICAST) && defined(CONFIG_PROC_FS)
1410     igmp_mc_proc_init();
1411 #endif
1412 }
很简单的三个函数调用。第三个函数只是为igmp协议在虚拟文件系统proc中创建目录,不再讨论。前两个函数依次展开讨论下。第一个是ip_rt_init()。在TCP/IP协议族里,rt就代表route,路由的意思。ip_rt_init就是ip系统里路由子系统的初始化。因为路由是IP协议的一个主要功能,没有这个功能,互联网就不存在了。可见其重要性。这个函数位于net/ipv4/route.c中。是目前看到的比较复杂的一个函数。为了方便查看,我把其中预编译的部分去掉了。
梳理初始化的过程,重点在于协议栈数据结构网络是如何构建的。这对于后续协议栈的工作流学习是个基础。
2922 int __init ip_rt_init(void)
2923 {
2924     int rc = 0;
2926     rt_hash_rnd = (int) ((num_physpages ^ (num_physpages>>8)) ^
2927                  (jiffies ^ (jiffies >> 7)));
2942     ipv4_dst_ops.kmem_cachep =
2943         kmem_cache_create("ip_dst_cache", sizeof(struct rtable), 0,
2944                   SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL);
2946     ipv4_dst_blackhole_ops.kmem_cachep = ipv4_dst_ops.kmem_cachep;
2948     rt_hash_table = (struct rt_hash_bucket *)
2949         alloc_large_system_hash("IP route cache",
2950                     sizeof(struct rt_hash_bucket),
2951                     rhash_entries,
2952                     (num_physpages >= 128 * 1024) ?
2953                     15 : 17,
2954                     0,
2955                     &rt_hash_log,
2956                     &rt_hash_mask,
2957                     0);
2958     memset(rt_hash_table, 0, (rt_hash_mask + 1) * sizeof(struct rt_hash_bucket));
2959     rt_hash_lock_init();
2961     ipv4_dst_ops.gc_thresh = (rt_hash_mask + 1);
2962     ip_rt_max_size = (rt_hash_mask + 1) * 16;
2964     devinet_init();
2965     ip_fib_init();
2967     init_timer(&rt_flush_timer);
2968     rt_flush_timer.function = rt_run_flush;
2969     init_timer(&rt_secret_timer);
2970     rt_secret_timer.function = rt_secret_rebuild;
2975     schedule_delayed_work(&expires_work,
2976         net_random() % ip_rt_gc_interval + ip_rt_gc_interval);
2978     rt_secret_timer.expires = jiffies + net_random() % ip_rt_secret_interval +
2979         ip_rt_secret_interval;
2980     add_timer(&rt_secret_timer);
3000     rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL);
3002     return rc;
3003 }
2924-2962 分配了资源,初始化了路由的散列表,初始化路由表的一些限制(最大数量等)。可以看到同tcp_proto一样,ipv4_dst_ops是路由表(struct rt_table)内存资源的持有者,它还负责dst路由表的维护。真正把路由表组织起来的是rt_hash_table。因为内核要经常访问路由表,修改它,所以要用到散列的实现高效的查找访问。这两个数据结构都位于route.c文件中。让我们暂时记下这些数据结构,以后会再次遇到它们。

158 static struct dst_ops ipv4_dst_ops = {
159     .family =        AF_INET,
160     .protocol =        __constant_htons(ETH_P_IP),
161     .gc =            rt_garbage_collect,
162     .check =        ipv4_dst_check,
163     .destroy =        ipv4_dst_destroy,
164     .ifdown =        ipv4_dst_ifdown,
165     .negative_advice =    ipv4_negative_advice,
166     .link_failure =        ipv4_link_failure,
167     .update_pmtu =        ip_rt_update_pmtu,
168     .entry_size =        sizeof(struct rtable),
169 };
247 static struct rt_hash_bucket     *rt_hash_table;
207 struct rt_hash_bucket {
208     struct rtable    *chain;
209 };
继续看,2964,完成与协议栈有关的设备初始化。这个函数不复杂,源码不再列出,对于协议栈的数据结构没有什么补充,需要注意到以下事情:
1 它定义了IP层的设备事件处理函数。ip_netdev_notifier中的inetdev_event(net/ipv4/devinet.c中)。这个函数不展开说,只需要知道,当网卡启用,MTU变化等事件发生时,是由inetdev_event函数处理的。与之前在ARP中遇到的类似。
2 为IP层注册了链路层传来消息时的处理函数。如有新地址、删除地址、请求地址等。
2964 devinet_init();
继续2965行。fib是forward information base的缩写。也是IP层完成路由转发的重要数据信息。
2965 ip_fib_init();
因为此函数会新增一个重要数据结构,所以把它的源码列出。位于net/ipv4/fib_frontend.c中。fib_table_hash是串联起fib信息的数据结构。FIB_TABLE_HASHSZ被固定编码为1。所以只会有一个元素。只是这个列表会怎么用到,现在还不知道。
912 void __init ip_fib_init(void)
913 {
914     unsigned int i;
916     for (i = 0; i < FIB_TABLE_HASHSZ; i++)
917         INIT_HLIST_HEAD(&fib_table_hash[i]);
919     fib4_rules_init();
921     register_netdevice_notifier(&fib_netdev_notifier);
922     register_inetaddr_notifier(&fib_inetaddr_notifier);
923     nl_fib_lookup_init();
925     rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL);
926     rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL);
927     rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib);
928 }
919行 fib4_rules_init(); 使用过linux iptables的人都知道,iptables是基于规则对IP数据包进行过滤,实现防火墙的功能。那么猜测这里的fib很可能与之有关系。因为带了一个rule。fib4_rules_init内容很简单,其源码不再列出,只是注册了但又会出现一个数据结构,就是fib4_rules_ops,其内容如下,在/net/ipv4/fib_rules.c中定义。全部是关于fib4_rules的操作,具体成员函数不再分析。fib4_rules_ops通过注册,被挂靠在了双向链表rules_ops上。OS以后也就是通过这个链表来搜寻到fib4_rules_ops,完成规则的适配。
277 static struct fib_rules_ops fib4_rules_ops = {
278     .family        = AF_INET,
279     .rule_size    = sizeof(struct fib4_rule),
280     .addr_size    = sizeof(u32),
281     .action        = fib4_rule_action,
282     .match        = fib4_rule_match,
283     .configure    = fib4_rule_configure,
284     .compare    = fib4_rule_compare,
285     .fill        = fib4_rule_fill,
286     .default_pref    = fib4_rule_default_pref,
287     .nlmsg_payload    = fib4_rule_nlmsg_payload,
288     .flush_cache    = fib4_rule_flush_cache,
289     .nlgroup    = RTNLGRP_IPV4_RULE,
290     .policy        = fib4_rule_policy,
291     .rules_list    = LIST_HEAD_INIT(fib4_rules_ops.rules_list),
292     .owner        = THIS_MODULE,
293 };
继续ip_fib_init函数。921-922为FIB注册了网卡事件处理函数和地址事件处理函数的接口数据结构。挂靠的上级数据结构,分别是netdev_chain(net/core/dev.c),inetaddr_chain(net/ipv4/devinet.c)。当对应类别的事件发生时,两个chain中所有处理全程,都会得到通知。
923行,为内核管理 FIB创建了一个内核socket。
925-927注册 路由事件的处理函数例程。
回到ip_rt_init函数中。
2967-2980 初始化路由表的两个维护定时器。分别是rt_flush_timer,更新定时器,rt_secret_rebuild,重建路由定时器。
最后3000行。为路由注册路由请求消息的处理例程。到这里ip_rt_init返回了。
总结下:ip_rt_init为内核建立了路由表、FIB表、路由的链路层处理函数等主要内容,为IP协议的正常工作做好了基础工作。
回到ip_init函数中。1407 inet_initpeers()。peer这个单词很有趣,意思是同龄人,地位相似的人。那么和IP协议在协议栈中有相同地位的东西是什么呢?然而看了源码中的注释后,这个函数的用途是用来为一个IP子组件申请内存资源。这个子组件就是inet_peer结构体实现的,用AVL树组织的。组件的目的是为了防范dos攻击。其原理没有细看。与理解协议关系不大。跳过。
至此IP层的初始化完成了。但网卡到IP层的接口还没有看到。尽管如此,我们协议栈的结构关系图又可以更新一些了,添加上IP层的一些数据结构。到现在为止,图中的大部分数据结构还是孤立的。等初始化完成后,一次socket调用就可以把它们基本串联起来了。
\

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网