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 <nordmark@arista.com> Signed-off-by: Bob Gilligan <gilligan@arista.com> Reviewed-by: Hannes Frederic Sowa <hannes@stressinduktion.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
ce84c7c663
commit
adc176c547
@@ -233,6 +233,7 @@ struct ndisc_options *ndisc_parse_options(const struct net_device *dev,
|
||||
case ND_OPT_SOURCE_LL_ADDR:
|
||||
case ND_OPT_TARGET_LL_ADDR:
|
||||
case ND_OPT_MTU:
|
||||
case ND_OPT_NONCE:
|
||||
case ND_OPT_REDIRECT_HDR:
|
||||
if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
|
||||
ND_PRINTK(2, warn,
|
||||
@@ -568,7 +569,8 @@ static void ndisc_send_unsol_na(struct net_device *dev)
|
||||
}
|
||||
|
||||
void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
const struct in6_addr *daddr, const struct in6_addr *saddr)
|
||||
const struct in6_addr *daddr, const struct in6_addr *saddr,
|
||||
u64 nonce)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct in6_addr addr_buf;
|
||||
@@ -588,6 +590,8 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
if (inc_opt)
|
||||
optlen += ndisc_opt_addr_space(dev,
|
||||
NDISC_NEIGHBOUR_SOLICITATION);
|
||||
if (nonce != 0)
|
||||
optlen += 8;
|
||||
|
||||
skb = ndisc_alloc_skb(dev, sizeof(*msg) + optlen);
|
||||
if (!skb)
|
||||
@@ -605,6 +609,13 @@ void ndisc_send_ns(struct net_device *dev, const struct in6_addr *solicit,
|
||||
ndisc_fill_addr_option(skb, ND_OPT_SOURCE_LL_ADDR,
|
||||
dev->dev_addr,
|
||||
NDISC_NEIGHBOUR_SOLICITATION);
|
||||
if (nonce != 0) {
|
||||
u8 *opt = skb_put(skb, 8);
|
||||
|
||||
opt[0] = ND_OPT_NONCE;
|
||||
opt[1] = 8 >> 3;
|
||||
memcpy(opt + 2, &nonce, 6);
|
||||
}
|
||||
|
||||
ndisc_send_skb(skb, daddr, saddr);
|
||||
}
|
||||
@@ -693,12 +704,12 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
|
||||
"%s: trying to ucast probe in NUD_INVALID: %pI6\n",
|
||||
__func__, target);
|
||||
}
|
||||
ndisc_send_ns(dev, target, target, saddr);
|
||||
ndisc_send_ns(dev, target, target, saddr, 0);
|
||||
} else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
|
||||
neigh_app_ns(neigh);
|
||||
} else {
|
||||
addrconf_addr_solict_mult(target, &mcaddr);
|
||||
ndisc_send_ns(dev, target, &mcaddr, saddr);
|
||||
ndisc_send_ns(dev, target, &mcaddr, saddr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,6 +753,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
|
||||
int dad = ipv6_addr_any(saddr);
|
||||
bool inc;
|
||||
int is_router = -1;
|
||||
u64 nonce = 0;
|
||||
|
||||
if (skb->len < sizeof(struct nd_msg)) {
|
||||
ND_PRINTK(2, warn, "NS: packet too short\n");
|
||||
@@ -786,6 +798,8 @@ static void ndisc_recv_ns(struct sk_buff *skb)
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (ndopts.nd_opts_nonce)
|
||||
memcpy(&nonce, (u8 *)(ndopts.nd_opts_nonce + 1), 6);
|
||||
|
||||
inc = ipv6_addr_is_multicast(daddr);
|
||||
|
||||
@@ -794,6 +808,15 @@ static void ndisc_recv_ns(struct sk_buff *skb)
|
||||
have_ifp:
|
||||
if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {
|
||||
if (dad) {
|
||||
if (nonce != 0 && ifp->dad_nonce == nonce) {
|
||||
u8 *np = (u8 *)&nonce;
|
||||
/* Matching nonce if looped back */
|
||||
ND_PRINTK(2, notice,
|
||||
"%s: IPv6 DAD loopback for address %pI6c nonce %pM ignored\n",
|
||||
ifp->idev->dev->name,
|
||||
&ifp->addr, np);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* We are colliding with another node
|
||||
* who is doing DAD
|
||||
|
Reference in New Issue
Block a user