diff -r a5e937c1fc66 share/man/man7/sysctl.7 --- a/share/man/man7/sysctl.7 Tue Jul 28 18:53:07 2020 +0000 +++ b/share/man/man7/sysctl.7 Thu Aug 20 13:11:34 2020 +0100 @@ -29,7 +29,7 @@ .\" .\" @(#)sysctl.3 8.4 (Berkeley) 5/9/95 .\" -.Dd July 13, 2020 +.Dd August 20, 2020 .Dt SYSCTL 7 .Os .Sh NAME @@ -1419,8 +1419,10 @@ The currently defined protocols and names are: .Bl -column "Protocol" "anonportalgo.available" "integer" "Changeable" -offset indent .It Sy Protocol Variable Ta Sy Type Ta Sy Changeable -.It arp down integer yes -.It arp keep integer yes +.It arp nd_delay integer yes +.It arp nd_bmaxtries integer yes +.It arp nd_umaxtries integer yes +.It arp nd_maxnudhint integer yes .It arp log_movements integer yes .It arp log_permanent_modify integer yes .It arp log_unknown_network integer yes @@ -1502,10 +1504,24 @@ .Pp The variables are as follows: .Bl -tag -width "123456" -.It Li arp.down -Failed ARP entry lifetime. -.It Li arp.keep -Valid ARP entry lifetime. +.It Li arp.nd_delay +The delay in seconds before sending the first probe, +after it has been decided that the entry is stale. +.It Li arp.nd_bmaxtries +The maximum number of broadcasts send to discover the hardware address +claiming an IP address. +.It Li arp.nd_umaxtries +The maximum number of unicasts send to the hardware address to ensure +it still claims an IP address. +.It Li arp.nd_maxnudhint +Neighbor discovery permits upper layer protocols to supply reachability +hints, to avoid unnecessary neighbor discovery exchanges. +The variable defines the number of consecutive hints the neighbor discovery +layer will take. +For example, by setting the variable to 3, neighbor discovery layer +will take 3 consecutive hints in maximum. +After receiving 3 hints, neighbor discovery layer will perform +normal neighbor discovery process. .It Li carp.allow If set to 0, incoming .Xr carp 4 @@ -2024,7 +2040,7 @@ .Pq RFC 2461 , in seconds. .It Li icmp6.nd6_maxnudhint -IPv6 neighbor discovery permits upper layer protocols to supply reachability +Neighbor discovery permits upper layer protocols to supply reachability hints, to avoid unnecessary neighbor discovery exchanges. The variable defines the number of consecutive hints the neighbor discovery layer will take. diff -r a5e937c1fc66 sys/net/Makefile --- a/sys/net/Makefile Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/net/Makefile Thu Aug 20 13:11:34 2020 +0100 @@ -6,7 +6,7 @@ if_bridgevar.h if_dl.h if_ether.h if_gif.h \ if_gre.h if_ieee1394.h if_ipsec.h if_llc.h if_media.h if_mpls.h \ if_pflog.h if_ppp.h if_pppoe.h if_l2tp.h if_sppp.h if_srt.h if_stats.h \ - if_stf.h if_tap.h if_tun.h if_types.h if_vlanvar.h net_stats.h \ + if_stf.h if_tap.h if_tun.h if_types.h if_vlanvar.h nd.h net_stats.h \ netisr.h pfil.h pfkeyv2.h pfvar.h ppp-comp.h ppp_defs.h radix.h \ raw_cb.h route.h slcompress.h slip.h zlib.h diff -r a5e937c1fc66 sys/net/files.net --- a/sys/net/files.net Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/net/files.net Thu Aug 20 13:11:34 2020 +0100 @@ -33,6 +33,7 @@ file net/if_tun.c tun file net/if_vlan.c vlan needs-flag file net/if_pppoe.c pppoe needs-flag +file net/nd.c inet | inet6 file net/pfil.c net file net/ppp-deflate.c ppp & ppp_deflate file net/ppp_tty.c ppp diff -r a5e937c1fc66 sys/net/if_llatbl.c --- a/sys/net/if_llatbl.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/net/if_llatbl.c Thu Aug 20 13:11:34 2020 +0100 @@ -59,11 +59,11 @@ #include #include #include +#include #include #include #include #include -#include static SLIST_HEAD(, lltable) lltables; krwlock_t lltable_rwlock; @@ -716,13 +716,19 @@ if ((rtm_flags & RTF_ANNOUNCE)) lle->la_flags |= LLE_PUB; lle->la_flags |= LLE_VALID; + switch (dst->sa_family) { +#ifdef INET + case AF_INET: + lle->ln_state = ND_LLINFO_REACHABLE; + break; +#endif #ifdef INET6 - /* - * ND6 - */ - if (dst->sa_family == AF_INET6) - lle->ln_state = ND6_LLINFO_REACHABLE; + case AF_INET6: + lle->ln_state = ND_LLINFO_REACHABLE; + break; #endif + } + /* * NB: arp and ndp always set (RTF_STATIC | RTF_HOST) */ diff -r a5e937c1fc66 sys/net/nd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/net/nd.c Thu Aug 20 13:11:34 2020 +0100 @@ -0,0 +1,420 @@ +/* $NetBSD: */ + +/* + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: $"); + +#include +#include +#include /* for softnet_lock */ + +#include +#include +#include + +#include +#include + +static struct nd_domain *nd_domains[AF_MAX]; + +static int nd_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */ + +static void nd_set_timertick(struct llentry *, time_t); +static struct nd_domain *nd_find_domain(int); + +static void +nd_timer(void *arg) +{ + struct llentry *ln = arg; + struct nd_domain *ndd; + struct ifnet *ifp = NULL; + struct psref psref; + struct mbuf *m = NULL; + bool send_ns = false, missed = false; + union nd_addr taddr, *daddrp = NULL; + + SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); + LLE_WLOCK(ln); + + if (!(ln->la_flags & LLE_LINKED)) + goto out; + if (ln->ln_ntick > 0) { + nd_set_timer(ln, ND_TIMER_TICK); + goto out; + } + + ndd = nd_find_domain(ln->lle_tbl->llt_af); + ifp = ln->lle_tbl->llt_ifp; + KASSERT(ifp != NULL); + if_acquire(ifp, &psref); + + memcpy(&taddr, &ln->r_l3addr, sizeof(taddr)); + + switch (ln->ln_state) { + case ND_LLINFO_WAITDELETE: + LLE_REMREF(ln); + ndd->ndd_free(ln, 0); + ln = NULL; + break; + + case ND_LLINFO_INCOMPLETE: + if (ln->ln_asked++ < ndd->ndd_mmaxtries) { + send_ns = true; + break; + } + + if (ln->ln_hold) { + struct mbuf *m0, *mnxt; + + /* + * Assuming every packet in ln_hold + * has the same IP header. + */ + m = ln->ln_hold; + for (m0 = m->m_nextpkt; m0 != NULL; m0 = mnxt) { + mnxt = m0->m_nextpkt; + m0->m_nextpkt = NULL; + m_freem(m0); + } + + m->m_nextpkt = NULL; + ln->ln_hold = NULL; + } + + missed = true; + ln->ln_state = ND_LLINFO_WAITDELETE; + if (ln->ln_asked == ndd->ndd_mmaxtries) + nd_set_timer(ln, ND_TIMER_RETRANS); + else + send_ns = true; + break; + + case ND_LLINFO_REACHABLE: + if (!ND_IS_LLINFO_PERMANENT(ln)) { + ln->ln_state = ND_LLINFO_STALE; + nd_set_timer(ln, ND_TIMER_GC); + } + break; + + case ND_LLINFO_PURGE: /* FALLTHROUGH */ + case ND_LLINFO_STALE: + if (!ND_IS_LLINFO_PERMANENT(ln)) { + LLE_REMREF(ln); + ndd->ndd_free(ln, 1); + ln = NULL; + } + break; + + case ND_LLINFO_DELAY: + if (ndd->ndd_enabled(ifp)) { + ln->ln_asked = 1; + ln->ln_state = ND_LLINFO_PROBE; + send_ns = true; + daddrp = &taddr; + } else { + ln->ln_state = ND_LLINFO_STALE; + nd_set_timer(ln, ND_TIMER_GC); + } + break; + + case ND_LLINFO_PROBE: + if (ln->ln_asked < ndd->ndd_umaxtries) { + ln->ln_asked++; + send_ns = true; + daddrp = &taddr; + } else { + LLE_REMREF(ln); + ndd->ndd_free(ln, 0); + ln = NULL; + } + break; + } + + if (send_ns) { + uint8_t lladdr[255], *lladdrp; + union nd_addr src, *psrc; + + nd_set_timer(ln, ND_TIMER_RETRANS); + if (ln->ln_state > ND_LLINFO_INCOMPLETE && + ln->la_flags & LLE_VALID) + { + KASSERT(sizeof(lladdr) >= ifp->if_addrlen); + memcpy(lladdr, &ln->ll_addr, ifp->if_addrlen); + lladdrp = lladdr; + } else + lladdrp = NULL; + psrc = ndd->ndd_holdsrc(ln, &src); + LLE_FREE_LOCKED(ln); + ln = NULL; + ndd->ndd_output(ifp, daddrp, &taddr, lladdrp, psrc); + } + +out: + if (ln != NULL) + LLE_FREE_LOCKED(ln); + SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); + + if (missed) + ndd->ndd_missed(ifp, &taddr, m); + if (ifp != NULL) + if_release(ifp, &psref); +} + +static void +nd_set_timertick(struct llentry *ln, time_t xtick) +{ + + CTASSERT(sizeof(time_t) > sizeof(int)); + KASSERT(xtick >= 0); + + /* + * We have to take care of a reference leak which occurs if + * callout_reset overwrites a pending callout schedule. Unfortunately + * we don't have a mean to know the overwrite, so we need to know it + * using callout_stop. We need to call callout_pending first to exclude + * the case that the callout has never been scheduled. + */ + if (callout_pending(&ln->la_timer)) { + bool expired; + + expired = callout_stop(&ln->la_timer); + if (!expired) + LLE_REMREF(ln); + } + + ln->ln_expire = time_uptime + xtick / hz; + LLE_ADDREF(ln); + if (xtick > INT_MAX) { + ln->ln_ntick = xtick - INT_MAX; + xtick = INT_MAX; + } else { + ln->ln_ntick = 0; + } + callout_reset(&ln->ln_timer_ch, xtick, nd_timer, ln); +} + +void +nd_set_timer(struct llentry *ln, int type) +{ + time_t xtick; + struct ifnet *ifp; + struct nd_domain *ndd; + + LLE_WLOCK_ASSERT(ln); + + ifp = ln->lle_tbl->llt_ifp; + ndd = nd_find_domain(ln->lle_tbl->llt_af); + + switch (type) { + case ND_TIMER_IMMEDIATE: + xtick = 0; + break; + case ND_TIMER_TICK: + xtick = ln->ln_ntick; + break; + case ND_TIMER_RETRANS: + xtick = ndd->ndd_retrans(ifp) * hz / 1000; + break; + case ND_TIMER_REACHABLE: + xtick = ndd->ndd_reachable(ifp) * hz; + break; + case ND_TIMER_EXPIRE: + if (ln->ln_expire > time_uptime) + xtick = (ln->ln_expire - time_uptime) * hz; + else + xtick = nd_gctimer * hz; + break; + case ND_TIMER_DELAY: + xtick = ndd->ndd_delay * hz; + break; + case ND_TIMER_GC: + xtick = nd_gctimer * hz; + break; + default: + panic("%s: invalid timer type\n", __func__); + } + + nd_set_timertick(ln, xtick); +} + +int +nd_resolve(struct llentry *ln, const struct rtentry *rt, struct mbuf *m, + uint8_t *lldst, size_t dstsize) +{ + struct ifnet *ifp; + struct nd_domain *ndd; + int error; + + LLE_WLOCK_ASSERT(ln); + + ifp = ln->lle_tbl->llt_ifp; + ndd = nd_find_domain(ln->lle_tbl->llt_af); + + /* We don't have to do link-layer address resolution on a p2p link. */ + if (ifp->if_flags & IFF_POINTOPOINT && + ln->ln_state < ND_LLINFO_REACHABLE) + { + ln->ln_state = ND_LLINFO_STALE; + nd_set_timer(ln, ND_TIMER_GC); + } + + /* + * The first time we send a packet to a neighbor whose entry is + * STALE, we have to change the state to DELAY and a sets a timer to + * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do + * neighbor unreachability detection on expiration. + * (RFC 2461 7.3.3) + */ + if (ln->ln_state == ND_LLINFO_STALE) { + ln->ln_asked = 0; + ln->ln_state = ND_LLINFO_DELAY; + nd_set_timer(ln, ND_TIMER_DELAY); + } + + /* + * If the neighbor cache entry has a state other than INCOMPLETE + * (i.e. its link-layer address is already resolved), just + * send the packet. + */ + if (ln->ln_state > ND_LLINFO_INCOMPLETE) { + KASSERT((ln->la_flags & LLE_VALID) != 0); + memcpy(lldst, &ln->ll_addr, MIN(dstsize, ifp->if_addrlen)); + LLE_WUNLOCK(ln); + return 0; + } + + /* + * There is a neighbor cache entry, but no ethernet address + * response yet. Append this latest packet to the end of the + * packet queue in the mbuf, unless the number of the packet + * does not exceed maxqueuelen. When it exceeds maxqueuelen, + * the oldest packet in the queue will be removed. + */ + if (ln->ln_state == ND_LLINFO_NOSTATE || + ln->ln_state == ND_LLINFO_WAITDELETE) + ln->ln_state = ND_LLINFO_INCOMPLETE; + + if (ln->ln_hold != NULL) { + struct mbuf *m_hold; + int i; + + i = 0; + for (m_hold = ln->ln_hold; m_hold; m_hold = m_hold->m_nextpkt) { + i++; + if (m_hold->m_nextpkt == NULL) { + m_hold->m_nextpkt = m; + break; + } + } + while (i >= ndd->ndd_maxqueuelen) { + m_hold = ln->ln_hold; + ln->ln_hold = ln->ln_hold->m_nextpkt; + m_freem(m_hold); + i--; + } + } else + ln->ln_hold = m; + + if (ln->ln_asked >= ndd->ndd_mmaxtries) + error = (rt != NULL && rt->rt_flags & RTF_GATEWAY) ? + EHOSTUNREACH : EHOSTDOWN; + else + error = EWOULDBLOCK; + + /* + * If there has been no NS for the neighbor after entering the + * INCOMPLETE state, send the first solicitation. + */ + if (!ND_IS_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) { + struct psref psref; + union nd_addr dst, src, *psrc; + + ln->ln_asked++; + nd_set_timer(ln, ND_TIMER_RETRANS); + memcpy(&dst, &ln->r_l3addr, sizeof(dst)); + psrc = ndd->ndd_holdsrc(ln, &src); + if_acquire(ifp, &psref); + LLE_WUNLOCK(ln); + + ndd->ndd_output(ifp, NULL, &dst, NULL, psrc); + if_release(ifp, &psref); + } else + LLE_WUNLOCK(ln); + + return error; +} + +void +nd_nud_hint(struct llentry *ln) +{ + struct nd_domain *ndd; + + if (ln == NULL) + return; + + LLE_WLOCK_ASSERT(ln); + + if (ln->ln_state < ND_LLINFO_REACHABLE) + goto done; + + ndd = nd_find_domain(ln->lle_tbl->llt_af); + + /* + * if we get upper-layer reachability confirmation many times, + * it is possible we have false information. + */ + ln->ln_byhint++; + if (ln->ln_byhint > ndd->ndd_maxnudhint) + goto done; + + ln->ln_state = ND_LLINFO_REACHABLE; + if (!ND_IS_LLINFO_PERMANENT(ln)) + nd_set_timer(ln, ND_TIMER_REACHABLE); + +done: + LLE_WUNLOCK(ln); + + return; +} + +static struct nd_domain * +nd_find_domain(int af) +{ + + KASSERT(af < __arraycount(nd_domains) && nd_domains[af] != NULL); + return nd_domains[af]; +} + +void +nd_attach_domain(struct nd_domain *ndd) +{ + + KASSERT(ndd->ndd_family < __arraycount(nd_domains)); + nd_domains[ndd->ndd_family] = ndd; +} diff -r a5e937c1fc66 sys/net/nd.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sys/net/nd.h Thu Aug 20 13:11:34 2020 +0100 @@ -0,0 +1,98 @@ +/* $NetBSD: $ */ + +/* + * Copyright (c) 2020 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NET_ND_H_ +#define _NET_ND_H_ + +/* ND LLINFO states */ +#define ND_LLINFO_PURGE -3 +#define ND_LLINFO_NOSTATE -2 +#define ND_LLINFO_WAITDELETE -1 +#define ND_LLINFO_INCOMPLETE 0 +#define ND_LLINFO_REACHABLE 1 +#define ND_LLINFO_STALE 2 +#define ND_LLINFO_DELAY 3 +#define ND_LLINFO_PROBE 4 + +#ifdef _KERNEL +#define ND_IS_LLINFO_PROBREACH(ln) \ + ((ln)->ln_state > ND_LLINFO_INCOMPLETE) +#define ND_IS_LLINFO_PERMANENT(ln) \ + (((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 + +/* node constants */ +#define MAX_REACHABLE_TIME 3600000 /* msec */ +#define REACHABLE_TIME 30000 /* msec */ +#define RETRANS_TIMER 1000 /* msec */ +#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ +#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ +#define ND_COMPUTE_RTIME(x) \ + (((MIN_RANDOM_FACTOR * (x >> 10)) + (cprng_fast32() & \ + ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) + +#include +union nd_addr { + struct in_addr nda_addr4; + struct in6_addr nda_addr6; +}; + +struct nd_domain { + int ndd_family; + int ndd_delay; /* delay first probe time in seconds */ + int ndd_mmaxtries; /* maximum multicast query */ + int ndd_umaxtries; /* maximum unicast query */ + int ndd_maxnudhint; /* max # of subsequent upper layer hints */ + int ndd_maxqueuelen; /* max # of packets in unresolved ND entries */ + bool (*ndd_enabled)(struct ifnet *); + uint32_t (*ndd_reachable)(struct ifnet *); /* seconds */ + uint32_t (*ndd_retrans)(struct ifnet *); /* milliseconds */ + union nd_addr *(*ndd_holdsrc)(struct llentry *, union nd_addr *); + void (*ndd_output)(struct ifnet *, const union nd_addr *, + const union nd_addr *, const uint8_t *, const union nd_addr *); + void (*ndd_missed)(struct ifnet *, const union nd_addr *, struct mbuf *); + void (*ndd_free)(struct llentry *, int); +}; + +int nd_resolve(struct llentry *, const struct rtentry *, struct mbuf *, + uint8_t *, size_t); +void nd_set_timer(struct llentry *, int); +void nd_nud_hint(struct llentry *); + +void nd_attach_domain(struct nd_domain *); +#endif /* !_KERNEL */ +#endif /* !_NET_ND_H_ */ diff -r a5e937c1fc66 sys/netinet/if_arp.c --- a/sys/netinet/if_arp.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet/if_arp.c Thu Aug 20 13:11:34 2020 +0100 @@ -108,6 +108,7 @@ #include #include #include +#include #include #include @@ -132,12 +133,35 @@ */ #define ETHERTYPE_IPTRAILERS ETHERTYPE_TRAIL -/* timer values */ -static int arpt_keep = (20*60); /* once resolved, good for 20 more minutes */ -static int arpt_down = 20; /* once declared down, don't send for 20 secs */ -static int arp_maxhold = 1; /* number of packets to hold per ARP entry */ -#define rt_expire rt_rmx.rmx_expire -#define rt_pksent rt_rmx.rmx_pksent +/* timers */ +static uint32_t arp_reachable; /* setup in arp_init() */ +static uint32_t arp_retrans = RETRANS_TIMER; + +static bool arp_llinfo_enabled(struct ifnet *); +static uint32_t arp_llinfo_reachable(struct ifnet *); +static uint32_t arp_llinfo_retrans(struct ifnet *); +static union nd_addr *arp_llinfo_holdsrc(struct llentry *, union nd_addr *); +static void arp_llinfo_output(struct ifnet *, const union nd_addr *, + const union nd_addr *, const uint8_t *, const union nd_addr *); +static void arp_llinfo_missed(struct ifnet *, const union nd_addr *, + struct mbuf *); +static void arp_free(struct llentry *, int); + +static struct nd_domain arp_nddomain = { + .ndd_family = AF_INET, + .ndd_delay = 5, /* delay first probe time 5 second */ + .ndd_mmaxtries = 3, /* maximum broadcast query */ + .ndd_umaxtries = 3, /* maximum unicast query */ + .ndd_maxnudhint = 0, /* max # of subsequent upper layer hints */ + .ndd_maxqueuelen = 1, /* max # of packets in unresolved ND entries */ + .ndd_enabled = arp_llinfo_enabled, + .ndd_reachable = arp_llinfo_reachable, + .ndd_retrans = arp_llinfo_retrans, + .ndd_holdsrc = arp_llinfo_holdsrc, + .ndd_output = arp_llinfo_output, + .ndd_missed = arp_llinfo_missed, + .ndd_free = arp_free, +}; int ip_dad_count = PROBE_NUM; #ifdef ARP_DEBUG @@ -151,14 +175,10 @@ static void arprequest(struct ifnet *, const struct in_addr *, const struct in_addr *, - const uint8_t *); + const uint8_t *, const uint8_t *); static void arpannounce1(struct ifaddr *); static struct sockaddr *arp_setgate(struct rtentry *, struct sockaddr *, const struct sockaddr *); -static void arptimer(void *); -static void arp_settimer(struct llentry *, int); -static struct llentry *arplookup(struct ifnet *, - const struct in_addr *, const struct sockaddr *, int); static struct llentry *arpcreate(struct ifnet *, const struct in_addr *, const struct sockaddr *, int); static void in_arpinput(struct mbuf *); @@ -173,8 +193,6 @@ static void arp_dad_stop(struct ifaddr *); static void arp_dad_duplicated(struct ifaddr *, const struct sockaddr_dl *); -static void arp_init_llentry(struct ifnet *, struct llentry *); - struct ifqueue arpintrq = { .ifq_head = NULL, .ifq_tail = NULL, @@ -182,7 +200,6 @@ .ifq_maxlen = 50, .ifq_drops = 0, }; -static int arp_maxtries = 5; static int useloopback = 1; /* use loopback interface for local traffic */ static percpu_t *arpstat_percpu; @@ -257,6 +274,8 @@ MOWNER_ATTACH(&arpdomain.dom_mowner); #endif + arp_reachable = ND_COMPUTE_RTIME(REACHABLE_TIME); + nd_attach_domain(&arp_nddomain); arp_dad_init(); } @@ -278,98 +297,6 @@ lltable_drain(AF_INET); } -static void -arptimer(void *arg) -{ - struct llentry *lle = arg; - struct ifnet *ifp; - - KASSERT((lle->la_flags & LLE_STATIC) == 0); - - LLE_WLOCK(lle); - - /* - * This shortcut is required to avoid trying to touch ifp that may be - * being destroyed. - */ - if ((lle->la_flags & LLE_LINKED) == 0) { - LLE_FREE_LOCKED(lle); - return; - } - - ifp = lle->lle_tbl->llt_ifp; - - /* XXX: LOR avoidance. We still have ref on lle. */ - LLE_WUNLOCK(lle); - - IF_AFDATA_LOCK(ifp); - LLE_WLOCK(lle); - - /* Guard against race with other llentry_free(). */ - if (lle->la_flags & LLE_LINKED) { - int rt_cmd; - struct in_addr *in; - struct sockaddr_in dsin, ssin; - struct sockaddr *sa; - const char *lladdr; - size_t pkts_dropped; - - in = &lle->r_l3addr.addr4; - sockaddr_in_init(&dsin, in, 0); - if (lle->la_flags & LLE_VALID) { - rt_cmd = RTM_DELETE; - sa = NULL; - lladdr = (const char *)&lle->ll_addr; - } else { - if (lle->la_hold != NULL) { - struct mbuf *m = lle->la_hold; - const struct ip *ip = mtod(m, const struct ip *); - - sockaddr_in_init(&ssin, &ip->ip_src, 0); - sa = sintosa(&ssin); - } else - sa = NULL; - rt_cmd = RTM_MISS; - lladdr = NULL; - - } - rt_clonedmsg(rt_cmd, sa, sintosa(&dsin), lladdr, ifp); - - LLE_REMREF(lle); - pkts_dropped = llentry_free(lle); - ARP_STATADD(ARP_STAT_DFRDROPPED, pkts_dropped); - ARP_STATADD(ARP_STAT_DFRTOTAL, pkts_dropped); - } else { - LLE_FREE_LOCKED(lle); - } - - IF_AFDATA_UNLOCK(ifp); -} - -static void -arp_settimer(struct llentry *la, int sec) -{ - - LLE_WLOCK_ASSERT(la); - KASSERT((la->la_flags & LLE_STATIC) == 0); - - /* - * We have to take care of a reference leak which occurs if - * callout_reset overwrites a pending callout schedule. Unfortunately - * we don't have a mean to know the overwrite, so we need to know it - * using callout_stop. We need to call callout_pending first to exclude - * the case that the callout has never been scheduled. - */ - if (callout_pending(&la->la_timer)) { - bool expired = callout_stop(&la->la_timer); - if (!expired) - /* A pending callout schedule is canceled. */ - LLE_REMREF(la); - } - LLE_ADDREF(la); - callout_reset(&la->la_timer, hz * sec, arptimer, la); -} - /* * We set the gateway for RTF_CLONING routes to a "prototype" * link-layer sockaddr whose interface type (if_type) and interface @@ -409,17 +336,6 @@ return gate; } -static void -arp_init_llentry(struct ifnet *ifp, struct llentry *lle) -{ - - switch (ifp->if_type) { - default: - /* Nothing. */ - break; - } -} - /* * Parallel to llc_rtrequest. */ @@ -478,13 +394,6 @@ if ((rt->rt_flags & RTF_CONNECTED) || (rt->rt_flags & RTF_LOCAL)) { /* - * Give this route an expiration time, even though - * it's a "permanent" route, so that routes cloned - * from it do not need their expiration time set. - */ - KASSERT(time_uptime != 0); - rt->rt_expire = time_uptime; - /* * linklayers with particular link MTU limitation. */ switch (ifp->if_type) { @@ -552,7 +461,6 @@ * interface. So check RTF_LOCAL instead. */ if (rt->rt_flags & RTF_LOCAL) { - rt->rt_expire = 0; if (useloopback) { rt->rt_ifp = lo0ifp; rt->rt_rmx.rmx_mtu = 0; @@ -567,7 +475,6 @@ goto out; } - rt->rt_expire = 0; if (useloopback) { rt->rt_ifp = lo0ifp; rt->rt_rmx.rmx_mtu = 0; @@ -603,7 +510,7 @@ static void arprequest(struct ifnet *ifp, const struct in_addr *sip, const struct in_addr *tip, - const uint8_t *enaddr) + const uint8_t *saddr, const uint8_t *taddr) { struct mbuf *m; struct arphdr *ah; @@ -612,7 +519,7 @@ KASSERT(sip != NULL); KASSERT(tip != NULL); - KASSERT(enaddr != NULL); + KASSERT(saddr != NULL); if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) return; @@ -644,12 +551,15 @@ ah->ar_hln = ifp->if_addrlen; /* hardware address length */ ah->ar_pln = sizeof(struct in_addr); /* protocol address length */ ah->ar_op = htons(ARPOP_REQUEST); - memcpy(ar_sha(ah), enaddr, ah->ar_hln); + memcpy(ar_sha(ah), saddr, ah->ar_hln); + if (taddr == NULL) + m->m_flags |= M_BCAST; + else + memcpy(ar_tha(ah), taddr, ah->ar_hln); memcpy(ar_spa(ah), sip, ah->ar_pln); memcpy(ar_tpa(ah), tip, ah->ar_pln); sa.sa_family = AF_ARP; sa.sa_len = 2; - m->m_flags |= M_BCAST; arps = ARP_STAT_GETREF(); arps[ARP_STAT_SNDTOTAL]++; arps[ARP_STAT_SENDREQUEST]++; @@ -667,7 +577,7 @@ ARPLOG(LOG_DEBUG, "%s not ready\n", ARPLOGADDR(ip)); return; } - arprequest(ifp, ip, ip, enaddr); + arprequest(ifp, ip, ip, enaddr, NULL); } static void @@ -694,9 +604,7 @@ { struct llentry *la; const char *create_lookup; - bool renew; int error; - struct ifnet *origifp = ifp; #if NCARP > 0 if (rt != NULL && rt->rt_ifp->if_type == IFT_CARP) @@ -709,8 +617,7 @@ if (la == NULL) goto notfound; - if ((la->la_flags & LLE_VALID) && - ((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) { + if (la->la_flags & LLE_VALID && la->ln_state == ND_LLINFO_REACHABLE) { KASSERT(destlen >= ifp->if_addrlen); memcpy(desten, &la->ll_addr, ifp->if_addrlen); LLE_RUNLOCK(la); @@ -738,7 +645,7 @@ if (la == NULL) ARP_STATINC(ARP_STAT_ALLOCFAIL); else - arp_init_llentry(ifp, la); + la->ln_state = ND_LLINFO_NOSTATE; } else if (LLE_TRY_UPGRADE(la) == 0) { create_lookup = "lookup"; LLE_RUNLOCK(la); @@ -756,126 +663,7 @@ goto bad; } - if ((la->la_flags & LLE_VALID) && - ((la->la_flags & LLE_STATIC) || la->la_expire > time_uptime)) - { - KASSERT(destlen >= ifp->if_addrlen); - memcpy(desten, &la->ll_addr, ifp->if_addrlen); - renew = false; - /* - * If entry has an expiry time and it is approaching, - * see if we need to send an ARP request within this - * arpt_down interval. - */ - if (!(la->la_flags & LLE_STATIC) && - time_uptime + la->la_preempt > la->la_expire) - { - renew = true; - la->la_preempt--; - } - - LLE_WUNLOCK(la); - - if (renew) { - const uint8_t *enaddr = CLLADDR(ifp->if_sadl); - arprequest(origifp, - &satocsin(rt->rt_ifa->ifa_addr)->sin_addr, - &satocsin(dst)->sin_addr, enaddr); - } - - return 0; - } - - if (la->la_flags & LLE_STATIC) { /* should not happen! */ - LLE_RUNLOCK(la); - log(LOG_DEBUG, "%s: ouch, empty static llinfo for %s\n", - __func__, inet_ntoa(satocsin(dst)->sin_addr)); - error = EINVAL; - goto bad; - } - - renew = (la->la_asked == 0 || la->la_expire != time_uptime); - - /* - * There is an arptab entry, but no ethernet address - * response yet. Add the mbuf to the list, dropping - * the oldest packet if we have exceeded the system - * setting. - */ - LLE_WLOCK_ASSERT(la); - if (la->la_numheld >= arp_maxhold) { - if (la->la_hold != NULL) { - struct mbuf *next = la->la_hold->m_nextpkt; - m_freem(la->la_hold); - la->la_hold = next; - la->la_numheld--; - ARP_STATINC(ARP_STAT_DFRDROPPED); - ARP_STATINC(ARP_STAT_DFRTOTAL); - } - } - if (la->la_hold != NULL) { - struct mbuf *curr = la->la_hold; - while (curr->m_nextpkt != NULL) - curr = curr->m_nextpkt; - curr->m_nextpkt = m; - } else - la->la_hold = m; - la->la_numheld++; - if (!renew) - LLE_DOWNGRADE(la); - - /* - * Return EWOULDBLOCK if we have tried less than arp_maxtries. It - * will be masked by ether_output(). Return EHOSTDOWN/EHOSTUNREACH - * if we have already sent arp_maxtries ARP requests. Retransmit the - * ARP request, but not faster than one request per second. - */ - if (la->la_asked < arp_maxtries) - error = EWOULDBLOCK; /* First request. */ - else - error = (rt != NULL && rt->rt_flags & RTF_GATEWAY) ? - EHOSTUNREACH : EHOSTDOWN; - - if (renew) { - const uint8_t *enaddr = CLLADDR(ifp->if_sadl); - struct sockaddr_in sin; - - la->la_expire = time_uptime; - arp_settimer(la, arpt_down); - la->la_asked++; - - sockaddr_in_init(&sin, &la->r_l3addr.addr4, 0); - if (error != EWOULDBLOCK) { - const struct ip *ip = mtod(m, const struct ip *); - struct sockaddr_in ssin; - - sockaddr_in_init(&ssin, &ip->ip_src, 0); - rt_clonedmsg(RTM_MISS, sintosa(&ssin), sintosa(&sin), - NULL, ifp); - } - - LLE_WUNLOCK(la); - - if (rt != NULL) { - arprequest(origifp, - &satocsin(rt->rt_ifa->ifa_addr)->sin_addr, - &satocsin(dst)->sin_addr, enaddr); - } else { - struct rtentry *_rt; - - /* XXX */ - _rt = rtalloc1((struct sockaddr *)&sin, 0); - if (_rt == NULL) - goto bad; - arprequest(origifp, - &satocsin(_rt->rt_ifa->ifa_addr)->sin_addr, - &satocsin(dst)->sin_addr, enaddr); - rt_unref(_rt); - } - return error; - } - - LLE_RUNLOCK(la); + error = nd_resolve(la, rt, m, desten, destlen); return error; bad: @@ -1003,7 +791,7 @@ #endif struct sockaddr sa; struct in_addr isaddr, itaddr, myaddr; - int op, rt_cmd; + int op, rt_cmd, new_state = 0; void *tha; uint64_t *arps; struct psref psref, psref_ia; @@ -1234,20 +1022,35 @@ IN_PRINT(ipbuf, &isaddr), llastr); } rt_cmd = RTM_CHANGE; - } else + new_state = ND_LLINFO_STALE; + } else { + if (op == ARPOP_REPLY && in_hosteq(itaddr, myaddr)) { + /* This was a solicited ARP reply. */ + la->ln_byhint = 0; + new_state = ND_LLINFO_REACHABLE; + } rt_cmd = la->la_flags & LLE_VALID ? 0 : RTM_ADD; + } KASSERT(ifp->if_sadl->sdl_alen == ifp->if_addrlen); KASSERT(sizeof(la->ll_addr) >= ifp->if_addrlen); memcpy(&la->ll_addr, ar_sha(ah), ifp->if_addrlen); la->la_flags |= LLE_VALID; - if ((la->la_flags & LLE_STATIC) == 0) { - la->la_expire = time_uptime + arpt_keep; - arp_settimer(la, arpt_keep); + la->ln_asked = 0; + if (new_state != 0) { + la->ln_state = new_state; + + if (new_state != ND_LLINFO_REACHABLE || + !(la->la_flags & LLE_STATIC)) + { + int timer = ND_TIMER_GC; + + if (new_state == ND_LLINFO_REACHABLE) + timer = ND_TIMER_REACHABLE; + nd_set_timer(la, timer); + } } - la->la_asked = 0; - /* rt->rt_flags &= ~RTF_REJECT; */ if (rt_cmd != 0) { struct sockaddr_in sin; @@ -1390,7 +1193,7 @@ /* * Lookup or a new address in arptab. */ -static struct llentry * +struct llentry * arplookup(struct ifnet *ifp, const struct in_addr *addr, const struct sockaddr *sa, int wlock) { @@ -1438,7 +1241,7 @@ rt_unref(rt); if (la != NULL) - arp_init_llentry(ifp, la); + la->ln_state = ND_LLINFO_NOSTATE; } return la; @@ -1476,6 +1279,178 @@ } } +static bool +arp_llinfo_enabled(struct ifnet *ifp) +{ + + return (ifp->if_flags & IFF_NOARP) == 0; +} + +static uint32_t +arp_llinfo_reachable(__unused struct ifnet *ifp) +{ + + return arp_reachable; +} + +static uint32_t +arp_llinfo_retrans(__unused struct ifnet *ifp) +{ + + return arp_retrans; +} + +/* + * Gets source address of the first packet in hold queue + * and stores it in @src. + * Returns pointer to @src (if hold queue is not empty) or NULL. + */ +static union nd_addr * +arp_llinfo_holdsrc(struct llentry *ln, union nd_addr *src) +{ + struct ip *ip; + + if (ln == NULL || ln->ln_hold == NULL) + return NULL; + + /* + * assuming every packet in ln_hold has the same IP header + */ + ip = mtod(ln->ln_hold, struct ip *); + /* XXX pullup? */ + if (sizeof(*ip) < ln->ln_hold->m_len) + src->nda_addr4 = ip->ip_src; + else + src = NULL; + + return src; +} + +static void +arp_llinfo_output(struct ifnet *ifp, __unused const union nd_addr *daddr, + const union nd_addr *taddr, const uint8_t *tlladdr, + const union nd_addr *hsrc) +{ + struct in_addr tip = taddr->nda_addr4, sip = zeroin_addr; + const uint8_t *slladdr = CLLADDR(ifp->if_sadl); + + if (hsrc != NULL) { + struct in_ifaddr *ia; + struct psref psref; + + ia = in_get_ia_on_iface_psref(hsrc->nda_addr4, ifp, &psref); + if (ia != NULL) { + sip = hsrc->nda_addr4; + ia4_release(ia, &psref); + } + } + + if (sip.s_addr == INADDR_ANY) { + struct sockaddr_in dst; + struct rtentry *rt; + + sockaddr_in_init(&dst, &tip, 0); + rt = rtalloc1(sintosa(&dst), 0); + if (rt != NULL) { + if (rt->rt_ifp == ifp && + rt->rt_ifa != NULL && + rt->rt_ifa->ifa_addr->sa_family == AF_INET) + sip = satosin(rt->rt_ifa->ifa_addr)->sin_addr; + rt_unref(rt); + } + if (sip.s_addr == INADDR_ANY) { + char ipbuf[INET_ADDRSTRLEN]; + + log(LOG_DEBUG, "source can't be " + "determined: dst=%s\n", + IN_PRINT(ipbuf, &tip)); + return; + } + } + + arprequest(ifp, &sip, &tip, slladdr, tlladdr); +} + + +static void +arp_llinfo_missed(struct ifnet *ifp, const union nd_addr *taddr, struct mbuf *m) +{ + struct in_addr mdaddr = zeroin_addr; + struct sockaddr_in dsin, tsin; + struct sockaddr *sa; + + if (m != NULL) { + struct ip *ip = mtod(m, struct ip *); + + if (sizeof(*ip) < m->m_len) + mdaddr = ip->ip_src; + + /* ip_input() will send ICMP_UNREACH_HOST, not us. */ + m_free(m); + } + + if (mdaddr.s_addr != INADDR_ANY) { + sockaddr_in_init(&dsin, &mdaddr, 0); + sa = sintosa(&dsin); + } else + sa = NULL; + + sockaddr_in_init(&tsin, &taddr->nda_addr4, 0); + rt_clonedmsg(RTM_MISS, sa, sintosa(&tsin), NULL, ifp); +} + +static void +arp_free(struct llentry *ln, int gc) +{ + struct ifnet *ifp; + + KASSERT(ln != NULL); + LLE_WLOCK_ASSERT(ln); + + ifp = ln->lle_tbl->llt_ifp; + + if (ln->la_flags & LLE_VALID || gc) { + struct sockaddr_in sin; + const char *lladdr; + + sockaddr_in_init(&sin, &ln->r_l3addr.addr4, 0); + lladdr = ln->la_flags & LLE_VALID ? + (const char *)&ln->ll_addr : NULL; + rt_clonedmsg(RTM_DELETE, NULL, sintosa(&sin), lladdr, ifp); + } + + /* + * Save to unlock. We still hold an extra reference and will not + * free(9) in llentry_free() if someone else holds one as well. + */ + LLE_WUNLOCK(ln); + IF_AFDATA_LOCK(ifp); + LLE_WLOCK(ln); + + lltable_free_entry(LLTABLE(ifp), ln); + + IF_AFDATA_UNLOCK(ifp); +} + +/* + * Upper-layer reachability hint for Neighbor Unreachability Detection. + * + * XXX cost-effective methods? + */ +void +arp_nud_hint(struct rtentry *rt) +{ + struct llentry *ln; + struct ifnet *ifp; + + if (rt == NULL) + return; + + ifp = rt->rt_ifp; + ln = arplookup(ifp, NULL, rt_getkey(rt), 1); + nd_nud_hint(ln); +} + TAILQ_HEAD(dadq_head, dadq); struct dadq { TAILQ_ENTRY(dadq) dad_list; @@ -1561,7 +1536,7 @@ memset(&sip, 0, sizeof(sip)); arprequest(ifa->ifa_ifp, &sip, &ia->ia_addr.sin_addr, - CLLADDR(ifa->ifa_ifp->if_sadl)); + CLLADDR(ifa->ifa_ifp->if_sadl), NULL); } /* @@ -2026,18 +2001,35 @@ CTL_NET, PF_INET, CTL_CREATE, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, - CTLFLAG_PERMANENT|CTLFLAG_READWRITE, - CTLTYPE_INT, "keep", - SYSCTL_DESCR("Valid ARP entry lifetime in seconds"), - NULL, 0, &arpt_keep, 0, - CTL_NET,PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); - + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "nd_delay", + SYSCTL_DESCR("First probe delay time"), + NULL, 0, &arp_nddomain.ndd_delay, 0, + CTL_NET, PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "nd_bmaxtries", + SYSCTL_DESCR("Number of broadcast discovery attempts"), + NULL, 0, &arp_nddomain.ndd_mmaxtries, 0, + CTL_NET, PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, - CTLFLAG_PERMANENT|CTLFLAG_READWRITE, - CTLTYPE_INT, "down", - SYSCTL_DESCR("Failed ARP entry lifetime in seconds"), - NULL, 0, &arpt_down, 0, - CTL_NET,PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "nd_umaxtries", + SYSCTL_DESCR("Number of unicast discovery attempts"), + NULL, 0, &arp_nddomain.ndd_umaxtries, 0, + CTL_NET, PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "nd_maxnudhint", + SYSCTL_DESCR("Maximum neighbor unreachable hint count"), + NULL, 0, &arp_nddomain.ndd_maxnudhint, 0, + CTL_NET, PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "maxqueuelen", + SYSCTL_DESCR("max packet queue len for a unresolved ARP"), + NULL, 1, &arp_nddomain.ndd_maxqueuelen, 0, + CTL_NET, PF_INET, node->sysctl_num, CTL_CREATE, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT, diff -r a5e937c1fc66 sys/netinet/if_inarp.h --- a/sys/netinet/if_inarp.h Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet/if_inarp.h Thu Aug 20 13:11:34 2020 +0100 @@ -76,9 +76,12 @@ const struct sockaddr *, void *, size_t); void arpintr(void); void arpannounce(struct ifnet *, struct ifaddr *, const uint8_t *); +struct llentry *arplookup(struct ifnet *, + const struct in_addr *, const struct sockaddr *, int); void arp_drain(void); int arpioctl(u_long, void *); void arpwhohas(struct ifnet *, struct in_addr *); +void arp_nud_hint(struct rtentry *); void revarpinput(struct mbuf *); int revarpwhoarewe(struct ifnet *, struct in_addr *, struct in_addr *); diff -r a5e937c1fc66 sys/netinet/in.c --- a/sys/netinet/in.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet/in.c Thu Aug 20 13:11:34 2020 +0100 @@ -425,6 +425,25 @@ if (ifp == NULL) return EINVAL; return ifaddrpref_ioctl(so, cmd, data, ifp); +#if NARP > 0 + case SIOCGNBRINFO: + { + struct in_nbrinfo *nbi = (struct in_nbrinfo *)data; + struct llentry *ln; + struct in_addr nb_addr = nbi->addr; /* make local for safety */ + + ln = arplookup(ifp, &nb_addr, NULL, 0); + if (ln == NULL) + return EINVAL; + nbi->state = ln->ln_state; + nbi->asked = ln->ln_asked; + nbi->expire = ln->ln_expire ? + time_mono_to_wall(ln->ln_expire) : 0; + LLE_RUNLOCK(ln); + return 0; + } +#endif + } bound = curlwp_bind(); @@ -1960,11 +1979,6 @@ if (lle == NULL) /* NB: caller generates msg */ return NULL; - /* - * For IPv4 this will trigger "arpresolve" to generate - * an ARP request. - */ - lle->la_expire = time_uptime; /* mark expired */ lle->r_l3addr.addr4 = addr4; lle->lle_refcnt = 1; lle->lle_free = in_lltable_destroy_lle; diff -r a5e937c1fc66 sys/netinet/in_var.h --- a/sys/netinet/in_var.h Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet/in_var.h Thu Aug 20 13:11:34 2020 +0100 @@ -115,6 +115,14 @@ #endif }; +struct in_nbrinfo { + char ifname[IFNAMSIZ]; /* if name, e.g. "en0" */ + struct in_addr addr; /* IPv4 address of the neighbor */ + long asked; /* number of queries already sent for this addr */ + int state; /* reachability state */ + int expire; /* lifetime for NDP state transition */ +}; + #ifdef _KERNEL static __inline void ia4_acquire(struct in_ifaddr *ia, struct psref *psref) diff -r a5e937c1fc66 sys/netinet/tcp_input.c --- a/sys/netinet/tcp_input.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet/tcp_input.c Thu Aug 20 13:11:34 2020 +0100 @@ -186,6 +186,9 @@ #include #include +#ifdef INET +#include +#endif #ifdef INET6 #include #include @@ -253,24 +256,52 @@ /* * Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. */ -#ifdef INET6 -static inline void -nd6_hint(struct tcpcb *tp) +static void +nd_hint(struct tcpcb *tp) { - struct rtentry *rt = NULL; - - if (tp != NULL && tp->t_in6pcb != NULL && tp->t_family == AF_INET6 && - (rt = rtcache_validate(&tp->t_in6pcb->in6p_route)) != NULL) { + struct route *ro = NULL; + struct rtentry *rt; + + if (tp == NULL) + return; + + switch (tp->t_family) { +#ifdef INET + case AF_INET: + if (tp->t_inpcb != NULL) + ro = &tp->t_inpcb->inp_route; + break; +#endif +#ifdef INET6 + case AF_INET6: + if (tp->t_in6pcb != NULL) + ro = &tp->t_in6pcb->in6p_route; + break; +#endif + } + + if (ro == NULL) + return; + + rt = rtcache_validate(ro); + if (rt == NULL) + return; + + switch (tp->t_family) { +#ifdef INET + case AF_INET: + arp_nud_hint(rt); + break; +#endif +#ifdef INET6 + case AF_INET6: nd6_nud_hint(rt); - rtcache_unref(rt, &tp->t_in6pcb->in6p_route); + break; +#endif } + + rtcache_unref(rt, ro); } -#else -static inline void -nd6_hint(struct tcpcb *tp) -{ -} -#endif /* * Compute ACK transmission behavior. Delay the ACK unless @@ -769,7 +800,7 @@ tp->rcv_nxt += q->ipqe_len; pkt_flags = q->ipqe_flags & TH_FIN; - nd6_hint(tp); + nd_hint(tp); TAILQ_REMOVE(&tp->segq, q, ipqe_q); TAILQ_REMOVE(&tp->timeq, q, ipqe_timeq); @@ -1884,7 +1915,7 @@ tcps[TCP_STAT_RCVACKPACK]++; tcps[TCP_STAT_RCVACKBYTE] += acked; TCP_STAT_PUTREF(); - nd6_hint(tp); + nd_hint(tp); if (acked > (tp->t_lastoff - tp->t_inoff)) tp->t_lastm = NULL; @@ -1941,7 +1972,7 @@ tcps[TCP_STAT_RCVPACK]++; tcps[TCP_STAT_RCVBYTE] += tlen; TCP_STAT_PUTREF(); - nd6_hint(tp); + nd_hint(tp); /* * Automatic sizing enables the performance of large buffers @@ -2570,7 +2601,7 @@ */ tp->t_congctl->newack(tp, th); - nd6_hint(tp); + nd_hint(tp); if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; sbdrop(&so->so_snd, (int)so->so_snd.sb_cc); @@ -2776,7 +2807,7 @@ tcps[TCP_STAT_RCVPACK]++; tcps[TCP_STAT_RCVBYTE] += tlen; TCP_STAT_PUTREF(); - nd6_hint(tp); + nd_hint(tp); if (so->so_state & SS_CANTRCVMORE) { m_freem(m); } else { diff -r a5e937c1fc66 sys/netinet6/icmp6.c --- a/sys/netinet6/icmp6.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet6/icmp6.c Thu Aug 20 13:11:34 2020 +0100 @@ -87,6 +87,7 @@ #include #include #include +#include #include #include @@ -98,9 +99,9 @@ #include #include #include -#include #include #include +#include #include #ifdef IPSEC @@ -2953,7 +2954,6 @@ static void sysctl_net_inet6_icmp6_setup(struct sysctllog **clog) { - extern int nd6_maxqueuelen; /* defined in nd6.c */ sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT, @@ -3008,23 +3008,37 @@ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "nd6_delay", SYSCTL_DESCR("First probe delay time"), - NULL, 0, &nd6_delay, 0, + NULL, 0, &nd6_nddomain.ndd_delay, 0, CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DELAY, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "nd6_mmaxtries", + SYSCTL_DESCR("Number of multicast discovery attempts"), + NULL, 0, &nd6_nddomain.ndd_mmaxtries, 0, + CTL_NET, PF_INET6, IPPROTO_ICMPV6, + ICMPV6CTL_ND6_MMAXTRIES, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "nd6_umaxtries", SYSCTL_DESCR("Number of unicast discovery attempts"), - NULL, 0, &nd6_umaxtries, 0, + NULL, 0, &nd6_nddomain.ndd_umaxtries, 0, CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_UMAXTRIES, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT|CTLFLAG_READWRITE, - CTLTYPE_INT, "nd6_mmaxtries", - SYSCTL_DESCR("Number of multicast discovery attempts"), - NULL, 0, &nd6_mmaxtries, 0, + CTLTYPE_INT, "nd6_maxnudhint", + SYSCTL_DESCR("Maximum neighbor unreachable hint count"), + NULL, 0, &nd6_nddomain.ndd_maxnudhint, 0, CTL_NET, PF_INET6, IPPROTO_ICMPV6, - ICMPV6CTL_ND6_MMAXTRIES, CTL_EOL); + ICMPV6CTL_ND6_MAXNUDHINT, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "maxqueuelen", + SYSCTL_DESCR("max packet queue len for a unresolved ND"), + NULL, 1, &nd6_nddomain.ndd_maxqueuelen, 0, + CTL_NET, PF_INET6, IPPROTO_ICMPV6, + ICMPV6CTL_ND6_MAXQLEN, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "nd6_useloopback", @@ -3056,13 +3070,6 @@ ICMPV6CTL_ERRPPSLIMIT, CTL_EOL); sysctl_createv(clog, 0, NULL, NULL, CTLFLAG_PERMANENT|CTLFLAG_READWRITE, - CTLTYPE_INT, "nd6_maxnudhint", - SYSCTL_DESCR("Maximum neighbor unreachable hint count"), - NULL, 0, &nd6_maxnudhint, 0, - CTL_NET, PF_INET6, IPPROTO_ICMPV6, - ICMPV6CTL_ND6_MAXNUDHINT, CTL_EOL); - sysctl_createv(clog, 0, NULL, NULL, - CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "mtudisc_hiwat", SYSCTL_DESCR("Low mark on MTU Discovery route timers"), NULL, 0, &icmp6_mtudisc_hiwat, 0, @@ -3098,13 +3105,6 @@ CTL_NET, PF_INET6, IPPROTO_ICMPV6, OICMPV6CTL_ND6_PRLIST, CTL_EOL); #endif - sysctl_createv(clog, 0, NULL, NULL, - CTLFLAG_PERMANENT|CTLFLAG_READWRITE, - CTLTYPE_INT, "maxqueuelen", - SYSCTL_DESCR("max packet queue len for a unresolved ND"), - NULL, 1, &nd6_maxqueuelen, 0, - CTL_NET, PF_INET6, IPPROTO_ICMPV6, - ICMPV6CTL_ND6_MAXQLEN, CTL_EOL); } void diff -r a5e937c1fc66 sys/netinet6/nd6.c --- a/sys/netinet6/nd6.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet6/nd6.c Thu Aug 20 13:11:34 2020 +0100 @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -86,19 +87,11 @@ /* timer values */ int nd6_prune = 1; /* walk list every 1 seconds */ -int nd6_delay = 5; /* delay first probe time 5 second */ -int nd6_umaxtries = 3; /* maximum unicast query */ -int nd6_mmaxtries = 3; /* maximum multicast query */ int nd6_useloopback = 1; /* use loopback interface for local traffic */ -int nd6_gctimer = (60 * 60 * 24); /* 1 day: garbage collection timer */ /* preventing too many loops in ND option parsing */ int nd6_maxndopt = 10; /* max # of ND options allowed */ -int nd6_maxnudhint = 0; /* max # of subsequent upper layer hints */ - -int nd6_maxqueuelen = 1; /* max # of packets cached in unresolved ND entries */ - #ifdef ND6_DEBUG int nd6_debug = 1; #else @@ -111,10 +104,16 @@ static void nd6_slowtimo(void *); static void nd6_free(struct llentry *, int); -static void nd6_llinfo_timer(void *); +static bool nd6_llinfo_enabled(struct ifnet *); +static uint32_t nd6_llinfo_reachable(struct ifnet *); +static uint32_t nd6_llinfo_retrans(struct ifnet *); +static union nd_addr *nd6_llinfo_holdsrc(struct llentry *, union nd_addr *); +static void nd6_llinfo_output(struct ifnet *, const union nd_addr *, + const union nd_addr *, const uint8_t *, const union nd_addr *); +static void nd6_llinfo_missed(struct ifnet *, const union nd_addr *, + struct mbuf *); static void nd6_timer(void *); static void nd6_timer_work(struct work *, void *); -static void clear_llinfo_pqueue(struct llentry *); static struct nd_opt_hdr *nd6_option(union nd_opts *); static callout_t nd6_slowtimo_ch; @@ -122,6 +121,22 @@ static struct workqueue *nd6_timer_wq; static struct work nd6_timer_wk; +struct nd_domain nd6_nddomain = { + .ndd_family = AF_INET6, + .ndd_delay = 5, /* delay first probe time 5 second */ + .ndd_mmaxtries = 3, /* maximum unicast query */ + .ndd_umaxtries = 3, /* maximum multicast query */ + .ndd_maxnudhint = 0, /* max # of subsequent upper layer hints */ + .ndd_maxqueuelen = 1, /* max # of packets in unresolved ND entries */ + .ndd_enabled = nd6_llinfo_enabled, + .ndd_reachable = nd6_llinfo_reachable, + .ndd_retrans = nd6_llinfo_retrans, + .ndd_holdsrc = nd6_llinfo_holdsrc, + .ndd_output = nd6_llinfo_output, + .ndd_missed = nd6_llinfo_missed, + .ndd_free = nd6_free, +}; + MALLOC_DEFINE(M_IP6NDP, "NDP", "IPv6 Neighbour Discovery"); void @@ -129,6 +144,7 @@ { int error; + nd_attach_domain(&nd6_nddomain); nd6_nbr_init(); rw_init(&nd6_lock); @@ -326,44 +342,6 @@ } /* - * ND6 timer routine to handle ND6 entries - */ -void -nd6_llinfo_settimer(struct llentry *ln, time_t xtick) -{ - - CTASSERT(sizeof(time_t) > sizeof(int)); - LLE_WLOCK_ASSERT(ln); - - KASSERT(xtick >= 0); - - /* - * We have to take care of a reference leak which occurs if - * callout_reset overwrites a pending callout schedule. Unfortunately - * we don't have a mean to know the overwrite, so we need to know it - * using callout_stop. We need to call callout_pending first to exclude - * the case that the callout has never been scheduled. - */ - if (callout_pending(&ln->la_timer)) { - bool expired = callout_stop(&ln->la_timer); - if (!expired) - LLE_REMREF(ln); - } - - ln->ln_expire = time_uptime + xtick / hz; - LLE_ADDREF(ln); - if (xtick > INT_MAX) { - ln->ln_ntick = xtick - INT_MAX; - callout_reset(&ln->ln_timer_ch, INT_MAX, - nd6_llinfo_timer, ln); - } else { - ln->ln_ntick = 0; - callout_reset(&ln->ln_timer_ch, xtick, - nd6_llinfo_timer, ln); - } -} - -/* * Gets source address of the first packet in hold queue * and stores it in @src. * Returns pointer to @src (if hold queue is not empty) or NULL. @@ -389,148 +367,67 @@ return src; } -static void -nd6_llinfo_timer(void *arg) +static union nd_addr * +nd6_llinfo_holdsrc(struct llentry *ln, union nd_addr *src) { - struct llentry *ln = arg; - struct ifnet *ifp; - struct nd_kifinfo *ndi; - bool send_ns = false; - const struct in6_addr *daddr6 = NULL; - const struct in6_addr *taddr6 = &ln->r_l3addr.addr6; - struct sockaddr_in6 dsin6, tsin6; - struct mbuf *m = NULL; - bool missed = false; - SOFTNET_KERNEL_LOCK_UNLESS_NET_MPSAFE(); - - LLE_WLOCK(ln); - if ((ln->la_flags & LLE_LINKED) == 0) - goto out; - if (ln->ln_ntick > 0) { - nd6_llinfo_settimer(ln, ln->ln_ntick); - goto out; - } - - ifp = ln->lle_tbl->llt_ifp; - KASSERT(ifp != NULL); - - ndi = ND_IFINFO(ifp); + if (nd6_llinfo_get_holdsrc(ln, &src->nda_addr6) == NULL) + return NULL; + return src; +} - switch (ln->ln_state) { - case ND6_LLINFO_WAITDELETE: - LLE_REMREF(ln); - nd6_free(ln, 0); - ln = NULL; - break; - - case ND6_LLINFO_INCOMPLETE: - if (ln->ln_asked++ < nd6_mmaxtries) { - send_ns = true; - break; - } +static void +nd6_llinfo_output(struct ifnet *ifp, const union nd_addr *daddr, + const union nd_addr *taddr, __unused const uint8_t *tlladdr, + const union nd_addr *hsrc) +{ - missed = true; - sockaddr_in6_init(&tsin6, taddr6, 0, 0, 0); - - if (ln->ln_hold) { - struct mbuf *m0; - - m = ln->ln_hold; + nd6_ns_output(ifp, &daddr->nda_addr6, &taddr->nda_addr6, + &hsrc->nda_addr6, NULL); +} - /* - * assuming every packet in ln_hold has - * the same IP header - */ - m0 = m->m_nextpkt; - m->m_nextpkt = NULL; - ln->ln_hold = m0; - clear_llinfo_pqueue(ln); - } +static bool +nd6_llinfo_enabled(struct ifnet *ifp) +{ + struct nd_kifinfo *ndi = ND_IFINFO(ifp); + + return ndi->flags & ND6_IFF_PERFORMNUD; +} - /* - * Move to the ND6_LLINFO_WAITDELETE state for another - * interval at which point the llentry will be freed - * unless it's attempted to be used again and we'll - * resend NS again, rinse and repeat. - */ - ln->ln_state = ND6_LLINFO_WAITDELETE; - if (ln->ln_asked == nd6_mmaxtries) - nd6_llinfo_settimer(ln, ndi->retrans * hz / 1000); - else - send_ns = true; - break; +static uint32_t +nd6_llinfo_reachable(struct ifnet *ifp) +{ + struct nd_kifinfo *ndi = ND_IFINFO(ifp); + + return ndi->reachable; +} - case ND6_LLINFO_REACHABLE: - if (!ND6_LLINFO_PERMANENT(ln)) { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer(ln, nd6_gctimer * hz); - } - break; +static uint32_t +nd6_llinfo_retrans(struct ifnet *ifp) +{ + struct nd_kifinfo *ndi = ND_IFINFO(ifp); - case ND6_LLINFO_PURGE: - case ND6_LLINFO_STALE: - /* Garbage Collection(RFC 2461 5.3) */ - if (!ND6_LLINFO_PERMANENT(ln)) { - LLE_REMREF(ln); - nd6_free(ln, 1); - ln = NULL; - } - break; + return ndi->retrans; +} - case ND6_LLINFO_DELAY: - if (ndi->flags & ND6_IFF_PERFORMNUD) { - /* We need NUD */ - ln->ln_asked = 1; - ln->ln_state = ND6_LLINFO_PROBE; - daddr6 = &ln->r_l3addr.addr6; - send_ns = true; - } else { - ln->ln_state = ND6_LLINFO_STALE; /* XXX */ - nd6_llinfo_settimer(ln, nd6_gctimer * hz); - } - break; - case ND6_LLINFO_PROBE: - if (ln->ln_asked < nd6_umaxtries) { - ln->ln_asked++; - daddr6 = &ln->r_l3addr.addr6; - send_ns = true; - } else { - LLE_REMREF(ln); - nd6_free(ln, 0); - ln = NULL; - } - break; - } +static void +nd6_llinfo_missed(struct ifnet *ifp, const union nd_addr *taddr, struct mbuf *m) +{ + struct in6_addr mdaddr6 = zeroin6_addr; + struct sockaddr_in6 dsin6, tsin6; + struct sockaddr *sa; - if (send_ns) { - struct in6_addr src, *psrc; - - nd6_llinfo_settimer(ln, ndi->retrans * hz / 1000); - psrc = nd6_llinfo_get_holdsrc(ln, &src); - LLE_FREE_LOCKED(ln); - ln = NULL; - nd6_ns_output(ifp, daddr6, taddr6, psrc, NULL); - } + if (m != NULL) + 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); + } else + sa = NULL; -out: - if (ln != NULL) - LLE_FREE_LOCKED(ln); - SOFTNET_KERNEL_UNLOCK_UNLESS_NET_MPSAFE(); - if (missed) { - struct in6_addr mdaddr6 = zeroin6_addr; - struct sockaddr *sa; - - if (m != NULL) - 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); - } else - sa = NULL; - rt_clonedmsg(RTM_MISS, sa, sin6tosa(&tsin6), NULL, ifp); - } + sockaddr_in6_init(&tsin6, &taddr->nda_addr6, 0, 0, 0); + rt_clonedmsg(RTM_MISS, sa, sin6tosa(&tsin6), NULL, ifp); } /* @@ -675,7 +572,7 @@ if (rt != NULL) rt_unref(rt); if (ln != NULL) - ln->ln_state = ND6_LLINFO_NOSTATE; + ln->ln_state = ND_LLINFO_NOSTATE; return ln; } @@ -822,13 +719,9 @@ * but we intentionally keep it just in case. */ if (!ip6_forwarding && ln->ln_router && - ln->ln_state == ND6_LLINFO_STALE && gc) + ln->ln_state == ND_LLINFO_STALE && gc) { - if (ln->ln_expire > time_uptime) - nd6_llinfo_settimer(ln, - (ln->ln_expire - time_uptime) * hz); - else - nd6_llinfo_settimer(ln, nd6_gctimer * hz); + nd_set_timer(ln, ND_TIMER_EXPIRE); LLE_WUNLOCK(ln); return; } @@ -874,28 +767,7 @@ ifp = rt->rt_ifp; ln = nd6_lookup(&(satocsin6(rt_getkey(rt)))->sin6_addr, ifp, true); - if (ln == NULL) - return; - - if (ln->ln_state < ND6_LLINFO_REACHABLE) - goto done; - - /* - * if we get upper-layer reachability confirmation many times, - * it is possible we have false information. - */ - ln->ln_byhint++; - if (ln->ln_byhint > nd6_maxnudhint) - goto done; - - ln->ln_state = ND6_LLINFO_REACHABLE; - if (!ND6_LLINFO_PERMANENT(ln)) - nd6_llinfo_settimer(ln, ND_IFINFO(rt->rt_ifp)->reachable * hz); - -done: - LLE_WUNLOCK(ln); - - return; + nd_nud_hint(ln); } struct gc_args { @@ -913,18 +785,18 @@ if (*n <= 0) return 0; - if (ND6_LLINFO_PERMANENT(ln)) + if (ND_IS_LLINFO_PERMANENT(ln)) return 0; if (IN6_ARE_ADDR_EQUAL(&ln->r_l3addr.addr6, skip_in6)) return 0; LLE_WLOCK(ln); - if (ln->ln_state > ND6_LLINFO_INCOMPLETE) - ln->ln_state = ND6_LLINFO_STALE; + if (ln->ln_state > ND_LLINFO_INCOMPLETE) + ln->ln_state = ND_LLINFO_STALE; else - ln->ln_state = ND6_LLINFO_PURGE; - nd6_llinfo_settimer(ln, 0); + ln->ln_state = ND_LLINFO_PURGE; + nd_set_timer(ln, ND_TIMER_IMMEDIATE); LLE_WUNLOCK(ln); (*n)--; @@ -1524,15 +1396,15 @@ if ((!olladdr && lladdr) || /* (3) */ (olladdr && lladdr && llchange)) { /* (5) */ do_update = 1; - newstate = ND6_LLINFO_STALE; + newstate = ND_LLINFO_STALE; } else /* (1-2,4) */ do_update = 0; } else { do_update = 1; if (lladdr == NULL) /* (6) */ - newstate = ND6_LLINFO_NOSTATE; + newstate = ND_LLINFO_NOSTATE; else /* (7) */ - newstate = ND6_LLINFO_STALE; + newstate = ND_LLINFO_STALE; } if (do_update) { @@ -1541,19 +1413,19 @@ */ ln->ln_state = newstate; - if (ln->ln_state == ND6_LLINFO_STALE) { + if (ln->ln_state == ND_LLINFO_STALE) { /* * XXX: since nd6_output() below will cause * state tansition to DELAY and reset the timer, * we must set the timer now, although it is actually * meaningless. */ - nd6_llinfo_settimer(ln, nd6_gctimer * hz); + nd_set_timer(ln, ND_TIMER_GC); nd6_llinfo_release_pkts(ln, ifp); - } else if (ln->ln_state == ND6_LLINFO_INCOMPLETE) { + } else if (ln->ln_state == ND_LLINFO_INCOMPLETE) { /* probe right away */ - nd6_llinfo_settimer((void *)ln, 0); + nd_set_timer(ln, ND_TIMER_IMMEDIATE); } } @@ -1704,7 +1576,7 @@ ln = nd6_lookup(&dst->sin6_addr, ifp, false); if (ln != NULL && (ln->la_flags & LLE_VALID) != 0 && - ln->ln_state == ND6_LLINFO_REACHABLE) { + ln->ln_state == ND_LLINFO_REACHABLE) { /* Fast path */ memcpy(lldst, &ln->ll_addr, MIN(dstsize, ifp->if_addrlen)); LLE_RUNLOCK(ln); @@ -1739,92 +1611,7 @@ return ENETDOWN; /* better error? */ } - LLE_WLOCK_ASSERT(ln); - - /* We don't have to do link-layer address resolution on a p2p link. */ - if ((ifp->if_flags & IFF_POINTOPOINT) != 0 && - ln->ln_state < ND6_LLINFO_REACHABLE) { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer(ln, nd6_gctimer * hz); - } - - /* - * The first time we send a packet to a neighbor whose entry is - * STALE, we have to change the state to DELAY and a sets a timer to - * expire in DELAY_FIRST_PROBE_TIME seconds to ensure do - * neighbor unreachability detection on expiration. - * (RFC 2461 7.3.3) - */ - if (ln->ln_state == ND6_LLINFO_STALE) { - ln->ln_asked = 0; - ln->ln_state = ND6_LLINFO_DELAY; - nd6_llinfo_settimer(ln, nd6_delay * hz); - } - - /* - * If the neighbor cache entry has a state other than INCOMPLETE - * (i.e. its link-layer address is already resolved), just - * send the packet. - */ - if (ln->ln_state > ND6_LLINFO_INCOMPLETE) { - KASSERT((ln->la_flags & LLE_VALID) != 0); - memcpy(lldst, &ln->ll_addr, MIN(dstsize, ifp->if_addrlen)); - LLE_WUNLOCK(ln); - return 0; - } - - /* - * There is a neighbor cache entry, but no ethernet address - * response yet. Append this latest packet to the end of the - * packet queue in the mbuf, unless the number of the packet - * does not exceed nd6_maxqueuelen. When it exceeds nd6_maxqueuelen, - * the oldest packet in the queue will be removed. - */ - if (ln->ln_state == ND6_LLINFO_NOSTATE || - ln->ln_state == ND6_LLINFO_WAITDELETE) - ln->ln_state = ND6_LLINFO_INCOMPLETE; - if (ln->ln_hold) { - struct mbuf *m_hold; - int i; - - i = 0; - for (m_hold = ln->ln_hold; m_hold; m_hold = m_hold->m_nextpkt) { - i++; - if (m_hold->m_nextpkt == NULL) { - m_hold->m_nextpkt = m; - break; - } - } - while (i >= nd6_maxqueuelen) { - m_hold = ln->ln_hold; - ln->ln_hold = ln->ln_hold->m_nextpkt; - m_freem(m_hold); - i--; - } - } else { - ln->ln_hold = m; - } - - if (ln->ln_asked >= nd6_mmaxtries) - error = (rt != NULL && rt->rt_flags & RTF_GATEWAY) ? - EHOSTUNREACH : EHOSTDOWN; - else - error = EWOULDBLOCK; - - /* - * If there has been no NS for the neighbor after entering the - * INCOMPLETE state, send the first solicitation. - */ - if (!ND6_LLINFO_PERMANENT(ln) && ln->ln_asked == 0) { - struct in6_addr src, *psrc; - - ln->ln_asked++; - nd6_llinfo_settimer(ln, ndi->retrans * hz / 1000); - psrc = nd6_llinfo_get_holdsrc(ln, &src); - LLE_WUNLOCK(ln); - nd6_ns_output(ifp, NULL, &dst->sin6_addr, psrc, NULL); - } else - LLE_WUNLOCK(ln); + error = nd_resolve(ln, rt, m, lldst, dstsize); if (created) nd6_gc_neighbors(LLTABLE6(ifp), &dst->sin6_addr); @@ -1856,21 +1643,6 @@ } } -static void -clear_llinfo_pqueue(struct llentry *ln) -{ - struct mbuf *m_hold, *m_hold_next; - - for (m_hold = ln->ln_hold; m_hold; m_hold = m_hold_next) { - m_hold_next = m_hold->m_nextpkt; - m_hold->m_nextpkt = NULL; - m_freem(m_hold); - } - - ln->ln_hold = NULL; - return; -} - int nd6_sysctl( int name, diff -r a5e937c1fc66 sys/netinet6/nd6.h --- a/sys/netinet6/nd6.h Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet6/nd6.h Thu Aug 20 13:11:34 2020 +0100 @@ -36,17 +36,18 @@ #include #include -#define ND6_LLINFO_PURGE -3 -#define ND6_LLINFO_NOSTATE -2 -#define ND6_LLINFO_WAITDELETE -1 -#define ND6_LLINFO_INCOMPLETE 0 -#define ND6_LLINFO_REACHABLE 1 -#define ND6_LLINFO_STALE 2 -#define ND6_LLINFO_DELAY 3 -#define ND6_LLINFO_PROBE 4 - -#define ND6_IS_LLINFO_PROBREACH(n) ((n)->ln_state > ND6_LLINFO_INCOMPLETE) -#define ND6_LLINFO_PERMANENT(n) (((n)->ln_expire == 0) && ((n)->ln_state > ND6_LLINFO_INCOMPLETE)) +#ifndef _KERNEL +/* Backwards compat */ +#include +#define ND6_LLINFO_PURGE ND_LLINFO_PURGE +#define ND6_LLINFO_NOSTATE ND_LLINFO_NOSTATE +#define ND6_LLINFO_WAITDELETE ND_LLINFO_WAITDELETE +#define ND6_LLINFO_INCOMPLETE ND_LLINFO_INCOMPLETE +#define ND6_LLINFO_REACHABLE ND_LLINFO_REACHABLE +#define ND6_LLINFO_STALE ND_LLINFO_STALE +#define ND6_LLINFO_DELAY ND_LLINFO_DELAY +#define ND6_LLINFO_PROBE ND_LLINFO_PROBE +#endif struct nd_ifinfo { uint8_t chlim; /* CurHopLimit */ @@ -98,29 +99,17 @@ #define ND6_INFINITE_LIFETIME ((u_int32_t)~0) #ifdef _KERNEL -/* node constants */ -#define MAX_REACHABLE_TIME 3600000 /* msec */ -#define REACHABLE_TIME 30000 /* msec */ -#define RETRANS_TIMER 1000 /* msec */ -#define MIN_RANDOM_FACTOR 512 /* 1024 * 0.5 */ -#define MAX_RANDOM_FACTOR 1536 /* 1024 * 1.5 */ -#define ND_COMPUTE_RTIME(x) \ - (((MIN_RANDOM_FACTOR * (x >> 10)) + (cprng_fast32() & \ - ((MAX_RANDOM_FACTOR - MIN_RANDOM_FACTOR) * (x >> 10)))) /1000) - #include MALLOC_DECLARE(M_IP6NDP); /* nd6.c */ extern int nd6_prune; -extern int nd6_delay; -extern int nd6_umaxtries; -extern int nd6_mmaxtries; extern int nd6_useloopback; extern int nd6_maxnudhint; -extern int nd6_gctimer; extern int nd6_debug; +extern struct nd_domain nd6_nddomain; + #define nd6log(level, fmt, args...) \ do { if (nd6_debug) log(level, "%s: " fmt, __func__, ##args);} while (0) @@ -181,7 +170,6 @@ int nd6_options(union nd_opts *); struct llentry *nd6_lookup(const struct in6_addr *, const struct ifnet *, bool); struct llentry *nd6_create(const struct in6_addr *, const struct ifnet *); -void nd6_llinfo_settimer(struct llentry *, time_t); void nd6_purge(struct ifnet *, struct in6_ifextra *); void nd6_nud_hint(struct rtentry *); int nd6_resolve(struct ifnet *, const struct rtentry *, struct mbuf *, @@ -200,7 +188,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 *, uint8_t *); + const struct in6_addr *, const struct in6_addr *, const uint8_t *); const void *nd6_ifptomac(const struct ifnet *); void nd6_dad_start(struct ifaddr *, int); void nd6_dad_stop(struct ifaddr *); diff -r a5e937c1fc66 sys/netinet6/nd6_nbr.c --- a/sys/netinet6/nd6_nbr.c Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/netinet6/nd6_nbr.c Thu Aug 20 13:11:34 2020 +0100 @@ -57,6 +57,8 @@ #include #include #include +#include +#include #include #include @@ -389,13 +391,14 @@ void nd6_ns_output(struct ifnet *ifp, const struct in6_addr *daddr6, const struct in6_addr *taddr6, - struct in6_addr *hsrc, - uint8_t *nonce /* duplicate address detection */) + const struct in6_addr *hsrc, + const uint8_t *nonce /* duplicate address detection */) { struct mbuf *m; struct ip6_hdr *ip6; struct nd_neighbor_solicit *nd_ns; - struct in6_addr *src, src_in; + const struct in6_addr *src; + struct in6_addr src_in; struct ip6_moptions im6o; int icmp6len; int maxlen; @@ -734,7 +737,7 @@ goto freeit; rt_cmd = 0; - if (ln->ln_state <= ND6_LLINFO_INCOMPLETE) { + if (ln->ln_state <= ND_LLINFO_INCOMPLETE) { /* * If the link-layer has address, and no lladdr option came, * discard the packet. @@ -749,15 +752,13 @@ ln->la_flags |= LLE_VALID; rt_cmd = RTM_ADD; if (is_solicited) { - ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_state = ND_LLINFO_REACHABLE; ln->ln_byhint = 0; - if (!ND6_LLINFO_PERMANENT(ln)) { - nd6_llinfo_settimer(ln, - ND_IFINFO(ln->lle_tbl->llt_ifp)->reachable * hz); - } + if (!ND_IS_LLINFO_PERMANENT(ln)) + nd_set_timer(ln, ND_TIMER_REACHABLE); } else { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer(ln, nd6_gctimer * hz); + ln->ln_state = ND_LLINFO_STALE; + nd_set_timer(ln, ND_TIMER_GC); } } else { bool llchange; @@ -803,9 +804,9 @@ * If state is REACHABLE, make it STALE. * no other updates should be done. */ - if (ln->ln_state == ND6_LLINFO_REACHABLE) { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer(ln, nd6_gctimer * hz); + if (ln->ln_state == ND_LLINFO_REACHABLE) { + ln->ln_state = ND_LLINFO_STALE; + nd_set_timer(ln, ND_TIMER_GC); } goto freeit; } else if (is_override /* (2a) */ @@ -825,17 +826,14 @@ * changed, make it STALE. */ if (is_solicited) { - ln->ln_state = ND6_LLINFO_REACHABLE; + ln->ln_state = ND_LLINFO_REACHABLE; ln->ln_byhint = 0; - if (!ND6_LLINFO_PERMANENT(ln)) { - nd6_llinfo_settimer(ln, - ND_IFINFO(ifp)->reachable * hz); - } + if (!ND_IS_LLINFO_PERMANENT(ln)) + nd_set_timer(ln, ND_TIMER_REACHABLE); } else { if (lladdr && llchange) { - ln->ln_state = ND6_LLINFO_STALE; - nd6_llinfo_settimer(ln, - nd6_gctimer * hz); + ln->ln_state = ND_LLINFO_STALE; + nd_set_timer(ln, ND_TIMER_GC); } } } diff -r a5e937c1fc66 sys/sys/sockio.h --- a/sys/sys/sockio.h Tue Jul 28 18:53:07 2020 +0000 +++ b/sys/sys/sockio.h Thu Aug 20 13:11:34 2020 +0100 @@ -153,4 +153,6 @@ #define SIOCSETPFSYNC _IOW('i', 247, struct ifreq) #define SIOCGETPFSYNC _IOWR('i', 248, struct ifreq) +#define SIOCGNBRINFO _IOWR('i', 249, struct in_nbrinfo) /* get IA ND info */ + #endif /* !_SYS_SOCKIO_H_ */ diff -r a5e937c1fc66 usr.sbin/arp/arp.c --- a/usr.sbin/arp/arp.c Tue Jul 28 18:53:07 2020 +0000 +++ b/usr.sbin/arp/arp.c Thu Aug 20 13:11:34 2020 +0100 @@ -60,8 +60,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -92,6 +94,8 @@ static int getetheraddr(struct in_addr, struct sockaddr_dl *); static struct rt_msghdr * rtmsg(const int, const int, struct rt_msghdr *, const struct sockaddr_inarp *, const struct sockaddr_dl *); +static struct in_nbrinfo * getnbrinfo(const char *, struct in_addr *); +static const char * sec2str(time_t); static int set(int, char **); static void usage(void) __dead; @@ -439,6 +443,8 @@ struct sockaddr_inarp *sina; struct sockaddr_dl *sdl; struct hostent *hp; + struct timeval tim; + struct in_nbrinfo *nbi; mib[0] = CTL_NET; mib[1] = PF_ROUTE; @@ -497,6 +503,49 @@ if (sina->sin_len != 8) (void)printf("(weird)"); } + + if (sdl->sdl_index == 0) + goto done; + (void)gettimeofday(&tim, 0); + nbi = getnbrinfo(ifname, &sina->sin_addr); + if (nbi != NULL) { + if (nbi->expire > tim.tv_sec) { + (void)printf(" %s", + sec2str(nbi->expire - tim.tv_sec)); + } else if (nbi->expire == 0) + (void)printf(" %s", "permanent"); + else + (void)printf(" %s", "expired"); + + switch (nbi->state) { + case ND_LLINFO_NOSTATE: + (void)printf(" N"); + break; + case ND_LLINFO_WAITDELETE: + (void)printf(" W"); + break; + case ND_LLINFO_INCOMPLETE: + (void)printf(" I"); + break; + case ND_LLINFO_REACHABLE: + (void)printf(" R"); + break; + case ND_LLINFO_STALE: + (void)printf(" S"); + break; + case ND_LLINFO_DELAY: + (void)printf(" D"); + break; + case ND_LLINFO_PROBE: + (void)printf(" P"); + break; + default: + (void)printf(" ?"); + break; + } + } + +done: (void)printf("\n"); } free(buf); @@ -800,3 +849,66 @@ freeifaddrs(ifaddrs); return -1; } + +static struct in_nbrinfo * +getnbrinfo(const char *ifname, struct in_addr *addr) +{ + static struct in_nbrinfo nbi, *nbip; + int s; + + if ((s = prog_socket(AF_INET, SOCK_DGRAM, 0)) == -1) + err(1, "socket"); + + (void)memset(&nbi, 0, sizeof(nbi)); + (void)strlcpy(nbi.ifname, ifname, sizeof(nbi.ifname)); + nbi.addr = *addr; + if (prog_ioctl(s, SIOCGNBRINFO, &nbi) == -1) { + warn("ioctl(SIOCGNBRINFO)"); + nbip = NULL; + } else + nbip = &nbi; + (void)prog_close(s); + + return nbip; +} + +static const char * +sec2str(time_t total) +{ + static char result[256]; + int days, hours, mins, secs; + int first = 1; + char *p = result; + char *ep = &result[sizeof(result)]; + int n; + + days = total / 3600 / 24; + hours = (total / 3600) % 24; + mins = (total / 60) % 60; + secs = total % 60; + + if (days) { + first = 0; + n = snprintf(p, (size_t)(ep - p), "%dd", days); + if (n < 0 || n >= ep - p) + return "?"; + p += n; + } + if (!first || hours) { + first = 0; + n = snprintf(p, (size_t)(ep - p), "%dh", hours); + if (n < 0 || n >= ep - p) + return "?"; + p += n; + } + if (!first || mins) { + first = 0; + n = snprintf(p, (size_t)(ep - p), "%dm", mins); + if (n < 0 || n >= ep - p) + return "?"; + p += n; + } + (void)snprintf(p, (size_t)(ep - p), "%ds", secs); + + return(result); +} diff -r a5e937c1fc66 usr.sbin/arp/arp_hostops.c --- a/usr.sbin/arp/arp_hostops.c Tue Jul 28 18:53:07 2020 +0000 +++ b/usr.sbin/arp/arp_hostops.c Thu Aug 20 13:11:34 2020 +0100 @@ -31,6 +31,7 @@ __RCSID("$NetBSD: arp_hostops.c,v 1.2 2019/02/27 23:29:50 dholland Exp $"); #endif /* !lint */ +#include #include #include #include @@ -45,6 +46,7 @@ .op_open = open, .op_getpid = getpid, + .op_ioctl = ioctl, .op_read = read, .op_write = write, .op_close = close, diff -r a5e937c1fc66 usr.sbin/arp/arp_rumpops.c --- a/usr.sbin/arp/arp_rumpops.c Tue Jul 28 18:53:07 2020 +0000 +++ b/usr.sbin/arp/arp_rumpops.c Thu Aug 20 13:11:34 2020 +0100 @@ -49,6 +49,7 @@ .op_open = rump_sys_open, .op_getpid = rump_sys_getpid, + .op_ioctl = rump_sys_ioctl, .op_read = rump_sys_read, .op_write = rump_sys_write, .op_close = rump_sys_close, diff -r a5e937c1fc66 usr.sbin/arp/prog_ops.h --- a/usr.sbin/arp/prog_ops.h Tue Jul 28 18:53:07 2020 +0000 +++ b/usr.sbin/arp/prog_ops.h Thu Aug 20 13:11:34 2020 +0100 @@ -43,6 +43,7 @@ int (*op_open)(const char *, int, ...); pid_t (*op_getpid)(void); + int (*op_ioctl)(int, unsigned long, ...); ssize_t (*op_read)(int, void *, size_t); ssize_t (*op_write)(int, const void *, size_t); @@ -54,6 +55,7 @@ #define prog_socket prog_ops.op_socket #define prog_open prog_ops.op_open #define prog_getpid prog_ops.op_getpid +#define prog_ioctl prog_ops.op_ioctl #define prog_read prog_ops.op_read #define prog_write prog_ops.op_write #define prog_close prog_ops.op_close @@ -63,6 +65,7 @@ #define prog_socket socket #define prog_open open #define prog_getpid getpid +#define prog_ioctl ioctl #define prog_read read #define prog_write write #define prog_close close