API Reference Manual 1.51.0
Loading...
Searching...
No Matches
odp_ipsecfwd.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2022-2023 Nokia
3 */
4
14#ifndef _GNU_SOURCE
15#define _GNU_SOURCE
16#endif
17
18#include <stdlib.h>
19#include <signal.h>
20#include <stdio.h>
21#include <inttypes.h>
22#include <string.h>
23
24#include <errno.h>
25
26#include <odp_api.h>
27#include <odp/helper/odph_api.h>
28#include <libconfig.h>
29
30#define PROG_NAME "odp_ipsecfwd"
31#define SHORT_PROG_NAME "ipsfwd"
32#define DELIMITER ","
33
34#define MAX_IFS 2U
35#define MAX_SAS 4000U
36#define MAX_FWDS 64U
37#define MAX_SPIS (UINT16_MAX + 1U)
38#define MAX_WORKERS (ODP_THREAD_COUNT_MAX - 1)
39#define MAX_QUEUES 64U
40#define MAX_SA_QUEUES 1024U
41#define PKT_SIZE 1024U
42#define PKT_CNT 32768U
43#define MAX_BURST 32U
44#define ORDERED 0U
45#define IP_ADDR_LEN 32U
46
47#define ALG_ENTRY(_alg_name, _type) \
48 { \
49 .idx = (_alg_name), \
50 .type = (_type), \
51 .name = #_alg_name \
52 }
53
54enum {
55 CIPHER_TYPE,
56 COMB_CIPHER_TYPE,
57 AUTH_TYPE,
58 COMB_AUTH_TYPE
59};
60
61typedef enum {
62 PRS_OK,
63 PRS_NOK,
64 PRS_TERM
65} parse_result_t;
66
67enum {
68 DIR_IN = 0,
69 DIR_OUT
70};
71
72typedef struct pktio_s pktio_t;
73
74typedef struct pktio_s {
75 union {
76 odp_pktout_queue_t out_dir_qs[MAX_QUEUES];
77 odp_queue_t out_ev_qs[MAX_QUEUES];
78 };
79
80 odp_pktin_queue_t in_dir_qs[MAX_QUEUES];
81 odph_ethaddr_t src_mac;
82 char *name;
83 odp_pktio_t handle;
84 uint32_t (*send_fn)(const pktio_t *pktio, uint8_t index, odp_packet_t pkts[], int num);
85 uint32_t num_tx_qs;
86 uint8_t idx;
87} pktio_t;
88
89typedef struct {
90 uint32_t prefix;
91 uint32_t mask;
92 odph_ethaddr_t dst_mac;
93 const pktio_t *pktio;
94} fwd_entry_t;
95
96typedef struct {
97 fwd_entry_t entries[MAX_FWDS];
98 uint32_t num;
99} lookup_table_t;
100
101typedef struct {
102 uint64_t ipsec_in_pkts;
103 uint64_t ipsec_out_pkts;
104 uint64_t ipsec_in_errs;
105 uint64_t ipsec_out_errs;
106 uint64_t status_errs;
107 uint64_t fwd_pkts;
108 uint64_t discards;
109} stats_t;
110
111typedef struct prog_config_s prog_config_t;
112
113typedef struct ODP_ALIGNED_CACHE {
114 stats_t stats;
115 prog_config_t *prog_config;
116 int thr_idx;
117 uint8_t pktio;
118} thread_config_t;
119
120typedef struct {
121 odp_ipsec_sa_param_t sa_param;
122 char cipher_key[65U];
123 char cipher_key_extra[5U];
124 char auth_key[65U];
125 char auth_key_extra[5U];
126 odp_u32be_t lkp_dst_ip;
127 odp_u32be_t src_ip;
128 odp_u32be_t dst_ip;
129} sa_config_t;
130
131typedef uint32_t (*rx_fn_t)(thread_config_t *config, odp_event_t evs[], int num);
132typedef void (*ipsec_fn_t)(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl, stats_t *stats);
133typedef void (*drain_fn_t)(prog_config_t *config);
134
135typedef struct {
136 rx_fn_t rx;
137 ipsec_fn_t proc;
138 ipsec_fn_t compl;
139 drain_fn_t drain;
140} ops_t;
141
142typedef struct prog_config_s {
143 odph_thread_t thread_tbl[MAX_WORKERS];
144 thread_config_t thread_config[MAX_WORKERS];
145 odp_ipsec_sa_t sas[MAX_SAS];
146 odp_queue_t sa_qs[MAX_SA_QUEUES];
147 pktio_t pktios[MAX_IFS];
148 lookup_table_t fwd_tbl;
149 odp_atomic_u32_t is_running;
150 sa_config_t default_cfg;
151 ops_t ops;
152 char *conf_file;
154 odp_queue_t compl_q;
155 odp_pool_t pktio_pool;
156 odp_barrier_t init_barrier;
157 odp_barrier_t term_barrier;
158 uint32_t num_input_qs;
159 uint32_t num_sa_qs;
160 uint32_t num_output_qs;
161 uint32_t num_pkts;
162 uint32_t pkt_len;
163 uint32_t num_ifs;
164 uint32_t num_sas;
165 int num_thrs;
166 odp_bool_t is_dir_rx;
167 odp_bool_t is_hashed_tx;
168 uint8_t mode;
169} prog_config_t;
170
171typedef struct {
172 const char *name;
173 int idx;
174 int type;
175} exposed_alg_t;
176
177typedef struct {
178 odp_packet_t pkts[MAX_BURST];
179 const pktio_t *pktio;
180 uint32_t num;
181} pkt_vec_t;
182
183typedef struct {
184 pkt_vec_t vecs[MAX_QUEUES];
185 uint8_t num_qs;
186} pkt_out_t;
187
188typedef struct {
189 pkt_out_t ifs[MAX_IFS];
190 odp_bool_t is_hashed_tx;
191 uint8_t q_idx;
192} pkt_ifs_t;
193
194static const exposed_alg_t exposed_algs[] = {
195 ALG_ENTRY(ODP_CIPHER_ALG_NULL, CIPHER_TYPE),
196 ALG_ENTRY(ODP_CIPHER_ALG_DES, CIPHER_TYPE),
197 ALG_ENTRY(ODP_CIPHER_ALG_3DES_CBC, CIPHER_TYPE),
198 ALG_ENTRY(ODP_CIPHER_ALG_AES_CBC, CIPHER_TYPE),
199 ALG_ENTRY(ODP_CIPHER_ALG_AES_CTR, CIPHER_TYPE),
200 ALG_ENTRY(ODP_CIPHER_ALG_AES_ECB, CIPHER_TYPE),
201 ALG_ENTRY(ODP_CIPHER_ALG_AES_GCM, COMB_CIPHER_TYPE),
202 ALG_ENTRY(ODP_CIPHER_ALG_AES_CCM, COMB_CIPHER_TYPE),
203 ALG_ENTRY(ODP_CIPHER_ALG_CHACHA20_POLY1305, COMB_CIPHER_TYPE),
204 ALG_ENTRY(ODP_AUTH_ALG_NULL, AUTH_TYPE),
205 ALG_ENTRY(ODP_AUTH_ALG_MD5_HMAC, AUTH_TYPE),
206 ALG_ENTRY(ODP_AUTH_ALG_SHA1_HMAC, AUTH_TYPE),
207 ALG_ENTRY(ODP_AUTH_ALG_SHA224_HMAC, AUTH_TYPE),
208 ALG_ENTRY(ODP_AUTH_ALG_SHA256_HMAC, AUTH_TYPE),
209 ALG_ENTRY(ODP_AUTH_ALG_SHA384_HMAC, AUTH_TYPE),
210 ALG_ENTRY(ODP_AUTH_ALG_SHA512_HMAC, AUTH_TYPE),
211 ALG_ENTRY(ODP_AUTH_ALG_AES_GCM, COMB_AUTH_TYPE),
212 ALG_ENTRY(ODP_AUTH_ALG_AES_GMAC, AUTH_TYPE),
213 ALG_ENTRY(ODP_AUTH_ALG_AES_CCM, COMB_AUTH_TYPE),
214 ALG_ENTRY(ODP_AUTH_ALG_AES_CMAC, AUTH_TYPE),
215 ALG_ENTRY(ODP_AUTH_ALG_AES_XCBC_MAC, AUTH_TYPE),
216 ALG_ENTRY(ODP_AUTH_ALG_CHACHA20_POLY1305, COMB_AUTH_TYPE)
217};
218
219/* SPIs for in and out directions */
220static odp_ipsec_sa_t *spi_to_sa_map[2U][MAX_SPIS];
221static const int ipsec_out_mark;
222static __thread pkt_ifs_t ifs;
223static prog_config_t *prog_conf;
224
225static void init_config(prog_config_t *config)
226{
227 memset(config, 0, sizeof(*config));
228 odp_ipsec_sa_param_init(&config->default_cfg.sa_param);
229 config->compl_q = ODP_QUEUE_INVALID;
230 config->pktio_pool = ODP_POOL_INVALID;
231 config->num_input_qs = 1;
232 config->num_sa_qs = 1;
233 config->num_output_qs = 1;
234 config->num_thrs = 1;
235}
236
237static void terminate(int signal ODP_UNUSED)
238{
239 odp_atomic_store_u32(&prog_conf->is_running, 0U);
240}
241
242static void parse_interfaces(prog_config_t *config, const char *optarg)
243{
244 char *tmp_str = strdup(optarg), *tmp;
245
246 if (tmp_str == NULL)
247 return;
248
249 tmp = strtok(tmp_str, DELIMITER);
250
251 while (tmp && config->num_ifs < MAX_IFS) {
252 config->pktios[config->num_ifs].name = strdup(tmp);
253
254 if (config->pktios[config->num_ifs].name != NULL)
255 ++config->num_ifs;
256
257 tmp = strtok(NULL, DELIMITER);
258 }
259
260 free(tmp_str);
261}
262
263static void print_supported_algos(const odp_ipsec_capability_t *ipsec_capa)
264{
265 int c_cnt, a_cnt;
266 const size_t len = ODPH_ARRAY_SIZE(exposed_algs);
267
268 printf(" Cipher algorithms:\n");
269
270 for (size_t i = 0U; i < len; ++i) {
271 if ((exposed_algs[i].type == CIPHER_TYPE ||
272 exposed_algs[i].type == COMB_CIPHER_TYPE) &&
273 (ipsec_capa->ciphers.all_bits & (1 << exposed_algs[i].idx)) > 0U) {
274 c_cnt = odp_ipsec_cipher_capability(exposed_algs[i].idx, NULL, 0);
275
276 if (c_cnt < 0)
277 continue;
278
279 printf(" %d: %s",
280 exposed_algs[i].idx, exposed_algs[i].name);
281 printf(exposed_algs[i].type == COMB_CIPHER_TYPE ? " (combined)" : "");
282
284
285 (void)odp_ipsec_cipher_capability(exposed_algs[i].idx, capa, c_cnt);
286
287 for (int j = 0; j < c_cnt; ++j)
288 printf(j == 0 ? " (key lengths: %u" : ", %u", capa[j].key_len);
289
290 printf(")\n");
291 }
292 }
293
294 printf(" Authentication algorithms:\n");
295
296 for (size_t i = 0U; i < len; ++i) {
297 if ((exposed_algs[i].type == AUTH_TYPE ||
298 exposed_algs[i].type == COMB_AUTH_TYPE) &&
299 (ipsec_capa->auths.all_bits & (1 << exposed_algs[i].idx)) > 0U) {
300 a_cnt = odp_ipsec_auth_capability(exposed_algs[i].idx, NULL, 0);
301
302 if (a_cnt < 0)
303 continue;
304
305 printf(" %d: %s",
306 exposed_algs[i].idx, exposed_algs[i].name);
307 printf(exposed_algs[i].type == COMB_AUTH_TYPE ? " (combined)" : "");
308
309 odp_ipsec_auth_capability_t capa[a_cnt];
310
311 (void)odp_ipsec_auth_capability(exposed_algs[i].idx, capa, a_cnt);
312
313 for (int j = 0; j < a_cnt; ++j)
314 printf(j == 0 ? " (key/icv lengths: %u/%u" : ", %u/%u",
315 capa[j].key_len, capa[j].icv_len);
316
317 printf(")\n");
318 }
319 }
320}
321
322static void print_usage(void)
323{
324 odp_pool_capability_t pool_capa;
325 odp_ipsec_capability_t ipsec_capa;
326
327 if (odp_pool_capability(&pool_capa) < 0) {
328 ODPH_ERR("Error querying pool capabilities\n");
329 return;
330 }
331
332 if (odp_ipsec_capability(&ipsec_capa) < 0) {
333 ODPH_ERR("Error querying IPsec capabilities\n");
334 return;
335 }
336
337 printf("\n"
338 "Simple IPsec performance tester. Forward and process plain and IPsec packets.\n"
339 "\n"
340 "Usage: %s OPTIONS\n"
341 "\n"
342 " E.g. %s -i ens9f1 -C /etc/odp/ipsecfwd.conf\n"
343 "\n"
344 " With ipsecfwd.conf containing, for example:\n"
345 " default: {\n"
346 " dir = 1\n"
347 " proto = 0\n"
348 " mode = 0\n"
349 " crypto: {\n"
350 " cipher_alg = 4\n"
351 " cipher_key = \"jWnZr4t7w!zwC*F-\"\n"
352 " auth_alg = 2\n"
353 " auth_key = \"n2r5u7x!A%%D*\"\n"
354 " icv_len = 12\n"
355 " };\n"
356 " };\n"
357 "\n"
358 " sa: (\n"
359 " {\n"
360 " spi = 1337\n"
361 " outbound: {\n"
362 " tunnel: {\n"
363 " src_addr = \"192.168.1.10\"\n"
364 " dst_addr = \"192.168.1.16\"\n"
365 " };\n"
366 " };\n"
367 " },\n"
368 " {\n"
369 " spi = 1338\n"
370 " outbound: {\n"
371 " tunnel: {\n"
372 " src_addr = \"192.168.3.110\"\n"
373 " dst_addr = \"192.168.3.116\"\n"
374 " };\n"
375 " };\n"
376 " }\n"
377 " );\n"
378 "\n"
379 " fwd: (\n"
380 " {\n"
381 " prefix: \"192.168.1.0/24\"\n"
382 " if: \"ens9f1\"\n"
383 " dst_mac: \"00:00:05:00:07:00\"\n"
384 " },\n"
385 " {\n"
386 " prefix: \"192.1.0.0/16\"\n"
387 " if: \"ens9f0\"\n"
388 " dst_mac: \"00:00:05:00:08:00\"\n"
389 " }\n"
390 " );\n"
391 "\n"
392 "Mandatory OPTIONS:\n"
393 "\n"
394 " -i, --interfaces Ethernet interfaces for packet I/O, comma-separated,\n"
395 " no spaces.\n"
396 " -C, --conf Configuration file. 'libconfig' syntax is expected.\n"
397 " SA configuration supports default fallback, i.e.\n"
398 " individual SA configuration blocks may omit some\n"
399 " parameters and instead set these once in default block\n"
400 " which then are used to fill missing parameters. The only\n"
401 " required argument for an SA is the 'spi' parameter.\n"
402 " Individual SA parameter blocks are expected to be in\n"
403 " 'sa'-named list. Parameter naming follows API\n"
404 " specification, see 'odp_ipsec_sa_param_t' for parameter\n"
405 " names and hierarchy. Traffic is mapped to SAs based on UDP\n"
406 " port: the port is used as the SPI. For forwarding entries,\n"
407 " individual parameter blocks are similarly expected to be\n"
408 " in 'fwd'-named list. With forwarding entries, every\n"
409 " parameter is always required and interfaces present in\n"
410 " forwarding entries should be one of the interfaces passed\n"
411 " with '--interfaces' option. The entries are looked up\n"
412 " in the order they are in the list. See example above for\n"
413 " potential SA and forwarding configuration.\n"
414 "\n"
415 " Supported cipher and authentication algorithms for SAs:\n",
416 PROG_NAME, PROG_NAME);
417 print_supported_algos(&ipsec_capa);
418 printf("\n"
419 "Optional OPTIONS:\n"
420 "\n"
421 " -n, --num_pkts Number of packet buffers allocated for packet I/O pool.\n"
422 " %u by default.\n"
423 " -l, --pkt_len Maximum size of packet buffers in packet I/O pool. %u by\n"
424 " default.\n"
425 " -c, --count Worker thread count. 1 by default.\n"
426 " -m, --mode Queueing mode.\n"
427 " 0: ordered (default)\n"
428 " 1: parallel\n"
429 " -I, --num_input_qs Input queue count. 1 by default.\n"
430 " -S, --num_sa_qs SA queue count. 1 by default.\n"
431 " -O, --num_output_qs Output queue count. 1 by default.\n"
432 " -d, --direct_rx Use direct RX. Interfaces will be polled by workers\n"
433 " directly. '--mode', '--num_input_qs' and '--num_output_qs'\n"
434 " options are ignored, input and output queue counts will\n"
435 " match worker count.\n"
436 " -h, --help This help.\n"
437 "\n", pool_capa.pkt.max_num > 0U ? ODPH_MIN(pool_capa.pkt.max_num, PKT_CNT) :
438 PKT_CNT, pool_capa.pkt.max_len > 0U ? ODPH_MIN(pool_capa.pkt.max_len, PKT_SIZE) :
439 PKT_SIZE);
440}
441
442static inline odp_ipsec_sa_t *get_in_sa(odp_packet_t pkt)
443{
444 odph_esphdr_t esp;
445 uint32_t spi;
446
447 if (!odp_packet_has_ipsec(pkt))
448 return NULL;
449
450 if (odp_packet_copy_to_mem(pkt, odp_packet_l4_offset(pkt), ODPH_ESPHDR_LEN, &esp) < 0)
451 return NULL;
452
453 spi = odp_be_to_cpu_32(esp.spi);
454
455 return spi <= UINT16_MAX ? spi_to_sa_map[DIR_IN][spi] : NULL;
456}
457
458static inline int process_ipsec_in_enq(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num)
459{
461 int left, sent = 0, ret;
462
463 memset(&param, 0, sizeof(param));
464 /* IPsec in/out need to be identified somehow, so use user_ptr for this. */
465 for (int i = 0; i < num; ++i)
466 odp_packet_user_ptr_set(pkts[i], NULL);
467
468 while (sent < num) {
469 left = num - sent;
470 param.num_sa = left;
471 param.sa = &sas[sent];
472 ret = odp_ipsec_in_enq(&pkts[sent], left, &param);
473
474 if (odp_unlikely(ret <= 0))
475 break;
476
477 sent += ret;
478 }
479
480 return sent;
481}
482
483static inline odp_ipsec_sa_t *get_out_sa(odp_packet_t pkt)
484{
485 odph_udphdr_t udp;
486 uint16_t dst_port;
487
488 if (!odp_packet_has_udp(pkt))
489 return NULL;
490
491 if (odp_packet_copy_to_mem(pkt, odp_packet_l4_offset(pkt), ODPH_UDPHDR_LEN, &udp) < 0)
492 return NULL;
493
494 dst_port = odp_be_to_cpu_16(udp.dst_port);
495
496 return dst_port ? spi_to_sa_map[DIR_OUT][dst_port] : NULL;
497}
498
499static inline int process_ipsec_out_enq(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num)
500{
502 int left, sent = 0, ret;
503
504 memset(&param, 0, sizeof(param));
505 /* IPsec in/out need to be identified somehow, so use user_ptr for this. */
506 for (int i = 0; i < num; ++i)
507 odp_packet_user_ptr_set(pkts[i], &ipsec_out_mark);
508
509 while (sent < num) {
510 left = num - sent;
511 param.num_sa = left;
512 param.sa = &sas[sent];
513 ret = odp_ipsec_out_enq(&pkts[sent], left, &param);
514
515 if (odp_unlikely(ret <= 0))
516 break;
517
518 sent += ret;
519 }
520
521 return sent;
522}
523
524static inline const fwd_entry_t *get_fwd_entry(lookup_table_t *table, uint32_t ip)
525{
526 fwd_entry_t *entry;
527
528 for (uint32_t i = 0U; i < table->num; ++i) {
529 entry = &table->entries[i];
530
531 if ((ip & entry->mask) == entry->prefix)
532 return entry;
533 }
534
535 return NULL;
536}
537
538static inline const pktio_t *lookup_and_apply(odp_packet_t pkt, lookup_table_t *fwd_tbl,
539 uint8_t *q_idx)
540{
541 const uint32_t l3_off = odp_packet_l3_offset(pkt);
542 odph_ipv4hdr_t ipv4;
543 uint32_t dst_ip, src_ip;
544 const fwd_entry_t *fwd;
545 odph_ethhdr_t eth;
546
547 if (odp_packet_copy_to_mem(pkt, l3_off, ODPH_IPV4HDR_LEN, &ipv4) < 0)
548 return NULL;
549
550 dst_ip = odp_be_to_cpu_32(ipv4.dst_addr);
551 fwd = get_fwd_entry(fwd_tbl, dst_ip);
552
553 if (fwd == NULL)
554 return NULL;
555
556 if (l3_off != ODPH_ETHHDR_LEN) {
557 if (l3_off > ODPH_ETHHDR_LEN) {
558 if (odp_packet_pull_head(pkt, l3_off - ODPH_ETHHDR_LEN) == NULL)
559 return NULL;
560 } else {
561 if (odp_packet_push_head(pkt, ODPH_ETHHDR_LEN - l3_off) == NULL)
562 return NULL;
563 }
564 }
565
566 eth.dst = fwd->dst_mac;
567 eth.src = fwd->pktio->src_mac;
568 eth.type = odp_cpu_to_be_16(ODPH_ETHTYPE_IPV4);
569
570 if (odp_packet_copy_from_mem(pkt, 0U, ODPH_ETHHDR_LEN, &eth) < 0)
571 return NULL;
572
573 if (q_idx != NULL) {
574 src_ip = odp_be_to_cpu_32(ipv4.src_addr);
575 *q_idx = (src_ip ^ dst_ip) % fwd->pktio->num_tx_qs;
576 }
577
578 return fwd->pktio;
579}
580
581static inline uint32_t forward_packets(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl)
582{
583 odp_packet_t pkt;
584 odp_bool_t is_hashed_tx = ifs.is_hashed_tx;
585 uint8_t q_idx = is_hashed_tx ? 0U : ifs.q_idx, qs_done;
586 uint8_t *q_idx_ptr = is_hashed_tx ? &q_idx : NULL;
587 const pktio_t *pktio;
588 pkt_out_t *out;
589 pkt_vec_t *vec;
590 uint32_t num_procd = 0U, ret;
591
592 for (int i = 0; i < num; ++i) {
593 pkt = pkts[i];
594 pktio = lookup_and_apply(pkt, fwd_tbl, q_idx_ptr);
595
596 if (pktio == NULL) {
597 odp_packet_free(pkt);
598 continue;
599 }
600
601 out = &ifs.ifs[pktio->idx];
602 vec = &out->vecs[q_idx];
603
604 if (vec->num == 0U)
605 out->num_qs++;
606
607 vec->pkts[vec->num++] = pkt;
608 vec->pktio = pktio;
609 }
610
611 for (uint32_t i = 0U; i < MAX_IFS; ++i) {
612 qs_done = 0U;
613 out = &ifs.ifs[i];
614
615 for (uint32_t j = 0U; j < MAX_QUEUES && qs_done < out->num_qs; ++j) {
616 if (out->vecs[j].num == 0U)
617 continue;
618
619 vec = &out->vecs[j];
620 pktio = vec->pktio;
621 ret = pktio->send_fn(pktio, j, vec->pkts, vec->num);
622
623 if (odp_unlikely(ret < vec->num))
624 odp_packet_free_multi(&vec->pkts[ret], vec->num - ret);
625
626 ++qs_done;
627 vec->num = 0U;
628 num_procd += ret;
629 }
630
631 out->num_qs = 0U;
632 }
633
634 return num_procd;
635}
636
637static inline void process_packets_out_enq(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
638 stats_t *stats)
639{
640 odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_fwd[MAX_BURST];
641 odp_ipsec_sa_t *sa, sas[MAX_BURST];
642 int num_pkts_ips = 0, num_pkts_fwd = 0, num_procd;
643
644 for (int i = 0; i < num; ++i) {
645 pkt = pkts[i];
646 sa = get_out_sa(pkt);
647
648 if (sa != NULL) {
649 sas[num_pkts_ips] = *sa;
650 pkts_ips[num_pkts_ips] = pkt;
651 ++num_pkts_ips;
652 } else {
653 pkts_fwd[num_pkts_fwd++] = pkt;
654 }
655 }
656
657 if (num_pkts_ips > 0) {
658 num_procd = process_ipsec_out_enq(pkts_ips, sas, num_pkts_ips);
659
660 if (odp_unlikely(num_procd < num_pkts_ips)) {
661 stats->ipsec_out_errs += num_pkts_ips - num_procd;
662 odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
663 }
664 }
665
666 if (num_pkts_fwd > 0) {
667 num_procd = forward_packets(pkts_fwd, num_pkts_fwd, fwd_tbl);
668 stats->discards += num_pkts_fwd - num_procd;
669 stats->fwd_pkts += num_procd;
670 }
671}
672
673static void process_packets_in_enq(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
674 stats_t *stats)
675{
676 odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_out[MAX_BURST];
677 odp_ipsec_sa_t *sa, sas[MAX_BURST];
678 int num_pkts_ips = 0, num_pkts_out = 0, num_procd;
679
680 for (int i = 0; i < num; ++i) {
681 pkt = pkts[i];
682
684 ++stats->discards;
685 odp_packet_free(pkt);
686 continue;
687 }
688
689 sa = get_in_sa(pkt);
690
691 if (sa != NULL) {
692 sas[num_pkts_ips] = *sa;
693 pkts_ips[num_pkts_ips] = pkt;
694 ++num_pkts_ips;
695 } else {
696 pkts_out[num_pkts_out++] = pkt;
697 }
698 }
699
700 if (num_pkts_ips > 0) {
701 num_procd = process_ipsec_in_enq(pkts_ips, sas, num_pkts_ips);
702
703 if (odp_unlikely(num_procd < num_pkts_ips)) {
704 stats->ipsec_in_errs += num_pkts_ips - num_procd;
705 odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
706 }
707 }
708
709 if (num_pkts_out > 0)
710 process_packets_out_enq(pkts_out, num_pkts_out, fwd_tbl, stats);
711}
712
713static inline odp_bool_t is_ipsec_in(odp_packet_t pkt)
714{
715 return odp_packet_user_ptr(pkt) == NULL;
716}
717
718static void complete_ipsec_ops(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
719 stats_t *stats)
720{
721 odp_packet_t pkt, pkts_out[MAX_BURST], pkts_fwd[MAX_BURST];
722 odp_bool_t is_in;
724 int num_pkts_out = 0, num_pkts_fwd = 0, num_procd;
725
726 for (int i = 0; i < num; ++i) {
727 pkt = pkts[i];
728 is_in = is_ipsec_in(pkt);
729
730 if (odp_unlikely(odp_ipsec_result(&result, pkt) < 0)) {
731 is_in ? ++stats->ipsec_in_errs : ++stats->ipsec_out_errs;
732 odp_packet_free(pkt);
733 continue;
734 }
735
736 if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
737 is_in ? ++stats->ipsec_in_errs : ++stats->ipsec_out_errs;
738 odp_packet_free(pkt);
739 continue;
740 }
741
742 if (is_in) {
743 ++stats->ipsec_in_pkts;
744 pkts_out[num_pkts_out++] = pkt;
745 } else {
746 ++stats->ipsec_out_pkts;
747 pkts_fwd[num_pkts_fwd++] = pkt;
748 }
749 }
750
751 if (num_pkts_out > 0)
752 process_packets_out_enq(pkts_out, num_pkts_out, fwd_tbl, stats);
753
754 if (num_pkts_fwd > 0) {
755 num_procd = forward_packets(pkts_fwd, num_pkts_fwd, fwd_tbl);
756 stats->discards += num_pkts_fwd - num_procd;
757 stats->fwd_pkts += num_procd;
758 }
759}
760
761static void drain_scheduler(prog_config_t *config ODP_UNUSED)
762{
763 odp_event_t ev;
764
765 while (true) {
767
768 if (ev == ODP_EVENT_INVALID)
769 break;
770
771 odp_event_free(ev);
772 }
773}
774
775static inline int process_ipsec_in(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num,
776 odp_packet_t pkts_out[])
777{
779 int left, sent = 0, num_out, ret;
780
781 memset(&param, 0, sizeof(param));
782
783 while (sent < num) {
784 left = num - sent;
785 num_out = left;
786 param.num_sa = left;
787 param.sa = &sas[sent];
788 ret = odp_ipsec_in(&pkts[sent], left, &pkts_out[sent], &num_out, &param);
789
790 if (odp_unlikely(ret <= 0))
791 break;
792
793 sent += ret;
794 }
795
796 return sent;
797}
798
799static inline int process_ipsec_out(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num,
800 odp_packet_t pkts_out[])
801{
803 int left, sent = 0, num_out, ret;
804
805 memset(&param, 0, sizeof(param));
806
807 while (sent < num) {
808 left = num - sent;
809 num_out = left;
810 param.num_sa = left;
811 param.sa = &sas[sent];
812 ret = odp_ipsec_out(&pkts[sent], left, &pkts_out[sent], &num_out, &param);
813
814 if (odp_unlikely(ret <= 0))
815 break;
816
817 sent += ret;
818 }
819
820 return sent;
821}
822
823static inline void process_packets_out(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
824 stats_t *stats)
825{
826 odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_fwd[MAX_BURST], pkts_ips_out[MAX_BURST];
827 odp_ipsec_sa_t *sa, sas[MAX_BURST];
828 int num_pkts_ips = 0, num_pkts_fwd = 0, num_procd;
830
831 for (int i = 0; i < num; ++i) {
832 pkt = pkts[i];
833 sa = get_out_sa(pkt);
834
835 if (sa != NULL) {
836 sas[num_pkts_ips] = *sa;
837 pkts_ips[num_pkts_ips] = pkt;
838 ++num_pkts_ips;
839 } else {
840 pkts_fwd[num_pkts_fwd++] = pkt;
841 }
842 }
843
844 if (num_pkts_ips > 0) {
845 num_procd = process_ipsec_out(pkts_ips, sas, num_pkts_ips, pkts_ips_out);
846
847 if (odp_unlikely(num_procd < num_pkts_ips)) {
848 stats->ipsec_out_errs += num_pkts_ips - num_procd;
849 odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
850 }
851
852 for (int i = 0; i < num_procd; ++i) {
853 pkt = pkts_ips_out[i];
854
855 if (odp_unlikely(odp_ipsec_result(&result, pkt) < 0)) {
856 ++stats->ipsec_out_errs;
857 odp_packet_free(pkt);
858 continue;
859 }
860
861 if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
862 ++stats->ipsec_out_errs;
863 odp_packet_free(pkt);
864 continue;
865 }
866
867 ++stats->ipsec_out_pkts;
868 pkts_fwd[num_pkts_fwd++] = pkt;
869 }
870 }
871
872 if (num_pkts_fwd > 0) {
873 num_procd = forward_packets(pkts_fwd, num_pkts_fwd, fwd_tbl);
874 stats->discards += num_pkts_fwd - num_procd;
875 stats->fwd_pkts += num_procd;
876 }
877}
878
879static void process_packets_in(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl,
880 stats_t *stats)
881{
882 odp_packet_t pkt, pkts_ips[MAX_BURST], pkts_out[MAX_BURST], pkts_ips_out[MAX_BURST];
883 odp_ipsec_sa_t *sa, sas[MAX_BURST];
884 int num_pkts_ips = 0, num_pkts_out = 0, num_procd;
886
887 for (int i = 0; i < num; ++i) {
888 pkt = pkts[i];
889
891 ++stats->discards;
892 odp_packet_free(pkt);
893 continue;
894 }
895
896 sa = get_in_sa(pkt);
897
898 if (sa != NULL) {
899 sas[num_pkts_ips] = *sa;
900 pkts_ips[num_pkts_ips] = pkt;
901 ++num_pkts_ips;
902 } else {
903 pkts_out[num_pkts_out++] = pkt;
904 }
905 }
906
907 if (num_pkts_ips > 0) {
908 num_procd = process_ipsec_in(pkts_ips, sas, num_pkts_ips, pkts_ips_out);
909
910 if (odp_unlikely(num_procd < num_pkts_ips)) {
911 stats->ipsec_in_errs += num_pkts_ips - num_procd;
912 odp_packet_free_multi(&pkts_ips[num_procd], num_pkts_ips - num_procd);
913 }
914
915 for (int i = 0; i < num_procd; ++i) {
916 pkt = pkts_ips_out[i];
917
918 if (odp_unlikely(odp_ipsec_result(&result, pkt) < 0)) {
919 ++stats->ipsec_in_errs;
920 odp_packet_free(pkt);
921 continue;
922 }
923
924 if (odp_unlikely(result.status.all != ODP_IPSEC_OK)) {
925 ++stats->ipsec_in_errs;
926 odp_packet_free(pkt);
927 continue;
928 }
929
930 ++stats->ipsec_in_pkts;
931 pkts_out[num_pkts_out++] = pkt;
932 }
933 }
934
935 if (num_pkts_out > 0)
936 process_packets_out(pkts_out, num_pkts_out, fwd_tbl, stats);
937}
938
939static void drain_direct_inputs(prog_config_t *config)
940{
941 odp_packet_t pkt;
942
943 for (uint32_t i = 0U; i < config->num_ifs; ++i) {
944 for (uint32_t j = 0U; j < config->num_input_qs; ++j) {
945 while (odp_pktin_recv(config->pktios[i].in_dir_qs[j], &pkt, 1) == 1)
946 odp_packet_free(pkt);
947 }
948 }
949}
950
951static odp_bool_t setup_ipsec(prog_config_t *config)
952{
953 odp_queue_param_t q_param;
954 odp_ipsec_config_t ipsec_config;
955 char q_name[ODP_QUEUE_NAME_LEN];
956
957 if (!config->is_dir_rx) {
958 snprintf(q_name, sizeof(q_name), SHORT_PROG_NAME "_sa_status");
959 odp_queue_param_init(&q_param);
960 q_param.type = ODP_QUEUE_TYPE_SCHED;
964 config->compl_q = odp_queue_create(q_name, &q_param);
965
966 if (config->compl_q == ODP_QUEUE_INVALID) {
967 ODPH_ERR("Error creating IPsec completion queue\n");
968 return false;
969 }
970 }
971
972 odp_ipsec_config_init(&ipsec_config);
973
974 if (!config->is_dir_rx) {
977 config->ops.proc = process_packets_in_enq;
978 config->ops.compl = complete_ipsec_ops;
979 config->ops.drain = drain_scheduler;
980 } else {
983 config->ops.proc = process_packets_in;
984 config->ops.compl = NULL;
985 config->ops.drain = drain_direct_inputs;
986 }
987
988 ipsec_config.inbound.default_queue = config->compl_q;
989 /* For tunnel to tunnel, we need to parse up to this to check the UDP port for SA. */
991
992 if (odp_ipsec_config(&ipsec_config) < 0) {
993 ODPH_ERR("Error configuring IPsec\n");
994 return false;
995 }
996
997 return true;
998}
999
1000static odp_bool_t create_sa_dest_queues(odp_ipsec_capability_t *ipsec_capa,
1001 prog_config_t *config)
1002{
1003 odp_queue_param_t q_param;
1004 const uint32_t max_sa_qs = ODPH_MIN(MAX_SA_QUEUES, ipsec_capa->max_queues);
1005
1006 if (config->num_sa_qs == 0U || config->num_sa_qs > max_sa_qs) {
1007 ODPH_ERR("Invalid number of SA queues: %u (min: 1, max: %u)\n", config->num_sa_qs,
1008 max_sa_qs);
1009 config->num_sa_qs = 0U;
1010 return false;
1011 }
1012
1013 for (uint32_t i = 0U; i < config->num_sa_qs; ++i) {
1014 char q_name[ODP_QUEUE_NAME_LEN];
1015
1016 snprintf(q_name, sizeof(q_name), SHORT_PROG_NAME "_sa_compl_%u", i);
1017 odp_queue_param_init(&q_param);
1018 q_param.type = ODP_QUEUE_TYPE_SCHED;
1019 q_param.sched.prio = odp_schedule_max_prio();
1020 q_param.sched.sync = config->mode == ORDERED ? ODP_SCHED_SYNC_ORDERED :
1023 config->sa_qs[i] = odp_queue_create(q_name, &q_param);
1024
1025 if (config->sa_qs[i] == ODP_QUEUE_INVALID) {
1026 ODPH_ERR("Error creating SA destination queue (created count: %u)\n", i);
1027 config->num_sa_qs = i;
1028 return false;
1029 }
1030 }
1031
1032 return true;
1033}
1034
1035static void parse_crypto(config_setting_t *cfg, sa_config_t *config)
1036{
1037 int val;
1038 const char *val_str;
1039 config_setting_t *cs = config_setting_lookup(cfg, "crypto");
1040
1041 if (cs == NULL)
1042 return;
1043
1044 if (config_setting_lookup_int(cs, "cipher_alg", &val) == CONFIG_TRUE)
1045 config->sa_param.crypto.cipher_alg = val;
1046
1047 if (config_setting_lookup_string(cs, "cipher_key", &val_str) == CONFIG_TRUE) {
1048 strcpy(config->cipher_key, val_str);
1049 config->sa_param.crypto.cipher_key.data = (uint8_t *)config->cipher_key;
1050 config->sa_param.crypto.cipher_key.length =
1051 strlen((const char *)config->cipher_key);
1052 }
1053
1054 if (config_setting_lookup_string(cs, "cipher_key_extra", &val_str) == CONFIG_TRUE) {
1055 strcpy(config->cipher_key_extra, val_str);
1056 config->sa_param.crypto.cipher_key_extra.data =
1057 (uint8_t *)config->cipher_key_extra;
1058 config->sa_param.crypto.cipher_key_extra.length =
1059 strlen((const char *)config->cipher_key_extra);
1060 }
1061
1062 if (config_setting_lookup_int(cs, "auth_alg", &val) == CONFIG_TRUE)
1063 config->sa_param.crypto.auth_alg = val;
1064
1065 if (config_setting_lookup_string(cs, "auth_key", &val_str) == CONFIG_TRUE) {
1066 strcpy(config->auth_key, val_str);
1067 config->sa_param.crypto.auth_key.data = (uint8_t *)config->auth_key;
1068 config->sa_param.crypto.auth_key.length = strlen((const char *)config->auth_key);
1069 }
1070
1071 if (config_setting_lookup_string(cs, "auth_key_extra", &val_str) == CONFIG_TRUE) {
1072 strcpy(config->auth_key_extra, val_str);
1073 config->sa_param.crypto.auth_key_extra.data = (uint8_t *)config->auth_key_extra;
1074 config->sa_param.crypto.auth_key_extra.length =
1075 strlen((const char *)config->auth_key_extra);
1076 }
1077
1078 if (config_setting_lookup_int(cs, "icv_len", &val) == CONFIG_TRUE)
1079 config->sa_param.crypto.icv_len = val;
1080}
1081
1082static void parse_opt(config_setting_t *cfg, sa_config_t *config)
1083{
1084 int val;
1085 config_setting_t *cs = config_setting_lookup(cfg, "opt");
1086
1087 if (cs == NULL)
1088 return;
1089
1090 if (config_setting_lookup_int(cs, "esn", &val) == CONFIG_TRUE)
1091 config->sa_param.opt.esn = val;
1092
1093 if (config_setting_lookup_int(cs, "udp_encap", &val) == CONFIG_TRUE)
1094 config->sa_param.opt.udp_encap = val;
1095
1096 if (config_setting_lookup_int(cs, "copy_dscp", &val) == CONFIG_TRUE)
1097 config->sa_param.opt.copy_dscp = val;
1098
1099 if (config_setting_lookup_int(cs, "copy_flabel", &val) == CONFIG_TRUE)
1100 config->sa_param.opt.copy_flabel = val;
1101
1102 if (config_setting_lookup_int(cs, "copy_df", &val) == CONFIG_TRUE)
1103 config->sa_param.opt.copy_df = val;
1104
1105 if (config_setting_lookup_int(cs, "dec_ttl", &val) == CONFIG_TRUE)
1106 config->sa_param.opt.dec_ttl = val;
1107}
1108
1109static void parse_limits(config_setting_t *cfg, sa_config_t *config)
1110{
1111 config_setting_t *cs = config_setting_lookup(cfg, "lifetime"), *soft, *hard;
1112 long long val;
1113
1114 if (cs == NULL)
1115 return;
1116
1117 soft = config_setting_lookup(cs, "soft_limit");
1118 hard = config_setting_lookup(cs, "hard_limit");
1119
1120 if (soft != NULL) {
1121 if (config_setting_lookup_int64(soft, "bytes", &val) == CONFIG_TRUE)
1122 config->sa_param.lifetime.soft_limit.bytes = val;
1123
1124 if (config_setting_lookup_int64(soft, "packets", &val) == CONFIG_TRUE)
1125 config->sa_param.lifetime.soft_limit.packets = val;
1126 }
1127
1128 if (hard != NULL) {
1129 if (config_setting_lookup_int64(hard, "bytes", &val) == CONFIG_TRUE)
1130 config->sa_param.lifetime.hard_limit.bytes = val;
1131
1132 if (config_setting_lookup_int64(hard, "packets", &val) == CONFIG_TRUE)
1133 config->sa_param.lifetime.hard_limit.packets = val;
1134 }
1135}
1136
1137static void parse_inbound(config_setting_t *cfg, sa_config_t *config)
1138{
1139 config_setting_t *cs = config_setting_lookup(cfg, "inbound");
1140 int val;
1141 const char *val_str;
1142
1143 if (cs == NULL)
1144 return;
1145
1146 if (config_setting_lookup_int(cs, "lookup_mode", &val) == CONFIG_TRUE)
1147 config->sa_param.inbound.lookup_mode = val;
1148
1149 if (config_setting_lookup_string(cs, "lookup_dst_addr", &val_str) == CONFIG_TRUE) {
1150 if (odph_ipv4_addr_parse(&config->lkp_dst_ip, val_str) == 0) {
1151 config->lkp_dst_ip = odp_cpu_to_be_32(config->lkp_dst_ip);
1152 config->sa_param.inbound.lookup_param.dst_addr = &config->lkp_dst_ip;
1153 }
1154 }
1155
1156 if (config_setting_lookup_int(cs, "antireplay_ws", &val) == CONFIG_TRUE)
1157 config->sa_param.inbound.antireplay_ws = val;
1158
1159 if (config_setting_lookup_int(cs, "reassembly_en", &val) == CONFIG_TRUE)
1160 config->sa_param.inbound.reassembly_en = val;
1161}
1162
1163static void parse_outbound(config_setting_t *cfg, sa_config_t *config)
1164{
1165 config_setting_t *cs = config_setting_lookup(cfg, "outbound"), *tunnel;
1166 const char *val_str;
1167 int val;
1168
1169 if (cs == NULL)
1170 return;
1171
1172 tunnel = config_setting_lookup(cs, "tunnel");
1173
1174 if (tunnel != NULL) {
1175 if (config_setting_lookup_string(tunnel, "src_addr", &val_str) == CONFIG_TRUE) {
1176 if (odph_ipv4_addr_parse(&config->src_ip, val_str) == 0) {
1177 config->src_ip = odp_cpu_to_be_32(config->src_ip);
1178 config->sa_param.outbound.tunnel.ipv4.src_addr = &config->src_ip;
1179 }
1180 }
1181
1182 if (config_setting_lookup_string(tunnel, "dst_addr", &val_str) == CONFIG_TRUE) {
1183 if (odph_ipv4_addr_parse(&config->dst_ip, val_str) == 0) {
1184 config->dst_ip = odp_cpu_to_be_32(config->dst_ip);
1185 config->sa_param.outbound.tunnel.ipv4.dst_addr = &config->dst_ip;
1186 }
1187 }
1188
1189 if (config_setting_lookup_int(tunnel, "dscp", &val) == CONFIG_TRUE)
1190 config->sa_param.outbound.tunnel.ipv4.dscp = val;
1191
1192 if (config_setting_lookup_int(tunnel, "df", &val) == CONFIG_TRUE)
1193 config->sa_param.outbound.tunnel.ipv4.df = val;
1194
1195 if (config_setting_lookup_int(tunnel, "ttl", &val) == CONFIG_TRUE)
1196 config->sa_param.outbound.tunnel.ipv4.ttl = val;
1197 }
1198
1199 if (config_setting_lookup_int(cs, "frag_mode", &val) == CONFIG_TRUE)
1200 config->sa_param.outbound.frag_mode = val;
1201
1202 if (config_setting_lookup_int(cs, "mtu", &val) == CONFIG_TRUE)
1203 config->sa_param.outbound.mtu = val;
1204}
1205
1206static void parse_sa_entry(config_setting_t *cfg, sa_config_t *config)
1207{
1208 int val;
1209
1210 if (config_setting_lookup_int(cfg, "dir", &val) == CONFIG_TRUE)
1211 config->sa_param.dir = val;
1212
1213 if (config_setting_lookup_int(cfg, "proto", &val) == CONFIG_TRUE)
1214 config->sa_param.proto = val;
1215
1216 if (config_setting_lookup_int(cfg, "mode", &val) == CONFIG_TRUE)
1217 config->sa_param.mode = val;
1218
1219 if (config_setting_lookup_int(cfg, "spi", &val) == CONFIG_TRUE)
1220 config->sa_param.spi = val;
1221
1222 parse_crypto(cfg, config);
1223 parse_opt(cfg, config);
1224 parse_limits(cfg, config);
1225 parse_inbound(cfg, config);
1226 parse_outbound(cfg, config);
1227}
1228
1229static void create_sa_entry(odp_ipsec_sa_param_t *sa_param, prog_config_t *config,
1230 uint32_t max_num_sa)
1231{
1232 uint32_t dir = sa_param->dir;
1233 uint32_t spi = sa_param->spi;
1234 odp_ipsec_sa_t sa;
1235
1236 if (config->num_sas == max_num_sa) {
1237 ODPH_ERR("Maximum number of SAs parsed (%u), ignoring rest\n", max_num_sa);
1238 return;
1239 }
1240
1241 if (spi > UINT16_MAX) {
1242 ODPH_ERR("Unsupported SPI value for SA %u (> %u)\n", spi, UINT16_MAX);
1243 return;
1244 }
1245
1246 if (spi_to_sa_map[dir][spi] != NULL) {
1247 ODPH_ERR("Non-unique SPIs not supported for SA %u\n", spi);
1248 return;
1249 }
1250
1251 sa_param->dest_queue = config->sa_qs[config->num_sas % config->num_sa_qs];
1252 sa = odp_ipsec_sa_create(sa_param);
1253
1254 if (sa == ODP_IPSEC_SA_INVALID) {
1255 ODPH_ERR("Error creating SA handle for SA %u\n", spi);
1256 return;
1257 }
1258
1259 config->sas[config->num_sas] = sa;
1260 spi_to_sa_map[dir][spi] = &config->sas[config->num_sas];
1261 ++config->num_sas;
1262}
1263
1264static void parse_and_create_sa_entries(config_t *cfg, prog_config_t *config, uint32_t max_num_sa)
1265{
1266 config_setting_t *cs;
1267 int count;
1268
1269 cs = config_lookup(cfg, "default");
1270
1271 if (cs != NULL)
1272 parse_sa_entry(cs, &config->default_cfg);
1273
1274 cs = config_lookup(cfg, "sa");
1275
1276 if (cs == NULL)
1277 return;
1278
1279 count = config_setting_length(cs);
1280
1281 for (int i = 0; i < count; i++) {
1282 sa_config_t sa_cfg;
1283 config_setting_t *sa;
1284 int val;
1285
1286 sa_cfg = config->default_cfg;
1287 sa = config_setting_get_elem(cs, i);
1288
1289 if (sa == NULL)
1290 continue;
1291
1292 if (config_setting_lookup_int(sa, "spi", &val) == CONFIG_TRUE) {
1293 parse_sa_entry(sa, &sa_cfg);
1294 create_sa_entry(&sa_cfg.sa_param, config, max_num_sa);
1295 }
1296 }
1297}
1298
1299static void parse_sas(config_t *cfg, prog_config_t *config)
1300{
1301 odp_ipsec_capability_t ipsec_capa;
1302 uint32_t max_num_sa;
1303
1304 if (odp_ipsec_capability(&ipsec_capa) < 0) {
1305 ODPH_ERR("Error querying IPsec capabilities\n");
1306 return;
1307 }
1308
1309 if (!setup_ipsec(config))
1310 return;
1311
1312 if (!config->is_dir_rx && !create_sa_dest_queues(&ipsec_capa, config))
1313 return;
1314
1315 max_num_sa = ODPH_MIN(MAX_SAS, ipsec_capa.max_num_sa);
1316 parse_and_create_sa_entries(cfg, config, max_num_sa);
1317}
1318
1319static const pktio_t *get_pktio(const char *iface, const prog_config_t *config)
1320{
1321 for (uint32_t i = 0U; i < config->num_ifs; ++i) {
1322 if (strcmp(iface, config->pktios[i].name) == 0)
1323 return &config->pktios[i];
1324 }
1325
1326 return NULL;
1327}
1328
1329static void create_fwd_table_entry(config_setting_t *cfg, prog_config_t *config)
1330{
1331 const char *val_str;
1332 char dst_ip_str[16U] = { 0 };
1333 uint32_t mask, dst_ip;
1334 odph_ethaddr_t dst_mac;
1335 const pktio_t *pktio = NULL;
1336 fwd_entry_t *entry;
1337
1338 if (config->fwd_tbl.num == MAX_FWDS) {
1339 ODPH_ERR("Maximum number of forwarding entries parsed (%u), ignoring rest\n",
1340 MAX_FWDS);
1341 return;
1342 }
1343
1344 if (config_setting_lookup_string(cfg, "prefix", &val_str) == CONFIG_TRUE) {
1345 if (sscanf(val_str, "%[^/]/%u", dst_ip_str, &mask) != 2) {
1346 ODPH_ERR("Error parsing IP and subnet mask for forwarding entry\n");
1347 return;
1348 }
1349
1350 if (odph_ipv4_addr_parse(&dst_ip, dst_ip_str) < 0) {
1351 ODPH_ERR("Syntax error in IP address for forwarding entry\n");
1352 return;
1353 }
1354
1355 if (mask > IP_ADDR_LEN) {
1356 ODPH_ERR("Invalid subnet mask for forwarding entry: %u\n", mask);
1357 return;
1358 }
1359 } else {
1360 return;
1361 }
1362
1363 if (config_setting_lookup_string(cfg, "if", &val_str) == CONFIG_TRUE) {
1364 pktio = get_pktio(val_str, config);
1365
1366 if (pktio == NULL) {
1367 ODPH_ERR("Error parsing next interface for forwarding entry\n");
1368 return;
1369 }
1370 } else {
1371 return;
1372 }
1373
1374 if (config_setting_lookup_string(cfg, "dst_mac", &val_str) == CONFIG_TRUE) {
1375 if (odph_eth_addr_parse(&dst_mac, val_str) < 0) {
1376 ODPH_ERR("Syntax error in destination MAC for forwarding entry\n");
1377 return;
1378 }
1379 } else {
1380 return;
1381 }
1382
1383 mask = mask > 0U ? 0xFFFFFFFF << (IP_ADDR_LEN - mask) : 0U;
1384 entry = &config->fwd_tbl.entries[config->fwd_tbl.num];
1385 entry->prefix = dst_ip & mask;
1386 entry->mask = mask;
1387 entry->dst_mac = dst_mac;
1388 entry->pktio = pktio;
1389 ++config->fwd_tbl.num;
1390}
1391
1392static void parse_fwd_table(config_t *cfg, prog_config_t *config)
1393{
1394 config_setting_t *cs;
1395 int count;
1396
1397 cs = config_lookup(cfg, "fwd");
1398
1399 if (cs == NULL)
1400 return;
1401
1402 count = config_setting_length(cs);
1403
1404 for (int i = 0; i < count; i++) {
1405 config_setting_t *fwd = config_setting_get_elem(cs, i);
1406
1407 if (fwd == NULL)
1408 continue;
1409
1410 create_fwd_table_entry(fwd, config);
1411 }
1412}
1413
1414static parse_result_t check_options(prog_config_t *config)
1415{
1416 odp_pool_capability_t pool_capa;
1417
1418 if (odp_pool_capability(&pool_capa) < 0) {
1419 ODPH_ERR("Error querying pool capabilities\n");
1420 return PRS_NOK;
1421 }
1422
1423 if (config->num_ifs == 0U) {
1424 ODPH_ERR("Invalid number of interfaces: %u (min: 1, max: %u)\n", config->num_ifs,
1425 MAX_IFS);
1426 return PRS_NOK;
1427 }
1428
1429 if (config->fwd_tbl.num == 0U) {
1430 ODPH_ERR("Invalid number of forwarding entries: %u (min: 1, max: %u)\n",
1431 config->fwd_tbl.num, MAX_FWDS);
1432 return PRS_NOK;
1433 }
1434
1435 if (pool_capa.pkt.max_num > 0U && config->num_pkts > pool_capa.pkt.max_num) {
1436 ODPH_ERR("Invalid pool packet count: %u (max: %u)\n", config->num_pkts,
1437 pool_capa.pkt.max_num);
1438 return PRS_NOK;
1439 }
1440
1441 if (config->num_pkts == 0U)
1442 config->num_pkts = pool_capa.pkt.max_num > 0U ?
1443 ODPH_MIN(pool_capa.pkt.max_num, PKT_CNT) : PKT_CNT;
1444
1445 if (pool_capa.pkt.max_len > 0U && config->pkt_len > pool_capa.pkt.max_len) {
1446 ODPH_ERR("Invalid pool packet length: %u (max: %u)\n", config->pkt_len,
1447 pool_capa.pkt.max_len);
1448 return PRS_NOK;
1449 }
1450
1451 if (config->pkt_len == 0U)
1452 config->pkt_len = pool_capa.pkt.max_len > 0U ?
1453 ODPH_MIN(pool_capa.pkt.max_len, PKT_SIZE) : PKT_SIZE;
1454
1455 if (config->num_thrs <= 0 || config->num_thrs > MAX_WORKERS) {
1456 ODPH_ERR("Invalid thread count: %d (min: 1, max: %d)\n", config->num_thrs,
1457 MAX_WORKERS);
1458 return PRS_NOK;
1459 }
1460
1461 if (config->is_dir_rx) {
1462 config->num_input_qs = config->num_thrs;
1463 config->num_output_qs = config->num_thrs;
1464 }
1465
1466 return PRS_OK;
1467}
1468
1469static parse_result_t parse_options(int argc, char **argv, prog_config_t *config)
1470{
1471 int opt;
1472 config_t cfg;
1473
1474 static const struct option longopts[] = {
1475 { "interfaces", required_argument, NULL, 'i' },
1476 { "num_pkts", required_argument, NULL, 'n' },
1477 { "pkt_len", required_argument, NULL, 'l' },
1478 { "count", required_argument, NULL, 'c' },
1479 { "mode", required_argument, NULL, 'm' },
1480 { "conf", required_argument, NULL, 'C' },
1481 { "num_input_qs", required_argument, NULL, 'I' },
1482 { "num_sa_qs", required_argument, NULL, 'S' },
1483 { "num_output_qs", required_argument, NULL, 'O' },
1484 { "direct_rx", no_argument, NULL, 'd' },
1485 { "help", no_argument, NULL, 'h' },
1486 { NULL, 0, NULL, 0 }
1487 };
1488
1489 static const char *shortopts = "i:n:l:c:m:C:I:S:O:dh";
1490
1491 while (true) {
1492 opt = getopt_long(argc, argv, shortopts, longopts, NULL);
1493
1494 if (opt == -1)
1495 break;
1496
1497 switch (opt) {
1498 case 'i':
1499 parse_interfaces(config, optarg);
1500 break;
1501 case 'n':
1502 config->num_pkts = atoi(optarg);
1503 break;
1504 case 'l':
1505 config->pkt_len = atoi(optarg);
1506 break;
1507 case 'c':
1508 config->num_thrs = atoi(optarg);
1509 break;
1510 case 'm':
1511 config->mode = !!atoi(optarg);
1512 break;
1513 case 'C':
1514 config->conf_file = strdup(optarg);
1515 break;
1516 case 'I':
1517 config->num_input_qs = atoi(optarg);
1518 break;
1519 case 'S':
1520 config->num_sa_qs = atoi(optarg);
1521 break;
1522 case 'O':
1523 config->num_output_qs = atoi(optarg);
1524 break;
1525 case 'd':
1526 config->is_dir_rx = true;
1527 break;
1528 case 'h':
1529 print_usage();
1530 return PRS_TERM;
1531 case '?':
1532 default:
1533 print_usage();
1534 return PRS_NOK;
1535 }
1536 }
1537
1538 config_init(&cfg);
1539
1540 if (config_read_file(&cfg, config->conf_file) == CONFIG_FALSE) {
1541 ODPH_ERR("Error opening SA configuration file: %s\n", config_error_text(&cfg));
1542 config_destroy(&cfg);
1543 return PRS_NOK;
1544 }
1545
1546 parse_sas(&cfg, config);
1547 parse_fwd_table(&cfg, config);
1548 config_destroy(&cfg);
1549
1550 return check_options(config);
1551}
1552
1553static parse_result_t setup_program(int argc, char **argv, prog_config_t *config)
1554{
1555 struct sigaction action = { .sa_handler = terminate };
1556
1557 if (sigemptyset(&action.sa_mask) == -1 || sigaddset(&action.sa_mask, SIGINT) == -1 ||
1558 sigaddset(&action.sa_mask, SIGTERM) == -1 ||
1559 sigaddset(&action.sa_mask, SIGHUP) == -1 || sigaction(SIGINT, &action, NULL) == -1 ||
1560 sigaction(SIGTERM, &action, NULL) == -1 || sigaction(SIGHUP, &action, NULL) == -1) {
1561 ODPH_ERR("Error installing signal handler\n");
1562 return PRS_NOK;
1563 }
1564
1565 return parse_options(argc, argv, config);
1566}
1567
1568static uint32_t schedule(thread_config_t *config ODP_UNUSED, odp_event_t evs[], int num)
1569{
1570 return odp_schedule_multi_no_wait(NULL, evs, num);
1571}
1572
1573static uint32_t recv(thread_config_t *config, odp_event_t evs[], int num)
1574{
1575 prog_config_t *prog_config = config->prog_config;
1576 pktio_t *pktio = &prog_config->pktios[config->pktio++ % prog_config->num_ifs];
1577 odp_pktin_queue_t in_q = pktio->in_dir_qs[config->thr_idx % prog_config->num_input_qs];
1578 odp_packet_t pkts[num];
1579 int ret;
1580
1581 ret = odp_pktin_recv(in_q, pkts, num);
1582
1583 if (odp_unlikely(ret <= 0))
1584 return 0U;
1585
1586 odp_packet_to_event_multi(pkts, evs, ret);
1587
1588 return ret;
1589}
1590
1591static uint32_t send(const pktio_t *pktio, uint8_t index, odp_packet_t pkts[], int num)
1592{
1593 int ret = odp_pktout_send(pktio->out_dir_qs[index], pkts, num);
1594
1595 return ret < 0 ? 0U : (uint32_t)ret;
1596}
1597
1598static uint32_t enqueue(const pktio_t *pktio, uint8_t index, odp_packet_t pkts[], int num)
1599{
1600 odp_event_t evs[MAX_BURST];
1601 int ret;
1602
1603 odp_packet_to_event_multi(pkts, evs, num);
1604
1605 ret = odp_queue_enq_multi(pktio->out_ev_qs[index], evs, num);
1606
1607 return ret < 0 ? 0U : (uint32_t)ret;
1608}
1609
1610static odp_bool_t setup_pktios(prog_config_t *config)
1611{
1612 odp_pool_param_t pool_param;
1613 pktio_t *pktio;
1614 odp_pktio_param_t pktio_param;
1615 odp_pktin_queue_param_t pktin_param;
1617 odp_pktout_queue_param_t pktout_param;
1618 odp_pktio_config_t pktio_config;
1619 uint32_t max_output_qs;
1620
1621 odp_pool_param_init(&pool_param);
1622 pool_param.pkt.seg_len = config->pkt_len;
1623 pool_param.pkt.len = config->pkt_len;
1624 pool_param.pkt.num = config->num_pkts;
1625 pool_param.type = ODP_POOL_PACKET;
1626 config->pktio_pool = odp_pool_create(PROG_NAME, &pool_param);
1627
1628 if (config->pktio_pool == ODP_POOL_INVALID) {
1629 ODPH_ERR("Error creating packet I/O pool\n");
1630 return false;
1631 }
1632
1633 config->ops.rx = !config->is_dir_rx ? schedule : recv;
1634 config->is_hashed_tx = !config->is_dir_rx && config->mode == ORDERED;
1635
1636 for (uint32_t i = 0U; i < config->num_ifs; ++i) {
1637 pktio = &config->pktios[i];
1638 pktio->idx = i;
1639 odp_pktio_param_init(&pktio_param);
1640 pktio_param.in_mode = !config->is_dir_rx ?
1642 pktio_param.out_mode = config->is_hashed_tx ?
1644 pktio->handle = odp_pktio_open(pktio->name, config->pktio_pool, &pktio_param);
1645
1646 if (pktio->handle == ODP_PKTIO_INVALID) {
1647 ODPH_ERR("Error opening packet I/O (%s)\n", pktio->name);
1648 return false;
1649 }
1650
1651 if (odp_pktio_capability(pktio->handle, &capa) < 0) {
1652 ODPH_ERR("Error querying packet I/O capabilities (%s)\n", pktio->name);
1653 return false;
1654 }
1655
1656 if (config->num_input_qs == 0U || config->num_input_qs > capa.max_input_queues) {
1657 ODPH_ERR("Invalid number of input queues for packet I/O: %u (min: 1, max: "
1658 "%u) (%s)\n", config->num_input_qs, capa.max_input_queues,
1659 pktio->name);
1660 return false;
1661 }
1662
1663 max_output_qs = ODPH_MIN(MAX_QUEUES, capa.max_output_queues);
1664
1665 if (config->num_output_qs == 0U || config->num_output_qs > max_output_qs) {
1666 ODPH_ERR("Invalid number of output queues for packet I/O: %u (min: 1, "
1667 "max: %u) (%s)\n", config->num_output_qs, max_output_qs,
1668 pktio->name);
1669 return false;
1670 }
1671
1672 odp_pktin_queue_param_init(&pktin_param);
1673
1674 if (config->is_hashed_tx)
1676
1677 if (config->num_input_qs > 1U) {
1678 pktin_param.hash_enable = true;
1679 pktin_param.hash_proto.proto.ipv4_udp = 1U;
1680 pktin_param.num_queues = config->num_input_qs;
1681 }
1682
1683 pktin_param.op_mode = (config->is_dir_rx &&
1684 config->num_thrs > (int)config->num_input_qs) ?
1686
1687 if (odp_pktin_queue_config(pktio->handle, &pktin_param) < 0) {
1688 ODPH_ERR("Error configuring packet I/O input queues (%s)\n", pktio->name);
1689 return false;
1690 }
1691
1692 if (config->is_dir_rx) {
1693 if (odp_pktin_queue(pktio->handle, pktio->in_dir_qs, config->num_input_qs)
1694 != (int)config->num_input_qs) {
1695 ODPH_ERR("Error querying packet I/O input queue (%s)\n",
1696 pktio->name);
1697 return false;
1698 }
1699 }
1700
1701 pktio->send_fn = config->is_hashed_tx ? enqueue : send;
1702 pktio->num_tx_qs = config->num_output_qs;
1703 odp_pktout_queue_param_init(&pktout_param);
1704 pktout_param.num_queues = pktio->num_tx_qs;
1705
1706 if (!config->is_hashed_tx) {
1707 pktout_param.op_mode = config->num_thrs > (int)pktio->num_tx_qs ?
1709 }
1710
1711 if (odp_pktout_queue_config(pktio->handle, &pktout_param) < 0) {
1712 ODPH_ERR("Error configuring packet I/O output queues (%s)\n", pktio->name);
1713 return false;
1714 }
1715
1716 if (config->is_hashed_tx) {
1717 if (odp_pktout_event_queue(pktio->handle, pktio->out_ev_qs,
1718 pktio->num_tx_qs) != (int)pktio->num_tx_qs) {
1719 ODPH_ERR("Error querying packet I/O output event queue (%s)\n",
1720 pktio->name);
1721 return false;
1722 }
1723 } else {
1724 if (odp_pktout_queue(pktio->handle, pktio->out_dir_qs, pktio->num_tx_qs)
1725 != (int)pktio->num_tx_qs) {
1726 ODPH_ERR("Error querying packet I/O output queue (%s)\n",
1727 pktio->name);
1728 return false;
1729 }
1730 }
1731
1732 odp_pktio_config_init(&pktio_config);
1733
1734 if (odp_pktio_config(pktio->handle, &pktio_config) < 0) {
1735 ODPH_ERR("Error configuring packet I/O extra options (%s)\n", pktio->name);
1736 return false;
1737 }
1738
1739 if (odp_pktio_mac_addr(pktio->handle, &pktio->src_mac, sizeof(pktio->src_mac))
1740 != sizeof(pktio->src_mac)) {
1741 ODPH_ERR("Error getting packet I/O MAC address (%s)\n", pktio->name);
1742 return false;
1743 }
1744
1745 if (odp_pktio_start(pktio->handle) < 0) {
1746 ODPH_ERR("Error starting packet I/O (%s)\n", pktio->name);
1747 return false;
1748 }
1749 }
1750
1751 return true;
1752}
1753
1754static inline void check_ipsec_status_ev(odp_event_t ev, stats_t *stats)
1755{
1756 odp_ipsec_status_t status;
1757
1758 if (odp_unlikely(odp_ipsec_status(&status, ev) < 0 || status.result < 0))
1759 ++stats->status_errs;
1760
1761 odp_event_free(ev);
1762}
1763
1764static int process_packets(void *args)
1765{
1766 thread_config_t *config = args;
1767 int thr_idx = odp_thread_id();
1768 odp_event_t evs[MAX_BURST], ev;
1769 ops_t ops = config->prog_config->ops;
1770 odp_atomic_u32_t *is_running = &config->prog_config->is_running;
1771 uint32_t cnt;
1772 odp_event_type_t type;
1773 odp_event_subtype_t subtype;
1774 odp_packet_t pkt, pkts_in[MAX_BURST], pkts_ips[MAX_BURST];
1775 lookup_table_t *fwd_tbl = &config->prog_config->fwd_tbl;
1776 stats_t *stats = &config->stats;
1777
1778 ifs.is_hashed_tx = config->prog_config->is_hashed_tx;
1779 ifs.q_idx = thr_idx % config->prog_config->num_output_qs;
1780 config->thr_idx = thr_idx;
1781 odp_barrier_wait(&config->prog_config->init_barrier);
1782
1783 while (odp_atomic_load_u32(is_running)) {
1784 int num_pkts_in = 0, num_pkts_ips = 0;
1785 /* TODO: Add possibility to configure scheduler and ipsec enq/deq burst sizes. */
1786 cnt = ops.rx(config, evs, MAX_BURST);
1787
1788 if (cnt == 0U)
1789 continue;
1790
1791 for (uint32_t i = 0U; i < cnt; ++i) {
1792 ev = evs[i];
1793 type = odp_event_types(ev, &subtype);
1794 pkt = odp_packet_from_event(ev);
1795
1796 if (type == ODP_EVENT_PACKET) {
1797 if (subtype == ODP_EVENT_PACKET_BASIC) {
1798 pkts_in[num_pkts_in++] = pkt;
1799 } else if (subtype == ODP_EVENT_PACKET_IPSEC) {
1800 pkts_ips[num_pkts_ips++] = pkt;
1801 } else {
1802 ++stats->discards;
1803 odp_event_free(ev);
1804 }
1805 } else if (type == ODP_EVENT_IPSEC_STATUS) {
1806 check_ipsec_status_ev(ev, stats);
1807 } else {
1808 ++stats->discards;
1809 odp_event_free(ev);
1810 }
1811 }
1812
1813 if (num_pkts_in > 0)
1814 ops.proc(pkts_in, num_pkts_in, fwd_tbl, stats);
1815
1816 if (ops.compl && num_pkts_ips > 0)
1817 ops.compl(pkts_ips, num_pkts_ips, fwd_tbl, stats);
1818 }
1819
1820 odp_barrier_wait(&config->prog_config->term_barrier);
1821 ops.drain(config->prog_config);
1822
1823 return 0;
1824}
1825
1826static odp_bool_t setup_workers(prog_config_t *config)
1827{
1828 odph_thread_common_param_t thr_common;
1829 odph_thread_param_t thr_param[config->num_thrs];
1830 odp_cpumask_t cpumask;
1831 int num_workers;
1832
1833 num_workers = odp_cpumask_default_worker(&cpumask, config->num_thrs);
1834 odph_thread_common_param_init(&thr_common);
1835 thr_common.instance = config->odp_instance;
1836 thr_common.cpumask = &cpumask;
1837
1838 for (int i = 0; i < config->num_thrs; ++i) {
1839 odph_thread_param_init(&thr_param[i]);
1840 thr_param[i].start = process_packets;
1841 thr_param[i].thr_type = ODP_THREAD_WORKER;
1842 config->thread_config[i].prog_config = config;
1843 thr_param[i].arg = &config->thread_config[i];
1844 }
1845
1846 num_workers = odph_thread_create(config->thread_tbl, &thr_common, thr_param, num_workers);
1847
1848 if (num_workers != config->num_thrs) {
1849 ODPH_ERR("Error configuring worker threads\n");
1850 return false;
1851 }
1852
1853 return true;
1854}
1855
1856static odp_bool_t setup_test(prog_config_t *config)
1857{
1858 odp_barrier_init(&config->init_barrier, config->num_thrs + 1);
1859 odp_barrier_init(&config->term_barrier, config->num_thrs + 1);
1860
1861 if (!setup_pktios(config))
1862 return false;
1863
1864 if (!setup_workers(config))
1865 return false;
1866
1867 odp_barrier_wait(&config->init_barrier);
1868
1869 return true;
1870}
1871
1872static void stop_test(prog_config_t *config)
1873{
1874 for (uint32_t i = 0U; i < config->num_ifs; ++i)
1875 if (config->pktios[i].handle != ODP_PKTIO_INVALID)
1876 (void)odp_pktio_stop(config->pktios[i].handle);
1877
1878 odp_barrier_wait(&config->term_barrier);
1879 (void)odph_thread_join(config->thread_tbl, config->num_thrs);
1880}
1881
1882static void print_stats(const prog_config_t *config)
1883{
1884 const stats_t *stats;
1885
1886 printf("\n====================\n\n"
1887 "IPsec forwarder done\n\n"
1888 " configuration file: %s\n"
1889 " queuing mode: %s\n"
1890 " input queue count: %u\n"
1891 " SA queue count: %u\n"
1892 " output queue count: %u\n"
1893 " RX mode: %s\n", config->conf_file,
1894 config->mode == ORDERED ? "ordered" : "parallel", config->num_input_qs,
1895 config->num_sa_qs, config->num_output_qs,
1896 config->is_dir_rx ? "direct" : "scheduled");
1897
1898 for (int i = 0; i < config->num_thrs; ++i) {
1899 stats = &config->thread_config[i].stats;
1900
1901 printf("\n worker %d:\n"
1902 " IPsec in packets: %" PRIu64 "\n"
1903 " IPsec out packets: %" PRIu64 "\n"
1904 " IPsec in packet errors: %" PRIu64 "\n"
1905 " IPsec out packet errors: %" PRIu64 "\n"
1906 " IPsec status errors: %" PRIu64 "\n"
1907 " packets forwarded: %" PRIu64 "\n"
1908 " packets dropped: %" PRIu64 "\n", i, stats->ipsec_in_pkts,
1909 stats->ipsec_out_pkts, stats->ipsec_in_errs, stats->ipsec_out_errs,
1910 stats->status_errs, stats->fwd_pkts, stats->discards);
1911 }
1912
1913 printf("\n====================\n");
1914}
1915
1916static void wait_sas_disabled(uint32_t num_sas)
1917{
1918 uint32_t num_sas_dis = 0U;
1919 odp_event_t ev;
1920 odp_ipsec_status_t status;
1921
1922 while (num_sas_dis < num_sas) {
1923 ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
1924
1925 if (ev == ODP_EVENT_INVALID)
1926 continue;
1927
1928 if (odp_event_type(ev) != ODP_EVENT_IPSEC_STATUS) {
1929 odp_event_free(ev);
1930 continue;
1931 }
1932
1933 if (odp_ipsec_status(&status, ev) < 0) {
1934 odp_event_free(ev);
1935 continue;
1936 }
1937
1938 if (status.id == ODP_IPSEC_STATUS_SA_DISABLE)
1939 ++num_sas_dis;
1940
1941 odp_event_free(ev);
1942 }
1943}
1944
1945static void teardown_test(const prog_config_t *config)
1946{
1947 for (uint32_t i = 0U; i < config->num_ifs; ++i) {
1948 free(config->pktios[i].name);
1949
1950 if (config->pktios[i].handle != ODP_PKTIO_INVALID)
1951 (void)odp_pktio_close(config->pktios[i].handle);
1952 }
1953
1954 if (config->pktio_pool != ODP_POOL_INVALID)
1955 (void)odp_pool_destroy(config->pktio_pool);
1956
1957 for (uint32_t i = 0U; i < config->num_sas; ++i)
1958 (void)odp_ipsec_sa_disable(config->sas[i]);
1959
1960 if (!config->is_dir_rx)
1961 /* Drain SA status events. */
1962 wait_sas_disabled(config->num_sas);
1963
1964 for (uint32_t i = 0U; i < config->num_sas; ++i)
1965 (void)odp_ipsec_sa_destroy(config->sas[i]);
1966
1967 for (uint32_t i = 0U; i < config->num_sa_qs; ++i)
1968 (void)odp_queue_destroy(config->sa_qs[i]);
1969
1970 if (config->compl_q != ODP_QUEUE_INVALID)
1971 (void)odp_queue_destroy(config->compl_q);
1972
1973 free(config->conf_file);
1974}
1975
1976int main(int argc, char **argv)
1977{
1978 odph_helper_options_t odph_opts;
1979 odp_init_t init_param;
1981 odp_shm_t shm_cfg = ODP_SHM_INVALID;
1982 parse_result_t parse_res;
1983 int ret = EXIT_SUCCESS;
1984
1985 argc = odph_parse_options(argc, argv);
1986
1987 if (odph_options(&odph_opts) == -1) {
1988 ODPH_ERR("Error while reading ODP helper options, exiting\n");
1989 exit(EXIT_FAILURE);
1990 }
1991
1992 odp_init_param_init(&init_param);
1993 init_param.mem_model = odph_opts.mem_model;
1994
1995 if (odp_init_global(&odp_instance, &init_param, NULL) < 0) {
1996 ODPH_ERR("ODP global init failed, exiting\n");
1997 exit(EXIT_FAILURE);
1998 }
1999
2001 ODPH_ERR("ODP local init failed, exiting\n");
2002 exit(EXIT_FAILURE);
2003 }
2004
2005 shm_cfg = odp_shm_reserve(PROG_NAME "_cfg", sizeof(prog_config_t), ODP_CACHE_LINE_SIZE,
2006 0U);
2007
2008 if (shm_cfg == ODP_SHM_INVALID) {
2009 ODPH_ERR("Error reserving shared memory\n");
2010 ret = EXIT_FAILURE;
2011 goto out;
2012 }
2013
2014 prog_conf = odp_shm_addr(shm_cfg);
2015
2016 if (prog_conf == NULL) {
2017 ODPH_ERR("Error resolving shared memory address\n");
2018 ret = EXIT_FAILURE;
2019 goto out;
2020 }
2021
2022 init_config(prog_conf);
2023
2024 if (!prog_conf->is_dir_rx && odp_schedule_config(NULL) < 0) {
2025 ODPH_ERR("Error configuring scheduler\n");
2026 ret = EXIT_FAILURE;
2027 goto out_test;
2028 }
2029
2030 parse_res = setup_program(argc, argv, prog_conf);
2031
2032 if (parse_res == PRS_NOK) {
2033 ret = EXIT_FAILURE;
2034 goto out_test;
2035 }
2036
2037 if (parse_res == PRS_TERM) {
2038 ret = EXIT_SUCCESS;
2039 goto out_test;
2040 }
2041
2042 prog_conf->odp_instance = odp_instance;
2043 odp_atomic_init_u32(&prog_conf->is_running, 1U);
2044
2045 if (!setup_test(prog_conf)) {
2046 ret = EXIT_FAILURE;
2047 goto out_test;
2048 }
2049
2050 while (odp_atomic_load_u32(&prog_conf->is_running))
2051 odp_cpu_pause();
2052
2053 stop_test(prog_conf);
2054 print_stats(prog_conf);
2055
2056out_test:
2057 teardown_test(prog_conf);
2058
2059out:
2060 if (shm_cfg != ODP_SHM_INVALID)
2061 (void)odp_shm_free(shm_cfg);
2062
2063 if (odp_term_local() < 0) {
2064 ODPH_ERR("ODP local terminate failed, exiting\n");
2065 exit(EXIT_FAILURE);
2066 }
2067
2068 if (odp_term_global(odp_instance) < 0) {
2069 ODPH_ERR("ODP global terminate failed, exiting\n");
2070 exit(EXIT_FAILURE);
2071 }
2072
2073 return ret;
2074}
void odp_atomic_init_u32(odp_atomic_u32_t *atom, uint32_t val)
Initialize atomic uint32 variable.
uint32_t odp_atomic_load_u32(odp_atomic_u32_t *atom)
Load value of atomic uint32 variable.
void odp_atomic_store_u32(odp_atomic_u32_t *atom, uint32_t val)
Store value to atomic uint32 variable.
void odp_barrier_init(odp_barrier_t *barr, int count)
Initialize barrier with thread count.
void odp_barrier_wait(odp_barrier_t *barr)
Synchronize thread execution on barrier.
#define ODP_ALIGNED_CACHE
Defines type/struct/variable to be cache line size aligned.
#define odp_unlikely(x)
Branch unlikely taken.
Definition spec/hints.h:64
uint32_t odp_u32be_t
unsigned 32bit big endian
odp_u16be_t odp_cpu_to_be_16(uint16_t cpu16)
Convert cpu native uint16_t to 16bit big endian.
#define ODP_UNUSED
Intentionally unused variables of functions.
Definition spec/hints.h:54
uint16_t odp_be_to_cpu_16(odp_u16be_t be16)
Convert 16bit big endian to cpu native uint16_t.
odp_u32be_t odp_cpu_to_be_32(uint32_t cpu32)
Convert cpu native uint32_t to 32bit big endian.
uint32_t odp_be_to_cpu_32(odp_u32be_t be32)
Convert 32bit big endian to cpu native uint32_t.
void odp_cpu_pause(void)
Pause CPU execution for a short while.
int odp_cpumask_default_worker(odp_cpumask_t *mask, int num)
Default CPU mask for worker threads.
@ ODP_CIPHER_ALG_AES_CCM
AES-CCM.
@ ODP_CIPHER_ALG_AES_ECB
AES with electronic codebook.
@ ODP_CIPHER_ALG_AES_CTR
AES with counter mode.
@ ODP_CIPHER_ALG_CHACHA20_POLY1305
ChaCha20-Poly1305.
@ ODP_CIPHER_ALG_AES_CBC
AES with cipher block chaining.
@ ODP_CIPHER_ALG_AES_GCM
AES-GCM.
@ ODP_CIPHER_ALG_3DES_CBC
Triple DES with cipher block chaining.
@ ODP_CIPHER_ALG_DES
DES.
@ ODP_CIPHER_ALG_NULL
No cipher algorithm specified.
@ ODP_AUTH_ALG_SHA384_HMAC
HMAC-SHA-384.
@ ODP_AUTH_ALG_CHACHA20_POLY1305
ChaCha20-Poly1305 AEAD.
@ ODP_AUTH_ALG_AES_CMAC
AES-CMAC.
@ ODP_AUTH_ALG_NULL
No authentication algorithm specified.
@ ODP_AUTH_ALG_MD5_HMAC
HMAC-MD5.
@ ODP_AUTH_ALG_SHA512_HMAC
HMAC-SHA-512.
@ ODP_AUTH_ALG_SHA1_HMAC
HMAC-SHA-1.
@ ODP_AUTH_ALG_SHA256_HMAC
HMAC-SHA-256.
@ ODP_AUTH_ALG_SHA224_HMAC
HMAC-SHA-224.
@ ODP_AUTH_ALG_AES_GMAC
AES-GMAC.
@ ODP_AUTH_ALG_AES_XCBC_MAC
AES-XCBC-MAC.
@ ODP_AUTH_ALG_AES_GCM
AES-GCM.
@ ODP_AUTH_ALG_AES_CCM
AES-CCM.
void odp_event_free(odp_event_t event)
Free event.
odp_event_type_t odp_event_type(odp_event_t event)
Event type of an event.
odp_event_subtype_t
Event subtype.
odp_event_type_t
Event type.
odp_event_type_t odp_event_types(odp_event_t event, odp_event_subtype_t *subtype)
Event type and subtype of an event.
#define ODP_EVENT_INVALID
Invalid event.
int odp_instance(odp_instance_t *instance)
Get instance handle.
void odp_init_param_init(odp_init_t *param)
Initialize the odp_init_t to default values for all fields.
int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type)
Thread local ODP initialization.
int odp_init_global(odp_instance_t *instance, const odp_init_t *params, const odp_platform_init_t *platform_params)
Global ODP initialization.
int odp_term_local(void)
Thread local ODP termination.
int odp_term_global(odp_instance_t instance)
Global ODP termination.
uint64_t odp_instance_t
ODP instance ID.
int odp_ipsec_in_enq(const odp_packet_t pkt[], int num, const odp_ipsec_in_param_t *param)
Inbound asynchronous IPSEC operation.
int odp_ipsec_in(const odp_packet_t pkt_in[], int num_in, odp_packet_t pkt_out[], int *num_out, const odp_ipsec_in_param_t *param)
Inbound synchronous IPSEC operation.
int odp_ipsec_auth_capability(odp_auth_alg_t auth, odp_ipsec_auth_capability_t capa[], int num)
Query supported IPSEC authentication algorithm capabilities.
#define ODP_IPSEC_OK
IPSEC operation status has no errors.
int odp_ipsec_capability(odp_ipsec_capability_t *capa)
Query IPSEC capabilities.
int odp_ipsec_out(const odp_packet_t pkt_in[], int num_in, odp_packet_t pkt_out[], int *num_out, const odp_ipsec_out_param_t *param)
Outbound synchronous IPSEC operation.
void odp_ipsec_config_init(odp_ipsec_config_t *config)
Initialize IPSEC configuration options.
void odp_ipsec_sa_param_init(odp_ipsec_sa_param_t *param)
Initialize IPSEC SA parameters.
int odp_ipsec_config(const odp_ipsec_config_t *config)
Global IPSEC configuration.
int odp_ipsec_sa_disable(odp_ipsec_sa_t sa)
Disable IPSEC SA.
int odp_ipsec_out_enq(const odp_packet_t pkt[], int num, const odp_ipsec_out_param_t *param)
Outbound asynchronous IPSEC operation.
int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event)
Get IPSEC status information from an ODP_EVENT_IPSEC_STATUS event.
int odp_ipsec_cipher_capability(odp_cipher_alg_t cipher, odp_ipsec_cipher_capability_t capa[], int num)
Query supported IPSEC cipher algorithm capabilities.
#define ODP_IPSEC_SA_INVALID
Invalid IPSEC SA.
odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
Create IPSEC SA.
int odp_ipsec_sa_destroy(odp_ipsec_sa_t sa)
Destroy IPSEC SA.
int odp_ipsec_result(odp_ipsec_packet_result_t *result, odp_packet_t packet)
Get IPSEC operation results from an IPSEC processed packet.
@ ODP_IPSEC_OP_MODE_SYNC
Synchronous IPSEC operation.
@ ODP_IPSEC_OP_MODE_ASYNC
Asynchronous IPSEC operation.
@ ODP_IPSEC_STATUS_SA_DISABLE
Response to SA disable command.
int odp_pktio_mac_addr(odp_pktio_t pktio, void *mac_addr, int size)
Get the default MAC address of a packet IO interface.
void odp_pktin_queue_param_init(odp_pktin_queue_param_t *param)
Initialize packet input queue parameters.
void odp_pktio_param_init(odp_pktio_param_t *param)
Initialize pktio params.
int odp_pktio_close(odp_pktio_t pktio)
Close a packet IO interface.
int odp_pktout_queue(odp_pktio_t pktio, odp_pktout_queue_t queues[], int num)
Direct packet output queues.
void odp_pktio_config_init(odp_pktio_config_t *config)
Initialize packet IO configuration options.
odp_pktio_t odp_pktio_open(const char *name, odp_pool_t pool, const odp_pktio_param_t *param)
Open a packet IO interface.
int odp_pktio_config(odp_pktio_t pktio, const odp_pktio_config_t *config)
Configure packet IO interface options.
int odp_pktio_start(odp_pktio_t pktio)
Start packet receive and transmit.
#define ODP_PKTIO_INVALID
Invalid packet IO handle.
int odp_pktin_queue(odp_pktio_t pktio, odp_pktin_queue_t queues[], int num)
Direct packet input queues.
void odp_pktout_queue_param_init(odp_pktout_queue_param_t *param)
Initialize packet output queue parameters.
int odp_pktout_event_queue(odp_pktio_t pktio, odp_queue_t queues[], int num)
Event queues for packet output.
int odp_pktio_stop(odp_pktio_t pktio)
Stop packet receive and transmit.
int odp_pktin_recv(odp_pktin_queue_t queue, odp_packet_t packets[], int num)
Receive packets directly from an interface input queue.
int odp_pktio_capability(odp_pktio_t pktio, odp_pktio_capability_t *capa)
Query packet IO interface capabilities.
int odp_pktout_send(odp_pktout_queue_t queue, const odp_packet_t packets[], int num)
Send packets directly to an interface output queue.
int odp_pktin_queue_config(odp_pktio_t pktio, const odp_pktin_queue_param_t *param)
Configure packet input queues.
int odp_pktout_queue_config(odp_pktio_t pktio, const odp_pktout_queue_param_t *param)
Configure packet output queues.
@ ODP_PKTOUT_MODE_DIRECT
Direct packet output on the interface.
@ ODP_PKTOUT_MODE_QUEUE
Packet output through event queues.
@ ODP_PKTIO_OP_MT_UNSAFE
Not multithread safe operation.
@ ODP_PKTIO_OP_MT
Multithread safe operation.
@ ODP_PKTIN_MODE_DIRECT
Direct packet input from the interface.
@ ODP_PKTIN_MODE_SCHED
Packet input through scheduler and scheduled event queues.
void odp_packet_to_event_multi(const odp_packet_t pkt[], odp_event_t ev[], int num)
Convert multiple packet handles to events.
uint32_t odp_packet_l4_offset(odp_packet_t pkt)
Layer 4 start offset.
void * odp_packet_push_head(odp_packet_t pkt, uint32_t len)
Push out packet head.
int odp_packet_has_error(odp_packet_t pkt)
Check for all parse errors in packet.
odp_packet_t odp_packet_from_event(odp_event_t ev)
Get packet handle from event.
void odp_packet_free(odp_packet_t pkt)
Free packet.
uint32_t odp_packet_l3_offset(odp_packet_t pkt)
Layer 3 start offset.
int odp_packet_copy_from_mem(odp_packet_t pkt, uint32_t offset, uint32_t len, const void *src)
Copy data from memory to packet.
int odp_packet_has_ipsec(odp_packet_t pkt)
Check for IPSec.
void odp_packet_user_ptr_set(odp_packet_t pkt, const void *user_ptr)
Set user context pointer.
void odp_packet_free_multi(const odp_packet_t pkt[], int num)
Free multiple packets.
int odp_packet_copy_to_mem(odp_packet_t pkt, uint32_t offset, uint32_t len, void *dst)
Copy data from packet to memory.
void * odp_packet_user_ptr(odp_packet_t pkt)
User context pointer.
int odp_packet_has_udp(odp_packet_t pkt)
Check for UDP.
void * odp_packet_pull_head(odp_packet_t pkt, uint32_t len)
Pull in packet head.
@ ODP_PROTO_LAYER_L4
Layer L4 protocols (UDP, TCP, SCTP)
odp_pool_t odp_pool_create(const char *name, const odp_pool_param_t *param)
Create a pool.
int odp_pool_capability(odp_pool_capability_t *capa)
Query pool capabilities.
void odp_pool_param_init(odp_pool_param_t *param)
Initialize pool params.
int odp_pool_destroy(odp_pool_t pool)
Destroy a pool previously created by odp_pool_create()
#define ODP_POOL_INVALID
Invalid pool.
@ ODP_POOL_PACKET
Packet pool.
int odp_queue_enq_multi(odp_queue_t queue, const odp_event_t events[], int num)
Enqueue multiple events to a queue.
void odp_queue_param_init(odp_queue_param_t *param)
Initialize queue params.
#define ODP_QUEUE_INVALID
Invalid queue.
#define ODP_QUEUE_NAME_LEN
Maximum queue name length, including the null character.
odp_queue_t odp_queue_create(const char *name, const odp_queue_param_t *param)
Queue create.
int odp_queue_destroy(odp_queue_t queue)
Destroy ODP queue.
@ ODP_QUEUE_TYPE_SCHED
Scheduled queue.
int odp_schedule_multi_no_wait(odp_queue_t *from, odp_event_t events[], int num)
Schedule, do not wait for events.
#define ODP_SCHED_SYNC_PARALLEL
Parallel scheduled queues.
#define ODP_SCHED_SYNC_ORDERED
Ordered queue synchronization.
#define ODP_SCHED_NO_WAIT
Do not wait.
int odp_schedule_default_prio(void)
Default scheduling priority level.
int odp_schedule_max_prio(void)
Maximum scheduling priority level.
int odp_schedule_config(const odp_schedule_config_t *config)
Global schedule configuration.
odp_event_t odp_schedule(odp_queue_t *from, uint64_t wait)
Schedule an event.
#define ODP_SCHED_GROUP_ALL
Group of all threads.
int odp_shm_free(odp_shm_t shm)
Free a contiguous block of shared memory.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
#define ODP_SHM_INVALID
Invalid shared memory block.
odp_shm_t odp_shm_reserve(const char *name, uint64_t size, uint64_t align, uint32_t flags)
Reserve a contiguous block of shared memory.
bool odp_bool_t
Boolean type.
int odp_thread_id(void)
Get thread identifier.
@ ODP_THREAD_WORKER
Worker thread.
@ ODP_THREAD_CONTROL
Control thread.
The OpenDataPlane API.
Global initialization parameters.
odp_mem_model_t mem_model
Application memory model.
Authentication algorithm capabilities.
odp_crypto_cipher_algos_t ciphers
Supported cipher algorithms.
uint32_t max_num_sa
Maximum number of IPSEC SAs.
odp_crypto_auth_algos_t auths
Supported authentication algorithms.
uint32_t max_queues
Maximum number of different destination queues.
Cipher algorithm capabilities.
IPSEC configuration options.
odp_ipsec_op_mode_t outbound_mode
Outbound IPSEC operation mode.
odp_ipsec_inbound_config_t inbound
IPSEC inbound processing configuration.
odp_ipsec_op_mode_t inbound_mode
Inbound IPSEC operation mode.
IPSEC inbound operation parameters.
const odp_ipsec_sa_t * sa
Pointer to an array of IPSEC SAs.
odp_queue_t default_queue
Default destination queue for IPSEC events.
odp_proto_layer_t parse_level
Parse packet headers after IPSEC transformation.
uint64_t all
All status bits.
IPSEC outbound operation parameters.
const odp_ipsec_sa_t * sa
Pointer to an array of IPSEC SAs.
IPSEC operation result for a packet.
odp_ipsec_op_status_t status
IPSEC operation status.
IPSEC Security Association (SA) parameters.
odp_queue_t dest_queue
Destination queue for IPSEC events.
odp_ipsec_dir_t dir
IPSEC SA direction: inbound or outbound.
IPSEC status content.
int result
Result of the operation.
odp_ipsec_status_id_t id
IPSEC status ID.
Packet input queue parameters.
uint32_t num_queues
Number of input queues to be created.
odp_pktio_op_mode_t op_mode
Operation mode.
odp_queue_param_t queue_param
Queue parameters.
odp_pktin_hash_proto_t hash_proto
Protocol field selection for hashing.
odp_bool_t hash_enable
Enable flow hashing.
uint32_t max_input_queues
Maximum number of input queues.
uint32_t max_output_queues
Maximum number of output queues.
Packet IO configuration options.
Packet IO parameters.
odp_pktin_mode_t in_mode
Packet input mode.
odp_pktout_mode_t out_mode
Packet output mode.
Packet output queue parameters.
odp_pktio_op_mode_t op_mode
Operation mode.
uint32_t num_queues
Number of output queues to be created.
uint32_t max_num
Maximum number of buffers of any size.
struct odp_pool_capability_t::@134 pkt
Packet pool capabilities
uint32_t max_len
Maximum packet data length in bytes.
uint32_t num
Number of buffers in the pool.
struct odp_pool_param_t::@139 pkt
Parameters for packet pools.
odp_pool_type_t type
Pool type.
uint32_t len
Minimum length of 'num' packets.
uint32_t seg_len
Minimum number of packet data bytes that can be stored in the first segment of a newly allocated pack...
ODP Queue parameters.
odp_schedule_param_t sched
Scheduler parameters.
odp_queue_type_t type
Queue type.
odp_schedule_group_t group
Thread group.
odp_schedule_prio_t prio
Priority level.
odp_schedule_sync_t sync
Synchronization method.
uint64_t all_bits
All bits of the bit field structure.
uint64_t all_bits
All bits of the bit field structure.
struct odp_pktin_hash_proto_t::@107 proto
Protocol header fields for hashing.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.