git: 069a67374ed9 - main - ip6: Remove support for RFC2675 (Jumbo Payload Option)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 27 Jan 2026 13:21:54 UTC
The branch main has been updated by thj:
URL: https://cgit.FreeBSD.org/src/commit/?id=069a67374ed9641ff1ada2aecaac1cc61a560649
commit 069a67374ed9641ff1ada2aecaac1cc61a560649
Author: Tom Jones <thj@FreeBSD.org>
AuthorDate: 2026-01-23 12:43:25 +0000
Commit: Tom Jones <thj@FreeBSD.org>
CommitDate: 2026-01-27 13:20:33 +0000
ip6: Remove support for RFC2675 (Jumbo Payload Option)
The Jumbo Payload option was intended to allow the deployment of IPv6 on
networks with a link MTU in excess of 65,735 octets.
Speaking to one of the authors of RFC2675 the networks which motivated
the Jumbo Payload option no longer exist.
FreeBSD does not currently support any links with this capacity and
discussion when this change was first proposed suggested that the loop
back interface had to be patched to test implementation.
As there are no known devices that can carry Jumbo Payloads remove
support.
Reviewed by: glebius, teuxen, kp
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D19960
---
sys/netinet/ip6.h | 12 ++++-
sys/netinet6/ip6_input.c | 110 ++++++++++-----------------------------------
sys/netinet6/ip6_output.c | 110 ++-------------------------------------------
sys/netinet6/ip6_var.h | 3 +-
sys/netinet6/udp6_usrreq.c | 22 +++++----
5 files changed, 53 insertions(+), 204 deletions(-)
diff --git a/sys/netinet/ip6.h b/sys/netinet/ip6.h
index 2f61d594e59d..580283b09745 100644
--- a/sys/netinet/ip6.h
+++ b/sys/netinet/ip6.h
@@ -257,7 +257,17 @@ struct ip6_frag {
#define IPV6_HLIMDEC 1 /* subtracted when forwarding */
#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */
-#define IPV6_MAXPACKET 65535 /* ip6 max packet size without Jumbo payload*/
+/*
+ * XXX: IPV6_MAXPACKET is historically used as the maximum packet size.
+ * The maximum IPv6 packet size is:
+ *
+ * v6 header size (40) + payload load size (65535)
+ *
+ * practically this isn't encountered as it requires a link with an mtu of
+ * 65575 octets. IPV6_MAXPACKET is preserved at this value for compatibility.
+ */
+#define IPV6_MAXPACKET 65535
+#define IPV6_MAXPAYLOAD 65535 /* max size that can be carried in a payload */
#define IPV6_MAXOPTHDR 2048 /* max option header size, 256 64-bit words */
#endif /* not _NETINET_IP6_H_ */
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index d914bfbcbdbf..a23f5d46d6a3 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -219,7 +219,7 @@ VNET_PCPUSTAT_SYSUNINIT(ip6stat);
struct rmlock in6_ifaddr_lock;
RM_SYSINIT(in6_ifaddr_lock, &in6_ifaddr_lock, "in6_ifaddr_lock");
-static int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *);
+static int ip6_hopopts_input(u_int32_t *, struct mbuf **, int *);
/*
* IP6 initialization: fill in IP6 protocol switch table.
@@ -407,14 +407,14 @@ VNET_SYSUNINIT(inet6, SI_SUB_PROTO_DOMAIN, SI_ORDER_THIRD, ip6_destroy, NULL);
#endif
static int
-ip6_input_hbh(struct mbuf **mp, uint32_t *plen, uint32_t *rtalert, int *off,
+ip6_input_hbh(struct mbuf **mp, uint32_t *rtalert, int *off,
int *nxt, int *ours)
{
struct mbuf *m;
struct ip6_hdr *ip6;
struct ip6_hbh *hbh;
- if (ip6_hopopts_input(plen, rtalert, mp, off)) {
+ if (ip6_hopopts_input(rtalert, mp, off)) {
#if 0 /*touches NULL pointer*/
in6_ifstat_inc((*mp)->m_pkthdr.rcvif, ifs6_in_discard);
#endif
@@ -426,16 +426,11 @@ ip6_input_hbh(struct mbuf **mp, uint32_t *plen, uint32_t *rtalert, int *off,
ip6 = mtod(m, struct ip6_hdr *);
/*
- * if the payload length field is 0 and the next header field
- * indicates Hop-by-Hop Options header, then a Jumbo Payload
- * option MUST be included.
+ * If the payload length field is 0 and the next header field indicates
+ * Hop-by-Hop Options header, then a Jumbo Payload option MUST be
+ * included. We no not support Jumbo Payloads so report an error.
*/
- if (ip6->ip6_plen == 0 && *plen == 0) {
- /*
- * Note that if a valid jumbo payload option is
- * contained, ip6_hopopts_input() must set a valid
- * (non-zero) payload length to the variable plen.
- */
+ if (ip6->ip6_plen == 0) {
IP6STAT_INC(ip6s_badoptions);
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
@@ -774,6 +769,15 @@ passin:
goto bad;
}
+ plen = (uint32_t)ntohs(ip6->ip6_plen);
+
+ /*
+ * We don't support Jumbograms, reject packets with plen == 0 as early
+ * as we can.
+ */
+ if (plen == 0)
+ goto bad;
+
/*
* Disambiguate address scope zones (if there is ambiguity).
* We first make sure that the original source or destination address
@@ -850,11 +854,9 @@ passin:
/*
* Process Hop-by-Hop options header if it's contained.
* m may be modified in ip6_hopopts_input().
- * If a JumboPayload option is included, plen will also be modified.
*/
- plen = (u_int32_t)ntohs(ip6->ip6_plen);
if (ip6->ip6_nxt == IPPROTO_HOPOPTS) {
- if (ip6_input_hbh(&m, &plen, &rtalert, &off, &nxt, &ours) != 0)
+ if (ip6_input_hbh(&m, &rtalert, &off, &nxt, &ours) != 0)
return;
} else
nxt = ip6->ip6_nxt;
@@ -963,13 +965,12 @@ bad:
/*
* Hop-by-Hop options header processing. If a valid jumbo payload option is
- * included, the real payload length will be stored in plenp.
+ * included report an error.
*
* rtalertp - XXX: should be stored more smart way
*/
static int
-ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp,
- struct mbuf **mp, int *offp)
+ip6_hopopts_input(u_int32_t *rtalertp, struct mbuf **mp, int *offp)
{
struct mbuf *m = *mp;
int off = *offp, hbhlen;
@@ -999,7 +1000,7 @@ ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp,
off += hbhlen;
hbhlen -= sizeof(struct ip6_hbh);
if (ip6_process_hopopts(m, (u_int8_t *)hbh + sizeof(struct ip6_hbh),
- hbhlen, rtalertp, plenp) < 0) {
+ hbhlen, rtalertp) < 0) {
*mp = NULL;
return (-1);
}
@@ -1021,13 +1022,11 @@ ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp,
*/
int
ip6_process_hopopts(struct mbuf *m, u_int8_t *opthead, int hbhlen,
- u_int32_t *rtalertp, u_int32_t *plenp)
+ u_int32_t *rtalertp)
{
- struct ip6_hdr *ip6;
int optlen = 0;
u_int8_t *opt = opthead;
u_int16_t rtalert_val;
- u_int32_t jumboplen;
const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh);
for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) {
@@ -1060,71 +1059,8 @@ ip6_process_hopopts(struct mbuf *m, u_int8_t *opthead, int hbhlen,
*rtalertp = ntohs(rtalert_val);
break;
case IP6OPT_JUMBO:
- /* XXX may need check for alignment */
- if (hbhlen < IP6OPT_JUMBO_LEN) {
- IP6STAT_INC(ip6s_toosmall);
- goto bad;
- }
- if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) {
- /* XXX stat */
- icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- erroff + opt + 1 - opthead);
- return (-1);
- }
- optlen = IP6OPT_JUMBO_LEN;
-
- /*
- * IPv6 packets that have non 0 payload length
- * must not contain a jumbo payload option.
- */
- ip6 = mtod(m, struct ip6_hdr *);
- if (ip6->ip6_plen) {
- IP6STAT_INC(ip6s_badoptions);
- icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- erroff + opt - opthead);
- return (-1);
- }
-
- /*
- * We may see jumbolen in unaligned location, so
- * we'd need to perform bcopy().
- */
- bcopy(opt + 2, &jumboplen, sizeof(jumboplen));
- jumboplen = (u_int32_t)htonl(jumboplen);
-
-#if 1
- /*
- * if there are multiple jumbo payload options,
- * *plenp will be non-zero and the packet will be
- * rejected.
- * the behavior may need some debate in ipngwg -
- * multiple options does not make sense, however,
- * there's no explicit mention in specification.
- */
- if (*plenp != 0) {
- IP6STAT_INC(ip6s_badoptions);
- icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- erroff + opt + 2 - opthead);
- return (-1);
- }
-#endif
-
- /*
- * jumbo payload length must be larger than 65535.
- */
- if (jumboplen <= IPV6_MAXPACKET) {
- IP6STAT_INC(ip6s_badoptions);
- icmp6_error(m, ICMP6_PARAM_PROB,
- ICMP6_PARAMPROB_HEADER,
- erroff + opt + 2 - opthead);
- return (-1);
- }
- *plenp = jumboplen;
-
- break;
+ /* We do not support the Jumbo Payload option. */
+ goto bad;
default: /* unknown option */
if (hbhlen < IP6OPT_MINLEN) {
IP6STAT_INC(ip6s_toosmall);
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index a9b31ac8061f..dca1bcf04371 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -142,7 +142,6 @@ static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *,
static int ip6_copyexthdr(struct mbuf **, caddr_t, int);
static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
struct ip6_frag **);
-static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
static void ip6_getpmtu(struct route_in6 *, int,
struct ifnet *, const struct in6_addr *, u_long *, u_int, u_int);
@@ -542,21 +541,9 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
m->m_pkthdr.len += optlen;
plen = m->m_pkthdr.len - sizeof(*ip6);
- /* If this is a jumbo payload, insert a jumbo payload option. */
if (plen > IPV6_MAXPACKET) {
- if (!hdrsplit) {
- if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
- m = NULL;
- goto freehdrs;
- }
- m = exthdrs.ip6e_ip6;
- ip6 = mtod(m, struct ip6_hdr *);
- hdrsplit = true;
- }
- if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
- goto freehdrs;
- ip6->ip6_plen = 0;
- optlen += 8; /* JUMBOOPTLEN */
+ error = EMSGSIZE;
+ goto freehdrs;
} else
ip6->ip6_plen = htons(plen);
nexthdrp = &ip6->ip6_nxt;
@@ -982,7 +969,6 @@ nonh6lookup:
if (exthdrs.ip6e_hbh) {
struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
u_int32_t dummy; /* XXX unused */
- u_int32_t plen = 0; /* XXX: ip6_process will check the value */
#ifdef DIAGNOSTIC
if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len)
@@ -998,7 +984,7 @@ nonh6lookup:
m->m_pkthdr.rcvif = ifp;
if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1),
((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh),
- &dummy, &plen) < 0) {
+ &dummy) < 0) {
/* m was already freed at this point. */
error = EINVAL;/* better error? */
goto done;
@@ -1186,7 +1172,7 @@ passout:
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
} else if (ip6->ip6_plen == 0) {
- /* Jumbo payload cannot be fragmented. */
+ /* We do not support jumbo payload. */
error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
@@ -1312,94 +1298,6 @@ ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen)
return (0);
}
-/*
- * Insert jumbo payload option.
- */
-static int
-ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen)
-{
- struct mbuf *mopt;
- u_char *optbuf;
- u_int32_t v;
-
-#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */
-
- /*
- * If there is no hop-by-hop options header, allocate new one.
- * If there is one but it doesn't have enough space to store the
- * jumbo payload option, allocate a cluster to store the whole options.
- * Otherwise, use it to store the options.
- */
- if (exthdrs->ip6e_hbh == NULL) {
- mopt = m_get(M_NOWAIT, MT_DATA);
- if (mopt == NULL)
- return (ENOBUFS);
- mopt->m_len = JUMBOOPTLEN;
- optbuf = mtod(mopt, u_char *);
- optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */
- exthdrs->ip6e_hbh = mopt;
- } else {
- struct ip6_hbh *hbh;
-
- mopt = exthdrs->ip6e_hbh;
- if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) {
- /*
- * XXX assumption:
- * - exthdrs->ip6e_hbh is not referenced from places
- * other than exthdrs.
- * - exthdrs->ip6e_hbh is not an mbuf chain.
- */
- int oldoptlen = mopt->m_len;
- struct mbuf *n;
-
- /*
- * XXX: give up if the whole (new) hbh header does
- * not fit even in an mbuf cluster.
- */
- if (oldoptlen + JUMBOOPTLEN > MCLBYTES)
- return (ENOBUFS);
-
- /*
- * As a consequence, we must always prepare a cluster
- * at this point.
- */
- n = m_getcl(M_NOWAIT, MT_DATA, 0);
- if (n == NULL)
- return (ENOBUFS);
- n->m_len = oldoptlen + JUMBOOPTLEN;
- bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t),
- oldoptlen);
- optbuf = mtod(n, caddr_t) + oldoptlen;
- m_freem(mopt);
- mopt = exthdrs->ip6e_hbh = n;
- } else {
- optbuf = mtod(mopt, u_char *) + mopt->m_len;
- mopt->m_len += JUMBOOPTLEN;
- }
- optbuf[0] = IP6OPT_PADN;
- optbuf[1] = 1;
-
- /*
- * Adjust the header length according to the pad and
- * the jumbo payload option.
- */
- hbh = mtod(mopt, struct ip6_hbh *);
- hbh->ip6h_len += (JUMBOOPTLEN >> 3);
- }
-
- /* fill in the option. */
- optbuf[2] = IP6OPT_JUMBO;
- optbuf[3] = 4;
- v = (u_int32_t)htonl(plen + JUMBOOPTLEN);
- bcopy(&v, &optbuf[4], sizeof(u_int32_t));
-
- /* finally, adjust the packet header length */
- exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN;
-
- return (0);
-#undef JUMBOOPTLEN
-}
-
/*
* Insert fragment header and copy unfragmentable header portions.
*/
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index db1631736c4a..c1645f587483 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -393,8 +393,7 @@ int ip6_lasthdr(const struct mbuf *, int, int, int *);
extern int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *,
struct mbuf *);
-int ip6_process_hopopts(struct mbuf *, u_int8_t *, int, u_int32_t *,
- u_int32_t *);
+int ip6_process_hopopts(struct mbuf *, u_int8_t *, int, u_int32_t *);
struct mbuf **ip6_savecontrol_v4(struct inpcb *, struct mbuf *,
struct mbuf **, int *);
void ip6_savecontrol(struct inpcb *, struct mbuf *, struct mbuf **);
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index ca7c95497510..1d1dcb75a1df 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -703,11 +703,6 @@ udp6_send(struct socket *so, int flags_arg, struct mbuf *m,
sin6 = (struct sockaddr_in6 *)addr6;
- /*
- * In contrast to IPv4 we do not validate the max. packet length
- * here due to IPv6 Jumbograms (RFC2675).
- */
-
scope_ambiguous = 0;
if (sin6) {
/* Protect *addr6 from overwrites. */
@@ -865,10 +860,21 @@ udp6_send(struct socket *so, int flags_arg, struct mbuf *m,
fport = inp->inp_fport;
}
+
+ /*
+ * We do not support IPv6 Jumbograms (RFC2675), so validate the payload
+ * length fits in a normal gram.
+ */
ulen = m->m_pkthdr.len;
plen = sizeof(struct udphdr) + ulen;
hlen = sizeof(struct ip6_hdr);
+ if (plen > IPV6_MAXPAYLOAD) {
+ m_freem(control);
+ m_freem(m);
+ return (EMSGSIZE);
+ }
+
/*
* Calculate data length and get a mbuf for UDP, IP6, and possible
* link-layer headers. Immediate slide the data pointer back forward
@@ -903,10 +909,10 @@ udp6_send(struct socket *so, int flags_arg, struct mbuf *m,
* the entire UDPLite packet is covered by the checksum.
*/
cscov_partial = (cscov == 0) ? 0 : 1;
- } else if (plen <= 0xffff)
+ } else {
+ MPASS(plen <= IPV6_MAXPAYLOAD);
udp6->uh_ulen = htons((u_short)plen);
- else
- udp6->uh_ulen = 0;
+ }
udp6->uh_sum = 0;
ip6 = mtod(m, struct ip6_hdr *);