TCP/IP协议栈初始化(五) 再向下,找到了IP协议的好队友ARP

      TCP层和socket/IP层之间的关系捋清了。但此时协议栈的初始化还远没有完成。此时IP协议还没有提到,TCP协议虽然注册了,但还没有进行初始化。还要继续向下看。
    IP分组数据在网络之间传递时依靠的是千千万万的路由器,进行包转发。可是一旦到了网络的边沿,局域网中,IP协议就不能完美工作了。它需要队友辅助。也就是下面说的ARP协议了。
   ARP,地址解析协议,是TCP/IP协议族中,不可缺少的一分子。在局域网中,举以太网为例,此时要和其中的主机通信,就需要用到以太网技术了。以太网协议属于数据链路层的协议,不在TCP/IP协议族的描述范围内。但我们的IP数据分组是以IP地址为目的地址的,到了局域网中,需要一个手段把IP地址与对应的以太网地址(网卡的MAC地址)绑定起来,而实现这个功能的正是ARP协议。
    当IP分组到达网关路由时,路由向局域网内发送ARP广播请求,“这个IP地址是谁的?”,目标IP的主机就会给出应答,报上自己的MAC。路由就把这个分组转发给它了。当然,想要工作,就要有资源,向OS备案,所以ARP协议初始化就来了。
    inet_init() 1414行。很简洁,没有参数。定义在net/ipv4/arp.c中。
    arp_init();
    这个函数也不复杂。我们假设第1236行的宏不生效。只需要看4个函数调用。
1230 void __init arp_init(void)
1231 {
1232     neigh_table_init(&arp_tbl);
1234     dev_add_pack(&arp_packet_type);
1235     arp_proc_init();
1236 #ifdef CONFIG_SYSCTL
1237     neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,
1238                   NET_IPV4_NEIGH, "ipv4", NULL, NULL);
1239 #endif
1240     register_netdevice_notifier(&arp_netdev_notifier);
1241 }
    1232 neigh_table_init(&arp_tbl)。初始化邻居表。去看下这个表的内容。还好,不是很大的结构体。其类型struct neigh_table是网络子系统中,通用的数据结构,用来描述网络中相邻设备之间的关系与状态,其成员较多,现在还没有涉及到,暂时不细说。arp_tbl中,初始化了一些成员变量。
struct neigh_table arp_tbl = {
174     .family =    AF_INET,
175     .entry_size =    sizeof(struct neighbour) + 4,
176     .key_len =    4,
177     .hash =        arp_hash,
178     .constructor =    arp_constructor,
179     .proxy_redo =    parp_redo,
180     .id =        "arp_cache",
181     .parms = {
182         .tbl =            &arp_tbl,
183         .base_reachable_time =    30 * HZ,
184         .retrans_time =    1 * HZ,
185         .gc_staletime =    60 * HZ,
186         .reachable_time =        30 * HZ,
187         .delay_probe_time =    5 * HZ,
188         .queue_len =        3,
189         .ucast_probes =    3,
190         .mcast_probes =    3,
191         .anycast_delay =    1 * HZ,
192         .proxy_delay =        (8 * HZ) / 10,
193         .proxy_qlen =        64,
194         .locktime =        1 * HZ,
195     },
196     .gc_interval =    30 * HZ,
197     .gc_thresh1 =    128,
198     .gc_thresh2 =    512,
199     .gc_thresh3 =    1024,
200 };
    family成员为AF_INET,说明这里的arp也是属于inet协议族的。entry_size初始化是以struct neighbour的大小为基数。而struct neightbour则是描述相邻主机的数据结构了。进入函数中看下,具体对arp_tbl做了什么。
    neigh_table_init函数位于net/core/neighbour.c中。这个函数内容如下:
    void neigh_table_init(struct neigh_table *tbl)
1392 {
1393     struct neigh_table *tmp;
1395     neigh_table_init_no_netlink(tbl);
1396     write_lock(&neigh_tbl_lock);
1397     for (tmp = neigh_tables; tmp; tmp = tmp->next) {
1398         if (tmp->family == tbl->family)
1399             break;
1400     }
1401     tbl->next    = neigh_tables;
1402     neigh_tables    = tbl;
1403     write_unlock(&neigh_tbl_lock);
1405     if (unlikely(tmp)) {
1406         printk(KERN_ERR "NEIGH: Registering multiple tables for "
1407                "family %d\n", tbl->family);
1408         dump_stack();
1409     }
1410 }
    这个函数同样很简单。由于第一次执行时,neigh_tables只是一个全局静态指针,所以编译器为它赋值为空,只是把传入的arp_tbl加入到neigh_tables的单向链表中。内核专门为这个这单向链表的操作加了一个写保护锁,neigh_tbl_lock。
    回到arp_init函数中。
    1234行 dev_add_pack(&arp_packet_type)。从字面意思理解是为设备添加一种包处理入口,而添加的正是arp协议包类型。先去看下arp_packet_type的内容。
1223 static struct packet_type arp_packet_type = {
1224     .type =    __constant_htons(ETH_P_ARP),
1225     .func =    arp_rcv,
1226 };
    结构体中定义了两个成员。一个是ARP包的类型ETH_P_ARP,也就是针对以太网类型的ARP。另一个是一个函数,arp_rcv。查看arp_rcv函数的注释可以得知,arp_rcv直接从网卡设备上得到数据,也就是处于IP层下方。可以简单地这么理解,当网卡收到一个数据分组时,它会根据协议的类型做出判断,把这个包传递给IP层,还是传递给ARP协议模块。由于现在是说协议的初始化,ARP协议的工作流程,暂时不细说。留待以后。继续向下。
    dev_add_pack也很有意思。因为它是两个层之间的接口的关键数据结构,通过它总能让我们的协议栈的流程越来越清晰。在IP层初始化时,我们会再次看到它。现在我们感兴趣的是dev_add_pack是把arp_packet_type添加到哪个列表中了?这个列表是有谁在维护的?
    dev_add_pack是位于net/core/dev.c。看到没有,它的地位可不低,是位网络子系统的核心层代码中,dev.c中说明它是与设备相关的。代码如下:
357 void dev_add_pack(struct packet_type *pt)
358 {
359     int hash;
361     spin_lock_bh(&ptype_lock);
362     if (pt->type == htons(ETH_P_ALL))
363         list_add_rcu(&pt->list, &ptype_all);
364     else {
365         hash = ntohs(pt->type) & 15;
366         list_add_rcu(&pt->list, &ptype_base[hash]);
367     }
368     spin_unlock_bh(&ptype_lock);
369 }
    整个函数的流程同样很简单。从之前arp_packet_type内容知道,现在走else这条支线。把arp_packet_type散列到ptype_base中。ptype_all和ptype_base是我们感兴趣的数据结构。ptype_base是定义在dev.c中的一个全局的双向链表数组,而ptype_all只是一个双向链表。后面会得到验证,既然是记录在了这个数组里的,以后用到的时候,自然还是来这个表中查找。这两个数据结构也会数据关系图增加了一些内容。
    再次回到arp_init中。
1235     arp_proc_init();
    这个函数只是在网络层proc文件系统中创建一个arp的目录,用来显示arp运行时的一些参数。对理解协议栈没有什么影响。
1240     register_netdevice_notifier(&arp_netdev_notifier);
    这是内核中的一种通知机制。arp协议要监听设备状态的变化,以便实时更新arp的缓存。register_netdevice_notifier,则为系统中每个网络设备(一般是网卡)注册了arp的通知函数。arp_netdev_notifier,是定义在 arp.c中。而函数事件处理函数arp_netdev_event中,只处理一件事,就是设备网络地址发生变化。暂时不展开细说。
    1205 static struct notifier_block arp_netdev_notifier = {
1206     .notifier_call = arp_netdev_event,
1207 };
    arp_init函数执行完成了。
    总结来说,为IP层服务的ARP协议已经准备好了。加入ptype_base后,我们协议栈的结构关系图又可以更新一些了。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值