Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144920090
D49681.id153205.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D49681.id153205.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D49681: Implement IPv6 RFC 7217
Attached
Detach File
Event Timeline
Log In to Comment