xref: /linux/drivers/net/netdevsim/ipsec.c (revision 7699353da875c2ae0d87a4f5a995c144115b31bb)
1*7699353dSShannon Nelson // SPDX-License-Identifier: GPL-2.0
2*7699353dSShannon Nelson /* Copyright(c) 2018 Oracle and/or its affiliates. All rights reserved. */
3*7699353dSShannon Nelson 
4*7699353dSShannon Nelson #include <crypto/aead.h>
5*7699353dSShannon Nelson #include <linux/debugfs.h>
6*7699353dSShannon Nelson #include <net/xfrm.h>
7*7699353dSShannon Nelson 
8*7699353dSShannon Nelson #include "netdevsim.h"
9*7699353dSShannon Nelson 
10*7699353dSShannon Nelson #define NSIM_IPSEC_AUTH_BITS	128
11*7699353dSShannon Nelson 
12*7699353dSShannon Nelson static ssize_t nsim_dbg_netdev_ops_read(struct file *filp,
13*7699353dSShannon Nelson 					char __user *buffer,
14*7699353dSShannon Nelson 					size_t count, loff_t *ppos)
15*7699353dSShannon Nelson {
16*7699353dSShannon Nelson 	struct netdevsim *ns = filp->private_data;
17*7699353dSShannon Nelson 	struct nsim_ipsec *ipsec = &ns->ipsec;
18*7699353dSShannon Nelson 	size_t bufsize;
19*7699353dSShannon Nelson 	char *buf, *p;
20*7699353dSShannon Nelson 	int len;
21*7699353dSShannon Nelson 	int i;
22*7699353dSShannon Nelson 
23*7699353dSShannon Nelson 	/* the buffer needed is
24*7699353dSShannon Nelson 	 * (num SAs * 3 lines each * ~60 bytes per line) + one more line
25*7699353dSShannon Nelson 	 */
26*7699353dSShannon Nelson 	bufsize = (ipsec->count * 4 * 60) + 60;
27*7699353dSShannon Nelson 	buf = kzalloc(bufsize, GFP_KERNEL);
28*7699353dSShannon Nelson 	if (!buf)
29*7699353dSShannon Nelson 		return -ENOMEM;
30*7699353dSShannon Nelson 
31*7699353dSShannon Nelson 	p = buf;
32*7699353dSShannon Nelson 	p += snprintf(p, bufsize - (p - buf),
33*7699353dSShannon Nelson 		      "SA count=%u tx=%u\n",
34*7699353dSShannon Nelson 		      ipsec->count, ipsec->tx);
35*7699353dSShannon Nelson 
36*7699353dSShannon Nelson 	for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) {
37*7699353dSShannon Nelson 		struct nsim_sa *sap = &ipsec->sa[i];
38*7699353dSShannon Nelson 
39*7699353dSShannon Nelson 		if (!sap->used)
40*7699353dSShannon Nelson 			continue;
41*7699353dSShannon Nelson 
42*7699353dSShannon Nelson 		p += snprintf(p, bufsize - (p - buf),
43*7699353dSShannon Nelson 			      "sa[%i] %cx ipaddr=0x%08x %08x %08x %08x\n",
44*7699353dSShannon Nelson 			      i, (sap->rx ? 'r' : 't'), sap->ipaddr[0],
45*7699353dSShannon Nelson 			      sap->ipaddr[1], sap->ipaddr[2], sap->ipaddr[3]);
46*7699353dSShannon Nelson 		p += snprintf(p, bufsize - (p - buf),
47*7699353dSShannon Nelson 			      "sa[%i]    spi=0x%08x proto=0x%x salt=0x%08x crypt=%d\n",
48*7699353dSShannon Nelson 			      i, be32_to_cpu(sap->xs->id.spi),
49*7699353dSShannon Nelson 			      sap->xs->id.proto, sap->salt, sap->crypt);
50*7699353dSShannon Nelson 		p += snprintf(p, bufsize - (p - buf),
51*7699353dSShannon Nelson 			      "sa[%i]    key=0x%08x %08x %08x %08x\n",
52*7699353dSShannon Nelson 			      i, sap->key[0], sap->key[1],
53*7699353dSShannon Nelson 			      sap->key[2], sap->key[3]);
54*7699353dSShannon Nelson 	}
55*7699353dSShannon Nelson 
56*7699353dSShannon Nelson 	len = simple_read_from_buffer(buffer, count, ppos, buf, p - buf);
57*7699353dSShannon Nelson 
58*7699353dSShannon Nelson 	kfree(buf);
59*7699353dSShannon Nelson 	return len;
60*7699353dSShannon Nelson }
61*7699353dSShannon Nelson 
62*7699353dSShannon Nelson static const struct file_operations ipsec_dbg_fops = {
63*7699353dSShannon Nelson 	.owner = THIS_MODULE,
64*7699353dSShannon Nelson 	.open = simple_open,
65*7699353dSShannon Nelson 	.read = nsim_dbg_netdev_ops_read,
66*7699353dSShannon Nelson };
67*7699353dSShannon Nelson 
68*7699353dSShannon Nelson static int nsim_ipsec_find_empty_idx(struct nsim_ipsec *ipsec)
69*7699353dSShannon Nelson {
70*7699353dSShannon Nelson 	u32 i;
71*7699353dSShannon Nelson 
72*7699353dSShannon Nelson 	if (ipsec->count == NSIM_IPSEC_MAX_SA_COUNT)
73*7699353dSShannon Nelson 		return -ENOSPC;
74*7699353dSShannon Nelson 
75*7699353dSShannon Nelson 	/* search sa table */
76*7699353dSShannon Nelson 	for (i = 0; i < NSIM_IPSEC_MAX_SA_COUNT; i++) {
77*7699353dSShannon Nelson 		if (!ipsec->sa[i].used)
78*7699353dSShannon Nelson 			return i;
79*7699353dSShannon Nelson 	}
80*7699353dSShannon Nelson 
81*7699353dSShannon Nelson 	return -ENOSPC;
82*7699353dSShannon Nelson }
83*7699353dSShannon Nelson 
84*7699353dSShannon Nelson static int nsim_ipsec_parse_proto_keys(struct xfrm_state *xs,
85*7699353dSShannon Nelson 				       u32 *mykey, u32 *mysalt)
86*7699353dSShannon Nelson {
87*7699353dSShannon Nelson 	const char aes_gcm_name[] = "rfc4106(gcm(aes))";
88*7699353dSShannon Nelson 	struct net_device *dev = xs->xso.dev;
89*7699353dSShannon Nelson 	unsigned char *key_data;
90*7699353dSShannon Nelson 	char *alg_name = NULL;
91*7699353dSShannon Nelson 	int key_len;
92*7699353dSShannon Nelson 
93*7699353dSShannon Nelson 	if (!xs->aead) {
94*7699353dSShannon Nelson 		netdev_err(dev, "Unsupported IPsec algorithm\n");
95*7699353dSShannon Nelson 		return -EINVAL;
96*7699353dSShannon Nelson 	}
97*7699353dSShannon Nelson 
98*7699353dSShannon Nelson 	if (xs->aead->alg_icv_len != NSIM_IPSEC_AUTH_BITS) {
99*7699353dSShannon Nelson 		netdev_err(dev, "IPsec offload requires %d bit authentication\n",
100*7699353dSShannon Nelson 			   NSIM_IPSEC_AUTH_BITS);
101*7699353dSShannon Nelson 		return -EINVAL;
102*7699353dSShannon Nelson 	}
103*7699353dSShannon Nelson 
104*7699353dSShannon Nelson 	key_data = &xs->aead->alg_key[0];
105*7699353dSShannon Nelson 	key_len = xs->aead->alg_key_len;
106*7699353dSShannon Nelson 	alg_name = xs->aead->alg_name;
107*7699353dSShannon Nelson 
108*7699353dSShannon Nelson 	if (strcmp(alg_name, aes_gcm_name)) {
109*7699353dSShannon Nelson 		netdev_err(dev, "Unsupported IPsec algorithm - please use %s\n",
110*7699353dSShannon Nelson 			   aes_gcm_name);
111*7699353dSShannon Nelson 		return -EINVAL;
112*7699353dSShannon Nelson 	}
113*7699353dSShannon Nelson 
114*7699353dSShannon Nelson 	/* 160 accounts for 16 byte key and 4 byte salt */
115*7699353dSShannon Nelson 	if (key_len > NSIM_IPSEC_AUTH_BITS) {
116*7699353dSShannon Nelson 		*mysalt = ((u32 *)key_data)[4];
117*7699353dSShannon Nelson 	} else if (key_len == NSIM_IPSEC_AUTH_BITS) {
118*7699353dSShannon Nelson 		*mysalt = 0;
119*7699353dSShannon Nelson 	} else {
120*7699353dSShannon Nelson 		netdev_err(dev, "IPsec hw offload only supports 128 bit keys with optional 32 bit salt\n");
121*7699353dSShannon Nelson 		return -EINVAL;
122*7699353dSShannon Nelson 	}
123*7699353dSShannon Nelson 	memcpy(mykey, key_data, 16);
124*7699353dSShannon Nelson 
125*7699353dSShannon Nelson 	return 0;
126*7699353dSShannon Nelson }
127*7699353dSShannon Nelson 
128*7699353dSShannon Nelson static int nsim_ipsec_add_sa(struct xfrm_state *xs)
129*7699353dSShannon Nelson {
130*7699353dSShannon Nelson 	struct nsim_ipsec *ipsec;
131*7699353dSShannon Nelson 	struct net_device *dev;
132*7699353dSShannon Nelson 	struct netdevsim *ns;
133*7699353dSShannon Nelson 	struct nsim_sa sa;
134*7699353dSShannon Nelson 	u16 sa_idx;
135*7699353dSShannon Nelson 	int ret;
136*7699353dSShannon Nelson 
137*7699353dSShannon Nelson 	dev = xs->xso.dev;
138*7699353dSShannon Nelson 	ns = netdev_priv(dev);
139*7699353dSShannon Nelson 	ipsec = &ns->ipsec;
140*7699353dSShannon Nelson 
141*7699353dSShannon Nelson 	if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) {
142*7699353dSShannon Nelson 		netdev_err(dev, "Unsupported protocol 0x%04x for ipsec offload\n",
143*7699353dSShannon Nelson 			   xs->id.proto);
144*7699353dSShannon Nelson 		return -EINVAL;
145*7699353dSShannon Nelson 	}
146*7699353dSShannon Nelson 
147*7699353dSShannon Nelson 	if (xs->calg) {
148*7699353dSShannon Nelson 		netdev_err(dev, "Compression offload not supported\n");
149*7699353dSShannon Nelson 		return -EINVAL;
150*7699353dSShannon Nelson 	}
151*7699353dSShannon Nelson 
152*7699353dSShannon Nelson 	/* find the first unused index */
153*7699353dSShannon Nelson 	ret = nsim_ipsec_find_empty_idx(ipsec);
154*7699353dSShannon Nelson 	if (ret < 0) {
155*7699353dSShannon Nelson 		netdev_err(dev, "No space for SA in Rx table!\n");
156*7699353dSShannon Nelson 		return ret;
157*7699353dSShannon Nelson 	}
158*7699353dSShannon Nelson 	sa_idx = (u16)ret;
159*7699353dSShannon Nelson 
160*7699353dSShannon Nelson 	memset(&sa, 0, sizeof(sa));
161*7699353dSShannon Nelson 	sa.used = true;
162*7699353dSShannon Nelson 	sa.xs = xs;
163*7699353dSShannon Nelson 
164*7699353dSShannon Nelson 	if (sa.xs->id.proto & IPPROTO_ESP)
165*7699353dSShannon Nelson 		sa.crypt = xs->ealg || xs->aead;
166*7699353dSShannon Nelson 
167*7699353dSShannon Nelson 	/* get the key and salt */
168*7699353dSShannon Nelson 	ret = nsim_ipsec_parse_proto_keys(xs, sa.key, &sa.salt);
169*7699353dSShannon Nelson 	if (ret) {
170*7699353dSShannon Nelson 		netdev_err(dev, "Failed to get key data for SA table\n");
171*7699353dSShannon Nelson 		return ret;
172*7699353dSShannon Nelson 	}
173*7699353dSShannon Nelson 
174*7699353dSShannon Nelson 	if (xs->xso.flags & XFRM_OFFLOAD_INBOUND) {
175*7699353dSShannon Nelson 		sa.rx = true;
176*7699353dSShannon Nelson 
177*7699353dSShannon Nelson 		if (xs->props.family == AF_INET6)
178*7699353dSShannon Nelson 			memcpy(sa.ipaddr, &xs->id.daddr.a6, 16);
179*7699353dSShannon Nelson 		else
180*7699353dSShannon Nelson 			memcpy(&sa.ipaddr[3], &xs->id.daddr.a4, 4);
181*7699353dSShannon Nelson 	}
182*7699353dSShannon Nelson 
183*7699353dSShannon Nelson 	/* the preparations worked, so save the info */
184*7699353dSShannon Nelson 	memcpy(&ipsec->sa[sa_idx], &sa, sizeof(sa));
185*7699353dSShannon Nelson 
186*7699353dSShannon Nelson 	/* the XFRM stack doesn't like offload_handle == 0,
187*7699353dSShannon Nelson 	 * so add a bitflag in case our array index is 0
188*7699353dSShannon Nelson 	 */
189*7699353dSShannon Nelson 	xs->xso.offload_handle = sa_idx | NSIM_IPSEC_VALID;
190*7699353dSShannon Nelson 	ipsec->count++;
191*7699353dSShannon Nelson 
192*7699353dSShannon Nelson 	return 0;
193*7699353dSShannon Nelson }
194*7699353dSShannon Nelson 
195*7699353dSShannon Nelson static void nsim_ipsec_del_sa(struct xfrm_state *xs)
196*7699353dSShannon Nelson {
197*7699353dSShannon Nelson 	struct netdevsim *ns = netdev_priv(xs->xso.dev);
198*7699353dSShannon Nelson 	struct nsim_ipsec *ipsec = &ns->ipsec;
199*7699353dSShannon Nelson 	u16 sa_idx;
200*7699353dSShannon Nelson 
201*7699353dSShannon Nelson 	sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID;
202*7699353dSShannon Nelson 	if (!ipsec->sa[sa_idx].used) {
203*7699353dSShannon Nelson 		netdev_err(ns->netdev, "Invalid SA for delete sa_idx=%d\n",
204*7699353dSShannon Nelson 			   sa_idx);
205*7699353dSShannon Nelson 		return;
206*7699353dSShannon Nelson 	}
207*7699353dSShannon Nelson 
208*7699353dSShannon Nelson 	memset(&ipsec->sa[sa_idx], 0, sizeof(struct nsim_sa));
209*7699353dSShannon Nelson 	ipsec->count--;
210*7699353dSShannon Nelson }
211*7699353dSShannon Nelson 
212*7699353dSShannon Nelson static bool nsim_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs)
213*7699353dSShannon Nelson {
214*7699353dSShannon Nelson 	struct netdevsim *ns = netdev_priv(xs->xso.dev);
215*7699353dSShannon Nelson 	struct nsim_ipsec *ipsec = &ns->ipsec;
216*7699353dSShannon Nelson 
217*7699353dSShannon Nelson 	ipsec->ok++;
218*7699353dSShannon Nelson 
219*7699353dSShannon Nelson 	return true;
220*7699353dSShannon Nelson }
221*7699353dSShannon Nelson 
222*7699353dSShannon Nelson static const struct xfrmdev_ops nsim_xfrmdev_ops = {
223*7699353dSShannon Nelson 	.xdo_dev_state_add	= nsim_ipsec_add_sa,
224*7699353dSShannon Nelson 	.xdo_dev_state_delete	= nsim_ipsec_del_sa,
225*7699353dSShannon Nelson 	.xdo_dev_offload_ok	= nsim_ipsec_offload_ok,
226*7699353dSShannon Nelson };
227*7699353dSShannon Nelson 
228*7699353dSShannon Nelson bool nsim_ipsec_tx(struct netdevsim *ns, struct sk_buff *skb)
229*7699353dSShannon Nelson {
230*7699353dSShannon Nelson 	struct nsim_ipsec *ipsec = &ns->ipsec;
231*7699353dSShannon Nelson 	struct xfrm_state *xs;
232*7699353dSShannon Nelson 	struct nsim_sa *tsa;
233*7699353dSShannon Nelson 	u32 sa_idx;
234*7699353dSShannon Nelson 
235*7699353dSShannon Nelson 	/* do we even need to check this packet? */
236*7699353dSShannon Nelson 	if (!skb->sp)
237*7699353dSShannon Nelson 		return true;
238*7699353dSShannon Nelson 
239*7699353dSShannon Nelson 	if (unlikely(!skb->sp->len)) {
240*7699353dSShannon Nelson 		netdev_err(ns->netdev, "no xfrm state len = %d\n",
241*7699353dSShannon Nelson 			   skb->sp->len);
242*7699353dSShannon Nelson 		return false;
243*7699353dSShannon Nelson 	}
244*7699353dSShannon Nelson 
245*7699353dSShannon Nelson 	xs = xfrm_input_state(skb);
246*7699353dSShannon Nelson 	if (unlikely(!xs)) {
247*7699353dSShannon Nelson 		netdev_err(ns->netdev, "no xfrm_input_state() xs = %p\n", xs);
248*7699353dSShannon Nelson 		return false;
249*7699353dSShannon Nelson 	}
250*7699353dSShannon Nelson 
251*7699353dSShannon Nelson 	sa_idx = xs->xso.offload_handle & ~NSIM_IPSEC_VALID;
252*7699353dSShannon Nelson 	if (unlikely(sa_idx > NSIM_IPSEC_MAX_SA_COUNT)) {
253*7699353dSShannon Nelson 		netdev_err(ns->netdev, "bad sa_idx=%d max=%d\n",
254*7699353dSShannon Nelson 			   sa_idx, NSIM_IPSEC_MAX_SA_COUNT);
255*7699353dSShannon Nelson 		return false;
256*7699353dSShannon Nelson 	}
257*7699353dSShannon Nelson 
258*7699353dSShannon Nelson 	tsa = &ipsec->sa[sa_idx];
259*7699353dSShannon Nelson 	if (unlikely(!tsa->used)) {
260*7699353dSShannon Nelson 		netdev_err(ns->netdev, "unused sa_idx=%d\n", sa_idx);
261*7699353dSShannon Nelson 		return false;
262*7699353dSShannon Nelson 	}
263*7699353dSShannon Nelson 
264*7699353dSShannon Nelson 	if (xs->id.proto != IPPROTO_ESP && xs->id.proto != IPPROTO_AH) {
265*7699353dSShannon Nelson 		netdev_err(ns->netdev, "unexpected proto=%d\n", xs->id.proto);
266*7699353dSShannon Nelson 		return false;
267*7699353dSShannon Nelson 	}
268*7699353dSShannon Nelson 
269*7699353dSShannon Nelson 	ipsec->tx++;
270*7699353dSShannon Nelson 
271*7699353dSShannon Nelson 	return true;
272*7699353dSShannon Nelson }
273*7699353dSShannon Nelson 
274*7699353dSShannon Nelson void nsim_ipsec_init(struct netdevsim *ns)
275*7699353dSShannon Nelson {
276*7699353dSShannon Nelson 	ns->netdev->xfrmdev_ops = &nsim_xfrmdev_ops;
277*7699353dSShannon Nelson 
278*7699353dSShannon Nelson #define NSIM_ESP_FEATURES	(NETIF_F_HW_ESP | \
279*7699353dSShannon Nelson 				 NETIF_F_HW_ESP_TX_CSUM | \
280*7699353dSShannon Nelson 				 NETIF_F_GSO_ESP)
281*7699353dSShannon Nelson 
282*7699353dSShannon Nelson 	ns->netdev->features |= NSIM_ESP_FEATURES;
283*7699353dSShannon Nelson 	ns->netdev->hw_enc_features |= NSIM_ESP_FEATURES;
284*7699353dSShannon Nelson 
285*7699353dSShannon Nelson 	ns->ipsec.pfile = debugfs_create_file("ipsec", 0400, ns->ddir, ns,
286*7699353dSShannon Nelson 					      &ipsec_dbg_fops);
287*7699353dSShannon Nelson }
288*7699353dSShannon Nelson 
289*7699353dSShannon Nelson void nsim_ipsec_teardown(struct netdevsim *ns)
290*7699353dSShannon Nelson {
291*7699353dSShannon Nelson 	struct nsim_ipsec *ipsec = &ns->ipsec;
292*7699353dSShannon Nelson 
293*7699353dSShannon Nelson 	if (ipsec->count)
294*7699353dSShannon Nelson 		netdev_err(ns->netdev, "tearing down IPsec offload with %d SAs left\n",
295*7699353dSShannon Nelson 			   ipsec->count);
296*7699353dSShannon Nelson 	debugfs_remove_recursive(ipsec->pfile);
297*7699353dSShannon Nelson }
298