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.
This commit is contained in:
parent
4fda12c619
commit
89bda1e316
@ -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 @@ struct nd_opt_hdr { /* Neighbor discovery option header */
|
||||
#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;
|
||||
|
@ -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 @@ 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",
|
||||
@ -554,7 +555,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:
|
||||
@ -2420,7 +2421,7 @@ nd6_resolve(struct ifnet *ifp, const struct rtentry *rt, struct mbuf *m,
|
||||
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);
|
||||
|
@ -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 ip6_temp_regen_advance; /* seconds */
|
||||
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 struct in6_addr *,
|
||||
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 *);
|
||||
|
@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.148 2018/02/24 07:53:15 ozaki-r Exp $"
|
||||
#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 1.148 2018/02/24 07:53:15 ozaki-r Exp $"
|
||||
#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, int icmp6len)
|
||||
* 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 struct in6_addr *daddr6,
|
||||
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 struct in6_addr *daddr6,
|
||||
* 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 struct in6_addr *daddr6,
|
||||
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 @@ 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 */
|
||||
/*
|
||||
* 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 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 +1204,7 @@ nd6_dad_start(struct ifaddr *ifa, int xtick)
|
||||
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 +1233,7 @@ nd6_dad_start(struct ifaddr *ifa, int xtick)
|
||||
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 +1260,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 +1396,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 +1453,7 @@ nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa)
|
||||
{
|
||||
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 +1470,15 @@ nd6_dad_ns_output(struct dadq *dp, struct ifaddr *ifa)
|
||||
}
|
||||
|
||||
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 +1493,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 +1525,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++;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user