The branch main has been updated by pouria: URL: https://cgit.FreeBSD.org/src/commit/?id=bac4760524a2a15ce75e35251f0c9cdf31732f3f
commit bac4760524a2a15ce75e35251f0c9cdf31732f3f Author: Vinícius Ferrão <[email protected]> AuthorDate: 2026-07-09 07:05:53 +0000 Commit: Pouria Mousavizadeh Tehrani <[email protected]> CommitDate: 2026-07-09 17:30:04 +0000 netinet6: Fix ND link-layer address option layout for IPoIB RFC 4391 (IP over InfiniBand), section 9.3, lays the ND source/target link-layer address option out as type, length (3), two reserved zero octets, then the 20-octet IPoIB link-layer address. The ND code assumed the Ethernet layout (RFC 4861, section 4.6.1) everywhere and read/wrote the address directly after the option header, i.e. two octets early. The option-length sanity check computes 24 for both layouts for a 20-octet address, so the mismatch was silent. PR: 296585 Reviewed by: adrian, pouria Sponsored by: VersatusHPC Differential Revision: https://reviews.freebsd.org/D58096 --- sys/netinet6/icmp6.c | 15 ++++++++++----- sys/netinet6/nd6.h | 1 + sys/netinet6/nd6_nbr.c | 47 +++++++++++++++++++++++++++++++++++++---------- sys/netinet6/nd6_rtr.c | 18 ++++++++++-------- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index 7fe18f4a4d2b..e92a433c21b1 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -2169,7 +2169,8 @@ icmp6_redirect_input(struct mbuf *m, int off) union nd_opts ndopts; char ip6buf[INET6_ADDRSTRLEN]; char *lladdr; - int icmp6len, is_onlink, is_router, lladdrlen; + int icmp6len, is_onlink, is_router; + u_int lladdr_pad, lladdrlen; M_ASSERTPKTHDR(m); KASSERT(m->m_pkthdr.rcvif != NULL, ("%s: no rcvif", __func__)); @@ -2296,16 +2297,17 @@ icmp6_redirect_input(struct mbuf *m, int off) lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_tgt_lladdr) { - lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "%s: lladdrlen mismatch for %s " - "(if %d, icmp6 packet %d): %s\n", + "(if %d, icmp6 packet %u): %s\n", __func__, ip6_sprintf(ip6buf, &redtgt6), - ifp->if_addrlen, lladdrlen - 2, + ifp->if_addrlen, lladdrlen - 2 - lladdr_pad, icmp6_redirect_diag(&src6, &reddst6, &redtgt6))); goto bad; } @@ -2492,6 +2494,7 @@ icmp6_redirect_output(struct mbuf *m0, struct nhop_object *nh) { /* target lladdr option */ int len; + u_int pad; struct nd_opt_hdr *nd_opt; char *lladdr; @@ -2499,6 +2502,7 @@ icmp6_redirect_output(struct mbuf *m0, struct nhop_object *nh) if (ln == NULL) goto nolladdropt; + pad = nd6_lladdr_opt_pad(ifp); len = sizeof(*nd_opt) + ifp->if_addrlen; len = (len + 7) & ~7; /* round by 8 */ /* safety check */ @@ -2507,9 +2511,10 @@ icmp6_redirect_output(struct mbuf *m0, struct nhop_object *nh) if (ln->la_flags & LLE_VALID) { nd_opt = (struct nd_opt_hdr *)p; + memset(nd_opt, 0, len); nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = len >> 3; - lladdr = (char *)(nd_opt + 1); + lladdr = (char *)(nd_opt + 1) + pad; bcopy(ln->ll_addr, lladdr, ifp->if_addrlen); p += len; } diff --git a/sys/netinet6/nd6.h b/sys/netinet6/nd6.h index 92b776b008b9..372d06b6574d 100644 --- a/sys/netinet6/nd6.h +++ b/sys/netinet6/nd6.h @@ -380,6 +380,7 @@ void nd6_ns_input(struct mbuf *, int, int); void nd6_ns_output(struct ifnet *, const struct in6_addr *, const struct in6_addr *, const struct in6_addr *, uint8_t *); caddr_t nd6_ifptomac(struct ifnet *); +u_int nd6_lladdr_opt_pad(struct ifnet *); void nd6_dad_init(void); void nd6_dad_start(struct ifaddr *, int); void nd6_dad_stop(struct ifaddr *); diff --git a/sys/netinet6/nd6_nbr.c b/sys/netinet6/nd6_nbr.c index c345e96f8d3f..0601b55a0f18 100644 --- a/sys/netinet6/nd6_nbr.c +++ b/sys/netinet6/nd6_nbr.c @@ -151,7 +151,8 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) union nd_opts ndopts; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; char *lladdr; - int lladdrlen, rflag, tentative, tlladdr; + int rflag, tentative, tlladdr; + u_int lladdr_pad, lladdrlen; uint32_t dflags; ifa = NULL; @@ -240,8 +241,9 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } @@ -302,9 +304,9 @@ nd6_ns_input(struct mbuf *m, int off, int icmp6len) if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_ns_input: lladdrlen mismatch for %s " - "(if %d, NS packet %d)\n", + "(if %d, NS packet %u)\n", ip6_sprintf(ip6bufs, &taddr6), - ifp->if_addrlen, lladdrlen - 2)); + ifp->if_addrlen, lladdrlen - 2 - lladdr_pad)); goto bad; } @@ -582,6 +584,7 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6, struct nd_opt_hdr *nd_opt; char *mac; int optlen; + u_int pad; mac = NULL; if (ifp->if_carp) @@ -591,6 +594,7 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6, if (mac != NULL) { nd_opt = (struct nd_opt_hdr *)(nd_ns + 1); + pad = nd6_lladdr_opt_pad(ifp); optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; /* 8 byte alignments... */ optlen = (optlen + 7) & ~7; @@ -600,7 +604,8 @@ nd6_ns_output_fib(struct ifnet *ifp, const struct in6_addr *saddr6, bzero(nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; - bcopy(mac, nd_opt + 1, ifp->if_addrlen); + memcpy((char *)(nd_opt + 1) + pad, mac, + ifp->if_addrlen); } } /* @@ -680,7 +685,8 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) char *lladdr; size_t linkhdrsize; int flags, is_override, is_router, is_solicited; - int lladdr_off, lladdrlen, checklink; + int lladdr_off, checklink; + u_int lladdr_pad, lladdrlen; bool flush_holdchain = false; NET_EPOCH_ASSERT(); @@ -749,8 +755,9 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_tgt_lladdr) { - lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_tgt_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; } @@ -795,8 +802,8 @@ nd6_na_input(struct mbuf *m, int off, int icmp6len) if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, "nd6_na_input: lladdrlen mismatch for %s " - "(if %d, NA packet %d)\n", ip6_sprintf(ip6bufs, &taddr6), - ifp->if_addrlen, lladdrlen - 2)); + "(if %d, NA packet %u)\n", ip6_sprintf(ip6bufs, &taddr6), + ifp->if_addrlen, lladdrlen - 2 - lladdr_pad)); goto bad; } @@ -1087,6 +1094,7 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, } } if ((tlladdr & ND6_NA_OPT_LLA) && mac != NULL) { + u_int pad = nd6_lladdr_opt_pad(ifp); int optlen = sizeof(struct nd_opt_hdr) + ifp->if_addrlen; struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *)(nd_na + 1); @@ -1099,7 +1107,7 @@ nd6_na_output_fib(struct ifnet *ifp, const struct in6_addr *daddr6_0, bzero((caddr_t)nd_opt, optlen); nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR; nd_opt->nd_opt_len = optlen >> 3; - bcopy(mac, (caddr_t)(nd_opt + 1), ifp->if_addrlen); + memcpy((char *)(nd_opt + 1) + pad, mac, ifp->if_addrlen); } else flags &= ~ND_NA_FLAG_OVERRIDE; @@ -1156,6 +1164,25 @@ nd6_ifptomac(struct ifnet *ifp) } } +/* + * Number of reserved octets between the header of an ND link-layer + * address option and the link-layer address itself. On IPoIB links + * two zero octets are prepended to the 20-octet link-layer address + * to fill the 24-octet option (RFC 4391, section 9.3); on all other + * link types the address immediately follows the option header. + */ +u_int +nd6_lladdr_opt_pad(struct ifnet *ifp) +{ + switch (ifp->if_type) { + case IFT_INFINIBAND: + case IFT_INFINIBANDLAG: + return (2); + default: + return (0); + } +} + struct dadq { TAILQ_ENTRY(dadq) dad_list; struct ifaddr *dad_ifa; diff --git a/sys/netinet6/nd6_rtr.c b/sys/netinet6/nd6_rtr.c index 637f00bef2c9..167f3704f02d 100644 --- a/sys/netinet6/nd6_rtr.c +++ b/sys/netinet6/nd6_rtr.c @@ -171,7 +171,7 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len) union nd_opts ndopts; char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN]; char *lladdr; - int lladdrlen; + u_int lladdr_pad, lladdrlen; ifp = m->m_pkthdr.rcvif; @@ -226,16 +226,17 @@ nd6_rs_input(struct mbuf *m, int off, int icmp6len) lladdr = NULL; lladdrlen = 0; + lladdr_pad = nd6_lladdr_opt_pad(ifp); if (ndopts.nd_opts_src_lladdr) { - lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1); + lladdr = (char *)(ndopts.nd_opts_src_lladdr + 1) + lladdr_pad; lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, - "%s: lladdrlen mismatch for %s (if %d, RS packet %d)\n", + "%s: lladdrlen mismatch for %s (if %d, RS packet %u)\n", __func__, ip6_sprintf(ip6bufs, &saddr6), - ifp->if_addrlen, lladdrlen - 2)); + ifp->if_addrlen, lladdrlen - 2 - lladdr_pad)); goto bad; } @@ -467,18 +468,19 @@ nd6_ra_opt_src_lladdr(struct nd_opt_hdr *opthdr, struct ifnet *ifp, { char ip6bufs[INET6_ADDRSTRLEN]; char *lladdr = NULL; - int lladdrlen = 0; + u_int lladdr_pad = nd6_lladdr_opt_pad(ifp); + u_int lladdrlen = 0; if (opthdr != NULL) { - lladdr = (char *)(opthdr + 1); + lladdr = (char *)(opthdr + 1) + lladdr_pad; lladdrlen = opthdr->nd_opt_len << 3; } if (lladdr && ((ifp->if_addrlen + 2 + 7) & ~7) != lladdrlen) { nd6log((LOG_INFO, - "%s: lladdrlen mismatch for %s (if %d, RA packet %d)\n", + "%s: lladdrlen mismatch for %s (if %d, RA packet %u)\n", __func__, ip6_sprintf(ip6bufs, &saddr6), - ifp->if_addrlen, lladdrlen - 2)); + ifp->if_addrlen, lladdrlen - 2 - lladdr_pad)); return (-1); }
