xref: /dpdk/drivers/net/nfp/nfp_ipsec.c (revision 7fb333e90c5796c5898d658c12faaa09eb2fcc2d)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2023 Corigine Systems, Inc.
3  * All rights reserved.
4  */
5 
6 #include "nfp_ipsec.h"
7 
8 #include <rte_cryptodev.h>
9 #include <rte_malloc.h>
10 #include <rte_security_driver.h>
11 
12 #include <ethdev_driver.h>
13 #include <ethdev_pci.h>
14 
15 #include "nfp_common.h"
16 #include "nfp_ctrl.h"
17 #include "nfp_logs.h"
18 #include "nfp_rxtx.h"
19 
20 static const struct rte_cryptodev_capabilities nfp_crypto_caps[] = {
21 	{
22 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
23 		.sym = {
24 			.xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
25 			.auth = {
26 				.algo = RTE_CRYPTO_AUTH_MD5_HMAC,
27 				.block_size = 64,
28 				.key_size = {
29 					.min = 16,
30 					.max = 16,
31 					.increment = 0
32 				},
33 				.digest_size = {
34 					.min = 12,
35 					.max = 16,
36 					.increment = 4
37 				},
38 			},
39 		},
40 	},
41 	{
42 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
43 		.sym = {
44 			.xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
45 			.auth = {
46 				.algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
47 				.block_size = 64,
48 				.key_size = {
49 					.min = 20,
50 					.max = 64,
51 					.increment = 1
52 				},
53 				.digest_size = {
54 					.min = 10,
55 					.max = 12,
56 					.increment = 2
57 				},
58 			},
59 		},
60 	},
61 	{
62 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
63 		.sym = {
64 			.xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
65 			.auth = {
66 				.algo = RTE_CRYPTO_AUTH_SHA256_HMAC,
67 				.block_size = 64,
68 				.key_size = {
69 					.min = 32,
70 					.max = 32,
71 					.increment = 0
72 				},
73 				.digest_size = {
74 					.min = 12,
75 					.max = 16,
76 					.increment = 4
77 				},
78 			},
79 		},
80 	},
81 	{
82 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
83 		.sym = {
84 			.xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
85 			.auth = {
86 				.algo = RTE_CRYPTO_AUTH_SHA384_HMAC,
87 				.block_size = 128,
88 				.key_size = {
89 					.min = 48,
90 					.max = 48,
91 					.increment = 0
92 				},
93 				.digest_size = {
94 					.min = 12,
95 					.max = 24,
96 					.increment = 12
97 				},
98 			},
99 		},
100 	},
101 	{
102 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
103 		.sym = {
104 			.xform_type = RTE_CRYPTO_SYM_XFORM_AUTH,
105 			.auth = {
106 				.algo = RTE_CRYPTO_AUTH_SHA512_HMAC,
107 				.block_size = 128,
108 				.key_size = {
109 					.min = 64,
110 					.max = 64,
111 					.increment = 1
112 				},
113 				.digest_size = {
114 					.min = 12,
115 					.max = 32,
116 					.increment = 4
117 				},
118 			},
119 		},
120 	},
121 	{
122 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
123 		.sym = {
124 			.xform_type = RTE_CRYPTO_SYM_XFORM_CIPHER,
125 			.cipher = {
126 				.algo = RTE_CRYPTO_CIPHER_3DES_CBC,
127 				.block_size = 8,
128 				.key_size = {
129 					.min = 24,
130 					.max = 24,
131 					.increment = 0
132 				},
133 				.iv_size = {
134 					.min = 8,
135 					.max = 16,
136 					.increment = 8
137 				},
138 			},
139 		},
140 	},
141 	{
142 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
143 		.sym = {
144 			.xform_type = RTE_CRYPTO_SYM_XFORM_CIPHER,
145 			.cipher = {
146 				.algo = RTE_CRYPTO_CIPHER_AES_CBC,
147 				.block_size = 16,
148 				.key_size = {
149 					.min = 16,
150 					.max = 32,
151 					.increment = 8
152 				},
153 				.iv_size = {
154 					.min = 8,
155 					.max = 16,
156 					.increment = 8
157 				},
158 			},
159 		},
160 	},
161 	{
162 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
163 		.sym = {
164 			.xform_type = RTE_CRYPTO_SYM_XFORM_AEAD,
165 			.aead = {
166 				.algo = RTE_CRYPTO_AEAD_AES_GCM,
167 				.block_size = 16,
168 				.key_size = {
169 					.min = 16,
170 					.max = 32,
171 					.increment = 8
172 				},
173 				.digest_size = {
174 					.min = 16,
175 					.max = 16,
176 					.increment = 0
177 				},
178 				.aad_size = {
179 					.min = 0,
180 					.max = 1024,
181 					.increment = 1
182 				},
183 				.iv_size = {
184 					.min = 8,
185 					.max = 16,
186 					.increment = 4
187 				}
188 			},
189 		},
190 	},
191 	{
192 		.op = RTE_CRYPTO_OP_TYPE_SYMMETRIC,
193 		.sym = {
194 			.xform_type = RTE_CRYPTO_SYM_XFORM_AEAD,
195 			.aead = {
196 				.algo = RTE_CRYPTO_AEAD_CHACHA20_POLY1305,
197 				.block_size = 16,
198 				.key_size = {
199 					.min = 32,
200 					.max = 32,
201 					.increment = 0
202 				},
203 				.digest_size = {
204 					.min = 16,
205 					.max = 16,
206 					.increment = 0
207 				},
208 				.aad_size = {
209 					.min = 0,
210 					.max = 1024,
211 					.increment = 1
212 				},
213 				.iv_size = {
214 					.min = 8,
215 					.max = 16,
216 					.increment = 4
217 				}
218 			},
219 		},
220 	},
221 	{
222 		.op = RTE_CRYPTO_OP_TYPE_UNDEFINED,
223 		.sym = {
224 			.xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED
225 		},
226 	}
227 };
228 
229 static const struct rte_security_capability nfp_security_caps[] = {
230 	{ /* IPsec Inline Crypto Tunnel Egress */
231 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO,
232 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
233 		.ipsec = {
234 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL,
235 			.direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS,
236 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
237 			.options = {
238 				.udp_encap = 1,
239 				.stats = 1,
240 				.esn = 1
241 				}
242 		},
243 		.crypto_capabilities = nfp_crypto_caps
244 	},
245 	{ /* IPsec Inline Crypto Tunnel Ingress */
246 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO,
247 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
248 		.ipsec = {
249 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL,
250 			.direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS,
251 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
252 			.options = {
253 				.udp_encap = 1,
254 				.stats = 1,
255 				.esn = 1
256 				}
257 		},
258 		.crypto_capabilities = nfp_crypto_caps,
259 		.ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA
260 	},
261 	{ /* IPsec Inline Crypto Transport Egress */
262 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO,
263 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
264 		.ipsec = {
265 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT,
266 			.direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS,
267 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
268 			.options = {
269 				.udp_encap = 1,
270 				.stats = 1,
271 				.esn = 1
272 				}
273 		},
274 		.crypto_capabilities = nfp_crypto_caps
275 	},
276 	{ /* IPsec Inline Crypto Transport Ingress */
277 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO,
278 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
279 		.ipsec = {
280 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT,
281 			.direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS,
282 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
283 			.options = {
284 				.udp_encap = 1,
285 				.stats = 1,
286 				.esn = 1
287 				}
288 		},
289 		.crypto_capabilities = nfp_crypto_caps,
290 		.ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA
291 	},
292 	{ /* IPsec Inline Protocol Tunnel Egress */
293 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL,
294 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
295 		.ipsec = {
296 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL,
297 			.direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS,
298 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
299 			.options = {
300 				.udp_encap = 1,
301 				.stats = 1,
302 				.esn = 1
303 				}
304 		},
305 		.crypto_capabilities = nfp_crypto_caps
306 	},
307 	{ /* IPsec Inline Protocol Tunnel Ingress */
308 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL,
309 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
310 		.ipsec = {
311 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL,
312 			.direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS,
313 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
314 			.options = {
315 				.udp_encap = 1,
316 				.stats = 1,
317 				.esn = 1
318 				}
319 		},
320 		.crypto_capabilities = nfp_crypto_caps,
321 		.ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA
322 	},
323 	{ /* IPsec Inline Protocol Transport Egress */
324 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL,
325 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
326 		.ipsec = {
327 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT,
328 			.direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS,
329 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
330 			.options = {
331 				.udp_encap = 1,
332 				.stats = 1,
333 				.esn = 1
334 				}
335 		},
336 		.crypto_capabilities = nfp_crypto_caps
337 	},
338 	{ /* IPsec Inline Protocol Transport Ingress */
339 		.action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL,
340 		.protocol = RTE_SECURITY_PROTOCOL_IPSEC,
341 		.ipsec = {
342 			.mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT,
343 			.direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS,
344 			.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP,
345 			.options = {
346 				.udp_encap = 1,
347 				.stats = 1,
348 				.esn = 1
349 				}
350 		},
351 		.crypto_capabilities = nfp_crypto_caps,
352 		.ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA
353 	},
354 	{
355 		.action = RTE_SECURITY_ACTION_TYPE_NONE
356 	}
357 };
358 
359 /* IPsec config message cmd codes */
360 enum nfp_ipsec_cfg_msg_cmd_codes {
361 	NFP_IPSEC_CFG_MSG_ADD_SA,       /**< Add a new SA */
362 	NFP_IPSEC_CFG_MSG_INV_SA,       /**< Invalidate an existing SA */
363 	NFP_IPSEC_CFG_MSG_MODIFY_SA,    /**< Modify an existing SA */
364 	NFP_IPSEC_CFG_MSG_GET_SA_STATS, /**< Report SA counters, flags, etc. */
365 	NFP_IPSEC_CFG_MSG_GET_SEQ_NUMS, /**< Allocate sequence numbers */
366 	NFP_IPSEC_CFG_MSG_LAST
367 };
368 
369 enum nfp_ipsec_cfg_msg_rsp_codes {
370 	NFP_IPSEC_CFG_MSG_OK,
371 	NFP_IPSEC_CFG_MSG_FAILED,
372 	NFP_IPSEC_CFG_MSG_SA_VALID,
373 	NFP_IPSEC_CFG_MSG_SA_HASH_ADD_FAILED,
374 	NFP_IPSEC_CFG_MSG_SA_HASH_DEL_FAILED,
375 	NFP_IPSEC_CFG_MSG_SA_INVALID_CMD
376 };
377 
378 static int
379 nfp_ipsec_cfg_cmd_issue(struct nfp_net_hw *hw,
380 		struct nfp_ipsec_msg *msg)
381 {
382 	int ret;
383 	uint32_t i;
384 	uint32_t msg_size;
385 
386 	msg_size = RTE_DIM(msg->raw);
387 	msg->rsp = NFP_IPSEC_CFG_MSG_OK;
388 
389 	for (i = 0; i < msg_size; i++)
390 		nn_cfg_writel(hw, NFP_NET_CFG_MBOX_VAL + 4 * i, msg->raw[i]);
391 
392 	ret = nfp_net_mbox_reconfig(hw, NFP_NET_CFG_MBOX_CMD_IPSEC);
393 	if (ret < 0) {
394 		PMD_DRV_LOG(ERR, "Failed to IPsec reconfig mbox");
395 		return ret;
396 	}
397 
398 	/*
399 	 * Not all commands and callers make use of response message data. But
400 	 * leave this up to the caller and always read and store the full
401 	 * response. One example where the data is needed is for statistics.
402 	 */
403 	for (i = 0; i < msg_size; i++)
404 		msg->raw[i] = nn_cfg_readl(hw, NFP_NET_CFG_MBOX_VAL + 4 * i);
405 
406 	switch (msg->rsp) {
407 	case NFP_IPSEC_CFG_MSG_OK:
408 		ret = 0;
409 		break;
410 	case NFP_IPSEC_CFG_MSG_SA_INVALID_CMD:
411 		ret = -EINVAL;
412 		break;
413 	case NFP_IPSEC_CFG_MSG_SA_VALID:
414 		ret = -EEXIST;
415 		break;
416 	case NFP_IPSEC_CFG_MSG_FAILED:
417 		/* FALLTHROUGH */
418 	case NFP_IPSEC_CFG_MSG_SA_HASH_ADD_FAILED:
419 		/* FALLTHROUGH */
420 	case NFP_IPSEC_CFG_MSG_SA_HASH_DEL_FAILED:
421 		ret = -EIO;
422 		break;
423 	default:
424 		ret = -EDOM;
425 	}
426 
427 	return ret;
428 }
429 
430 /**
431  * Get discards packet statistics for each SA
432  *
433  * The sa_discard_stats contains the statistics of discards packets
434  * of an SA. This function calculates the sum total of discarded packets.
435  *
436  * @param errors
437  *   The value is SA discards packet sum total
438  * @param sa_discard_stats
439  *   The struct is SA discards packet Statistics
440  */
441 static void
442 nfp_get_errorstats(uint64_t *errors,
443 		struct ipsec_discard_stats *sa_discard_stats)
444 {
445 	uint32_t i;
446 	uint32_t len;
447 	uint32_t *perror;
448 
449 	perror = &sa_discard_stats->discards_auth;
450 	len = sizeof(struct ipsec_discard_stats) / sizeof(uint32_t);
451 
452 	for (i = 0; i < len; i++)
453 		*errors += *perror++;
454 
455 	*errors -= sa_discard_stats->ipv4_id_counter;
456 }
457 
458 static int
459 nfp_security_session_get_stats(void *device,
460 		struct rte_security_session *session,
461 		struct rte_security_stats *stats)
462 {
463 	int ret;
464 	struct nfp_net_hw *hw;
465 	struct nfp_ipsec_msg msg;
466 	struct rte_eth_dev *eth_dev;
467 	struct ipsec_get_sa_stats *cfg_s;
468 	struct rte_security_ipsec_stats *ips_s;
469 	struct nfp_ipsec_session *priv_session;
470 	enum rte_security_ipsec_sa_direction direction;
471 
472 	eth_dev = device;
473 	priv_session = SECURITY_GET_SESS_PRIV(session);
474 	memset(&msg, 0, sizeof(msg));
475 	msg.cmd = NFP_IPSEC_CFG_MSG_GET_SA_STATS;
476 	msg.sa_idx = priv_session->sa_index;
477 	hw = NFP_NET_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
478 
479 	ret = nfp_ipsec_cfg_cmd_issue(hw, &msg);
480 	if (ret < 0) {
481 		PMD_DRV_LOG(ERR, "Failed to get SA stats");
482 		return ret;
483 	}
484 
485 	cfg_s = &msg.cfg_get_stats;
486 	direction = priv_session->ipsec.direction;
487 	memset(stats, 0, sizeof(struct rte_security_stats)); /* Start with zeros */
488 	stats->protocol = RTE_SECURITY_PROTOCOL_IPSEC;
489 	ips_s = &stats->ipsec;
490 
491 	/* Only display SA if any counters are non-zero */
492 	if (cfg_s->lifetime_byte_count != 0 || cfg_s->pkt_count != 0) {
493 		if (direction == RTE_SECURITY_IPSEC_SA_DIR_INGRESS) {
494 			ips_s->ipackets = cfg_s->pkt_count;
495 			ips_s->ibytes = cfg_s->lifetime_byte_count;
496 			nfp_get_errorstats(&ips_s->ierrors, &cfg_s->sa_discard_stats);
497 		} else {
498 			ips_s->opackets = cfg_s->pkt_count;
499 			ips_s->obytes = cfg_s->lifetime_byte_count;
500 			nfp_get_errorstats(&ips_s->oerrors, &cfg_s->sa_discard_stats);
501 		}
502 	}
503 
504 	return 0;
505 }
506 
507 static const struct rte_security_capability *
508 nfp_crypto_capabilities_get(void *device __rte_unused)
509 {
510 	return nfp_security_caps;
511 }
512 
513 static uint32_t
514 nfp_security_session_get_size(void *device __rte_unused)
515 {
516 	return sizeof(struct nfp_ipsec_session);
517 }
518 
519 static const struct rte_security_ops nfp_security_ops = {
520 	.session_get_size = nfp_security_session_get_size,
521 	.session_stats_get = nfp_security_session_get_stats,
522 	.capabilities_get = nfp_crypto_capabilities_get,
523 };
524 
525 static int
526 nfp_ipsec_ctx_create(struct rte_eth_dev *dev,
527 		struct nfp_net_ipsec_data *data)
528 {
529 	struct rte_security_ctx *ctx;
530 	static const struct rte_mbuf_dynfield pkt_md_dynfield = {
531 		.name = "nfp_ipsec_crypto_pkt_metadata",
532 		.size = sizeof(struct nfp_tx_ipsec_desc_msg),
533 		.align = __alignof__(struct nfp_tx_ipsec_desc_msg),
534 	};
535 
536 	ctx = rte_zmalloc("security_ctx",
537 			sizeof(struct rte_security_ctx), 0);
538 	if (ctx == NULL) {
539 		PMD_INIT_LOG(ERR, "Failed to malloc security_ctx");
540 		return -ENOMEM;
541 	}
542 
543 	ctx->device = dev;
544 	ctx->ops = &nfp_security_ops;
545 	ctx->sess_cnt = 0;
546 	dev->security_ctx = ctx;
547 
548 	data->pkt_dynfield_offset = rte_mbuf_dynfield_register(&pkt_md_dynfield);
549 	if (data->pkt_dynfield_offset < 0) {
550 		PMD_INIT_LOG(ERR, "Failed to register mbuf esn_dynfield");
551 		return -ENOMEM;
552 	}
553 
554 	return 0;
555 }
556 
557 int
558 nfp_ipsec_init(struct rte_eth_dev *dev)
559 {
560 	int ret;
561 	uint32_t cap_extend;
562 	struct nfp_net_hw *hw;
563 	struct nfp_net_ipsec_data *data;
564 
565 	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
566 
567 	cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1);
568 	if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) == 0) {
569 		PMD_INIT_LOG(INFO, "Unsupported IPsec extend capability");
570 		return 0;
571 	}
572 
573 	data = rte_zmalloc("ipsec_data", sizeof(struct nfp_net_ipsec_data), 0);
574 	if (data == NULL) {
575 		PMD_INIT_LOG(ERR, "Failed to malloc ipsec_data");
576 		return -ENOMEM;
577 	}
578 
579 	data->pkt_dynfield_offset = -1;
580 	data->sa_free_cnt = NFP_NET_IPSEC_MAX_SA_CNT;
581 	hw->ipsec_data = data;
582 
583 	ret = nfp_ipsec_ctx_create(dev, data);
584 	if (ret != 0) {
585 		PMD_INIT_LOG(ERR, "Failed to create IPsec ctx");
586 		goto ipsec_cleanup;
587 	}
588 
589 	return 0;
590 
591 ipsec_cleanup:
592 	nfp_ipsec_uninit(dev);
593 
594 	return ret;
595 }
596 
597 static void
598 nfp_ipsec_ctx_destroy(struct rte_eth_dev *dev)
599 {
600 	if (dev->security_ctx != NULL)
601 		rte_free(dev->security_ctx);
602 }
603 
604 void
605 nfp_ipsec_uninit(struct rte_eth_dev *dev)
606 {
607 	uint16_t i;
608 	uint32_t cap_extend;
609 	struct nfp_net_hw *hw;
610 	struct nfp_ipsec_session *priv_session;
611 
612 	hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private);
613 
614 	cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1);
615 	if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) == 0) {
616 		PMD_INIT_LOG(INFO, "Unsupported IPsec extend capability");
617 		return;
618 	}
619 
620 	nfp_ipsec_ctx_destroy(dev);
621 
622 	if (hw->ipsec_data == NULL) {
623 		PMD_INIT_LOG(INFO, "IPsec data is NULL!");
624 		return;
625 	}
626 
627 	for (i = 0; i < NFP_NET_IPSEC_MAX_SA_CNT; i++) {
628 		priv_session = hw->ipsec_data->sa_entries[i];
629 		if (priv_session != NULL)
630 			memset(priv_session, 0, sizeof(struct nfp_ipsec_session));
631 	}
632 
633 	rte_free(hw->ipsec_data);
634 }
635 
636