sys/netinet6: Implement RFC 7217

Implement RFC 7217 (A Method for Generating Semantically Opaque
Interface Identifiers with IPv6 Stateless Address Autoconfiguration
(SLAAC)) in our IPv6 stack.

A new ifconfig `stableaddr` flag is added to enable the feature on
interfaces, which defaults to on or off for new interfaces based
on the sysctl `net.inet6.ip6.use_stableaddr` (off by default, so
this commit causes no change in behavior with default settings).

The algorithm follows the RFC in its logic, using SHA256-HMAC as
the algorithm to derive addresses so as to provide code that can
be leveraged by future implentations of RFC 8981, leveraging the
`hostuuid` as the secret.

The source of the hostidentifier can be configured using the sysctl
`net.inet6.ip6.stableaddr_netifsource`, while the number of retries
generating a new address in case of collision can be configured
using the `net.inet6.ip6.stableaddr_maxretries` sysctl (default 3).

Documentation about all these flags is added to the ifconfig(8) man
page.

Reviewed by:		cognet, glebius, hrs
Tested by:		zarychtam@plan-b.pwste.edu.pl
Approved by:		cognet, glebius
Relnotes:		yes
Differential Revision:	https://reviews.freebsd.org/D49681
This commit is contained in:
Guido Falsi
2025-09-20 14:26:41 +02:00
parent 174d5d9397
commit 31ec8b6407
14 changed files with 429 additions and 100 deletions
+2
View File
@@ -726,6 +726,8 @@ static struct cmd inet6_cmds[] = {
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),
+1
View File
@@ -66,6 +66,7 @@ static const char *ND6BITS[] = {
[9] = "IPV6_ONLY",
[10] = "IPV6_ONLY_MANUAL",
#endif
[11] = "STABLEADDR",
[15] = "DEFAULTIF",
};
+30
View File
@@ -1004,6 +1004,36 @@ Set a flag to disable Duplicate Address Detection.
.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.
+3
View File
@@ -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
+231 -42
View File
@@ -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(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)
@@ -98,6 +102,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.
@@ -147,22 +154,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();
@@ -175,14 +174,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) {
@@ -193,36 +208,21 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
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;
if (memcmp(addr, allzero, *len) == 0 || memcmp(addr, allone, *len) == 0)
return (NULL);
/* 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];
}
break;
case IFT_GIF:
@@ -233,16 +233,51 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
* 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 @@ in6_get_hw_ifid(struct ifnet *ifp, struct in6_addr *in6)
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 @@ in6_get_ifid(struct ifnet *ifp0, struct ifnet *altifp,
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)));
+2
View File
@@ -39,6 +39,8 @@ 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 *);
+10
View File
@@ -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(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_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",
+1
View File
@@ -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,
+12
View File
@@ -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(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 */
+9
View File
@@ -324,6 +324,13 @@ 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;
}
nd->dad_failures = counter_u64_alloc(M_WAITOK);
return nd;
}
@@ -343,6 +350,8 @@ nd6_ifdetach(struct ifnet *ifp, struct nd_ifinfo *nd)
}
NET_EPOCH_EXIT(et);
counter_u64_free(nd->dad_failures);
free(nd, M_IP6NDP);
}
+2
View File
@@ -76,6 +76,7 @@ struct nd_ifinfo {
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 @@ 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 */
+30 -5
View File
@@ -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 @@ 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))
counter_u64_zero(ND_IFINFO(ifp)->dad_failures);
}
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
@@ -1497,20 +1503,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)) {
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
+89 -53
View File
@@ -92,6 +92,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;
@@ -1184,7 +1185,7 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast)
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 @@ 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;
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);
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;
}
}
}
if (ifid_addr == NULL) {
nd6log((LOG_INFO,
"%s: could not determine ifid for %s\n",
__func__, if_name(ifp)));
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) {
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;
}
}
}
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 @@ 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();
@@ -1640,9 +1651,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
@@ -1702,6 +1710,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)) {
@@ -1730,6 +1745,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 &&
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 @@ 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 "
+7
View File
@@ -1058,6 +1058,9 @@ ifinfo(char *ifname, int argc, char **argv)
#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 @@ ifinfo(char *ifname, int argc, char **argv)
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");