Skip to content

Commit 1b34be7

Browse files
committed
ipv6 addrconf: add accept_dad sysctl to control DAD operation.
- If 0, disable DAD. - If 1, perform DAD (default). - If >1, perform DAD and disable IPv6 operation if DAD for MAC-based link-local address has been failed (RFC4862 5.4.5). We do not follow RFC4862 by default. Refer to the netdev thread entitled "Linux IPv6 DAD not full conform to RFC 4862 ?" http://www.spinics.net/lists/netdev/msg52027.html Signed-off-by: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
1 parent 778d80b commit 1b34be7

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,13 @@ disable_ipv6 - BOOLEAN
10291029
Disable IPv6 operation.
10301030
Default: FALSE (enable IPv6 operation)
10311031

1032+
accept_dad - INTEGER
1033+
Whether to accept DAD (Duplicate Address Detection).
1034+
0: Disable DAD
1035+
1: Enable DAD (default)
1036+
2: Enable DAD, and disable IPv6 operation if MAC-based duplicate
1037+
link-local address has been found.
1038+
10321039
icmp/*:
10331040
ratelimit - INTEGER
10341041
Limit the maximal rates for sending ICMPv6 packets.

include/linux/ipv6.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ struct ipv6_devconf {
164164
__s32 mc_forwarding;
165165
#endif
166166
__s32 disable_ipv6;
167+
__s32 accept_dad;
167168
void *sysctl;
168169
};
169170

@@ -196,6 +197,7 @@ enum {
196197
DEVCONF_ACCEPT_SOURCE_ROUTE,
197198
DEVCONF_MC_FORWARDING,
198199
DEVCONF_DISABLE_IPV6,
200+
DEVCONF_ACCEPT_DAD,
199201
DEVCONF_MAX
200202
};
201203

net/ipv6/addrconf.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ static void ipv6_regen_rndid(unsigned long data);
119119
static int desync_factor = MAX_DESYNC_FACTOR * HZ;
120120
#endif
121121

122+
static int ipv6_generate_eui64(u8 *eui, struct net_device *dev);
122123
static int ipv6_count_addresses(struct inet6_dev *idev);
123124

124125
/*
@@ -184,6 +185,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = {
184185
.proxy_ndp = 0,
185186
.accept_source_route = 0, /* we do not accept RH0 by default. */
186187
.disable_ipv6 = 0,
188+
.accept_dad = 1,
187189
};
188190

189191
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -217,6 +219,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
217219
.proxy_ndp = 0,
218220
.accept_source_route = 0, /* we do not accept RH0 by default. */
219221
.disable_ipv6 = 0,
222+
.accept_dad = 1,
220223
};
221224

222225
/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
@@ -380,6 +383,9 @@ static struct inet6_dev * ipv6_add_dev(struct net_device *dev)
380383
*/
381384
in6_dev_hold(ndev);
382385

386+
if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
387+
ndev->cnf.accept_dad = -1;
388+
383389
#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
384390
if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
385391
printk(KERN_INFO
@@ -1421,6 +1427,20 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp)
14211427

14221428
void addrconf_dad_failure(struct inet6_ifaddr *ifp)
14231429
{
1430+
struct inet6_dev *idev = ifp->idev;
1431+
if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
1432+
struct in6_addr addr;
1433+
1434+
addr.s6_addr32[0] = htonl(0xfe800000);
1435+
addr.s6_addr32[1] = 0;
1436+
1437+
if (!ipv6_generate_eui64(addr.s6_addr + 8, idev->dev) &&
1438+
ipv6_addr_equal(&ifp->addr, &addr)) {
1439+
/* DAD failed for link-local based on MAC address */
1440+
idev->cnf.disable_ipv6 = 1;
1441+
}
1442+
}
1443+
14241444
if (net_ratelimit())
14251445
printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
14261446
addrconf_dad_stop(ifp);
@@ -2753,6 +2773,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
27532773
spin_lock_bh(&ifp->lock);
27542774

27552775
if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
2776+
idev->cnf.accept_dad < 1 ||
27562777
!(ifp->flags&IFA_F_TENTATIVE) ||
27572778
ifp->flags & IFA_F_NODAD) {
27582779
ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
@@ -2800,6 +2821,11 @@ static void addrconf_dad_timer(unsigned long data)
28002821
read_unlock_bh(&idev->lock);
28012822
goto out;
28022823
}
2824+
if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) {
2825+
read_unlock_bh(&idev->lock);
2826+
addrconf_dad_failure(ifp);
2827+
return;
2828+
}
28032829
spin_lock_bh(&ifp->lock);
28042830
if (ifp->probes == 0) {
28052831
/*
@@ -3660,6 +3686,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
36603686
array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding;
36613687
#endif
36623688
array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
3689+
array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
36633690
}
36643691

36653692
static inline size_t inet6_if_nlmsg_size(void)
@@ -4226,6 +4253,14 @@ static struct addrconf_sysctl_table
42264253
.mode = 0644,
42274254
.proc_handler = &proc_dointvec,
42284255
},
4256+
{
4257+
.ctl_name = CTL_UNNUMBERED,
4258+
.procname = "accept_dad",
4259+
.data = &ipv6_devconf.accept_dad,
4260+
.maxlen = sizeof(int),
4261+
.mode = 0644,
4262+
.proc_handler = &proc_dointvec,
4263+
},
42294264
{
42304265
.ctl_name = 0, /* sentinel */
42314266
}

0 commit comments

Comments
 (0)