// 初始化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;
}