]>
Commit | Line | Data |
---|---|---|
c003a296 GKH |
1 | From 3d010c8031e39f5fa1e8b13ada77e0321091011f Mon Sep 17 00:00:00 2001 |
2 | From: Antoine Tenart <atenart@kernel.org> | |
3 | Date: Tue, 26 Mar 2024 12:33:58 +0100 | |
4 | Subject: udp: do not accept non-tunnel GSO skbs landing in a tunnel | |
5 | ||
6 | From: Antoine Tenart <atenart@kernel.org> | |
7 | ||
8 | commit 3d010c8031e39f5fa1e8b13ada77e0321091011f upstream. | |
9 | ||
10 | When rx-udp-gro-forwarding is enabled UDP packets might be GROed when | |
11 | being forwarded. If such packets might land in a tunnel this can cause | |
12 | various issues and udp_gro_receive makes sure this isn't the case by | |
13 | looking for a matching socket. This is performed in | |
14 | udp4/6_gro_lookup_skb but only in the current netns. This is an issue | |
15 | with tunneled packets when the endpoint is in another netns. In such | |
16 | cases the packets will be GROed at the UDP level, which leads to various | |
17 | issues later on. The same thing can happen with rx-gro-list. | |
18 | ||
19 | We saw this with geneve packets being GROed at the UDP level. In such | |
20 | case gso_size is set; later the packet goes through the geneve rx path, | |
21 | the geneve header is pulled, the offset are adjusted and frag_list skbs | |
22 | are not adjusted with regard to geneve. When those skbs hit | |
23 | skb_fragment, it will misbehave. Different outcomes are possible | |
24 | depending on what the GROed skbs look like; from corrupted packets to | |
25 | kernel crashes. | |
26 | ||
27 | One example is a BUG_ON[1] triggered in skb_segment while processing the | |
28 | frag_list. Because gso_size is wrong (geneve header was pulled) | |
29 | skb_segment thinks there is "geneve header size" of data in frag_list, | |
30 | although it's in fact the next packet. The BUG_ON itself has nothing to | |
31 | do with the issue. This is only one of the potential issues. | |
32 | ||
33 | Looking up for a matching socket in udp_gro_receive is fragile: the | |
34 | lookup could be extended to all netns (not speaking about performances) | |
35 | but nothing prevents those packets from being modified in between and we | |
36 | could still not find a matching socket. It's OK to keep the current | |
37 | logic there as it should cover most cases but we also need to make sure | |
38 | we handle tunnel packets being GROed too early. | |
39 | ||
40 | This is done by extending the checks in udp_unexpected_gso: GSO packets | |
41 | lacking the SKB_GSO_UDP_TUNNEL/_CSUM bits and landing in a tunnel must | |
42 | be segmented. | |
43 | ||
44 | [1] kernel BUG at net/core/skbuff.c:4408! | |
45 | RIP: 0010:skb_segment+0xd2a/0xf70 | |
46 | __udp_gso_segment+0xaa/0x560 | |
47 | ||
48 | Fixes: 9fd1ff5d2ac7 ("udp: Support UDP fraglist GRO/GSO.") | |
49 | Fixes: 36707061d6ba ("udp: allow forwarding of plain (non-fraglisted) UDP GRO packets") | |
50 | Signed-off-by: Antoine Tenart <atenart@kernel.org> | |
51 | Reviewed-by: Willem de Bruijn <willemb@google.com> | |
52 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
53 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
54 | --- | |
55 | include/linux/udp.h | 28 ++++++++++++++++++++++++++++ | |
56 | net/ipv4/udp.c | 7 +++++++ | |
57 | net/ipv4/udp_offload.c | 6 ++++-- | |
58 | net/ipv6/udp.c | 2 +- | |
59 | 4 files changed, 40 insertions(+), 3 deletions(-) | |
60 | ||
61 | --- a/include/linux/udp.h | |
62 | +++ b/include/linux/udp.h | |
63 | @@ -140,6 +140,24 @@ static inline void udp_cmsg_recv(struct | |
64 | } | |
65 | } | |
66 | ||
67 | +DECLARE_STATIC_KEY_FALSE(udp_encap_needed_key); | |
68 | +#if IS_ENABLED(CONFIG_IPV6) | |
69 | +DECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key); | |
70 | +#endif | |
71 | + | |
72 | +static inline bool udp_encap_needed(void) | |
73 | +{ | |
74 | + if (static_branch_unlikely(&udp_encap_needed_key)) | |
75 | + return true; | |
76 | + | |
77 | +#if IS_ENABLED(CONFIG_IPV6) | |
78 | + if (static_branch_unlikely(&udpv6_encap_needed_key)) | |
79 | + return true; | |
80 | +#endif | |
81 | + | |
82 | + return false; | |
83 | +} | |
84 | + | |
85 | static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb) | |
86 | { | |
87 | if (!skb_is_gso(skb)) | |
88 | @@ -153,6 +171,16 @@ static inline bool udp_unexpected_gso(st | |
89 | !udp_test_bit(ACCEPT_FRAGLIST, sk)) | |
90 | return true; | |
91 | ||
92 | + /* GSO packets lacking the SKB_GSO_UDP_TUNNEL/_CSUM bits might still | |
93 | + * land in a tunnel as the socket check in udp_gro_receive cannot be | |
94 | + * foolproof. | |
95 | + */ | |
96 | + if (udp_encap_needed() && | |
97 | + READ_ONCE(udp_sk(sk)->encap_rcv) && | |
98 | + !(skb_shinfo(skb)->gso_type & | |
99 | + (SKB_GSO_UDP_TUNNEL | SKB_GSO_UDP_TUNNEL_CSUM))) | |
100 | + return true; | |
101 | + | |
102 | return false; | |
103 | } | |
104 | ||
105 | --- a/net/ipv4/udp.c | |
106 | +++ b/net/ipv4/udp.c | |
107 | @@ -584,6 +584,13 @@ static inline bool __udp_is_mcast_sock(s | |
108 | } | |
109 | ||
110 | DEFINE_STATIC_KEY_FALSE(udp_encap_needed_key); | |
111 | +EXPORT_SYMBOL(udp_encap_needed_key); | |
112 | + | |
113 | +#if IS_ENABLED(CONFIG_IPV6) | |
114 | +DEFINE_STATIC_KEY_FALSE(udpv6_encap_needed_key); | |
115 | +EXPORT_SYMBOL(udpv6_encap_needed_key); | |
116 | +#endif | |
117 | + | |
118 | void udp_encap_enable(void) | |
119 | { | |
120 | static_branch_inc(&udp_encap_needed_key); | |
121 | --- a/net/ipv4/udp_offload.c | |
122 | +++ b/net/ipv4/udp_offload.c | |
123 | @@ -552,8 +552,10 @@ struct sk_buff *udp_gro_receive(struct l | |
124 | unsigned int off = skb_gro_offset(skb); | |
125 | int flush = 1; | |
126 | ||
127 | - /* we can do L4 aggregation only if the packet can't land in a tunnel | |
128 | - * otherwise we could corrupt the inner stream | |
129 | + /* We can do L4 aggregation only if the packet can't land in a tunnel | |
130 | + * otherwise we could corrupt the inner stream. Detecting such packets | |
131 | + * cannot be foolproof and the aggregation might still happen in some | |
132 | + * cases. Such packets should be caught in udp_unexpected_gso later. | |
133 | */ | |
134 | NAPI_GRO_CB(skb)->is_flist = 0; | |
135 | if (!sk || !udp_sk(sk)->gro_receive) { | |
136 | --- a/net/ipv6/udp.c | |
137 | +++ b/net/ipv6/udp.c | |
138 | @@ -450,7 +450,7 @@ csum_copy_err: | |
139 | goto try_again; | |
140 | } | |
141 | ||
142 | -DEFINE_STATIC_KEY_FALSE(udpv6_encap_needed_key); | |
143 | +DECLARE_STATIC_KEY_FALSE(udpv6_encap_needed_key); | |
144 | void udpv6_encap_enable(void) | |
145 | { | |
146 | static_branch_inc(&udpv6_encap_needed_key); |