1 /* $NetBSD: npf_data.c,v 1.9 2011/11/05 19:19:29 jakllsch Exp $ */ 2 3 /*- 4 * Copyright (c) 2009-2011 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) helper routines. 31 */ 32 33 #include <sys/cdefs.h> 34 __RCSID("$NetBSD: npf_data.c,v 1.9 2011/11/05 19:19:29 jakllsch Exp $"); 35 36 #include <sys/types.h> 37 #include <sys/socket.h> 38 #include <sys/ioctl.h> 39 #include <net/if.h> 40 #include <netinet/tcp.h> 41 42 #include <arpa/inet.h> 43 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <ctype.h> 48 #include <err.h> 49 #include <ifaddrs.h> 50 #include <netdb.h> 51 #include <assert.h> 52 53 #include "npfctl.h" 54 55 static struct ifaddrs * ifs_list = NULL; 56 nl_config_t * npf_conf = NULL; 57 58 void 59 npfctl_init_data(void) 60 { 61 62 npf_conf = npf_config_create(); 63 if (npf_conf == NULL) { 64 errx(EXIT_FAILURE, "npf_config_create"); 65 } 66 if (getifaddrs(&ifs_list) == -1) { 67 err(EXIT_FAILURE, "getifaddrs"); 68 } 69 } 70 71 int 72 npfctl_ioctl_send(int fd) 73 { 74 int error = npf_config_submit(npf_conf, fd); 75 npf_config_destroy(npf_conf); 76 return error; 77 } 78 79 /* 80 * Helper routines: 81 * 82 * npfctl_getif() - get interface addresses and index number from name. 83 * npfctl_parse_v4mask() - parse address/mask integers from CIDR block. 84 * npfctl_parse_port() - parse port number (which may be a service name). 85 * npfctl_parse_tcpfl() - parse TCP flags. 86 */ 87 88 struct ifaddrs * 89 npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr, sa_family_t addrtype) 90 { 91 struct ifaddrs *ifent; 92 struct sockaddr_in *sin; 93 94 for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) { 95 sin = (struct sockaddr_in *)ifent->ifa_addr; 96 if (sin->sin_family != addrtype && reqaddr) 97 continue; 98 if (strcmp(ifent->ifa_name, ifname) == 0) 99 break; 100 } 101 if (ifent) { 102 *if_idx = if_nametoindex(ifname); 103 } 104 return ifent; 105 } 106 107 bool 108 npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport) 109 { 110 char *str = xstrdup(ostr), *sep; 111 112 *range = false; 113 if ((sep = strchr(str, ':')) != NULL) { 114 /* Port range (only numeric). */ 115 *range = true; 116 *sep = '\0'; 117 118 } else if (isalpha((unsigned char)*str)) { 119 struct servent *se; 120 121 se = getservbyname(str, NULL); 122 if (se == NULL) { 123 free(str); 124 return false; 125 } 126 *fport = se->s_port; 127 } else { 128 *fport = htons(atoi(str)); 129 } 130 *tport = sep ? htons(atoi(sep + 1)) : *fport; 131 free(str); 132 return true; 133 } 134 135 void 136 npfctl_create_mask(sa_family_t family, u_int length, npf_addr_t *omask) 137 { 138 uint32_t part; 139 uint32_t *mask = (uint32_t*)omask; 140 141 memset(omask, 0, sizeof(npf_addr_t)); 142 if (family == AF_INET) { 143 part = htonl(0xffffffff << (32 - length)); 144 memcpy(mask, &part, 4); 145 } else if (family == AF_INET6) { 146 while (length > 32) { 147 part = htonl(0xffffffff); 148 memcpy(mask, &part, 4); 149 mask += 1; 150 length -= 32; 151 } 152 part = htonl(0xffffffff << (32 - length)); 153 memcpy(mask, &part, 4); 154 } 155 } 156 157 sa_family_t 158 npfctl_get_addrfamily(const char *ostr) 159 { 160 struct addrinfo hint, *res = NULL; 161 int ret; 162 char *str = xstrdup(ostr); 163 char *p = strchr(str, '/'); 164 sa_family_t family; 165 166 if (p) 167 *p = '\0'; 168 memset(&hint, '\0', sizeof(hint)); 169 hint.ai_family = PF_UNSPEC; 170 hint.ai_flags = AI_NUMERICHOST; 171 ret = getaddrinfo(str, NULL, &hint, &res); 172 if (ret) { 173 family = AF_UNSPEC; 174 } else { 175 family = res->ai_family; 176 freeaddrinfo(res); 177 } 178 free(str); 179 return family; 180 } 181 182 sa_family_t 183 npfctl_parse_cidr(char *str, sa_family_t addrfamily, npf_addr_t *addr, npf_netmask_t *mask) 184 { 185 186 if (strcmp(str, "any") == 0) { 187 memset(addr, 0, sizeof(npf_addr_t)); 188 memset(mask, 0, sizeof(npf_netmask_t)); 189 } else if (isalpha((unsigned char)*str)) { 190 /* TODO: handle multiple addresses per interface */ 191 struct ifaddrs *ifa; 192 struct sockaddr_in *sin; 193 u_int idx; 194 if ((ifa = npfctl_getif(str, &idx, true, AF_INET)) == NULL) { 195 errx(EXIT_FAILURE, "invalid interface '%s'", str); 196 } 197 /* Interface address. */ 198 sin = (struct sockaddr_in *)ifa->ifa_addr; 199 memcpy(addr, &(sin->sin_addr.s_addr), sizeof(struct in_addr)); 200 //v4mask = 0xffffffff; - TODO! 201 } else { 202 char *p = strchr(str, '/'); 203 if (p != NULL) { 204 *p++ = '\0'; 205 *mask = atoi(p); 206 } else { 207 if (addrfamily == AF_INET) 208 *mask = 32; 209 else 210 *mask = 128; 211 } 212 memset(addr, 0, sizeof(npf_addr_t)); 213 int ret = inet_pton(addrfamily, str, addr); 214 if (ret != 1) { 215 printf("TODO: error"); 216 } 217 } 218 219 return addrfamily; 220 } 221 222 static bool 223 npfctl_parse_tcpfl(char *s, uint8_t *tfl, uint8_t *tfl_mask) 224 { 225 uint8_t tcpfl = 0; 226 bool mask = false; 227 228 while (*s) { 229 switch (*s) { 230 case 'F': tcpfl |= TH_FIN; break; 231 case 'S': tcpfl |= TH_SYN; break; 232 case 'R': tcpfl |= TH_RST; break; 233 case 'P': tcpfl |= TH_PUSH; break; 234 case 'A': tcpfl |= TH_ACK; break; 235 case 'U': tcpfl |= TH_URG; break; 236 case 'E': tcpfl |= TH_ECE; break; 237 case 'W': tcpfl |= TH_CWR; break; 238 case '/': 239 *s = '\0'; 240 *tfl = tcpfl; 241 tcpfl = 0; 242 mask = true; 243 break; 244 default: 245 return false; 246 } 247 s++; 248 } 249 if (!mask) { 250 *tfl = tcpfl; 251 } 252 *tfl_mask = tcpfl; 253 return true; 254 } 255 256 void 257 npfctl_fill_table(nl_table_t *tl, char *fname) 258 { 259 char *buf; 260 FILE *fp; 261 size_t n; 262 int l; 263 264 fp = fopen(fname, "r"); 265 if (fp == NULL) { 266 err(EXIT_FAILURE, "open '%s'", fname); 267 } 268 l = 1; 269 buf = NULL; 270 while (getline(&buf, &n, fp) != -1) { 271 npf_addr_t addr; 272 npf_netmask_t mask; 273 274 if (*buf == '\n' || *buf == '#') 275 continue; 276 277 if (!npfctl_parse_cidr(buf, npfctl_get_addrfamily(buf), &addr, &mask)) { 278 errx(EXIT_FAILURE, "invalid table entry at line %d", l); 279 } 280 281 /* Create and add table entry. */ 282 npf_table_add_entry(tl, &addr, mask); 283 l++; 284 } 285 if (buf != NULL) { 286 free(buf); 287 } 288 } 289 290 /* 291 * N-code generation helpers. 292 */ 293 294 static void 295 npfctl_rulenc_cidr(void **nc, int nblocks[], var_t *dat, bool sd, sa_family_t addrfamily) 296 { 297 element_t *el = dat->v_elements; 298 int foff; 299 300 /* If table, generate a single table matching block. */ 301 if (dat->v_type == VAR_TABLE) { 302 u_int tid = atoi(el->e_data); 303 304 nblocks[0]--; 305 foff = npfctl_failure_offset(nblocks); 306 npfctl_gennc_tbl(nc, foff, tid, sd); 307 return; 308 } 309 310 /* Generate v4/v6 CIDR matching blocks. */ 311 for (el = dat->v_elements; el != NULL; el = el->e_next) { 312 npf_addr_t addr; 313 npf_netmask_t mask; 314 315 npfctl_parse_cidr(el->e_data, addrfamily, &addr, &mask); 316 if (addrfamily == AF_INET) 317 nblocks[1]--; 318 else if (addrfamily == AF_INET6) 319 nblocks[3]--; 320 foff = npfctl_failure_offset(nblocks); 321 if (addrfamily == AF_INET) 322 npfctl_gennc_v4cidr(nc, foff, &addr, mask, sd); 323 else if (addrfamily == AF_INET6) 324 npfctl_gennc_v6cidr(nc, foff, &addr, mask, sd); 325 } 326 } 327 328 static void 329 npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp, 330 bool both, bool sd) 331 { 332 element_t *el = dat->v_elements; 333 int foff; 334 335 assert(dat->v_type != VAR_TABLE); 336 337 /* Generate TCP/UDP port matching blocks. */ 338 for (el = dat->v_elements; el != NULL; el = el->e_next) { 339 in_port_t fport, tport; 340 bool range; 341 342 if (!npfctl_parse_port(el->e_data, &range, &fport, &tport)) { 343 errx(EXIT_FAILURE, "invalid service '%s'", el->e_data); 344 } 345 nblocks[0]--; 346 foff = both ? 0 : npfctl_failure_offset(nblocks); 347 npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd); 348 } 349 } 350 351 static void 352 npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports, 353 bool both, bool tcpudp, bool sd, sa_family_t addrfamily) 354 { 355 356 npfctl_rulenc_cidr(nc, nblocks, cidr, sd, addrfamily); 357 if (ports == NULL) { 358 return; 359 } 360 npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd); 361 if (!both) { 362 return; 363 } 364 npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd); 365 } 366 367 void 368 npfctl_rule_ncode(nl_rule_t *rl, char *proto, char *tcpfl, int icmp_type, 369 int icmp_code, var_t *from, sa_family_t addrfamily, var_t *fports, var_t *to, var_t *tports) 370 { 371 int nblocks[4] = { 0, 0, 0, 0 }; 372 bool icmp, tcpudp, both; 373 void *ncptr, *nc; 374 size_t sz, foff; 375 376 /* 377 * Default: both TCP and UDP. 378 */ 379 icmp = false; 380 tcpudp = true; 381 if (proto == NULL) { 382 both = true; 383 goto skip_proto; 384 } 385 both = false; 386 387 if (strcmp(proto, "icmp") == 0) { 388 /* ICMP case. */ 389 fports = NULL; 390 tports = NULL; 391 icmp = true; 392 393 } else if (strcmp(proto, "tcp") == 0) { 394 /* Just TCP. */ 395 tcpudp = true; 396 397 } else if (strcmp(proto, "udp") == 0) { 398 /* Just UDP. */ 399 tcpudp = false; 400 401 } else { 402 /* Default. */ 403 } 404 skip_proto: 405 if (icmp || icmp_type != -1) { 406 assert(tcpfl == NULL); 407 icmp = true; 408 nblocks[2] += 1; 409 } 410 if (tcpudp && tcpfl) { 411 assert(icmp_type == -1 && icmp_code == -1); 412 nblocks[2] += 1; 413 } 414 415 /* Calculate how blocks to determince n-code. */ 416 if (from && from->v_count) { 417 if (from->v_type == VAR_TABLE) 418 nblocks[0] += 1; 419 else { 420 if (addrfamily == AF_INET) 421 nblocks[1] += from->v_count; 422 else 423 nblocks[3] += from->v_count; 424 } 425 if (fports && fports->v_count) 426 nblocks[0] += fports->v_count * (both ? 2 : 1); 427 } 428 if (to && to->v_count) { 429 if (to->v_type == VAR_TABLE) 430 nblocks[0] += 1; 431 else { 432 if (addrfamily == AF_INET) 433 nblocks[1] += to->v_count; 434 else 435 nblocks[3] += to->v_count; 436 } 437 if (tports && tports->v_count) 438 nblocks[0] += tports->v_count * (both ? 2 : 1); 439 } 440 441 /* Any n-code to generate? */ 442 if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2] + nblocks[3]) == 0) { 443 /* Done, if none. */ 444 return; 445 } 446 447 /* Allocate memory for the n-code. */ 448 sz = npfctl_calc_ncsize(nblocks); 449 ncptr = malloc(sz); 450 if (ncptr == NULL) { 451 err(EXIT_FAILURE, "malloc"); 452 } 453 nc = ncptr; 454 455 /* 456 * Generate v4/v6 CIDR matching blocks and TCP/UDP port matching. 457 */ 458 if (from) { 459 npfctl_rulenc_block(&nc, nblocks, from, fports, 460 both, tcpudp, true, addrfamily); 461 } 462 if (to) { 463 npfctl_rulenc_block(&nc, nblocks, to, tports, 464 both, tcpudp, false, addrfamily); 465 } 466 467 if (icmp) { 468 /* 469 * ICMP case. 470 */ 471 nblocks[2]--; 472 foff = npfctl_failure_offset(nblocks); 473 npfctl_gennc_icmp(&nc, foff, icmp_type, icmp_code); 474 475 } else if (tcpudp && tcpfl) { 476 /* 477 * TCP case, flags. 478 */ 479 uint8_t tfl = 0, tfl_mask; 480 481 nblocks[2]--; 482 foff = npfctl_failure_offset(nblocks); 483 if (!npfctl_parse_tcpfl(tcpfl, &tfl, &tfl_mask)) { 484 errx(EXIT_FAILURE, "invalid TCP flags '%s'", tcpfl); 485 } 486 npfctl_gennc_tcpfl(&nc, foff, tfl, tfl_mask); 487 } 488 npfctl_gennc_complete(&nc); 489 490 if ((uintptr_t)nc - (uintptr_t)ncptr != sz) { 491 errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)", 492 (uintptr_t)nc - (uintptr_t)ncptr, sz); 493 } 494 495 #ifdef DEBUG 496 uint32_t *op = ncptr; 497 size_t n = sz; 498 do { 499 DPRINTF(("\t> |0x%02x|\n", (u_int)*op)); 500 op++; 501 n -= sizeof(*op); 502 } while (n); 503 #endif 504 505 /* Create a final memory block of data, ready to send. */ 506 if (npf_rule_setcode(rl, NPF_CODE_NCODE, ncptr, sz) == -1) { 507 errx(EXIT_FAILURE, "npf_rule_setcode"); 508 } 509 free(ncptr); 510 } 511