From: Florian Obser Subject: Remove upper layer IPv6 neighbor reachability hints. To: tech Date: Mon, 15 Sep 2025 16:58:07 +0200 Running neighbor discovery once in a while is better than touching the neighbor cache for (nearly) every TCP packet. This functionality was disabled by default, however due to a bug / missing optimization, the code did a bunch of work before figuring out that the feature is disabled. With this, net.inet6.icmp6.nd6_maxnudhint can be removed. OK? diff --git lib/libc/sys/sysctl.2 lib/libc/sys/sysctl.2 index 412ead4fa01..30499de85e4 100644 --- lib/libc/sys/sysctl.2 +++ lib/libc/sys/sysctl.2 @@ -1838,7 +1838,6 @@ The currently defined protocols and names are: .It icmp6 Ta mtudisc_hiwat Ta integer Ta yes .It icmp6 Ta mtudisc_lowat Ta integer Ta yes .It icmp6 Ta nd6_delay Ta integer Ta yes -.It icmp6 Ta nd6_maxnudhint Ta integer Ta yes .It icmp6 Ta nd6_mmaxtries Ta integer Ta yes .It icmp6 Ta nd6_umaxtries Ta integer Ta yes .It icmp6 Ta redirtimeout Ta integer Ta yes @@ -1892,16 +1891,6 @@ timing constant in IPv6 neighbor discovery specification .Pq RFC 4861 , in seconds. .Pp -.It Li icmp6.nd6_maxnudhint Pq Va net.inet6.icmp6.nd6_maxnudhint -IPv6 neighbor discovery permits upper layer protocols to supply reachability -hints, to avoid unnecessary neighbor discovery exchanges. -This variable defines the number of consecutive hints the neighbor discovery -layer will take. -For example, by setting the variable to 3, neighbor discovery will take -a maximum of 3 consecutive hints. -After receiving 3 hints, the neighbor discovery layer will instead perform -the normal neighbor discovery process. -.Pp .It Li icmp6.nd6_mmaxtries Pq Va net.inet6.icmp6.nd6_mmaxtries This variable specifies the .Dv MAX_MULTICAST_SOLICIT diff --git sys/netinet/icmp6.h sys/netinet/icmp6.h index 20e8761d8a1..48c26d30c91 100644 --- sys/netinet/icmp6.h +++ sys/netinet/icmp6.h @@ -508,7 +508,6 @@ struct icmp6stat { #define ICMPV6CTL_ND6_QUEUED 11 #define ICMPV6CTL_NODEINFO 13 #define ICMPV6CTL_ERRPPSLIMIT 14 /* ICMPv6 error pps limitation */ -#define ICMPV6CTL_ND6_MAXNUDHINT 15 #define ICMPV6CTL_MTUDISC_HIWAT 16 #define ICMPV6CTL_MTUDISC_LOWAT 17 #define ICMPV6CTL_MAXID 18 @@ -529,7 +528,7 @@ struct icmp6stat { { 0, 0 }, \ { 0, 0 }, \ { "errppslimit", CTLTYPE_INT }, \ - { "nd6_maxnudhint", CTLTYPE_INT }, \ + { 0, 0 }, \ { "mtudisc_hiwat", CTLTYPE_INT }, \ { "mtudisc_lowat", CTLTYPE_INT }, \ } diff --git sys/netinet/tcp_input.c sys/netinet/tcp_input.c index bd61054df4b..732ad9f477e 100644 --- sys/netinet/tcp_input.c +++ sys/netinet/tcp_input.c @@ -125,22 +125,6 @@ struct timeval tcp_ackdrop_ppslim_last; #define SEQ_MIN(a,b) (SEQ_LT(a,b) ? (a) : (b)) #define SEQ_MAX(a,b) (SEQ_GT(a,b) ? (a) : (b)) -/* - * Neighbor Discovery, Neighbor Unreachability Detection Upper layer hint. - */ -#ifdef INET6 -#define ND6_HINT(tp, hint) \ -do { \ - if (tp && tp->t_inpcb && \ - ISSET(tp->t_inpcb->inp_flags, INP_IPV6) && \ - rtisvalid(tp->t_inpcb->inp_route.ro_rt)) { \ - nd6_nud_hint(tp->t_inpcb->inp_route.ro_rt, hint); \ - } \ -} while (0) -#else -#define ND6_HINT(tp, hint) -#endif - #ifdef TCP_ECN /* * ECN (Explicit Congestion Notification) support based on RFC3168 @@ -311,9 +295,6 @@ tcp_flush_queue(struct tcpcb *tp) { struct socket *so = tp->t_inpcb->inp_socket; struct tcpqent *q, *nq; -#ifdef INET6 - int nd6_maxnudhint_local = atomic_load_int(&nd6_maxnudhint); -#endif int flags; /* @@ -333,7 +314,6 @@ tcp_flush_queue(struct tcpcb *tp) nq = TAILQ_NEXT(q, tcpqe_q); TAILQ_REMOVE(&tp->t_segq, q, tcpqe_q); - ND6_HINT(tp, nd6_maxnudhint_local); if (so->so_rcv.sb_state & SS_CANTRCVMORE) m_freem(q->tcpqe_m); else { @@ -427,9 +407,6 @@ tcp_input_solocked(struct mbuf **mp, int *offp, int proto, int af, struct ip6_hdr *ip6 = NULL; #endif /* INET6 */ int do_ecn = 0; -#ifdef INET6 - int nd6_maxnudhint_local = atomic_load_int(&nd6_maxnudhint); -#endif #ifdef TCP_ECN u_char iptos; #endif @@ -973,7 +950,6 @@ findpcb: tcpstat_pkt(tcps_rcvackpack, tcps_rcvackbyte, acked); tp->t_rcvacktime = now; - ND6_HINT(tp, nd6_maxnudhint_local); mtx_enter(&so->so_snd.sb_mtx); sbdrop(&so->so_snd, acked); @@ -1058,7 +1034,6 @@ findpcb: /* Packet has most recent segment, no urgent exists. */ tp->rcv_up = tp->rcv_nxt; tcpstat_pkt(tcps_rcvpack, tcps_rcvbyte, tlen); - ND6_HINT(tp, nd6_maxnudhint_local); TCP_SETUP_ACK(tp, tiflags, m); /* @@ -1763,7 +1738,6 @@ trimthenstep6: tp->snd_cwnd = ulmin(cw + incr, TCP_MAXWIN << tp->snd_scale); } - ND6_HINT(tp, nd6_maxnudhint_local); if (acked > so->so_snd.sb_cc) { if (tp->snd_wnd > so->so_snd.sb_cc) tp->snd_wnd -= so->so_snd.sb_cc; @@ -1993,7 +1967,6 @@ dodata: /* XXX */ tp->rcv_nxt += tlen; tiflags = th->th_flags & TH_FIN; tcpstat_pkt(tcps_rcvpack, tcps_rcvbyte, tlen); - ND6_HINT(tp, nd6_maxnudhint_local); if (so->so_rcv.sb_state & SS_CANTRCVMORE) m_freem(m); else { diff --git sys/netinet6/icmp6.c sys/netinet6/icmp6.c index 6eb4ebf90f6..23a35fbf2f4 100644 --- sys/netinet6/icmp6.c +++ sys/netinet6/icmp6.c @@ -1781,7 +1781,6 @@ const struct sysctl_bounded_args icmpv6ctl_vars_unlocked[] = { { ICMPV6CTL_ND6_UMAXTRIES, &nd6_umaxtries, 0, INT_MAX }, { ICMPV6CTL_ND6_MMAXTRIES, &nd6_mmaxtries, 0, INT_MAX }, { ICMPV6CTL_MTUDISC_HIWAT, &icmp6_mtudisc_hiwat, 0, INT_MAX }, - { ICMPV6CTL_ND6_MAXNUDHINT, &nd6_maxnudhint, 0, INT_MAX }, { ICMPV6CTL_MTUDISC_LOWAT, &icmp6_mtudisc_lowat, 0, INT_MAX }, }; @@ -1851,7 +1850,6 @@ icmp6_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, case ICMPV6CTL_ND6_DELAY: case ICMPV6CTL_ND6_UMAXTRIES: case ICMPV6CTL_ND6_MMAXTRIES: - case ICMPV6CTL_ND6_MAXNUDHINT: case ICMPV6CTL_MTUDISC_HIWAT: case ICMPV6CTL_MTUDISC_LOWAT: error = sysctl_bounded_arr(icmpv6ctl_vars_unlocked, diff --git sys/netinet6/nd6.c sys/netinet6/nd6.c index 80ef6a28506..cc52d7d487b 100644 --- sys/netinet6/nd6.c +++ sys/netinet6/nd6.c @@ -83,8 +83,6 @@ const 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; /* [a] max # of subsequent upper layer hints */ - /* llinfo_nd6 live time, rt_llinfo and RTF_LLINFO are protected by nd6_mtx */ struct mutex nd6_mtx = MUTEX_INITIALIZER(IPL_SOFTNET); @@ -715,55 +713,6 @@ nd6_free(struct rtentry *rt, struct ifnet *ifp, int i_am_router) rtdeletemsg(rt, ifp, ifp->if_rdomain); } -/* - * Upper-layer reachability hint for Neighbor Unreachability Detection. - * - * XXX cost-effective methods? - */ -void -nd6_nud_hint(struct rtentry *rt, int maxnudhint) -{ - struct llinfo_nd6 *ln; - struct ifnet *ifp; - - NET_ASSERT_LOCKED(); - - if ((rt->rt_flags & RTF_GATEWAY) != 0 || - (rt->rt_flags & RTF_LLINFO) == 0 || - rt->rt_gateway == NULL || - rt->rt_gateway->sa_family != AF_LINK) { - /* This is not a host route. */ - return; - } - - ifp = if_get(rt->rt_ifidx); - if (ifp == NULL) - return; - - mtx_enter(&nd6_mtx); - - ln = (struct llinfo_nd6 *)rt->rt_llinfo; - if (ln == NULL) - goto out; - if (ln->ln_state < ND6_LLINFO_REACHABLE) - goto out; - - /* - * if we get upper-layer reachability confirmation many times, - * it is possible we have false information. - */ - ln->ln_byhint++; - if (ln->ln_byhint > maxnudhint) - goto out; - - ln->ln_state = ND6_LLINFO_REACHABLE; - if (!ND6_LLINFO_PERMANENT(ln)) - nd6_llinfo_settimer(ln, ifp->if_nd->reachable); -out: - mtx_leave(&nd6_mtx); - if_put(ifp); -} - void nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) { @@ -855,7 +804,6 @@ nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) * which is specified by ndp command. */ ln->ln_state = ND6_LLINFO_REACHABLE; - ln->ln_byhint = 0; } else { /* * When req == RTM_RESOLVE, rt is created and @@ -907,7 +855,6 @@ nd6_rtrequest(struct ifnet *ifp, int req, struct rtentry *rt) if (ifa != NULL || (rt->rt_flags & RTF_ANNOUNCE)) { ln->ln_state = ND6_LLINFO_REACHABLE; - ln->ln_byhint = 0; rt->rt_expire = 0; } mtx_leave(&nd6_mtx); diff --git sys/netinet6/nd6.h sys/netinet6/nd6.h index 4967266f46a..eb7001a4560 100644 --- sys/netinet6/nd6.h +++ sys/netinet6/nd6.h @@ -87,7 +87,6 @@ struct llinfo_nd6 { struct mbuf_queue ln_mq; /* hold packets until resolved */ struct in6_addr ln_saddr6; /* source of prompting packet */ long ln_asked; /* number of queries already sent for addr */ - int ln_byhint; /* # of times we made it reachable by UL hint */ short ln_state; /* reachability state */ short ln_router; /* 2^0: ND6 router bit */ }; @@ -116,7 +115,6 @@ extern unsigned int ln_hold_total; extern int nd6_delay; extern int nd6_umaxtries; extern int nd6_mmaxtries; -extern int nd6_maxnudhint; extern const int nd6_gctimer; struct nd_opts { @@ -133,7 +131,6 @@ struct rtentry *nd6_lookup(const struct in6_addr *, int, struct ifnet *, u_int); void nd6_llinfo_settimer(const struct llinfo_nd6 *, unsigned int); void nd6_purge(struct ifnet *); -void nd6_nud_hint(struct rtentry *, int); void nd6_rtrequest(struct ifnet *, int, struct rtentry *); int nd6_ioctl(u_long, caddr_t, struct ifnet *); void nd6_cache_lladdr(struct ifnet *, const struct in6_addr *, char *, diff --git sys/netinet6/nd6_nbr.c sys/netinet6/nd6_nbr.c index 2e09e591a73..489016b42dd 100644 --- sys/netinet6/nd6_nbr.c +++ sys/netinet6/nd6_nbr.c @@ -670,7 +670,6 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) bcopy(lladdr, LLADDR(sdl), ifp->if_addrlen); if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; - ln->ln_byhint = 0; /* Notify userland that a new ND entry is reachable. */ rtm_send(rt, RTM_RESOLVE, 0, ifp->if_rdomain); if (!ND6_LLINFO_PERMANENT(ln)) { @@ -764,7 +763,6 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) */ if (is_solicited) { ln->ln_state = ND6_LLINFO_REACHABLE; - ln->ln_byhint = 0; if (!ND6_LLINFO_PERMANENT(ln)) { nd6_llinfo_settimer(ln, ifp->if_nd->reachable); -- In my defence, I have been left unsupervised.