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:
@@ -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),
|
||||
|
||||
@@ -66,6 +66,7 @@ static const char *ND6BITS[] = {
|
||||
[9] = "IPV6_ONLY",
|
||||
[10] = "IPV6_ONLY_MANUAL",
|
||||
#endif
|
||||
[11] = "STABLEADDR",
|
||||
[15] = "DEFAULTIF",
|
||||
};
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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)));
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+28
-3
@@ -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));
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
+52
-16
@@ -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,7 +1211,20 @@ 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
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
@@ -1251,20 +1265,16 @@ in6_ifadd(struct nd_prefixctl *pr, int mcast)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* make ifaddr */
|
||||
in6_prepare_ifra(&ifra, &pr->ndpr_prefix.sin6_addr, &mask);
|
||||
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 "
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user