Page MenuHomeFreeBSD

D49681.id153205.diff
No OneTemporary

D49681.id153205.diff

diff --git a/libexec/rc/rc.conf b/libexec/rc/rc.conf
--- a/libexec/rc/rc.conf
+++ b/libexec/rc/rc.conf
@@ -526,6 +526,8 @@
# packets not explicitly addressed to itself.
ipv6_privacy="NO" # Use privacy address on RA-receiving IFs
# (RFC 4941)
+ipv6_privacy_stable="NO" # Use stable algorithm to create SLAAC IPv6 addresses
+ # (RFC 7217)
route6d_enable="NO" # Set to YES to enable an IPv6 routing daemon.
route6d_program="/usr/sbin/route6d" # Name of IPv6 routing daemon.
diff --git a/libexec/rc/rc.d/netoptions b/libexec/rc/rc.d/netoptions
--- a/libexec/rc/rc.d/netoptions
+++ b/libexec/rc/rc.d/netoptions
@@ -107,6 +107,12 @@
${SYSCTL} net.inet6.ip6.prefer_tempaddr=1 >/dev/null
fi
+ if checkyesno ipv6_privacy_stable; then
+ netoptions_init
+ echo -n " IPv6 Privacy Stable Address"
+ ${SYSCTL} net.inet6.ip6.use_stableaddr=1 >/dev/null
+ fi
+
case $ipv6_cpe_wanif in
""|[Nn][Oo]|[Nn][Oo][Nn][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
${SYSCTL} net.inet6.ip6.no_radr=0 >/dev/null
diff --git a/sys/netinet6/in6.h b/sys/netinet6/in6.h
--- a/sys/netinet6/in6.h
+++ b/sys/netinet6/in6.h
@@ -617,6 +617,7 @@
#define IPV6CTL_PREFER_TEMPADDR 37 /* prefer temporary addr as src */
#define IPV6CTL_ADDRCTLPOLICY 38 /* get/set address selection policy */
#define IPV6CTL_USE_DEFAULTZONE 39 /* use default scope zone */
+#define IPV6CTL_STABLEADDR 40 /* use semantically opaque addresses (RFC7217) */
#define IPV6CTL_MAXFRAGS 41 /* max fragments */
#if 0
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -2618,6 +2618,8 @@
ext->mld_ifinfo = mld_domifattach(ifp);
+ ext->dad_failures = counter_u64_alloc(M_WAITOK);
+
return ext;
}
@@ -2635,6 +2637,7 @@
{
struct in6_ifextra *ext = (struct in6_ifextra *)aux;
+ counter_u64_free(ext->dad_failures);
mld_domifdetach(ifp);
scope6_ifdetach(ext->scope6_id);
nd6_ifdetach(ifp, ext->nd_ifinfo);
diff --git a/sys/netinet6/in6_ifattach.h b/sys/netinet6/in6_ifattach.h
--- a/sys/netinet6/in6_ifattach.h
+++ b/sys/netinet6/in6_ifattach.h
@@ -40,6 +40,7 @@
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);
+int 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_nigroup(struct ifnet *, const char *, int, struct in6_addr *);
diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c
--- a/sys/netinet6/in6_ifattach.c
+++ b/sys/netinet6/in6_ifattach.c
@@ -33,6 +33,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/counter.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/sockio.h>
@@ -43,6 +44,7 @@
#include <sys/rmlock.h>
#include <sys/syslog.h>
#include <sys/md5.h>
+#include <crypto/sha1.h>
#include <net/if.h>
#include <net/if_var.h>
@@ -345,6 +347,64 @@
return 0;
}
+static int
+generate_stable_ifid(u_int8_t *prefix, size_t prefix_len, const u_int8_t *netiface, size_t netiface_len, u_int64_t dad_failures, struct in6_addr *in6)
+{
+ static u_int8_t allzero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ SHA1_CTX ctxt;
+ u_int8_t hostuuid[HOSTUUIDLEN + 1];
+ u_int8_t digest[SHA1_RESULTLEN];
+
+ /* Use hostuuid as constant "secret" key */
+ getcredhostuuid(curthread->td_ucred, hostuuid, sizeof(hostuuid));
+
+ SHA1Init(&ctxt);
+ SHA1Update(&ctxt, prefix, prefix_len);
+ SHA1Update(&ctxt, netiface, netiface_len);
+ SHA1Update(&ctxt, (u_int8_t *)&dad_failures, 8);
+ SHA1Update(&ctxt, hostuuid, strlen(hostuuid));
+ SHA1Final(digest, &ctxt);
+
+ /* assumes sizeof(digest) > sizeof(ifid) */
+ bcopy(digest, &in6->s6_addr[8], 8);
+
+ /* Make sure not to generate all zero interface identifier */
+ if (bcmp(&in6->s6_addr[9], allzero, 7) == 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * Get interface identifier for the specified interface, according to
+ * RFC 7217 Stable and Opaque IDs with SLAAC
+ *
+ * in6 - upper 64bits are preserved
+ */
+int
+in6_get_stableifid(struct ifnet *ifp, struct in6_addr *in6, int prefixlen)
+{
+ const u_int8_t * netiface;
+ u_int64_t dad_failures;
+
+ netiface = (const u_int8_t *)if_name(ifp);
+
+ dad_failures = counter_u64_fetch(((struct in6_ifextra *)((ifp)->if_afdata[AF_INET6]))->dad_failures);
+
+ /*
+ * RFC 7217 section 7
+ *
+ * default max retries
+ */
+ if (dad_failures > IP6_IDGEN_RETRIES)
+ return -1;
+
+ if (generate_stable_ifid(in6->s6_addr, prefixlen / 8, netiface, strlen(netiface), dad_failures, in6) != 0)
+ return -1;
+
+ return 0;
+}
+
/*
* Get interface identifier for the specified interface. If it is not
* available on ifp0, borrow interface identifier from other information
@@ -360,7 +420,14 @@
NET_EPOCH_ASSERT();
- /* first, try to get it from the interface itself */
+ /* first, try to get it from the interface itself, with stable algorithm, if configured */
+ if (V_ip6_use_stableaddr && in6_get_stableifid(ifp0, in6, 64) == 0) {
+ nd6log((LOG_DEBUG, "%s: got interface identifier from itself (stable private)\n",
+ if_name(ifp0)));
+ goto success;
+ }
+
+ /* then/otherwise try to get it from the interface itself */
if (in6_get_hw_ifid(ifp0, in6) == 0) {
nd6log((LOG_DEBUG, "%s: got interface identifier from itself\n",
if_name(ifp0)));
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -309,6 +309,9 @@
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_INT(_net_inet6_ip6, IPV6CTL_STABLEADDR, use_stableaddr,
+ CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(ip6_use_stableaddr), 0,
+ "Create RFC7217 semantically opaque address for autoconfigured addresses");
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_var.h b/sys/netinet6/in6_var.h
--- a/sys/netinet6/in6_var.h
+++ b/sys/netinet6/in6_var.h
@@ -106,6 +106,7 @@
struct scope6_id *scope6_id;
struct lltable *lltable;
struct mld_ifsoftc *mld_ifinfo;
+ counter_u64_t dad_failures;
};
#define LLTABLE6(ifp) (((struct in6_ifextra *)(ifp)->if_afdata[AF_INET6])->lltable)
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -338,8 +338,12 @@
VNET_DECLARE(int, ip6_prefer_tempaddr); /* Whether to prefer temporary
* addresses in the source address
* selection */
+VNET_DECLARE(int, 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 max retries */
VNET_DECLARE(int, ip6_use_defzone); /* Whether to use the default scope
* zone when unspecified */
diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c
--- a/sys/netinet6/nd6_nbr.c
+++ b/sys/netinet6/nd6_nbr.c
@@ -38,6 +38,7 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/counter.h>
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/libkern.h>
@@ -1466,9 +1467,14 @@
* No duplicate address found. Check IFDISABLED flag
* again in case that it is changed between the
* beginning of this function and here.
+ *
+ * Reset DAD failures counter if using stable addresses.
*/
- if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0)
+ if ((ND_IFINFO(ifp)->flags & ND6_IFF_IFDISABLED) == 0) {
ia->ia6_flags &= ~IN6_IFF_TENTATIVE;
+ if (V_ip6_use_stableaddr)
+ counter_u64_zero(((struct in6_ifextra *)((ifp)->if_afdata[AF_INET6]))->dad_failures);
+ }
nd6log((LOG_DEBUG,
"%s: DAD complete for %s - no duplicates found\n",
@@ -1497,20 +1503,30 @@
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, just increment counter here. A new one will be generated.
+ *
+ * Otherwise a new address could be generated here (I need to call in6_ifadd() with a faked struct nd_prefixctl argment?).
+ */
+ if (V_ip6_use_stableaddr)
+ counter_u64_add(((struct in6_ifextra *)((ifp)->if_afdata[AF_INET6]))->dad_failures, 1);
+ else
+ log(LOG_ERR, "%s: manual intervention required\n",
+ if_name(ifp));
/*
* If the address is a link-local address formed from an interface
diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c
--- a/sys/netinet6/nd6_rtr.c
+++ b/sys/netinet6/nd6_rtr.c
@@ -90,6 +90,7 @@
#define V_nd6_defifp VNET(nd6_defifp)
VNET_DEFINE(int, ip6_use_tempaddr) = 0;
+VNET_DEFINE(int, ip6_use_stableaddr) = 0;
VNET_DEFINE(int, ip6_desync_factor);
VNET_DEFINE(u_int32_t, ip6_temp_preferred_lifetime) = DEF_TEMP_PREFERRED_LIFETIME;
@@ -1186,7 +1187,7 @@
struct in6_aliasreq ifra;
struct in6_ifaddr *ia, *ib;
int error, plen0;
- struct in6_addr mask;
+ struct in6_addr mask, new;
int prefixlen = pr->ndpr_plen;
int updateflags;
char ip6buf[INET6_ADDRSTRLEN];
@@ -1212,37 +1213,55 @@
* (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);
- 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);
+ if (V_ip6_use_stableaddr) {
+ bcopy(&pr->ndpr_prefix.sin6_addr, &new, sizeof(pr->ndpr_prefix.sin6_addr));
+
+ if(in6_get_stableifid(ifp, &new, prefixlen) != 0)
+ return NULL;
+
+ IN6_MASK_ADDR(&ifra.ifra_addr.sin6_addr, &mask);
+ /* interface ID */
+ ifra.ifra_addr.sin6_addr.s6_addr32[0] |= (new.s6_addr32[0] & ~mask.s6_addr32[0]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[1] |= (new.s6_addr32[1] & ~mask.s6_addr32[1]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[2] |= (new.s6_addr32[2] & ~mask.s6_addr32[2]);
+ ifra.ifra_addr.sin6_addr.s6_addr32[3] |= (new.s6_addr32[3] & ~mask.s6_addr32[3]);
+ } else {
+ 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;
+ }
+
+ 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);
+ }
/* lifetimes. */
ifra.ifra_lifetime.ia6t_vltime = pr->ndpr_vltime;
@@ -1590,6 +1609,7 @@
CK_STAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
struct in6_ifaddr *ifa6;
u_int32_t remaininglifetime;
+ u_int64_t dad_failures;
if (ifa->ifa_addr->sa_family != AF_INET6)
continue;
@@ -1618,6 +1638,20 @@
if (ifa6->ia6_ndpr != pr)
continue;
+ /*
+ * If using stable addresses (RFC 7217) and we sill have retries to perform, ignore
+ * addresses already marked as duplicated, since a new one will be generated.
+ *
+ * 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 (V_ip6_use_stableaddr) {
+ dad_failures = counter_u64_fetch(((struct in6_ifextra *)((ifp)->if_afdata[AF_INET6]))->dad_failures);
+ if (dad_failures <= IP6_IDGEN_RETRIES && (ifa6->ia6_flags & IN6_IFF_DUPLICATED))
+ continue;
+ }
+
if (ia6_match == NULL) /* remember the first one */
ia6_match = ifa6;

File Metadata

Mime Type
text/plain
Expires
Sun, Feb 15, 3:15 AM (13 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28724629
Default Alt Text
D49681.id153205.diff (13 KB)

Event Timeline