Skip to content

Commit 31c417c

Browse files
peilin-yedavem330
authored andcommitted
ip_gre, ip6_gre: Fix race condition on o_seqno in collect_md mode
As pointed out by Jakub Kicinski, currently using TUNNEL_SEQ in collect_md mode is racy for [IP6]GRE[TAP] devices. Consider the following sequence of events: 1. An [IP6]GRE[TAP] device is created in collect_md mode using "ip link add ... external". "ip" ignores "[o]seq" if "external" is specified, so TUNNEL_SEQ is off, and the device is marked as NETIF_F_LLTX (i.e. it uses lockless TX); 2. Someone sets TUNNEL_SEQ on outgoing skb's, using e.g. bpf_skb_set_tunnel_key() in an eBPF program attached to this device; 3. gre_fb_xmit() or __gre6_xmit() processes these skb's: gre_build_header(skb, tun_hlen, flags, protocol, tunnel_id_to_key32(tun_info->key.tun_id), (flags & TUNNEL_SEQ) ? htonl(tunnel->o_seqno++) : 0); ^^^^^^^^^^^^^^^^^ Since we are not using the TX lock (&txq->_xmit_lock), multiple CPUs may try to do this tunnel->o_seqno++ in parallel, which is racy. Fix it by making o_seqno atomic_t. As mentioned by Eric Dumazet in commit b790e01 ("ip_gre: lockless xmit"), making o_seqno atomic_t increases "chance for packets being out of order at receiver" when NETIF_F_LLTX is on. Maybe a better fix would be: 1. Do not ignore "oseq" in external mode. Users MUST specify "oseq" if they want the kernel to allow sequencing of outgoing packets; 2. Reject all outgoing TUNNEL_SEQ packets if the device was not created with "oseq". Unfortunately, that would break userspace. We could now make [IP6]GRE[TAP] devices always NETIF_F_LLTX, but let us do it in separate patches to keep this fix minimal. Suggested-by: Jakub Kicinski <kuba@kernel.org> Fixes: 77a5196 ("gre: add sequence number for collect md mode.") Signed-off-by: Peilin Ye <peilin.ye@bytedance.com> Acked-by: William Tu <u9012063@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent fde98ae commit 31c417c

4 files changed

Lines changed: 9 additions & 8 deletions

File tree

include/net/ip6_tunnel.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ struct ip6_tnl {
5858

5959
/* These fields used only by GRE */
6060
__u32 i_seqno; /* The last seen seqno */
61-
__u32 o_seqno; /* The last output seqno */
61+
atomic_t o_seqno; /* The last output seqno */
6262
int hlen; /* tun_hlen + encap_hlen */
6363
int tun_hlen; /* Precalculated header length */
6464
int encap_hlen; /* Encap header length (FOU,GUE) */

include/net/ip_tunnels.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ struct ip_tunnel {
116116

117117
/* These four fields used only by GRE */
118118
u32 i_seqno; /* The last seen seqno */
119-
u32 o_seqno; /* The last output seqno */
119+
atomic_t o_seqno; /* The last output seqno */
120120
int tun_hlen; /* Precalculated header length */
121121

122122
/* These four fields used only by ERSPAN */

net/ipv4/ip_gre.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
464464
/* Push GRE header. */
465465
gre_build_header(skb, tunnel->tun_hlen,
466466
flags, proto, tunnel->parms.o_key,
467-
(flags & TUNNEL_SEQ) ? htonl(tunnel->o_seqno++) : 0);
467+
(flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno)) : 0);
468468

469469
ip_tunnel_xmit(skb, dev, tnl_params, tnl_params->protocol);
470470
}
@@ -502,7 +502,7 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev,
502502
(TUNNEL_CSUM | TUNNEL_KEY | TUNNEL_SEQ);
503503
gre_build_header(skb, tunnel_hlen, flags, proto,
504504
tunnel_id_to_key32(tun_info->key.tun_id),
505-
(flags & TUNNEL_SEQ) ? htonl(tunnel->o_seqno++) : 0);
505+
(flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno)) : 0);
506506

507507
ip_md_tunnel_xmit(skb, dev, IPPROTO_GRE, tunnel_hlen);
508508

@@ -579,7 +579,7 @@ static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev)
579579
}
580580

581581
gre_build_header(skb, 8, TUNNEL_SEQ,
582-
proto, 0, htonl(tunnel->o_seqno++));
582+
proto, 0, htonl(atomic_fetch_inc(&tunnel->o_seqno)));
583583

584584
ip_md_tunnel_xmit(skb, dev, IPPROTO_GRE, tunnel_hlen);
585585

net/ipv6/ip6_gre.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -766,7 +766,7 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
766766
gre_build_header(skb, tun_hlen,
767767
flags, protocol,
768768
tunnel_id_to_key32(tun_info->key.tun_id),
769-
(flags & TUNNEL_SEQ) ? htonl(tunnel->o_seqno++)
769+
(flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
770770
: 0);
771771

772772
} else {
@@ -777,7 +777,8 @@ static netdev_tx_t __gre6_xmit(struct sk_buff *skb,
777777

778778
gre_build_header(skb, tunnel->tun_hlen, flags,
779779
protocol, tunnel->parms.o_key,
780-
(flags & TUNNEL_SEQ) ? htonl(tunnel->o_seqno++) : 0);
780+
(flags & TUNNEL_SEQ) ? htonl(atomic_fetch_inc(&tunnel->o_seqno))
781+
: 0);
781782
}
782783

783784
return ip6_tnl_xmit(skb, dev, dsfield, fl6, encap_limit, pmtu,
@@ -1055,7 +1056,7 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
10551056
/* Push GRE header. */
10561057
proto = (t->parms.erspan_ver == 1) ? htons(ETH_P_ERSPAN)
10571058
: htons(ETH_P_ERSPAN2);
1058-
gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(t->o_seqno++));
1059+
gre_build_header(skb, 8, TUNNEL_SEQ, proto, 0, htonl(atomic_fetch_inc(&t->o_seqno)));
10591060

10601061
/* TooBig packet may have updated dst->dev's mtu */
10611062
if (!t->parms.collect_md && dst && dst_mtu(dst) > dst->dev->mtu)

0 commit comments

Comments
 (0)