1 /*- 2 * Copyright (c) 2009-2025 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /* 28 * npfctl(8) data manipulation and helper routines. 29 */ 30 31 #include <sys/cdefs.h> 32 __RCSID("$NetBSD: npf_data.c,v 1.34 2025/07/01 19:55:15 joe Exp $"); 33 34 #include <stdlib.h> 35 #include <stddef.h> 36 37 #include <sys/types.h> 38 #include <netinet/in.h> 39 #include <netinet/in_systm.h> 40 #include <netinet/ip.h> 41 #define ICMP_STRINGS 42 #include <netinet/ip_icmp.h> 43 #define ICMP6_STRINGS 44 #include <netinet/icmp6.h> 45 #define __FAVOR_BSD 46 #include <netinet/tcp.h> 47 #include <net/if.h> 48 49 #include <string.h> 50 #include <ctype.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <ifaddrs.h> 54 #include <netdb.h> 55 #include <pwd.h> 56 #include <grp.h> 57 58 #include "npfctl.h" 59 60 static struct ifaddrs * ifs_list = NULL; 61 62 void 63 npfctl_note_interface(const char *ifname) 64 { 65 unsigned long if_idx = if_nametoindex(ifname); 66 bool testif = npfctl_debug_addif(ifname); 67 const char *p = ifname; 68 69 /* If such interface exists or if it is a test interface - done. */ 70 if (if_idx || testif) { 71 return; 72 } 73 74 /* 75 * Minimum sanity check. The interface name shall be non-empty 76 * string shorter than IFNAMSIZ and alphanumeric only. 77 */ 78 if (*p == '\0') { 79 goto err; 80 } 81 while (*p) { 82 const size_t len = (ptrdiff_t)p - (ptrdiff_t)ifname; 83 84 if (!isalnum((unsigned char)*p) || len > IFNAMSIZ) { 85 goto err; 86 } 87 p++; 88 } 89 90 /* Throw a warning, so that the user could double check. */ 91 warnx("warning - unknown interface '%s'", ifname); 92 return; 93 err: 94 yyerror("illegitimate interface name '%s'", ifname); 95 } 96 97 static unsigned long 98 npfctl_find_ifindex(const char *ifname) 99 { 100 unsigned long if_idx = if_nametoindex(ifname); 101 bool testif = npfctl_debug_addif(ifname); 102 103 if (!if_idx) { 104 if (testif) { 105 static u_int dummy_if_idx = (1 << 15); 106 return ++dummy_if_idx; 107 } 108 yyerror("unknown interface '%s'", ifname); 109 } 110 return if_idx; 111 } 112 113 static bool 114 npfctl_copy_address(sa_family_t fam, npf_addr_t *addr, const void *ptr) 115 { 116 memset(addr, 0, sizeof(npf_addr_t)); 117 118 switch (fam) { 119 case AF_INET: { 120 const struct sockaddr_in *sin = ptr; 121 memcpy(addr, &sin->sin_addr, sizeof(sin->sin_addr)); 122 return true; 123 } 124 case AF_INET6: { 125 const struct sockaddr_in6 *sin6 = ptr; 126 memcpy(addr, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); 127 return true; 128 } 129 default: 130 yyerror("unknown address family %u", fam); 131 return false; 132 } 133 } 134 135 /* 136 * npfctl_parse_fam_addr: parse a given a string and return the address 137 * family with the actual address as npf_addr_t. 138 * 139 * => Return true on success; false otherwise. 140 */ 141 static bool 142 npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr) 143 { 144 static const struct addrinfo hint = { 145 .ai_family = AF_UNSPEC, 146 .ai_flags = AI_NUMERICHOST 147 }; 148 struct addrinfo *ai; 149 int ret; 150 151 ret = getaddrinfo(name, NULL, &hint, &ai); 152 if (ret) { 153 yyerror("cannot parse '%s' (%s)", name, gai_strerror(ret)); 154 return false; 155 } 156 if (fam) { 157 *fam = ai->ai_family; 158 } 159 if (!npfctl_copy_address(*fam, addr, ai->ai_addr)) { 160 return false; 161 } 162 freeaddrinfo(ai); 163 return true; 164 } 165 166 /* 167 * npfctl_parse_mask: parse a given string which represents a mask and 168 * can either be in quad-dot or CIDR block notation; validates the mask 169 * given the family. 170 * 171 * => Returns true if mask is valid (or is NULL); false otherwise. 172 */ 173 static bool 174 npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask) 175 { 176 unsigned max_mask = NPF_MAX_NETMASK; 177 char *ep = NULL; 178 npf_addr_t addr; 179 uint8_t *ap; 180 181 assert(fam == AF_INET || fam == AF_INET6); 182 if (!s) { 183 /* No mask. */ 184 *mask = NPF_NO_NETMASK; 185 return true; 186 } 187 188 errno = 0; 189 *mask = (npf_netmask_t)strtol(s, &ep, 0); 190 if (*ep == '\0' && s != ep && errno != ERANGE) { 191 /* Just a number -- CIDR notation. */ 192 goto check; 193 } 194 195 /* Other characters: try to parse a full address. */ 196 if (!npfctl_parse_fam_addr(s, &fam, &addr)) { 197 return false; 198 } 199 200 /* Convert the address to CIDR block number. */ 201 ap = addr.word8 + (*mask / 8) - 1; 202 while (ap >= addr.word8) { 203 for (int j = 8; j > 0; j--) { 204 if (*ap & 1) 205 goto check; 206 *ap >>= 1; 207 (*mask)--; 208 if (*mask == 0) 209 goto check; 210 } 211 ap--; 212 } 213 *mask = NPF_NO_NETMASK; 214 return true; 215 check: 216 switch (fam) { 217 case AF_INET: 218 max_mask = 32; 219 break; 220 case AF_INET6: 221 max_mask = 128; 222 break; 223 } 224 return *mask <= max_mask; 225 } 226 227 /* 228 * npfctl_parse_fam_addr_mask: return address family, address and mask. 229 * 230 * => Mask is optional and can be NULL. 231 * => Returns true on success or false if unable to parse. 232 */ 233 npfvar_t * 234 npfctl_parse_fam_addr_mask(const char *addr, const char *mask, 235 unsigned long *nummask) 236 { 237 fam_addr_mask_t fam; 238 char buf[32]; 239 240 memset(&fam, 0, sizeof(fam)); 241 242 if (!npfctl_parse_fam_addr(addr, &fam.fam_family, &fam.fam_addr)) 243 return NULL; 244 245 /* 246 * Mask may be NULL. In such case, "no mask" value will be set. 247 */ 248 if (nummask) { 249 /* Let npfctl_parse_mask() validate the number. */ 250 snprintf(buf, sizeof(buf), "%lu", *nummask); 251 mask = buf; 252 } 253 if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) { 254 return NULL; 255 } 256 return npfvar_create_element(NPFVAR_FAM, &fam, sizeof(fam)); 257 } 258 259 npfvar_t * 260 npfctl_parse_table_id(const char *name) 261 { 262 u_int tid; 263 264 tid = npfctl_table_getid(name); 265 if (tid == (unsigned)-1) { 266 yyerror("table '%s' is not defined", name); 267 return NULL; 268 } 269 return npfvar_create_element(NPFVAR_TABLE, &tid, sizeof(u_int)); 270 } 271 272 int 273 npfctl_parse_user(const char *user, uint32_t *uid) 274 { 275 if (!strcmp(user, "unknown")) 276 *uid = UID_MAX; 277 else { 278 struct passwd *pw; 279 280 if ((pw = getpwnam(user)) == NULL) { 281 return -1; 282 } 283 *uid = pw->pw_uid; 284 } 285 return 0; 286 } 287 288 int 289 npfctl_parse_group(const char *group, uint32_t *gid) 290 { 291 if (!strcmp(group, "unknown")) 292 *gid = GID_MAX; 293 else { 294 struct group *grp; 295 296 if ((grp = getgrnam(group)) == NULL) { 297 return -1; 298 } 299 *gid = grp->gr_gid; 300 } 301 return 0; 302 } 303 304 /* 305 * this function is called for both gid and uid init in parser 306 * both uid and gid are both uint32_t 307 */ 308 void 309 npfctl_init_rid(rid_t *rid, uint32_t id1, uint32_t id2, uint8_t op) 310 { 311 rid->id[0] = id1; 312 rid->id[1] = id2; 313 rid->op = op; 314 } 315 316 /* 317 * npfctl_parse_port_range: create a port-range variable. Note that the 318 * passed port numbers should be in host byte order. 319 */ 320 npfvar_t * 321 npfctl_parse_port_range(in_port_t s, in_port_t e) 322 { 323 port_range_t pr; 324 325 pr.pr_start = htons(s); 326 pr.pr_end = htons(e); 327 328 return npfvar_create_element(NPFVAR_PORT_RANGE, &pr, sizeof(pr)); 329 } 330 331 npfvar_t * 332 npfctl_parse_port_range_variable(const char *v, npfvar_t *vp) 333 { 334 size_t count = npfvar_get_count(vp); 335 npfvar_t *pvp = npfvar_create(); 336 port_range_t *pr; 337 338 for (size_t i = 0; i < count; i++) { 339 int type = npfvar_get_type(vp, i); 340 void *data = npfvar_get_data(vp, type, i); 341 in_port_t p; 342 343 switch (type) { 344 case NPFVAR_IDENTIFIER: 345 case NPFVAR_STRING: 346 p = npfctl_portno(data); 347 npfvar_add_elements(pvp, npfctl_parse_port_range(p, p)); 348 break; 349 case NPFVAR_PORT_RANGE: 350 pr = data; 351 npfvar_add_element(pvp, NPFVAR_PORT_RANGE, pr, 352 sizeof(*pr)); 353 break; 354 case NPFVAR_NUM: 355 p = *(uint32_t *)data; 356 npfvar_add_elements(pvp, npfctl_parse_port_range(p, p)); 357 break; 358 default: 359 if (v) { 360 yyerror("wrong variable '%s' type '%s' " 361 "for port range", v, npfvar_type(type)); 362 } else { 363 yyerror("wrong element '%s' in the " 364 "inline list", npfvar_type(type)); 365 } 366 npfvar_destroy(pvp); 367 return NULL; 368 } 369 } 370 return pvp; 371 } 372 373 npfvar_t * 374 npfctl_parse_ifnet(const char *ifname, const int family) 375 { 376 struct ifaddrs *ifa; 377 ifnet_addr_t ifna; 378 npfvar_t *vpa; 379 380 if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) { 381 err(EXIT_FAILURE, "getifaddrs"); 382 } 383 384 vpa = npfvar_create(); 385 ifna.ifna_name = estrdup(ifname); 386 ifna.ifna_addrs = vpa; 387 ifna.ifna_index = npfctl_find_ifindex(ifname); 388 assert(ifna.ifna_index != 0); 389 390 for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) { 391 fam_addr_mask_t fam; 392 struct sockaddr *sa; 393 394 if (strcmp(ifa->ifa_name, ifname) != 0) 395 continue; 396 397 if ((ifa->ifa_flags & IFF_UP) == 0) 398 warnx("interface '%s' is down", ifname); 399 400 sa = ifa->ifa_addr; 401 if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) 402 continue; 403 if (family != AF_UNSPEC && sa->sa_family != family) 404 continue; 405 406 memset(&fam, 0, sizeof(fam)); 407 fam.fam_family = sa->sa_family; 408 fam.fam_ifindex = ifna.ifna_index; 409 fam.fam_mask = NPF_NO_NETMASK; 410 411 if (!npfctl_copy_address(sa->sa_family, &fam.fam_addr, sa)) 412 goto out; 413 414 if (!npfvar_add_element(vpa, NPFVAR_FAM, &fam, sizeof(fam))) 415 goto out; 416 } 417 if (npfvar_get_count(vpa) == 0) { 418 yyerror("no addresses matched for interface '%s'", ifname); 419 goto out; 420 } 421 422 return npfvar_create_element(NPFVAR_INTERFACE, &ifna, sizeof(ifna)); 423 out: 424 npfvar_destroy(ifna.ifna_addrs); 425 return NULL; 426 } 427 428 bool 429 npfctl_parse_cidr(char *cidr, fam_addr_mask_t *fam, int *alen) 430 { 431 char *mask, *p; 432 433 p = strchr(cidr, '\n'); 434 if (p) { 435 *p = '\0'; 436 } 437 mask = strchr(cidr, '/'); 438 if (mask) { 439 *mask++ = '\0'; 440 } 441 442 memset(fam, 0, sizeof(*fam)); 443 if (!npfctl_parse_fam_addr(cidr, &fam->fam_family, &fam->fam_addr)) { 444 return false; 445 } 446 if (!npfctl_parse_mask(mask, fam->fam_family, &fam->fam_mask)) { 447 return false; 448 } 449 switch (fam->fam_family) { 450 case AF_INET: 451 *alen = sizeof(struct in_addr); 452 break; 453 case AF_INET6: 454 *alen = sizeof(struct in6_addr); 455 break; 456 default: 457 return false; 458 } 459 return true; 460 } 461 462 int 463 npfctl_protono(const char *proto) 464 { 465 struct protoent *pe; 466 467 pe = getprotobyname(proto); 468 if (pe == NULL) { 469 yyerror("unknown protocol '%s'", proto); 470 return -1; 471 } 472 return pe->p_proto; 473 } 474 475 /* 476 * npfctl_portno: convert port identifier (string) to a number. 477 * 478 * => Returns port number in host byte order. 479 */ 480 in_port_t 481 npfctl_portno(const char *port) 482 { 483 struct addrinfo *ai, *rai; 484 in_port_t p = 0; 485 int e; 486 487 e = getaddrinfo(NULL, port, NULL, &rai); 488 if (e != 0) { 489 yyerror("invalid port name '%s' (%s)", port, gai_strerror(e)); 490 return 0; 491 } 492 493 for (ai = rai; ai; ai = ai->ai_next) { 494 switch (ai->ai_family) { 495 case AF_INET: { 496 struct sockaddr_in *sin = (void *)ai->ai_addr; 497 p = sin->sin_port; 498 goto out; 499 } 500 case AF_INET6: { 501 struct sockaddr_in6 *sin6 = (void *)ai->ai_addr; 502 p = sin6->sin6_port; 503 goto out; 504 } 505 default: 506 break; 507 } 508 } 509 out: 510 freeaddrinfo(rai); 511 return ntohs(p); 512 } 513 514 npfvar_t * 515 npfctl_parse_tcpflag(const char *s) 516 { 517 uint8_t tfl = 0; 518 519 while (*s) { 520 switch (*s) { 521 case 'F': tfl |= TH_FIN; break; 522 case 'S': tfl |= TH_SYN; break; 523 case 'R': tfl |= TH_RST; break; 524 case 'P': tfl |= TH_PUSH; break; 525 case 'A': tfl |= TH_ACK; break; 526 case 'U': tfl |= TH_URG; break; 527 case 'E': tfl |= TH_ECE; break; 528 case 'W': tfl |= TH_CWR; break; 529 default: 530 yyerror("invalid flag '%c'", *s); 531 return NULL; 532 } 533 s++; 534 } 535 return npfvar_create_element(NPFVAR_TCPFLAG, &tfl, sizeof(tfl)); 536 } 537 538 uint8_t 539 npfctl_icmptype(int proto, const char *type) 540 { 541 #ifdef __NetBSD__ 542 uint8_t ul; 543 544 switch (proto) { 545 case IPPROTO_ICMP: 546 for (ul = 0; icmp_type[ul]; ul++) 547 if (strcmp(icmp_type[ul], type) == 0) 548 return ul; 549 break; 550 case IPPROTO_ICMPV6: 551 for (ul = 0; icmp6_type_err[ul]; ul++) 552 if (strcmp(icmp6_type_err[ul], type) == 0) 553 return ul; 554 for (ul = 0; icmp6_type_info[ul]; ul++) 555 if (strcmp(icmp6_type_info[ul], type) == 0) 556 return ul + 128; 557 break; 558 default: 559 assert(false); 560 } 561 #else 562 (void)proto; 563 #endif 564 yyerror("unknown icmp-type %s", type); 565 return ~0; 566 } 567 568 uint8_t 569 npfctl_icmpcode(int proto, uint8_t type, const char *code) 570 { 571 #ifdef __NetBSD__ 572 const char * const *arr; 573 574 switch (proto) { 575 case IPPROTO_ICMP: 576 switch (type) { 577 case ICMP_ECHOREPLY: 578 case ICMP_SOURCEQUENCH: 579 case ICMP_ALTHOSTADDR: 580 case ICMP_ECHO: 581 case ICMP_ROUTERSOLICIT: 582 case ICMP_TSTAMP: 583 case ICMP_TSTAMPREPLY: 584 case ICMP_IREQ: 585 case ICMP_IREQREPLY: 586 case ICMP_MASKREQ: 587 case ICMP_MASKREPLY: 588 arr = icmp_code_none; 589 break; 590 case ICMP_ROUTERADVERT: 591 arr = icmp_code_routeradvert; 592 break; 593 case ICMP_UNREACH: 594 arr = icmp_code_unreach; 595 break; 596 case ICMP_REDIRECT: 597 arr = icmp_code_redirect; 598 break; 599 case ICMP_TIMXCEED: 600 arr = icmp_code_timxceed; 601 break; 602 case ICMP_PARAMPROB: 603 arr = icmp_code_paramprob; 604 break; 605 case ICMP_PHOTURIS: 606 arr = icmp_code_photuris; 607 break; 608 default: 609 yyerror("unknown icmp-type %d while parsing code %s", 610 type, code); 611 return ~0; 612 } 613 break; 614 case IPPROTO_ICMPV6: 615 switch (type) { 616 case ICMP6_DST_UNREACH: 617 arr = icmp6_code_unreach; 618 break; 619 case ICMP6_TIME_EXCEEDED: 620 arr = icmp6_code_timxceed; 621 break; 622 case ICMP6_PARAM_PROB: 623 arr = icmp6_code_paramprob; 624 break; 625 case ICMP6_PACKET_TOO_BIG: 626 /* code-less info ICMPs */ 627 case ICMP6_ECHO_REQUEST: 628 case ICMP6_ECHO_REPLY: 629 case MLD_LISTENER_QUERY: 630 case MLD_LISTENER_REPORT: 631 case MLD_LISTENER_DONE: 632 case ND_ROUTER_SOLICIT: 633 case ND_ROUTER_ADVERT: 634 case ND_NEIGHBOR_SOLICIT: 635 case ND_NEIGHBOR_ADVERT: 636 case ND_REDIRECT: 637 arr = icmp6_code_none; 638 break; 639 /* XXX TODO: info ICMPs with code values */ 640 default: 641 yyerror("unknown icmp-type %d while parsing code %s", 642 type, code); 643 return ~0; 644 } 645 break; 646 default: 647 assert(false); 648 } 649 650 for (uint8_t ul = 0; arr[ul]; ul++) { 651 if (strcmp(arr[ul], code) == 0) 652 return ul; 653 } 654 #else 655 (void)proto; 656 #endif 657 yyerror("unknown code %s for icmp-type %d", code, type); 658 return ~0; 659 } 660 661 npfvar_t * 662 npfctl_parse_icmp(int proto __unused, int type, int code) 663 { 664 npfvar_t *vp = npfvar_create(); 665 666 if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type))) 667 goto out; 668 669 if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code))) 670 goto out; 671 672 return vp; 673 out: 674 npfvar_destroy(vp); 675 return NULL; 676 } 677 678 filt_opts_t 679 npfctl_parse_l3filt_opt(npfvar_t *src_addr, npfvar_t *src_port, bool tnot, 680 npfvar_t *dst_addr, npfvar_t *dst_port, bool fnot, rid_t uid, rid_t gid) 681 { 682 filt_opts_t fopts; 683 684 fopts.filt.opt3.fo_from.ap_netaddr = src_addr; 685 fopts.filt.opt3.fo_from.ap_portrange = src_port; 686 fopts.fo_finvert = tnot; 687 fopts.filt.opt3.fo_to.ap_netaddr = dst_addr; 688 fopts.filt.opt3.fo_to.ap_portrange = dst_port; 689 fopts.fo_tinvert = fnot; 690 fopts.uid = uid; 691 fopts.gid = gid; 692 fopts.layer = NPF_RULE_LAYER_3; 693 694 return fopts; 695 } 696 697 filt_opts_t 698 npfctl_parse_l2filt_opt(npfvar_t *src_addr, bool fnot, npfvar_t *dst_addr, 699 bool tnot, uint16_t eth_type) 700 { 701 filt_opts_t fopts; 702 703 fopts.filt.opt2.from_mac = src_addr; 704 fopts.fo_finvert = fnot; 705 fopts.filt.opt2.to_mac = dst_addr; 706 fopts.fo_tinvert = tnot; 707 fopts.filt.opt2.ether_type = eth_type; 708 fopts.layer = NPF_RULE_LAYER_2; 709 710 return fopts; 711 } 712 713 #define atox(c) (((c) <= '9') ? ((c) - '0') : ((toupper(c) - 'A') + 10)) 714 /* 715 * general function to parse ether type and mac address 716 */ 717 static void 718 parse_ether_hex(uint8_t *dest, const char *str, int hexlength, const char *err) 719 { 720 const uint8_t *cp = (const uint8_t *)str; 721 uint8_t *ep; 722 723 ep = dest + hexlength; /* check null terminated boundary */ 724 725 while (*cp) { 726 if (!isxdigit(*cp)) 727 yyerror("%s: %s", err, str); 728 729 *dest = atox(*cp); 730 cp++; 731 if (isxdigit(*cp)) { 732 *dest = (*dest << 4) | atox(*cp); 733 cp++; 734 } 735 dest++; 736 737 if (dest == ep) { 738 if (*cp == '\0') 739 return; 740 else 741 yyerror("%s: %s", err, str); 742 } 743 744 switch (*cp) { 745 case ':': 746 case '-': 747 case '.': 748 cp++; 749 break; 750 } 751 } 752 } 753 754 uint16_t 755 npfctl_parse_ether_type(const char *str) 756 { 757 #define ETHER_LEN 4 758 const char *err = "invalid ether type format"; 759 uint8_t etype[2]; 760 parse_ether_hex(etype, str + 2, ETHER_LEN, err); 761 762 uint16_t *e_type = (uint16_t *)etype; /* fetch the whole two byte blocks */ 763 764 return *e_type; 765 } 766 767 npfvar_t * 768 npfctl_parse_mac_addr(const char *mac_addr) 769 { 770 const char *err = "invalid mac address format"; 771 struct ether_addr *ether; 772 uint8_t addr[ETHER_ADDR_LEN]; 773 774 ether = (struct ether_addr *)addr; 775 parse_ether_hex(addr, mac_addr, ETHER_ADDR_LEN, err); 776 777 return npfvar_create_element(NPFVAR_MAC, ether, sizeof(*ether)); 778 } 779 780 /* 781 * npfctl_npt66_calcadj: calculate the adjustment for NPTv6 as per RFC 6296. 782 */ 783 uint16_t 784 npfctl_npt66_calcadj(npf_netmask_t len, const npf_addr_t *pref_in, 785 const npf_addr_t *pref_out) 786 { 787 const uint16_t *addr6_in = (const uint16_t *)pref_in; 788 const uint16_t *addr6_out = (const uint16_t *)pref_out; 789 unsigned i, remnant, wordmask, preflen = len >> 4; 790 uint32_t adj, isum = 0, osum = 0; 791 792 /* 793 * Extract the bits within a 16-bit word (when prefix length is 794 * not dividable by 16) and include them into the sum. 795 */ 796 remnant = len - (preflen << 4); 797 wordmask = (1U << remnant) - 1; 798 assert(wordmask == 0 || (len % 16) != 0); 799 800 /* Inner prefix - sum and fold. */ 801 for (i = 0; i < preflen; i++) { 802 isum += addr6_in[i]; 803 } 804 isum += addr6_in[i] & wordmask; 805 while (isum >> 16) { 806 isum = (isum >> 16) + (isum & 0xffff); 807 } 808 809 /* Outer prefix - sum and fold. */ 810 for (i = 0; i < preflen; i++) { 811 osum += addr6_out[i]; 812 } 813 osum += addr6_out[i] & wordmask; 814 while (osum >> 16) { 815 osum = (osum >> 16) + (osum & 0xffff); 816 } 817 818 /* Calculate 1's complement difference. */ 819 adj = isum + ~osum; 820 while (adj >> 16) { 821 adj = (adj >> 16) + (adj & 0xffff); 822 } 823 return (uint16_t)adj; 824 } 825