1 /* $NetBSD: npf_data.c,v 1.2 2010/08/23 06:01:04 jnemeth Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2010 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 * NPF proplib(9) dictionary producer. 31 * 32 * XXX: Needs some clean-up. 33 */ 34 35 #include <sys/types.h> 36 #include <sys/socket.h> 37 #include <sys/ioctl.h> 38 #include <net/if.h> 39 40 #include <arpa/inet.h> 41 #include <prop/proplib.h> 42 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <ctype.h> 47 #include <err.h> 48 #include <ifaddrs.h> 49 #include <netdb.h> 50 #include <assert.h> 51 52 #include "npfctl.h" 53 54 static struct ifaddrs * ifs_list = NULL; 55 56 static prop_dictionary_t npf_dict, settings_dict; 57 static prop_array_t nat_arr, tables_arr, rules_arr; 58 59 static pri_t gr_prio_counter = 1; 60 static pri_t rl_prio_counter = 1; 61 static pri_t nat_prio_counter = 1; 62 63 void 64 npfctl_init_data(void) 65 { 66 prop_number_t ver; 67 68 if (getifaddrs(&ifs_list) == -1) 69 err(EXIT_FAILURE, "getifaddrs"); 70 71 npf_dict = prop_dictionary_create(); 72 73 ver = prop_number_create_integer(NPF_VERSION); 74 prop_dictionary_set(npf_dict, "version", ver); 75 76 nat_arr = prop_array_create(); 77 prop_dictionary_set(npf_dict, "nat", nat_arr); 78 79 settings_dict = prop_dictionary_create(); 80 prop_dictionary_set(npf_dict, "settings", settings_dict); 81 82 tables_arr = prop_array_create(); 83 prop_dictionary_set(npf_dict, "tables", tables_arr); 84 85 rules_arr = prop_array_create(); 86 prop_dictionary_set(npf_dict, "rules", rules_arr); 87 } 88 89 int 90 npfctl_ioctl_send(int fd) 91 { 92 int ret = 0, errval; 93 94 #ifdef DEBUG 95 prop_dictionary_externalize_to_file(npf_dict, "/tmp/npf.plist"); 96 #else 97 errval = prop_dictionary_send_ioctl(npf_dict, fd, IOC_NPF_RELOAD); 98 if (errval) { 99 errx(EXIT_FAILURE, "npf_ioctl_send: %s\n", strerror(errval)); 100 ret = -1; 101 } 102 #endif 103 prop_object_release(npf_dict); 104 return ret; 105 } 106 107 /* 108 * Helper routines: 109 * 110 * npfctl_getif() - get interface addresses and index number from name. 111 * npfctl_servname2port() - get service ports from name. 112 * npfctl_parse_v4mask() - parse address/mask integers from CIDR block. 113 */ 114 115 static struct ifaddrs * 116 npfctl_getif(char *ifname, unsigned int *if_idx) 117 { 118 struct ifaddrs *ifent; 119 struct sockaddr_in *sin; 120 121 for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) { 122 sin = (struct sockaddr_in *)ifent->ifa_addr; 123 124 if (sin->sin_family != AF_INET) 125 continue; 126 if (strcmp(ifent->ifa_name, ifname) == 0) 127 break; 128 } 129 if (ifent) { 130 *if_idx = if_nametoindex(ifname); 131 } 132 return ifent; 133 } 134 135 static int 136 npfctl_servname2port(char *name) 137 { 138 struct servent *se; 139 140 se = getservbyname(name, NULL); 141 return se ? se->s_port : -1; 142 } 143 144 bool 145 npfctl_parse_v4mask(char *str, in_addr_t *addr, in_addr_t *mask) 146 { 147 char *p = strchr(str, '/'); 148 u_int bits; 149 150 /* In network byte order. */ 151 if (p) { 152 *p++ = '\0'; 153 bits = (u_int)atoi(p); 154 *mask = bits ? htonl(0xffffffff << (32 - bits)) : 0; 155 } else { 156 *mask = 0xffffffff; 157 } 158 return inet_aton(str, (struct in_addr *)addr) != 0; 159 } 160 161 static void 162 npfctl_parse_cidr(char *str, in_addr_t *addr, in_addr_t *mask) 163 { 164 165 if (isalpha((unsigned char)*str)) { 166 struct ifaddrs *ifa; 167 struct sockaddr_in *sin; 168 u_int idx; 169 170 if ((ifa = npfctl_getif(str, &idx)) == NULL) { 171 errx(EXIT_FAILURE, "invalid interface '%s'", str); 172 } 173 /* Interface address. */ 174 sin = (struct sockaddr_in *)ifa->ifa_addr; 175 *addr = sin->sin_addr.s_addr; 176 *mask = 0xffffffff; 177 178 } else if (!npfctl_parse_v4mask(str, addr, mask)) { 179 errx(EXIT_FAILURE, "invalid CIDR '%s'\n", str); 180 } 181 } 182 183 /* 184 * NPF table creation and construction routines. 185 */ 186 187 prop_dictionary_t 188 npfctl_lookup_table(char *tidstr) 189 { 190 prop_dictionary_t tl; 191 prop_object_iterator_t it; 192 prop_object_t obj; 193 u_int tid; 194 195 if ((it = prop_array_iterator(tables_arr)) == NULL) 196 err(EXIT_FAILURE, "prop_array_iterator"); 197 198 tid = atoi(tidstr); 199 while ((tl = prop_object_iterator_next(it)) != NULL) { 200 obj = prop_dictionary_get(tl, "id"); 201 if (tid == prop_number_integer_value(obj)) 202 break; 203 } 204 return tl; 205 } 206 207 prop_dictionary_t 208 npfctl_mk_table(void) 209 { 210 prop_dictionary_t tl; 211 prop_array_t tlist; 212 213 tl = prop_dictionary_create(); 214 tlist = prop_array_create(); 215 prop_dictionary_set(tl, "entries", tlist); 216 217 return tl; 218 } 219 220 void 221 npfctl_table_setup(prop_dictionary_t tl, char *idstr, char *typestr) 222 { 223 prop_number_t typenum; 224 unsigned int id; 225 226 id = atoi(idstr); 227 /* TODO: 1. check ID range 2. check if not a duplicate */ 228 prop_dictionary_set(tl, "id", prop_number_create_integer(id)); 229 230 if (strcmp(typestr, "hash")) { 231 typenum = prop_number_create_integer(NPF_TABLE_HASH); 232 } else if (strcmp(typestr, "tree")) { 233 typenum = prop_number_create_integer(NPF_TABLE_RBTREE); 234 } else { 235 errx(EXIT_FAILURE, "invalid table type '%s'\n", typestr); 236 } 237 prop_dictionary_set(tl, "type", typenum); 238 } 239 240 void 241 npfctl_construct_table(prop_dictionary_t tl, char *fname) 242 { 243 prop_dictionary_t entdict; 244 prop_array_t tblents; 245 char *buf; 246 FILE *fp; 247 size_t n; 248 int l; 249 250 tblents = prop_dictionary_get(tl, "entries"); 251 assert(tblents != NULL); 252 253 fp = fopen(fname, "r"); 254 if (fp == NULL) { 255 err(EXIT_FAILURE, "fopen"); 256 } 257 l = 1; 258 buf = NULL; 259 while (getline(&buf, &n, fp) != -1) { 260 in_addr_t addr, mask; 261 262 if (*buf == '\n' || *buf == '#') 263 continue; 264 265 /* IPv4 CIDR: a.b.c.d/mask */ 266 if (!npfctl_parse_v4mask(buf, &addr, &mask)) 267 errx(EXIT_FAILURE, "invalid table entry at line %d", l); 268 269 /* Create and add table entry. */ 270 entdict = prop_dictionary_create(); 271 prop_dictionary_set(entdict, "addr", 272 prop_number_create_integer(addr)); 273 prop_dictionary_set(entdict, "mask", 274 prop_number_create_integer(mask)); 275 prop_array_add(tblents, entdict); 276 l++; 277 } 278 if (buf != NULL) { 279 free(buf); 280 } 281 } 282 283 void 284 npfctl_add_table(prop_dictionary_t tl) 285 { 286 287 prop_array_add(tables_arr, tl); 288 } 289 290 /* 291 * npfctl_mk_rule: create a rule (or group) dictionary. 292 * 293 * Note: group is a rule containing subrules. It has no n-code, however. 294 */ 295 prop_dictionary_t 296 npfctl_mk_rule(bool group) 297 { 298 prop_dictionary_t rl; 299 prop_array_t subrl; 300 pri_t pri; 301 302 rl = prop_dictionary_create(); 303 if (group) { 304 subrl = prop_array_create(); 305 prop_dictionary_set(rl, "subrules", subrl); 306 /* Give new priority, reset rule priority counter. */ 307 pri = gr_prio_counter++; 308 rl_prio_counter = 1; 309 } else { 310 pri = rl_prio_counter++; 311 } 312 prop_dictionary_set(rl, "priority", 313 prop_number_create_integer(pri)); 314 315 return rl; 316 } 317 318 void 319 npfctl_add_rule(prop_dictionary_t rl, prop_dictionary_t parent) 320 { 321 prop_array_t rlset; 322 323 if (parent) { 324 rlset = prop_dictionary_get(parent, "subrules"); 325 assert(rlset != NULL); 326 } else { 327 rlset = rules_arr; 328 } 329 prop_array_add(rlset, rl); 330 } 331 332 void 333 npfctl_rule_setattr(prop_dictionary_t rl, int attr, char *iface) 334 { 335 prop_number_t attrnum; 336 337 attrnum = prop_number_create_integer(attr); 338 prop_dictionary_set(rl, "attributes", attrnum); 339 if (iface) { 340 prop_number_t ifnum; 341 unsigned int if_idx; 342 343 if (npfctl_getif(iface, &if_idx) == NULL) { 344 errx(EXIT_FAILURE, "invalid interface '%s'", iface); 345 } 346 ifnum = prop_number_create_integer(if_idx); 347 prop_dictionary_set(rl, "interface", ifnum); 348 } 349 } 350 351 /* 352 * Main rule generation routines. 353 */ 354 355 static void 356 npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd) 357 { 358 element_t *el = dat->v_elements; 359 int foff; 360 361 /* If table, generate a single table matching block. */ 362 if (dat->v_type == VAR_TABLE) { 363 u_int tid = atoi(el->e_data); 364 365 nblocks[0]--; 366 foff = npfctl_failure_offset(nblocks); 367 npfctl_gennc_tbl(nc, foff, tid, sd); 368 return; 369 } 370 371 /* Generate v4 CIDR matching blocks. */ 372 for (el = dat->v_elements; el != NULL; el = el->e_next) { 373 in_addr_t addr, mask; 374 375 npfctl_parse_cidr(el->e_data, &addr, &mask); 376 377 nblocks[1]--; 378 foff = npfctl_failure_offset(nblocks); 379 npfctl_gennc_v4cidr(nc, foff, addr, mask, sd); 380 } 381 } 382 383 static void 384 npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp, bool sd) 385 { 386 element_t *el = dat->v_elements; 387 int foff; 388 389 assert(dat->v_type != VAR_TABLE); 390 391 /* Generate TCP/UDP port matching blocks. */ 392 for (el = dat->v_elements; el != NULL; el = el->e_next) { 393 int pfrom, pto; 394 char *sep; 395 396 if ((sep = strchr(el->e_data, ':')) != NULL) { 397 /* Port range (only numeric). */ 398 *sep = '\0'; 399 } 400 if (isalpha((unsigned char)*el->e_data)) { 401 pfrom = npfctl_servname2port(el->e_data); 402 if (pfrom == -1) { 403 errx(EXIT_FAILURE, "invalid service '%s'", 404 el->e_data); 405 } 406 } else { 407 pfrom = htons(atoi(el->e_data)); 408 } 409 pto = sep ? htons(atoi(sep + 1)) : pfrom; 410 411 nblocks[0]--; 412 foff = npfctl_failure_offset(nblocks); 413 npfctl_gennc_ports(nc, foff, pfrom, pto, tcpudp, sd); 414 } 415 } 416 417 static void 418 npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports, 419 bool both, bool tcpudp, bool sd) 420 { 421 422 npfctl_rulenc_v4cidr(nc, nblocks, cidr, sd); 423 if (ports == NULL) { 424 return; 425 } 426 npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, sd); 427 if (!both) { 428 return; 429 } 430 npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, sd); 431 } 432 433 void 434 npfctl_rule_protodata(prop_dictionary_t rl, char *proto, var_t *from, 435 var_t *fports, var_t *to, var_t *tports) 436 { 437 prop_data_t ncdata; 438 bool icmp, tcpudp, both; 439 int nblocks[2] = { 0, 0 }; 440 void *ncptr, *nc; 441 size_t sz; 442 443 /* 444 * Default: both TCP and UDP. 445 */ 446 icmp = false; 447 tcpudp = true; 448 both = false; 449 if (proto == NULL) { 450 goto skip_proto; 451 } 452 453 if (strcmp(proto, "icmp") == 0) { 454 /* ICMP case. */ 455 fports = NULL; 456 tports = NULL; 457 icmp = true; 458 nblocks[0] += 1; 459 460 } else if (strcmp(proto, "tcp") == 0) { 461 /* Just TCP. */ 462 tcpudp = true; 463 464 } else if (strcmp(proto, "udp") == 0) { 465 /* Just UDP. */ 466 tcpudp = false; 467 468 } else { 469 /* Default. */ 470 } 471 skip_proto: 472 473 /* Calculate how blocks to determince n-code. */ 474 if (from && from->v_count) { 475 if (from->v_type == VAR_TABLE) 476 nblocks[0] += 1; 477 else 478 nblocks[1] += from->v_count; 479 if (fports && fports->v_count) 480 nblocks[0] += fports->v_count * (both ? 2 : 1); 481 } 482 if (to && to->v_count) { 483 if (to->v_type == VAR_TABLE) 484 nblocks[0] += 1; 485 else 486 nblocks[1] += to->v_count; 487 if (tports && tports->v_count) 488 nblocks[0] += tports->v_count * (both ? 2 : 1); 489 } 490 491 /* Allocate memory for the n-code. */ 492 sz = npfctl_calc_ncsize(nblocks); 493 ncptr = malloc(sz); 494 if (ncptr == NULL) { 495 perror("malloc"); 496 exit(EXIT_FAILURE); 497 } 498 nc = ncptr; 499 500 /* Ethernet fragment (ETHERTYPE_IP), XXX. */ 501 npfctl_gennc_ether(&nc, npfctl_failure_offset(nblocks), htons(0x0800)); 502 503 /* Generate v4 CIDR matching blocks and TCP/UDP port matching. */ 504 if (from) { 505 npfctl_rulenc_block(&nc, nblocks, from, fports, 506 both, tcpudp, true); 507 } 508 if (to) { 509 npfctl_rulenc_block(&nc, nblocks, to, tports, 510 both, tcpudp, false); 511 } 512 /* ICMP case. */ 513 if (icmp) { 514 const int foff = npfctl_failure_offset(nblocks); 515 npfctl_gennc_icmp(&nc, foff, -1, -1); 516 } 517 npfctl_gennc_complete(&nc); 518 519 if ((uintptr_t)nc - (uintptr_t)ncptr != sz) 520 errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)", 521 (uintptr_t)nc - (uintptr_t)ncptr, sz); 522 523 #ifdef DEBUG 524 uint32_t *op = ncptr; 525 size_t n = sz; 526 do { 527 DPRINTF(("\t> |0x%02x|\n", (u_int)*op)); 528 op++; 529 n -= sizeof(*op); 530 } while (n); 531 #endif 532 533 /* Create a final memory block of data, ready to send. */ 534 ncdata = prop_data_create_data(ncptr, sz); 535 if (ncdata == NULL) { 536 perror("prop_data_create_data"); 537 exit(EXIT_FAILURE); 538 } 539 prop_dictionary_set(rl, "ncode", ncdata); 540 free(ncptr); 541 } 542 543 /* 544 * NAT policy construction routines. 545 */ 546 547 prop_dictionary_t 548 npfctl_mk_nat(void) 549 { 550 prop_dictionary_t rl; 551 pri_t pri; 552 553 /* NAT policy is rule with extra info. */ 554 rl = prop_dictionary_create(); 555 pri = nat_prio_counter++; 556 prop_dictionary_set(rl, "priority", 557 prop_number_create_integer(pri)); 558 return rl; 559 } 560 561 void 562 npfctl_add_nat(prop_dictionary_t nat) 563 { 564 prop_array_add(nat_arr, nat); 565 } 566 567 void 568 npfctl_nat_setup(prop_dictionary_t rl, char *iface, char *gwip) 569 { 570 const int attr = NPF_RULE_PASS | NPF_RULE_OUT | NPF_RULE_FINAL; 571 in_addr_t addr, mask; 572 573 /* Interface and attributes. */ 574 npfctl_rule_setattr(rl, attr, iface); 575 576 /* Gateway IP, XXX should be no mask. */ 577 npfctl_parse_cidr(gwip, &addr, &mask); 578 prop_dictionary_set(rl, "gateway_ip", prop_number_create_integer(addr)); 579 } 580