tech-net archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
IPv6 NS looback avoidance - RFC7527 support
Hi
Following on from my prior email, this new patch removes the limitation
of only checking the most recent nonce sent.
It can store 3 nonces (configurable at compile time) and if the user
wants to send more probes, it will store the last 3.
Currently we default to sending 1 probe and the storage for an extra 2
is just a nice to have.
Commentary and ideas on how to test it welcome.
Roy
Index: sys/netinet/icmp6.h
===================================================================
RCS file: /cvsroot/src/sys/netinet/icmp6.h,v
retrieving revision 1.49
diff -u -p -r1.49 icmp6.h
--- sys/netinet/icmp6.h 23 Jan 2018 10:55:38 -0000 1.49
+++ sys/netinet/icmp6.h 2 Mar 2018 12:29:07 -0000
@@ -305,10 +305,12 @@ struct nd_opt_hdr { /* Neighbor discove
#define ND_OPT_HOMEAGENT_INFO 8
#define ND_OPT_SOURCE_ADDRLIST 9
#define ND_OPT_TARGET_ADDRLIST 10
+#define ND_OPT_NONCE 14 /* RFC 3971 */
#define ND_OPT_MAP 23 /* RFC 5380 */
#define ND_OPT_ROUTE_INFO 24 /* RFC 4191 */
#define ND_OPT_RDNSS 25 /* RFC 6016 */
#define ND_OPT_DNSSL 31 /* RFC 6016 */
+#define ND_OPT_MAX 31
struct nd_opt_route_info { /* route info */
u_int8_t nd_opt_rti_type;
@@ -348,6 +350,16 @@ struct nd_opt_mtu { /* MTU option */
u_int32_t nd_opt_mtu_mtu;
} __packed;
+#define ND_OPT_NONCE_LEN ((1 * 8) - 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 {
+ 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_rdnss { /* RDNSS option RFC 6106 */
u_int8_t nd_opt_rdnss_type;
u_int8_t nd_opt_rdnss_len;
Index: sys/netinet6/nd6.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6.c,v
retrieving revision 1.245
diff -u -p -r1.245 nd6.c
--- sys/netinet6/nd6.c 29 Jan 2018 19:51:15 -0000 1.245
+++ sys/netinet6/nd6.c 2 Mar 2018 12:29:08 -0000
@@ -347,6 +347,7 @@ nd6_options(union nd_opts *ndopts)
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",
@@ -560,7 +561,7 @@ nd6_llinfo_timer(void *arg)
psrc = nd6_llinfo_get_holdsrc(ln, &src);
LLE_FREE_LOCKED(ln);
ln = NULL;
- nd6_ns_output(ifp, daddr6, taddr6, psrc, 0);
+ nd6_ns_output(ifp, daddr6, taddr6, psrc, NULL);
}
out:
@@ -2426,7 +2427,7 @@ nd6_resolve(struct ifnet *ifp, const str
psrc = nd6_llinfo_get_holdsrc(ln, &src);
LLE_WUNLOCK(ln);
ln = NULL;
- nd6_ns_output(ifp, NULL, &dst->sin6_addr, psrc, 0);
+ nd6_ns_output(ifp, NULL, &dst->sin6_addr, psrc, NULL);
} else {
/* We did the lookup so we need to do the unlock here. */
LLE_WUNLOCK(ln);
Index: sys/netinet6/nd6.h
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6.h,v
retrieving revision 1.85
diff -u -p -r1.85 nd6.h
--- sys/netinet6/nd6.h 22 Jun 2017 09:24:02 -0000 1.85
+++ sys/netinet6/nd6.h 2 Mar 2018 12:29:08 -0000
@@ -397,7 +397,7 @@ extern int ip6_temp_regen_advance; /* se
extern int nd6_numroutes;
union nd_opts {
- struct nd_opt_hdr *nd_opt_array[8];
+ struct nd_opt_hdr *nd_opt_array[16]; /* max = ND_OPT_NONCE */
struct {
struct nd_opt_hdr *zero;
struct nd_opt_hdr *src_lladdr;
@@ -405,6 +405,16 @@ union nd_opts {
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;
@@ -417,6 +427,7 @@ union nd_opts {
#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
@@ -454,7 +465,7 @@ void nd6_na_output(struct ifnet *, const
const struct in6_addr *, u_long, int, const 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 in6_addr *, int);
+ const struct in6_addr *, struct in6_addr *, uint8_t *);
const void *nd6_ifptomac(const struct ifnet *);
void nd6_dad_start(struct ifaddr *, int);
void nd6_dad_stop(struct ifaddr *);
Index: sys/netinet6/nd6_nbr.c
===================================================================
RCS file: /cvsroot/src/sys/netinet6/nd6_nbr.c,v
retrieving revision 1.148
diff -u -p -r1.148 nd6_nbr.c
--- sys/netinet6/nd6_nbr.c 24 Feb 2018 07:53:15 -0000 1.148
+++ sys/netinet6/nd6_nbr.c 2 Mar 2018 12:29:08 -0000
@@ -52,6 +52,7 @@ __KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v
#include <sys/syslog.h>
#include <sys/queue.h>
#include <sys/callout.h>
+#include <sys/cprng.h>
#include <net/if.h>
#include <net/if_types.h>
@@ -77,16 +78,15 @@ __KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v
#include <net/net_osdep.h>
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_starttimer(struct dadq *, int);
static void nd6_dad_destroytimer(struct dadq *);
static void nd6_dad_timer(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_dad_duplicated(struct dadq *);
-static int dad_ignore_ns = 0; /* ignore NS in DAD - specwise incorrect*/
static int dad_maxtry = 15; /* max # of *tries* to transmit DAD packet */
/*
@@ -309,7 +309,7 @@ nd6_ns_input(struct mbuf *m, int off, in
* silently ignore it.
*/
if (IN6_IS_ADDR_UNSPECIFIED(&saddr6))
- nd6_dad_ns_input(ifa);
+ nd6_dad_ns_input(ifa, ndopts.nd_opts_nonce);
ifa_release(ifa, &psref_ia);
ifa = NULL;
@@ -374,7 +374,7 @@ void
nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6,
const struct in6_addr *taddr6,
struct in6_addr *hsrc,
- int dad /* duplicate address detection */)
+ uint8_t *nonce /* duplicate address detection */)
{
struct mbuf *m;
struct ip6_hdr *ip6;
@@ -441,7 +441,7 @@ nd6_ns_output(struct ifnet *ifp, const s
if (in6_setscope(&ip6->ip6_dst, ifp, NULL) != 0)
goto bad;
}
- if (!dad) {
+ if (nonce == NULL) {
int s;
/*
* RFC2461 7.2.2:
@@ -512,7 +512,7 @@ nd6_ns_output(struct ifnet *ifp, const s
* 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... */
@@ -527,12 +527,30 @@ nd6_ns_output(struct ifnet *ifp, const s
memcpy((void *)(nd_opt + 1), mac, ifp->if_addrlen);
}
+ /* Add a nonce option (RFC 3971) to detect looped back NS messages.
+ * This behavior is documented in RFC 7527. */
+ if (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;
+ memset(nd_opt, 0, optlen);
+ nd_opt->nd_opt_type = ND_OPT_NONCE;
+ nd_opt->nd_opt_len = optlen >> 3;
+ memcpy(nd_opt + 1, nonce, ND_OPT_NONCE_LEN);
+ }
+
ip6->ip6_plen = htons((u_int16_t)icmp6len);
nd_ns->nd_ns_cksum = 0;
nd_ns->nd_ns_cksum =
in6_cksum(m, IPPROTO_ICMPV6, sizeof(*ip6), icmp6len);
- 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);
ICMP6_STATINC(ICMP6_STAT_OUTHIST + ND_NEIGHBOR_SOLICIT);
@@ -1055,12 +1073,15 @@ TAILQ_HEAD(dadq_head, dadq);
struct dadq {
TAILQ_ENTRY(dadq) dad_list;
struct ifaddr *dad_ifa;
- int dad_count; /* max NS to send */
- int dad_ns_tcount; /* # of trials to send NS */
- int dad_ns_ocount; /* NS sent so far */
+ int dad_count; /* max NS to send */
+ int dad_ns_tcount; /* # of trials to send NS */
+ 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;
+#define ND_OPT_NONCE_STORE 3 /* dad_count should not exceed this */
+ uint8_t dad_nonce[ND_OPT_NONCE_STORE][ND_OPT_NONCE_LEN];
};
static struct dadq_head dadq;
@@ -1068,17 +1089,42 @@ static int dad_init = 0;
static kmutex_t nd6_dad_lock;
static struct dadq *
-nd6_dad_find(struct ifaddr *ifa)
+nd6_dad_find(struct ifaddr *ifa, struct nd_opt_nonce *nonce)
{
struct dadq *dp;
+ int i, nonce_max;
KASSERT(mutex_owned(&nd6_dad_lock));
TAILQ_FOREACH(dp, &dadq, dad_list) {
- if (dp->dad_ifa == ifa)
- return dp;
+ if (dp->dad_ifa != ifa)
+ continue;
+
+ if (nonce == NULL ||
+ nonce->nd_opt_nonce_len != (ND_OPT_NONCE_LEN + 2) / 8)
+ break;
+
+ nonce_max = MIN(dp->dad_ns_ocount, ND_OPT_NONCE_STORE);
+ for (i = 0; i < nonce_max; i++) {
+ if (memcmp(nonce->nd_opt_nonce,
+ dp->dad_nonce[i],
+ ND_OPT_NONCE_LEN) == 0)
+ break;
+ }
+ if (i < nonce_max) {
+ char ip6buf[INET6_ADDRSTRLEN];
+
+ log(LOG_DEBUG,
+ "%s: detected a looped back NS message for %s\n",
+ ifa->ifa_ifp ? if_name(ifa->ifa_ifp) : "???",
+ IN6_PRINT(ip6buf, IFA_IN6(ifa)));
+ dp->dad_ns_lcount++;
+ continue;
+ }
+
+ break;
}
- return NULL;
+ return dp;
}
static void
@@ -1149,7 +1195,7 @@ nd6_dad_start(struct ifaddr *ifa, int xt
dp = kmem_intr_alloc(sizeof(*dp), KM_NOSLEEP);
mutex_enter(&nd6_dad_lock);
- if (nd6_dad_find(ifa) != NULL) {
+ if (nd6_dad_find(ifa, NULL) != NULL) {
mutex_exit(&nd6_dad_lock);
/* DAD already in progress */
if (dp != NULL)
@@ -1178,6 +1224,7 @@ nd6_dad_start(struct ifaddr *ifa, int xt
dp->dad_count = 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;
TAILQ_INSERT_TAIL(&dadq, (struct dadq *)dp, dad_list);
nd6log(LOG_DEBUG, "%s: starting DAD for %s\n", if_name(ifa->ifa_ifp),
@@ -1204,7 +1251,7 @@ nd6_dad_stop(struct ifaddr *ifa)
return;
mutex_enter(&nd6_dad_lock);
- dp = nd6_dad_find(ifa);
+ dp = nd6_dad_find(ifa, NULL);
if (dp == NULL) {
mutex_exit(&nd6_dad_lock);
/* DAD wasn't started yet */
@@ -1340,9 +1387,10 @@ nd6_dad_duplicated(struct dadq *dp)
ifp = ifa->ifa_ifp;
ia = (struct in6_ifaddr *)ifa;
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(ifp), IN6_PRINT(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;
@@ -1396,6 +1444,7 @@ nd6_dad_ns_output(struct dadq *dp, struc
{
struct in6_ifaddr *ia = (struct in6_ifaddr *)ifa;
struct ifnet *ifp = ifa->ifa_ifp;
+ uint8_t *nonce;
dp->dad_ns_tcount++;
if ((ifp->if_flags & IFF_UP) == 0) {
@@ -1412,12 +1461,15 @@ nd6_dad_ns_output(struct dadq *dp, struc
}
dp->dad_ns_tcount = 0;
+ nonce = dp->dad_nonce[dp->dad_ns_ocount % ND_OPT_NONCE_STORE];
+ cprng_fast(nonce, ND_OPT_NONCE_LEN);
dp->dad_ns_ocount++;
- nd6_ns_output(ifp, NULL, &ia->ia_addr.sin6_addr, NULL, 1);
+
+ 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 *nonce)
{
struct in6_ifaddr *ia;
const struct in6_addr *taddr6;
@@ -1432,16 +1484,7 @@ nd6_dad_ns_input(struct ifaddr *ifa)
duplicate = 0;
mutex_enter(&nd6_dad_lock);
- dp = nd6_dad_find(ifa);
-
- /* Quickhack - completely ignore DAD NS packets */
- if (dad_ignore_ns) {
- char ip6buf[INET6_ADDRSTRLEN];
- nd6log(LOG_INFO, "ignoring DAD NS packet for "
- "address %s(%s)\n", IN6_PRINT(ip6buf, taddr6),
- if_name(ifa->ifa_ifp));
- return;
- }
+ dp = nd6_dad_find(ifa, nonce);
/*
* if I'm yet to start DAD, someone else started using this address
@@ -1473,7 +1516,7 @@ nd6_dad_na_input(struct ifaddr *ifa)
KASSERT(ifa != NULL);
mutex_enter(&nd6_dad_lock);
- dp = nd6_dad_find(ifa);
+ dp = nd6_dad_find(ifa, NULL);
if (dp)
dp->dad_na_icount++;
Home |
Main Index |
Thread Index |
Old Index