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
This commit is contained in:
Tom Jones
2026-01-23 12:43:25 +00:00
parent 823f158a01
commit 069a67374e
5 changed files with 53 additions and 204 deletions
+11 -1
View File
@@ -257,7 +257,17 @@ struct ip6_frag {
#define IPV6_HLIMDEC 1 /* subtracted when forwarding */ #define IPV6_HLIMDEC 1 /* subtracted when forwarding */
#define IPV6_MMTU 1280 /* minimal MTU and reassembly. 1024 + 256 */ #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 */ #define IPV6_MAXOPTHDR 2048 /* max option header size, 256 64-bit words */
#endif /* not _NETINET_IP6_H_ */ #endif /* not _NETINET_IP6_H_ */
+23 -87
View File
@@ -219,7 +219,7 @@ VNET_PCPUSTAT_SYSUNINIT(ip6stat);
struct rmlock in6_ifaddr_lock; struct rmlock in6_ifaddr_lock;
RM_SYSINIT(in6_ifaddr_lock, &in6_ifaddr_lock, "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. * 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 #endif
static int 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) int *nxt, int *ours)
{ {
struct mbuf *m; struct mbuf *m;
struct ip6_hdr *ip6; struct ip6_hdr *ip6;
struct ip6_hbh *hbh; struct ip6_hbh *hbh;
if (ip6_hopopts_input(plen, rtalert, mp, off)) { if (ip6_hopopts_input(rtalert, mp, off)) {
#if 0 /*touches NULL pointer*/ #if 0 /*touches NULL pointer*/
in6_ifstat_inc((*mp)->m_pkthdr.rcvif, ifs6_in_discard); in6_ifstat_inc((*mp)->m_pkthdr.rcvif, ifs6_in_discard);
#endif #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 *); ip6 = mtod(m, struct ip6_hdr *);
/* /*
* if the payload length field is 0 and the next header field * If the payload length field is 0 and the next header field indicates
* indicates Hop-by-Hop Options header, then a Jumbo Payload * Hop-by-Hop Options header, then a Jumbo Payload option MUST be
* option MUST be included. * included. We no not support Jumbo Payloads so report an error.
*/ */
if (ip6->ip6_plen == 0 && *plen == 0) { if (ip6->ip6_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.
*/
IP6STAT_INC(ip6s_badoptions); IP6STAT_INC(ip6s_badoptions);
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard);
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
@@ -774,6 +769,15 @@ ip6_input(struct mbuf *m)
goto bad; 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). * Disambiguate address scope zones (if there is ambiguity).
* We first make sure that the original source or destination address * We first make sure that the original source or destination address
@@ -850,11 +854,9 @@ ip6_input(struct mbuf *m)
/* /*
* Process Hop-by-Hop options header if it's contained. * Process Hop-by-Hop options header if it's contained.
* m may be modified in ip6_hopopts_input(). * 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->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; return;
} else } else
nxt = ip6->ip6_nxt; nxt = ip6->ip6_nxt;
@@ -963,13 +965,12 @@ ip6_input(struct mbuf *m)
/* /*
* Hop-by-Hop options header processing. If a valid jumbo payload option is * 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 * rtalertp - XXX: should be stored more smart way
*/ */
static int static int
ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp, ip6_hopopts_input(u_int32_t *rtalertp, struct mbuf **mp, int *offp)
struct mbuf **mp, int *offp)
{ {
struct mbuf *m = *mp; struct mbuf *m = *mp;
int off = *offp, hbhlen; int off = *offp, hbhlen;
@@ -999,7 +1000,7 @@ ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp,
off += hbhlen; off += hbhlen;
hbhlen -= sizeof(struct ip6_hbh); hbhlen -= sizeof(struct ip6_hbh);
if (ip6_process_hopopts(m, (u_int8_t *)hbh + 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; *mp = NULL;
return (-1); return (-1);
} }
@@ -1021,13 +1022,11 @@ ip6_hopopts_input(u_int32_t *plenp, u_int32_t *rtalertp,
*/ */
int int
ip6_process_hopopts(struct mbuf *m, u_int8_t *opthead, int hbhlen, 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; int optlen = 0;
u_int8_t *opt = opthead; u_int8_t *opt = opthead;
u_int16_t rtalert_val; u_int16_t rtalert_val;
u_int32_t jumboplen;
const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh); const int erroff = sizeof(struct ip6_hdr) + sizeof(struct ip6_hbh);
for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { 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); *rtalertp = ntohs(rtalert_val);
break; break;
case IP6OPT_JUMBO: case IP6OPT_JUMBO:
/* XXX may need check for alignment */ /* We do not support the Jumbo Payload option. */
if (hbhlen < IP6OPT_JUMBO_LEN) { goto bad;
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;
default: /* unknown option */ default: /* unknown option */
if (hbhlen < IP6OPT_MINLEN) { if (hbhlen < IP6OPT_MINLEN) {
IP6STAT_INC(ip6s_toosmall); IP6STAT_INC(ip6s_toosmall);
+4 -106
View File
@@ -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_copyexthdr(struct mbuf **, caddr_t, int);
static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int,
struct ip6_frag **); struct ip6_frag **);
static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t);
static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *);
static void ip6_getpmtu(struct route_in6 *, int, static void ip6_getpmtu(struct route_in6 *, int,
struct ifnet *, const struct in6_addr *, u_long *, u_int, u_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; m->m_pkthdr.len += optlen;
plen = m->m_pkthdr.len - sizeof(*ip6); plen = m->m_pkthdr.len - sizeof(*ip6);
/* If this is a jumbo payload, insert a jumbo payload option. */
if (plen > IPV6_MAXPACKET) { if (plen > IPV6_MAXPACKET) {
if (!hdrsplit) { error = EMSGSIZE;
if ((error = ip6_splithdr(m, &exthdrs)) != 0) { goto freehdrs;
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 */
} else } else
ip6->ip6_plen = htons(plen); ip6->ip6_plen = htons(plen);
nexthdrp = &ip6->ip6_nxt; nexthdrp = &ip6->ip6_nxt;
@@ -982,7 +969,6 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
if (exthdrs.ip6e_hbh) { if (exthdrs.ip6e_hbh) {
struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
u_int32_t dummy; /* XXX unused */ u_int32_t dummy; /* XXX unused */
u_int32_t plen = 0; /* XXX: ip6_process will check the value */
#ifdef DIAGNOSTIC #ifdef DIAGNOSTIC
if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len)
@@ -998,7 +984,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
m->m_pkthdr.rcvif = ifp; m->m_pkthdr.rcvif = ifp;
if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1), if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1),
((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh), ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh),
&dummy, &plen) < 0) { &dummy) < 0) {
/* m was already freed at this point. */ /* m was already freed at this point. */
error = EINVAL;/* better error? */ error = EINVAL;/* better error? */
goto done; goto done;
@@ -1186,7 +1172,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
in6_ifstat_inc(ifp, ifs6_out_fragfail); in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad; goto bad;
} else if (ip6->ip6_plen == 0) { } else if (ip6->ip6_plen == 0) {
/* Jumbo payload cannot be fragmented. */ /* We do not support jumbo payload. */
error = EMSGSIZE; error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail); in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad; goto bad;
@@ -1312,94 +1298,6 @@ ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen)
return (0); 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. * Insert fragment header and copy unfragmentable header portions.
*/ */
+1 -2
View File
@@ -393,8 +393,7 @@ int ip6_lasthdr(const struct mbuf *, int, int, int *);
extern int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, extern int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *,
struct mbuf *); struct mbuf *);
int ip6_process_hopopts(struct mbuf *, u_int8_t *, int, u_int32_t *, int ip6_process_hopopts(struct mbuf *, u_int8_t *, int, u_int32_t *);
u_int32_t *);
struct mbuf **ip6_savecontrol_v4(struct inpcb *, struct mbuf *, struct mbuf **ip6_savecontrol_v4(struct inpcb *, struct mbuf *,
struct mbuf **, int *); struct mbuf **, int *);
void ip6_savecontrol(struct inpcb *, struct mbuf *, struct mbuf **); void ip6_savecontrol(struct inpcb *, struct mbuf *, struct mbuf **);
+14 -8
View File
@@ -703,11 +703,6 @@ udp6_send(struct socket *so, int flags_arg, struct mbuf *m,
sin6 = (struct sockaddr_in6 *)addr6; 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; scope_ambiguous = 0;
if (sin6) { if (sin6) {
/* Protect *addr6 from overwrites. */ /* Protect *addr6 from overwrites. */
@@ -865,10 +860,21 @@ udp6_send(struct socket *so, int flags_arg, struct mbuf *m,
fport = inp->inp_fport; 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; ulen = m->m_pkthdr.len;
plen = sizeof(struct udphdr) + ulen; plen = sizeof(struct udphdr) + ulen;
hlen = sizeof(struct ip6_hdr); 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 * Calculate data length and get a mbuf for UDP, IP6, and possible
* link-layer headers. Immediate slide the data pointer back forward * 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. * the entire UDPLite packet is covered by the checksum.
*/ */
cscov_partial = (cscov == 0) ? 0 : 1; cscov_partial = (cscov == 0) ? 0 : 1;
} else if (plen <= 0xffff) } else {
MPASS(plen <= IPV6_MAXPAYLOAD);
udp6->uh_ulen = htons((u_short)plen); udp6->uh_ulen = htons((u_short)plen);
else }
udp6->uh_ulen = 0;
udp6->uh_sum = 0; udp6->uh_sum = 0;
ip6 = mtod(m, struct ip6_hdr *); ip6 = mtod(m, struct ip6_hdr *);