API Reference Manual  1.46.0
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 
54 enum {
55  CIPHER_TYPE,
56  COMB_CIPHER_TYPE,
57  AUTH_TYPE,
58  COMB_AUTH_TYPE
59 };
60 
61 typedef enum {
62  PRS_OK,
63  PRS_NOK,
64  PRS_TERM
65 } parse_result_t;
66 
67 enum {
68  DIR_IN = 0,
69  DIR_OUT
70 };
71 
72 typedef struct pktio_s pktio_t;
73 
74 typedef 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 
89 typedef 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 
96 typedef struct {
97  fwd_entry_t entries[MAX_FWDS];
98  uint32_t num;
99 } lookup_table_t;
100 
101 typedef 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 
111 typedef struct prog_config_s prog_config_t;
112 
113 typedef 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 
120 typedef 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 
131 typedef uint32_t (*rx_fn_t)(thread_config_t *config, odp_event_t evs[], int num);
132 typedef void (*ipsec_fn_t)(odp_packet_t pkts[], int num, lookup_table_t *fwd_tbl, stats_t *stats);
133 typedef void (*drain_fn_t)(prog_config_t *config);
134 
135 typedef 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 
142 typedef 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 
171 typedef struct {
172  const char *name;
173  int idx;
174  int type;
175 } exposed_alg_t;
176 
177 typedef struct {
178  odp_packet_t pkts[MAX_BURST];
179  const pktio_t *pktio;
180  uint32_t num;
181 } pkt_vec_t;
182 
183 typedef struct {
184  pkt_vec_t vecs[MAX_QUEUES];
185  uint8_t num_qs;
186 } pkt_out_t;
187 
188 typedef 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 
194 static 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 */
220 static odp_ipsec_sa_t *spi_to_sa_map[2U][MAX_SPIS];
221 static const int ipsec_out_mark;
222 static __thread pkt_ifs_t ifs;
223 static prog_config_t *prog_conf;
224 
225 static 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 
237 static void terminate(int signal ODP_UNUSED)
238 {
239  odp_atomic_store_u32(&prog_conf->is_running, 0U);
240 }
241 
242 static 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 
263 static 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 
283  odp_ipsec_cipher_capability_t capa[c_cnt];
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 
322 static 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 
442 static 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 
458 static inline int process_ipsec_in_enq(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num)
459 {
460  odp_ipsec_in_param_t param;
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 
483 static 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 
499 static inline int process_ipsec_out_enq(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num)
500 {
501  odp_ipsec_out_param_t param;
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 
524 static 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 
538 static 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 
581 static 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 
637 static 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 
673 static 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 
713 static inline odp_bool_t is_ipsec_in(odp_packet_t pkt)
714 {
715  return odp_packet_user_ptr(pkt) == NULL;
716 }
717 
718 static 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 
761 static void drain_scheduler(prog_config_t *config ODP_UNUSED)
762 {
763  odp_event_t ev;
764 
765  while (true) {
766  ev = odp_schedule(NULL, ODP_SCHED_NO_WAIT);
767 
768  if (ev == ODP_EVENT_INVALID)
769  break;
770 
771  odp_event_free(ev);
772  }
773 }
774 
775 static inline int process_ipsec_in(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num,
776  odp_packet_t pkts_out[])
777 {
778  odp_ipsec_in_param_t param;
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 
799 static inline int process_ipsec_out(odp_packet_t pkts[], const odp_ipsec_sa_t sas[], int num,
800  odp_packet_t pkts_out[])
801 {
802  odp_ipsec_out_param_t param;
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 
823 static 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 
879 static 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 
939 static 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 
951 static 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;
963  q_param.sched.group = ODP_SCHED_GROUP_ALL;
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) {
975  ipsec_config.inbound_mode = ODP_IPSEC_OP_MODE_ASYNC;
976  ipsec_config.outbound_mode = ODP_IPSEC_OP_MODE_ASYNC;
977  config->ops.proc = process_packets_in_enq;
978  config->ops.compl = complete_ipsec_ops;
979  config->ops.drain = drain_scheduler;
980  } else {
981  ipsec_config.inbound_mode = ODP_IPSEC_OP_MODE_SYNC;
982  ipsec_config.outbound_mode = ODP_IPSEC_OP_MODE_SYNC;
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. */
990  ipsec_config.inbound.parse_level = ODP_PROTO_LAYER_L4;
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 
1000 static 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 :
1022  q_param.sched.group = ODP_SCHED_GROUP_ALL;
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 
1035 static 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 
1082 static 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 
1109 static 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 
1137 static 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 
1163 static 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 
1206 static 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 
1229 static 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 
1264 static 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 
1299 static 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 
1319 static 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 
1329 static 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 
1392 static 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 
1414 static 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 
1469 static 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 
1553 static 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 
1568 static 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 
1573 static 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 
1591 static 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 
1598 static 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 
1610 static 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 
1754 static 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 
1764 static 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 
1826 static 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 
1856 static 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 
1872 static 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 
1882 static 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 
1916 static 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 
1945 static 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 
1976 int 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 
2056 out_test:
2057  teardown_test(prog_conf);
2058 
2059 out:
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_pull_head(odp_packet_t pkt, uint32_t len)
Pull in packet head.
void * odp_packet_user_ptr(odp_packet_t pkt)
User context pointer.
void odp_packet_to_event_multi(const odp_packet_t pkt[], odp_event_t ev[], int num)
Convert multiple packet handles to events.
void * odp_packet_push_head(odp_packet_t pkt, uint32_t len)
Push out packet head.
uint32_t odp_packet_l4_offset(odp_packet_t pkt)
Layer 4 start offset.
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.
int odp_packet_has_udp(odp_packet_t pkt)
Check for UDP.
@ 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.
#define ODP_SHM_INVALID
Invalid shared memory block.
void * odp_shm_addr(odp_shm_t shm)
Shared memory block address.
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.
uint32_t spi
SPI value.
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.
struct odp_pool_capability_t::@122 pkt
Packet pool capabilities
uint32_t max_num
Maximum number of buffers of any size.
uint32_t max_len
Maximum packet data length in bytes.
Pool parameters.
uint32_t num
Number of buffers in the pool.
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...
struct odp_pool_param_t::@126 pkt
Parameters for packet pools.
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.
uint32_t ipv4_udp
IPv4 addresses and UDP port numbers.
struct odp_pktin_hash_proto_t::@99 proto
Protocol header fields for hashing.