Skip to content

Commit e3fd057

Browse files
tobiasbrunnerklassert
authored andcommitted
xfrm: Fix UDP GRO handling for some corner cases
This fixes an issue that's caused if there is a mismatch between the data offset in the GRO header and the length fields in the regular sk_buff due to the pskb_pull()/skb_push() calls. That's because the UDP GRO layer stripped off the UDP header via skb_gro_pull() already while the UDP header was explicitly not pulled/pushed in this function. For example, an IKE packet that triggered this had len=data_len=1268 and the data_offset in the GRO header was 28 (IPv4 + UDP). So pskb_pull() was called with an offset of 28-8=20, which reduced len to 1248 and via pskb_may_pull() and __pskb_pull_tail() it also set data_len to 1248. As the ESP offload module was not loaded, the function bailed out and called skb_push(), which restored len to 1268, however, data_len remained at 1248. So while skb_headlen() was 0 before, it was now 20. The latter caused a difference of 8 instead of 28 (or 0 if pskb_pull()/skb_push() was called with the complete GRO data_offset) in gro_try_pull_from_frag0() that triggered a call to gro_pull_from_frag0() that corrupted the packet. This change uses a more GRO-like approach seen in other GRO receivers via skb_gro_header() to just read the actual data we are interested in and does not try to "restore" the UDP header at this point to call the existing function. If the offload module is not loaded, it immediately bails out, otherwise, it only does a quick check to see if the packet is an IKE or keepalive packet instead of calling the existing function. Fixes: 172bf00 ("xfrm: Support GRO for IPv4 ESP in UDP encapsulation") Fixes: 221ddb7 ("xfrm: Support GRO for IPv6 ESP in UDP encapsulation") Signed-off-by: Tobias Brunner <[email protected]> Signed-off-by: Steffen Klassert <[email protected]>
1 parent 0283636 commit e3fd057

File tree

2 files changed

+20
-16
lines changed

2 files changed

+20
-16
lines changed

net/ipv4/xfrm4_input.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,23 +182,26 @@ struct sk_buff *xfrm4_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
182182
int offset = skb_gro_offset(skb);
183183
const struct net_offload *ops;
184184
struct sk_buff *pp = NULL;
185-
int ret;
186-
187-
offset = offset - sizeof(struct udphdr);
185+
int len, dlen;
186+
__u8 *udpdata;
187+
__be32 *udpdata32;
188188

189-
if (!pskb_pull(skb, offset))
189+
len = skb->len - offset;
190+
dlen = offset + min(len, 8);
191+
udpdata = skb_gro_header(skb, dlen, offset);
192+
udpdata32 = (__be32 *)udpdata;
193+
if (unlikely(!udpdata))
190194
return NULL;
191195

192196
rcu_read_lock();
193197
ops = rcu_dereference(inet_offloads[IPPROTO_ESP]);
194198
if (!ops || !ops->callbacks.gro_receive)
195199
goto out;
196200

197-
ret = __xfrm4_udp_encap_rcv(sk, skb, false);
198-
if (ret)
201+
/* check if it is a keepalive or IKE packet */
202+
if (len <= sizeof(struct ip_esp_hdr) || udpdata32[0] == 0)
199203
goto out;
200204

201-
skb_push(skb, offset);
202205
NAPI_GRO_CB(skb)->proto = IPPROTO_UDP;
203206

204207
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
@@ -208,7 +211,6 @@ struct sk_buff *xfrm4_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
208211

209212
out:
210213
rcu_read_unlock();
211-
skb_push(skb, offset);
212214
NAPI_GRO_CB(skb)->same_flow = 0;
213215
NAPI_GRO_CB(skb)->flush = 1;
214216

net/ipv6/xfrm6_input.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,26 +179,29 @@ struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
179179
int offset = skb_gro_offset(skb);
180180
const struct net_offload *ops;
181181
struct sk_buff *pp = NULL;
182-
int ret;
182+
int len, dlen;
183+
__u8 *udpdata;
184+
__be32 *udpdata32;
183185

184186
if (skb->protocol == htons(ETH_P_IP))
185187
return xfrm4_gro_udp_encap_rcv(sk, head, skb);
186188

187-
offset = offset - sizeof(struct udphdr);
188-
189-
if (!pskb_pull(skb, offset))
189+
len = skb->len - offset;
190+
dlen = offset + min(len, 8);
191+
udpdata = skb_gro_header(skb, dlen, offset);
192+
udpdata32 = (__be32 *)udpdata;
193+
if (unlikely(!udpdata))
190194
return NULL;
191195

192196
rcu_read_lock();
193197
ops = rcu_dereference(inet6_offloads[IPPROTO_ESP]);
194198
if (!ops || !ops->callbacks.gro_receive)
195199
goto out;
196200

197-
ret = __xfrm6_udp_encap_rcv(sk, skb, false);
198-
if (ret)
201+
/* check if it is a keepalive or IKE packet */
202+
if (len <= sizeof(struct ip_esp_hdr) || udpdata32[0] == 0)
199203
goto out;
200204

201-
skb_push(skb, offset);
202205
NAPI_GRO_CB(skb)->proto = IPPROTO_UDP;
203206

204207
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
@@ -208,7 +211,6 @@ struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
208211

209212
out:
210213
rcu_read_unlock();
211-
skb_push(skb, offset);
212214
NAPI_GRO_CB(skb)->same_flow = 0;
213215
NAPI_GRO_CB(skb)->flush = 1;
214216

0 commit comments

Comments
 (0)