Page MenuHomeFreeBSD

D49681.diff
No OneTemporary

D49681.diff

diff --git a/sbin/ifconfig/af_inet6.c b/sbin/ifconfig/af_inet6.c
--- a/sbin/ifconfig/af_inet6.c
+++ b/sbin/ifconfig/af_inet6.c
@@ -726,6 +726,8 @@
DEF_CMD_ARG("pltime", setip6pltime),
DEF_CMD_ARG("vltime", setip6vltime),
DEF_CMD("eui64", 0, setip6eui64),
+ DEF_CMD("stableaddr", ND6_IFF_STABLEADDR, setnd6flags),
+ DEF_CMD("-stableaddr", -ND6_IFF_STABLEADDR, setnd6flags),
#ifdef EXPERIMENTAL
DEF_CMD("ipv6_only", ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags),
DEF_CMD("-ipv6_only", -ND6_IFF_IPV6_ONLY_MANUAL,setnd6flags),
diff --git a/sbin/ifconfig/af_nd6.c b/sbin/ifconfig/af_nd6.c
--- a/sbin/ifconfig/af_nd6.c
+++ b/sbin/ifconfig/af_nd6.c
@@ -66,6 +66,7 @@
[9] = "IPV6_ONLY",
[10] = "IPV6_ONLY_MANUAL",
#endif
+ [11] = "STABLEADDR",
[15] = "DEFAULTIF",
};
diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -1004,6 +1004,36 @@
.It Cm -no_dad
Clear a flag
.Cm no_dad .
+.It Cm stableaddr
+Set a flag to create SLAAC addresses using a stable algorithm according to RFC 7217
+The
+.Xr sysctl 8
+variable
+.Va net.inet6.ip6.use_stableaddr
+controls whether this flag is set by default or not for newly created interfaces.
+To get consistent defaults for interfaces created at boot it should be set as a tunable via loader.conf(8).
+The
+.Xr sysctl 8
+variable
+.Va net.inet6.ip6.stableaddr_maxretries
+sets the maximum number of retries to generate a unique IPv6 address to be performed in case of DAD failures.
+This defaults to 3 which is also the reccommended minimum value.
+The interface ID source can be configured using the
+.Xr sysctl 8
+variable
+.Va net.inet6.ip6.stableaddr_netifsource:
+.Bl -tag -compact
+.It Cm 0
+uses the interface name string (the default)
+.It Cm 1
+uses the interface ID
+.It Cm 2
+uses the MAC address of the interface (if one can be obtained for it)
+.El
+.Pp
+.It Cm -stableaddr
+Clear the flag
+.Cm stableaddr .
.El
.Ss IPv6 Parameters
The following parameters are specific for IPv6 addresses.
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -609,6 +609,8 @@
/* 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 @@
#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.h b/sys/netinet6/in6_ifattach.h
--- a/sys/netinet6/in6_ifattach.h
+++ b/sys/netinet6/in6_ifattach.h
@@ -39,6 +39,8 @@
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 *);
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
--- 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>
@@ -43,6 +44,7 @@
#include <sys/rmlock.h>
#include <sys/syslog.h>
#include <sys/md5.h>
+#include <crypto/sha2/sha256.h>
#include <net/if.h>
#include <net/if_var.h>
@@ -79,6 +81,8 @@
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)
@@ -98,6 +102,9 @@
#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.
@@ -147,22 +154,14 @@
}
-/*
- * 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();
@@ -175,14 +174,30 @@
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) {
@@ -193,36 +208,21 @@
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:
@@ -233,16 +233,51 @@
* 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" */
@@ -263,6 +298,153 @@
return 0;
}
+/*
+ * 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 = counter_u64_fetch(ND_IFINFO(ifp)->dad_failures);
+
+ /*
+ * 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
@@ -278,7 +460,14 @@
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)));
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -167,6 +167,7 @@
* 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(int, ip6_stableaddr_maxretries) = IP6_IDGEN_RETRIES;
#ifdef IPSTEALTH
VNET_DEFINE(int, ip6stealth) = 0;
@@ -313,6 +314,15 @@
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_INT(_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/ip6_input.c b/sys/netinet6/ip6_input.c
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -235,6 +235,7 @@
&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,
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -338,8 +338,20 @@
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(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 */
diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h
--- a/sys/netinet6/nd6.h
+++ b/sys/netinet6/nd6.h
@@ -76,6 +76,7 @@
u_int8_t randomseed0[8]; /* upper 64 bits of MD5 digest */
u_int8_t randomseed1[8]; /* lower 64 bits (usually the EUI64 IFID) */
u_int8_t randomid[8]; /* current random ID */
+ counter_u64_t dad_failures; /* DAD failures when using RFC 7217 stable addresses */
};
#define ND6_IFF_PERFORMNUD 0x1
@@ -89,6 +90,7 @@
#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 */
diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c
+++ b/sys/netinet6/nd6.c
@@ -324,6 +324,13 @@
/* 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;
+ }
+
+ nd->dad_failures = counter_u64_alloc(M_WAITOK);
+
return nd;
}
@@ -343,6 +350,8 @@
}
NET_EPOCH_EXIT(et);
+ counter_u64_free(nd->dad_failures);
+
free(nd, M_IP6NDP);
}
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
--- 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>
@@ -1466,9 +1467,14 @@
* 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))
+ counter_u64_zero(ND_IFINFO(ifp)->dad_failures);
+ }
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
@@ -1497,20 +1503,39 @@
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)) {
+ uint64_t dad_failures = counter_u64_fetch(ND_IFINFO(ifp)->dad_failures);
+
+ if (dad_failures <= V_ip6_stableaddr_maxretries) {
+ counter_u64_add(ND_IFINFO(ifp)->dad_failures, 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
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -92,6 +92,7 @@
#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;
@@ -1184,7 +1185,7 @@
struct in6_aliasreq ifra;
struct in6_ifaddr *ia = NULL, *ib = NULL;
int error, plen0;
- struct in6_addr *ifid_addr = NULL, mask;
+ struct in6_addr *ifid_addr = NULL, mask, newaddr;
int prefixlen = pr->ndpr_plen;
int updateflags;
char ip6buf[INET6_ADDRSTRLEN];
@@ -1210,61 +1211,70 @@
* (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;
- 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) {
- struct in6_addr taddr;
- ifa = ifa_alloc(sizeof(taddr), M_WAITOK);
+ /* 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;
- if(in6_get_ifid(ifp, NULL, ifid_addr) != 0) {
- nd6log((LOG_DEBUG,
- "%s: failed to get ifid for %s\n",
- __func__, if_name(ifp)));
+
+ /* 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));
}
}
- }
- if (ifid_addr == NULL) {
- nd6log((LOG_INFO,
- "%s: could not determine ifid for %s\n",
- __func__, if_name(ifp)));
- return NULL;
- }
+ /* No suitable LL address, get the ifid directly */
+ if (ifid_addr == NULL) {
+ struct in6_addr taddr;
+ ifa = ifa_alloc(sizeof(taddr), M_WAITOK);
+ if (ifa) {
+ 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;
+ }
+ }
+ }
- /* make ifaddr */
- in6_prepare_ifra(&ifra, &pr->ndpr_prefix.sin6_addr, &mask);
+ 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] |=
- (ifid_addr->s6_addr32[0] & ~mask.s6_addr32[0]);
- ifra.ifra_addr.sin6_addr.s6_addr32[1] |=
- (ifid_addr->s6_addr32[1] & ~mask.s6_addr32[1]);
- ifra.ifra_addr.sin6_addr.s6_addr32[2] |=
- (ifid_addr->s6_addr32[2] & ~mask.s6_addr32[2]);
- ifra.ifra_addr.sin6_addr.s6_addr32[3] |=
- (ifid_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;
@@ -1495,6 +1505,7 @@
int auth;
struct in6_addrlifetime lt6_tmp;
char ip6buf[INET6_ADDRSTRLEN];
+ bool has_temporary = false;
NET_EPOCH_ASSERT();
@@ -1640,9 +1651,6 @@
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
@@ -1702,6 +1710,13 @@
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)) {
@@ -1730,6 +1745,24 @@
}
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 &&
+ counter_u64_fetch(ND_IFINFO(ifp)->dad_failures) <= 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;
@@ -1780,8 +1813,11 @@
* 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 "
diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c
--- a/usr.sbin/ndp/ndp.c
+++ b/usr.sbin/ndp/ndp.c
@@ -1058,6 +1058,9 @@
#endif
#ifdef ND6_IFF_NO_PREFER_IFACE
SETFLAG("no_prefer_iface", ND6_IFF_NO_PREFER_IFACE);
+#endif
+#ifdef ND6_IFF_STABLEADDR
+ SETFLAG("stableaddr", ND6_IFF_STABLEADDR);
#endif
SETVALUE("basereachable", ND.basereachable);
SETVALUE("retrans", ND.retrans);
@@ -1144,6 +1147,10 @@
if ((ND.flags & ND6_IFF_AUTO_LINKLOCAL))
xo_emit("{l:%s} ", "auto_linklocal");
#endif
+#ifdef ND6_IFF_STABLEADDR
+ if ((ND.flags & ND6_IFF_STABLEADDR))
+ xo_emit("{l:%s} ", "stableaddr");
+#endif
#ifdef ND6_IFF_NO_PREFER_IFACE
if ((ND.flags & ND6_IFF_NO_PREFER_IFACE))
xo_emit("{l:%s} ", "no_prefer_iface");

File Metadata

Mime Type
text/plain
Expires
Thu, Oct 23, 1:33 PM (6 h, 22 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
24092399
Default Alt Text
D49681.diff (27 KB)

Event Timeline