Skip to content

Commit 221ddb7

Browse files
committed
xfrm: Support GRO for IPv6 ESP in UDP encapsulation
This patch enables the GRO codepath for IPv6 ESP in UDP encapsulated packets. Decapsulation happens at L2 and saves a full round through the stack for each packet. This is also needed to support HW offload for ESP in UDP encapsulation. Signed-off-by: Steffen Klassert <steffen.klassert@secunet.com> Co-developed-by: Antony Antony <antony.antony@secunet.com> Signed-off-by: Antony Antony <antony.antony@secunet.com> Reviewed-by: Eyal Birger <eyal.birger@gmail.com>
1 parent 172bf00 commit 221ddb7

6 files changed

Lines changed: 92 additions & 20 deletions

File tree

include/net/ipv6_stubs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ struct ipv6_stub {
6060
#if IS_ENABLED(CONFIG_XFRM)
6161
void (*xfrm6_local_rxpmtu)(struct sk_buff *skb, u32 mtu);
6262
int (*xfrm6_udp_encap_rcv)(struct sock *sk, struct sk_buff *skb);
63+
struct sk_buff *(*xfrm6_gro_udp_encap_rcv)(struct sock *sk,
64+
struct list_head *head,
65+
struct sk_buff *skb);
6366
int (*xfrm6_rcv_encap)(struct sk_buff *skb, int nexthdr, __be32 spi,
6467
int encap_type);
6568
#endif

include/net/xfrm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,8 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
17121712
int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb);
17131713
struct sk_buff *xfrm4_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
17141714
struct sk_buff *skb);
1715+
struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
1716+
struct sk_buff *skb);
17151717
int xfrm_user_policy(struct sock *sk, int optname, sockptr_t optval,
17161718
int optlen);
17171719
#else

net/ipv4/udp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2632,6 +2632,8 @@ static void set_xfrm_gro_udp_encap_rcv(__u16 encap_type, unsigned short family,
26322632
if (udp_test_bit(GRO_ENABLED, sk) && encap_type == UDP_ENCAP_ESPINUDP) {
26332633
if (family == AF_INET)
26342634
WRITE_ONCE(udp_sk(sk)->gro_receive, xfrm4_gro_udp_encap_rcv);
2635+
else if (IS_ENABLED(CONFIG_IPV6) && family == AF_INET6)
2636+
WRITE_ONCE(udp_sk(sk)->gro_receive, ipv6_stub->xfrm6_gro_udp_encap_rcv);
26352637
}
26362638
#endif
26372639
}

net/ipv6/af_inet6.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,7 @@ static const struct ipv6_stub ipv6_stub_impl = {
10491049
#if IS_ENABLED(CONFIG_XFRM)
10501050
.xfrm6_local_rxpmtu = xfrm6_local_rxpmtu,
10511051
.xfrm6_udp_encap_rcv = xfrm6_udp_encap_rcv,
1052+
.xfrm6_gro_udp_encap_rcv = xfrm6_gro_udp_encap_rcv,
10521053
.xfrm6_rcv_encap = xfrm6_rcv_encap,
10531054
#endif
10541055
.nd_tbl = &nd_tbl,

net/ipv6/esp6_offload.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ static __u16 esp6_nexthdr_esp_offset(struct ipv6hdr *ipv6_hdr, int nhlen)
3434
int off = sizeof(struct ipv6hdr);
3535
struct ipv6_opt_hdr *exthdr;
3636

37-
if (likely(ipv6_hdr->nexthdr == NEXTHDR_ESP))
37+
/* ESP or ESPINUDP */
38+
if (likely(ipv6_hdr->nexthdr == NEXTHDR_ESP ||
39+
ipv6_hdr->nexthdr == NEXTHDR_UDP))
3840
return offsetof(struct ipv6hdr, nexthdr);
3941

4042
while (off < nhlen) {
@@ -54,10 +56,14 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
5456
int offset = skb_gro_offset(skb);
5557
struct xfrm_offload *xo;
5658
struct xfrm_state *x;
59+
int encap_type = 0;
5760
__be32 seq;
5861
__be32 spi;
5962
int nhoff;
6063

64+
if (NAPI_GRO_CB(skb)->proto == IPPROTO_UDP)
65+
encap_type = UDP_ENCAP_ESPINUDP;
66+
6167
if (!pskb_pull(skb, offset))
6268
return NULL;
6369

@@ -104,7 +110,7 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head,
104110

105111
/* We don't need to handle errors from xfrm_input, it does all
106112
* the error handling and frees the resources on error. */
107-
xfrm_input(skb, IPPROTO_ESP, spi, 0);
113+
xfrm_input(skb, IPPROTO_ESP, spi, encap_type);
108114

109115
return ERR_PTR(-EINPROGRESS);
110116
out_reset:

net/ipv6/xfrm6_input.c

Lines changed: 76 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
#include <linux/netfilter_ipv6.h>
1717
#include <net/ipv6.h>
1818
#include <net/xfrm.h>
19+
#include <net/protocol.h>
20+
#include <net/gro.h>
1921

2022
int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi,
2123
struct ip6_tnl *t)
@@ -67,14 +69,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
6769
return 0;
6870
}
6971

70-
/* If it's a keepalive packet, then just eat it.
71-
* If it's an encapsulated packet, then pass it to the
72-
* IPsec xfrm input.
73-
* Returns 0 if skb passed to xfrm or was dropped.
74-
* Returns >0 if skb should be passed to UDP.
75-
* Returns <0 if skb should be resubmitted (-ret is protocol)
76-
*/
77-
int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
72+
static int __xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb, bool pull)
7873
{
7974
struct udp_sock *up = udp_sk(sk);
8075
struct udphdr *uh;
@@ -109,7 +104,7 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
109104
case UDP_ENCAP_ESPINUDP:
110105
/* Check if this is a keepalive packet. If so, eat it. */
111106
if (len == 1 && udpdata[0] == 0xff) {
112-
goto drop;
107+
return -EINVAL;
113108
} else if (len > sizeof(struct ip_esp_hdr) && udpdata32[0] != 0) {
114109
/* ESP Packet without Non-ESP header */
115110
len = sizeof(struct udphdr);
@@ -120,7 +115,7 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
120115
case UDP_ENCAP_ESPINUDP_NON_IKE:
121116
/* Check if this is a keepalive packet. If so, eat it. */
122117
if (len == 1 && udpdata[0] == 0xff) {
123-
goto drop;
118+
return -EINVAL;
124119
} else if (len > 2 * sizeof(u32) + sizeof(struct ip_esp_hdr) &&
125120
udpdata32[0] == 0 && udpdata32[1] == 0) {
126121

@@ -138,31 +133,94 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
138133
* protocol to ESP, and then call into the transform receiver.
139134
*/
140135
if (skb_unclone(skb, GFP_ATOMIC))
141-
goto drop;
136+
return -EINVAL;
142137

143138
/* Now we can update and verify the packet length... */
144139
ip6h = ipv6_hdr(skb);
145140
ip6h->payload_len = htons(ntohs(ip6h->payload_len) - len);
146141
if (skb->len < ip6hlen + len) {
147142
/* packet is too small!?! */
148-
goto drop;
143+
return -EINVAL;
149144
}
150145

151146
/* pull the data buffer up to the ESP header and set the
152147
* transport header to point to ESP. Keep UDP on the stack
153148
* for later.
154149
*/
155-
__skb_pull(skb, len);
156-
skb_reset_transport_header(skb);
150+
if (pull) {
151+
__skb_pull(skb, len);
152+
skb_reset_transport_header(skb);
153+
} else {
154+
skb_set_transport_header(skb, len);
155+
}
157156

158157
/* process ESP */
159-
return xfrm6_rcv_encap(skb, IPPROTO_ESP, 0, encap_type);
160-
161-
drop:
162-
kfree_skb(skb);
163158
return 0;
164159
}
165160

161+
/* If it's a keepalive packet, then just eat it.
162+
* If it's an encapsulated packet, then pass it to the
163+
* IPsec xfrm input.
164+
* Returns 0 if skb passed to xfrm or was dropped.
165+
* Returns >0 if skb should be passed to UDP.
166+
* Returns <0 if skb should be resubmitted (-ret is protocol)
167+
*/
168+
int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
169+
{
170+
int ret;
171+
172+
ret = __xfrm6_udp_encap_rcv(sk, skb, true);
173+
if (!ret)
174+
return xfrm6_rcv_encap(skb, IPPROTO_ESP, 0,
175+
udp_sk(sk)->encap_type);
176+
177+
if (ret < 0) {
178+
kfree_skb(skb);
179+
return 0;
180+
}
181+
182+
return ret;
183+
}
184+
185+
struct sk_buff *xfrm6_gro_udp_encap_rcv(struct sock *sk, struct list_head *head,
186+
struct sk_buff *skb)
187+
{
188+
int offset = skb_gro_offset(skb);
189+
const struct net_offload *ops;
190+
struct sk_buff *pp = NULL;
191+
int ret;
192+
193+
offset = offset - sizeof(struct udphdr);
194+
195+
if (!pskb_pull(skb, offset))
196+
return NULL;
197+
198+
rcu_read_lock();
199+
ops = rcu_dereference(inet6_offloads[IPPROTO_ESP]);
200+
if (!ops || !ops->callbacks.gro_receive)
201+
goto out;
202+
203+
ret = __xfrm6_udp_encap_rcv(sk, skb, false);
204+
if (ret)
205+
goto out;
206+
207+
skb_push(skb, offset);
208+
NAPI_GRO_CB(skb)->proto = IPPROTO_UDP;
209+
210+
pp = call_gro_receive(ops->callbacks.gro_receive, head, skb);
211+
rcu_read_unlock();
212+
213+
return pp;
214+
215+
out:
216+
rcu_read_unlock();
217+
skb_push(skb, offset);
218+
NAPI_GRO_CB(skb)->same_flow = 0;
219+
NAPI_GRO_CB(skb)->flush = 1;
220+
221+
return NULL;
222+
}
223+
166224
int xfrm6_rcv_tnl(struct sk_buff *skb, struct ip6_tnl *t)
167225
{
168226
return xfrm6_rcv_spi(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],

0 commit comments

Comments
 (0)