1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2018 Oracle and/or its affiliates. All rights reserved. */
4 #include <crypto/aead.h>
5 #include <linux/debugfs.h>
10 #define NSIM_IPSEC_AUTH_BITS 128
12 static ssize_t
nsim_dbg_netdev_ops_read(struct file
*filp
,
14 size_t count
, loff_t
*ppos
)
16 struct netdevsim
*ns
= filp
->private_data
;
17 struct nsim_ipsec
*ipsec
= &ns
->ipsec
;
23 /* the buffer needed is
24 * (num SAs * 3 lines each * ~60 bytes per line) + one more line
26 bufsize
= (ipsec
->count
* 4 * 60) + 60;
27 buf
= kzalloc(bufsize
, GFP_KERNEL
);
32 p
+= snprintf(p
, bufsize
- (p
- buf
),
33 "SA count=%u tx=%u\n",
34 ipsec
->count
, ipsec
->tx
);
36 for (i
= 0; i
< NSIM_IPSEC_MAX_SA_COUNT
; i
++) {
37 struct nsim_sa
*sap
= &ipsec
->sa
[i
];
42 p
+= snprintf(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
+= snprintf(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
+= snprintf(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]);
56 len
= simple_read_from_buffer(buffer
, count
, ppos
, buf
, p
- buf
);
62 static const struct file_operations ipsec_dbg_fops
= {
65 .read
= nsim_dbg_netdev_ops_read
,
68 static int nsim_ipsec_find_empty_idx(struct nsim_ipsec
*ipsec
)
72 if (ipsec
->count
== NSIM_IPSEC_MAX_SA_COUNT
)
76 for (i
= 0; i
< NSIM_IPSEC_MAX_SA_COUNT
; i
++) {
77 if (!ipsec
->sa
[i
].used
)
84 static int nsim_ipsec_parse_proto_keys(struct xfrm_state
*xs
,
85 u32
*mykey
, u32
*mysalt
)
87 const char aes_gcm_name
[] = "rfc4106(gcm(aes))";
88 struct net_device
*dev
= xs
->xso
.dev
;
89 unsigned char *key_data
;
90 char *alg_name
= NULL
;
94 netdev_err(dev
, "Unsupported IPsec algorithm\n");
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
);
104 key_data
= &xs
->aead
->alg_key
[0];
105 key_len
= xs
->aead
->alg_key_len
;
106 alg_name
= xs
->aead
->alg_name
;
108 if (strcmp(alg_name
, aes_gcm_name
)) {
109 netdev_err(dev
, "Unsupported IPsec algorithm - please use %s\n",
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
) {
120 netdev_err(dev
, "IPsec hw offload only supports 128 bit keys with optional 32 bit salt\n");
123 memcpy(mykey
, key_data
, 16);
128 static int nsim_ipsec_add_sa(struct xfrm_state
*xs
)
130 struct nsim_ipsec
*ipsec
;
131 struct net_device
*dev
;
132 struct netdevsim
*ns
;
138 ns
= netdev_priv(dev
);
141 if (xs
->id
.proto
!= IPPROTO_ESP
&& xs
->id
.proto
!= IPPROTO_AH
) {
142 netdev_err(dev
, "Unsupported protocol 0x%04x for ipsec offload\n",
148 netdev_err(dev
, "Compression offload not supported\n");
152 /* find the first unused index */
153 ret
= nsim_ipsec_find_empty_idx(ipsec
);
155 netdev_err(dev
, "No space for SA in Rx table!\n");
160 memset(&sa
, 0, sizeof(sa
));
164 if (sa
.xs
->id
.proto
& IPPROTO_ESP
)
165 sa
.crypt
= xs
->ealg
|| xs
->aead
;
167 /* get the key and salt */
168 ret
= nsim_ipsec_parse_proto_keys(xs
, sa
.key
, &sa
.salt
);
170 netdev_err(dev
, "Failed to get key data for SA table\n");
174 if (xs
->xso
.flags
& XFRM_OFFLOAD_INBOUND
) {
177 if (xs
->props
.family
== AF_INET6
)
178 memcpy(sa
.ipaddr
, &xs
->id
.daddr
.a6
, 16);
180 memcpy(&sa
.ipaddr
[3], &xs
->id
.daddr
.a4
, 4);
183 /* the preparations worked, so save the info */
184 memcpy(&ipsec
->sa
[sa_idx
], &sa
, sizeof(sa
));
186 /* the XFRM stack doesn't like offload_handle == 0,
187 * so add a bitflag in case our array index is 0
189 xs
->xso
.offload_handle
= sa_idx
| NSIM_IPSEC_VALID
;
195 static void nsim_ipsec_del_sa(struct xfrm_state
*xs
)
197 struct netdevsim
*ns
= netdev_priv(xs
->xso
.dev
);
198 struct nsim_ipsec
*ipsec
= &ns
->ipsec
;
201 sa_idx
= xs
->xso
.offload_handle
& ~NSIM_IPSEC_VALID
;
202 if (!ipsec
->sa
[sa_idx
].used
) {
203 netdev_err(ns
->netdev
, "Invalid SA for delete sa_idx=%d\n",
208 memset(&ipsec
->sa
[sa_idx
], 0, sizeof(struct nsim_sa
));
212 static bool nsim_ipsec_offload_ok(struct sk_buff
*skb
, struct xfrm_state
*xs
)
214 struct netdevsim
*ns
= netdev_priv(xs
->xso
.dev
);
215 struct nsim_ipsec
*ipsec
= &ns
->ipsec
;
222 static const struct xfrmdev_ops nsim_xfrmdev_ops
= {
223 .xdo_dev_state_add
= nsim_ipsec_add_sa
,
224 .xdo_dev_state_delete
= nsim_ipsec_del_sa
,
225 .xdo_dev_offload_ok
= nsim_ipsec_offload_ok
,
228 bool nsim_ipsec_tx(struct netdevsim
*ns
, struct sk_buff
*skb
)
230 struct sec_path
*sp
= skb_sec_path(skb
);
231 struct nsim_ipsec
*ipsec
= &ns
->ipsec
;
232 struct xfrm_state
*xs
;
236 /* do we even need to check this packet? */
240 if (unlikely(!sp
->len
)) {
241 netdev_err(ns
->netdev
, "no xfrm state len = %d\n",
246 xs
= xfrm_input_state(skb
);
248 netdev_err(ns
->netdev
, "no xfrm_input_state() xs = %p\n", xs
);
252 sa_idx
= xs
->xso
.offload_handle
& ~NSIM_IPSEC_VALID
;
253 if (unlikely(sa_idx
>= NSIM_IPSEC_MAX_SA_COUNT
)) {
254 netdev_err(ns
->netdev
, "bad sa_idx=%d max=%d\n",
255 sa_idx
, NSIM_IPSEC_MAX_SA_COUNT
);
259 tsa
= &ipsec
->sa
[sa_idx
];
260 if (unlikely(!tsa
->used
)) {
261 netdev_err(ns
->netdev
, "unused sa_idx=%d\n", sa_idx
);
265 if (xs
->id
.proto
!= IPPROTO_ESP
&& xs
->id
.proto
!= IPPROTO_AH
) {
266 netdev_err(ns
->netdev
, "unexpected proto=%d\n", xs
->id
.proto
);
275 void nsim_ipsec_init(struct netdevsim
*ns
)
277 ns
->netdev
->xfrmdev_ops
= &nsim_xfrmdev_ops
;
279 #define NSIM_ESP_FEATURES (NETIF_F_HW_ESP | \
280 NETIF_F_HW_ESP_TX_CSUM | \
283 ns
->netdev
->features
|= NSIM_ESP_FEATURES
;
284 ns
->netdev
->hw_enc_features
|= NSIM_ESP_FEATURES
;
286 ns
->ipsec
.pfile
= debugfs_create_file("ipsec", 0400,
287 ns
->nsim_dev_port
->ddir
, ns
,
291 void nsim_ipsec_teardown(struct netdevsim
*ns
)
293 struct nsim_ipsec
*ipsec
= &ns
->ipsec
;
296 netdev_err(ns
->netdev
, "tearing down IPsec offload with %d SAs left\n",
298 debugfs_remove_recursive(ipsec
->pfile
);