diff options
Diffstat (limited to 'sys/netinet6')
-rw-r--r-- | sys/netinet6/in6.c | 35 | ||||
-rw-r--r-- | sys/netinet6/in6.h | 7 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.c | 284 | ||||
-rw-r--r-- | sys/netinet6/in6_ifattach.h | 3 | ||||
-rw-r--r-- | sys/netinet6/in6_proto.c | 10 | ||||
-rw-r--r-- | sys/netinet6/in6_src.c | 54 | ||||
-rw-r--r-- | sys/netinet6/in6_var.h | 2 | ||||
-rw-r--r-- | sys/netinet6/ip6_input.c | 54 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 14 | ||||
-rw-r--r-- | sys/netinet6/nd6.c | 5 | ||||
-rw-r--r-- | sys/netinet6/nd6.h | 5 | ||||
-rw-r--r-- | sys/netinet6/nd6_nbr.c | 158 | ||||
-rw-r--r-- | sys/netinet6/nd6_rtr.c | 121 |
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 " |