Skip to content

Commit d5b60c6

Browse files
committed
Merge branch 'Support IPsec crypto offload for IPv6 ESP and IPv4 UDP-encapsulated ESP data paths'
Mike Yu says: ==================== Currently, IPsec crypto offload is enabled for GRO code path. However, there are other code paths where the XFRM stack is involved; for example, IPv6 ESP packets handled by xfrm6_esp_rcv() in ESP layer, and IPv4 UDP-encapsulated ESP packets handled by udp_rcv() in UDP layer. This patchset extends the crypto offload support to cover these two cases. This is useful for devices with traffic accounting (e.g., Android), where GRO can lead to inaccurate accounting on the underlying network. For example, VPN traffic might not be counted on the wifi network interface wlan0 if the packets are handled in GRO code path before entering the network stack for accounting. Below is the RX data path scenario the crypto offload can be applied to. +-----------+ +-------+ | HW Driver |-->| wlan0 |--------+ +-----------+ +-------+ | v +---------------+ +------+ +------>| Network Stack |-->| Apps | | +---------------+ +------+ | | | v +--------+ +------------+ | ipsec1 |<--| XFRM Stack | +--------+ +------------+ ==================== Signed-off-by: Steffen Klassert <[email protected]>
2 parents f531d13 + 447bc4b commit d5b60c6

File tree

5 files changed

+32
-7
lines changed

5 files changed

+32
-7
lines changed

net/ipv4/esp4.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
349349
{
350350
struct udphdr *uh;
351351
unsigned int len;
352+
struct xfrm_offload *xo = xfrm_offload(skb);
352353

353354
len = skb->len + esp->tailen - skb_transport_offset(skb);
354355
if (len + sizeof(struct iphdr) > IP_MAX_MTU)
@@ -360,7 +361,12 @@ static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb,
360361
uh->len = htons(len);
361362
uh->check = 0;
362363

363-
*skb_mac_header(skb) = IPPROTO_UDP;
364+
/* For IPv4 ESP with UDP encapsulation, if xo is not null, the skb is in the crypto offload
365+
* data path, which means that esp_output_udp_encap is called outside of the XFRM stack.
366+
* In this case, the mac header doesn't point to the IPv4 protocol field, so don't set it.
367+
*/
368+
if (!xo || encap_type != UDP_ENCAP_ESPINUDP)
369+
*skb_mac_header(skb) = IPPROTO_UDP;
364370

365371
return (struct ip_esp_hdr *)(uh + 1);
366372
}

net/ipv4/esp4_offload.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
264264
struct esp_info esp;
265265
bool hw_offload = true;
266266
__u32 seq;
267+
int encap_type = 0;
267268

268269
esp.inplace = true;
269270

@@ -296,8 +297,10 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
296297

297298
esp.esph = ip_esp_hdr(skb);
298299

300+
if (x->encap)
301+
encap_type = x->encap->encap_type;
299302

300-
if (!hw_offload || !skb_is_gso(skb)) {
303+
if (!hw_offload || !skb_is_gso(skb) || (hw_offload && encap_type == UDP_ENCAP_ESPINUDP)) {
301304
esp.nfrags = esp_output_head(x, skb, &esp);
302305
if (esp.nfrags < 0)
303306
return esp.nfrags;
@@ -324,6 +327,18 @@ static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb, netdev_features_
324327

325328
esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));
326329

330+
if (hw_offload && encap_type == UDP_ENCAP_ESPINUDP) {
331+
/* In the XFRM stack, the encapsulation protocol is set to iphdr->protocol by
332+
* setting *skb_mac_header(skb) (see esp_output_udp_encap()) where skb->mac_header
333+
* points to iphdr->protocol (see xfrm4_tunnel_encap_add()).
334+
* However, in esp_xmit(), skb->mac_header doesn't point to iphdr->protocol.
335+
* Therefore, the protocol field needs to be corrected.
336+
*/
337+
ip_hdr(skb)->protocol = IPPROTO_UDP;
338+
339+
esph->seq_no = htonl(seq);
340+
}
341+
327342
ip_hdr(skb)->tot_len = htons(skb->len);
328343
ip_send_check(ip_hdr(skb));
329344

net/xfrm/xfrm_device.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,9 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x,
261261

262262
is_packet_offload = xuo->flags & XFRM_OFFLOAD_PACKET;
263263

264-
/* We don't yet support UDP encapsulation and TFC padding. */
265-
if ((!is_packet_offload && x->encap) || x->tfcpad) {
266-
NL_SET_ERR_MSG(extack, "Encapsulation and TFC padding can't be offloaded");
264+
/* We don't yet support TFC padding. */
265+
if (x->tfcpad) {
266+
NL_SET_ERR_MSG(extack, "TFC padding can't be offloaded");
267267
return -EINVAL;
268268
}
269269

net/xfrm/xfrm_input.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,8 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
471471
struct xfrm_offload *xo = xfrm_offload(skb);
472472
struct sec_path *sp;
473473

474-
if (encap_type < 0 || (xo && xo->flags & XFRM_GRO)) {
474+
if (encap_type < 0 || (xo && (xo->flags & XFRM_GRO || encap_type == 0 ||
475+
encap_type == UDP_ENCAP_ESPINUDP))) {
475476
x = xfrm_input_state(skb);
476477

477478
if (unlikely(x->dir && x->dir != XFRM_SA_DIR_IN)) {

net/xfrm/xfrm_policy.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3718,12 +3718,15 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
37183718
pol = xfrm_in_fwd_icmp(skb, &fl, family, if_id);
37193719

37203720
if (!pol) {
3721+
const bool is_crypto_offload = sp &&
3722+
(xfrm_input_state(skb)->xso.type == XFRM_DEV_OFFLOAD_CRYPTO);
3723+
37213724
if (net->xfrm.policy_default[dir] == XFRM_USERPOLICY_BLOCK) {
37223725
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
37233726
return 0;
37243727
}
37253728

3726-
if (sp && secpath_has_nontransport(sp, 0, &xerr_idx)) {
3729+
if (sp && secpath_has_nontransport(sp, 0, &xerr_idx) && !is_crypto_offload) {
37273730
xfrm_secpath_reject(xerr_idx, skb, &fl);
37283731
XFRM_INC_STATS(net, LINUX_MIB_XFRMINNOPOLS);
37293732
return 0;

0 commit comments

Comments
 (0)