xref: /dpdk/examples/ipsec-secgw/sp4.c (revision 089e5ed727a15da2729cfee9b63533dd120bd04c)
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 }
595