1 /* $NetBSD: npf_data.c,v 1.11 2012/02/26 21:50:05 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * npfctl(8) data manipulation and helper routines. 31 */ 32 33 #include <sys/cdefs.h> 34 __RCSID("$NetBSD: npf_data.c,v 1.11 2012/02/26 21:50:05 christos Exp $"); 35 36 #include <sys/types.h> 37 #include <sys/null.h> 38 39 #include <netinet/in.h> 40 #include <netinet/in_systm.h> 41 #include <netinet/ip.h> 42 #define ICMP_STRINGS 43 #include <netinet/ip_icmp.h> 44 #include <netinet/tcp.h> 45 #include <net/if.h> 46 47 #include <stdlib.h> 48 #include <string.h> 49 #include <err.h> 50 #include <errno.h> 51 #include <ifaddrs.h> 52 #include <netdb.h> 53 54 #include "npfctl.h" 55 56 static struct ifaddrs * ifs_list = NULL; 57 58 unsigned long 59 npfctl_find_ifindex(const char *ifname) 60 { 61 return if_nametoindex(ifname); 62 } 63 64 static bool 65 npfctl_copy_address(sa_family_t fam, npf_addr_t *addr, const void *ptr) 66 { 67 switch (fam) { 68 case AF_INET: { 69 const struct sockaddr_in *sin = ptr; 70 memcpy(addr, &sin->sin_addr, sizeof(sin->sin_addr)); 71 return true; 72 } 73 case AF_INET6: { 74 const struct sockaddr_in6 *sin6 = ptr; 75 memcpy(addr, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); 76 return true; 77 } 78 default: 79 yyerror("unknown address family %u", fam); 80 return false; 81 } 82 } 83 84 static bool 85 npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr) 86 { 87 static const struct addrinfo hint = { 88 .ai_family = AF_UNSPEC, 89 .ai_flags = AI_NUMERICHOST 90 }; 91 struct addrinfo *ai; 92 int ret; 93 94 ret = getaddrinfo(name, NULL, &hint, &ai); 95 if (ret) { 96 yyerror("cannot parse '%s' (%s)", name, gai_strerror(ret)); 97 return false; 98 } 99 if (fam) { 100 *fam = ai->ai_family; 101 } 102 if (!npfctl_copy_address(*fam, addr, ai->ai_addr)) { 103 return false; 104 } 105 freeaddrinfo(ai); 106 return true; 107 } 108 109 static bool 110 npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask) 111 { 112 char *ep = NULL; 113 npf_addr_t addr; 114 uint8_t *ap; 115 116 if (s) { 117 errno = 0; 118 *mask = (npf_netmask_t)strtol(s, &ep, 0); 119 if (*ep == '\0' && s != ep && errno != ERANGE) 120 return true; 121 if (!npfctl_parse_fam_addr(s, &fam, &addr)) 122 return false; 123 } 124 125 switch (fam) { 126 case AF_INET: 127 *mask = 32; 128 break; 129 case AF_INET6: 130 *mask = 128; 131 break; 132 default: 133 yyerror("unknown address family %u", fam); 134 return false; 135 } 136 137 if (ep == NULL) { 138 return true; 139 } 140 ap = addr.s6_addr + (*mask / 8) - 1; 141 while (ap >= addr.s6_addr) { 142 for (int j = 8; j > 0; j--) { 143 if (*ap & 1) 144 return true; 145 *ap >>= 1; 146 (*mask)--; 147 if (*mask == 0) 148 return true; 149 } 150 ap--; 151 } 152 return true; 153 } 154 155 /* 156 * npfctl_parse_fam_addr_mask: return address family, address and mask. 157 * 158 * => Mask is optional and can be NULL. 159 * => Returns true on success or false if unable to parse. 160 */ 161 npfvar_t * 162 npfctl_parse_fam_addr_mask(const char *addr, const char *mask, 163 unsigned long *nummask) 164 { 165 npfvar_t *vp = npfvar_create(".addr"); 166 fam_addr_mask_t fam; 167 168 memset(&fam, 0, sizeof(fam)); 169 170 if (!npfctl_parse_fam_addr(addr, &fam.fam_family, &fam.fam_addr)) 171 goto out; 172 173 /* 174 * Note: both mask and nummask may be NULL. In such case, 175 * npfctl_parse_mask() will handle and will set full mask. 176 */ 177 if (nummask) { 178 fam.fam_mask = *nummask; 179 } else if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) { 180 goto out; 181 } 182 183 if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam))) 184 goto out; 185 186 return vp; 187 out: 188 npfvar_destroy(vp); 189 return NULL; 190 } 191 192 npfvar_t * 193 npfctl_parse_table_id(const char *id) 194 { 195 npfvar_t *vp; 196 197 if (!npfctl_table_exists_p(id)) { 198 yyerror("table '%s' is not defined", id); 199 return NULL; 200 } 201 vp = npfvar_create(".table"); 202 203 if (!npfvar_add_element(vp, NPFVAR_TABLE, id, strlen(id) + 1)) 204 goto out; 205 206 return vp; 207 out: 208 npfvar_destroy(vp); 209 return NULL; 210 } 211 212 /* 213 * npfctl_parse_port_range: create a port-range variable. Note that the 214 * passed port numbers are in network byte order. 215 */ 216 npfvar_t * 217 npfctl_parse_port_range(in_port_t s, in_port_t e) 218 { 219 npfvar_t *vp = npfvar_create(".port_range"); 220 port_range_t pr; 221 222 pr.pr_start = s; 223 pr.pr_end = e; 224 225 if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr))) 226 goto out; 227 228 return vp; 229 out: 230 npfvar_destroy(vp); 231 return NULL; 232 } 233 234 npfvar_t * 235 npfctl_parse_port_range_variable(const char *v) 236 { 237 npfvar_t *vp = npfvar_lookup(v); 238 in_port_t p; 239 port_range_t *pr; 240 size_t count = npfvar_get_count(vp); 241 npfvar_t *pvp = npfvar_create(".port_range"); 242 243 for (size_t i = 0; i < count; i++) { 244 int type = npfvar_get_type(vp, i); 245 void *data = npfvar_get_data(vp, type, i); 246 switch (type) { 247 case NPFVAR_IDENTIFIER: 248 case NPFVAR_STRING: 249 p = npfctl_portno(data); 250 npfvar_add_elements(pvp, npfctl_parse_port_range(p, p)); 251 break; 252 case NPFVAR_PORT_RANGE: 253 pr = data; 254 npfvar_add_element(pvp, NPFVAR_PORT_RANGE, pr, 255 sizeof(*pr)); 256 break; 257 case NPFVAR_NUM: 258 p = *(unsigned long *)data; 259 npfvar_add_elements(pvp, npfctl_parse_port_range(p, p)); 260 break; 261 default: 262 yyerror("wrong variable '%s' type '%s' for port range", 263 v, npfvar_type(type)); 264 goto out; 265 } 266 } 267 return pvp; 268 out: 269 npfvar_destroy(pvp); 270 return NULL; 271 } 272 273 npfvar_t * 274 npfctl_parse_iface(const char *ifname) 275 { 276 npfvar_t *vp = npfvar_create(".iface"); 277 struct ifaddrs *ifa; 278 fam_addr_mask_t fam; 279 bool gotif = false; 280 281 if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) { 282 err(EXIT_FAILURE, "getifaddrs"); 283 } 284 memset(&fam, 0, sizeof(fam)); 285 286 npfvar_t *ip = npfvar_create(".ifname"); 287 if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1)) 288 goto out; 289 290 for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) { 291 struct sockaddr *sa; 292 sa_family_t family; 293 294 if (strcmp(ifa->ifa_name, ifname) != 0) 295 continue; 296 297 gotif = true; 298 if ((ifa->ifa_flags & IFF_UP) == 0) 299 warnx("interface '%s' is down", ifname); 300 301 sa = ifa->ifa_addr; 302 family = sa->sa_family; 303 if (family != AF_INET && family != AF_INET6) 304 continue; 305 306 fam.fam_family = family; 307 fam.fam_interface = ip; 308 309 if (!npfctl_copy_address(family, &fam.fam_addr, sa)) 310 goto out; 311 312 if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask)) 313 goto out; 314 315 if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam))) 316 goto out; 317 } 318 if (!gotif) { 319 yyerror("interface '%s' not found", ifname); 320 goto out; 321 } 322 if (npfvar_get_count(vp) == 0) { 323 yyerror("no addresses matched for interface '%s'", ifname); 324 goto out; 325 } 326 return vp; 327 out: 328 npfvar_destroy(vp); 329 npfvar_destroy(ip); 330 return NULL; 331 } 332 333 fam_addr_mask_t * 334 npfctl_parse_cidr(char *cidr) 335 { 336 npfvar_t *vp; 337 char *p; 338 339 p = strchr(cidr, '/'); 340 if (p) { 341 *p++ = '\0'; 342 } 343 vp = npfctl_parse_fam_addr_mask(cidr, p, NULL); 344 if (vp == NULL) { 345 return NULL; 346 } 347 return npfvar_get_data(vp, NPFVAR_FAM, 0); 348 } 349 350 /* 351 * npfctl_portno: convert port identifier (string) to a number. 352 * 353 * => Returns port number in network byte order. 354 */ 355 in_port_t 356 npfctl_portno(const char *port) 357 { 358 struct addrinfo *ai, *rai; 359 in_port_t p = 0; 360 int e; 361 362 e = getaddrinfo(NULL, port, NULL, &rai); 363 if (e != 0) { 364 yyerror("invalid port name: '%s' (%s)", port, gai_strerror(e)); 365 return 0; 366 } 367 368 for (ai = rai; ai; ai = ai->ai_next) { 369 switch (ai->ai_family) { 370 case AF_INET: { 371 struct sockaddr_in *sin = (void *)ai->ai_addr; 372 p = sin->sin_port; 373 goto out; 374 } 375 case AF_INET6: { 376 struct sockaddr_in6 *sin6 = (void *)ai->ai_addr; 377 p = sin6->sin6_port; 378 goto out; 379 } 380 default: 381 break; 382 } 383 } 384 out: 385 freeaddrinfo(rai); 386 return p; 387 } 388 389 npfvar_t * 390 npfctl_parse_tcpflag(const char *s) 391 { 392 uint8_t tfl = 0; 393 394 while (*s) { 395 switch (*s) { 396 case 'F': tfl |= TH_FIN; break; 397 case 'S': tfl |= TH_SYN; break; 398 case 'R': tfl |= TH_RST; break; 399 case 'P': tfl |= TH_PUSH; break; 400 case 'A': tfl |= TH_ACK; break; 401 case 'U': tfl |= TH_URG; break; 402 case 'E': tfl |= TH_ECE; break; 403 case 'W': tfl |= TH_CWR; break; 404 default: 405 yyerror("invalid flag '%c'", *s); 406 return NULL; 407 } 408 s++; 409 } 410 411 npfvar_t *vp = npfvar_create(".tcp_flag"); 412 if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) { 413 npfvar_destroy(vp); 414 return NULL; 415 } 416 417 return vp; 418 } 419 420 uint8_t 421 npfctl_icmptype(const char *type) 422 { 423 for (uint8_t ul = 0; icmp_type[ul]; ul++) 424 if (strcmp(icmp_type[ul], type) == 0) 425 return ul; 426 return ~0; 427 } 428 429 uint8_t 430 npfctl_icmpcode(uint8_t type, const char *code) 431 { 432 const char **arr; 433 434 switch (type) { 435 case ICMP_ECHOREPLY: 436 case ICMP_SOURCEQUENCH: 437 case ICMP_ALTHOSTADDR: 438 case ICMP_ECHO: 439 case ICMP_ROUTERSOLICIT: 440 case ICMP_TSTAMP: 441 case ICMP_TSTAMPREPLY: 442 case ICMP_IREQ: 443 case ICMP_IREQREPLY: 444 case ICMP_MASKREQ: 445 case ICMP_MASKREPLY: 446 arr = icmp_code_none; 447 break; 448 case ICMP_ROUTERADVERT: 449 arr = icmp_code_routeradvert; 450 break; 451 case ICMP_UNREACH: 452 arr = icmp_code_unreach; 453 break; 454 case ICMP_REDIRECT: 455 arr = icmp_code_redirect; 456 break; 457 case ICMP_TIMXCEED: 458 arr = icmp_code_timxceed; 459 break; 460 case ICMP_PARAMPROB: 461 arr = icmp_code_paramprob; 462 break; 463 case ICMP_PHOTURIS: 464 arr = icmp_code_photuris; 465 break; 466 default: 467 return ~0; 468 } 469 470 for (uint8_t ul = 0; arr[ul]; ul++) { 471 if (strcmp(arr[ul], code) == 0) 472 return ul; 473 } 474 return ~0; 475 } 476 477 npfvar_t * 478 npfctl_parse_icmp(uint8_t type, uint8_t code) 479 { 480 npfvar_t *vp = npfvar_create(".icmp"); 481 482 if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type))) 483 goto out; 484 485 if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code))) 486 goto out; 487 488 return vp; 489 out: 490 npfvar_destroy(vp); 491 return NULL; 492 } 493