Blame view

net/ipv6/esp6_offload.c 7.46 KB
7785bba29   Steffen Klassert   esp: Add a softwa...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
   * IPV6 GSO/GRO offload support
   * Linux INET implementation
   *
   * Copyright (C) 2016 secunet Security Networks AG
   * Author: Steffen Klassert <steffen.klassert@secunet.com>
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms and conditions of the GNU General Public License,
   * version 2, as published by the Free Software Foundation.
   *
   * ESP GRO support
   */
  
  #include <linux/skbuff.h>
  #include <linux/init.h>
  #include <net/protocol.h>
  #include <crypto/aead.h>
  #include <crypto/authenc.h>
  #include <linux/err.h>
  #include <linux/module.h>
  #include <net/ip.h>
  #include <net/xfrm.h>
  #include <net/esp.h>
  #include <linux/scatterlist.h>
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <net/ip6_route.h>
  #include <net/ipv6.h>
  #include <linux/icmpv6.h>
ca3a1b856   Yossi Kuperman   esp6_offload: Fix...
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  static __u16 esp6_nexthdr_esp_offset(struct ipv6hdr *ipv6_hdr, int nhlen)
  {
  	int off = sizeof(struct ipv6hdr);
  	struct ipv6_opt_hdr *exthdr;
  
  	if (likely(ipv6_hdr->nexthdr == NEXTHDR_ESP))
  		return offsetof(struct ipv6hdr, nexthdr);
  
  	while (off < nhlen) {
  		exthdr = (void *)ipv6_hdr + off;
  		if (exthdr->nexthdr == NEXTHDR_ESP)
  			return off;
  
  		off += ipv6_optlen(exthdr);
  	}
  
  	return 0;
  }
7785bba29   Steffen Klassert   esp: Add a softwa...
50
51
52
53
54
55
56
57
  static struct sk_buff **esp6_gro_receive(struct sk_buff **head,
  					 struct sk_buff *skb)
  {
  	int offset = skb_gro_offset(skb);
  	struct xfrm_offload *xo;
  	struct xfrm_state *x;
  	__be32 seq;
  	__be32 spi;
ca3a1b856   Yossi Kuperman   esp6_offload: Fix...
58
  	int nhoff;
7785bba29   Steffen Klassert   esp: Add a softwa...
59
  	int err;
201941360   Steffen Klassert   esp: Fix GRO when...
60
61
  	if (!pskb_pull(skb, offset))
  		return NULL;
7785bba29   Steffen Klassert   esp: Add a softwa...
62
63
64
  
  	if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0)
  		goto out;
bcd1f8a45   Steffen Klassert   xfrm: Prepare the...
65
66
67
68
69
  	xo = xfrm_offload(skb);
  	if (!xo || !(xo->flags & CRYPTO_DONE)) {
  		err = secpath_set(skb);
  		if (err)
  			goto out;
7785bba29   Steffen Klassert   esp: Add a softwa...
70

bcd1f8a45   Steffen Klassert   xfrm: Prepare the...
71
72
  		if (skb->sp->len == XFRM_MAX_DEPTH)
  			goto out;
7785bba29   Steffen Klassert   esp: Add a softwa...
73

bcd1f8a45   Steffen Klassert   xfrm: Prepare the...
74
75
76
77
78
  		x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
  				      (xfrm_address_t *)&ipv6_hdr(skb)->daddr,
  				      spi, IPPROTO_ESP, AF_INET6);
  		if (!x)
  			goto out;
7785bba29   Steffen Klassert   esp: Add a softwa...
79

bcd1f8a45   Steffen Klassert   xfrm: Prepare the...
80
81
  		skb->sp->xvec[skb->sp->len++] = x;
  		skb->sp->olen++;
7785bba29   Steffen Klassert   esp: Add a softwa...
82

bcd1f8a45   Steffen Klassert   xfrm: Prepare the...
83
84
85
86
87
  		xo = xfrm_offload(skb);
  		if (!xo) {
  			xfrm_state_put(x);
  			goto out;
  		}
7785bba29   Steffen Klassert   esp: Add a softwa...
88
  	}
bcd1f8a45   Steffen Klassert   xfrm: Prepare the...
89

7785bba29   Steffen Klassert   esp: Add a softwa...
90
  	xo->flags |= XFRM_GRO;
ca3a1b856   Yossi Kuperman   esp6_offload: Fix...
91
92
93
94
95
  	nhoff = esp6_nexthdr_esp_offset(ipv6_hdr(skb), offset);
  	if (!nhoff)
  		goto out;
  
  	IP6CB(skb)->nhoff = nhoff;
7785bba29   Steffen Klassert   esp: Add a softwa...
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
  	XFRM_SPI_SKB_CB(skb)->family = AF_INET6;
  	XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
  	XFRM_SPI_SKB_CB(skb)->seq = seq;
  
  	/* We don't need to handle errors from xfrm_input, it does all
  	 * the error handling and frees the resources on error. */
  	xfrm_input(skb, IPPROTO_ESP, spi, -2);
  
  	return ERR_PTR(-EINPROGRESS);
  out:
  	skb_push(skb, offset);
  	NAPI_GRO_CB(skb)->same_flow = 0;
  	NAPI_GRO_CB(skb)->flush = 1;
  
  	return NULL;
  }
7862b4058   Steffen Klassert   esp: Add gso hand...
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
  static void esp6_gso_encap(struct xfrm_state *x, struct sk_buff *skb)
  {
  	struct ip_esp_hdr *esph;
  	struct ipv6hdr *iph = ipv6_hdr(skb);
  	struct xfrm_offload *xo = xfrm_offload(skb);
  	int proto = iph->nexthdr;
  
  	skb_push(skb, -skb_network_offset(skb));
  	esph = ip_esp_hdr(skb);
  	*skb_mac_header(skb) = IPPROTO_ESP;
  
  	esph->spi = x->id.spi;
  	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
  
  	xo->proto = proto;
  }
  
  static struct sk_buff *esp6_gso_segment(struct sk_buff *skb,
  				        netdev_features_t features)
  {
  	__u32 seq;
  	int err = 0;
  	struct sk_buff *skb2;
  	struct xfrm_state *x;
  	struct ip_esp_hdr *esph;
  	struct crypto_aead *aead;
  	struct sk_buff *segs = ERR_PTR(-EINVAL);
  	netdev_features_t esp_features = features;
  	struct xfrm_offload *xo = xfrm_offload(skb);
ffa6f571e   Colin Ian King   esp6: fix incorre...
142
  	if (!xo)
7862b4058   Steffen Klassert   esp: Add gso hand...
143
  		goto out;
dd7e1cbd2   Willem de Bruijn   gso: validate gso...
144
145
146
  
  	if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP))
  		goto out;
7862b4058   Steffen Klassert   esp: Add gso hand...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  
  	seq = xo->seq.low;
  
  	x = skb->sp->xvec[skb->sp->len - 1];
  	aead = x->data;
  	esph = ip_esp_hdr(skb);
  
  	if (esph->spi != x->id.spi)
  		goto out;
  
  	if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
  		goto out;
  
  	__skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead));
  
  	skb->encap_hdr_csum = 1;
  
  	if (!(features & NETIF_F_HW_ESP))
  		esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK);
  
  	segs = x->outer_mode->gso_segment(x, skb, esp_features);
  	if (IS_ERR_OR_NULL(segs))
  		goto out;
  
  	__skb_pull(skb, skb->data - skb_mac_header(skb));
  
  	skb2 = segs;
  	do {
  		struct sk_buff *nskb = skb2->next;
  
  		xo = xfrm_offload(skb2);
  		xo->flags |= XFRM_GSO_SEGMENT;
  		xo->seq.low = seq;
  		xo->seq.hi = xfrm_replay_seqhi(x, seq);
  
  		if(!(features & NETIF_F_HW_ESP))
  			xo->flags |= CRYPTO_FALLBACK;
  
  		x->outer_mode->xmit(x, skb2);
  
  		err = x->type_offload->xmit(x, skb2, esp_features);
  		if (err) {
  			kfree_skb_list(segs);
  			return ERR_PTR(err);
  		}
  
  		if (!skb_is_gso(skb2))
  			seq++;
  		else
  			seq += skb_shinfo(skb2)->gso_segs;
  
  		skb_push(skb2, skb2->mac_len);
  		skb2 = nskb;
  	} while (skb2);
  
  out:
  	return segs;
  }
383d0350f   Steffen Klassert   esp6: Reorganize ...
205
206
207
  static int esp6_input_tail(struct xfrm_state *x, struct sk_buff *skb)
  {
  	struct crypto_aead *aead = x->data;
e51a64727   Ilan Tayari   esp6: Support RX ...
208
  	struct xfrm_offload *xo = xfrm_offload(skb);
383d0350f   Steffen Klassert   esp6: Reorganize ...
209
210
211
  
  	if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead)))
  		return -EINVAL;
e51a64727   Ilan Tayari   esp6: Support RX ...
212
213
  	if (!(xo->flags & CRYPTO_DONE))
  		skb->ip_summed = CHECKSUM_NONE;
383d0350f   Steffen Klassert   esp6: Reorganize ...
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  
  	return esp6_input_done2(skb, 0);
  }
  
  static int esp6_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_t features)
  {
  	int err;
  	int alen;
  	int blksize;
  	struct xfrm_offload *xo;
  	struct ip_esp_hdr *esph;
  	struct crypto_aead *aead;
  	struct esp_info esp;
  	bool hw_offload = true;
  
  	esp.inplace = true;
  
  	xo = xfrm_offload(skb);
  
  	if (!xo)
  		return -EINVAL;
8f92e03ec   Ilan Tayari   esp4/6: Fix GSO p...
235
236
  	if (!(features & NETIF_F_HW_ESP) || !x->xso.offload_handle ||
  	    (x->xso.dev != skb->dev)) {
383d0350f   Steffen Klassert   esp6: Reorganize ...
237
  		xo->flags |= CRYPTO_FALLBACK;
8f92e03ec   Ilan Tayari   esp4/6: Fix GSO p...
238
  		hw_offload = false;
383d0350f   Steffen Klassert   esp6: Reorganize ...
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  	}
  
  	esp.proto = xo->proto;
  
  	/* skb is pure payload to encrypt */
  
  	aead = x->data;
  	alen = crypto_aead_authsize(aead);
  
  	esp.tfclen = 0;
  	/* XXX: Add support for tfc padding here. */
  
  	blksize = ALIGN(crypto_aead_blocksize(aead), 4);
  	esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize);
  	esp.plen = esp.clen - skb->len - esp.tfclen;
  	esp.tailen = esp.tfclen + esp.plen + alen;
  
  	if (!hw_offload || (hw_offload && !skb_is_gso(skb))) {
  		esp.nfrags = esp6_output_head(x, skb, &esp);
  		if (esp.nfrags < 0)
  			return esp.nfrags;
  	}
  
  	esph = ip_esp_hdr(skb);
  	esph->spi = x->id.spi;
  
  	skb_push(skb, -skb_network_offset(skb));
  
  	if (xo->flags & XFRM_GSO_SEGMENT) {
  		esph->seq_no = htonl(xo->seq.low);
  	} else {
  		int len;
  
  		len = skb->len - sizeof(struct ipv6hdr);
  		if (len > IPV6_MAXPLEN)
  			len = 0;
  
  		ipv6_hdr(skb)->payload_len = htons(len);
  	}
8f92e03ec   Ilan Tayari   esp4/6: Fix GSO p...
278
  	if (hw_offload)
383d0350f   Steffen Klassert   esp6: Reorganize ...
279
280
281
282
283
  		return 0;
  
  	esp.seqno = cpu_to_be64(xo->seq.low + ((u64)xo->seq.hi << 32));
  
  	err = esp6_output_tail(x, skb, &esp);
4ff0308f0   Steffen Klassert   esp: Fix error ha...
284
  	if (err)
383d0350f   Steffen Klassert   esp6: Reorganize ...
285
286
287
288
289
290
  		return err;
  
  	secpath_reset(skb);
  
  	return 0;
  }
7785bba29   Steffen Klassert   esp: Add a softwa...
291
292
293
  static const struct net_offload esp6_offload = {
  	.callbacks = {
  		.gro_receive = esp6_gro_receive,
7862b4058   Steffen Klassert   esp: Add gso hand...
294
  		.gso_segment = esp6_gso_segment,
7785bba29   Steffen Klassert   esp: Add a softwa...
295
296
  	},
  };
383d0350f   Steffen Klassert   esp6: Reorganize ...
297
298
299
300
301
302
  static const struct xfrm_type_offload esp6_type_offload = {
  	.description	= "ESP6 OFFLOAD",
  	.owner		= THIS_MODULE,
  	.proto	     	= IPPROTO_ESP,
  	.input_tail	= esp6_input_tail,
  	.xmit		= esp6_xmit,
7862b4058   Steffen Klassert   esp: Add gso hand...
303
  	.encap		= esp6_gso_encap,
383d0350f   Steffen Klassert   esp6: Reorganize ...
304
  };
7785bba29   Steffen Klassert   esp: Add a softwa...
305
306
  static int __init esp6_offload_init(void)
  {
383d0350f   Steffen Klassert   esp6: Reorganize ...
307
308
309
310
311
  	if (xfrm_register_type_offload(&esp6_type_offload, AF_INET6) < 0) {
  		pr_info("%s: can't add xfrm type offload
  ", __func__);
  		return -EAGAIN;
  	}
7785bba29   Steffen Klassert   esp: Add a softwa...
312
313
314
315
316
  	return inet6_add_offload(&esp6_offload, IPPROTO_ESP);
  }
  
  static void __exit esp6_offload_exit(void)
  {
383d0350f   Steffen Klassert   esp6: Reorganize ...
317
318
319
  	if (xfrm_unregister_type_offload(&esp6_type_offload, AF_INET6) < 0)
  		pr_info("%s: can't remove xfrm type offload
  ", __func__);
7785bba29   Steffen Klassert   esp: Add a softwa...
320
321
322
323
324
325
326
  	inet6_del_offload(&esp6_offload, IPPROTO_ESP);
  }
  
  module_init(esp6_offload_init);
  module_exit(esp6_offload_exit);
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");
ffdb5211d   Ilan Tayari   xfrm: Auto-load x...
327
  MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET6, XFRM_PROTO_ESP);