# Re: \[PATCH net v2 3/6\] net/sched: act\_mirred: reject redirects without a link-layer header

- From: Eric Dumazet \<edumazet@kernel.org\>
- Date: 2026-10-23 13:17:28 
- List: netdev
- Page: https://ratatoskr.run/netdev/2026/10/17793602
- Thread: https://ratatoskr.run/netdev/2026/10/17793006/t.md

```text
Le ven. 23 oct. 2026 à 13:59, Jamal Hadi Salim [ref] a écrit :
>
> The issue:
> tcf_mirred_to_dev() may redirect a packet that has no link-layer header to
> a device that expects one.  TUN builds a valid L3 skb with mac_len == 0;
> mirred then pushes zero bytes and the IP header is parsed as Ethernet by
> eth_type_trans().  The same happens after a VLAN POP_ETH or MPLS POP chain
> whose producer did not leave a full inner Ethernet header.
>
> The Fix:
> After the existing push/pull adjustment, when the target expects data at
> the MAC header, require a link-layer header of at least
> dev->min_header_len bytes and pull it into the linear area.  Drop the
> packet otherwise.  This mirrors the check bpf_redirect() already performs
> and keeps the legitimate push_eth-then-mirred case working.
>
> Conditions to recreate the bug:
> - create a TUN device with IFF_TUN | IFF_VNET_HDR | IFF_NO_PI;
> - attach egress mirred redirect to an Ethernet device (for example lo);
> - write a virtio_net_hdr with hdr_len=1 and an 8192-byte payload.
> The unfixed kernel parses the IP header as Ethernet and can BUG in
> eth_type_trans() via loopback_xmit.
>
> Fixes: 53592b364001 ("net/sched: act_mirred: Implement ingress actions")
> Suggested-by: Eric Dumazet <edumazet@kernel.org>
> Suggested-by: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
> Cc: <redacted>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
> ---
>  net/sched/act_mirred.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
> index 553342c55cf7..e3e0e4aa70a1 100644
> --- a/net/sched/act_mirred.c
> +++ b/net/sched/act_mirred.c
> @@ -316,6 +316,11 @@ static int tcf_mirred_to_dev(struct sk_buff *skb, struct tcf_mirred *m,
>                 }
>         }
>
> +       if (!expects_nh &&
> +           (unlikely(skb_network_offset(skb_to_send) < dev->min_header_len) ||
> +            !pskb_may_pull(skb_to_send, dev->min_header_len)))
> +               goto err_cant_do;
> +

Oops, sorry this breaks tools/testing/selftests/net/forwarding/tc_mpls_l2vpn.sh

My bad, the skb_network_offset() part I suggested in v1 is wrong.

Please remove it:

diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index b0390b08600c4a709b18f6d92aebe6629081c735..0bd73c5072a90d3e952d0a724461adcf0307592e
100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -317,8 +317,7 @@ static int tcf_mirred_to_dev(struct sk_buff *skb,
struct tcf_mirred *m,
        }

        if (!expects_nh &&
-           (unlikely(skb_network_offset(skb_to_send) < dev->min_header_len) ||
-            !pskb_may_pull(skb_to_send, dev->min_header_len)))
+           !pskb_may_pull(skb_to_send, dev->min_header_len))
                goto err_cant_do;

        skb_to_send->skb_iif = skb->dev->ifindex;

Keep only the pskb_may_pull() which is enough to prevent
eth_type_trans() from crashing on a short linear part.

Thanks!
```
