网络子系统15_arp邻居项初始化
// 初始化struct neighbour // 当邻居子系统新创一个neighbour时,邻居子系统调用特定协议的初始化函数,初始化邻居项。 // 调用路径:neigh_create->arp_constructor // 函数主要任务: // 1.设置邻居项的地址类型,a,b,c,d // 2.使用与此邻居项关联的接口设备的neigh_param作为该邻居项调整邻居协议的参数。 // 3.根据与此邻居项关联的接口设备的信息,初始化邻居项的状态,以及ops // 3.1 驱动没有提供填充l2帧头的函数,表明此接口不需要进行地址解析 // 3.1.1 设置邻居项nud_state=NUD_NOARP,ops=arp_direct_ops // 3.2 否则,根据邻居l3地址类型,设置邻居项状态 // 3.2.1 多播地址,广播地址,nud_state=NUD_NOARP // 3.2.2 设备dev->flags表明无需进行地址解析,nud_state=NUD_NOARP // 3.2.3 设备dev->flags表明回环设备,nud_state=NUD_NOARP // 3.3 如果设备驱动提供l2帧头缓存的能力: // 3.3.1 使用arp_hh_ops // 3.4 否则使用最通用的arp_generic_ops // 注:根据邻居项与本机的连接情况,为neighbour设置最合适操作函数 // 1.当邻居项与本机之连时,无需arp解析,使用arp_direct_ops // 2.当邻居项需要进行地址解析时,而且到达此邻居项的接口设备,提供l2帧头缓存的能力, // 则使用优化过的arp_hh_ops 3.当1,2 均不满足时,使用可以适用于一切情况下的arp_generic_ops 2.2 static int arp_constructor(struct neighbour *neigh) { u32 addr = *(u32*)neigh->primary_key;//neighbour的key值,为ip地址 struct net_device *dev = neigh->dev;//到达此邻居通过的设备接口 struct in_device *in_dev;//ipv4配置信息 struct neigh_parms *parms;//调整邻居协议的参数 //a,b,c,d类地址 neigh->type = inet_addr_type(addr); rcu_read_lock(); //获得设备的ipv4配置信息 in_dev = rcu_dereference(__in_dev_get(dev)); if (in_dev == NULL) { rcu_read_unlock(); return -EINVAL; } //获取此设备调整arp协议的参数控制块 parms = in_dev->arp_parms; //递减引用计数 __neigh_parms_put(neigh->parms); //克隆一份设备使用的控制参数,添加到邻居项中 neigh->parms = neigh_parms_clone(parms); rcu_read_unlock(); //设备驱动没有提供填充mac头的回调函数 if (dev->hard_header == NULL) { //设置当前邻居项的状态为NUD_NOARP,说明不需要进行地址解析 neigh->nud_state = NUD_NOARP; //使用直连操作回调函数 neigh->ops = &arp_direct_ops; //通过该邻居项的输出函数初始化为arp_direct_ops->queue_xmit,为dev_queue_xmit neigh->output = neigh->ops->queue_xmit; } else { //否则,需要进行地址解析 //如果邻居的地址类型为多播地址,则不需要进行地址解析 if (neigh->type == RTN_MULTICAST) { neigh->nud_state = NUD_NOARP; //初始化多播地址链表 arp_mc_map(addr, neigh->ha, dev, 1); //如果设备指示不能支持地址解析,或者设备为回环设备 } else if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { //则不需要进行地址解析 neigh->nud_state = NUD_NOARP; //邻居的mac地址为dev的设备mac地址 memcpy(neigh->ha, dev->dev_addr, dev->addr_len); //如果邻居为广播地址,或者设备为点到点连接 } else if (neigh->type == RTN_BROADCAST || dev->flags&IFF_POINTOPOINT) { //则不需要进行地址解析 neigh->nud_state = NUD_NOARP; //邻居的mac地址为dev的设备的广播地址 memcpy(neigh->ha, dev->broadcast, dev->addr_len); } //设备提供了mac头缓存回调函数 if (dev->hard_header_cache) neigh->ops = &arp_hh_ops;//初始化为arp_hh_ops else neigh->ops = &arp_generic_ops;//负责初始化为通用例程 if (neigh->nud_state&NUD_VALID)//如果当前邻居状态为有效状态 neigh->output = neigh->ops->connected_output;//则初始化邻居的输出函数为连接状态下的输出回调函数 else neigh->output = neigh->ops->output; } return 0; } // 最通用的邻居项操作 2.1 static struct neigh_ops arp_generic_ops = { .family = AF_INET,//地址族 .solicit = arp_solicit,//协议提供,用于发送solicitation请求的回调函数 .error_report = arp_error_report,//当一个arp事物中发生错误时,arp_error_report函数就通知上层的网络层 .output = neigh_resolve_output,//可用于所有情况下,它会检查地址是否已经被解析过,在没有被解析的情况下,启动解析程序,如果地址还没有准备好, //则会把封包保存在arp_queue中,并启动解析程序。该函数为了保证接收方可到达,做好每一件必要的操作 .connected_output = neigh_connected_output,//当已经知道邻居是可到达时,(NUD_CONNECTED态),使用该函数 .hh_output = dev_queue_xmit,//当地址已经解析过,并且整个封包头已经根据上一次传输结果放入帧头缓存时,就使用该函数 .queue_xmit = dev_queue_xmit,//之前的所有函数,除了hh_output函数外,都不会实际传输封包,他们所做的工作就是确保封包帧头是编写好的,然后当帧头 //缓存准备好时,调用queue_xmit执行传输。 }; // 驱动程序提供l2帧头缓存功能,arp_hh_ops提高性能 3.1 static struct neigh_ops arp_hh_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, .output = neigh_resolve_output, .connected_output = neigh_resolve_output, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; // 不需要进行地址解析时采用的操作 4.1 static struct neigh_ops arp_direct_ops = { .family = AF_INET, .output = dev_queue_xmit,//直接启动设备的传输 .connected_output = dev_queue_xmit, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; // 发送solicitation请求 5.1 static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb) { u32 saddr = 0; u8 *dst_ha = NULL; struct net_device *dev = neigh->dev; u32 target = *(u32*)neigh->primary_key; int probes = atomic_read(&neigh->probes);//失败的solicitation尝试的次数 //获取设备上的ipv4配置信息 struct in_device *in_dev = in_dev_get(dev); if (!in_dev) return; //当发送arp请求的主机有多个ip地址时,ANNOUNCE这个选项控制哪个地址应该放到solicitation请求的ARP头中。 switch (IN_DEV_ARP_ANNOUNCE(in_dev)) { default: case 0: //任何本地ip都可以 //源地址为本机地址 if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL) saddr = skb->nh.iph->saddr; break; case 1: //如果可能,选择和目的地址位于同一子网内的地址 if (!skb) break; saddr = skb->nh.iph->saddr; if (inet_addr_type(saddr) == RTN_LOCAL) { //判断skb中的源地址与目标地址是否在同一子网内 if (inet_addr_onlink(in_dev, target, saddr)) break; } saddr = 0; break; case 2: //优先使用主地址 break; } //递减ipv4配置信息的引用 if (in_dev) in_dev_put(in_dev); //说明需要优先使用主地址 if (!saddr) saddr = inet_select_addr(dev, target, RT_SCOPE_LINK); //当前邻居已经消耗尽证实一个地址可到达性测试的探测次数 if ((probes -= neigh->parms->ucast_probes) < 0) { //当前状态非VALID if (!(neigh->nud_state&NUD_VALID)) printk(KERN_DEBUG "trying to ucast probe in NUD_INVALID\n"); dst_ha = neigh->ha;//与primary_key表示的l3地址关联的l2地址 read_lock_bh(&neigh->lock); } else if ((probes -= neigh->parms->app_probes) < 0) { #ifdef CONFIG_ARPD //如果使用arpd,则唤醒用户态进程 neigh_app_ns(neigh); #endif return; } //发送arp报文 arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr, dst_ha, dev->dev_addr, NULL); if (dst_ha) read_unlock_bh(&neigh->lock); } // 通知上层网络协议arp事物错误 5.2 static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb) { //向路由子系统的路由项缓存通知链路失效 dst_link_failure(skb); //释放当前skb kfree_skb(skb); } // 可用于所有情况下的skb发送 5.3 int neigh_resolve_output(struct sk_buff *skb) { //skb对应的路由项缓存 struct dst_entry *dst = skb->dst; struct neighbour *neigh; int rc = 0; //skb没有关联的路由缓存或者路由缓存没有有效的邻居 if (!dst || !(neigh = dst->neighbour)) goto discard; //将skb->data移动到l3协议头处 __skb_pull(skb, skb->nh.raw - skb->data); //调整邻居的状态机,启动定时器,并且skb没有接管 if (!neigh_event_send(neigh, skb)) { int err; struct net_device *dev = neigh->dev; //如果驱动程序提供了l2帧头缓存,并且路由缓存没有关联的l2头缓存 if (dev->hard_header_cache && !dst->hh) { write_lock_bh(&neigh->lock); //路由项缓存没有l2头缓存 if (!dst->hh) neigh_hh_init(neigh, dst, dst->ops->protocol);//初始化dst的l2帧头缓存 //填充skb的l2头 err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); write_unlock_bh(&neigh->lock); } else { read_lock_bh(&neigh->lock); //驱动没有提供帧头缓存,调用驱动提供回调函数,填充skb的l2帧头 //此时dst依然没有对应的l2帧头缓存 err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); read_unlock_bh(&neigh->lock); } if (err >= 0) { //准备好l2头之后,调用queue_xmit进行传输 rc = neigh->ops->queue_xmit(skb); } else goto out_kfree_skb; } out: return rc; discard: NEIGH_PRINTK1("neigh_resolve_output: dst=%p neigh=%p\n", dst, dst ? dst->neighbour : NULL); out_kfree_skb: rc = -EINVAL; kfree_skb(skb); goto out; } //调用路径neigh_resolve_output->neigh_event_send 5.4 static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) { //更新当前邻居最近一次被使用的时间 neigh->used = jiffies; //邻居状态已经开始处理solicitation if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE))) return __neigh_event_send(neigh, skb); return 0; } //调用路径neigh_resolve_output->neigh_event_send->__neigh_event_send //在skb被该函数接管后,返回1,否则返回0 5.5 int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb) { int rc; unsigned long now; write_lock_bh(&neigh->lock); rc = 0; //邻居没有未决的solicitation,或者已经开始执行solicitation if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE)) goto out_unlock_bh; now = jiffies; //邻居不需要可到底性确认,并且没有待决的solicitation请求 if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) { //邻居项配置参指示,仍然可以使用用户空间解析地址,或者多播解析地址 if (neigh->parms->mcast_probes + neigh->parms->app_probes) { //设置邻居项的试探次数为邻居项调整参数的单播次数 atomic_set(&neigh->probes, neigh->parms->ucast_probes); neigh->nud_state = NUD_INCOMPLETE;//设置邻居项有未决的solicitation请求 neigh_hold(neigh);//增加引用计数 neigh->timer.expires = now + 1;//调整到期时间为下一个jiffies add_timer(&neigh->timer); } else { neigh->nud_state = NUD_FAILED;//设置邻居不可达 write_unlock_bh(&neigh->lock); if (skb) kfree_skb(skb); return 1; } } else if (neigh->nud_state & NUD_STALE) {//邻居项已经有一段时间没有被确认过了 NEIGH_PRINTK2("neigh %p is delayed.\n", neigh); neigh_hold(neigh);//增加引用计数 neigh->nud_state = NUD_DELAY;//邻居项使用旧的l2地址,进入时间窗 neigh->timer.expires = jiffies + neigh->parms->delay_probe_time;//设置时间窗的长度 add_timer(&neigh->timer);//启动定时器 } if (neigh->nud_state == NUD_INCOMPLETE) {//如果已经发送了solicitation请求,但还没有收到应答 if (skb) { if (skb_queue_len(&neigh->arp_queue) >= neigh->parms->queue_len) { struct sk_buff *buff; buff = neigh->arp_queue.next; __skb_unlink(buff, &neigh->arp_queue); kfree_skb(buff); } __skb_queue_tail(&neigh->arp_queue, skb);//将skb添加到arp_queue中,等待收到solicitation应答 } rc = 1; } out_unlock_bh: write_unlock_bh(&neigh->lock); return rc; } //使用hh_cache,尽快完成skb发送 5.5 int neigh_connected_output(struct sk_buff *skb) { int err; struct dst_entry *dst = skb->dst; struct neighbour *neigh = dst->neighbour; struct net_device *dev = neigh->dev; //将skb->data调整到l3头 __skb_pull(skb, skb->nh.raw - skb->data); read_lock_bh(&neigh->lock); //填充skb的l2头 err = dev->hard_header(skb, dev, ntohs(skb->protocol), neigh->ha, NULL, skb->len); read_unlock_bh(&neigh->lock); if (err >= 0) err = neigh->ops->queue_xmit(skb);//委托给ops->queue_xmit完成最后的传输 else { err = -EINVAL; kfree_skb(skb); } return err; }
最后更新:2017-04-03 15:22:03