Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F153186652
D1835.id3761.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
D1835.id3761.diff
View Options
Index: sys/netinet/icmp6.h
===================================================================
--- sys/netinet/icmp6.h
+++ sys/netinet/icmp6.h
@@ -297,9 +297,11 @@
#define ND_OPT_PREFIX_INFORMATION 3
#define ND_OPT_REDIRECTED_HEADER 4
#define ND_OPT_MTU 5
+#define ND_OPT_NONCE 14 /* RFC 3971 */
#define ND_OPT_ROUTE_INFO 24 /* RFC 4191 */
#define ND_OPT_RDNSS 25 /* RFC 6106 */
#define ND_OPT_DNSSL 31 /* RFC 6106 */
+#define ND_OPT_MAX 31
struct nd_opt_prefix_info { /* prefix information */
u_int8_t nd_opt_pi_type;
@@ -330,6 +332,16 @@
u_int32_t nd_opt_mtu_mtu;
} __packed;
+#define ND_OPT_NONCE_LEN ((8 * 2) - 2)
+#if ((ND_OPT_NONCE_LEN + 2) % 8) != 0
+#error "(ND_OPT_NONCE_LEN + 2) must be a multiple of 8."
+#endif
+struct nd_opt_nonce { /* nonce option */
+ u_int8_t nd_opt_nonce_type;
+ u_int8_t nd_opt_nonce_len;
+ u_int8_t nd_opt_nonce[ND_OPT_NONCE_LEN];
+} __packed;
+
struct nd_opt_route_info { /* route info */
u_int8_t nd_opt_rti_type;
u_int8_t nd_opt_rti_len;
Index: sys/netinet6/nd6.h
===================================================================
--- sys/netinet6/nd6.h
+++ sys/netinet6/nd6.h
@@ -359,7 +359,7 @@
#define V_ip6_temp_regen_advance VNET(ip6_temp_regen_advance)
union nd_opts {
- struct nd_opt_hdr *nd_opt_array[8]; /* max = target address list */
+ struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */
struct {
struct nd_opt_hdr *zero;
struct nd_opt_hdr *src_lladdr;
@@ -367,6 +367,16 @@
struct nd_opt_prefix_info *pi_beg; /* multiple opts, start */
struct nd_opt_rd_hdr *rh;
struct nd_opt_mtu *mtu;
+ struct nd_opt_hdr *__res6;
+ struct nd_opt_hdr *__res7;
+ struct nd_opt_hdr *__res8;
+ struct nd_opt_hdr *__res9;
+ struct nd_opt_hdr *__res10;
+ struct nd_opt_hdr *__res11;
+ struct nd_opt_hdr *__res12;
+ struct nd_opt_hdr *__res13;
+ struct nd_opt_nonce *nonce;
+ struct nd_opt_hdr *__res15;
struct nd_opt_hdr *search; /* multiple opts */
struct nd_opt_hdr *last; /* multiple opts */
int done;
@@ -379,6 +389,7 @@
#define nd_opts_pi_end nd_opt_each.pi_end
#define nd_opts_rh nd_opt_each.rh
#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_nonce nd_opt_each.nonce
#define nd_opts_search nd_opt_each.search
#define nd_opts_last nd_opt_each.last
#define nd_opts_done nd_opt_each.done
@@ -425,7 +436,7 @@
const struct in6_addr *, u_long, int, struct sockaddr *);
void nd6_ns_input(struct mbuf *, int, int);
void nd6_ns_output(struct ifnet *, const struct in6_addr *,
- const struct in6_addr *, struct llentry *, int);
+ const struct in6_addr *, struct llentry *, uint8_t *);
caddr_t nd6_ifptomac(struct ifnet *);
void nd6_dad_init(void);
void nd6_dad_start(struct ifaddr *, int);
Index: sys/netinet6/nd6.c
===================================================================
--- sys/netinet6/nd6.c
+++ sys/netinet6/nd6.c
@@ -372,6 +372,7 @@
case ND_OPT_TARGET_LINKADDR:
case ND_OPT_MTU:
case ND_OPT_REDIRECTED_HEADER:
+ case ND_OPT_NONCE:
if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) {
nd6log((LOG_INFO,
"duplicated ND6 option found (type=%d)\n",
@@ -507,7 +508,7 @@
ln->la_asked++;
nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
LLE_WUNLOCK(ln);
- nd6_ns_output(ifp, NULL, dst, ln, 0);
+ nd6_ns_output(ifp, NULL, dst, ln, NULL);
LLE_WLOCK(ln);
} else {
struct mbuf *m = ln->la_hold;
@@ -554,7 +555,7 @@
ln->ln_state = ND6_LLINFO_PROBE;
nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
LLE_WUNLOCK(ln);
- nd6_ns_output(ifp, dst, dst, ln, 0);
+ nd6_ns_output(ifp, dst, dst, ln, NULL);
LLE_WLOCK(ln);
} else {
ln->ln_state = ND6_LLINFO_STALE; /* XXX */
@@ -566,7 +567,7 @@
ln->la_asked++;
nd6_llinfo_settimer_locked(ln, (long)ndi->retrans * hz / 1000);
LLE_WUNLOCK(ln);
- nd6_ns_output(ifp, dst, dst, ln, 0);
+ nd6_ns_output(ifp, dst, dst, ln, NULL);
LLE_WLOCK(ln);
} else {
EVENTHANDLER_INVOKE(lle_event, ln, LLENTRY_EXPIRED);
@@ -2065,7 +2066,7 @@
nd6_llinfo_settimer_locked(lle,
(long)ND_IFINFO(ifp)->retrans * hz / 1000);
LLE_WUNLOCK(lle);
- nd6_ns_output(ifp, NULL, &dst->sin6_addr, lle, 0);
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr, lle, NULL);
} else {
/* We did the lookup so we need to do the unlock here. */
LLE_WUNLOCK(lle);
Index: sys/netinet6/nd6_nbr.c
===================================================================
--- sys/netinet6/nd6_nbr.c
+++ sys/netinet6/nd6_nbr.c
@@ -40,6 +40,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
+#include <sys/libkern.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/mbuf.h>
@@ -48,6 +49,7 @@
#include <sys/time.h>
#include <sys/kernel.h>
#include <sys/errno.h>
+#include <sys/sysctl.h>
#include <sys/syslog.h>
#include <sys/queue.h>
#include <sys/callout.h>
@@ -61,6 +63,7 @@
#ifdef RADIX_MPATH
#include <net/radix_mpath.h>
#endif
+#include <net/vnet.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
@@ -79,7 +82,7 @@
#define SDL(s) ((struct sockaddr_dl *)s)
struct dadq;
-static struct dadq *nd6_dad_find(struct ifaddr *);
+static struct dadq *nd6_dad_find(struct ifaddr *, struct nd_opt_nonce *);
static void nd6_dad_add(struct dadq *dp);
static void nd6_dad_del(struct dadq *dp);
static void nd6_dad_rele(struct dadq *);
@@ -88,16 +91,21 @@
static void nd6_dad_timer(struct dadq *);
static void nd6_dad_duplicated(struct ifaddr *, struct dadq *);
static void nd6_dad_ns_output(struct dadq *, struct ifaddr *);
-static void nd6_dad_ns_input(struct ifaddr *);
+static void nd6_dad_ns_input(struct ifaddr *, struct nd_opt_nonce *);
static void nd6_dad_na_input(struct ifaddr *);
static void nd6_na_output_fib(struct ifnet *, const struct in6_addr *,
const struct in6_addr *, u_long, int, struct sockaddr *, u_int);
-static VNET_DEFINE(int, dad_ignore_ns) = 0; /* ignore NS in DAD
- - specwise incorrect */
+static VNET_DEFINE(int, dad_enhanced) = 1;
+#define V_dad_enhanced VNET(dad_enhanced)
+
+SYSCTL_DECL(_net_inet6_ip6);
+SYSCTL_INT(_net_inet6_ip6, OID_AUTO, dad_enhanced, CTLFLAG_VNET | CTLFLAG_RW,
+ &VNET_NAME(dad_enhanced), 0,
+ "Enable Enhanced DAD, which adds a random nonce to NS messages for DAD.");
+
static VNET_DEFINE(int, dad_maxtry) = 15; /* max # of *tries* to
transmit DAD packet */
-#define V_dad_ignore_ns VNET(dad_ignore_ns)
#define V_dad_maxtry VNET(dad_maxtry)
/*
@@ -321,7 +329,7 @@
* silently ignore it.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
- nd6_dad_ns_input(ifa);
+ nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce);
goto freeit;
}
@@ -382,12 +390,13 @@
* Based on RFC 2461
* Based on RFC 2462 (duplicate address detection)
*
- * ln - for source address determination
- * dad - duplicate address detection
+ * ln - for source address determination
+ * nonce - If non-NULL, NS is used for duplicate address detection and
+ * the value (length is ND_OPT_NONCE_LEN) is used as a random nonce.
*/
void
nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
- const struct in6_addr *taddr6, struct llentry *ln, int dad)
+ const struct in6_addr *taddr6, struct llentry *ln, uint8_t *nonce)
{
struct mbuf *m;
struct m_tag *mtag;
@@ -453,7 +462,7 @@
if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
goto bad;
}
- if (!dad) {
+ if (nonce == NULL) {
struct ifaddr *ifa;
/*
@@ -550,7 +559,7 @@
* Multicast NS MUST add one add the option
* Unicast NS SHOULD add one add the option
*/
- if (!dad && (mac = nd6_ifptomac(ifp))) {
+ if (nonce == NULL && (mac = nd6_ifptomac(ifp))) {
int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen;
struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
/* 8 byte alignments... */
@@ -564,7 +573,26 @@
nd_opt->nd_opt_len = optlen >> 3;
bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen);
}
+ /*
+ * Add a Nonce option (RFC 3971) to detect looped back NS messages.
+ * This behavior is documented as Enhanced Duplicate Address
+ * Detection in draft-ietf-6man-enhanced-dad-13.
+ * net.inet6.ip6.dad_enhanced=0 disables this.
+ */
+ if (V_dad_enhanced != 0 && nonce != NULL) {
+ int optlen = sizeof(struct nd_opt_hdr) + ND_OPT_NONCE_LEN;
+ struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_ns + 1);
+ /* 8-byte alignment is required. */
+ optlen = (optlen + 7) & ~7;
+ m->m_pkthdr.len += optlen;
+ m->m_len += optlen;
+ icmp6len += optlen;
+ bzero((caddr_t)nd_opt, optlen);
+ nd_opt->nd_opt_type = ND_OPT_NONCE;
+ nd_opt->nd_opt_len = optlen >> 3;
+ bcopy(nonce, (caddr_t)(nd_opt + 1), ND_OPT_NONCE_LEN);
+ }
ip6->ip6_plen = htons((u_short)icmp6len);
nd_ns->nd_ns_cksum = 0;
nd_ns->nd_ns_cksum =
@@ -579,7 +607,8 @@
m_tag_prepend(m, mtag);
}
- ip6_output(m, NULL, &ro, dad ? IPV6_UNSPECSRC : 0, &im6o, NULL, NULL);
+ ip6_output(m, NULL, &ro, (nonce != NULL) ? IPV6_UNSPECSRC : 0,
+ &im6o, NULL, NULL);
icmp6_ifstat_inc(ifp, ifs6_out_msg);
icmp6_ifstat_inc(ifp, ifs6_out_neighborsolicit);
ICMP6STAT_INC(icp6s_outhist[ND_NEIGHBOR_SOLICIT]);
@@ -1139,9 +1168,13 @@
int dad_ns_ocount; /* NS sent so far */
int dad_ns_icount;
int dad_na_icount;
+ int dad_ns_lcount; /* looped back NS */
struct callout dad_timer_ch;
struct vnet *dad_vnet;
u_int dad_refcnt;
+#define ND_OPT_NONCE_LEN32 \
+ ((ND_OPT_NONCE_LEN + sizeof(uint32_t) - 1)/sizeof(uint32_t))
+ uint32_t dad_nonce[ND_OPT_NONCE_LEN32];
};
static VNET_DEFINE(TAILQ_HEAD(, dadq), dadq);
@@ -1174,16 +1207,34 @@
}
static struct dadq *
-nd6_dad_find(struct ifaddr *ifa)
+nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *n)
{
struct dadq *dp;
+ char ip6buf[INET6_ADDRSTRLEN];
DADQ_RLOCK();
- TAILQ_FOREACH(dp, &V_dadq, dad_list)
- if (dp->dad_ifa == ifa) {
- refcount_acquire(&dp->dad_refcnt);
- break;
+ TAILQ_FOREACH(dp, &V_dadq, dad_list) {
+ if (dp->dad_ifa != ifa)
+ continue;
+ /*
+ * Skip if the nonce matches the received one.
+ * +2 in the length is required because of type and
+ * length fields are included in a header.
+ */
+ if (n != NULL &&
+ n->nd_opt_nonce_len == (ND_OPT_NONCE_LEN + 2) / 8 &&
+ memcmp(&n->nd_opt_nonce[0], &dp->dad_nonce[0],
+ ND_OPT_NONCE_LEN) == 0) {
+ log(LOG_ERR, "%s: a looped back NS message is "
+ "detected during DAD for %s.\n",
+ if_name(ifa->ifa_ifp),
+ ip6_sprintf(ip6buf, IFA_IN6(ifa)));
+ dp->dad_ns_lcount++;
+ continue;
}
+ refcount_acquire(&dp->dad_refcnt);
+ break;
+ }
DADQ_RUNLOCK();
return (dp);
@@ -1261,7 +1312,7 @@
}
if (ND_IFINFO(ifa->ifa_ifp)->flags & ND6_IFF_IFDISABLED)
return;
- if ((dp = nd6_dad_find(ifa)) != NULL) {
+ if ((dp = nd6_dad_find(ifa, NULL)) != NULL) {
/* DAD already in progress */
nd6_dad_rele(dp);
return;
@@ -1293,6 +1344,7 @@
dp->dad_count = V_ip6_dad_count;
dp->dad_ns_icount = dp->dad_na_icount = 0;
dp->dad_ns_ocount = dp->dad_ns_tcount = 0;
+ dp->dad_ns_lcount = 0;
refcount_init(&dp->dad_refcnt, 1);
nd6_dad_add(dp);
if (delay == 0) {
@@ -1312,7 +1364,7 @@
{
struct dadq *dp;
- dp = nd6_dad_find(ifa);
+ dp = nd6_dad_find(ifa, NULL);
if (!dp) {
/* DAD wasn't started yet */
return;
@@ -1325,7 +1377,7 @@
* we were waiting for it to stop, so re-do the lookup.
*/
nd6_dad_rele(dp);
- if (nd6_dad_find(ifa) == NULL)
+ if (nd6_dad_find(ifa, NULL) == NULL)
return;
nd6_dad_del(dp);
@@ -1421,9 +1473,10 @@
char ip6buf[INET6_ADDRSTRLEN];
log(LOG_ERR, "%s: DAD detected duplicate IPv6 address %s: "
- "NS in/out=%d/%d, NA in=%d\n",
+ "NS in/out/loopback=%d/%d/%d, NA in=%d\n",
if_name(ifa->ifa_ifp), ip6_sprintf(ip6buf, &ia->ia_addr.sin6_addr),
- dp->dad_ns_icount, dp->dad_ns_ocount, dp->dad_na_icount);
+ 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;
@@ -1475,6 +1528,8 @@
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct ifnet *ifp = ifa->ifa_ifp;
+ uint8_t *nonce;
+ int i;
dp->dad_ns_tcount++;
if ((ifp->if_flags & IFF_UP) == 0) {
@@ -1485,11 +1540,25 @@
}
dp->dad_ns_ocount++;
- nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1);
+ if (V_dad_enhanced != 0) {
+ for (i = 0; i < ND_OPT_NONCE_LEN32; i++)
+ dp->dad_nonce[i] = arc4random();
+ nonce = (uint8_t *)&dp->dad_nonce[0];
+ /*
+ * XXXHRS: Note that in the case that
+ * DupAddrDetectTransmits > 1, multiple NS messages with
+ * different nonces can be looped back in an unexpected
+ * order. The current implementation recognizes only
+ * the latest nonce on the sender side. Practically it
+ * should work well in almost all cases.
+ */
+ } else
+ nonce = NULL;
+ nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, nonce);
}
static void
-nd6_dad_ns_input(struct ifaddr *ifa)
+nd6_dad_ns_input(struct ifaddr *ifa, struct nd_opt_nonce *ndopt_nonce)
{
struct in6_ifaddr *ia;
struct ifnet *ifp;
@@ -1502,22 +1571,13 @@
ia = (struct in6_ifaddr *)ifa;
ifp = ifa->ifa_ifp;
taddr6 = &ia->ia_addr.sin6_addr;
- dp = nd6_dad_find(ifa);
+ /* Ignore Nonce option when Enhanced DAD is disabled. */
+ if (V_dad_enhanced == 0)
+ ndopt_nonce = NULL;
+ dp = nd6_dad_find(ifa, ndopt_nonce);
if (dp == NULL)
return;
- /* Quickhack - completely ignore DAD NS packets */
- if (V_dad_ignore_ns) {
- char ip6buf[INET6_ADDRSTRLEN];
- nd6log((LOG_INFO,
- "nd6_dad_ns_input: ignoring DAD NS packet for "
- "address %s(%s)\n", ip6_sprintf(ip6buf, taddr6),
- if_name(ifa->ifa_ifp)));
- return;
- }
-
- /* XXX more checks for loopback situation - see nd6_dad_timer too */
-
dp->dad_ns_icount++;
nd6_dad_rele(dp);
}
@@ -1530,7 +1590,7 @@
if (ifa == NULL)
panic("ifa == NULL in nd6_dad_na_input");
- dp = nd6_dad_find(ifa);
+ dp = nd6_dad_find(ifa, NULL);
if (dp != NULL) {
dp->dad_na_icount++;
nd6_dad_rele(dp);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Apr 20, 4:38 PM (17 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31851308
Default Alt Text
D1835.id3761.diff (13 KB)
Attached To
Mode
D1835: Enhanced IPv6 DAD algorithm
Attached
Detach File
Event Timeline
Log In to Comment