xref: /openbmc/linux/net/ipv4/esp4_offload.c (revision 023e4163)
1 /*
2  * IPV4 GSO/GRO offload support
3  * Linux INET implementation
4  *
5  * Copyright (C) 2016 secunet Security Networks AG
6  * Author: Steffen Klassert <steffen.klassert@secunet.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  *
12  * ESP GRO support
13  */
14 
15 #include <linux/skbuff.h>
16 #include <linux/init.h>
17 #include <net/protocol.h>
18 #include <crypto/aead.h>
19 #include <crypto/authenc.h>
20 #include <linux/err.h>
21 #include <linux/module.h>
22 #include <net/ip.h>
23 #include <net/xfrm.h>
24 #include <net/esp.h>
25 #include <linux/scatterlist.h>
26 #include <linux/kernel.h>
27 #include <linux/slab.h>
28 #include <linux/spinlock.h>
29 #include <net/udp.h>
30 
31 static struct sk_buff *esp4_gro_receive(struct list_head *head,
32 					struct sk_buff *skb)
33 {
34 	int offset = skb_gro_offset(skb);
35 	struct xfrm_offload *xo;
36 	struct xfrm_state *x;
37 	__be32 seq;
38 	__be32 spi;
39 	int err;
40 
41 	if (!pskb_pull(skb, offset))
42 		return NULL;
43 
44 	if ((err = xfrm_parse_spi(skb, IPPROTO_ESP, &spi, &seq)) != 0)
45 		goto out;
46 
47 	xo = xfrm_offload(skb);
48 	if (!xo || !(xo->flags & CRYPTO_DONE)) {
49 		struct sec_path *sp = secpath_set(skb);
50 
51 		if (!sp)
52 			goto out;
53 
54 		if (sp->len == XFRM_MAX_DEPTH)
55 			goto out;
56 
57 		x = xfrm_state_lookup(dev_net(skb->dev), skb->mark,
58 				      (xfrm_address_t *)&ip_hdr(skb)->daddr,
59 				      spi, IPPROTO_ESP, AF_INET);
60 		if (!x)
61 			goto out;
62 
63 		sp->xvec[sp->len++] = x;
64 		sp->olen++;
65 
66 		xo = xfrm_offload(skb);
67 		if (!xo) {
68 			xfrm_state_put(x);
69 			goto out;
70 		}
71 	}
72 
73 	xo->flags |= XFRM_GRO;
74 
75 	XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
76 	XFRM_SPI_SKB_CB(skb)->family = AF_INET;
77 	XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
78 	XFRM_SPI_SKB_CB(skb)->seq = seq;
79 
80 	/* We don't need to handle errors from xfrm_input, it does all
81 	 * the error handling and frees the resources on error. */
82 	xfrm_input(skb, IPPROTO_ESP, spi, -2);
83 
84 	return ERR_PTR(-EINPROGRESS);
85 out:
86 	skb_push(skb, offset);
87 	NAPI_GRO_CB(skb)->same_flow = 0;
88 	NAPI_GRO_CB(skb)->flush = 1;
89 
90 	return NULL;
91 }
92 
93 static void esp4_gso_encap(struct xfrm_state *x, struct sk_buff *skb)
94 {
95 	struct ip_esp_hdr *esph;
96 	struct iphdr *iph = ip_hdr(skb);
97 	struct xfrm_offload *xo = xfrm_offload(skb);
98 	int proto = iph->protocol;
99 
100 	skb_push(skb, -skb_network_offset(skb));
101 	esph = ip_esp_hdr(skb);
102 	*skb_mac_header(skb) = IPPROTO_ESP;
103 
104 	esph->spi = x->id.spi;
105 	esph->seq_no = htonl(XFRM_SKB_CB(skb)->seq.output.low);
106 
107 	xo->proto = proto;
108 }
109 
110 static struct sk_buff *esp4_gso_segment(struct sk_buff *skb,
111 				        netdev_features_t features)
112 {
113 	struct xfrm_state *x;
114 	struct ip_esp_hdr *esph;
115 	struct crypto_aead *aead;
116 	netdev_features_t esp_features = features;
117 	struct xfrm_offload *xo = xfrm_offload(skb);
118 	struct sec_path *sp;
119 
120 	if (!xo)
121 		return ERR_PTR(-EINVAL);
122 
123 	if (!(skb_shinfo(skb)->gso_type & SKB_GSO_ESP))
124 		return ERR_PTR(-EINVAL);
125 
126 	sp = skb_sec_path(skb);
127 	x = sp->xvec[sp->len - 1];
128 	aead = x->data;
129 	esph = ip_esp_hdr(skb);
130 
131 	if (esph->spi != x->id.spi)
132 		return ERR_PTR(-EINVAL);
133 
134 	if (!pskb_may_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead)))
135 		return ERR_PTR(-EINVAL);
136 
137 	__skb_pull(skb, sizeof(*esph) + crypto_aead_ivsize(aead));
138 
139 	skb->encap_hdr_csum = 1;
140 
141 	if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev)
142 		esp_features = features & ~(NETIF_F_SG | NETIF_F_CSUM_MASK);
143 	else if (!(features & NETIF_F_HW_ESP_TX_CSUM))
144 		esp_features = features & ~NETIF_F_CSUM_MASK;
145 
146 	xo->flags |= XFRM_GSO_SEGMENT;
147 
148 	return x->outer_mode->gso_segment(x, skb, esp_features);
149 }
150 
151 static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb)
152 {
153 	struct crypto_aead *aead = x->data;
154 	struct xfrm_offload *xo = xfrm_offload(skb);
155 
156 	if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead)))
157 		return -EINVAL;
158 
159 	if (!(xo->flags & CRYPTO_DONE))
160 		skb->ip_summed = CHECKSUM_NONE;
161 
162 	return esp_input_done2(skb, 0);
163 }
164 
165 static int esp_xmit(struct xfrm_state *x, struct sk_buff *skb,  netdev_features_t features)
166 {
167 	int err;
168 	int alen;
169 	int blksize;
170 	struct xfrm_offload *xo;
171 	struct ip_esp_hdr *esph;
172 	struct crypto_aead *aead;
173 	struct esp_info esp;
174 	bool hw_offload = true;
175 	__u32 seq;
176 
177 	esp.inplace = true;
178 
179 	xo = xfrm_offload(skb);
180 
181 	if (!xo)
182 		return -EINVAL;
183 
184 	if (!(features & NETIF_F_HW_ESP) || x->xso.dev != skb->dev) {
185 		xo->flags |= CRYPTO_FALLBACK;
186 		hw_offload = false;
187 	}
188 
189 	esp.proto = xo->proto;
190 
191 	/* skb is pure payload to encrypt */
192 
193 	aead = x->data;
194 	alen = crypto_aead_authsize(aead);
195 
196 	esp.tfclen = 0;
197 	/* XXX: Add support for tfc padding here. */
198 
199 	blksize = ALIGN(crypto_aead_blocksize(aead), 4);
200 	esp.clen = ALIGN(skb->len + 2 + esp.tfclen, blksize);
201 	esp.plen = esp.clen - skb->len - esp.tfclen;
202 	esp.tailen = esp.tfclen + esp.plen + alen;
203 
204 	esp.esph = ip_esp_hdr(skb);
205 
206 
207 	if (!hw_offload || (hw_offload && !skb_is_gso(skb))) {
208 		esp.nfrags = esp_output_head(x, skb, &esp);
209 		if (esp.nfrags < 0)
210 			return esp.nfrags;
211 	}
212 
213 	seq = xo->seq.low;
214 
215 	esph = esp.esph;
216 	esph->spi = x->id.spi;
217 
218 	skb_push(skb, -skb_network_offset(skb));
219 
220 	if (xo->flags & XFRM_GSO_SEGMENT) {
221 		esph->seq_no = htonl(seq);
222 
223 		if (!skb_is_gso(skb))
224 			xo->seq.low++;
225 		else
226 			xo->seq.low += skb_shinfo(skb)->gso_segs;
227 	}
228 
229 	esp.seqno = cpu_to_be64(seq + ((u64)xo->seq.hi << 32));
230 
231 	ip_hdr(skb)->tot_len = htons(skb->len);
232 	ip_send_check(ip_hdr(skb));
233 
234 	if (hw_offload)
235 		return 0;
236 
237 	err = esp_output_tail(x, skb, &esp);
238 	if (err)
239 		return err;
240 
241 	secpath_reset(skb);
242 
243 	return 0;
244 }
245 
246 static const struct net_offload esp4_offload = {
247 	.callbacks = {
248 		.gro_receive = esp4_gro_receive,
249 		.gso_segment = esp4_gso_segment,
250 	},
251 };
252 
253 static const struct xfrm_type_offload esp_type_offload = {
254 	.description	= "ESP4 OFFLOAD",
255 	.owner		= THIS_MODULE,
256 	.proto	     	= IPPROTO_ESP,
257 	.input_tail	= esp_input_tail,
258 	.xmit		= esp_xmit,
259 	.encap		= esp4_gso_encap,
260 };
261 
262 static int __init esp4_offload_init(void)
263 {
264 	if (xfrm_register_type_offload(&esp_type_offload, AF_INET) < 0) {
265 		pr_info("%s: can't add xfrm type offload\n", __func__);
266 		return -EAGAIN;
267 	}
268 
269 	return inet_add_offload(&esp4_offload, IPPROTO_ESP);
270 }
271 
272 static void __exit esp4_offload_exit(void)
273 {
274 	if (xfrm_unregister_type_offload(&esp_type_offload, AF_INET) < 0)
275 		pr_info("%s: can't remove xfrm type offload\n", __func__);
276 
277 	inet_del_offload(&esp4_offload, IPPROTO_ESP);
278 }
279 
280 module_init(esp4_offload_init);
281 module_exit(esp4_offload_exit);
282 MODULE_LICENSE("GPL");
283 MODULE_AUTHOR("Steffen Klassert <steffen.klassert@secunet.com>");
284 MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET, XFRM_PROTO_ESP);
285