* [PATCH net-next 1/2] net: qdisc_pkt_len_segs_init() cleanup
2026-04-03 22:15 [PATCH net-next 0/2] net: pull gso packet headers in core stack Eric Dumazet
@ 2026-04-03 22:15 ` Eric Dumazet
2026-04-06 18:37 ` Joe Damato
2026-04-03 22:15 ` [PATCH net-next 2/2] net: pull headers in qdisc_pkt_len_segs_init() Eric Dumazet
1 sibling, 1 reply; 5+ messages in thread
From: Eric Dumazet @ 2026-04-03 22:15 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Andrew Lunn, Joe Damato, netdev, eric.dumazet,
Eric Dumazet
Reduce indentation level by returning early if the transport header
was not set.
Add an unlikely() clause as this is not the common case.
No functional change.
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/core/dev.c | 62 +++++++++++++++++++++++++-------------------------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 4519f0e59beb695fffb69a0a9cf89a7b75fe209f..3eb2f50f516564c6a6465c19a6d802c136e0ddde 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4104,6 +4104,7 @@ EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
static void qdisc_pkt_len_segs_init(struct sk_buff *skb)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
+ unsigned int hdr_len;
u16 gso_segs;
qdisc_skb_cb(skb)->pkt_len = skb->len;
@@ -4117,44 +4118,43 @@ static void qdisc_pkt_len_segs_init(struct sk_buff *skb)
/* To get more precise estimation of bytes sent on wire,
* we add to pkt_len the headers size of all segments
*/
- if (skb_transport_header_was_set(skb)) {
- unsigned int hdr_len;
+ if (unlikely(!skb_transport_header_was_set(skb)))
+ return;
- /* mac layer + network layer */
- if (!skb->encapsulation)
- hdr_len = skb_transport_offset(skb);
- else
- hdr_len = skb_inner_transport_offset(skb);
+ /* mac layer + network layer */
+ if (!skb->encapsulation)
+ hdr_len = skb_transport_offset(skb);
+ else
+ hdr_len = skb_inner_transport_offset(skb);
- /* + transport layer */
- if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
- const struct tcphdr *th;
- struct tcphdr _tcphdr;
+ /* + transport layer */
+ if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
+ const struct tcphdr *th;
+ struct tcphdr _tcphdr;
- th = skb_header_pointer(skb, hdr_len,
- sizeof(_tcphdr), &_tcphdr);
- if (likely(th))
- hdr_len += __tcp_hdrlen(th);
- } else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
- struct udphdr _udphdr;
+ th = skb_header_pointer(skb, hdr_len,
+ sizeof(_tcphdr), &_tcphdr);
+ if (likely(th))
+ hdr_len += __tcp_hdrlen(th);
+ } else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
+ struct udphdr _udphdr;
- if (skb_header_pointer(skb, hdr_len,
- sizeof(_udphdr), &_udphdr))
- hdr_len += sizeof(struct udphdr);
- }
+ if (skb_header_pointer(skb, hdr_len,
+ sizeof(_udphdr), &_udphdr))
+ hdr_len += sizeof(struct udphdr);
+ }
- if (unlikely(shinfo->gso_type & SKB_GSO_DODGY)) {
- int payload = skb->len - hdr_len;
+ if (unlikely(shinfo->gso_type & SKB_GSO_DODGY)) {
+ int payload = skb->len - hdr_len;
- /* Malicious packet. */
- if (payload <= 0)
- return;
- gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size);
- shinfo->gso_segs = gso_segs;
- qdisc_skb_cb(skb)->pkt_segs = gso_segs;
- }
- qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len;
+ /* Malicious packet. */
+ if (payload <= 0)
+ return;
+ gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size);
+ shinfo->gso_segs = gso_segs;
+ qdisc_skb_cb(skb)->pkt_segs = gso_segs;
}
+ qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len;
}
static int dev_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *q,
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH net-next 2/2] net: pull headers in qdisc_pkt_len_segs_init()
2026-04-03 22:15 [PATCH net-next 0/2] net: pull gso packet headers in core stack Eric Dumazet
2026-04-03 22:15 ` [PATCH net-next 1/2] net: qdisc_pkt_len_segs_init() cleanup Eric Dumazet
@ 2026-04-03 22:15 ` Eric Dumazet
2026-04-06 18:40 ` Joe Damato
1 sibling, 1 reply; 5+ messages in thread
From: Eric Dumazet @ 2026-04-03 22:15 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Andrew Lunn, Joe Damato, netdev, eric.dumazet,
Eric Dumazet
Most ndo_start_xmit() methods expects headers of gso packets
to be already in skb->head.
net/core/tso.c users are particularly at risk, because tso_build_hdr()
does a memcpy(hdr, skb->data, hdr_len);
qdisc_pkt_len_segs_init() already does a dissection of gso packets.
Use pskb_may_pull() instead of skb_header_pointer() to make
sure drivers do not have to reimplement this.
Some malicious packets could be fed, detect them so that we can
drop them sooner with a new SKB_DROP_REASON_SKB_BAD_GSO drop_reason.
Fixes: e876f208af18 ("net: Add a software TSO helper API")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
include/net/dropreason-core.h | 3 +++
net/core/dev.c | 51 +++++++++++++++++++++--------------
2 files changed, 34 insertions(+), 20 deletions(-)
diff --git a/include/net/dropreason-core.h b/include/net/dropreason-core.h
index de61dd5dbfd9dc7d91d22d79a510d42fb69eb60a..51855de5d20819bff950e1c5846a6a4f3f8a3b95 100644
--- a/include/net/dropreason-core.h
+++ b/include/net/dropreason-core.h
@@ -74,6 +74,7 @@
FN(UNHANDLED_PROTO) \
FN(SKB_CSUM) \
FN(SKB_GSO_SEG) \
+ FN(SKB_BAD_GSO) \
FN(SKB_UCOPY_FAULT) \
FN(DEV_HDR) \
FN(DEV_READY) \
@@ -392,6 +393,8 @@ enum skb_drop_reason {
SKB_DROP_REASON_SKB_CSUM,
/** @SKB_DROP_REASON_SKB_GSO_SEG: gso segmentation error */
SKB_DROP_REASON_SKB_GSO_SEG,
+ /** @SKB_DROP_REASON_SKB_BAD_GSO: malicious gso packet. */
+ SKB_DROP_REASON_SKB_BAD_GSO,
/**
* @SKB_DROP_REASON_SKB_UCOPY_FAULT: failed to copy data from user space,
* e.g., via zerocopy_sg_from_iter() or skb_orphan_frags_rx()
diff --git a/net/core/dev.c b/net/core/dev.c
index 3eb2f50f516564c6a6465c19a6d802c136e0ddde..5a31f9d2128c28d07d2dd5d58baa2374d469ed82 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4101,16 +4101,16 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d
}
EXPORT_SYMBOL_GPL(validate_xmit_skb_list);
-static void qdisc_pkt_len_segs_init(struct sk_buff *skb)
+static enum skb_drop_reason qdisc_pkt_len_segs_init(struct sk_buff *skb)
{
struct skb_shared_info *shinfo = skb_shinfo(skb);
- unsigned int hdr_len;
+ unsigned int hdr_len, tlen;
u16 gso_segs;
qdisc_skb_cb(skb)->pkt_len = skb->len;
if (!shinfo->gso_size) {
qdisc_skb_cb(skb)->pkt_segs = 1;
- return;
+ return SKB_NOT_DROPPED_YET;
}
qdisc_skb_cb(skb)->pkt_segs = gso_segs = shinfo->gso_segs;
@@ -4118,43 +4118,49 @@ static void qdisc_pkt_len_segs_init(struct sk_buff *skb)
/* To get more precise estimation of bytes sent on wire,
* we add to pkt_len the headers size of all segments
*/
- if (unlikely(!skb_transport_header_was_set(skb)))
- return;
/* mac layer + network layer */
- if (!skb->encapsulation)
+ if (!skb->encapsulation) {
+ if (unlikely(!skb_transport_header_was_set(skb)))
+ return SKB_NOT_DROPPED_YET;
hdr_len = skb_transport_offset(skb);
- else
+ } else {
hdr_len = skb_inner_transport_offset(skb);
-
+ }
/* + transport layer */
if (likely(shinfo->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))) {
const struct tcphdr *th;
- struct tcphdr _tcphdr;
- th = skb_header_pointer(skb, hdr_len,
- sizeof(_tcphdr), &_tcphdr);
- if (likely(th))
- hdr_len += __tcp_hdrlen(th);
- } else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
- struct udphdr _udphdr;
+ if (!pskb_may_pull(skb, hdr_len + sizeof(struct tcphdr)))
+ return SKB_DROP_REASON_SKB_BAD_GSO;
- if (skb_header_pointer(skb, hdr_len,
- sizeof(_udphdr), &_udphdr))
- hdr_len += sizeof(struct udphdr);
+ th = (const struct tcphdr *)(skb->data + hdr_len);
+ tlen = __tcp_hdrlen(th);
+ if (tlen < sizeof(*th))
+ return SKB_DROP_REASON_SKB_BAD_GSO;
+ hdr_len += tlen;
+ if (!pskb_may_pull(skb, hdr_len))
+ return SKB_DROP_REASON_SKB_BAD_GSO;
+ } else if (shinfo->gso_type & SKB_GSO_UDP_L4) {
+ if (!pskb_may_pull(skb, hdr_len + sizeof(struct udphdr)))
+ return SKB_DROP_REASON_SKB_BAD_GSO;
+ hdr_len += sizeof(struct udphdr);
}
+ /* prior pskb_may_pull() might have changed skb->head. */
+ shinfo = skb_shinfo(skb);
if (unlikely(shinfo->gso_type & SKB_GSO_DODGY)) {
int payload = skb->len - hdr_len;
/* Malicious packet. */
if (payload <= 0)
- return;
+ return SKB_DROP_REASON_SKB_BAD_GSO;
gso_segs = DIV_ROUND_UP(payload, shinfo->gso_size);
shinfo->gso_segs = gso_segs;
qdisc_skb_cb(skb)->pkt_segs = gso_segs;
}
qdisc_skb_cb(skb)->pkt_len += (gso_segs - 1) * hdr_len;
+ return SKB_NOT_DROPPED_YET;
}
static int dev_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *q,
@@ -4771,6 +4777,12 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
(SKBTX_SCHED_TSTAMP | SKBTX_BPF)))
__skb_tstamp_tx(skb, NULL, NULL, skb->sk, SCM_TSTAMP_SCHED);
+ reason = qdisc_pkt_len_segs_init(skb);
+ if (unlikely(reason)) {
+ dev_core_stats_tx_dropped_inc(dev);
+ kfree_skb_reason(skb, reason);
+ return -EINVAL;
+ }
/* Disable soft irqs for various locks below. Also
* stops preemption for RCU.
*/
@@ -4778,7 +4790,6 @@ int __dev_queue_xmit(struct sk_buff *skb, struct net_device *sb_dev)
skb_update_prio(skb);
- qdisc_pkt_len_segs_init(skb);
tcx_set_ingress(skb, false);
#ifdef CONFIG_NET_EGRESS
if (static_branch_unlikely(&egress_needed_key)) {
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 5+ messages in thread