PROC文件accept_dad控制DAD(Duplicate Address Detection)报文的接收,内核中根据目录all和接口(ens33)名称目录下的accept_dad的两个值中的最大值来决定最终的值。accept_dad的取值有以下三个:
0 - 关闭DAD
1 - 开启DAD
2 - 开启DAD,并且,在基于MAC地址的本地链路地址检测到冲突时,关闭IPv6功能。
$ cat /proc/sys/net/ipv6/conf/all/accept_dad
0
$ cat /proc/sys/net/ipv6/conf/default/accept_dad
1
$ cat /proc/sys/net/ipv6/conf/ens33/accept_dad
1
$
$ cat /proc/sys/net/ipv6/conf/lo/accept_dad
-1
另外,对于回环接口lo,accept_dad的值为一个非法值:-1。以下可见,目录all下的accept_dad默认值为0,而default目录下的值为1。
static struct ipv6_devconf ipv6_devconf __read_mostly = {
.accept_dad = 0,
...
}
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.accept_dad = 1,
在函数ipv6_add_dev添加inet6_dev结构时,对于回环接口(如以上的接口lo),和不需要ARP功能的接口(如虚拟接口vti,gre等),将接口的accept_dad值设置为非法值-1。
static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
{
struct inet6_dev *ndev;
/* One reference from device. */
refcount_set(&ndev->refcnt, 1);
if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
ndev->cnf.accept_dad = -1;
在DAD开启函数中,先进行合法性判断,如果网络设备设置了IFF_NOARP和IFF_LOOPBACK标志,不进行DAD流程。另外以下情况也不进行DAD:
- 设备自身配置的accept_dad值小于1,并且其所在命名空间中all所对应的accept_dad也小于1;
- 当前地址不是暂定地址
- 当前地址在配置时使用了nodad参数
$ ip -6 addr add 2000:10:229:142::32/64 dev br0 nodad
$ ip -6 addr show dev br0
5: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
inet6 2000:10:229:142::32/64 scope global nodad
static void addrconf_dad_begin(struct inet6_ifaddr *ifp)
{
struct inet6_dev *idev = ifp->idev;
struct net_device *dev = idev->dev;
...
net = dev_net(dev);
if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
(net->ipv6.devconf_all->accept_dad < 1 &&
idev->cnf.accept_dad < 1) ||
!(ifp->flags&IFA_F_TENTATIVE) ||
ifp->flags & IFA_F_NODAD) {
bool send_na = false;
if (ifp->flags & IFA_F_TENTATIVE && !(ifp->flags & IFA_F_OPTIMISTIC))
send_na = true;
bump_id = ifp->flags & IFA_F_TENTATIVE;
ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
spin_unlock(&ifp->lock);
read_unlock_bh(&idev->lock);
addrconf_dad_completed(ifp, bump_id, send_na);
return;
}
如果DAD失败,并且1a)设备自身配置的accept_dad大于1,或者1b)设备所在命名空间的all下的accept_dad大于1,并且2)设备的disable_ipv6为零(开启状态),并且3)此地址不是stable privacy地址,即地址生成方式不是IN6_ADDR_GEN_MODE_STABLE_PRIVACY。
在确定当前地址与使用生成方式IN6_ADDR_GEN_MODE_EUI64生成的本地链路地址相等之后,关闭IPv6。
static void addrconf_dad_work(struct work_struct *w)
{
struct inet6_ifaddr *ifp = container_of(to_delayed_work(w), struct inet6_ifaddr, dad_work);
struct inet6_dev *idev = ifp->idev;
bool bump_id, disable_ipv6 = false;
if (ifp->state == INET6_IFADDR_STATE_PREDAD) {
...
} else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) {
action = DAD_ABORT;
ifp->state = INET6_IFADDR_STATE_POSTDAD;
if ((dev_net(idev->dev)->ipv6.devconf_all->accept_dad > 1 ||
idev->cnf.accept_dad > 1) &&
!idev->cnf.disable_ipv6 &&
!(ifp->flags & IFA_F_STABLE_PRIVACY)) {
addr.s6_addr32[0] = htonl(0xfe800000);
addr.s6_addr32[1] = 0;
if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) &&
ipv6_addr_equal(&ifp->addr, &addr)) {
/* DAD failed for link-local based on MAC */
idev->cnf.disable_ipv6 = 1;
pr_info("%s: IPv6 being disabled!\n", ifp->idev->dev->name);
内核版本 5.10