diff --git a/sys/net/nd.c b/sys/net/nd.c index d70d86eac286..39161f74ca58 100644 --- a/sys/net/nd.c +++ b/sys/net/nd.c @@ -1,4 +1,4 @@ -/* $NetBSD: */ +/* $NetBSD: nd.c,v 1.3 2020/09/15 10:05:36 roy Exp $ */ /* * Copyright (c) 2020 The NetBSD Foundation, Inc. @@ -28,7 +28,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: nd.c,v 1.2 2020/09/14 15:09:57 roy Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nd.c,v 1.3 2020/09/15 10:05:36 roy Exp $"); #include #include @@ -56,7 +56,8 @@ nd_timer(void *arg) struct ifnet *ifp = NULL; struct psref psref; struct mbuf *m = NULL; - bool send_ns = false, missed = false; + bool send_ns = false; + uint16_t missed = 0; union l3addr taddr, *daddrp = NULL; SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); @@ -84,10 +85,9 @@ nd_timer(void *arg) break; case ND_LLINFO_INCOMPLETE: - if (ln->ln_asked++ < nd->nd_mmaxtries) { - send_ns = true; + send_ns = true; + if (ln->ln_asked++ < nd->nd_mmaxtries) break; - } if (ln->ln_hold) { struct mbuf *m0, *mnxt; @@ -107,12 +107,8 @@ nd_timer(void *arg) ln->ln_hold = NULL; } - missed = true; + missed = ND_LLINFO_INCOMPLETE; ln->ln_state = ND_LLINFO_WAITDELETE; - if (ln->ln_asked == nd->nd_mmaxtries) - nd_set_timer(ln, ND_TIMER_RETRANS); - else - send_ns = true; break; case ND_LLINFO_REACHABLE: @@ -144,23 +140,50 @@ nd_timer(void *arg) break; case ND_LLINFO_PROBE: - if (ln->ln_asked < nd->nd_umaxtries) { - ln->ln_asked++; - send_ns = true; + send_ns = true; + if (ln->ln_asked++ < nd->nd_umaxtries) { daddrp = &taddr; } else { - LLE_REMREF(ln); - nd->nd_free(ln, 0); - ln = NULL; + ln->ln_state = ND_LLINFO_UNREACHABLE; + ln->ln_asked = 1; + missed = ND_LLINFO_PROBE; + /* nd_missed() consumers can use missed to know if + * they need to send ICMP UNREACHABLE or not. */ } break; + case ND_LLINFO_UNREACHABLE: + /* + * RFC 7048 Section 3 says in the UNREACHABLE state + * packets continue to be sent to the link-layer address and + * then backoff exponentially. + * We adjust this slightly and move to the INCOMPLETE state + * after nd_mmaxtries probes and then start backing off. + * + * This results in simpler code whilst providing a more robust + * model which doubles the time to failure over what we did + * before. We don't want to be back to the old ARP model where + * no unreachability errors are returned because very + * few applications would look at unreachability hints provided + * such as ND_LLINFO_UNREACHABLE or RTM_MISS. + */ + send_ns = true; + if (ln->ln_asked++ < nd->nd_mmaxtries) + break; + + missed = ND_LLINFO_UNREACHABLE; + ln->ln_state = ND_LLINFO_WAITDELETE; + ln->la_flags &= ~LLE_VALID; + break; } if (send_ns) { uint8_t lladdr[255], *lladdrp; union l3addr src, *psrc; - nd_set_timer(ln, ND_TIMER_RETRANS); + if (ln->ln_state == ND_LLINFO_WAITDELETE) + nd_set_timer(ln, ND_TIMER_RETRANS_BACKOFF); + else + nd_set_timer(ln, ND_TIMER_RETRANS); if (ln->ln_state > ND_LLINFO_INCOMPLETE && ln->la_flags & LLE_VALID) { @@ -181,7 +204,7 @@ out: SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); if (missed) - nd->nd_missed(ifp, &taddr, m); + nd->nd_missed(ifp, &taddr, missed, m); if (ifp != NULL) if_release(ifp, &psref); } @@ -241,6 +264,22 @@ nd_set_timer(struct llentry *ln, int type) case ND_TIMER_RETRANS: xtick = nd->nd_retrans(ifp) * hz / 1000; break; + case ND_TIMER_RETRANS_BACKOFF: + { + unsigned int retrans = nd->nd_retrans(ifp); + unsigned int attempts = ln->ln_asked - nd->nd_mmaxtries; + + xtick = retrans; + while (attempts-- != 0) { + xtick *= nd->nd_retransmultiple; + if (xtick > nd->nd_maxretrans || xtick < retrans) { + xtick = nd->nd_maxretrans; + break; + } + } + xtick = xtick * hz / 1000; + break; + } case ND_TIMER_REACHABLE: xtick = nd->nd_reachable(ifp) * hz / 1000; break; diff --git a/sys/net/nd.h b/sys/net/nd.h index 0165e3224487..b0edb99f2270 100644 --- a/sys/net/nd.h +++ b/sys/net/nd.h @@ -1,4 +1,4 @@ -/* $NetBSD: nd.h,v 1.2 2020/09/14 15:09:57 roy Exp $ */ +/* $NetBSD: nd.h,v 1.3 2020/09/15 10:05:36 roy Exp $ */ /* * Copyright (c) 2020 The NetBSD Foundation, Inc. @@ -39,6 +39,7 @@ #define ND_LLINFO_STALE 2 #define ND_LLINFO_DELAY 3 #define ND_LLINFO_PROBE 4 +#define ND_LLINFO_UNREACHABLE 5 #ifdef _KERNEL #define ND_IS_LLINFO_PROBREACH(ln) \ @@ -47,18 +48,21 @@ (((ln)->ln_expire == 0) && ((ln)->ln_state > ND_LLINFO_INCOMPLETE)) /* ND timer types */ -#define ND_TIMER_IMMEDIATE 0 -#define ND_TIMER_TICK 1 -#define ND_TIMER_REACHABLE 2 -#define ND_TIMER_RETRANS 3 -#define ND_TIMER_EXPIRE 4 -#define ND_TIMER_DELAY 5 -#define ND_TIMER_GC 6 +#define ND_TIMER_IMMEDIATE 0 +#define ND_TIMER_TICK 1 +#define ND_TIMER_REACHABLE 2 +#define ND_TIMER_RETRANS 3 +#define ND_TIMER_RETRANS_BACKOFF 4 +#define ND_TIMER_EXPIRE 5 +#define ND_TIMER_DELAY 6 +#define ND_TIMER_GC 7 /* node constants */ #define MAX_REACHABLE_TIME 3600000 /* msec */ #define REACHABLE_TIME 30000 /* msec */ #define RETRANS_TIMER 1000 /* msec */ +#define MAX_RETRANS_TIMER 60000 /* msec */ +#define BACKOFF_MULTIPLE 3 #define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ #define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ #define ND_COMPUTE_RTIME(x) \ @@ -70,6 +74,8 @@ struct nd_domain { int nd_delay; /* delay first probe time in seconds */ int nd_mmaxtries; /* maximum multicast query */ int nd_umaxtries; /* maximum unicast query */ + int nd_retransmultiple; /* retransmission multiplier for backoff */ + int nd_maxretrans; /* maximum retransmission time in msec */ int nd_maxnudhint; /* max # of subsequent upper layer hints */ int nd_maxqueuelen; /* max # of packets in unresolved ND entries */ bool (*nd_nud_enabled)(struct ifnet *); @@ -78,7 +84,8 @@ struct nd_domain { union l3addr *(*nd_holdsrc)(struct llentry *, union l3addr *); void (*nd_output)(struct ifnet *, const union l3addr *, const union l3addr *, const uint8_t *, const union l3addr *); - void (*nd_missed)(struct ifnet *, const union l3addr *, struct mbuf *); + void (*nd_missed)(struct ifnet *, const union l3addr *, + int16_t, struct mbuf *); void (*nd_free)(struct llentry *, int); }; diff --git a/sys/netinet/if_arp.c b/sys/netinet/if_arp.c index 2c42b4f8847d..6f6ff3050213 100644 --- a/sys/netinet/if_arp.c +++ b/sys/netinet/if_arp.c @@ -1,4 +1,4 @@ -/* $NetBSD: if_arp.c,v 1.296 2020/09/14 15:09:57 roy Exp $ */ +/* $NetBSD: if_arp.c,v 1.297 2020/09/15 10:05:36 roy Exp $ */ /* * Copyright (c) 1998, 2000, 2008 The NetBSD Foundation, Inc. @@ -68,7 +68,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.296 2020/09/14 15:09:57 roy Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_arp.c,v 1.297 2020/09/15 10:05:36 roy Exp $"); #ifdef _KERNEL_OPT #include "opt_ddb.h" @@ -145,7 +145,7 @@ static union l3addr *arp_llinfo_holdsrc(struct llentry *, union l3addr *); static void arp_llinfo_output(struct ifnet *, const union l3addr *, const union l3addr *, const uint8_t *, const union l3addr *); static void arp_llinfo_missed(struct ifnet *, const union l3addr *, - struct mbuf *); + int16_t, struct mbuf *); static void arp_free(struct llentry *, int); static struct nd_domain arp_nd_domain = { @@ -153,6 +153,8 @@ static struct nd_domain arp_nd_domain = { .nd_delay = 5, /* delay first probe time 5 second */ .nd_mmaxtries = 3, /* maximum broadcast query */ .nd_umaxtries = 3, /* maximum unicast query */ + .nd_retransmultiple = BACKOFF_MULTIPLE, + .nd_maxretrans = MAX_RETRANS_TIMER, .nd_maxnudhint = 0, /* max # of subsequent upper layer hints */ .nd_maxqueuelen = 1, /* max # of packets in unresolved ND entries */ .nd_nud_enabled = arp_nud_enabled, @@ -1374,7 +1376,7 @@ arp_llinfo_output(struct ifnet *ifp, __unused const union l3addr *daddr, static void arp_llinfo_missed(struct ifnet *ifp, const union l3addr *taddr, - struct mbuf *m) + __unused int16_t type, struct mbuf *m) { struct in_addr mdaddr = zeroin_addr; struct sockaddr_in dsin, tsin; diff --git a/sys/netinet6/nd6.c b/sys/netinet6/nd6.c index 8d5d4618a275..bec59d85f164 100644 --- a/sys/netinet6/nd6.c +++ b/sys/netinet6/nd6.c @@ -1,4 +1,4 @@ -/* $NetBSD: nd6.c,v 1.273 2020/09/14 15:09:57 roy Exp $ */ +/* $NetBSD: nd6.c,v 1.274 2020/09/15 10:05:36 roy Exp $ */ /* $KAME: nd6.c,v 1.279 2002/06/08 11:16:51 itojun Exp $ */ /* @@ -31,7 +31,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.273 2020/09/14 15:09:57 roy Exp $"); +__KERNEL_RCSID(0, "$NetBSD: nd6.c,v 1.274 2020/09/15 10:05:36 roy Exp $"); #ifdef _KERNEL_OPT #include "opt_compat_netbsd.h" @@ -111,7 +111,7 @@ static union l3addr *nd6_llinfo_holdsrc(struct llentry *, union l3addr *); static void nd6_llinfo_output(struct ifnet *, const union l3addr *, const union l3addr *, const uint8_t *, const union l3addr *); static void nd6_llinfo_missed(struct ifnet *, const union l3addr *, - struct mbuf *); + int16_t, struct mbuf *); static void nd6_timer(void *); static void nd6_timer_work(struct work *, void *); static struct nd_opt_hdr *nd6_option(union nd_opts *); @@ -126,6 +126,8 @@ struct nd_domain nd6_nd_domain = { .nd_delay = 5, /* delay first probe time 5 second */ .nd_mmaxtries = 3, /* maximum unicast query */ .nd_umaxtries = 3, /* maximum multicast query */ + .nd_retransmultiple = BACKOFF_MULTIPLE, + .nd_maxretrans = MAX_RETRANS_TIMER, .nd_maxnudhint = 0, /* max # of subsequent upper layer hints */ .nd_maxqueuelen = 1, /* max # of packets in unresolved ND entries */ .nd_nud_enabled = nd6_nud_enabled, @@ -411,15 +413,25 @@ nd6_llinfo_retrans(struct ifnet *ifp) } static void -nd6_llinfo_missed(struct ifnet *ifp, const union l3addr *taddr, struct mbuf *m) +nd6_llinfo_missed(struct ifnet *ifp, const union l3addr *taddr, + int16_t type, struct mbuf *m) { struct in6_addr mdaddr6 = zeroin6_addr; struct sockaddr_in6 dsin6, tsin6; struct sockaddr *sa; - if (m != NULL) - icmp6_error2(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_ADDR, 0, ifp, &mdaddr6); + if (m != NULL) { + if (type == ND_LLINFO_PROBE) { + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + + /* XXX pullup? */ + if (sizeof(*ip6) < m->m_len) + mdaddr6 = ip6->ip6_src; + m_freem(m); + } else + icmp6_error2(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, 0, ifp, &mdaddr6); + } if (!IN6_IS_ADDR_UNSPECIFIED(&mdaddr6)) { sockaddr_in6_init(&dsin6, &mdaddr6, 0, 0, 0); sa = sin6tosa(&dsin6);