Home | History | Annotate | Line # | Download | only in npfctl
      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