Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: allow userspace to add IFA_F_OPTIMISTIC addresses

According to RFC 4429 (section 3.1), adding new IPv6 addresses as
optimistic addresses is acceptable, as long as the implementation
follows some rules:

* Optimistic DAD SHOULD only be used when the implementation is aware
that the address is based on a most likely unique interface
identifier (such as in [RFC2464]), generated randomly [RFC3041],
or by a well-distributed hash function [RFC3972] or assigned by
Dynamic Host Configuration Protocol for IPv6 (DHCPv6) [RFC3315].
Optimistic DAD SHOULD NOT be used for manually entered
addresses.

Thus, it seems reasonable to allow userspace to set the optimistic flag
when adding new addresses.

We must not let userspace set NODAD + OPTIMISTIC, since if the kernel is
not performing DAD we would never clear the optimistic flag. We must
also ignore userspace's request to add OPTIMISTIC flag to addresses that
have already completed DAD (addresses that don't have the TENTATIVE
flag, or that have the DADFAILED flag).

Then we also need to clear the OPTIMISTIC flag on permanent addresses
when DAD fails. Otherwise, IFA_F_OPTIMISTIC addresses added by userspace
can still be used after DAD has failed, because in
ipv6_chk_addr_and_flags(), IFA_F_OPTIMISTIC overrides IFA_F_TENTATIVE.

Setting IFA_F_OPTIMISTIC from userspace is conditional on
CONFIG_IPV6_OPTIMISTIC_DAD and the optimistic_dad sysctl.

Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
Reviewed-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Sabrina Dubroca and committed by
David S. Miller
f1c02cfb 3a053b1a

+34 -1
+34 -1
net/ipv6/addrconf.c
··· 1459 1459 #endif 1460 1460 } 1461 1461 1462 + static bool ipv6_allow_optimistic_dad(struct net *net, 1463 + struct inet6_dev *idev) 1464 + { 1465 + #ifdef CONFIG_IPV6_OPTIMISTIC_DAD 1466 + if (!idev) 1467 + return false; 1468 + if (!net->ipv6.devconf_all->optimistic_dad && !idev->cnf.optimistic_dad) 1469 + return false; 1470 + 1471 + return true; 1472 + #else 1473 + return false; 1474 + #endif 1475 + } 1476 + 1462 1477 static int ipv6_get_saddr_eval(struct net *net, 1463 1478 struct ipv6_saddr_score *score, 1464 1479 struct ipv6_saddr_dst *dst, ··· 1983 1968 spin_lock_bh(&ifp->lock); 1984 1969 addrconf_del_dad_work(ifp); 1985 1970 ifp->flags |= IFA_F_TENTATIVE; 1971 + if (dad_failed) 1972 + ifp->flags &= ~IFA_F_OPTIMISTIC; 1986 1973 spin_unlock_bh(&ifp->lock); 1987 1974 if (dad_failed) 1988 1975 ipv6_ifa_notify(0, ifp); ··· 4518 4501 (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64)) 4519 4502 return -EINVAL; 4520 4503 4504 + if (!(ifp->flags & IFA_F_TENTATIVE) || ifp->flags & IFA_F_DADFAILED) 4505 + ifa_flags &= ~IFA_F_OPTIMISTIC; 4506 + 4521 4507 timeout = addrconf_timeout_fixup(valid_lft, HZ); 4522 4508 if (addrconf_finite_timeout(timeout)) { 4523 4509 expires = jiffies_to_clock_t(timeout * HZ); ··· 4594 4574 struct in6_addr *pfx, *peer_pfx; 4595 4575 struct inet6_ifaddr *ifa; 4596 4576 struct net_device *dev; 4577 + struct inet6_dev *idev; 4597 4578 u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME; 4598 4579 u32 ifa_flags; 4599 4580 int err; ··· 4628 4607 4629 4608 /* We ignore other flags so far. */ 4630 4609 ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR | 4631 - IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN; 4610 + IFA_F_NOPREFIXROUTE | IFA_F_MCAUTOJOIN | IFA_F_OPTIMISTIC; 4611 + 4612 + idev = ipv6_find_idev(dev); 4613 + if (IS_ERR(idev)) 4614 + return PTR_ERR(idev); 4615 + 4616 + if (!ipv6_allow_optimistic_dad(net, idev)) 4617 + ifa_flags &= ~IFA_F_OPTIMISTIC; 4618 + 4619 + if (ifa_flags & IFA_F_NODAD && ifa_flags & IFA_F_OPTIMISTIC) { 4620 + NL_SET_ERR_MSG(extack, "IFA_F_NODAD and IFA_F_OPTIMISTIC are mutually exclusive"); 4621 + return -EINVAL; 4622 + } 4632 4623 4633 4624 ifa = ipv6_get_ifaddr(net, pfx, dev, 1); 4634 4625 if (!ifa) {