Module Name: src Committed By: roy Date: Tue Sep 15 10:05:37 UTC 2020
Modified Files: src/sys/net: nd.c nd.h src/sys/netinet: if_arp.c src/sys/netinet6: nd6.c Log Message: Implement RFC 7048, making Neighbor Unreachability Detection less impatient 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. To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/sys/net/nd.c src/sys/net/nd.h cvs rdiff -u -r1.296 -r1.297 src/sys/netinet/if_arp.c cvs rdiff -u -r1.273 -r1.274 src/sys/netinet6/nd6.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/nd.c diff -u src/sys/net/nd.c:1.2 src/sys/net/nd.c:1.3 --- src/sys/net/nd.c:1.2 Mon Sep 14 15:09:57 2020 +++ src/sys/net/nd.c Tue Sep 15 10:05:36 2020 @@ -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 <sys/cdefs.h> -__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 <sys/callout.h> #include <sys/mbuf.h> @@ -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 typ 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; Index: src/sys/net/nd.h diff -u src/sys/net/nd.h:1.2 src/sys/net/nd.h:1.3 --- src/sys/net/nd.h:1.2 Mon Sep 14 15:09:57 2020 +++ src/sys/net/nd.h Tue Sep 15 10:05:36 2020 @@ -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); }; Index: src/sys/netinet/if_arp.c diff -u src/sys/netinet/if_arp.c:1.296 src/sys/netinet/if_arp.c:1.297 --- src/sys/netinet/if_arp.c:1.296 Mon Sep 14 15:09:57 2020 +++ src/sys/netinet/if_arp.c Tue Sep 15 10:05:36 2020 @@ -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 <sys/cdefs.h> -__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( 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, __u 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; Index: src/sys/netinet6/nd6.c diff -u src/sys/netinet6/nd6.c:1.273 src/sys/netinet6/nd6.c:1.274 --- src/sys/netinet6/nd6.c:1.273 Mon Sep 14 15:09:57 2020 +++ src/sys/netinet6/nd6.c Tue Sep 15 10:05:36 2020 @@ -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 <sys/cdefs.h> -__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( 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);