Skip to content

Commit adc176c

Browse files
Erik Nordmarkdavem330
authored andcommitted
ipv6 addrconf: Implemented enhanced DAD (RFC7527)
Implemented RFC7527 Enhanced DAD. IPv6 duplicate address detection can fail if there is some temporary loopback of Ethernet frames. RFC7527 solves this by including a random nonce in the NS messages used for DAD, and if an NS is received with the same nonce it is assumed to be a looped back DAD probe and is ignored. RFC7527 is enabled by default. Can be disabled by setting both of conf/{all,interface}/enhanced_dad to zero. Signed-off-by: Erik Nordmark <[email protected]> Signed-off-by: Bob Gilligan <[email protected]> Reviewed-by: Hannes Frederic Sowa <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ce84c7c commit adc176c

File tree

8 files changed

+64
-6
lines changed

8 files changed

+64
-6
lines changed

Documentation/networking/ip-sysctl.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,15 @@ drop_unsolicited_na - BOOLEAN
17341734

17351735
By default this is turned off.
17361736

1737+
enhanced_dad - BOOLEAN
1738+
Include a nonce option in the IPv6 neighbor solicitation messages used for
1739+
duplicate address detection per RFC7527. A received DAD NS will only signal
1740+
a duplicate address if the nonce is different. This avoids any false
1741+
detection of duplicates due to loopback of the NS messages that we send.
1742+
The nonce option will be sent on an interface unless both of
1743+
conf/{all,interface}/enhanced_dad are set to FALSE.
1744+
Default: TRUE
1745+
17371746
icmp/*:
17381747
ratelimit - INTEGER
17391748
Limit the maximal rates for sending ICMPv6 packets.

include/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct ipv6_devconf {
6868
#ifdef CONFIG_IPV6_SEG6_HMAC
6969
__s32 seg6_require_hmac;
7070
#endif
71+
__u32 enhanced_dad;
7172

7273
struct ctl_table_header *sysctl_header;
7374
};

include/net/if_inet6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct inet6_ifaddr {
5555
__u8 stable_privacy_retry;
5656

5757
__u16 scope;
58+
__u64 dad_nonce;
5859

5960
unsigned long cstamp; /* created timestamp */
6061
unsigned long tstamp; /* updated timestamp */

include/net/ndisc.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ enum {
3131
ND_OPT_PREFIX_INFO = 3, /* RFC2461 */
3232
ND_OPT_REDIRECT_HDR = 4, /* RFC2461 */
3333
ND_OPT_MTU = 5, /* RFC2461 */
34+
ND_OPT_NONCE = 14, /* RFC7527 */
3435
__ND_OPT_ARRAY_MAX,
3536
ND_OPT_ROUTE_INFO = 24, /* RFC4191 */
3637
ND_OPT_RDNSS = 25, /* RFC5006 */
@@ -121,6 +122,7 @@ struct ndisc_options {
121122
#define nd_opts_pi_end nd_opt_array[__ND_OPT_PREFIX_INFO_END]
122123
#define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR]
123124
#define nd_opts_mtu nd_opt_array[ND_OPT_MTU]
125+
#define nd_opts_nonce nd_opt_array[ND_OPT_NONCE]
124126
#define nd_802154_opts_src_lladdr nd_802154_opt_array[ND_OPT_SOURCE_LL_ADDR]
125127
#define nd_802154_opts_tgt_lladdr nd_802154_opt_array[ND_OPT_TARGET_LL_ADDR]
126128

@@ -398,7 +400,8 @@ void ndisc_cleanup(void);
398400
int ndisc_rcv(struct sk_buff *skb);
399401

400402
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
401-
const struct in6_addr *daddr, const struct in6_addr *saddr);
403+
const struct in6_addr *daddr, const struct in6_addr *saddr,
404+
u64 nonce);
402405

403406
void ndisc_send_rs(struct net_device *dev,
404407
const struct in6_addr *saddr, const struct in6_addr *daddr);

include/uapi/linux/ipv6.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ enum {
181181
DEVCONF_RTR_SOLICIT_MAX_INTERVAL,
182182
DEVCONF_SEG6_ENABLED,
183183
DEVCONF_SEG6_REQUIRE_HMAC,
184+
DEVCONF_ENHANCED_DAD,
184185
DEVCONF_MAX
185186
};
186187

net/ipv6/addrconf.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = {
242242
#ifdef CONFIG_IPV6_SEG6_HMAC
243243
.seg6_require_hmac = 0,
244244
#endif
245+
.enhanced_dad = 1,
245246
};
246247

247248
static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
@@ -292,6 +293,7 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = {
292293
#ifdef CONFIG_IPV6_SEG6_HMAC
293294
.seg6_require_hmac = 0,
294295
#endif
296+
.enhanced_dad = 1,
295297
};
296298

297299
/* Check if a valid qdisc is available */
@@ -3735,12 +3737,21 @@ static void addrconf_dad_kick(struct inet6_ifaddr *ifp)
37353737
{
37363738
unsigned long rand_num;
37373739
struct inet6_dev *idev = ifp->idev;
3740+
u64 nonce;
37383741

37393742
if (ifp->flags & IFA_F_OPTIMISTIC)
37403743
rand_num = 0;
37413744
else
37423745
rand_num = prandom_u32() % (idev->cnf.rtr_solicit_delay ? : 1);
37433746

3747+
nonce = 0;
3748+
if (idev->cnf.enhanced_dad ||
3749+
dev_net(idev->dev)->ipv6.devconf_all->enhanced_dad) {
3750+
do
3751+
get_random_bytes(&nonce, 6);
3752+
while (nonce == 0);
3753+
}
3754+
ifp->dad_nonce = nonce;
37443755
ifp->dad_probes = idev->cnf.dad_transmits;
37453756
addrconf_mod_dad_work(ifp, rand_num);
37463757
}
@@ -3918,7 +3929,8 @@ static void addrconf_dad_work(struct work_struct *w)
39183929

39193930
/* send a neighbour solicitation for our addr */
39203931
addrconf_addr_solict_mult(&ifp->addr, &mcaddr);
3921-
ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any);
3932+
ndisc_send_ns(ifp->idev->dev, &ifp->addr, &mcaddr, &in6addr_any,
3933+
ifp->dad_nonce);
39223934
out:
39233935
in6_ifa_put(ifp);
39243936
rtnl_unlock();
@@ -4962,6 +4974,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
49624974
#ifdef CONFIG_IPV6_SEG6_HMAC
49634975
array[DEVCONF_SEG6_REQUIRE_HMAC] = cnf->seg6_require_hmac;
49644976
#endif
4977+
array[DEVCONF_ENHANCED_DAD] = cnf->enhanced_dad;
49654978
}
49664979

49674980
static inline size_t inet6_ifla6_size(void)
@@ -6069,6 +6082,13 @@ static const struct ctl_table addrconf_sysctl[] = {
60696082
.proc_handler = proc_dointvec,
60706083
},
60716084
#endif
6085+
{
6086+
.procname = "enhanced_dad",
6087+
.data = &ipv6_devconf.enhanced_dad,
6088+
.maxlen = sizeof(int),
6089+
.mode = 0644,
6090+
.proc_handler = proc_dointvec,
6091+
},
60726092
{
60736093
/* sentinel */
60746094
}

net/ipv6/ndisc.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
233233
case ND_OPT_SOURCE_LL_ADDR:
234234
case ND_OPT_TARGET_LL_ADDR:
235235
case ND_OPT_MTU:
236+
case ND_OPT_NONCE:
236237
case ND_OPT_REDIRECT_HDR:
237238
if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
238239
ND_PRINTK(2, warn,
@@ -568,7 +569,8 @@ static void ndisc_send_unsol_na(struct net_device *dev)
568569
}
569570

570571
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
571-
const struct in6_addr *daddr, const struct in6_addr *saddr)
572+
const struct in6_addr *daddr, const struct in6_addr *saddr,
573+
u64 nonce)
572574
{
573575
struct sk_buff *skb;
574576
struct in6_addr addr_buf;
@@ -588,6 +590,8 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
588590
if (inc_opt)
589591
optlen += ndisc_opt_addr_space(dev,
590592
NDISC_NEIGHBOUR_SOLICITATION);
593+
if (nonce != 0)
594+
optlen += 8;
591595

592596
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
593597
if (!skb)
@@ -605,6 +609,13 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
605609
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
606610
dev->dev_addr,
607611
NDISC_NEIGHBOUR_SOLICITATION);
612+
if (nonce != 0) {
613+
u8 *opt = skb_put(skb, 8);
614+
615+
opt[0] = ND_OPT_NONCE;
616+
opt[1] = 8 >> 3;
617+
memcpy(opt + 2, &nonce, 6);
618+
}
608619

609620
ndisc_send_skb(skb, daddr, saddr);
610621
}
@@ -693,12 +704,12 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
693704
"%s: trying to ucast probe in NUD_INVALID: %pI6\n",
694705
__func__, target);
695706
}
696-
ndisc_send_ns(dev, target, target, saddr);
707+
ndisc_send_ns(dev, target, target, saddr, 0);
697708
} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
698709
neigh_app_ns(neigh);
699710
} else {
700711
addrconf_addr_solict_mult(target, &mcaddr);
701-
ndisc_send_ns(dev, target, &mcaddr, saddr);
712+
ndisc_send_ns(dev, target, &mcaddr, saddr, 0);
702713
}
703714
}
704715

@@ -742,6 +753,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
742753
int dad = ipv6_addr_any(saddr);
743754
bool inc;
744755
int is_router = -1;
756+
u64 nonce = 0;
745757

746758
if (skb->len < sizeof(struct nd_msg)) {
747759
ND_PRINTK(2, warn, "NS: packet too short\n");
@@ -786,6 +798,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
786798
return;
787799
}
788800
}
801+
if (ndopts.nd_opts_nonce)
802+
memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6);
789803

790804
inc = ipv6_addr_is_multicast(daddr);
791805

@@ -794,6 +808,15 @@ static void ndisc_recv_ns(struct sk_buff *skb)
794808
have_ifp:
795809
if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
796810
if (dad) {
811+
if (nonce != 0 && ifp->dad_nonce == nonce) {
812+
u8 *np = (u8 *)&nonce;
813+
/* Matching nonce if looped back */
814+
ND_PRINTK(2, notice,
815+
"%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n",
816+
ifp->idev->dev->name,
817+
&ifp->addr, np);
818+
goto out;
819+
}
797820
/*
798821
* We are colliding with another node
799822
* who is doing DAD

net/ipv6/route.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ static void rt6_probe_deferred(struct work_struct *w)
527527
container_of(w, struct __rt6_probe_work, work);
528528

529529
addrconf_addr_solict_mult(&work->target, &mcaddr);
530-
ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL);
530+
ndisc_send_ns(work->dev, &work->target, &mcaddr, NULL, 0);
531531
dev_put(work->dev);
532532
kfree(work);
533533
}

0 commit comments

Comments
 (0)