xref: /f-stack/dpdk/examples/ipsec-secgw/sp4.c (revision 2bfe3f2e)
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 }
536