网络子系统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;
}

上一篇:个人自学前端39-Vue16-后台管理系统,登录、权限的业务逻辑,修改UI框架样式


下一篇:高阶函数