git: f1726686cb31 - main - iflib: Do not pull up trailing IPv4 fragments to the L4 header
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 23 Oct 2026 18:29:51 UTC
The branch main has been updated by rcm:
URL: https://cgit.FreeBSD.org/src/commit/?id=f1726686cb31ff141e507760124f44442c9f141a
commit f1726686cb31ff141e507760124f44442c9f141a
Author: R. Christian McDonald <rcm@FreeBSD.org>
AuthorDate: 2026-10-23 18:28:29 +0000
Commit: R. Christian McDonald <rcm@FreeBSD.org>
CommitDate: 2026-10-23 18:28:29 +0000
iflib: Do not pull up trailing IPv4 fragments to the L4 header
iflib_parse_header() pulls every IPv4 packet up to the end of the TCP or
UDP header, including fragments at a non-zero offset, which carry no L4
header at all. A trailing fragment shorter than that header (1-7 bytes
of UDP, 1-19 bytes of TCP) makes m_pullup() fail and the frame is freed
without a trace in the interface counters.
Only parse the L4 header on the first fragment. TSO never produces
fragments, so assert that.
With a 1500-byte MTU every UDP datagram of 1473-1479 bytes is lost
behind a forwarding host whose ingress NIC sets receive checksum flags,
or on any VLAN with hardware tagging. SIP over UDP is a common victim.
Reported by: Alex Roberts
Reviewed by: gallatin
Fixes: c224b2ce7de0 ("iflib: don't pullup UDP payloads to the TCP header size")
See also: https://redmine.pfsense.org/issues/17196
MFC after: 3 days
Sponsored by: Rubicon Communications, LLC ("Netgate")
---
sys/net/iflib.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/sys/net/iflib.c b/sys/net/iflib.c
index d47c108292ae..6682ccbcfca8 100644
--- a/sys/net/iflib.c
+++ b/sys/net/iflib.c
@@ -3674,7 +3674,7 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp)
case ETHERTYPE_IP:
{
struct ip *ip;
- struct tcphdr *th;
+ struct tcphdr *th = NULL;
uint8_t hlen;
hlen = pi->ipi_ehdrlen + sizeof(*ip);
@@ -3685,7 +3685,13 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp)
}
ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen);
hlen = pi->ipi_ehdrlen + (ip->ip_hl << 2);
- if (ip->ip_p == IPPROTO_TCP) {
+ if ((ip->ip_off & htons(IP_OFFMASK)) != 0) {
+ /*
+ * Only the first fragment carries the TCP/UDP header,
+ * so do not extend hlen beyond the IP header.
+ */
+ MPASS(!IS_TSO4(pi));
+ } else if (ip->ip_p == IPPROTO_TCP) {
hlen += sizeof(*th);
th = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
} else if (ip->ip_p == IPPROTO_UDP) {
@@ -3697,7 +3703,8 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp)
return (ENOMEM);
/* reset pointers after pullup */
ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen);
- th = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
+ if (th != NULL)
+ th = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
}
pi->ipi_ip_hlen = ip->ip_hl << 2;
pi->ipi_ipproto = ip->ip_p;
@@ -3706,7 +3713,7 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp)
/* TCP checksum offload may require TCP header length */
if (IS_TX_OFFLOAD4(pi)) {
- if (__predict_true(pi->ipi_ipproto == IPPROTO_TCP)) {
+ if (__predict_true(th != NULL)) {
pi->ipi_tcp_hflags = tcp_get_flags(th);
pi->ipi_tcp_hlen = th->th_off << 2;
pi->ipi_tcp_seq = th->th_seq;