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 | |
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(size: bufsize, GFP_KERNEL); |
28 | if (!buf) |
29 | return -ENOMEM; |
30 | |
31 | p = buf; |
32 | p += scnprintf(buf: p, size: bufsize - (p - buf), |
33 | fmt: "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(buf: p, size: bufsize - (p - buf), |
43 | fmt: "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(buf: p, size: bufsize - (p - buf), |
47 | fmt: "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(buf: p, size: bufsize - (p - buf), |
51 | fmt: "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(to: buffer, count, ppos, from: buf, available: p - buf); |
57 | |
58 | kfree(objp: 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 | |
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 | |
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, format: "Unsupported IPsec algorithm\n" ); |
95 | return -EINVAL; |
96 | } |
97 | |
98 | if (xs->aead->alg_icv_len != NSIM_IPSEC_AUTH_BITS) { |
99 | netdev_err(dev, format: "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, format: "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, format: "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 | |
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, mykey: sa.key, mysalt: &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 | |
200 | static void nsim_ipsec_del_sa(struct xfrm_state *xs) |
201 | { |
202 | struct netdevsim *ns = netdev_priv(dev: 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(dev: ns->netdev, format: "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 | |
217 | static bool nsim_ipsec_offload_ok(struct sk_buff *skb, struct xfrm_state *xs) |
218 | { |
219 | struct netdevsim *ns = netdev_priv(dev: 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 | |
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(dev: ns->netdev, format: "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(dev: ns->netdev, format: "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(dev: ns->netdev, format: "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(dev: ns->netdev, format: "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(dev: ns->netdev, format: "unexpected proto=%d\n" , xs->id.proto); |
272 | return false; |
273 | } |
274 | |
275 | ipsec->tx++; |
276 | |
277 | return true; |
278 | } |
279 | |
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(name: "ipsec" , mode: 0400, |
292 | parent: ns->nsim_dev_port->ddir, data: ns, |
293 | fops: &ipsec_dbg_fops); |
294 | } |
295 | |
296 | void nsim_ipsec_teardown(struct netdevsim *ns) |
297 | { |
298 | struct nsim_ipsec *ipsec = &ns->ipsec; |
299 | |
300 | if (ipsec->count) |
301 | netdev_err(dev: ns->netdev, format: "tearing down IPsec offload with %d SAs left\n" , |
302 | ipsec->count); |
303 | debugfs_remove_recursive(dentry: ipsec->pfile); |
304 | } |
305 | |