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