Skip to content

Commit 85b51b1

Browse files
Mike Manningdavem330
authored andcommitted
net: ipv6: Remove addresses for failures with strict DAD
If DAD fails with accept_dad set to 2, global addresses and host routes are incorrectly left in place. Even though disable_ipv6 is set, contrary to documentation, the addresses are not dynamically deleted from the interface. It is only on a subsequent link down/up that these are removed. The fix is not only to set the disable_ipv6 flag, but also to call addrconf_ifdown(), which is the action to carry out when disabling IPv6. This results in the addresses and routes being deleted immediately. The DAD failure for the LL addr is determined as before via netlink, or by the absence of the LL addr (which also previously would have had to be checked for in case of an intervening link down and up). As the call to addrconf_ifdown() requires an rtnl lock, the logic to disable IPv6 when DAD fails is moved to addrconf_dad_work(). Previous behavior: root@vm1:/# sysctl net.ipv6.conf.eth3.accept_dad=2 net.ipv6.conf.eth3.accept_dad = 2 root@vm1:/# ip -6 addr add 2000::10/64 dev eth3 root@vm1:/# ip link set up eth3 root@vm1:/# ip -6 addr show dev eth3 5: eth3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qlen 1000 inet6 2000::10/64 scope global valid_lft forever preferred_lft forever inet6 fe80::5054:ff:fe43:dd5a/64 scope link tentative dadfailed valid_lft forever preferred_lft forever root@vm1:/# ip -6 route show dev eth3 2000::/64 proto kernel metric 256 fe80::/64 proto kernel metric 256 root@vm1:/# ip link set down eth3 root@vm1:/# ip link set up eth3 root@vm1:/# ip -6 addr show dev eth3 root@vm1:/# ip -6 route show dev eth3 root@vm1:/# New behavior: root@vm1:/# sysctl net.ipv6.conf.eth3.accept_dad=2 net.ipv6.conf.eth3.accept_dad = 2 root@vm1:/# ip -6 addr add 2000::10/64 dev eth3 root@vm1:/# ip link set up eth3 root@vm1:/# ip -6 addr show dev eth3 root@vm1:/# ip -6 route show dev eth3 root@vm1:/# Signed-off-by: Mike Manning <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 53dc65d commit 85b51b1

File tree

1 file changed

+21
-13
lines changed

1 file changed

+21
-13
lines changed

net/ipv6/addrconf.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1872,7 +1872,6 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp)
18721872

18731873
void addrconf_dad_failure(struct inet6_ifaddr *ifp)
18741874
{
1875-
struct in6_addr addr;
18761875
struct inet6_dev *idev = ifp->idev;
18771876
struct net *net = dev_net(ifp->idev->dev);
18781877

@@ -1934,18 +1933,6 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
19341933
in6_ifa_put(ifp2);
19351934
lock_errdad:
19361935
spin_lock_bh(&ifp->lock);
1937-
} else if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
1938-
addr.s6_addr32[0] = htonl(0xfe800000);
1939-
addr.s6_addr32[1] = 0;
1940-
1941-
if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) &&
1942-
ipv6_addr_equal(&ifp->addr, &addr)) {
1943-
/* DAD failed for link-local based on MAC address */
1944-
idev->cnf.disable_ipv6 = 1;
1945-
1946-
pr_info("%s: IPv6 being disabled!\n",
1947-
ifp->idev->dev->name);
1948-
}
19491936
}
19501937

19511938
errdad:
@@ -3821,6 +3808,7 @@ static void addrconf_dad_work(struct work_struct *w)
38213808
dad_work);
38223809
struct inet6_dev *idev = ifp->idev;
38233810
struct in6_addr mcaddr;
3811+
bool disable_ipv6 = false;
38243812

38253813
enum {
38263814
DAD_PROCESS,
@@ -3837,6 +3825,24 @@ static void addrconf_dad_work(struct work_struct *w)
38373825
} else if (ifp->state == INET6_IFADDR_STATE_ERRDAD) {
38383826
action = DAD_ABORT;
38393827
ifp->state = INET6_IFADDR_STATE_POSTDAD;
3828+
3829+
if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6 &&
3830+
!(ifp->flags & IFA_F_STABLE_PRIVACY)) {
3831+
struct in6_addr addr;
3832+
3833+
addr.s6_addr32[0] = htonl(0xfe800000);
3834+
addr.s6_addr32[1] = 0;
3835+
3836+
if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) &&
3837+
ipv6_addr_equal(&ifp->addr, &addr)) {
3838+
/* DAD failed for link-local based on MAC */
3839+
idev->cnf.disable_ipv6 = 1;
3840+
3841+
pr_info("%s: IPv6 being disabled!\n",
3842+
ifp->idev->dev->name);
3843+
disable_ipv6 = true;
3844+
}
3845+
}
38403846
}
38413847
spin_unlock_bh(&ifp->lock);
38423848

@@ -3845,6 +3851,8 @@ static void addrconf_dad_work(struct work_struct *w)
38453851
goto out;
38463852
} else if (action == DAD_ABORT) {
38473853
addrconf_dad_stop(ifp, 1);
3854+
if (disable_ipv6)
3855+
addrconf_ifdown(idev->dev, 0);
38483856
goto out;
38493857
}
38503858

0 commit comments

Comments
 (0)