git: bac4760524a2 - main - netinet6: Fix ND link-layer address option layout for IPoIB
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 09 Jul 2026 18:20:34 UTC
The branch main has been updated by pouria:
URL: https://cgit.FreeBSD.org/src/commit/?id=bac4760524a2a15ce75e35251f0c9cdf31732f3f
commit bac4760524a2a15ce75e35251f0c9cdf31732f3f
Author: Vinícius Ferrão <vinicius@ferrao.net.br>
AuthorDate: 2026-07-09 07:05:53 +0000
Commit: Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
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);
}