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/ip6.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 enum { 21 IP6_PROTO, 22 IP6_SRC0, 23 IP6_SRC1, 24 IP6_SRC2, 25 IP6_SRC3, 26 IP6_DST0, 27 IP6_DST1, 28 IP6_DST2, 29 IP6_DST3, 30 IP6_SRCP, 31 IP6_DSTP, 32 IP6_NUM 33 }; 34 35 #define IP6_ADDR_SIZE 16 36 37 static struct rte_acl_field_def ip6_defs[IP6_NUM] = { 38 { 39 .type = RTE_ACL_FIELD_TYPE_BITMASK, 40 .size = sizeof(uint8_t), 41 .field_index = IP6_PROTO, 42 .input_index = IP6_PROTO, 43 .offset = 0, 44 }, 45 { 46 .type = RTE_ACL_FIELD_TYPE_MASK, 47 .size = 4, 48 .field_index = IP6_SRC0, 49 .input_index = IP6_SRC0, 50 .offset = 2 51 }, 52 { 53 .type = RTE_ACL_FIELD_TYPE_MASK, 54 .size = 4, 55 .field_index = IP6_SRC1, 56 .input_index = IP6_SRC1, 57 .offset = 6 58 }, 59 { 60 .type = RTE_ACL_FIELD_TYPE_MASK, 61 .size = 4, 62 .field_index = IP6_SRC2, 63 .input_index = IP6_SRC2, 64 .offset = 10 65 }, 66 { 67 .type = RTE_ACL_FIELD_TYPE_MASK, 68 .size = 4, 69 .field_index = IP6_SRC3, 70 .input_index = IP6_SRC3, 71 .offset = 14 72 }, 73 { 74 .type = RTE_ACL_FIELD_TYPE_MASK, 75 .size = 4, 76 .field_index = IP6_DST0, 77 .input_index = IP6_DST0, 78 .offset = 18 79 }, 80 { 81 .type = RTE_ACL_FIELD_TYPE_MASK, 82 .size = 4, 83 .field_index = IP6_DST1, 84 .input_index = IP6_DST1, 85 .offset = 22 86 }, 87 { 88 .type = RTE_ACL_FIELD_TYPE_MASK, 89 .size = 4, 90 .field_index = IP6_DST2, 91 .input_index = IP6_DST2, 92 .offset = 26 93 }, 94 { 95 .type = RTE_ACL_FIELD_TYPE_MASK, 96 .size = 4, 97 .field_index = IP6_DST3, 98 .input_index = IP6_DST3, 99 .offset = 30 100 }, 101 { 102 .type = RTE_ACL_FIELD_TYPE_RANGE, 103 .size = sizeof(uint16_t), 104 .field_index = IP6_SRCP, 105 .input_index = IP6_SRCP, 106 .offset = 34 107 }, 108 { 109 .type = RTE_ACL_FIELD_TYPE_RANGE, 110 .size = sizeof(uint16_t), 111 .field_index = IP6_DSTP, 112 .input_index = IP6_SRCP, 113 .offset = 36 114 } 115 }; 116 117 RTE_ACL_RULE_DEF(acl6_rules, RTE_DIM(ip6_defs)); 118 119 static struct acl6_rules acl6_rules_out[MAX_ACL_RULE_NUM]; 120 static uint32_t nb_acl6_rules_out; 121 122 static struct acl6_rules acl6_rules_in[MAX_ACL_RULE_NUM]; 123 static uint32_t nb_acl6_rules_in; 124 125 void 126 parse_sp6_tokens(char **tokens, uint32_t n_tokens, 127 struct parse_status *status) 128 { 129 struct acl6_rules *rule_ipv6 = NULL; 130 131 uint32_t *ri = NULL; /* rule index */ 132 uint32_t ti = 0; /* token index */ 133 uint32_t tv; 134 135 uint32_t esp_p = 0; 136 uint32_t protect_p = 0; 137 uint32_t bypass_p = 0; 138 uint32_t discard_p = 0; 139 uint32_t pri_p = 0; 140 uint32_t src_p = 0; 141 uint32_t dst_p = 0; 142 uint32_t proto_p = 0; 143 uint32_t sport_p = 0; 144 uint32_t dport_p = 0; 145 146 if (strcmp(tokens[1], "in") == 0) { 147 ri = &nb_acl6_rules_in; 148 149 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status, "too " 150 "many sp rules, abort insertion\n"); 151 if (status->status < 0) 152 return; 153 154 rule_ipv6 = &acl6_rules_in[*ri]; 155 156 } else if (strcmp(tokens[1], "out") == 0) { 157 ri = &nb_acl6_rules_out; 158 159 APP_CHECK(*ri <= MAX_ACL_RULE_NUM - 1, status, "too " 160 "many sp rules, abort insertion\n"); 161 if (status->status < 0) 162 return; 163 164 rule_ipv6 = &acl6_rules_out[*ri]; 165 166 } else { 167 APP_CHECK(0, status, "unrecognized input \"%s\", expect" 168 " \"in\" or \"out\"\n", tokens[ti]); 169 return; 170 } 171 172 rule_ipv6->data.category_mask = 1; 173 174 175 for (ti = 2; ti < n_tokens; ti++) { 176 if (strcmp(tokens[ti], "esp") == 0) { 177 /* currently do nothing */ 178 APP_CHECK_PRESENCE(esp_p, tokens[ti], status); 179 if (status->status < 0) 180 return; 181 esp_p = 1; 182 continue; 183 } 184 185 if (strcmp(tokens[ti], "protect") == 0) { 186 APP_CHECK_PRESENCE(protect_p, tokens[ti], status); 187 if (status->status < 0) 188 return; 189 APP_CHECK(bypass_p == 0, status, "conflict item " 190 "between \"%s\" and \"%s\"", tokens[ti], 191 "bypass"); 192 if (status->status < 0) 193 return; 194 APP_CHECK(discard_p == 0, status, "conflict item " 195 "between \"%s\" and \"%s\"", tokens[ti], 196 "discard"); 197 if (status->status < 0) 198 return; 199 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 200 if (status->status < 0) 201 return; 202 APP_CHECK_TOKEN_IS_NUM(tokens, ti, status); 203 if (status->status < 0) 204 return; 205 206 tv = atoi(tokens[ti]); 207 APP_CHECK(tv != DISCARD && tv != BYPASS, status, 208 "invalid SPI: %s", tokens[ti]); 209 if (status->status < 0) 210 return; 211 rule_ipv6->data.userdata = tv; 212 213 protect_p = 1; 214 continue; 215 } 216 217 if (strcmp(tokens[ti], "bypass") == 0) { 218 APP_CHECK_PRESENCE(bypass_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(discard_p == 0, status, "conflict item " 227 "between \"%s\" and \"%s\"", tokens[ti], 228 "discard"); 229 if (status->status < 0) 230 return; 231 232 rule_ipv6->data.userdata = BYPASS; 233 234 bypass_p = 1; 235 continue; 236 } 237 238 if (strcmp(tokens[ti], "discard") == 0) { 239 APP_CHECK_PRESENCE(discard_p, tokens[ti], status); 240 if (status->status < 0) 241 return; 242 APP_CHECK(protect_p == 0, status, "conflict item " 243 "between \"%s\" and \"%s\"", tokens[ti], 244 "protect"); 245 if (status->status < 0) 246 return; 247 APP_CHECK(bypass_p == 0, status, "conflict item " 248 "between \"%s\" and \"%s\"", tokens[ti], 249 "discard"); 250 if (status->status < 0) 251 return; 252 253 rule_ipv6->data.userdata = DISCARD; 254 255 discard_p = 1; 256 continue; 257 } 258 259 if (strcmp(tokens[ti], "pri") == 0) { 260 APP_CHECK_PRESENCE(pri_p, tokens[ti], status); 261 if (status->status < 0) 262 return; 263 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 264 if (status->status < 0) 265 return; 266 APP_CHECK_TOKEN_IS_NUM(tokens, ti, status); 267 if (status->status < 0) 268 return; 269 270 rule_ipv6->data.priority = atoi(tokens[ti]); 271 272 pri_p = 1; 273 continue; 274 } 275 276 if (strcmp(tokens[ti], "src") == 0) { 277 struct in6_addr ip; 278 uint32_t depth; 279 280 APP_CHECK_PRESENCE(src_p, tokens[ti], status); 281 if (status->status < 0) 282 return; 283 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 284 if (status->status < 0) 285 return; 286 287 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip, 288 &depth) == 0, status, "unrecognized " 289 "input \"%s\", expect valid ipv6 " 290 "addr", tokens[ti]); 291 if (status->status < 0) 292 return; 293 294 rule_ipv6->field[1].value.u32 = 295 (uint32_t)ip.s6_addr[0] << 24 | 296 (uint32_t)ip.s6_addr[1] << 16 | 297 (uint32_t)ip.s6_addr[2] << 8 | 298 (uint32_t)ip.s6_addr[3]; 299 rule_ipv6->field[1].mask_range.u32 = 300 (depth > 32) ? 32 : depth; 301 depth = (depth > 32) ? (depth - 32) : 0; 302 rule_ipv6->field[2].value.u32 = 303 (uint32_t)ip.s6_addr[4] << 24 | 304 (uint32_t)ip.s6_addr[5] << 16 | 305 (uint32_t)ip.s6_addr[6] << 8 | 306 (uint32_t)ip.s6_addr[7]; 307 rule_ipv6->field[2].mask_range.u32 = 308 (depth > 32) ? 32 : depth; 309 depth = (depth > 32) ? (depth - 32) : 0; 310 rule_ipv6->field[3].value.u32 = 311 (uint32_t)ip.s6_addr[8] << 24 | 312 (uint32_t)ip.s6_addr[9] << 16 | 313 (uint32_t)ip.s6_addr[10] << 8 | 314 (uint32_t)ip.s6_addr[11]; 315 rule_ipv6->field[3].mask_range.u32 = 316 (depth > 32) ? 32 : depth; 317 depth = (depth > 32) ? (depth - 32) : 0; 318 rule_ipv6->field[4].value.u32 = 319 (uint32_t)ip.s6_addr[12] << 24 | 320 (uint32_t)ip.s6_addr[13] << 16 | 321 (uint32_t)ip.s6_addr[14] << 8 | 322 (uint32_t)ip.s6_addr[15]; 323 rule_ipv6->field[4].mask_range.u32 = 324 (depth > 32) ? 32 : depth; 325 326 src_p = 1; 327 continue; 328 } 329 330 if (strcmp(tokens[ti], "dst") == 0) { 331 struct in6_addr ip; 332 uint32_t depth; 333 334 APP_CHECK_PRESENCE(dst_p, tokens[ti], status); 335 if (status->status < 0) 336 return; 337 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 338 if (status->status < 0) 339 return; 340 341 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip, 342 &depth) == 0, status, "unrecognized " 343 "input \"%s\", expect valid ipv6 " 344 "addr", tokens[ti]); 345 if (status->status < 0) 346 return; 347 348 rule_ipv6->field[5].value.u32 = 349 (uint32_t)ip.s6_addr[0] << 24 | 350 (uint32_t)ip.s6_addr[1] << 16 | 351 (uint32_t)ip.s6_addr[2] << 8 | 352 (uint32_t)ip.s6_addr[3]; 353 rule_ipv6->field[5].mask_range.u32 = 354 (depth > 32) ? 32 : depth; 355 depth = (depth > 32) ? (depth - 32) : 0; 356 rule_ipv6->field[6].value.u32 = 357 (uint32_t)ip.s6_addr[4] << 24 | 358 (uint32_t)ip.s6_addr[5] << 16 | 359 (uint32_t)ip.s6_addr[6] << 8 | 360 (uint32_t)ip.s6_addr[7]; 361 rule_ipv6->field[6].mask_range.u32 = 362 (depth > 32) ? 32 : depth; 363 depth = (depth > 32) ? (depth - 32) : 0; 364 rule_ipv6->field[7].value.u32 = 365 (uint32_t)ip.s6_addr[8] << 24 | 366 (uint32_t)ip.s6_addr[9] << 16 | 367 (uint32_t)ip.s6_addr[10] << 8 | 368 (uint32_t)ip.s6_addr[11]; 369 rule_ipv6->field[7].mask_range.u32 = 370 (depth > 32) ? 32 : depth; 371 depth = (depth > 32) ? (depth - 32) : 0; 372 rule_ipv6->field[8].value.u32 = 373 (uint32_t)ip.s6_addr[12] << 24 | 374 (uint32_t)ip.s6_addr[13] << 16 | 375 (uint32_t)ip.s6_addr[14] << 8 | 376 (uint32_t)ip.s6_addr[15]; 377 rule_ipv6->field[8].mask_range.u32 = 378 (depth > 32) ? 32 : depth; 379 380 dst_p = 1; 381 continue; 382 } 383 384 if (strcmp(tokens[ti], "proto") == 0) { 385 uint16_t low, high; 386 387 APP_CHECK_PRESENCE(proto_p, tokens[ti], status); 388 if (status->status < 0) 389 return; 390 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 391 if (status->status < 0) 392 return; 393 394 APP_CHECK(parse_range(tokens[ti], &low, &high) 395 == 0, status, "unrecognized input \"%s\"" 396 ", expect \"from:to\"", tokens[ti]); 397 if (status->status < 0) 398 return; 399 APP_CHECK(low <= 0xff, status, "proto low " 400 "over-limit"); 401 if (status->status < 0) 402 return; 403 APP_CHECK(high <= 0xff, status, "proto high " 404 "over-limit"); 405 if (status->status < 0) 406 return; 407 408 rule_ipv6->field[0].value.u8 = (uint8_t)low; 409 rule_ipv6->field[0].mask_range.u8 = (uint8_t)high; 410 411 proto_p = 1; 412 continue; 413 } 414 415 if (strcmp(tokens[ti], "sport") == 0) { 416 uint16_t port_low, port_high; 417 418 APP_CHECK_PRESENCE(sport_p, tokens[ti], status); 419 if (status->status < 0) 420 return; 421 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 422 if (status->status < 0) 423 return; 424 425 APP_CHECK(parse_range(tokens[ti], &port_low, 426 &port_high) == 0, status, "unrecognized " 427 "input \"%s\", expect \"port_from:" 428 "port_to\"", tokens[ti]); 429 if (status->status < 0) 430 return; 431 432 rule_ipv6->field[9].value.u16 = port_low; 433 rule_ipv6->field[9].mask_range.u16 = port_high; 434 435 sport_p = 1; 436 continue; 437 } 438 439 if (strcmp(tokens[ti], "dport") == 0) { 440 uint16_t port_low, port_high; 441 442 APP_CHECK_PRESENCE(dport_p, tokens[ti], status); 443 if (status->status < 0) 444 return; 445 INCREMENT_TOKEN_INDEX(ti, n_tokens, status); 446 if (status->status < 0) 447 return; 448 449 APP_CHECK(parse_range(tokens[ti], &port_low, 450 &port_high) == 0, status, "unrecognized " 451 "input \"%s\", expect \"port_from:" 452 "port_to\"", tokens[ti]); 453 if (status->status < 0) 454 return; 455 456 rule_ipv6->field[10].value.u16 = port_low; 457 rule_ipv6->field[10].mask_range.u16 = port_high; 458 459 dport_p = 1; 460 continue; 461 } 462 463 /* unrecognizeable input */ 464 APP_CHECK(0, status, "unrecognized input \"%s\"", 465 tokens[ti]); 466 return; 467 } 468 469 /* check if argument(s) are missing */ 470 APP_CHECK(esp_p == 1, status, "missing argument \"esp\""); 471 if (status->status < 0) 472 return; 473 474 APP_CHECK(protect_p | bypass_p | discard_p, status, "missing " 475 "argument \"protect\", \"bypass\", or \"discard\""); 476 if (status->status < 0) 477 return; 478 479 *ri = *ri + 1; 480 } 481 482 static inline void 483 print_one_ip6_rule(const struct acl6_rules *rule, int32_t extra) 484 { 485 uint8_t a, b, c, d; 486 487 uint32_t_to_char(rule->field[IP6_SRC0].value.u32, 488 &a, &b, &c, &d); 489 printf("%.2x%.2x:%.2x%.2x", a, b, c, d); 490 uint32_t_to_char(rule->field[IP6_SRC1].value.u32, 491 &a, &b, &c, &d); 492 printf(":%.2x%.2x:%.2x%.2x", a, b, c, d); 493 uint32_t_to_char(rule->field[IP6_SRC2].value.u32, 494 &a, &b, &c, &d); 495 printf(":%.2x%.2x:%.2x%.2x", a, b, c, d); 496 uint32_t_to_char(rule->field[IP6_SRC3].value.u32, 497 &a, &b, &c, &d); 498 printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d, 499 rule->field[IP6_SRC0].mask_range.u32 500 + rule->field[IP6_SRC1].mask_range.u32 501 + rule->field[IP6_SRC2].mask_range.u32 502 + rule->field[IP6_SRC3].mask_range.u32); 503 504 uint32_t_to_char(rule->field[IP6_DST0].value.u32, 505 &a, &b, &c, &d); 506 printf("%.2x%.2x:%.2x%.2x", a, b, c, d); 507 uint32_t_to_char(rule->field[IP6_DST1].value.u32, 508 &a, &b, &c, &d); 509 printf(":%.2x%.2x:%.2x%.2x", a, b, c, d); 510 uint32_t_to_char(rule->field[IP6_DST2].value.u32, 511 &a, &b, &c, &d); 512 printf(":%.2x%.2x:%.2x%.2x", a, b, c, d); 513 uint32_t_to_char(rule->field[IP6_DST3].value.u32, 514 &a, &b, &c, &d); 515 printf(":%.2x%.2x:%.2x%.2x/%u ", a, b, c, d, 516 rule->field[IP6_DST0].mask_range.u32 517 + rule->field[IP6_DST1].mask_range.u32 518 + rule->field[IP6_DST2].mask_range.u32 519 + rule->field[IP6_DST3].mask_range.u32); 520 521 printf("%hu : %hu %hu : %hu 0x%hhx/0x%hhx ", 522 rule->field[IP6_SRCP].value.u16, 523 rule->field[IP6_SRCP].mask_range.u16, 524 rule->field[IP6_DSTP].value.u16, 525 rule->field[IP6_DSTP].mask_range.u16, 526 rule->field[IP6_PROTO].value.u8, 527 rule->field[IP6_PROTO].mask_range.u8); 528 if (extra) 529 printf("0x%x-0x%x-0x%x ", 530 rule->data.category_mask, 531 rule->data.priority, 532 rule->data.userdata); 533 } 534 535 static inline void 536 dump_ip6_rules(const struct acl6_rules *rule, int32_t num, int32_t extra) 537 { 538 int32_t i; 539 540 for (i = 0; i < num; i++, rule++) { 541 printf("\t%d:", i + 1); 542 print_one_ip6_rule(rule, extra); 543 printf("\n"); 544 } 545 } 546 547 static struct rte_acl_ctx * 548 acl6_init(const char *name, int32_t socketid, const struct acl6_rules *rules, 549 uint32_t rules_nb) 550 { 551 char s[PATH_MAX]; 552 struct rte_acl_param acl_param; 553 struct rte_acl_config acl_build_param; 554 struct rte_acl_ctx *ctx; 555 556 printf("Creating SP context with %u max rules\n", MAX_ACL_RULE_NUM); 557 558 memset(&acl_param, 0, sizeof(acl_param)); 559 560 /* Create ACL contexts */ 561 snprintf(s, sizeof(s), "%s_%d", name, socketid); 562 563 printf("IPv4 %s entries [%u]:\n", s, rules_nb); 564 dump_ip6_rules(rules, rules_nb, 1); 565 566 acl_param.name = s; 567 acl_param.socket_id = socketid; 568 acl_param.rule_size = RTE_ACL_RULE_SZ(RTE_DIM(ip6_defs)); 569 acl_param.max_rule_num = MAX_ACL_RULE_NUM; 570 571 ctx = rte_acl_create(&acl_param); 572 if (ctx == NULL) 573 rte_exit(EXIT_FAILURE, "Failed to create ACL context\n"); 574 575 if (rte_acl_add_rules(ctx, (const struct rte_acl_rule *)rules, 576 rules_nb) < 0) 577 rte_exit(EXIT_FAILURE, "add rules failed\n"); 578 579 /* Perform builds */ 580 memset(&acl_build_param, 0, sizeof(acl_build_param)); 581 582 acl_build_param.num_categories = DEFAULT_MAX_CATEGORIES; 583 acl_build_param.num_fields = RTE_DIM(ip6_defs); 584 memcpy(&acl_build_param.defs, ip6_defs, sizeof(ip6_defs)); 585 586 if (rte_acl_build(ctx, &acl_build_param) != 0) 587 rte_exit(EXIT_FAILURE, "Failed to build ACL trie\n"); 588 589 rte_acl_dump(ctx); 590 591 return ctx; 592 } 593 594 /* 595 * check that for each rule it's SPI has a correspondent entry in SAD 596 */ 597 static int 598 check_spi_value(int inbound) 599 { 600 uint32_t i, num, spi; 601 const struct acl6_rules *acr; 602 603 if (inbound != 0) { 604 acr = acl6_rules_in; 605 num = nb_acl6_rules_in; 606 } else { 607 acr = acl6_rules_out; 608 num = nb_acl6_rules_out; 609 } 610 611 for (i = 0; i != num; i++) { 612 spi = acr[i].data.userdata; 613 if (spi != DISCARD && spi != BYPASS && 614 sa_spi_present(spi, inbound) < 0) { 615 RTE_LOG(ERR, IPSEC, "SPI %u is not present in SAD\n", 616 spi); 617 return -ENOENT; 618 } 619 } 620 621 return 0; 622 } 623 624 void 625 sp6_init(struct socket_ctx *ctx, int32_t socket_id) 626 { 627 const char *name; 628 629 if (ctx == NULL) 630 rte_exit(EXIT_FAILURE, "NULL context.\n"); 631 632 if (ctx->sp_ip6_in != NULL) 633 rte_exit(EXIT_FAILURE, "Inbound IPv6 SP DB for socket %u " 634 "already initialized\n", socket_id); 635 636 if (ctx->sp_ip6_out != NULL) 637 rte_exit(EXIT_FAILURE, "Outbound IPv6 SP DB for socket %u " 638 "already initialized\n", socket_id); 639 640 if (check_spi_value(1) < 0) 641 rte_exit(EXIT_FAILURE, 642 "Inbound IPv6 SP DB has unmatched in SAD SPIs\n"); 643 644 if (check_spi_value(0) < 0) 645 rte_exit(EXIT_FAILURE, 646 "Outbound IPv6 SP DB has unmatched in SAD SPIs\n"); 647 648 if (nb_acl6_rules_in > 0) { 649 name = "sp_ip6_in"; 650 ctx->sp_ip6_in = (struct sp_ctx *)acl6_init(name, 651 socket_id, acl6_rules_in, nb_acl6_rules_in); 652 } else 653 RTE_LOG(WARNING, IPSEC, "No IPv6 SP Inbound rule " 654 "specified\n"); 655 656 if (nb_acl6_rules_out > 0) { 657 name = "sp_ip6_out"; 658 ctx->sp_ip6_out = (struct sp_ctx *)acl6_init(name, 659 socket_id, acl6_rules_out, nb_acl6_rules_out); 660 } else 661 RTE_LOG(WARNING, IPSEC, "No IPv6 SP Outbound rule " 662 "specified\n"); 663 } 664 665 /* 666 * Search though SP rules for given SPI. 667 */ 668 int 669 sp6_spi_present(uint32_t spi, int inbound) 670 { 671 uint32_t i, num; 672 const struct acl6_rules *acr; 673 674 if (inbound != 0) { 675 acr = acl6_rules_in; 676 num = nb_acl6_rules_in; 677 } else { 678 acr = acl6_rules_out; 679 num = nb_acl6_rules_out; 680 } 681 682 for (i = 0; i != num; i++) { 683 if (acr[i].data.userdata == spi) 684 return i; 685 } 686 687 return -ENOENT; 688 } 689