aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet6
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/in6.c35
-rw-r--r--sys/netinet6/in6.h7
-rw-r--r--sys/netinet6/in6_ifattach.c284
-rw-r--r--sys/netinet6/in6_ifattach.h3
-rw-r--r--sys/netinet6/in6_proto.c10
-rw-r--r--sys/netinet6/in6_src.c54
-rw-r--r--sys/netinet6/in6_var.h2
-rw-r--r--sys/netinet6/ip6_input.c54
-rw-r--r--sys/netinet6/ip6_var.h14
-rw-r--r--sys/netinet6/nd6.c5
-rw-r--r--sys/netinet6/nd6.h5
-rw-r--r--sys/netinet6/nd6_nbr.c158
-rw-r--r--sys/netinet6/nd6_rtr.c121
13 files changed, 557 insertions, 195 deletions
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index ce0655408a28..b98703bdfbfe 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -1235,11 +1235,20 @@ in6_addifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *i
int carp_attached = 0;
int error;
- /* Check if this interface is a bridge member */
- if (ifp->if_bridge && bridge_member_ifaddrs_p &&
- !bridge_member_ifaddrs_p()) {
- error = EINVAL;
- goto out;
+ /*
+ * Check if bridge wants to allow adding addrs to member interfaces.
+ */
+ if (ifp->if_bridge != NULL && ifp->if_type != IFT_GIF &&
+ bridge_member_ifaddrs_p != NULL) {
+ if (bridge_member_ifaddrs_p()) {
+ if_printf(ifp, "WARNING: Assigning an IP address to "
+ "an interface which is also a bridge member is "
+ "deprecated and will be unsupported in a future "
+ "release.\n");
+ } else {
+ error = EINVAL;
+ goto out;
+ }
}
/*
@@ -1286,8 +1295,8 @@ in6_addifaddr(struct ifnet *ifp, struct in6_aliasreq *ifra, struct in6_ifaddr *i
*/
bzero(&pr0, sizeof(pr0));
pr0.ndpr_ifp = ifp;
- pr0.ndpr_plen = in6_mask2len(&ifra->ifra_prefixmask.sin6_addr,
- NULL);
+ pr0.ndpr_plen = ia->ia_plen =
+ in6_mask2len(&ifra->ifra_prefixmask.sin6_addr, NULL);
if (pr0.ndpr_plen == 128) {
/* we don't need to install a host route. */
goto aifaddr_out;
@@ -1481,16 +1490,16 @@ in6_unlink_ifa(struct in6_ifaddr *ia, struct ifnet *ifp)
* positive reference.
*/
remove_lle = 0;
- if (ia->ia6_ndpr == NULL) {
- nd6log((LOG_NOTICE,
- "in6_unlink_ifa: autoconf'ed address "
- "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia))));
- } else {
+ if (ia->ia6_ndpr != NULL) {
ia->ia6_ndpr->ndpr_addrcnt--;
/* Do not delete lles within prefix if refcont != 0 */
if (ia->ia6_ndpr->ndpr_addrcnt == 0)
remove_lle = 1;
ia->ia6_ndpr = NULL;
+ } else if (ia->ia_plen < 128) {
+ nd6log((LOG_NOTICE,
+ "in6_unlink_ifa: autoconf'ed address "
+ "%s has no prefix\n", ip6_sprintf(ip6buf, IA6_IN6(ia))));
}
nd6_rem_ifa_lle(ia, remove_lle);
@@ -2618,6 +2627,8 @@ in6_domifdetach(struct ifnet *ifp, void *aux)
{
struct in6_ifextra *ext = (struct in6_ifextra *)aux;
+ MPASS(ifp->if_afdata[AF_INET6] == NULL);
+
mld_domifdetach(ifp);
scope6_ifdetach(ext->scope6_id);
nd6_ifdetach(ifp, ext->nd_ifinfo);
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
index 1ca846ebf514..a7fe03b9c3d7 100644
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -358,11 +358,11 @@ extern const struct in6_addr in6addr_linklocal_allv2routers;
#define IFA6_IS_DEPRECATED(a) \
((a)->ia6_lifetime.ia6t_pltime != ND6_INFINITE_LIFETIME && \
- (u_int32_t)((time_uptime - (a)->ia6_updatetime)) > \
+ (u_int32_t)((time_uptime - (a)->ia6_updatetime)) >= \
(a)->ia6_lifetime.ia6t_pltime)
#define IFA6_IS_INVALID(a) \
((a)->ia6_lifetime.ia6t_vltime != ND6_INFINITE_LIFETIME && \
- (u_int32_t)((time_uptime - (a)->ia6_updatetime)) > \
+ (u_int32_t)((time_uptime - (a)->ia6_updatetime)) >= \
(a)->ia6_lifetime.ia6t_vltime)
#endif /* _KERNEL */
@@ -609,6 +609,8 @@ struct ip6_mtuinfo {
/* IPV6CTL_RTMINEXPIRE 26 deprecated */
/* IPV6CTL_RTMAXCACHE 27 deprecated */
+#define IPV6CTL_STABLEADDR_NETIFSRC 30 /* semantically opaque addresses (RFC7217) hash algo netif parameter src */
+#define IPV6CTL_STABLEADDR_MAXRETRIES 31 /* semantically opaque addresses (RFC7217) max DAD retries */
#define IPV6CTL_USETEMPADDR 32 /* use temporary addresses (RFC3041) */
#define IPV6CTL_TEMPPLTIME 33 /* preferred lifetime for tmpaddrs */
#define IPV6CTL_TEMPVLTIME 34 /* valid lifetime for tmpaddrs */
@@ -617,6 +619,7 @@ struct ip6_mtuinfo {
#define IPV6CTL_PREFER_TEMPADDR 37 /* prefer temporary addr as src */
#define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */
#define IPV6CTL_USE_DEFAULTZONE 39 /* use default scope zone */
+#define IPV6CTL_USESTABLEADDR 40 /* use semantically opaque addresses (RFC7217) */
#define IPV6CTL_MAXFRAGS 41 /* max fragments */
#if 0
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
index f284f7fa5ffc..090ba610460b 100644
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -33,6 +33,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/counter.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@@ -70,6 +71,9 @@
#include <netinet6/mld6_var.h>
#include <netinet6/scope6_var.h>
+#include <crypto/sha2/sha256.h>
+#include <machine/atomic.h>
+
#ifdef IP6_AUTO_LINKLOCAL
VNET_DEFINE(int, ip6_auto_linklocal) = IP6_AUTO_LINKLOCAL;
#else
@@ -79,11 +83,12 @@ VNET_DEFINE(int, ip6_auto_linklocal) = 1; /* enabled by default */
VNET_DEFINE(struct callout, in6_tmpaddrtimer_ch);
#define V_in6_tmpaddrtimer_ch VNET(in6_tmpaddrtimer_ch)
+VNET_DEFINE(int, ip6_stableaddr_netifsource) = IP6_STABLEADDR_NETIFSRC_NAME; /* Use interface name by default */
+
VNET_DECLARE(struct inpcbinfo, ripcbinfo);
#define V_ripcbinfo VNET(ripcbinfo)
static int get_rand_ifid(struct ifnet *, struct in6_addr *);
-static int get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
static int in6_ifattach_linklocal(struct ifnet *, struct ifnet *);
static int in6_ifattach_loopback(struct ifnet *);
static void in6_purgemaddrs(struct ifnet *);
@@ -99,6 +104,9 @@ static void in6_purgemaddrs(struct ifnet *);
#define IFID_LOCAL(in6) (!EUI64_LOCAL(in6))
#define IFID_UNIVERSAL(in6) (!EUI64_UNIVERSAL(in6))
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5C
+
/*
* Generate a last-resort interface identifier, when the machine has no
* IEEE802/EUI64 address sources.
@@ -148,22 +156,14 @@ get_rand_ifid(struct ifnet *ifp, struct in6_addr *in6)
}
-/*
- * Get interface identifier for the specified interface.
- * XXX assumes single sockaddr_dl (AF_LINK address) per an interface
- *
- * in6 - upper 64bits are preserved
+/**
+ * Get interface link level sockaddr
*/
-int
-in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
+static struct sockaddr_dl *
+get_interface_link_level(struct ifnet *ifp)
{
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
- u_int8_t *addr;
- size_t addrlen;
- static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- static u_int8_t allone[8] =
- { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
NET_EPOCH_ASSERT();
@@ -176,14 +176,30 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
if (sdl->sdl_alen == 0)
continue;
- goto found;
+ return sdl;
}
- return -1;
+ return NULL;
+}
+
+/*
+ * Get hwaddr from link interface
+ */
+static uint8_t *
+in6_get_interface_hwaddr(struct ifnet *ifp, size_t *len)
+{
+ struct sockaddr_dl *sdl;
+ u_int8_t *addr;
+ static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static u_int8_t allone[8] =
+ { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ sdl = get_interface_link_level(ifp);
+ if (sdl == NULL)
+ return (NULL);
-found:
addr = LLADDR(sdl);
- addrlen = sdl->sdl_alen;
+ *len = sdl->sdl_alen;
/* get EUI64 */
switch (ifp->if_type) {
@@ -194,36 +210,21 @@ found:
case IFT_IEEE1394:
/* IEEE802/EUI64 cases - what others? */
/* IEEE1394 uses 16byte length address starting with EUI64 */
- if (addrlen > 8)
- addrlen = 8;
+ if (*len > 8)
+ *len = 8;
/* look at IEEE802/EUI64 only */
- if (addrlen != 8 && addrlen != 6)
- return -1;
+ if (*len != 8 && *len != 6)
+ return (NULL);
/*
* check for invalid MAC address - on bsdi, we see it a lot
* since wildboar configures all-zero MAC on pccard before
* card insertion.
*/
- if (bcmp(addr, allzero, addrlen) == 0)
- return -1;
- if (bcmp(addr, allone, addrlen) == 0)
- return -1;
-
- /* make EUI64 address */
- if (addrlen == 8)
- bcopy(addr, &in6->s6_addr[8], 8);
- else if (addrlen == 6) {
- in6->s6_addr[8] = addr[0];
- in6->s6_addr[9] = addr[1];
- in6->s6_addr[10] = addr[2];
- in6->s6_addr[11] = 0xff;
- in6->s6_addr[12] = 0xfe;
- in6->s6_addr[13] = addr[3];
- in6->s6_addr[14] = addr[4];
- in6->s6_addr[15] = addr[5];
- }
+ if (memcmp(addr, allzero, *len) == 0 || memcmp(addr, allone, *len) == 0)
+ return (NULL);
+
break;
case IFT_GIF:
@@ -234,16 +235,51 @@ found:
* identifier source (can be renumbered).
* we don't do this.
*/
- return -1;
+ return (NULL);
case IFT_INFINIBAND:
- if (addrlen != 20)
- return -1;
- bcopy(addr + 12, &in6->s6_addr[8], 8);
+ if (*len != 20)
+ return (NULL);
+ *len = 8;
+ addr += 12;
break;
default:
+ return (NULL);
+ }
+
+ return addr;
+}
+
+ /*
+ * Get interface identifier for the specified interface.
+ * XXX assumes single sockaddr_dl (AF_LINK address) per an interface
+ *
+ * in6 - upper 64bits are preserved
+ */
+int
+in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
+{
+ size_t hwaddr_len;
+ uint8_t *hwaddr;
+ static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+ hwaddr = in6_get_interface_hwaddr(ifp, &hwaddr_len);
+ if (hwaddr == NULL || (hwaddr_len != 6 && hwaddr_len != 8))
return -1;
+
+ /* make EUI64 address */
+ if (hwaddr_len == 8)
+ memcpy(&in6->s6_addr[8], hwaddr, 8);
+ else if (hwaddr_len == 6) {
+ in6->s6_addr[8] = hwaddr[0];
+ in6->s6_addr[9] = hwaddr[1];
+ in6->s6_addr[10] = hwaddr[2];
+ in6->s6_addr[11] = 0xff;
+ in6->s6_addr[12] = 0xfe;
+ in6->s6_addr[13] = hwaddr[3];
+ in6->s6_addr[14] = hwaddr[4];
+ in6->s6_addr[15] = hwaddr[5];
}
/* sanity check: g bit must not indicate "group" */
@@ -265,21 +301,175 @@ found:
}
/*
+ * Validate generated interface id to make sure it does not fall in any reserved range:
+ *
+ * https://www.iana.org/assignments/ipv6-interface-ids/ipv6-interface-ids.xhtml
+ */
+static bool
+validate_ifid(uint8_t *iid)
+{
+ static uint8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ static uint8_t reserved_eth[5] = { 0x02, 0x00, 0x5E, 0xFF, 0xFE };
+ static uint8_t reserved_anycast[7] = { 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+ /* Subnet-Router Anycast (RFC 4291)*/
+ if (memcmp(iid, allzero, 8) == 0)
+ return (false);
+
+ /*
+ * Reserved IPv6 Interface Identifiers corresponding to the IANA Ethernet Block (RFC 4291)
+ * and
+ * Proxy Mobile IPv6 (RFC 6543)
+ */
+ if (memcmp(iid, reserved_eth, 5) == 0)
+ return (false);
+
+ /* Reserved Subnet Anycast Addresses (RFC 2526) */
+ if (memcmp(iid, reserved_anycast, 7) == 0 && iid[7] >= 0x80)
+ return (false);
+
+ return (true);
+}
+
+/*
+ * Get interface identifier for the specified interface, according to
+ * RFC 7217 Stable and Opaque IDs with SLAAC, using HMAC-SHA256 digest.
+ *
+ * in6 - upper 64bits are preserved
+ */
+bool
+in6_get_stableifid(struct ifnet *ifp, struct in6_addr *in6, int prefixlen)
+{
+ struct sockaddr_dl *sdl;
+ const uint8_t *netiface;
+ size_t netiface_len, hostuuid_len;
+ uint8_t hostuuid[HOSTUUIDLEN + 1], hmac_key[SHA256_BLOCK_LENGTH],
+ hk_ipad[SHA256_BLOCK_LENGTH], hk_opad[SHA256_BLOCK_LENGTH];
+ uint64_t dad_failures;
+ SHA256_CTX ctxt;
+
+ switch (V_ip6_stableaddr_netifsource) {
+ case IP6_STABLEADDR_NETIFSRC_ID:
+ sdl = get_interface_link_level(ifp);
+ if (sdl == NULL)
+ return (false);
+ netiface = (uint8_t *)&LLINDEX(sdl);
+ netiface_len = sizeof(u_short); /* real return type of LLINDEX */
+ break;
+
+ case IP6_STABLEADDR_NETIFSRC_MAC:
+ netiface = in6_get_interface_hwaddr(ifp, &netiface_len);
+ if (netiface == NULL)
+ return (false);
+ break;
+
+ case IP6_STABLEADDR_NETIFSRC_NAME:
+ default:
+ netiface = (const uint8_t *)if_name(ifp);
+ netiface_len = strlen(netiface);
+ break;
+ }
+
+ /* Use hostuuid as constant "secret" key */
+ getcredhostuuid(curthread->td_ucred, hostuuid, sizeof(hostuuid));
+ if (strncmp(hostuuid, DEFAULT_HOSTUUID, sizeof(hostuuid)) == 0) {
+ // If hostuuid is not set, use a random value
+ arc4rand(hostuuid, HOSTUUIDLEN, 0);
+ hostuuid[HOSTUUIDLEN] = '\0';
+ }
+ hostuuid_len = strlen(hostuuid);
+
+ dad_failures = atomic_load_int(&DAD_FAILURES(ifp));
+
+ /*
+ * RFC 7217 section 7
+ *
+ * default max retries
+ */
+ if (dad_failures > V_ip6_stableaddr_maxretries)
+ return (false);
+
+ /*
+ * Use hostuuid as basis for HMAC key
+ */
+ memset(hmac_key, 0, sizeof(hmac_key));
+ if (hostuuid_len <= SHA256_BLOCK_LENGTH) {
+ /* copy to hmac key variable, zero padded */
+ memcpy(hmac_key, hostuuid, hostuuid_len);
+ } else {
+ /* if longer than block length, use hash of the value, zero padded */
+ SHA256_Init(&ctxt);
+ SHA256_Update(&ctxt, hostuuid, hostuuid_len);
+ SHA256_Final(hmac_key, &ctxt);
+ }
+ /* XOR key with ipad and opad values */
+ for (uint16_t i = 0; i < sizeof(hmac_key); i++) {
+ hk_ipad[i] = hmac_key[i] ^ HMAC_IPAD;
+ hk_opad[i] = hmac_key[i] ^ HMAC_OPAD;
+ }
+
+ /*
+ * Generate interface id in a loop, adding an offset to be factored in the hash function.
+ * This is necessary, because if the generated interface id happens to be invalid we
+ * want to force the hash function to generate a different one, otherwise we would end up
+ * in an infinite loop trying the same invalid interface id over and over again.
+ *
+ * Using an uint8 counter for the offset, so limit iteration at UINT8_MAX. This is a safety
+ * measure, this will never iterate more than once or twice in practice.
+ */
+ for(uint8_t offset = 0; offset < UINT8_MAX; offset++) {
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+
+ /* Calculate inner hash */
+ SHA256_Init(&ctxt);
+ SHA256_Update(&ctxt, hk_ipad, sizeof(hk_ipad));
+ SHA256_Update(&ctxt, in6->s6_addr, prefixlen / 8);
+ SHA256_Update(&ctxt, netiface, netiface_len);
+ SHA256_Update(&ctxt, (uint8_t *)&dad_failures, 8);
+ SHA256_Update(&ctxt, hostuuid, hostuuid_len);
+ SHA256_Update(&ctxt, &offset, 1);
+ SHA256_Final(digest, &ctxt);
+
+ /* Calculate outer hash */
+ SHA256_Init(&ctxt);
+ SHA256_Update(&ctxt, hk_opad, sizeof(hk_opad));
+ SHA256_Update(&ctxt, digest, sizeof(digest));
+ SHA256_Final(digest, &ctxt);
+
+ if (validate_ifid(digest)) {
+ /* assumes sizeof(digest) > sizeof(ifid) */
+ memcpy(&in6->s6_addr[8], digest, 8);
+
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+/*
* Get interface identifier for the specified interface. If it is not
* available on ifp0, borrow interface identifier from other information
* sources.
*
* altifp - secondary EUI64 source
*/
-static int
-get_ifid(struct ifnet *ifp0, struct ifnet *altifp,
+int
+in6_get_ifid(struct ifnet *ifp0, struct ifnet *altifp,
struct in6_addr *in6)
{
struct ifnet *ifp;
NET_EPOCH_ASSERT();
- /* first, try to get it from the interface itself */
+ /* first, try to get it from the interface itself, with stable algorithm, if configured */
+ if ((ND_IFINFO(ifp0)->flags & ND6_IFF_STABLEADDR) && in6_get_stableifid(ifp0, in6, 64) == 0) {
+ nd6log((LOG_DEBUG, "%s: got interface identifier from itself (stable private)\n",
+ if_name(ifp0)));
+ goto success;
+ }
+
+ /* then/otherwise try to get it from the interface itself */
if (in6_get_hw_ifid(ifp0, in6) == 0) {
nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n",
if_name(ifp0)));
@@ -356,7 +546,7 @@ in6_ifattach_linklocal(struct ifnet *ifp, struct ifnet *altifp)
ifra.ifra_addr.sin6_addr.s6_addr32[3] = htonl(1);
} else {
NET_EPOCH_ENTER(et);
- error = get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr);
+ error = in6_get_ifid(ifp, altifp, &ifra.ifra_addr.sin6_addr);
NET_EPOCH_EXIT(et);
if (error != 0) {
nd6log((LOG_ERR,
diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h
index 897926e90078..75b2ca4fa018 100644
--- a/sys/netinet6/in6_ifattach.h
+++ b/sys/netinet6/in6_ifattach.h
@@ -39,8 +39,11 @@ void in6_ifattach(struct ifnet *, struct ifnet *);
void in6_ifattach_destroy(void);
void in6_ifdetach(struct ifnet *);
void in6_ifdetach_destroy(struct ifnet *);
+int in6_get_tmpifid(struct ifnet *, u_int8_t *, const u_int8_t *, int);
+bool in6_get_stableifid(struct ifnet *, struct in6_addr *, int);
void in6_tmpaddrtimer(void *);
int in6_get_hw_ifid(struct ifnet *, struct in6_addr *);
+int in6_get_ifid(struct ifnet *, struct ifnet *, struct in6_addr *);
int in6_nigroup(struct ifnet *, const char *, int, struct in6_addr *);
int in6_nigroup_oldmcprefix(struct ifnet *, const char *, int, struct in6_addr *);
#endif /* _KERNEL */
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index b289d4eeb0a2..f567b42b42ca 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -167,6 +167,7 @@ VNET_DEFINE(int, ip6_rr_prune) = 5; /* router renumbering prefix
* walk list every 5 sec. */
VNET_DEFINE(int, ip6_mcast_pmtu) = 0; /* enable pMTU discovery for multicast? */
VNET_DEFINE(int, ip6_v6only) = 1;
+VNET_DEFINE(u_int, ip6_stableaddr_maxretries) = IP6_IDGEN_RETRIES;
#ifdef IPSTEALTH
VNET_DEFINE(int, ip6stealth) = 0;
@@ -313,6 +314,15 @@ SYSCTL_INT(_net_inet6_ip6, IPV6CTL_RR_PRUNE, rr_prune,
SYSCTL_INT(_net_inet6_ip6, IPV6CTL_USETEMPADDR, use_tempaddr,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_use_tempaddr), 0,
"Create RFC3041 temporary addresses for autoconfigured addresses");
+SYSCTL_BOOL(_net_inet6_ip6, IPV6CTL_USESTABLEADDR, use_stableaddr,
+ CTLFLAG_VNET | CTLFLAG_RWTUN, &VNET_NAME(ip6_use_stableaddr), 0,
+ "Create RFC7217 semantically opaque address for autoconfigured addresses (default for new interfaces)");
+SYSCTL_UINT(_net_inet6_ip6, IPV6CTL_STABLEADDR_MAXRETRIES, stableaddr_maxretries,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_stableaddr_maxretries), IP6_IDGEN_RETRIES,
+ "RFC7217 semantically opaque address DAD max retries");
+SYSCTL_INT(_net_inet6_ip6, IPV6CTL_STABLEADDR_NETIFSRC, stableaddr_netifsource,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_stableaddr_netifsource), IP6_STABLEADDR_NETIFSRC_NAME,
+ "RFC7217 semantically opaque address Net_Iface source (0 - name, 1 - ID, 2 - MAC addr)");
SYSCTL_PROC(_net_inet6_ip6, IPV6CTL_TEMPPLTIME, temppltime,
CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
NULL, 0, sysctl_ip6_temppltime, "I",
diff --git a/sys/netinet6/in6_src.c b/sys/netinet6/in6_src.c
index dd6864482b3c..3e55c6e5fc05 100644
--- a/sys/netinet6/in6_src.c
+++ b/sys/netinet6/in6_src.c
@@ -132,8 +132,8 @@ static int in6_selectif(struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct ifnet **,
struct ifnet *, u_int);
static int in6_selectsrc(uint32_t, struct sockaddr_in6 *,
- struct ip6_pktopts *, struct inpcb *, struct ucred *,
- struct ifnet **, struct in6_addr *);
+ struct ip6_pktopts *, struct ip6_moptions *, struct inpcb *,
+ struct ucred *, struct ifnet **, struct in6_addr *);
static struct in6_addrpolicy *lookup_addrsel_policy(struct sockaddr_in6 *);
@@ -173,8 +173,8 @@ static struct in6_addrpolicy *match_addrsel_policy(struct sockaddr_in6 *);
static int
in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
- struct ip6_pktopts *opts, struct inpcb *inp, struct ucred *cred,
- struct ifnet **ifpp, struct in6_addr *srcp)
+ struct ip6_pktopts *opts, struct ip6_moptions *mopts, struct inpcb *inp,
+ struct ucred *cred, struct ifnet **ifpp, struct in6_addr *srcp)
{
struct rm_priotracker in6_ifa_tracker;
struct in6_addr dst, tmp;
@@ -186,7 +186,6 @@ in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
u_int32_t odstzone;
int prefer_tempaddr;
int error;
- struct ip6_moptions *mopts;
NET_EPOCH_ASSERT();
KASSERT(srcp != NULL, ("%s: srcp is NULL", __func__));
@@ -205,13 +204,6 @@ in6_selectsrc(uint32_t fibnum, struct sockaddr_in6 *dstsock,
*ifpp = NULL;
}
- if (inp != NULL) {
- INP_LOCK_ASSERT(inp);
- mopts = inp->in6p_moptions;
- } else {
- mopts = NULL;
- }
-
/*
* If the source address is explicitly specified by the caller,
* check if the requested source address is indeed a unicast address
@@ -552,10 +544,13 @@ in6_selectsrc_socket(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
uint32_t fibnum;
int error;
+ INP_LOCK_ASSERT(inp);
+
fibnum = inp->inp_inc.inc_fibnum;
retifp = NULL;
- error = in6_selectsrc(fibnum, dstsock, opts, inp, cred, &retifp, srcp);
+ error = in6_selectsrc(fibnum, dstsock, opts, inp->in6p_moptions,
+ inp, cred, &retifp, srcp);
if (error != 0)
return (error);
@@ -583,7 +578,7 @@ in6_selectsrc_socket(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
* Stores selected address to @srcp.
* Returns 0 on success.
*
- * Used by non-socket based consumers (ND code mostly)
+ * Used by non-socket based consumers
*/
int
in6_selectsrc_addr(uint32_t fibnum, const struct in6_addr *dst,
@@ -602,13 +597,42 @@ in6_selectsrc_addr(uint32_t fibnum, const struct in6_addr *dst,
dst_sa.sin6_scope_id = scopeid;
sa6_embedscope(&dst_sa, 0);
- error = in6_selectsrc(fibnum, &dst_sa, NULL, NULL, NULL, &retifp, srcp);
+ error = in6_selectsrc(fibnum, &dst_sa, NULL, NULL,
+ NULL, NULL, &retifp, srcp);
if (hlim != NULL)
*hlim = in6_selecthlim(NULL, retifp);
return (error);
}
+/*
+ * Select source address based on @fibnum, @dst and @mopts.
+ * Stores selected address to @srcp.
+ * Returns 0 on success.
+ *
+ * Used by non-socket based consumers (ND code mostly)
+ */
+int
+in6_selectsrc_nbr(uint32_t fibnum, const struct in6_addr *dst,
+ struct ip6_moptions *mopts, struct ifnet *ifp, struct in6_addr *srcp)
+{
+ struct sockaddr_in6 dst_sa;
+ struct ifnet *retifp;
+ int error;
+
+ retifp = ifp;
+ bzero(&dst_sa, sizeof(dst_sa));
+ dst_sa.sin6_family = AF_INET6;
+ dst_sa.sin6_len = sizeof(dst_sa);
+ dst_sa.sin6_addr = *dst;
+ dst_sa.sin6_scope_id = ntohs(in6_getscope(dst));
+ sa6_embedscope(&dst_sa, 0);
+
+ error = in6_selectsrc(fibnum, &dst_sa, NULL, mopts,
+ NULL, NULL, &retifp, srcp);
+ return (error);
+}
+
static struct nhop_object *
cache_route(uint32_t fibnum, const struct sockaddr_in6 *dst, struct route_in6 *ro,
uint32_t flowid)
diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h
index e5ab83e6a2a1..1414cc71388d 100644
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -106,9 +106,11 @@ struct in6_ifextra {
struct scope6_id *scope6_id;
struct lltable *lltable;
struct mld_ifsoftc *mld_ifinfo;
+ u_int dad_failures; /* DAD failures when using RFC 7217 stable addresses */
};
#define LLTABLE6(ifp) (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->lltable)
+#define DAD_FAILURES(ifp) (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->dad_failures)
#ifdef _KERNEL
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index 45fd23ea6c21..99dad1e7c309 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -235,6 +235,7 @@ ip6_vnet_init(void *arg __unused)
&V_ip6_auto_linklocal);
TUNABLE_INT_FETCH("net.inet6.ip6.accept_rtadv", &V_ip6_accept_rtadv);
TUNABLE_INT_FETCH("net.inet6.ip6.no_radr", &V_ip6_no_radr);
+ TUNABLE_BOOL_FETCH("net.inet6.ip6.use_stableaddr", &V_ip6_use_stableaddr);
CK_STAILQ_INIT(&V_in6_ifaddrhead);
V_in6_ifaddrhashtbl = hashinit(IN6ADDR_NHASH, M_IFADDR,
@@ -1197,8 +1198,8 @@ ip6_savecontrol_v4(struct inpcb *inp, struct mbuf *m, struct mbuf **mp,
{
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
-#ifdef SO_TIMESTAMP
- if ((inp->inp_socket->so_options & SO_TIMESTAMP) != 0) {
+#if defined(SO_TIMESTAMP) && defined(SO_BINTIME)
+ if ((inp->inp_socket->so_options & (SO_TIMESTAMP | SO_BINTIME)) != 0) {
union {
struct timeval tv;
struct bintime bt;
@@ -1206,47 +1207,66 @@ ip6_savecontrol_v4(struct inpcb *inp, struct mbuf *m, struct mbuf **mp,
} t;
struct bintime boottimebin, bt1;
struct timespec ts1;
+ int ts_clock;
bool stamped;
+ ts_clock = inp->inp_socket->so_ts_clock;
stamped = false;
- switch (inp->inp_socket->so_ts_clock) {
- case SO_TS_REALTIME_MICRO:
+
+ /*
+ * Handle BINTIME first. We create the same output options
+ * for both SO_BINTIME and the case where SO_TIMESTAMP is
+ * set with the timestamp clock set to SO_TS_BINTIME.
+ */
+ if ((inp->inp_socket->so_options & SO_BINTIME) != 0 ||
+ ts_clock == SO_TS_BINTIME) {
if ((m->m_flags & (M_PKTHDR | M_TSTMP)) == (M_PKTHDR |
M_TSTMP)) {
mbuf_tstmp2timespec(m, &ts1);
- timespec2bintime(&ts1, &bt1);
+ timespec2bintime(&ts1, &t.bt);
getboottimebin(&boottimebin);
- bintime_add(&bt1, &boottimebin);
- bintime2timeval(&bt1, &t.tv);
+ bintime_add(&t.bt, &boottimebin);
} else {
- microtime(&t.tv);
+ bintime(&t.bt);
}
- *mp = sbcreatecontrol(&t.tv, sizeof(t.tv),
- SCM_TIMESTAMP, SOL_SOCKET, M_NOWAIT);
+ *mp = sbcreatecontrol(&t.bt, sizeof(t.bt), SCM_BINTIME,
+ SOL_SOCKET, M_NOWAIT);
if (*mp != NULL) {
mp = &(*mp)->m_next;
stamped = true;
}
- break;
- case SO_TS_BINTIME:
+ /*
+ * Suppress other timestamps if SO_TIMESTAMP is not
+ * set.
+ */
+ if ((inp->inp_socket->so_options & SO_TIMESTAMP) == 0)
+ ts_clock = SO_TS_BINTIME;
+ }
+
+ switch (ts_clock) {
+ case SO_TS_REALTIME_MICRO:
if ((m->m_flags & (M_PKTHDR | M_TSTMP)) == (M_PKTHDR |
M_TSTMP)) {
mbuf_tstmp2timespec(m, &ts1);
- timespec2bintime(&ts1, &t.bt);
+ timespec2bintime(&ts1, &bt1);
getboottimebin(&boottimebin);
- bintime_add(&t.bt, &boottimebin);
+ bintime_add(&bt1, &boottimebin);
+ bintime2timeval(&bt1, &t.tv);
} else {
- bintime(&t.bt);
+ microtime(&t.tv);
}
- *mp = sbcreatecontrol(&t.bt, sizeof(t.bt), SCM_BINTIME,
- SOL_SOCKET, M_NOWAIT);
+ *mp = sbcreatecontrol(&t.tv, sizeof(t.tv),
+ SCM_TIMESTAMP, SOL_SOCKET, M_NOWAIT);
if (*mp != NULL) {
mp = &(*mp)->m_next;
stamped = true;
}
break;
+ case SO_TS_BINTIME:
+ break;
+
case SO_TS_REALTIME:
if ((m->m_flags & (M_PKTHDR | M_TSTMP)) == (M_PKTHDR |
M_TSTMP)) {
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 12b00d4f9934..db1631736c4a 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -338,8 +338,20 @@ VNET_DECLARE(int, ip6_use_tempaddr); /* Whether to use temporary addresses */
VNET_DECLARE(int, ip6_prefer_tempaddr); /* Whether to prefer temporary
* addresses in the source address
* selection */
+VNET_DECLARE(bool, ip6_use_stableaddr); /* Whether to use stable address generation (RFC 7217) */
#define V_ip6_use_tempaddr VNET(ip6_use_tempaddr)
#define V_ip6_prefer_tempaddr VNET(ip6_prefer_tempaddr)
+#define V_ip6_use_stableaddr VNET(ip6_use_stableaddr)
+
+#define IP6_IDGEN_RETRIES 3 /* RFC 7217 section 7 default max retries */
+VNET_DECLARE(u_int, ip6_stableaddr_maxretries);
+#define V_ip6_stableaddr_maxretries VNET(ip6_stableaddr_maxretries)
+
+#define IP6_STABLEADDR_NETIFSRC_NAME 0
+#define IP6_STABLEADDR_NETIFSRC_ID 1
+#define IP6_STABLEADDR_NETIFSRC_MAC 2
+VNET_DECLARE(int, ip6_stableaddr_netifsource);
+#define V_ip6_stableaddr_netifsource VNET(ip6_stableaddr_netifsource)
VNET_DECLARE(int, ip6_use_defzone); /* Whether to use the default scope
* zone when unspecified */
@@ -428,6 +440,8 @@ int in6_selectsrc_socket(struct sockaddr_in6 *, struct ip6_pktopts *,
struct inpcb *, struct ucred *, int, struct in6_addr *, int *);
int in6_selectsrc_addr(uint32_t, const struct in6_addr *,
uint32_t, struct ifnet *, struct in6_addr *, int *);
+int in6_selectsrc_nbr(uint32_t, const struct in6_addr *,
+ struct ip6_moptions *, struct ifnet *, struct in6_addr *);
int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route_in6 *, struct ifnet **,
struct nhop_object **, u_int, uint32_t);
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
index 8480e7fc90e3..00df5efcef92 100644
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -324,6 +324,11 @@ nd6_ifattach(struct ifnet *ifp)
/* XXX: we cannot call nd6_setmtu since ifp is not fully initialized */
nd6_setmtu0(ifp, nd);
+ /* Configure default value for stable addresses algorithm, skip loopback interface */
+ if (V_ip6_use_stableaddr && !(ifp->if_flags & IFF_LOOPBACK)) {
+ nd->flags |= ND6_IFF_STABLEADDR;
+ }
+
return nd;
}
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
index 9cb2571da58b..e484c709e29a 100644
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -89,6 +89,7 @@ struct nd_ifinfo {
#define ND6_IFF_NO_RADR 0x40
#define ND6_IFF_NO_PREFER_IFACE 0x80 /* XXX: not related to ND. */
#define ND6_IFF_NO_DAD 0x100
+#define ND6_IFF_STABLEADDR 0x800
#ifdef EXPERIMENTAL
/* XXX: not related to ND. */
#define ND6_IFF_IPV6_ONLY 0x200 /* draft-ietf-6man-ipv6only-flag */
@@ -170,6 +171,10 @@ struct in6_ndifreq {
#define NDPRF_ONLINK 0x1
#define NDPRF_DETACHED 0x2
+/* ND6 NA output flags */
+#define ND6_NA_OPT_LLA 0x01
+#define ND6_NA_CARP_MASTER 0x02
+
/* protocol constants */
#define MAX_RTR_SOLICITATION_DELAY 1 /* 1sec */
#define RTR_SOLICITATION_INTERVAL 4 /* 4sec */
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
index 640348a1d198..29151b29a071 100644
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -38,6 +38,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/counter.h>
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/libkern.h>
@@ -76,6 +77,8 @@
#include <netinet/ip_carp.h>
#include <netinet6/send.h>
+#include <machine/atomic.h>
+
#define SDL(s) ((struct sockaddr_dl *)s)
struct dadq;
@@ -244,10 +247,9 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len)
* In implementation, we add target link-layer address by default.
* We do not add one in MUST NOT cases.
*/
- if (!IN6_IS_ADDR_MULTICAST(&daddr6))
- tlladdr = 0;
- else
- tlladdr = 1;
+ tlladdr = 0;
+ if (IN6_IS_ADDR_MULTICAST(&daddr6))
+ tlladdr |= ND6_NA_OPT_LLA;
/*
* Target address (taddr6) must be either:
@@ -256,9 +258,11 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len)
* (3) "tentative" address on which DAD is being performed.
*/
/* (1) and (3) check. */
- if (ifp->if_carp)
+ if (ifp->if_carp) {
ifa = (*carp_iamatch6_p)(ifp, &taddr6);
- else
+ if (ifa != NULL)
+ tlladdr |= ND6_NA_CARP_MASTER;
+ } else
ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, &taddr6);
/* (2) check. */
@@ -322,32 +326,28 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len)
}
/*
+ * If the Target Address is either an anycast address or a unicast
+ * address for which the node is providing proxy service, or the Target
+ * Link-Layer Address option is not included, the Override flag SHOULD
+ * be set to zero. Otherwise, the Override flag SHOULD be set to one.
+ */
+ if (anycast == 0 && proxy == 0 && (tlladdr & ND6_NA_OPT_LLA) != 0)
+ rflag |= ND_NA_FLAG_OVERRIDE;
+ /*
* If the source address is unspecified address, entries must not
* be created or updated.
- * It looks that sender is performing DAD. Output NA toward
- * all-node multicast address, to tell the sender that I'm using
- * the address.
+ * It looks that sender is performing DAD. nd6_na_output() will
+ * send NA toward all-node multicast address, to tell the sender
+ * that I'm using the address.
* S bit ("solicited") must be zero.
*/
- if (IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
- struct in6_addr in6_all;
-
- in6_all = in6addr_linklocal_allnodes;
- if (in6_setscope(&in6_all, ifp, NULL) != 0)
- goto bad;
- nd6_na_output_fib(ifp, &in6_all, &taddr6,
- ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
- rflag, tlladdr, proxy ? (struct sockaddr *)&proxydl : NULL,
- M_GETFIB(m));
- goto freeit;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&saddr6)) {
+ nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen,
+ ND_NEIGHBOR_SOLICIT, 0);
+ rflag |= ND_NA_FLAG_SOLICITED;
}
- nd6_cache_lladdr(ifp, &saddr6, lladdr, lladdrlen,
- ND_NEIGHBOR_SOLICIT, 0);
-
- nd6_na_output_fib(ifp, &saddr6, &taddr6,
- ((anycast || proxy || !tlladdr) ? 0 : ND_NA_FLAG_OVERRIDE) |
- rflag | ND_NA_FLAG_SOLICITED, tlladdr,
+ nd6_na_output_fib(ifp, &saddr6, &taddr6, rflag, tlladdr,
proxy ? (struct sockaddr *)&proxydl : NULL, M_GETFIB(m));
freeit:
if (ifa != NULL)
@@ -439,13 +439,6 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6,
return;
M_SETFIB(m, fibnum);
- if (daddr6 == NULL || IN6_IS_ADDR_MULTICAST(daddr6)) {
- m->m_flags |= M_MCAST;
- im6o.im6o_multicast_ifp = ifp;
- im6o.im6o_multicast_hlim = 255;
- im6o.im6o_multicast_loop = 0;
- }
-
icmp6len = sizeof(*nd_ns);
m->m_pkthdr.len = m->m_len = sizeof(*ip6) + icmp6len;
m->m_data += max_linkhdr; /* or M_ALIGN() equivalent? */
@@ -470,6 +463,12 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6,
if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
goto bad;
}
+ if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
+ m->m_flags |= M_MCAST;
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 255;
+ im6o.im6o_multicast_loop = 0;
+ }
if (nonce == NULL) {
char ip6buf[INET6_ADDRSTRLEN];
struct ifaddr *ifa = NULL;
@@ -491,20 +490,16 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6,
ifa = (struct ifaddr *)in6ifa_ifpwithaddr(ifp, saddr6);
if (ifa == NULL) {
int error;
- struct in6_addr dst6, src6;
- uint32_t scopeid;
- in6_splitscope(&ip6->ip6_dst, &dst6, &scopeid);
- error = in6_selectsrc_addr(fibnum, &dst6,
- scopeid, ifp, &src6, NULL);
+ error = in6_selectsrc_nbr(fibnum, &ip6->ip6_dst, &im6o,
+ ifp, &ip6->ip6_src);
if (error) {
nd6log((LOG_DEBUG, "%s: source can't be "
"determined: dst=%s, error=%d\n", __func__,
- ip6_sprintf(ip6buf, &dst6),
+ ip6_sprintf(ip6buf, &ip6->ip6_dst),
error));
goto bad;
}
- ip6->ip6_src = src6;
} else
ip6->ip6_src = *saddr6;
@@ -967,7 +962,9 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len)
* - proxy advertisement delay rule (RFC2461 7.2.8, last paragraph, SHOULD)
* - anycast advertisement delay rule (RFC2461 7.2.7, SHOULD)
*
- * tlladdr - 1 if include target link-layer address
+ * tlladdr:
+ * - 0x01 if include target link-layer address
+ * - 0x02 if target address is CARP MASTER
* sdl0 - sockaddr_dl (= proxy NA) or NULL
*/
static void
@@ -980,8 +977,7 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
struct ip6_hdr *ip6;
struct nd_neighbor_advert *nd_na;
struct ip6_moptions im6o;
- struct in6_addr daddr6, dst6, src6;
- uint32_t scopeid;
+ struct in6_addr daddr6;
NET_EPOCH_ASSERT();
@@ -1005,13 +1001,6 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
return;
M_SETFIB(m, fibnum);
- if (IN6_IS_ADDR_MULTICAST(&daddr6)) {
- m->m_flags |= M_MCAST;
- im6o.im6o_multicast_ifp = ifp;
- im6o.im6o_multicast_hlim = 255;
- im6o.im6o_multicast_loop = 0;
- }
-
icmp6len = sizeof(*nd_na);
m->m_pkthdr.len = m->m_len = sizeof(struct ip6_hdr) + icmp6len;
m->m_data += max_linkhdr; /* or M_ALIGN() equivalent? */
@@ -1023,26 +1012,24 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
ip6->ip6_vfc |= IPV6_VERSION;
ip6->ip6_nxt = IPPROTO_ICMPV6;
ip6->ip6_hlim = 255;
+
if (IN6_IS_ADDR_UNSPECIFIED(&daddr6)) {
/* reply to DAD */
- daddr6.s6_addr16[0] = IPV6_ADDR_INT16_MLL;
- daddr6.s6_addr16[1] = 0;
- daddr6.s6_addr32[1] = 0;
- daddr6.s6_addr32[2] = 0;
- daddr6.s6_addr32[3] = IPV6_ADDR_INT32_ONE;
+ daddr6 = in6addr_linklocal_allnodes;
if (in6_setscope(&daddr6, ifp, NULL))
goto bad;
flags &= ~ND_NA_FLAG_SOLICITED;
}
- ip6->ip6_dst = daddr6;
+ if (IN6_IS_ADDR_MULTICAST(&daddr6)) {
+ m->m_flags |= M_MCAST;
+ im6o.im6o_multicast_ifp = ifp;
+ im6o.im6o_multicast_hlim = 255;
+ im6o.im6o_multicast_loop = 0;
+ }
- /*
- * Select a source whose scope is the same as that of the dest.
- */
- in6_splitscope(&daddr6, &dst6, &scopeid);
- error = in6_selectsrc_addr(fibnum, &dst6,
- scopeid, ifp, &src6, NULL);
+ ip6->ip6_dst = daddr6;
+ error = in6_selectsrc_nbr(fibnum, &daddr6, &im6o, ifp, &ip6->ip6_src);
if (error) {
char ip6buf[INET6_ADDRSTRLEN];
nd6log((LOG_DEBUG, "nd6_na_output: source can't be "
@@ -1050,7 +1037,6 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
ip6_sprintf(ip6buf, &daddr6), error));
goto bad;
}
- ip6->ip6_src = src6;
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
@@ -1058,20 +1044,24 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
in6_clearscope(&nd_na->nd_na_target); /* XXX */
/*
+ * If we respond from CARP address, we need to prepare mac address
+ * for carp_output().
+ */
+ if (ifp->if_carp && (tlladdr & ND6_NA_CARP_MASTER))
+ mac = (*carp_macmatch6_p)(ifp, m, taddr6);
+ /*
* "tlladdr" indicates NS's condition for adding tlladdr or not.
* see nd6_ns_input() for details.
* Basically, if NS packet is sent to unicast/anycast addr,
* target lladdr option SHOULD NOT be included.
*/
- if (tlladdr) {
+ if (tlladdr & ND6_NA_OPT_LLA) {
/*
* sdl0 != NULL indicates proxy NA. If we do proxy, use
* lladdr in sdl0. If we are not proxying (sending NA for
* my address) use lladdr configured for the interface.
*/
if (sdl0 == NULL) {
- if (ifp->if_carp)
- mac = (*carp_macmatch6_p)(ifp, m, taddr6);
if (mac == NULL)
mac = nd6_ifptomac(ifp);
} else if (sdl0->sa_family == AF_LINK) {
@@ -1081,7 +1071,7 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0,
mac = LLADDR(sdl);
}
}
- if (tlladdr && mac) {
+ if ((tlladdr & ND6_NA_OPT_LLA) && mac != NULL) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1);
@@ -1466,9 +1456,14 @@ nd6_dad_timer(void *arg)
* No duplicate address found. Check IFDISABLED flag
* again in case that it is changed between the
* beginning of this function and here.
+ *
+ * Reset DAD failures counter if using stable addresses.
*/
- if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0)
+ if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) {
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ if ((ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY))
+ atomic_store_int(&DAD_FAILURES(ifp), 0);
+ }
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
@@ -1497,20 +1492,39 @@ nd6_dad_duplicated(struct ifaddr *ifa, struct dadq *dp)
struct ifnet *ifp;
char ip6buf[INET6_ADDRSTRLEN];
+ ifp = ifa->ifa_ifp;
+
log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
"NS in/out/loopback=%d/%d/%d, NA in=%d\n",
- if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
+ if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_ns_lcount,
dp->dad_na_icount);
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
ia->ia6_flags |= IN6_IFF_DUPLICATED;
- ifp = ifa->ifa_ifp;
log(LOG_ERR, "%s: DAD complete for %s - duplicate found\n",
if_name(ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr));
- log(LOG_ERR, "%s: manual intervention required\n",
- if_name(ifp));
+
+ /*
+ * For RFC 7217 stable addresses, increment failure counter here if we still have retries.
+ * More addresses will be generated as long as retries are not exhausted.
+ */
+ if ((ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR) && !(ia->ia6_flags & IN6_IFF_TEMPORARY)) {
+ u_int dad_failures = atomic_load_int(&DAD_FAILURES(ifp));
+
+ if (dad_failures <= V_ip6_stableaddr_maxretries) {
+ atomic_add_int(&DAD_FAILURES(ifp), 1);
+ /* if retries exhausted, output an informative error message */
+ if (dad_failures == V_ip6_stableaddr_maxretries)
+ log(LOG_ERR, "%s: manual intervention required, consider disabling \"stableaddr\" on the interface"
+ " or checking hostuuid for uniqueness\n",
+ if_name(ifp));
+ }
+ } else {
+ log(LOG_ERR, "%s: manual intervention required\n",
+ if_name(ifp));
+ }
/*
* If the address is a link-local address formed from an interface
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
index b9af0a78a584..10f0342f2bc4 100644
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -74,6 +74,8 @@
#include <netinet/icmp6.h>
#include <netinet6/scope6_var.h>
+#include <machine/atomic.h>
+
static struct nd_defrouter *defrtrlist_update(struct nd_defrouter *);
static int prelist_update(struct nd_prefixctl *, struct nd_defrouter *,
struct mbuf *, int);
@@ -92,6 +94,7 @@ VNET_DEFINE(int, nd6_defifindex);
#define V_nd6_defifp VNET(nd6_defifp)
VNET_DEFINE(int, ip6_use_tempaddr) = 0;
+VNET_DEFINE(bool, ip6_use_stableaddr) = 0;
VNET_DEFINE(int, ip6_desync_factor);
VNET_DEFINE(uint32_t, ip6_temp_max_desync_factor) = TEMP_MAX_DESYNC_FACTOR_BASE;
@@ -1182,9 +1185,9 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast)
struct ifnet *ifp = pr->ndpr_ifp;
struct ifaddr *ifa;
struct in6_aliasreq ifra;
- struct in6_ifaddr *ia, *ib;
+ struct in6_ifaddr *ia = NULL, *ib = NULL;
int error, plen0;
- struct in6_addr mask;
+ struct in6_addr *ifid_addr = NULL, mask, newaddr;
int prefixlen = pr->ndpr_plen;
int updateflags;
char ip6buf[INET6_ADDRSTRLEN];
@@ -1210,37 +1213,69 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast)
* (4) it is easier to manage when an interface has addresses
* with the same interface identifier, than to have multiple addresses
* with different interface identifiers.
+ *
+ * If using stable privacy generation, generate a new address with
+ * the algorithm specified in RFC 7217 section 5
*/
- ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */
- if (ifa)
- ib = (struct in6_ifaddr *)ifa;
- else
- return NULL;
-
- /* prefixlen + ifidlen must be equal to 128 */
- plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL);
- if (prefixlen != plen0) {
- ifa_free(ifa);
- nd6log((LOG_INFO,
- "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n",
- __func__, if_name(ifp), prefixlen, 128 - plen0));
- return NULL;
- }
/* make ifaddr */
in6_prepare_ifra(&ifra, &pr->ndpr_prefix.sin6_addr, &mask);
+ if (ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR) {
+ memcpy(&newaddr, &pr->ndpr_prefix.sin6_addr, sizeof(pr->ndpr_prefix.sin6_addr));
+
+ if(!in6_get_stableifid(ifp, &newaddr, prefixlen))
+ return NULL;
+ } else {
+ ifa = (struct ifaddr *)in6ifa_ifpforlinklocal(ifp, 0); /* 0 is OK? */
+ if (ifa) {
+ ib = (struct in6_ifaddr *)ifa;
+ ifid_addr = &ib->ia_addr.sin6_addr;
+
+ /* prefixlen + ifidlen must be equal to 128 */
+ plen0 = in6_mask2len(&ib->ia_prefixmask.sin6_addr, NULL);
+ if (prefixlen != plen0) {
+ ifa_free(ifa);
+ ifid_addr = NULL;
+ nd6log((LOG_DEBUG,
+ "%s: wrong prefixlen for %s (prefix=%d ifid=%d)\n",
+ __func__, if_name(ifp), prefixlen, 128 - plen0));
+ }
+ }
+
+ /* No suitable LL address, get the ifid directly */
+ if (ifid_addr == NULL) {
+ ifa = ifa_alloc(sizeof(struct in6_ifaddr), M_NOWAIT);
+ if (ifa != NULL) {
+ ib = (struct in6_ifaddr *)ifa;
+ ifid_addr = &ib->ia_addr.sin6_addr;
+ if(in6_get_ifid(ifp, NULL, ifid_addr) != 0) {
+ nd6log((LOG_DEBUG,
+ "%s: failed to get ifid for %s\n",
+ __func__, if_name(ifp)));
+ ifa_free(ifa);
+ ifid_addr = NULL;
+ }
+ }
+ }
+
+ if (ifid_addr == NULL) {
+ nd6log((LOG_INFO,
+ "%s: could not determine ifid for %s\n",
+ __func__, if_name(ifp)));
+ return NULL;
+ }
+
+ memcpy(&newaddr, &ib->ia_addr.sin6_addr, sizeof(ib->ia_addr.sin6_addr));
+ ifa_free(ifa);
+ }
+
IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask);
/* interface ID */
- ifra.ifra_addr.sin6_addr.s6_addr32[0] |=
- (ib->ia_addr.sin6_addr.s6_addr32[0] & ~mask.s6_addr32[0]);
- ifra.ifra_addr.sin6_addr.s6_addr32[1] |=
- (ib->ia_addr.sin6_addr.s6_addr32[1] & ~mask.s6_addr32[1]);
- ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
- (ib->ia_addr.sin6_addr.s6_addr32[2] & ~mask.s6_addr32[2]);
- ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
- (ib->ia_addr.sin6_addr.s6_addr32[3] & ~mask.s6_addr32[3]);
- ifa_free(ifa);
+ ifra.ifra_addr.sin6_addr.s6_addr32[0] |= (newaddr.s6_addr32[0] & ~mask.s6_addr32[0]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[1] |= (newaddr.s6_addr32[1] & ~mask.s6_addr32[1]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] |= (newaddr.s6_addr32[2] & ~mask.s6_addr32[2]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] |= (newaddr.s6_addr32[3] & ~mask.s6_addr32[3]);
/* lifetimes. */
ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime;
@@ -1471,6 +1506,7 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
int auth;
struct in6_addrlifetime lt6_tmp;
char ip6buf[INET6_ADDRSTRLEN];
+ bool has_temporary = false;
NET_EPOCH_ASSERT();
@@ -1616,9 +1652,6 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
if (ifa6->ia6_ndpr != pr)
continue;
- if (ia6_match == NULL) /* remember the first one */
- ia6_match = ifa6;
-
/*
* An already autoconfigured address matched. Now that we
* are sure there is at least one matched address, we can
@@ -1678,6 +1711,13 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
if ((ifa6->ia6_flags & IN6_IFF_TEMPORARY) != 0) {
u_int32_t maxvltime, maxpltime;
+ /*
+ * if stable addresses (RFC 7217) are enabled, mark that a temporary address has been found
+ * to avoid generating uneeded extra ones.
+ */
+ if (ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR)
+ has_temporary = true;
+
if (V_ip6_temp_valid_lifetime >
(u_int32_t)((time_uptime - ifa6->ia6_createtime) +
V_ip6_desync_factor)) {
@@ -1706,6 +1746,24 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
}
ifa6->ia6_lifetime = lt6_tmp;
ifa6->ia6_updatetime = time_uptime;
+
+ /*
+ * If using stable addresses (RFC 7217) and we still have retries to perform, ignore
+ * addresses already marked as duplicated, since a new one will be generated.
+ * Also ignore addresses marked as temporary, since their generation is orthogonal to
+ * opaque stable ones.
+ *
+ * There is a small race condition, in that the dad_counter could be incremented
+ * between here and when a new address is generated, but this will cause that generation
+ * to fail and no further retries should happen.
+ */
+ if (ND_IFINFO(ifp)->flags & ND6_IFF_STABLEADDR &&
+ atomic_load_int(&DAD_FAILURES(ifp)) <= V_ip6_stableaddr_maxretries &&
+ ifa6->ia6_flags & (IN6_IFF_DUPLICATED | IN6_IFF_TEMPORARY))
+ continue;
+
+ if (ia6_match == NULL) /* remember the first one */
+ ia6_match = ifa6;
}
if (ia6_match == NULL && new->ndpr_vltime) {
int ifidlen;
@@ -1756,8 +1814,11 @@ prelist_update(struct nd_prefixctl *new, struct nd_defrouter *dr,
* immediately together with a new set of temporary
* addresses. Thus, we specifiy 1 as the 2nd arg of
* in6_tmpifadd().
+ *
+ * Skip this if a temporary address has been marked as
+ * found (happens only if stable addresses (RFC 7217) is in use)
*/
- if (V_ip6_use_tempaddr) {
+ if (V_ip6_use_tempaddr && !has_temporary) {
int e;
if ((e = in6_tmpifadd(ia6, 1, 1)) != 0) {
nd6log((LOG_NOTICE, "%s: failed to "