1 /* $NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind 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.10 2012/01/08 21:34:21 rmind 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_iface(const char *ifname) 236 { 237 npfvar_t *vp = npfvar_create(".iface"); 238 struct ifaddrs *ifa; 239 fam_addr_mask_t fam; 240 bool gotif = false; 241 242 if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) { 243 err(EXIT_FAILURE, "getifaddrs"); 244 } 245 memset(&fam, 0, sizeof(fam)); 246 247 npfvar_t *ip = npfvar_create(".ifname"); 248 if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1)) 249 goto out; 250 251 for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) { 252 struct sockaddr *sa; 253 sa_family_t family; 254 255 if (strcmp(ifa->ifa_name, ifname) != 0) 256 continue; 257 258 gotif = true; 259 if ((ifa->ifa_flags & IFF_UP) == 0) 260 warnx("interface '%s' is down", ifname); 261 262 sa = ifa->ifa_addr; 263 family = sa->sa_family; 264 if (family != AF_INET && family != AF_INET6) 265 continue; 266 267 fam.fam_family = family; 268 fam.fam_interface = ip; 269 270 if (!npfctl_copy_address(family, &fam.fam_addr, sa)) 271 goto out; 272 273 if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask)) 274 goto out; 275 276 if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam))) 277 goto out; 278 } 279 if (!gotif) { 280 yyerror("interface '%s' not found", ifname); 281 goto out; 282 } 283 if (npfvar_get_count(vp) == 0) { 284 yyerror("no addresses matched for interface '%s'", ifname); 285 goto out; 286 } 287 return vp; 288 out: 289 npfvar_destroy(vp); 290 npfvar_destroy(ip); 291 return NULL; 292 } 293 294 fam_addr_mask_t * 295 npfctl_parse_cidr(char *cidr) 296 { 297 npfvar_t *vp; 298 char *p; 299 300 p = strchr(cidr, '/'); 301 if (p) { 302 *p++ = '\0'; 303 } 304 vp = npfctl_parse_fam_addr_mask(cidr, p, NULL); 305 if (vp == NULL) { 306 return NULL; 307 } 308 return npfvar_get_data(vp, NPFVAR_FAM, 0); 309 } 310 311 /* 312 * npfctl_portno: convert port identifier (string) to a number. 313 * 314 * => Returns port number in network byte order. 315 */ 316 in_port_t 317 npfctl_portno(const char *port) 318 { 319 struct addrinfo *ai, *rai; 320 in_port_t p = 0; 321 int e; 322 323 e = getaddrinfo(NULL, port, NULL, &rai); 324 if (e != 0) { 325 yyerror("invalid port name: '%s' (%s)", port, gai_strerror(e)); 326 return 0; 327 } 328 329 for (ai = rai; ai; ai = ai->ai_next) { 330 switch (ai->ai_family) { 331 case AF_INET: { 332 struct sockaddr_in *sin = (void *)ai->ai_addr; 333 p = sin->sin_port; 334 goto out; 335 } 336 case AF_INET6: { 337 struct sockaddr_in6 *sin6 = (void *)ai->ai_addr; 338 p = sin6->sin6_port; 339 goto out; 340 } 341 default: 342 break; 343 } 344 } 345 out: 346 freeaddrinfo(rai); 347 return p; 348 } 349 350 npfvar_t * 351 npfctl_parse_tcpflag(const char *s) 352 { 353 uint8_t tfl = 0; 354 355 while (*s) { 356 switch (*s) { 357 case 'F': tfl |= TH_FIN; break; 358 case 'S': tfl |= TH_SYN; break; 359 case 'R': tfl |= TH_RST; break; 360 case 'P': tfl |= TH_PUSH; break; 361 case 'A': tfl |= TH_ACK; break; 362 case 'U': tfl |= TH_URG; break; 363 case 'E': tfl |= TH_ECE; break; 364 case 'W': tfl |= TH_CWR; break; 365 default: 366 yyerror("invalid flag '%c'", *s); 367 return NULL; 368 } 369 s++; 370 } 371 372 npfvar_t *vp = npfvar_create(".tcp_flag"); 373 if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) { 374 npfvar_destroy(vp); 375 return NULL; 376 } 377 378 return vp; 379 } 380 381 uint8_t 382 npfctl_icmptype(const char *type) 383 { 384 for (uint8_t ul = 0; icmp_type[ul]; ul++) 385 if (strcmp(icmp_type[ul], type) == 0) 386 return ul; 387 return ~0; 388 } 389 390 uint8_t 391 npfctl_icmpcode(uint8_t type, const char *code) 392 { 393 const char **arr; 394 395 switch (type) { 396 case ICMP_ECHOREPLY: 397 case ICMP_SOURCEQUENCH: 398 case ICMP_ALTHOSTADDR: 399 case ICMP_ECHO: 400 case ICMP_ROUTERSOLICIT: 401 case ICMP_TSTAMP: 402 case ICMP_TSTAMPREPLY: 403 case ICMP_IREQ: 404 case ICMP_IREQREPLY: 405 case ICMP_MASKREQ: 406 case ICMP_MASKREPLY: 407 arr = icmp_code_none; 408 break; 409 case ICMP_ROUTERADVERT: 410 arr = icmp_code_routeradvert; 411 break; 412 case ICMP_UNREACH: 413 arr = icmp_code_unreach; 414 break; 415 case ICMP_REDIRECT: 416 arr = icmp_code_redirect; 417 break; 418 case ICMP_TIMXCEED: 419 arr = icmp_code_timxceed; 420 break; 421 case ICMP_PARAMPROB: 422 arr = icmp_code_paramprob; 423 break; 424 case ICMP_PHOTURIS: 425 arr = icmp_code_photuris; 426 break; 427 default: 428 return ~0; 429 } 430 431 for (uint8_t ul = 0; arr[ul]; ul++) { 432 if (strcmp(arr[ul], code) == 0) 433 return ul; 434 } 435 return ~0; 436 } 437 438 npfvar_t * 439 npfctl_parse_icmp(uint8_t type, uint8_t code) 440 { 441 npfvar_t *vp = npfvar_create(".icmp"); 442 443 if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type))) 444 goto out; 445 446 if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code))) 447 goto out; 448 449 return vp; 450 out: 451 npfvar_destroy(vp); 452 return NULL; 453 } 454