DAD报文控制

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:

  1. 设备自身配置的accept_dad值小于1,并且其所在命名空间中all所对应的accept_dad也小于1;
  2. 当前地址不是暂定地址
  3. 当前地址在配置时使用了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

上一篇:十大经典排序之堆排序(C++实现)


下一篇:对象创建的过程(重点理解)