Imported Upstream version 16.11
[deb_dpdk.git] / examples / ipsec-secgw / sp4.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2016 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /*
35  * Security Policies
36  */
37 #include <sys/types.h>
38 #include <netinet/in.h>
39 #include <netinet/ip.h>
40
41 #include <rte_acl.h>
42 #include <rte_ip.h>
43
44 #include "ipsec.h"
45 #include "parser.h"
46
47 #define MAX_ACL_RULE_NUM        1024
48
49 /*
50  * Rule and trace formats definitions.
51  */
52 enum {
53         PROTO_FIELD_IPV4,
54         SRC_FIELD_IPV4,
55         DST_FIELD_IPV4,
56         SRCP_FIELD_IPV4,
57         DSTP_FIELD_IPV4,
58         NUM_FIELDS_IPV4
59 };
60
61 /*
62  * That effectively defines order of IPV4 classifications:
63  *  - PROTO
64  *  - SRC IP ADDRESS
65  *  - DST IP ADDRESS
66  *  - PORTS (SRC and DST)
67  */
68 enum {
69         RTE_ACL_IPV4_PROTO,
70         RTE_ACL_IPV4_SRC,
71         RTE_ACL_IPV4_DST,
72         RTE_ACL_IPV4_PORTS,
73         RTE_ACL_IPV4_NUM
74 };
75
76 struct rte_acl_field_def ip4_defs[NUM_FIELDS_IPV4] = {
77         {
78         .type = RTE_ACL_FIELD_TYPE_BITMASK,
79         .size = sizeof(uint8_t),
80         .field_index = PROTO_FIELD_IPV4,
81         .input_index = RTE_ACL_IPV4_PROTO,
82         .offset = 0,
83         },
84         {
85         .type = RTE_ACL_FIELD_TYPE_MASK,
86         .size = sizeof(uint32_t),
87         .field_index = SRC_FIELD_IPV4,
88         .input_index = RTE_ACL_IPV4_SRC,
89         .offset = offsetof(struct ip, ip_src) - offsetof(struct ip, ip_p)
90         },
91         {
92         .type = RTE_ACL_FIELD_TYPE_MASK,
93         .size = sizeof(uint32_t),
94         .field_index = DST_FIELD_IPV4,
95         .input_index = RTE_ACL_IPV4_DST,
96         .offset = offsetof(struct ip, ip_dst) - offsetof(struct ip, ip_p)
97         },
98         {
99         .type = RTE_ACL_FIELD_TYPE_RANGE,
100         .size = sizeof(uint16_t),
101         .field_index = SRCP_FIELD_IPV4,
102         .input_index = RTE_ACL_IPV4_PORTS,
103         .offset = sizeof(struct ip) - offsetof(struct ip, ip_p)
104         },
105         {
106         .type = RTE_ACL_FIELD_TYPE_RANGE,
107         .size = sizeof(uint16_t),
108         .field_index = DSTP_FIELD_IPV4,
109         .input_index = RTE_ACL_IPV4_PORTS,
110         .offset = sizeof(struct ip) - offsetof(struct ip, ip_p) +
111                 sizeof(uint16_t)
112         },
113 };
114
115 RTE_ACL_RULE_DEF(acl4_rules, RTE_DIM(ip4_defs));
116
117 struct acl4_rules acl4_rules_out[MAX_ACL_RULE_NUM];
118 uint32_t nb_acl4_rules_out;
119
120 struct acl4_rules acl4_rules_in[MAX_ACL_RULE_NUM];
121 uint32_t nb_acl4_rules_in;
122
123 void
124 parse_sp4_tokens(char **tokens, uint32_t n_tokens,
125         struct parse_status *status)
126 {
127         struct acl4_rules *rule_ipv4 = NULL;
128
129         uint32_t *ri = NULL; /* rule index */
130         uint32_t ti = 0; /* token index */
131
132         uint32_t esp_p = 0;
133         uint32_t protect_p = 0;
134         uint32_t bypass_p = 0;
135         uint32_t discard_p = 0;
136         uint32_t pri_p = 0;
137         uint32_t src_p = 0;
138         uint32_t dst_p = 0;
139         uint32_t proto_p = 0;
140         uint32_t sport_p = 0;
141         uint32_t dport_p = 0;
142
143         if (strcmp(tokens[1], "in") == 0) {
144                 ri = &nb_acl4_rules_in;
145
146                 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
147                         "too many sp rules, abort insertion\n");
148                 if (status->status < 0)
149                         return;
150
151                 rule_ipv4 = &acl4_rules_in[*ri];
152
153         } else if (strcmp(tokens[1], "out") == 0) {
154                 ri = &nb_acl4_rules_out;
155
156                 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
157                         "too many sp rules, abort insertion\n");
158                 if (status->status < 0)
159                         return;
160
161                 rule_ipv4 = &acl4_rules_out[*ri];
162         } else {
163                 APP_CHECK(0, status, "unrecognized input \"%s\", expect"
164                         " \"in\" or \"out\"\n", tokens[ti]);
165                 return;
166         }
167
168         rule_ipv4->data.category_mask = 1;
169
170         for (ti = 2; ti < n_tokens; ti++) {
171                 if (strcmp(tokens[ti], "esp") == 0) {
172                         /* currently do nothing */
173                         APP_CHECK_PRESENCE(esp_p, tokens[ti], status);
174                         if (status->status < 0)
175                                 return;
176                         esp_p = 1;
177                         continue;
178                 }
179
180                 if (strcmp(tokens[ti], "protect") == 0) {
181                         APP_CHECK_PRESENCE(protect_p, tokens[ti], status);
182                         if (status->status < 0)
183                                 return;
184                         APP_CHECK(bypass_p == 0, status, "conflict item "
185                                 "between \"%s\" and \"%s\"", tokens[ti],
186                                 "bypass");
187                         if (status->status < 0)
188                                 return;
189                         APP_CHECK(discard_p == 0, status, "conflict item "
190                                 "between \"%s\" and \"%s\"", tokens[ti],
191                                 "discard");
192                         if (status->status < 0)
193                                 return;
194                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
195                         if (status->status < 0)
196                                 return;
197                         APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
198                         if (status->status < 0)
199                                 return;
200
201                         rule_ipv4->data.userdata =
202                                 PROTECT(atoi(tokens[ti]));
203
204                         protect_p = 1;
205                         continue;
206                 }
207
208                 if (strcmp(tokens[ti], "bypass") == 0) {
209                         APP_CHECK_PRESENCE(bypass_p, tokens[ti], status);
210                         if (status->status < 0)
211                                 return;
212                         APP_CHECK(protect_p == 0, status, "conflict item "
213                                 "between \"%s\" and \"%s\"", tokens[ti],
214                                 "protect");
215                         if (status->status < 0)
216                                 return;
217                         APP_CHECK(discard_p == 0, status, "conflict item "
218                                 "between \"%s\" and \"%s\"", tokens[ti],
219                                 "discard");
220                         if (status->status < 0)
221                                 return;
222
223                         rule_ipv4->data.userdata = BYPASS;
224
225                         bypass_p = 1;
226                         continue;
227                 }
228
229                 if (strcmp(tokens[ti], "discard") == 0) {
230                         APP_CHECK_PRESENCE(discard_p, tokens[ti], status);
231                         if (status->status < 0)
232                                 return;
233                         APP_CHECK(protect_p == 0, status, "conflict item "
234                                 "between \"%s\" and \"%s\"", tokens[ti],
235                                 "protect");
236                         if (status->status < 0)
237                                 return;
238                         APP_CHECK(bypass_p == 0, status, "conflict item "
239                                 "between \"%s\" and \"%s\"", tokens[ti],
240                                 "discard");
241                         if (status->status < 0)
242                                 return;
243
244                         rule_ipv4->data.userdata = DISCARD;
245
246                         discard_p = 1;
247                         continue;
248                 }
249
250                 if (strcmp(tokens[ti], "pri") == 0) {
251                         APP_CHECK_PRESENCE(pri_p, tokens[ti], status);
252                         if (status->status < 0)
253                                 return;
254                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
255                         if (status->status < 0)
256                                 return;
257                         APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
258                         if (status->status < 0)
259                                 return;
260
261                         rule_ipv4->data.priority = atoi(tokens[ti]);
262
263                         pri_p = 1;
264                         continue;
265                 }
266
267                 if (strcmp(tokens[ti], "src") == 0) {
268                         struct in_addr ip;
269                         uint32_t depth;
270
271                         APP_CHECK_PRESENCE(src_p, tokens[ti], status);
272                         if (status->status < 0)
273                                 return;
274                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
275                         if (status->status < 0)
276                                 return;
277
278                         APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
279                                 &depth) == 0, status, "unrecognized "
280                                 "input \"%s\", expect valid ipv4 addr",
281                                 tokens[ti]);
282                         if (status->status < 0)
283                                 return;
284
285                         rule_ipv4->field[1].value.u32 =
286                                 rte_bswap32(ip.s_addr);
287                         rule_ipv4->field[1].mask_range.u32 =
288                                 depth;
289
290                         src_p = 1;
291                         continue;
292                 }
293
294                 if (strcmp(tokens[ti], "dst") == 0) {
295                         struct in_addr ip;
296                         uint32_t depth;
297
298                         APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
299                         if (status->status < 0)
300                                 return;
301                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
302                         if (status->status < 0)
303                                 return;
304                         APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
305                                 &depth) == 0, status, "unrecognized "
306                                 "input \"%s\", expect valid ipv4 addr",
307                                 tokens[ti]);
308                         if (status->status < 0)
309                                 return;
310
311                         rule_ipv4->field[2].value.u32 =
312                                 rte_bswap32(ip.s_addr);
313                         rule_ipv4->field[2].mask_range.u32 =
314                                 depth;
315
316                         dst_p = 1;
317                         continue;
318                 }
319
320                 if (strcmp(tokens[ti], "proto") == 0) {
321                         uint16_t low, high;
322
323                         APP_CHECK_PRESENCE(proto_p, tokens[ti], status);
324                         if (status->status < 0)
325                                 return;
326                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
327                         if (status->status < 0)
328                                 return;
329
330                         APP_CHECK(parse_range(tokens[ti], &low, &high)
331                                 == 0, status, "unrecognized input \"%s\""
332                                 ", expect \"from:to\"", tokens[ti]);
333                         if (status->status < 0)
334                                 return;
335                         APP_CHECK(low <= 0xff, status, "proto low "
336                                 "over-limit");
337                         if (status->status < 0)
338                                 return;
339                         APP_CHECK(high <= 0xff, status, "proto high "
340                                 "over-limit");
341                         if (status->status < 0)
342                                 return;
343
344                         rule_ipv4->field[0].value.u8 = (uint8_t)low;
345                         rule_ipv4->field[0].mask_range.u8 = (uint8_t)high;
346
347                         proto_p = 1;
348                         continue;
349                 }
350
351                 if (strcmp(tokens[ti], "sport") == 0) {
352                         uint16_t port_low, port_high;
353
354                         APP_CHECK_PRESENCE(sport_p, tokens[ti], status);
355                         if (status->status < 0)
356                                 return;
357                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
358                         if (status->status < 0)
359                                 return;
360
361                         APP_CHECK(parse_range(tokens[ti], &port_low,
362                                 &port_high) == 0, status, "unrecognized "
363                                 "input \"%s\", expect \"port_from:"
364                                 "port_to\"", tokens[ti]);
365                         if (status->status < 0)
366                                 return;
367
368                         rule_ipv4->field[3].value.u16 = port_low;
369                         rule_ipv4->field[3].mask_range.u16 = port_high;
370
371                         sport_p = 1;
372                         continue;
373                 }
374
375                 if (strcmp(tokens[ti], "dport") == 0) {
376                         uint16_t port_low, port_high;
377
378                         APP_CHECK_PRESENCE(dport_p, tokens[ti], status);
379                         if (status->status < 0)
380                                 return;
381                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
382                         if (status->status < 0)
383                                 return;
384
385                         APP_CHECK(parse_range(tokens[ti], &port_low,
386                                 &port_high) == 0, status, "unrecognized "
387                                 "input \"%s\", expect \"port_from:"
388                                 "port_to\"", tokens[ti]);
389                         if (status->status < 0)
390                                 return;
391
392                         rule_ipv4->field[4].value.u16 = port_low;
393                         rule_ipv4->field[4].mask_range.u16 = port_high;
394
395                         dport_p = 1;
396                         continue;
397                 }
398
399                 /* unrecognizeable input */
400                 APP_CHECK(0, status, "unrecognized input \"%s\"",
401                         tokens[ti]);
402                 return;
403         }
404
405         /* check if argument(s) are missing */
406         APP_CHECK(esp_p == 1, status, "missing argument \"esp\"");
407         if (status->status < 0)
408                 return;
409
410         APP_CHECK(protect_p | bypass_p | discard_p, status, "missing "
411                 "argument \"protect\", \"bypass\", or \"discard\"");
412         if (status->status < 0)
413                 return;
414
415         *ri = *ri + 1;
416 }
417
418 static void
419 print_one_ip4_rule(const struct acl4_rules *rule, int32_t extra)
420 {
421         uint8_t a, b, c, d;
422
423         uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
424                         &a, &b, &c, &d);
425         printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
426                         rule->field[SRC_FIELD_IPV4].mask_range.u32);
427         uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
428                         &a, &b, &c, &d);
429         printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
430                         rule->field[DST_FIELD_IPV4].mask_range.u32);
431         printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
432                 rule->field[SRCP_FIELD_IPV4].value.u16,
433                 rule->field[SRCP_FIELD_IPV4].mask_range.u16,
434                 rule->field[DSTP_FIELD_IPV4].value.u16,
435                 rule->field[DSTP_FIELD_IPV4].mask_range.u16,
436                 rule->field[PROTO_FIELD_IPV4].value.u8,
437                 rule->field[PROTO_FIELD_IPV4].mask_range.u8);
438         if (extra)
439                 printf("0x%x-0x%x-0x%x ",
440                         rule->data.category_mask,
441                         rule->data.priority,
442                         rule->data.userdata);
443 }
444
445 static inline void
446 dump_ip4_rules(const struct acl4_rules *rule, int32_t num, int32_t extra)
447 {
448         int32_t i;
449
450         for (i = 0; i < num; i++, rule++) {
451                 printf("\t%d:", i + 1);
452                 print_one_ip4_rule(rule, extra);
453                 printf("\n");
454         }
455 }
456
457 static struct rte_acl_ctx *
458 acl4_init(const char *name, int32_t socketid, const struct acl4_rules *rules,
459                 uint32_t rules_nb)
460 {
461         char s[PATH_MAX];
462         struct rte_acl_param acl_param;
463         struct rte_acl_config acl_build_param;
464         struct rte_acl_ctx *ctx;
465
466         printf("Creating SP context with %u max rules\n", MAX_ACL_RULE_NUM);
467
468         memset(&acl_param, 0, sizeof(acl_param));
469
470         /* Create ACL contexts */
471         snprintf(s, sizeof(s), "%s_%d", name, socketid);
472
473         printf("IPv4 %s entries [%u]:\n", s, rules_nb);
474         dump_ip4_rules(rules, rules_nb, 1);
475
476         acl_param.name = s;
477         acl_param.socket_id = socketid;
478         acl_param.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ip4_defs));
479         acl_param.max_rule_num = MAX_ACL_RULE_NUM;
480
481         ctx = rte_acl_create(&acl_param);
482         if (ctx == NULL)
483                 rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
484
485         if (rte_acl_add_rules(ctx, (const struct rte_acl_rule *)rules,
486                                 rules_nb) < 0)
487                 rte_exit(EXIT_FAILURE, "add rules failed\n");
488
489         /* Perform builds */
490         memset(&acl_build_param, 0, sizeof(acl_build_param));
491
492         acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
493         acl_build_param.num_fields = RTE_DIM(ip4_defs);
494         memcpy(&acl_build_param.defs, ip4_defs, sizeof(ip4_defs));
495
496         if (rte_acl_build(ctx, &acl_build_param) != 0)
497                 rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
498
499         rte_acl_dump(ctx);
500
501         return ctx;
502 }
503
504 void
505 sp4_init(struct socket_ctx *ctx, int32_t socket_id)
506 {
507         const char *name;
508
509         if (ctx == NULL)
510                 rte_exit(EXIT_FAILURE, "NULL context.\n");
511
512         if (ctx->sp_ip4_in != NULL)
513                 rte_exit(EXIT_FAILURE, "Inbound SP DB for socket %u already "
514                                 "initialized\n", socket_id);
515
516         if (ctx->sp_ip4_out != NULL)
517                 rte_exit(EXIT_FAILURE, "Outbound SP DB for socket %u already "
518                                 "initialized\n", socket_id);
519
520         if (nb_acl4_rules_in > 0) {
521                 name = "sp_ip4_in";
522                 ctx->sp_ip4_in = (struct sp_ctx *)acl4_init(name,
523                         socket_id, acl4_rules_in, nb_acl4_rules_in);
524         } else
525                 RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
526                         "specified\n");
527
528         if (nb_acl4_rules_out > 0) {
529                 name = "sp_ip4_out";
530                 ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
531                         socket_id, acl4_rules_out, nb_acl4_rules_out);
532         } else
533                 RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
534                         "specified\n");
535 }