mempool: fix slow allocation of large mempools
[dpdk.git] / examples / ipsec-secgw / sp4.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016 Intel Corporation
3  */
4
5 /*
6  * Security Policies
7  */
8 #include <sys/types.h>
9 #include <netinet/in.h>
10 #include <netinet/ip.h>
11
12 #include <rte_acl.h>
13 #include <rte_ip.h>
14
15 #include "ipsec.h"
16 #include "parser.h"
17
18 #define MAX_ACL_RULE_NUM        1024
19
20 #define IPV4_DST_FROM_SP(acr) \
21                 (rte_cpu_to_be_32((acr).field[DST_FIELD_IPV4].value.u32))
22
23 #define IPV4_SRC_FROM_SP(acr) \
24                 (rte_cpu_to_be_32((acr).field[SRC_FIELD_IPV4].value.u32))
25
26 #define IPV4_DST_MASK_FROM_SP(acr) \
27                 ((acr).field[DST_FIELD_IPV4].mask_range.u32)
28
29 #define IPV4_SRC_MASK_FROM_SP(acr) \
30                 ((acr).field[SRC_FIELD_IPV4].mask_range.u32)
31
32 /*
33  * Rule and trace formats definitions.
34  */
35 enum {
36         PROTO_FIELD_IPV4,
37         SRC_FIELD_IPV4,
38         DST_FIELD_IPV4,
39         SRCP_FIELD_IPV4,
40         DSTP_FIELD_IPV4,
41         NUM_FIELDS_IPV4
42 };
43
44 /*
45  * That effectively defines order of IPV4 classifications:
46  *  - PROTO
47  *  - SRC IP ADDRESS
48  *  - DST IP ADDRESS
49  *  - PORTS (SRC and DST)
50  */
51 enum {
52         RTE_ACL_IPV4_PROTO,
53         RTE_ACL_IPV4_SRC,
54         RTE_ACL_IPV4_DST,
55         RTE_ACL_IPV4_PORTS,
56         RTE_ACL_IPV4_NUM
57 };
58
59 static struct rte_acl_field_def ip4_defs[NUM_FIELDS_IPV4] = {
60         {
61         .type = RTE_ACL_FIELD_TYPE_BITMASK,
62         .size = sizeof(uint8_t),
63         .field_index = PROTO_FIELD_IPV4,
64         .input_index = RTE_ACL_IPV4_PROTO,
65         .offset = 0,
66         },
67         {
68         .type = RTE_ACL_FIELD_TYPE_MASK,
69         .size = sizeof(uint32_t),
70         .field_index = SRC_FIELD_IPV4,
71         .input_index = RTE_ACL_IPV4_SRC,
72         .offset = offsetof(struct ip, ip_src) - offsetof(struct ip, ip_p)
73         },
74         {
75         .type = RTE_ACL_FIELD_TYPE_MASK,
76         .size = sizeof(uint32_t),
77         .field_index = DST_FIELD_IPV4,
78         .input_index = RTE_ACL_IPV4_DST,
79         .offset = offsetof(struct ip, ip_dst) - offsetof(struct ip, ip_p)
80         },
81         {
82         .type = RTE_ACL_FIELD_TYPE_RANGE,
83         .size = sizeof(uint16_t),
84         .field_index = SRCP_FIELD_IPV4,
85         .input_index = RTE_ACL_IPV4_PORTS,
86         .offset = sizeof(struct ip) - offsetof(struct ip, ip_p)
87         },
88         {
89         .type = RTE_ACL_FIELD_TYPE_RANGE,
90         .size = sizeof(uint16_t),
91         .field_index = DSTP_FIELD_IPV4,
92         .input_index = RTE_ACL_IPV4_PORTS,
93         .offset = sizeof(struct ip) - offsetof(struct ip, ip_p) +
94                 sizeof(uint16_t)
95         },
96 };
97
98 RTE_ACL_RULE_DEF(acl4_rules, RTE_DIM(ip4_defs));
99
100 static struct acl4_rules acl4_rules_out[MAX_ACL_RULE_NUM];
101 static uint32_t nb_acl4_rules_out;
102
103 static struct acl4_rules acl4_rules_in[MAX_ACL_RULE_NUM];
104 static uint32_t nb_acl4_rules_in;
105
106 void
107 parse_sp4_tokens(char **tokens, uint32_t n_tokens,
108         struct parse_status *status)
109 {
110         struct acl4_rules *rule_ipv4 = NULL;
111
112         uint32_t *ri = NULL; /* rule index */
113         uint32_t ti = 0; /* token index */
114         uint32_t tv;
115
116         uint32_t esp_p = 0;
117         uint32_t protect_p = 0;
118         uint32_t bypass_p = 0;
119         uint32_t discard_p = 0;
120         uint32_t pri_p = 0;
121         uint32_t src_p = 0;
122         uint32_t dst_p = 0;
123         uint32_t proto_p = 0;
124         uint32_t sport_p = 0;
125         uint32_t dport_p = 0;
126
127         if (strcmp(tokens[1], "in") == 0) {
128                 ri = &nb_acl4_rules_in;
129
130                 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
131                         "too many sp rules, abort insertion\n");
132                 if (status->status < 0)
133                         return;
134
135                 rule_ipv4 = &acl4_rules_in[*ri];
136
137         } else if (strcmp(tokens[1], "out") == 0) {
138                 ri = &nb_acl4_rules_out;
139
140                 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
141                         "too many sp rules, abort insertion\n");
142                 if (status->status < 0)
143                         return;
144
145                 rule_ipv4 = &acl4_rules_out[*ri];
146         } else {
147                 APP_CHECK(0, status, "unrecognized input \"%s\", expect"
148                         " \"in\" or \"out\"\n", tokens[ti]);
149                 return;
150         }
151
152         rule_ipv4->data.category_mask = 1;
153
154         for (ti = 2; ti < n_tokens; ti++) {
155                 if (strcmp(tokens[ti], "esp") == 0) {
156                         /* currently do nothing */
157                         APP_CHECK_PRESENCE(esp_p, tokens[ti], status);
158                         if (status->status < 0)
159                                 return;
160                         esp_p = 1;
161                         continue;
162                 }
163
164                 if (strcmp(tokens[ti], "protect") == 0) {
165                         APP_CHECK_PRESENCE(protect_p, tokens[ti], status);
166                         if (status->status < 0)
167                                 return;
168                         APP_CHECK(bypass_p == 0, status, "conflict item "
169                                 "between \"%s\" and \"%s\"", tokens[ti],
170                                 "bypass");
171                         if (status->status < 0)
172                                 return;
173                         APP_CHECK(discard_p == 0, status, "conflict item "
174                                 "between \"%s\" and \"%s\"", tokens[ti],
175                                 "discard");
176                         if (status->status < 0)
177                                 return;
178                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
179                         if (status->status < 0)
180                                 return;
181                         APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
182                         if (status->status < 0)
183                                 return;
184
185                         tv = atoi(tokens[ti]);
186                         APP_CHECK(tv != DISCARD && tv != BYPASS, status,
187                                 "invalid SPI: %s", tokens[ti]);
188                         if (status->status < 0)
189                                 return;
190                         rule_ipv4->data.userdata = tv;
191
192                         protect_p = 1;
193                         continue;
194                 }
195
196                 if (strcmp(tokens[ti], "bypass") == 0) {
197                         APP_CHECK_PRESENCE(bypass_p, tokens[ti], status);
198                         if (status->status < 0)
199                                 return;
200                         APP_CHECK(protect_p == 0, status, "conflict item "
201                                 "between \"%s\" and \"%s\"", tokens[ti],
202                                 "protect");
203                         if (status->status < 0)
204                                 return;
205                         APP_CHECK(discard_p == 0, status, "conflict item "
206                                 "between \"%s\" and \"%s\"", tokens[ti],
207                                 "discard");
208                         if (status->status < 0)
209                                 return;
210
211                         rule_ipv4->data.userdata = BYPASS;
212
213                         bypass_p = 1;
214                         continue;
215                 }
216
217                 if (strcmp(tokens[ti], "discard") == 0) {
218                         APP_CHECK_PRESENCE(discard_p, tokens[ti], status);
219                         if (status->status < 0)
220                                 return;
221                         APP_CHECK(protect_p == 0, status, "conflict item "
222                                 "between \"%s\" and \"%s\"", tokens[ti],
223                                 "protect");
224                         if (status->status < 0)
225                                 return;
226                         APP_CHECK(bypass_p == 0, status, "conflict item "
227                                 "between \"%s\" and \"%s\"", tokens[ti],
228                                 "discard");
229                         if (status->status < 0)
230                                 return;
231
232                         rule_ipv4->data.userdata = DISCARD;
233
234                         discard_p = 1;
235                         continue;
236                 }
237
238                 if (strcmp(tokens[ti], "pri") == 0) {
239                         APP_CHECK_PRESENCE(pri_p, tokens[ti], status);
240                         if (status->status < 0)
241                                 return;
242                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
243                         if (status->status < 0)
244                                 return;
245                         APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
246                         if (status->status < 0)
247                                 return;
248
249                         rule_ipv4->data.priority = atoi(tokens[ti]);
250
251                         pri_p = 1;
252                         continue;
253                 }
254
255                 if (strcmp(tokens[ti], "src") == 0) {
256                         struct in_addr ip;
257                         uint32_t depth;
258
259                         APP_CHECK_PRESENCE(src_p, tokens[ti], status);
260                         if (status->status < 0)
261                                 return;
262                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
263                         if (status->status < 0)
264                                 return;
265
266                         APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
267                                 &depth) == 0, status, "unrecognized "
268                                 "input \"%s\", expect valid ipv4 addr",
269                                 tokens[ti]);
270                         if (status->status < 0)
271                                 return;
272
273                         rule_ipv4->field[1].value.u32 =
274                                 rte_bswap32(ip.s_addr);
275                         rule_ipv4->field[1].mask_range.u32 =
276                                 depth;
277
278                         src_p = 1;
279                         continue;
280                 }
281
282                 if (strcmp(tokens[ti], "dst") == 0) {
283                         struct in_addr ip;
284                         uint32_t depth;
285
286                         APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
287                         if (status->status < 0)
288                                 return;
289                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
290                         if (status->status < 0)
291                                 return;
292                         APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
293                                 &depth) == 0, status, "unrecognized "
294                                 "input \"%s\", expect valid ipv4 addr",
295                                 tokens[ti]);
296                         if (status->status < 0)
297                                 return;
298
299                         rule_ipv4->field[2].value.u32 =
300                                 rte_bswap32(ip.s_addr);
301                         rule_ipv4->field[2].mask_range.u32 =
302                                 depth;
303
304                         dst_p = 1;
305                         continue;
306                 }
307
308                 if (strcmp(tokens[ti], "proto") == 0) {
309                         uint16_t low, high;
310
311                         APP_CHECK_PRESENCE(proto_p, tokens[ti], status);
312                         if (status->status < 0)
313                                 return;
314                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
315                         if (status->status < 0)
316                                 return;
317
318                         APP_CHECK(parse_range(tokens[ti], &low, &high)
319                                 == 0, status, "unrecognized input \"%s\""
320                                 ", expect \"from:to\"", tokens[ti]);
321                         if (status->status < 0)
322                                 return;
323                         APP_CHECK(low <= 0xff, status, "proto low "
324                                 "over-limit");
325                         if (status->status < 0)
326                                 return;
327                         APP_CHECK(high <= 0xff, status, "proto high "
328                                 "over-limit");
329                         if (status->status < 0)
330                                 return;
331
332                         rule_ipv4->field[0].value.u8 = (uint8_t)low;
333                         rule_ipv4->field[0].mask_range.u8 = (uint8_t)high;
334
335                         proto_p = 1;
336                         continue;
337                 }
338
339                 if (strcmp(tokens[ti], "sport") == 0) {
340                         uint16_t port_low, port_high;
341
342                         APP_CHECK_PRESENCE(sport_p, tokens[ti], status);
343                         if (status->status < 0)
344                                 return;
345                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
346                         if (status->status < 0)
347                                 return;
348
349                         APP_CHECK(parse_range(tokens[ti], &port_low,
350                                 &port_high) == 0, status, "unrecognized "
351                                 "input \"%s\", expect \"port_from:"
352                                 "port_to\"", tokens[ti]);
353                         if (status->status < 0)
354                                 return;
355
356                         rule_ipv4->field[3].value.u16 = port_low;
357                         rule_ipv4->field[3].mask_range.u16 = port_high;
358
359                         sport_p = 1;
360                         continue;
361                 }
362
363                 if (strcmp(tokens[ti], "dport") == 0) {
364                         uint16_t port_low, port_high;
365
366                         APP_CHECK_PRESENCE(dport_p, tokens[ti], status);
367                         if (status->status < 0)
368                                 return;
369                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
370                         if (status->status < 0)
371                                 return;
372
373                         APP_CHECK(parse_range(tokens[ti], &port_low,
374                                 &port_high) == 0, status, "unrecognized "
375                                 "input \"%s\", expect \"port_from:"
376                                 "port_to\"", tokens[ti]);
377                         if (status->status < 0)
378                                 return;
379
380                         rule_ipv4->field[4].value.u16 = port_low;
381                         rule_ipv4->field[4].mask_range.u16 = port_high;
382
383                         dport_p = 1;
384                         continue;
385                 }
386
387                 /* unrecognizeable input */
388                 APP_CHECK(0, status, "unrecognized input \"%s\"",
389                         tokens[ti]);
390                 return;
391         }
392
393         /* check if argument(s) are missing */
394         APP_CHECK(esp_p == 1, status, "missing argument \"esp\"");
395         if (status->status < 0)
396                 return;
397
398         APP_CHECK(protect_p | bypass_p | discard_p, status, "missing "
399                 "argument \"protect\", \"bypass\", or \"discard\"");
400         if (status->status < 0)
401                 return;
402
403         *ri = *ri + 1;
404 }
405
406 static void
407 print_one_ip4_rule(const struct acl4_rules *rule, int32_t extra)
408 {
409         uint8_t a, b, c, d;
410
411         uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
412                         &a, &b, &c, &d);
413         printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
414                         rule->field[SRC_FIELD_IPV4].mask_range.u32);
415         uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
416                         &a, &b, &c, &d);
417         printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
418                         rule->field[DST_FIELD_IPV4].mask_range.u32);
419         printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
420                 rule->field[SRCP_FIELD_IPV4].value.u16,
421                 rule->field[SRCP_FIELD_IPV4].mask_range.u16,
422                 rule->field[DSTP_FIELD_IPV4].value.u16,
423                 rule->field[DSTP_FIELD_IPV4].mask_range.u16,
424                 rule->field[PROTO_FIELD_IPV4].value.u8,
425                 rule->field[PROTO_FIELD_IPV4].mask_range.u8);
426         if (extra)
427                 printf("0x%x-0x%x-0x%x ",
428                         rule->data.category_mask,
429                         rule->data.priority,
430                         rule->data.userdata);
431 }
432
433 static inline void
434 dump_ip4_rules(const struct acl4_rules *rule, int32_t num, int32_t extra)
435 {
436         int32_t i;
437
438         for (i = 0; i < num; i++, rule++) {
439                 printf("\t%d:", i + 1);
440                 print_one_ip4_rule(rule, extra);
441                 printf("\n");
442         }
443 }
444
445 static struct rte_acl_ctx *
446 acl4_init(const char *name, int32_t socketid, const struct acl4_rules *rules,
447                 uint32_t rules_nb)
448 {
449         char s[PATH_MAX];
450         struct rte_acl_param acl_param;
451         struct rte_acl_config acl_build_param;
452         struct rte_acl_ctx *ctx;
453
454         printf("Creating SP context with %u max rules\n", MAX_ACL_RULE_NUM);
455
456         memset(&acl_param, 0, sizeof(acl_param));
457
458         /* Create ACL contexts */
459         snprintf(s, sizeof(s), "%s_%d", name, socketid);
460
461         printf("IPv4 %s entries [%u]:\n", s, rules_nb);
462         dump_ip4_rules(rules, rules_nb, 1);
463
464         acl_param.name = s;
465         acl_param.socket_id = socketid;
466         acl_param.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ip4_defs));
467         acl_param.max_rule_num = MAX_ACL_RULE_NUM;
468
469         ctx = rte_acl_create(&acl_param);
470         if (ctx == NULL)
471                 rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
472
473         if (rte_acl_add_rules(ctx, (const struct rte_acl_rule *)rules,
474                                 rules_nb) < 0)
475                 rte_exit(EXIT_FAILURE, "add rules failed\n");
476
477         /* Perform builds */
478         memset(&acl_build_param, 0, sizeof(acl_build_param));
479
480         acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
481         acl_build_param.num_fields = RTE_DIM(ip4_defs);
482         memcpy(&acl_build_param.defs, ip4_defs, sizeof(ip4_defs));
483
484         if (rte_acl_build(ctx, &acl_build_param) != 0)
485                 rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
486
487         rte_acl_dump(ctx);
488
489         return ctx;
490 }
491
492 /*
493  * check that for each rule it's SPI has a correspondent entry in SAD
494  */
495 static int
496 check_spi_value(int inbound)
497 {
498         uint32_t i, num, spi;
499         const struct acl4_rules *acr;
500
501         if (inbound != 0) {
502                 acr = acl4_rules_in;
503                 num = nb_acl4_rules_in;
504         } else {
505                 acr = acl4_rules_out;
506                 num = nb_acl4_rules_out;
507         }
508
509         for (i = 0; i != num; i++) {
510                 spi = acr[i].data.userdata;
511                 if (spi != DISCARD && spi != BYPASS &&
512                                 sa_spi_present(spi, inbound) < 0) {
513                         RTE_LOG(ERR, IPSEC, "SPI %u is not present in SAD\n",
514                                 spi);
515                         return -ENOENT;
516                 }
517         }
518
519         return 0;
520 }
521
522 void
523 sp4_init(struct socket_ctx *ctx, int32_t socket_id)
524 {
525         const char *name;
526
527         if (ctx == NULL)
528                 rte_exit(EXIT_FAILURE, "NULL context.\n");
529
530         if (ctx->sp_ip4_in != NULL)
531                 rte_exit(EXIT_FAILURE, "Inbound SP DB for socket %u already "
532                                 "initialized\n", socket_id);
533
534         if (ctx->sp_ip4_out != NULL)
535                 rte_exit(EXIT_FAILURE, "Outbound SP DB for socket %u already "
536                                 "initialized\n", socket_id);
537
538         if (check_spi_value(1) < 0)
539                 rte_exit(EXIT_FAILURE,
540                         "Inbound IPv4 SP DB has unmatched in SAD SPIs\n");
541
542         if (check_spi_value(0) < 0)
543                 rte_exit(EXIT_FAILURE,
544                         "Outbound IPv4 SP DB has unmatched in SAD SPIs\n");
545
546         if (nb_acl4_rules_in > 0) {
547                 name = "sp_ip4_in";
548                 ctx->sp_ip4_in = (struct sp_ctx *)acl4_init(name,
549                         socket_id, acl4_rules_in, nb_acl4_rules_in);
550         } else
551                 RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
552                         "specified\n");
553
554         if (nb_acl4_rules_out > 0) {
555                 name = "sp_ip4_out";
556                 ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
557                         socket_id, acl4_rules_out, nb_acl4_rules_out);
558         } else
559                 RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
560                         "specified\n");
561 }
562
563 /*
564  * Search though SP rules for given SPI.
565  */
566 int
567 sp4_spi_present(uint32_t spi, int inbound, struct ip_addr ip_addr[2],
568                         uint32_t mask[2])
569 {
570         uint32_t i, num;
571         const struct acl4_rules *acr;
572
573         if (inbound != 0) {
574                 acr = acl4_rules_in;
575                 num = nb_acl4_rules_in;
576         } else {
577                 acr = acl4_rules_out;
578                 num = nb_acl4_rules_out;
579         }
580
581         for (i = 0; i != num; i++) {
582                 if (acr[i].data.userdata == spi) {
583                         if (NULL != ip_addr && NULL != mask) {
584                                 ip_addr[0].ip.ip4 = IPV4_SRC_FROM_SP(acr[i]);
585                                 ip_addr[1].ip.ip4 = IPV4_DST_FROM_SP(acr[i]);
586                                 mask[0] = IPV4_SRC_MASK_FROM_SP(acr[i]);
587                                 mask[1] = IPV4_DST_MASK_FROM_SP(acr[i]);
588                         }
589                         return i;
590                 }
591         }
592
593         return -ENOENT;
594 }