ipv6 addrconf: add accept_dad sysctl to control DAD operation.
authorYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Sat, 28 Jun 2008 05:18:38 +0000 (14:18 +0900)
committerYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Thu, 3 Jul 2008 08:51:56 +0000 (17:51 +0900)
- 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>
Documentation/networking/ip-sysctl.txt
include/linux/ipv6.h
net/ipv6/addrconf.c

index dae980e8f1b9d32017f44ede5d602f5ef8d5cbfb..72f6d52e52e6e6886321fa3af37248819a41262c 100644 (file)
@@ -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.
index d9d7f9b69eb4f2c8da0e455cd9c459ceb6ef5965..391ad0843a46af6b4af10abc5b7b552a26d95c0e 100644 (file)
@@ -164,6 +164,7 @@ struct ipv6_devconf {
        __s32           mc_forwarding;
 #endif
        __s32           disable_ipv6;
+       __s32           accept_dad;
        void            *sysctl;
 };
 
@@ -196,6 +197,7 @@ enum {
        DEVCONF_ACCEPT_SOURCE_ROUTE,
        DEVCONF_MC_FORWARDING,
        DEVCONF_DISABLE_IPV6,
+       DEVCONF_ACCEPT_DAD,
        DEVCONF_MAX
 };
 
index 8c5cff50bbedc92a3db6c23e7cf82443bc750bcd..2ec73e62202c18b741adc862168c7e0da854be3a 100644 (file)
@@ -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);
 
 /*
@@ -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 = {
@@ -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 */
@@ -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
@@ -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);
@@ -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);
@@ -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) {
                /*
@@ -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)
@@ -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 */
                }