xref: /dpdk/examples/ipsec-secgw/sp4.c (revision 200bc52e5aa0d72e70464c9cd22b55cf536ed13c)
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 static 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 static struct acl4_rules acl4_rules_out[MAX_ACL_RULE_NUM];
89 static uint32_t nb_acl4_rules_out;
90 
91 static struct acl4_rules acl4_rules_in[MAX_ACL_RULE_NUM];
92 static 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 	uint32_t tv;
103 
104 	uint32_t esp_p = 0;
105 	uint32_t protect_p = 0;
106 	uint32_t bypass_p = 0;
107 	uint32_t discard_p = 0;
108 	uint32_t pri_p = 0;
109 	uint32_t src_p = 0;
110 	uint32_t dst_p = 0;
111 	uint32_t proto_p = 0;
112 	uint32_t sport_p = 0;
113 	uint32_t dport_p = 0;
114 
115 	if (strcmp(tokens[1], "in") == 0) {
116 		ri = &nb_acl4_rules_in;
117 
118 		APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
119 			"too many sp rules, abort insertion\n");
120 		if (status->status < 0)
121 			return;
122 
123 		rule_ipv4 = &acl4_rules_in[*ri];
124 
125 	} else if (strcmp(tokens[1], "out") == 0) {
126 		ri = &nb_acl4_rules_out;
127 
128 		APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status,
129 			"too many sp rules, abort insertion\n");
130 		if (status->status < 0)
131 			return;
132 
133 		rule_ipv4 = &acl4_rules_out[*ri];
134 	} else {
135 		APP_CHECK(0, status, "unrecognized input \"%s\", expect"
136 			" \"in\" or \"out\"\n", tokens[ti]);
137 		return;
138 	}
139 
140 	rule_ipv4->data.category_mask = 1;
141 
142 	for (ti = 2; ti < n_tokens; ti++) {
143 		if (strcmp(tokens[ti], "esp") == 0) {
144 			/* currently do nothing */
145 			APP_CHECK_PRESENCE(esp_p, tokens[ti], status);
146 			if (status->status < 0)
147 				return;
148 			esp_p = 1;
149 			continue;
150 		}
151 
152 		if (strcmp(tokens[ti], "protect") == 0) {
153 			APP_CHECK_PRESENCE(protect_p, tokens[ti], status);
154 			if (status->status < 0)
155 				return;
156 			APP_CHECK(bypass_p == 0, status, "conflict item "
157 				"between \"%s\" and \"%s\"", tokens[ti],
158 				"bypass");
159 			if (status->status < 0)
160 				return;
161 			APP_CHECK(discard_p == 0, status, "conflict item "
162 				"between \"%s\" and \"%s\"", tokens[ti],
163 				"discard");
164 			if (status->status < 0)
165 				return;
166 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
167 			if (status->status < 0)
168 				return;
169 			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
170 			if (status->status < 0)
171 				return;
172 
173 			tv = atoi(tokens[ti]);
174 			APP_CHECK(tv != DISCARD && tv != BYPASS, status,
175 				"invalid SPI: %s", tokens[ti]);
176 			if (status->status < 0)
177 				return;
178 			rule_ipv4->data.userdata = tv;
179 
180 			protect_p = 1;
181 			continue;
182 		}
183 
184 		if (strcmp(tokens[ti], "bypass") == 0) {
185 			APP_CHECK_PRESENCE(bypass_p, tokens[ti], status);
186 			if (status->status < 0)
187 				return;
188 			APP_CHECK(protect_p == 0, status, "conflict item "
189 				"between \"%s\" and \"%s\"", tokens[ti],
190 				"protect");
191 			if (status->status < 0)
192 				return;
193 			APP_CHECK(discard_p == 0, status, "conflict item "
194 				"between \"%s\" and \"%s\"", tokens[ti],
195 				"discard");
196 			if (status->status < 0)
197 				return;
198 
199 			rule_ipv4->data.userdata = BYPASS;
200 
201 			bypass_p = 1;
202 			continue;
203 		}
204 
205 		if (strcmp(tokens[ti], "discard") == 0) {
206 			APP_CHECK_PRESENCE(discard_p, tokens[ti], status);
207 			if (status->status < 0)
208 				return;
209 			APP_CHECK(protect_p == 0, status, "conflict item "
210 				"between \"%s\" and \"%s\"", tokens[ti],
211 				"protect");
212 			if (status->status < 0)
213 				return;
214 			APP_CHECK(bypass_p == 0, status, "conflict item "
215 				"between \"%s\" and \"%s\"", tokens[ti],
216 				"discard");
217 			if (status->status < 0)
218 				return;
219 
220 			rule_ipv4->data.userdata = DISCARD;
221 
222 			discard_p = 1;
223 			continue;
224 		}
225 
226 		if (strcmp(tokens[ti], "pri") == 0) {
227 			APP_CHECK_PRESENCE(pri_p, tokens[ti], status);
228 			if (status->status < 0)
229 				return;
230 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
231 			if (status->status < 0)
232 				return;
233 			APP_CHECK_TOKEN_IS_NUM(tokens, ti, status);
234 			if (status->status < 0)
235 				return;
236 
237 			rule_ipv4->data.priority = atoi(tokens[ti]);
238 
239 			pri_p = 1;
240 			continue;
241 		}
242 
243 		if (strcmp(tokens[ti], "src") == 0) {
244 			struct in_addr ip;
245 			uint32_t depth;
246 
247 			APP_CHECK_PRESENCE(src_p, tokens[ti], status);
248 			if (status->status < 0)
249 				return;
250 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
251 			if (status->status < 0)
252 				return;
253 
254 			APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
255 				&depth) == 0, status, "unrecognized "
256 				"input \"%s\", expect valid ipv4 addr",
257 				tokens[ti]);
258 			if (status->status < 0)
259 				return;
260 
261 			rule_ipv4->field[1].value.u32 =
262 				rte_bswap32(ip.s_addr);
263 			rule_ipv4->field[1].mask_range.u32 =
264 				depth;
265 
266 			src_p = 1;
267 			continue;
268 		}
269 
270 		if (strcmp(tokens[ti], "dst") == 0) {
271 			struct in_addr ip;
272 			uint32_t depth;
273 
274 			APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
275 			if (status->status < 0)
276 				return;
277 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
278 			if (status->status < 0)
279 				return;
280 			APP_CHECK(parse_ipv4_addr(tokens[ti], &ip,
281 				&depth) == 0, status, "unrecognized "
282 				"input \"%s\", expect valid ipv4 addr",
283 				tokens[ti]);
284 			if (status->status < 0)
285 				return;
286 
287 			rule_ipv4->field[2].value.u32 =
288 				rte_bswap32(ip.s_addr);
289 			rule_ipv4->field[2].mask_range.u32 =
290 				depth;
291 
292 			dst_p = 1;
293 			continue;
294 		}
295 
296 		if (strcmp(tokens[ti], "proto") == 0) {
297 			uint16_t low, high;
298 
299 			APP_CHECK_PRESENCE(proto_p, tokens[ti], status);
300 			if (status->status < 0)
301 				return;
302 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
303 			if (status->status < 0)
304 				return;
305 
306 			APP_CHECK(parse_range(tokens[ti], &low, &high)
307 				== 0, status, "unrecognized input \"%s\""
308 				", expect \"from:to\"", tokens[ti]);
309 			if (status->status < 0)
310 				return;
311 			APP_CHECK(low <= 0xff, status, "proto low "
312 				"over-limit");
313 			if (status->status < 0)
314 				return;
315 			APP_CHECK(high <= 0xff, status, "proto high "
316 				"over-limit");
317 			if (status->status < 0)
318 				return;
319 
320 			rule_ipv4->field[0].value.u8 = (uint8_t)low;
321 			rule_ipv4->field[0].mask_range.u8 = (uint8_t)high;
322 
323 			proto_p = 1;
324 			continue;
325 		}
326 
327 		if (strcmp(tokens[ti], "sport") == 0) {
328 			uint16_t port_low, port_high;
329 
330 			APP_CHECK_PRESENCE(sport_p, tokens[ti], status);
331 			if (status->status < 0)
332 				return;
333 			INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
334 			if (status->status < 0)
335 				return;
336 
337 			APP_CHECK(parse_range(tokens[ti], &port_low,
338 				&port_high) == 0, status, "unrecognized "
339 				"input \"%s\", expect \"port_from:"
340 				"port_to\"", tokens[ti]);
341 			if (status->status < 0)
342 				return;
343 
344 			rule_ipv4->field[3].value.u16 = port_low;
345 			rule_ipv4->field[3].mask_range.u16 = port_high;
346 
347 			sport_p = 1;
348 			continue;
349 		}
350 
351 		if (strcmp(tokens[ti], "dport") == 0) {
352 			uint16_t port_low, port_high;
353 
354 			APP_CHECK_PRESENCE(dport_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[4].value.u16 = port_low;
369 			rule_ipv4->field[4].mask_range.u16 = port_high;
370 
371 			dport_p = 1;
372 			continue;
373 		}
374 
375 		/* unrecognizeable input */
376 		APP_CHECK(0, status, "unrecognized input \"%s\"",
377 			tokens[ti]);
378 		return;
379 	}
380 
381 	/* check if argument(s) are missing */
382 	APP_CHECK(esp_p == 1, status, "missing argument \"esp\"");
383 	if (status->status < 0)
384 		return;
385 
386 	APP_CHECK(protect_p | bypass_p | discard_p, status, "missing "
387 		"argument \"protect\", \"bypass\", or \"discard\"");
388 	if (status->status < 0)
389 		return;
390 
391 	*ri = *ri + 1;
392 }
393 
394 static void
395 print_one_ip4_rule(const struct acl4_rules *rule, int32_t extra)
396 {
397 	uint8_t a, b, c, d;
398 
399 	uint32_t_to_char(rule->field[SRC_FIELD_IPV4].value.u32,
400 			&a, &b, &c, &d);
401 	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
402 			rule->field[SRC_FIELD_IPV4].mask_range.u32);
403 	uint32_t_to_char(rule->field[DST_FIELD_IPV4].value.u32,
404 			&a, &b, &c, &d);
405 	printf("%hhu.%hhu.%hhu.%hhu/%u ", a, b, c, d,
406 			rule->field[DST_FIELD_IPV4].mask_range.u32);
407 	printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ",
408 		rule->field[SRCP_FIELD_IPV4].value.u16,
409 		rule->field[SRCP_FIELD_IPV4].mask_range.u16,
410 		rule->field[DSTP_FIELD_IPV4].value.u16,
411 		rule->field[DSTP_FIELD_IPV4].mask_range.u16,
412 		rule->field[PROTO_FIELD_IPV4].value.u8,
413 		rule->field[PROTO_FIELD_IPV4].mask_range.u8);
414 	if (extra)
415 		printf("0x%x-0x%x-0x%x ",
416 			rule->data.category_mask,
417 			rule->data.priority,
418 			rule->data.userdata);
419 }
420 
421 static inline void
422 dump_ip4_rules(const struct acl4_rules *rule, int32_t num, int32_t extra)
423 {
424 	int32_t i;
425 
426 	for (i = 0; i < num; i++, rule++) {
427 		printf("\t%d:", i + 1);
428 		print_one_ip4_rule(rule, extra);
429 		printf("\n");
430 	}
431 }
432 
433 static struct rte_acl_ctx *
434 acl4_init(const char *name, int32_t socketid, const struct acl4_rules *rules,
435 		uint32_t rules_nb)
436 {
437 	char s[PATH_MAX];
438 	struct rte_acl_param acl_param;
439 	struct rte_acl_config acl_build_param;
440 	struct rte_acl_ctx *ctx;
441 
442 	printf("Creating SP context with %u max rules\n", MAX_ACL_RULE_NUM);
443 
444 	memset(&acl_param, 0, sizeof(acl_param));
445 
446 	/* Create ACL contexts */
447 	snprintf(s, sizeof(s), "%s_%d", name, socketid);
448 
449 	printf("IPv4 %s entries [%u]:\n", s, rules_nb);
450 	dump_ip4_rules(rules, rules_nb, 1);
451 
452 	acl_param.name = s;
453 	acl_param.socket_id = socketid;
454 	acl_param.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ip4_defs));
455 	acl_param.max_rule_num = MAX_ACL_RULE_NUM;
456 
457 	ctx = rte_acl_create(&acl_param);
458 	if (ctx == NULL)
459 		rte_exit(EXIT_FAILURE, "Failed to create ACL context\n");
460 
461 	if (rte_acl_add_rules(ctx, (const struct rte_acl_rule *)rules,
462 				rules_nb) < 0)
463 		rte_exit(EXIT_FAILURE, "add rules failed\n");
464 
465 	/* Perform builds */
466 	memset(&acl_build_param, 0, sizeof(acl_build_param));
467 
468 	acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES;
469 	acl_build_param.num_fields = RTE_DIM(ip4_defs);
470 	memcpy(&acl_build_param.defs, ip4_defs, sizeof(ip4_defs));
471 
472 	if (rte_acl_build(ctx, &acl_build_param) != 0)
473 		rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n");
474 
475 	rte_acl_dump(ctx);
476 
477 	return ctx;
478 }
479 
480 /*
481  * check that for each rule it's SPI has a correspondent entry in SAD
482  */
483 static int
484 check_spi_value(int inbound)
485 {
486 	uint32_t i, num, spi;
487 	const struct acl4_rules *acr;
488 
489 	if (inbound != 0) {
490 		acr = acl4_rules_in;
491 		num = nb_acl4_rules_in;
492 	} else {
493 		acr = acl4_rules_out;
494 		num = nb_acl4_rules_out;
495 	}
496 
497 	for (i = 0; i != num; i++) {
498 		spi = acr[i].data.userdata;
499 		if (spi != DISCARD && spi != BYPASS &&
500 				sa_spi_present(spi, inbound) < 0) {
501 			RTE_LOG(ERR, IPSEC, "SPI %u is not present in SAD\n",
502 				spi);
503 			return -ENOENT;
504 		}
505 	}
506 
507 	return 0;
508 }
509 
510 void
511 sp4_init(struct socket_ctx *ctx, int32_t socket_id)
512 {
513 	const char *name;
514 
515 	if (ctx == NULL)
516 		rte_exit(EXIT_FAILURE, "NULL context.\n");
517 
518 	if (ctx->sp_ip4_in != NULL)
519 		rte_exit(EXIT_FAILURE, "Inbound SP DB for socket %u already "
520 				"initialized\n", socket_id);
521 
522 	if (ctx->sp_ip4_out != NULL)
523 		rte_exit(EXIT_FAILURE, "Outbound SP DB for socket %u already "
524 				"initialized\n", socket_id);
525 
526 	if (check_spi_value(1) < 0)
527 		rte_exit(EXIT_FAILURE,
528 			"Inbound IPv4 SP DB has unmatched in SAD SPIs\n");
529 
530 	if (check_spi_value(0) < 0)
531 		rte_exit(EXIT_FAILURE,
532 			"Outbound IPv4 SP DB has unmatched in SAD SPIs\n");
533 
534 	if (nb_acl4_rules_in > 0) {
535 		name = "sp_ip4_in";
536 		ctx->sp_ip4_in = (struct sp_ctx *)acl4_init(name,
537 			socket_id, acl4_rules_in, nb_acl4_rules_in);
538 	} else
539 		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Inbound rule "
540 			"specified\n");
541 
542 	if (nb_acl4_rules_out > 0) {
543 		name = "sp_ip4_out";
544 		ctx->sp_ip4_out = (struct sp_ctx *)acl4_init(name,
545 			socket_id, acl4_rules_out, nb_acl4_rules_out);
546 	} else
547 		RTE_LOG(WARNING, IPSEC, "No IPv4 SP Outbound rule "
548 			"specified\n");
549 }
550 
551 /*
552  * Search though SP rules for given SPI.
553  */
554 int
555 sp4_spi_present(uint32_t spi, int inbound)
556 {
557 	uint32_t i, num;
558 	const struct acl4_rules *acr;
559 
560 	if (inbound != 0) {
561 		acr = acl4_rules_in;
562 		num = nb_acl4_rules_in;
563 	} else {
564 		acr = acl4_rules_out;
565 		num = nb_acl4_rules_out;
566 	}
567 
568 	for (i = 0; i != num; i++) {
569 		if (acr[i].data.userdata == spi)
570 			return i;
571 	}
572 
573 	return -ENOENT;
574 }
575