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