Skip to content

Commit

Permalink
ipv6 addrconf: add accept_dad sysctl to control DAD operation.
Browse files Browse the repository at this point in the history
- 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 <[email protected]>
  • Loading branch information
yoshfuji committed Jul 3, 2008
1 parent 778d80b commit 1b34be7
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Documentation/networking/ip-sysctl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,13 @@ disable_ipv6 - BOOLEAN
Disable IPv6 operation.
Default: FALSE (enable IPv6 operation)

accept_dad - INTEGER
Whether to accept DAD (Duplicate Address Detection).
0: Disable DAD
1: Enable DAD (default)
2: Enable DAD, and disable IPv6 operation if MAC-based duplicate
link-local address has been found.

icmp/*:
ratelimit - INTEGER
Limit the maximal rates for sending ICMPv6 packets.
Expand Down
2 changes: 2 additions & 0 deletions include/linux/ipv6.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ struct ipv6_devconf {
__s32 mc_forwarding;
#endif
__s32 disable_ipv6;
__s32 accept_dad;
void *sysctl;
};

Expand Down Expand Up @@ -196,6 +197,7 @@ enum {
DEVCONF_ACCEPT_SOURCE_ROUTE,
DEVCONF_MC_FORWARDING,
DEVCONF_DISABLE_IPV6,
DEVCONF_ACCEPT_DAD,
DEVCONF_MAX
};

Expand Down
35 changes: 35 additions & 0 deletions net/ipv6/addrconf.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ static void ipv6_regen_rndid(unsigned long data);
static int desync_factor = MAX_DESYNC_FACTOR * HZ;
#endif

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

/*
Expand Down Expand Up @@ -184,6 +185,7 @@ struct ipv6_devconf ipv6_devconf __read_mostly = {
.proxy_ndp = 0,
.accept_source_route = 0, /* we do not accept RH0 by default. */
.disable_ipv6 = 0,
.accept_dad = 1,
};

static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
Expand Down Expand Up @@ -217,6 +219,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
.proxy_ndp = 0,
.accept_source_route = 0, /* we do not accept RH0 by default. */
.disable_ipv6 = 0,
.accept_dad = 1,
};

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

if (dev->flags & (IFF_NOARP | IFF_LOOPBACK))
ndev->cnf.accept_dad = -1;

#if defined(CONFIG_IPV6_SIT) || defined(CONFIG_IPV6_SIT_MODULE)
if (dev->type == ARPHRD_SIT && (dev->priv_flags & IFF_ISATAP)) {
printk(KERN_INFO
Expand Down Expand Up @@ -1421,6 +1427,20 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp)

void addrconf_dad_failure(struct inet6_ifaddr *ifp)
{
struct inet6_dev *idev = ifp->idev;
if (idev->cnf.accept_dad > 1 && !idev->cnf.disable_ipv6) {
struct in6_addr addr;

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 address */
idev->cnf.disable_ipv6 = 1;
}
}

if (net_ratelimit())
printk(KERN_INFO "%s: duplicate address detected!\n", ifp->idev->dev->name);
addrconf_dad_stop(ifp);
Expand Down Expand Up @@ -2753,6 +2773,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
spin_lock_bh(&ifp->lock);

if (dev->flags&(IFF_NOARP|IFF_LOOPBACK) ||
idev->cnf.accept_dad < 1 ||
!(ifp->flags&IFA_F_TENTATIVE) ||
ifp->flags & IFA_F_NODAD) {
ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
Expand Down Expand Up @@ -2800,6 +2821,11 @@ static void addrconf_dad_timer(unsigned long data)
read_unlock_bh(&idev->lock);
goto out;
}
if (idev->cnf.accept_dad > 1 && idev->cnf.disable_ipv6) {
read_unlock_bh(&idev->lock);
addrconf_dad_failure(ifp);
return;
}
spin_lock_bh(&ifp->lock);
if (ifp->probes == 0) {
/*
Expand Down Expand Up @@ -3660,6 +3686,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding;
#endif
array[DEVCONF_DISABLE_IPV6] = cnf->disable_ipv6;
array[DEVCONF_ACCEPT_DAD] = cnf->accept_dad;
}

static inline size_t inet6_if_nlmsg_size(void)
Expand Down Expand Up @@ -4226,6 +4253,14 @@ static struct addrconf_sysctl_table
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = CTL_UNNUMBERED,
.procname = "accept_dad",
.data = &ipv6_devconf.accept_dad,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec,
},
{
.ctl_name = 0, /* sentinel */
}
Expand Down

0 comments on commit 1b34be7

Please sign in to comment.