From: Eric Dumazet <edumazet@google.com>
To: "David S . Miller" <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>,
Paolo Abeni <pabeni@redhat.com>
Cc: Simon Horman <horms@kernel.org>,
Kuniyuki Iwashima <kuniyu@google.com>,
netdev@vger.kernel.org, eric.dumazet@gmail.com,
William Tu <u9012063@gmail.com>,
Eric Dumazet <edumazet@google.com>,
stable@vger.kernel.org
Subject: [PATCH net v3 5/5] gre: do not read inner frame as erspan metadata in collect_md mode
Date: Wed, 23 Sep 2026 03:52:17 +0000 [thread overview]
Message-ID: <20260923035217.179102-6-edumazet@google.com> (raw)
In-Reply-To: <20260923035217.179102-1-edumazet@google.com>
erspan_rcv() and ip6erspan_rcv() copy the ERSPAN metadata out of the
packet for collect_md tunnels:
pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
sizeof(*ershdr));
...
memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
ERSPAN_V2_MDSIZE);
Both read 8 bytes at 12 bytes from the start of the GRE header, assuming
an ERSPAN version 1 or 2 header was pulled. Because
__iptunnel_pull_header() with ETH_P_TEB linearizes an extra ETH_HLEN (14)
bytes beyond @len, this does not read past skb->tail, but when
erspan_hdr_len(ver) is 0 it copies bytes from the inner Ethernet frame
into md->u.md2. Two ways to get there:
- An ERSPAN type I packet has a 4-byte ETH_P_ERSPAN GRE header and no
ERSPAN header at all, so erspan_rcv() sets ver = 0 and only pulls the
GRE header. It still falls back to a collect_md tunnel through
itn->collect_md_tun, and there the ternary above picks ERSPAN_V2_MDSIZE
and reads 8 bytes of the inner Ethernet header as ERSPAN v2 metadata.
Also check tpi->proto == htons(ETH_P_ERSPAN) in is_erspan_type1() to
match gre_parse_header() so a 4-byte ETH_P_ERSPAN2 frame is not treated
as Type I.
- A packet with an 8-byte GRE header (or IPv6 ERSPAN) and ershdr->ver == 0
is malformed, yet neither erspan_rcv() nor ip6erspan_rcv() validates the
version before using it, so erspan_hdr_len(0) pulls 0 bytes (leaving the
4-byte ERSPAN base header inside the inner frame) and copies inner frame
bytes into md->u.md2. A version above 2 also stores a version that the
transmit side (erspan_fb_xmit(), ip6erspan_tunnel_xmit()) rejects.
Reject a base header whose version is neither 1 nor 2, and skip the
metadata extraction for type I, which has none: ip_tun_rx_dst() hands
out a zeroed option area, so md->version = 0 alone describes it.
Fixes: f989d546a2d5 ("erspan: Add type I version 0 support.")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
net/ipv4/ip_gre.c | 43 +++++++++++++++++++++++++++----------------
net/ipv6/ip6_gre.c | 2 ++
2 files changed, 29 insertions(+), 16 deletions(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 7385d66a94bf49c84bc674ded51ed3f9f5352e28..a00df7bda0012b03aed50fd569cfd0611a67ce9f 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -255,13 +255,13 @@ static void gre_err(struct sk_buff *skb, u32 info)
ipgre_err(skb, info, &tpi);
}
-static bool is_erspan_type1(int gre_hdr_len)
+static bool is_erspan_type1(int gre_hdr_len, __be16 proto)
{
/* Both ERSPAN type I (version 0) and type II (version 1) use
* protocol 0x88BE, but the type I has only 4-byte GRE header,
* while type II has 8-byte.
*/
- return gre_hdr_len == 4;
+ return proto == htons(ETH_P_ERSPAN) && gre_hdr_len == 4;
}
static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
@@ -274,7 +274,6 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
struct ip_tunnel_net *itn;
struct ip_tunnel *tunnel;
const struct iphdr *iph;
- struct erspan_md2 *md2;
int ver;
int len;
@@ -282,7 +281,7 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
itn = net_generic(net, erspan_net_id);
iph = ip_hdr(skb);
- if (is_erspan_type1(gre_hdr_len)) {
+ if (is_erspan_type1(gre_hdr_len, tpi->proto)) {
ver = 0;
__set_bit(IP_TUNNEL_NO_KEY_BIT, flags);
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, flags,
@@ -294,6 +293,9 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
ershdr = (struct erspan_base_hdr *)(skb->data + gre_hdr_len);
ver = ershdr->ver;
+ if (unlikely(ver != 1 && ver != 2))
+ return PACKET_REJECT;
+
iph = ip_hdr(skb);
__set_bit(IP_TUNNEL_KEY_BIT, flags);
tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, flags,
@@ -301,7 +303,7 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
}
if (tunnel) {
- if (is_erspan_type1(gre_hdr_len))
+ if (is_erspan_type1(gre_hdr_len, tpi->proto))
len = gre_hdr_len;
else
len = gre_hdr_len + erspan_hdr_len(ver);
@@ -318,6 +320,7 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
if (tunnel->collect_md) {
struct erspan_metadata *pkt_md, *md;
struct ip_tunnel_info *info;
+ struct erspan_md2 *md2;
unsigned char *gh;
__be64 tun_id;
@@ -334,19 +337,27 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi,
info = &tun_dst->u.tun_info;
info->options_len = sizeof(*md);
- /* skb can be uncloned in __iptunnel_pull_header, so
- * old pkt_md is no longer valid and we need to reset
- * it
- */
- gh = skb_network_header(skb) +
- skb_network_header_len(skb);
- pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
- sizeof(*ershdr));
md = ip_tunnel_info_opts(&tun_dst->u.tun_info);
md->version = ver;
- md2 = &md->u.md2;
- memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
- ERSPAN_V2_MDSIZE);
+
+ /* Type I has no ERSPAN header, thus no metadata to
+ * extract: pkt_md would point into the inner Ethernet
+ * frame just pulled by __iptunnel_pull_header().
+ * ip_tun_rx_dst() zeroed @md for us.
+ */
+ if (!is_erspan_type1(gre_hdr_len, tpi->proto)) {
+ /* skb can be uncloned in __iptunnel_pull_header, so
+ * old pkt_md is no longer valid and we need to reset
+ * it
+ */
+ gh = skb_network_header(skb) +
+ skb_network_header_len(skb);
+ pkt_md = (struct erspan_metadata *)(gh + gre_hdr_len +
+ sizeof(*ershdr));
+ md2 = &md->u.md2;
+ memcpy(md2, pkt_md, ver == 1 ? ERSPAN_V1_MDSIZE :
+ ERSPAN_V2_MDSIZE);
+ }
__set_bit(IP_TUNNEL_ERSPAN_OPT_BIT,
info->key.tun_flags);
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index e61cb10b50dc96295c14aa6da4c9c0fe47151335..774975955747ec4d822b66b8aaaee824c8d331df 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -503,6 +503,8 @@ static int ip6erspan_rcv(struct sk_buff *skb,
ipv6h = ipv6_hdr(skb);
ershdr = (struct erspan_base_hdr *)skb->data;
ver = ershdr->ver;
+ if (unlikely(ver != 1 && ver != 2))
+ return PACKET_REJECT;
tunnel = ip6gre_tunnel_lookup(skb->dev,
&ipv6h->saddr, &ipv6h->daddr, tpi->key,
--
2.55.0.1082.g2b9226bbc0-goog
next prev parent reply other threads:[~2026-09-23 3:52 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-23 3:52 [PATCH net v3 0/5] ip_tunnel, ip_gre: fix header length and validation bugs Eric Dumazet
2026-09-23 3:52 ` [PATCH net v3 1/5] ip_tunnel: do not clear the active encap before validating the new one Eric Dumazet
2026-09-25 6:52 ` netdev-bot+sashiko
2026-09-25 9:27 ` Eric Dumazet
2026-09-23 3:52 ` [PATCH net v3 2/5] ip_gre: validate netlink attributes before changing the tunnel Eric Dumazet
2026-09-25 6:52 ` netdev-bot+sashiko
2026-09-23 3:52 ` [PATCH net v3 3/5] ip_gre: compute tunnel lengths absolutely instead of by delta Eric Dumazet
2026-09-25 6:53 ` netdev-bot+sashiko
2026-09-25 9:28 ` Eric Dumazet
2026-09-23 3:52 ` [PATCH net v3 4/5] ip_gre: recompute erspan header lengths after a change Eric Dumazet
2026-09-25 6:53 ` netdev-bot+sashiko
2026-09-25 9:31 ` Eric Dumazet
2026-09-23 3:52 ` Eric Dumazet [this message]
2026-09-25 6:53 ` [PATCH net v3 5/5] gre: do not read inner frame as erspan metadata in collect_md mode netdev-bot+sashiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260923035217.179102-6-edumazet@google.com \
--to=edumazet@google.com \
--cc=davem@davemloft.net \
--cc=eric.dumazet@gmail.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
--cc=u9012063@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox