Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys nd6: add a nonce to DaD probes in-case they are looped b...



details:   https://anonhg.NetBSD.org/src/rev/f5403f29b242
branches:  trunk
changeset: 321206:f5403f29b242
user:      roy <roy%NetBSD.org@localhost>
date:      Tue Mar 06 10:57:00 2018 +0000

description:
nd6: add a nonce to DaD probes in-case they are looped back to us

This implements RFC 7527, based a similar change in FreeBSD.

diffstat:

 sys/netinet/icmp6.h    |   14 +++++-
 sys/netinet6/nd6.c     |    9 ++-
 sys/netinet6/nd6.h     |   17 +++++-
 sys/netinet6/nd6_nbr.c |  120 +++++++++++++++++++++++++++++++++++-------------
 4 files changed, 118 insertions(+), 42 deletions(-)

diffs (truncated from 415 to 300 lines):

diff -r 2497a32bfba2 -r f5403f29b242 sys/netinet/icmp6.h
--- a/sys/netinet/icmp6.h       Tue Mar 06 10:07:06 2018 +0000
+++ b/sys/netinet/icmp6.h       Tue Mar 06 10:57:00 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: icmp6.h,v 1.49 2018/01/23 10:55:38 maxv Exp $  */
+/*     $NetBSD: icmp6.h,v 1.50 2018/03/06 10:57:00 roy Exp $   */
 /*     $KAME: icmp6.h,v 1.84 2003/04/23 10:26:51 itojun Exp $  */
 
 
@@ -305,10 +305,12 @@
 #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 @@
        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;
diff -r 2497a32bfba2 -r f5403f29b242 sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c        Tue Mar 06 10:07:06 2018 +0000
+++ b/sys/netinet6/nd6.c        Tue Mar 06 10:57:00 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nd6.c,v 1.246 2018/03/06 07:24:01 ozaki-r Exp $        */
+/*     $NetBSD: nd6.c,v 1.247 2018/03/06 10:57:00 roy Exp $    */
 /*     $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $   */
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.246 2018/03/06 07:24:01 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.247 2018/03/06 10:57:00 roy Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_net_mpsafe.h"
@@ -347,6 +347,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",
@@ -554,7 +555,7 @@
                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:
@@ -2420,7 +2421,7 @@
                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);
diff -r 2497a32bfba2 -r f5403f29b242 sys/netinet6/nd6.h
--- a/sys/netinet6/nd6.h        Tue Mar 06 10:07:06 2018 +0000
+++ b/sys/netinet6/nd6.h        Tue Mar 06 10:57:00 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nd6.h,v 1.85 2017/06/22 09:24:02 ozaki-r Exp $ */
+/*     $NetBSD: nd6.h,v 1.86 2018/03/06 10:57:00 roy Exp $     */
 /*     $KAME: nd6.h,v 1.95 2002/06/08 11:31:06 itojun Exp $    */
 
 /*
@@ -397,7 +397,7 @@
 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 @@
                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 @@
 #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 @@
        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 *);
diff -r 2497a32bfba2 -r f5403f29b242 sys/netinet6/nd6_nbr.c
--- a/sys/netinet6/nd6_nbr.c    Tue Mar 06 10:07:06 2018 +0000
+++ b/sys/netinet6/nd6_nbr.c    Tue Mar 06 10:57:00 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: nd6_nbr.c,v 1.148 2018/02/24 07:53:15 ozaki-r Exp $    */
+/*     $NetBSD: nd6_nbr.c,v 1.149 2018/03/06 10:57:00 roy Exp $        */
 /*     $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $        */
 
 /*
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.148 2018/02/24 07:53:15 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.149 2018/03/06 10:57:00 roy Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -52,6 +52,7 @@
 #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 @@
 #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 @@
                 * 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 @@
 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 @@
                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 @@
         *      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 @@
                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,24 @@
 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 */
+       /*
+        * The default ip6_dad_count is 1 as specified by RFC 4862 and
+        * practically must users won't exceed this.
+        * A storage of 3 is defaulted to here, in-case the administrator wants
+        * to match the equivalent behaviour in our ARP implementation.
+        * This constraint could be removed by sending the on wire nonce as
+        * hmac(key, dad_ns_ocount), but that would increase the nonce size
+        * sent on the wire.
+        */
+       uint8_t dad_nonce[ND_OPT_NONCE_STORE][ND_OPT_NONCE_LEN];
 };
 
 static struct dadq_head dadq;
@@ -1068,17 +1098,42 @@
 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++) {



Home | Main Index | Thread Index | Old Index