1 /* $NetBSD: npf_data.c,v 1.7 2011/02/02 02:20:25 rmind 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.7 2011/02/02 02:20:25 rmind 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) 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 != AF_INET && 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_v4mask(char *ostr, in_addr_t *addr, in_addr_t *mask) 109 { 110 char *str = xstrdup(ostr); 111 char *p = strchr(str, '/'); 112 u_int bits; 113 bool ret; 114 115 /* In network byte order. */ 116 if (p) { 117 *p++ = '\0'; 118 bits = (u_int)atoi(p); 119 *mask = bits ? htonl(0xffffffff << (32 - bits)) : 0; 120 } else { 121 *mask = 0xffffffff; 122 } 123 ret = inet_aton(str, (struct in_addr *)addr) != 0; 124 free(str); 125 return ret; 126 } 127 128 bool 129 npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport) 130 { 131 char *str = xstrdup(ostr), *sep; 132 133 *range = false; 134 if ((sep = strchr(str, ':')) != NULL) { 135 /* Port range (only numeric). */ 136 *range = true; 137 *sep = '\0'; 138 139 } else if (isalpha((unsigned char)*str)) { 140 struct servent *se; 141 142 se = getservbyname(str, NULL); 143 if (se == NULL) { 144 free(str); 145 return false; 146 } 147 *fport = se->s_port; 148 } else { 149 *fport = htons(atoi(str)); 150 } 151 *tport = sep ? htons(atoi(sep + 1)) : *fport; 152 free(str); 153 return true; 154 } 155 156 void 157 npfctl_parse_cidr(char *str, in_addr_t *addr, in_addr_t *mask) 158 { 159 160 if (strcmp(str, "any") == 0) { 161 *addr = 0x0; 162 *mask = 0x0; 163 164 } else if (isalpha((unsigned char)*str)) { 165 struct ifaddrs *ifa; 166 struct sockaddr_in *sin; 167 u_int idx; 168 169 if ((ifa = npfctl_getif(str, &idx, true)) == NULL) { 170 errx(EXIT_FAILURE, "invalid interface '%s'", str); 171 } 172 /* Interface address. */ 173 sin = (struct sockaddr_in *)ifa->ifa_addr; 174 *addr = sin->sin_addr.s_addr; 175 *mask = 0xffffffff; 176 177 } else if (!npfctl_parse_v4mask(str, addr, mask)) { 178 errx(EXIT_FAILURE, "invalid CIDR '%s'\n", str); 179 } 180 } 181 182 static bool 183 npfctl_parse_tcpfl(char *s, uint8_t *tfl, uint8_t *tfl_mask) 184 { 185 uint8_t tcpfl = 0; 186 bool mask = false; 187 188 while (*s) { 189 switch (*s) { 190 case 'F': tcpfl |= TH_FIN; break; 191 case 'S': tcpfl |= TH_SYN; break; 192 case 'R': tcpfl |= TH_RST; break; 193 case 'P': tcpfl |= TH_PUSH; break; 194 case 'A': tcpfl |= TH_ACK; break; 195 case 'U': tcpfl |= TH_URG; break; 196 case 'E': tcpfl |= TH_ECE; break; 197 case 'W': tcpfl |= TH_CWR; break; 198 case '/': 199 *s = '\0'; 200 *tfl = tcpfl; 201 tcpfl = 0; 202 mask = true; 203 break; 204 default: 205 return false; 206 } 207 s++; 208 } 209 if (!mask) { 210 *tfl = tcpfl; 211 } 212 *tfl_mask = tcpfl; 213 return true; 214 } 215 216 void 217 npfctl_fill_table(nl_table_t *tl, char *fname) 218 { 219 char *buf; 220 FILE *fp; 221 size_t n; 222 int l; 223 224 fp = fopen(fname, "r"); 225 if (fp == NULL) { 226 err(EXIT_FAILURE, "open '%s'", fname); 227 } 228 l = 1; 229 buf = NULL; 230 while (getline(&buf, &n, fp) != -1) { 231 in_addr_t addr, mask; 232 233 if (*buf == '\n' || *buf == '#') 234 continue; 235 236 /* IPv4 CIDR: a.b.c.d/mask */ 237 if (!npfctl_parse_v4mask(buf, &addr, &mask)) { 238 errx(EXIT_FAILURE, "invalid table entry at line %d", l); 239 } 240 241 /* Create and add table entry. */ 242 npf_table_add_entry(tl, addr, mask); 243 l++; 244 } 245 if (buf != NULL) { 246 free(buf); 247 } 248 } 249 250 /* 251 * N-code generation helpers. 252 */ 253 254 static void 255 npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd) 256 { 257 element_t *el = dat->v_elements; 258 int foff; 259 260 /* If table, generate a single table matching block. */ 261 if (dat->v_type == VAR_TABLE) { 262 u_int tid = atoi(el->e_data); 263 264 nblocks[0]--; 265 foff = npfctl_failure_offset(nblocks); 266 npfctl_gennc_tbl(nc, foff, tid, sd); 267 return; 268 } 269 270 /* Generate v4 CIDR matching blocks. */ 271 for (el = dat->v_elements; el != NULL; el = el->e_next) { 272 in_addr_t addr, mask; 273 274 npfctl_parse_cidr(el->e_data, &addr, &mask); 275 276 nblocks[1]--; 277 foff = npfctl_failure_offset(nblocks); 278 npfctl_gennc_v4cidr(nc, foff, addr, mask, sd); 279 } 280 } 281 282 static void 283 npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp, 284 bool both, bool sd) 285 { 286 element_t *el = dat->v_elements; 287 int foff; 288 289 assert(dat->v_type != VAR_TABLE); 290 291 /* Generate TCP/UDP port matching blocks. */ 292 for (el = dat->v_elements; el != NULL; el = el->e_next) { 293 in_port_t fport, tport; 294 bool range; 295 296 if (!npfctl_parse_port(el->e_data, &range, &fport, &tport)) { 297 errx(EXIT_FAILURE, "invalid service '%s'", el->e_data); 298 } 299 nblocks[0]--; 300 foff = both ? 0 : npfctl_failure_offset(nblocks); 301 npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd); 302 } 303 } 304 305 static void 306 npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports, 307 bool both, bool tcpudp, bool sd) 308 { 309 310 npfctl_rulenc_v4cidr(nc, nblocks, cidr, sd); 311 if (ports == NULL) { 312 return; 313 } 314 npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd); 315 if (!both) { 316 return; 317 } 318 npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd); 319 } 320 321 void 322 npfctl_rule_ncode(nl_rule_t *rl, char *proto, char *tcpfl, int icmp_type, 323 int icmp_code, var_t *from, var_t *fports, var_t *to, var_t *tports) 324 { 325 int nblocks[3] = { 0, 0, 0 }; 326 bool icmp, tcpudp, both; 327 void *ncptr, *nc; 328 size_t sz, foff; 329 330 /* 331 * Default: both TCP and UDP. 332 */ 333 icmp = false; 334 tcpudp = true; 335 if (proto == NULL) { 336 both = true; 337 goto skip_proto; 338 } 339 both = false; 340 341 if (strcmp(proto, "icmp") == 0) { 342 /* ICMP case. */ 343 fports = NULL; 344 tports = NULL; 345 icmp = true; 346 347 } else if (strcmp(proto, "tcp") == 0) { 348 /* Just TCP. */ 349 tcpudp = true; 350 351 } else if (strcmp(proto, "udp") == 0) { 352 /* Just UDP. */ 353 tcpudp = false; 354 355 } else { 356 /* Default. */ 357 } 358 skip_proto: 359 if (icmp || icmp_type != -1) { 360 assert(tcpfl == NULL); 361 icmp = true; 362 nblocks[2] += 1; 363 } 364 if (tcpudp && tcpfl) { 365 assert(icmp_type == -1 && icmp_code == -1); 366 nblocks[2] += 1; 367 } 368 369 /* Calculate how blocks to determince n-code. */ 370 if (from && from->v_count) { 371 if (from->v_type == VAR_TABLE) 372 nblocks[0] += 1; 373 else 374 nblocks[1] += from->v_count; 375 if (fports && fports->v_count) 376 nblocks[0] += fports->v_count * (both ? 2 : 1); 377 } 378 if (to && to->v_count) { 379 if (to->v_type == VAR_TABLE) 380 nblocks[0] += 1; 381 else 382 nblocks[1] += to->v_count; 383 if (tports && tports->v_count) 384 nblocks[0] += tports->v_count * (both ? 2 : 1); 385 } 386 387 /* Any n-code to generate? */ 388 if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2]) == 0) { 389 /* Done, if none. */ 390 return; 391 } 392 393 /* Allocate memory for the n-code. */ 394 sz = npfctl_calc_ncsize(nblocks); 395 ncptr = malloc(sz); 396 if (ncptr == NULL) { 397 err(EXIT_FAILURE, "malloc"); 398 } 399 nc = ncptr; 400 401 /* 402 * Generate v4 CIDR matching blocks and TCP/UDP port matching. 403 */ 404 if (from) { 405 npfctl_rulenc_block(&nc, nblocks, from, fports, 406 both, tcpudp, true); 407 } 408 if (to) { 409 npfctl_rulenc_block(&nc, nblocks, to, tports, 410 both, tcpudp, false); 411 } 412 413 if (icmp) { 414 /* 415 * ICMP case. 416 */ 417 nblocks[2]--; 418 foff = npfctl_failure_offset(nblocks); 419 npfctl_gennc_icmp(&nc, foff, icmp_type, icmp_code); 420 421 } else if (tcpudp && tcpfl) { 422 /* 423 * TCP case, flags. 424 */ 425 uint8_t tfl = 0, tfl_mask; 426 427 nblocks[2]--; 428 foff = npfctl_failure_offset(nblocks); 429 if (!npfctl_parse_tcpfl(tcpfl, &tfl, &tfl_mask)) { 430 errx(EXIT_FAILURE, "invalid TCP flags '%s'", tcpfl); 431 } 432 npfctl_gennc_tcpfl(&nc, foff, tfl, tfl_mask); 433 } 434 npfctl_gennc_complete(&nc); 435 436 if ((uintptr_t)nc - (uintptr_t)ncptr != sz) { 437 errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)", 438 (uintptr_t)nc - (uintptr_t)ncptr, sz); 439 } 440 441 #ifdef DEBUG 442 uint32_t *op = ncptr; 443 size_t n = sz; 444 do { 445 DPRINTF(("\t> |0x%02x|\n", (u_int)*op)); 446 op++; 447 n -= sizeof(*op); 448 } while (n); 449 #endif 450 451 /* Create a final memory block of data, ready to send. */ 452 if (npf_rule_setcode(rl, NPF_CODE_NCODE, ncptr, sz) == -1) { 453 errx(EXIT_FAILURE, "npf_rule_setcode"); 454 } 455 free(ncptr); 456 } 457