Source-Changes-HG archive

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

[src/trunk]: src/sys Implement RFC 7048, making Neighbor Unreachability Detec...



details:   https://anonhg.NetBSD.org/src/rev/aa1c18978cd4
branches:  trunk
changeset: 1014291:aa1c18978cd4
user:      roy <roy%NetBSD.org@localhost>
date:      Tue Sep 15 10:05:36 2020 +0000

description:
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.

diffstat:

 sys/net/nd.c         |  77 +++++++++++++++++++++++++++++++++++++++------------
 sys/net/nd.h         |  25 ++++++++++------
 sys/netinet/if_arp.c |  10 ++++--
 sys/netinet6/nd6.c   |  26 ++++++++++++----
 4 files changed, 99 insertions(+), 39 deletions(-)

diffs (truncated from 321 to 300 lines):

diff -r 09d6ed98dcce -r aa1c18978cd4 sys/net/nd.c
--- a/sys/net/nd.c      Tue Sep 15 09:33:12 2020 +0000
+++ b/sys/net/nd.c      Tue Sep 15 10:05:36 2020 +0000
@@ -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 @@
        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 @@
                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 @@
                        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 @@
                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 @@
        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 @@
        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 -r 09d6ed98dcce -r aa1c18978cd4 sys/net/nd.h
--- a/sys/net/nd.h      Tue Sep 15 09:33:12 2020 +0000
+++ b/sys/net/nd.h      Tue Sep 15 10:05:36 2020 +0000
@@ -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 @@
        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 @@
        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 -r 09d6ed98dcce -r aa1c18978cd4 sys/netinet/if_arp.c
--- a/sys/netinet/if_arp.c      Tue Sep 15 09:33:12 2020 +0000
+++ b/sys/netinet/if_arp.c      Tue Sep 15 10:05:36 2020 +0000
@@ -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 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 @@
        .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 @@
 
 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 -r 09d6ed98dcce -r aa1c18978cd4 sys/netinet6/nd6.c
--- a/sys/netinet6/nd6.c        Tue Sep 15 09:33:12 2020 +0000
+++ b/sys/netinet6/nd6.c        Tue Sep 15 10:05:36 2020 +0000
@@ -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 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 @@
        .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 @@
 }
 
 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;



Home | Main Index | Thread Index | Old Index