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