ipv6 ping未知单播地址时发送ns报文(进行链路不可达探测)的流程

环境:

内核版本4.1.15

学习Linux协议栈过程中,没有机会可以体系化学习,只能通过问题一点点做积累。如果文章中有问题,希望大家给我指正。

1、起因

        调试交换机带内口的过程中,测试使用了ipv6本地链路地址做为服务器地址,与设备进行通信。这时从服务器ping设备,链路正常,nd状态为reachable。但如果从设备ping服务器,如果没有正常reachable的nd时,无法ping通。

        问题原因其实很简单,设备的带内口作为linux网络设备进行初始化时都自动生成了对应的ipv6本地链路地址并生成了对应的FE80::/64的路由,使用设备ping FE80::/64网段时,会使用优先级最高的路由发送ns报文(邻居发现请求)。但设备中并没有指定路由优先级,ns报文使用路由表中第一条FE80::/64路由发出,交换机接口很多,大概率不是正确的出接口。导致nd的状态一直都是不正常(failed)。

下面总结一下ns报文的发送流程

2、流程图

3、__neigh_create函数

        我们要重点看一下__neigh_create函数,它用于创建本地邻居缓存表表项,函数的输入参数包括邻居缓存表 tbl、用于查找邻居的主键 pkey、所属网络设备 dev 以及一个 bool 类型的标志 want_ref,表明是否要在新项上增加一个引用计数。

struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
				 struct net_device *dev, bool want_ref)
{
	u32 hash_val;
	int key_len = tbl->key_len;
	int error;
	struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev);
	struct neigh_hash_table *nht;

	if (!n) {
		rc = ERR_PTR(-ENOBUFS);
		goto out;
	}

	memcpy(n->primary_key, pkey, key_len);
	n->dev = dev;
	dev_hold(dev);

	/* Protocol specific setup. */
    //. 调用邻居表 tbl 中的 constructor() 方法,完成协议相关的初始化工作。如果这个方法返回错误,则函数返回,否则继续执行
	if (tbl->constructor &&	(error = tbl->constructor(n)) < 0) {
		rc = ERR_PTR(error);
		goto out_neigh_release;
	}
    //如果网络设备 dev 上有一个定义为 ndo_neigh_construct() 方法的驱动方法,则调用该方法,完成设备相关的初始化工作
	if (dev->netdev_ops->ndo_neigh_construct) {
		error = dev->netdev_ops->ndo_neigh_construct(n);
		if (error < 0) {
			rc = ERR_PTR(error);
			goto out_neigh_release;
		}
	}
    //调用邻居项 n 中存储的参数 parms 中的 neigh_setup() 方法,完成邻居项本身的初始化工作。如果这个方法返回错误,则函数返回,否则继续执行。
	/* Device specific setup. */
	if (n->parms->neigh_setup &&
	    (error = n->parms->neigh_setup(n)) < 0) {
		rc = ERR_PTR(error);
		goto out_neigh_release;
	}
    //设置邻居项的 confirmed 字段以及 dead 字段。
	n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1);

	write_lock_bh(&tbl->lock);
	nht = rcu_dereference_protected(tbl->nht,
					lockdep_is_held(&tbl->lock));
    //获取邻居表 tbl 中的哈希表结构,然后计算 pkey 对应的哈希桶下标 hash_val。
	if (atomic_read(&tbl->entries) > (1 << nht->hash_shift))
		nht = neigh_hash_grow(tbl, nht->hash_shift + 1);

	hash_val = tbl->hash(pkey, dev, nht->hash_rnd) >> (32 - nht->hash_shift);

	if (n->parms->dead) {
		rc = ERR_PTR(-EINVAL);
		goto out_tbl_unlock;
	}
    //遍历这个哈希桶,查找是否有与新项的主键 pkey 相同的邻居项。
	for (n1 = rcu_dereference_protected(nht->hash_buckets[hash_val],
					    lockdep_is_held(&tbl->lock));
	     n1 != NULL;
	     n1 = rcu_dereference_protected(n1->next,
			lockdep_is_held(&tbl->lock))) {
        //如果找到了相同的邻居项,则根据标志 want_ref 确定是否增加一个引用计数,并返回该邻居项。
		if (dev == n1->dev && !memcmp(n1->primary_key, pkey, key_len)) {
			if (want_ref)
				neigh_hold(n1);
			rc = n1;
			goto out_tbl_unlock;
		}
	}
    //如果没有找到相同的邻居项,则将新项加入哈希桶中,并更新邻居表中统计的条目数信息。
	n->dead = 0;
	if (want_ref)
		neigh_hold(n);
	rcu_assign_pointer(n->next,
			   rcu_dereference_protected(nht->hash_buckets[hash_val],
						     lockdep_is_held(&tbl->lock)));
	rcu_assign_pointer(nht->hash_buckets[hash_val], n);
	write_unlock_bh(&tbl->lock);
	neigh_dbg(2, "neigh %p is created\n", n);
	rc = n;
out:
	return rc;
out_tbl_unlock:
	write_unlock_bh(&tbl->lock);
out_neigh_release:
	neigh_release(n);
	goto out;
}
4、neigh_resolve_output函数

        neigh_resolve_output 函数会首先检查邻居缓存(neighbor cache)中是否存在目标主机的缓存条目,如果该条目在缓存中存在,并且其状态是可达(Reachable),则直接返回该缓存条目中的 MAC 地址。如果该缓存条目不存在,或者其状态不可达,那么内核将向目标主机发送 ARP 或 ND 请求,并等待其响应。如果目标主机响应了请求,则内核将在邻居缓存中创建一个新的缓存条目,并将目标主机的 MAC 地址保存在该条目中,状态设置为可达(Reachable)。如果目标主机没有响应,则对应的缓存条目状态将被设置为不可达(Unreachable)

5、基础知识

IPv6邻居发现协议介绍
IPv6 ND(IPv6 Neighbor Discovery,IPv6邻居发现)协议使用五种类型的ICMPv6消息,实现下面一些功能:地址解析、验证邻居是否可达、重复地址检测、路由器发现/前缀发现、地址自动配置和重定向等功能。

邻居发现协议使用的ICMPv6消息类型及作用

ICMPv6消息类型号作用
邻居请求消息NS(Neighbor Solicitation)135

获取邻居的链路层地址

验证邻居是否可达

进行重复地址检测

邻居通告消息NA(Neighbor Advertisement)136

对NS消息进行响应

节点在链路层变化时主动发送NA消息,向邻居节点通告本节点的变化信息

路由器请求消息RS(Router Solicitation)

路由器请求消息RS(Router Solicitation)133

节点启动后,通过RS消息向路由器发出请求,请求前缀和其他配置信息,用于节点的自动配置

路由器通告消息RA(Router Advertisement)

路由器通告消息RA(Router Advertisement)134

对RS消息进行响应

在没有抑制RA消息发布的条件下,路由器会周期性地发布RA消息,其中包括前缀信息选项和一些标志位的信息

重定向消息(Redirect)137当满足一定的条件时,缺省网关通过向源主机发送重定向消息,使主机重新选择正确的下一跳地址进行后续报文的发送


 

6、引用

Linux内核分析 - 网络[九]:邻居表_linux 邻居表作用-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值