1 /* $NetBSD: npf_show.c,v 1.19 2015/06/03 23:36:05 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * NPF configuration printing. 34 * 35 * Each rule having BPF byte-code has a binary description. 36 */ 37 38 #include <sys/cdefs.h> 39 __RCSID("$NetBSD: npf_show.c,v 1.19 2015/06/03 23:36:05 rmind Exp $"); 40 41 #include <sys/socket.h> 42 #include <netinet/in.h> 43 #include <netinet/tcp.h> 44 #include <net/if.h> 45 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <stdbool.h> 50 #include <inttypes.h> 51 #include <errno.h> 52 #include <err.h> 53 54 #include "npfctl.h" 55 56 #define SEEN_SRC 0x01 57 #define SEEN_DST 0x02 58 59 typedef struct { 60 nl_config_t * conf; 61 FILE * fp; 62 long fpos; 63 u_int flags; 64 uint32_t curmark; 65 } npf_conf_info_t; 66 67 static npf_conf_info_t stdout_ctx = { 68 .fp = stdout, 69 .fpos = 0, 70 .flags = 0 71 }; 72 73 static void print_indent(npf_conf_info_t *, u_int); 74 static void print_linesep(npf_conf_info_t *); 75 76 /* 77 * Helper routines to print various pieces of information. 78 */ 79 80 static void 81 print_indent(npf_conf_info_t *ctx, u_int level) 82 { 83 if (level == 0) { /* XXX */ 84 print_linesep(ctx); 85 } 86 while (level--) 87 fprintf(ctx->fp, "\t"); 88 } 89 90 static void 91 print_linesep(npf_conf_info_t *ctx) 92 { 93 if (ftell(ctx->fp) != ctx->fpos) { 94 fputs("\n", ctx->fp); 95 ctx->fpos = ftell(ctx->fp); 96 } 97 } 98 99 static size_t 100 tcpflags2string(char *buf, u_int tfl) 101 { 102 u_int i = 0; 103 104 if (tfl & TH_FIN) buf[i++] = 'F'; 105 if (tfl & TH_SYN) buf[i++] = 'S'; 106 if (tfl & TH_RST) buf[i++] = 'R'; 107 if (tfl & TH_PUSH) buf[i++] = 'P'; 108 if (tfl & TH_ACK) buf[i++] = 'A'; 109 if (tfl & TH_URG) buf[i++] = 'U'; 110 if (tfl & TH_ECE) buf[i++] = 'E'; 111 if (tfl & TH_CWR) buf[i++] = 'C'; 112 buf[i] = '\0'; 113 return i; 114 } 115 116 static char * 117 print_family(npf_conf_info_t *ctx, const uint32_t *words) 118 { 119 const int af = words[0]; 120 121 switch (af) { 122 case AF_INET: 123 return estrdup("inet4"); 124 case AF_INET6: 125 return estrdup("inet6"); 126 default: 127 errx(EXIT_FAILURE, "invalid byte-code mark (family)"); 128 } 129 return NULL; 130 } 131 132 static char * 133 print_address(npf_conf_info_t *ctx, const uint32_t *words) 134 { 135 const int af = *words++; 136 const u_int mask = *words++; 137 const npf_addr_t *addr; 138 int alen = 0; 139 140 switch (af) { 141 case AF_INET: 142 alen = 4; 143 break; 144 case AF_INET6: 145 alen = 16; 146 break; 147 default: 148 errx(EXIT_FAILURE, "invalid byte-code mark (address)"); 149 } 150 addr = (const npf_addr_t *)words; 151 return npfctl_print_addrmask(alen, addr, mask); 152 } 153 154 static char * 155 print_number(npf_conf_info_t *ctx, const uint32_t *words) 156 { 157 char *p; 158 easprintf(&p, "%u", words[0]); 159 return p; 160 } 161 162 static char * 163 print_table(npf_conf_info_t *ctx, const uint32_t *words) 164 { 165 unsigned tid = words[0]; 166 nl_table_t *tl; 167 char *p = NULL; 168 169 /* XXX: Iterating all as we need to rewind for the next call. */ 170 while ((tl = npf_table_iterate(ctx->conf)) != NULL) { 171 if (!p && npf_table_getid(tl) == tid) { 172 easprintf(&p, "%s", npf_table_getname(tl)); 173 } 174 } 175 assert(p != NULL); 176 return p; 177 } 178 179 static char * 180 print_proto(npf_conf_info_t *ctx, const uint32_t *words) 181 { 182 switch (words[0]) { 183 case IPPROTO_TCP: 184 return estrdup("tcp"); 185 case IPPROTO_UDP: 186 return estrdup("udp"); 187 case IPPROTO_ICMP: 188 return estrdup("icmp"); 189 case IPPROTO_ICMPV6: 190 return estrdup("ipv6-icmp"); 191 } 192 return print_number(ctx, words); 193 } 194 195 static char * 196 print_tcpflags(npf_conf_info_t *ctx, const uint32_t *words) 197 { 198 const u_int tf = words[0], tf_mask = words[1]; 199 char buf[16]; 200 201 size_t n = tcpflags2string(buf, tf); 202 if (tf != tf_mask) { 203 buf[n++] = '/'; 204 tcpflags2string(buf + n, tf_mask); 205 } 206 return estrdup(buf); 207 } 208 209 static char * 210 print_portrange(npf_conf_info_t *ctx, const uint32_t *words) 211 { 212 u_int fport = words[0], tport = words[1]; 213 const char *any_str = ""; 214 char *p; 215 216 if (ctx->curmark == BM_SRC_PORTS && (ctx->flags & SEEN_SRC) == 0) 217 any_str = "from any "; 218 if (ctx->curmark == BM_DST_PORTS && (ctx->flags & SEEN_DST) == 0) 219 any_str = "to any "; 220 221 if (fport != tport) { 222 easprintf(&p, "%sport %u:%u", any_str, fport, tport); 223 } else { 224 easprintf(&p, "%sport %u", any_str, fport); 225 } 226 return p; 227 } 228 229 /* 230 * The main keyword mapping tables defining the syntax: 231 * - Mapping of rule attributes (flags) to the keywords. 232 * - Mapping of the byte-code marks to the keywords. 233 */ 234 235 #define F(name) __CONCAT(NPF_RULE_, name) 236 #define STATEFUL_ENDS (NPF_RULE_STATEFUL | NPF_RULE_MULTIENDS) 237 #define NAME_AT 2 238 239 static const struct attr_keyword_mapent { 240 uint32_t mask; 241 uint32_t flags; 242 const char * val; 243 } attr_keyword_map[] = { 244 { F(GROUP)|F(DYNAMIC), F(GROUP), "group" }, 245 { F(DYNAMIC), F(DYNAMIC), "ruleset" }, 246 { F(GROUP)|F(PASS), 0, "block" }, 247 { F(GROUP)|F(PASS), F(PASS), "pass" }, 248 { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" }, 249 { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" }, 250 { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" }, 251 { STATEFUL_ENDS, F(STATEFUL), "stateful" }, 252 { STATEFUL_ENDS, STATEFUL_ENDS, "stateful-ends" }, 253 { F(DIMASK), F(IN), "in" }, 254 { F(DIMASK), F(OUT), "out" }, 255 { F(FINAL), F(FINAL), "final" }, 256 }; 257 258 static const struct mark_keyword_mapent { 259 u_int mark; 260 const char * token; 261 const char * sep; 262 u_int set_flags; 263 char * (*printfn)(npf_conf_info_t *, const uint32_t *); 264 u_int fwords; 265 } mark_keyword_map[] = { 266 { BM_IPVER, "family %s", NULL, 0, print_family, 1 }, 267 { BM_PROTO, "proto %s", ", ", 0, print_proto, 1 }, 268 { BM_TCPFL, "flags %s", NULL, 0, print_tcpflags, 2 }, 269 { BM_ICMP_TYPE, "icmp-type %s", NULL, 0, print_number, 1 }, 270 { BM_ICMP_CODE, "code %s", NULL, 0, print_number, 1 }, 271 272 { BM_SRC_CIDR, "from %s", ", ", SEEN_SRC, print_address, 6 }, 273 { BM_SRC_TABLE, "from <%s>", NULL, SEEN_SRC, print_table, 1 }, 274 { BM_SRC_PORTS, "%s", ", ", 0, print_portrange,2 }, 275 276 { BM_DST_CIDR, "to %s", ", ", SEEN_DST, print_address, 6 }, 277 { BM_DST_TABLE, "to <%s>", NULL, SEEN_DST, print_table, 1 }, 278 { BM_DST_PORTS, "%s", ", ", 0, print_portrange,2 }, 279 }; 280 281 static const char * __attribute__((format_arg(2))) 282 verified_fmt(const char *fmt, const char *t __unused) 283 { 284 return fmt; 285 } 286 287 static char * 288 scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk, 289 const uint32_t *marks, size_t mlen) 290 { 291 char buf[2048], *vals[256], *p; 292 size_t nvals = 0; 293 294 /* Scan for the marks and extract the values. */ 295 mlen /= sizeof(uint32_t); 296 while (mlen > 2) { 297 const uint32_t m = *marks++; 298 const u_int nwords = *marks++; 299 300 if ((mlen -= 2) < nwords) { 301 errx(EXIT_FAILURE, "byte-code marking inconsistency"); 302 } 303 if (m == mk->mark) { 304 /* Set the current mark and the flags. */ 305 ctx->flags |= mk->set_flags; 306 ctx->curmark = m; 307 308 /* Value is processed by the print function. */ 309 assert(mk->fwords == nwords); 310 vals[nvals++] = mk->printfn(ctx, marks); 311 } 312 marks += nwords; 313 mlen -= nwords; 314 } 315 if (nvals == 0) { 316 return NULL; 317 } 318 assert(nvals == 1 || mk->sep != NULL); 319 320 /* 321 * Join all the values and print. Add curly brackets if there 322 * is more than value and it can be a set. 323 */ 324 if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) { 325 errx(EXIT_FAILURE, "out of memory while parsing the rule"); 326 } 327 easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf); 328 329 for (u_int i = 0; i < nvals; i++) { 330 free(vals[i]); 331 } 332 return p; 333 } 334 335 static void 336 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl) 337 { 338 const void *marks; 339 size_t mlen, len; 340 const void *code; 341 int type; 342 343 marks = npf_rule_getinfo(rl, &mlen); 344 if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) { 345 /* 346 * No marks, but the byte-code is present. This must 347 * have been filled by libpcap(3) or possibly an unknown 348 * to us byte-code. 349 */ 350 fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ? 351 "pcap-filter \"...\"" : "unrecognized-bytecode"); 352 return; 353 } 354 ctx->flags = 0; 355 356 /* 357 * BPF filter criteria described by the byte-code marks. 358 */ 359 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) { 360 const struct mark_keyword_mapent *mk = &mark_keyword_map[i]; 361 char *val; 362 363 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) { 364 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val); 365 fputs(" ", ctx->fp); 366 free(val); 367 } 368 } 369 if (!mlen) { 370 fputs("all ", ctx->fp); 371 } 372 } 373 374 static void 375 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl) 376 { 377 const uint32_t attr = npf_rule_getattr(rl); 378 const char *rproc, *ifname, *name; 379 380 /* Rule attributes/flags. */ 381 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) { 382 const struct attr_keyword_mapent *ak = &attr_keyword_map[i]; 383 384 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) { 385 fprintf(ctx->fp, "\"%s\" ", name); 386 } 387 if ((attr & ak->mask) == ak->flags) { 388 fprintf(ctx->fp, "%s ", ak->val); 389 } 390 } 391 if ((ifname = npf_rule_getinterface(rl)) != NULL) { 392 fprintf(ctx->fp, "on %s ", ifname); 393 } 394 395 if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) { 396 /* Group; done. */ 397 fputs("\n", ctx->fp); 398 return; 399 } 400 401 /* Print filter criteria. */ 402 npfctl_print_filter(ctx, rl); 403 404 /* Rule procedure. */ 405 if ((rproc = npf_rule_getproc(rl)) != NULL) { 406 fprintf(ctx->fp, "apply \"%s\" ", rproc); 407 } 408 409 /* If dynamic rule - print its ID. */ 410 if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_DYNAMIC) { 411 uint64_t id = npf_rule_getid(rl); 412 fprintf(ctx->fp, "# id = \"%" PRIx64 "\" ", id); 413 } 414 415 fputs("\n", ctx->fp); 416 } 417 418 static void 419 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt) 420 { 421 nl_rule_t *rl = (nl_nat_t *)nt; 422 const char *ifname, *seg1, *seg2, *arrow; 423 npf_addr_t addr; 424 in_port_t port; 425 size_t alen; 426 u_int flags; 427 char *seg; 428 429 /* Get the interface. */ 430 ifname = npf_rule_getinterface(rl); 431 assert(ifname != NULL); 432 433 /* Get the translation address (and port, if used). */ 434 npf_nat_getmap(nt, &addr, &alen, &port); 435 seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK); 436 if (port) { 437 char *p; 438 easprintf(&p, "%s port %u", seg, ntohs(port)); 439 free(seg), seg = p; 440 } 441 seg1 = seg2 = "any"; 442 443 /* Get the NAT type and determine the translation segment. */ 444 switch (npf_nat_gettype(nt)) { 445 case NPF_NATIN: 446 arrow = "<-"; 447 seg1 = seg; 448 break; 449 case NPF_NATOUT: 450 arrow = "->"; 451 seg2 = seg; 452 break; 453 default: 454 abort(); 455 } 456 flags = npf_nat_getflags(nt); 457 458 /* Print out the NAT policy with the filter criteria. */ 459 fprintf(ctx->fp, "map %s %s %s %s %s pass ", 460 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic", 461 seg1, arrow, seg2); 462 npfctl_print_filter(ctx, rl); 463 fputs("\n", ctx->fp); 464 free(seg); 465 } 466 467 static void 468 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl) 469 { 470 const char *name = npf_table_getname(tl); 471 const unsigned type = npf_table_gettype(tl); 472 const char *table_types[] = { 473 [NPF_TABLE_HASH] = "hash", 474 [NPF_TABLE_TREE] = "tree", 475 [NPF_TABLE_CDB] = "cdb", 476 }; 477 478 if (name[0] == '.') { 479 /* Internal tables use dot and are hidden. */ 480 return; 481 } 482 assert(type < __arraycount(table_types)); 483 fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]); 484 } 485 486 int 487 npfctl_config_show(int fd) 488 { 489 npf_conf_info_t *ctx = &stdout_ctx; 490 nl_config_t *ncf; 491 bool active, loaded; 492 493 if (fd) { 494 ncf = npf_config_retrieve(fd, &active, &loaded); 495 if (ncf == NULL) { 496 return errno; 497 } 498 fprintf(ctx->fp, "# filtering:\t%s\n# config:\t%s\n", 499 active ? "active" : "inactive", 500 loaded ? "loaded" : "empty"); 501 print_linesep(ctx); 502 } else { 503 npfctl_config_send(0, NULL); 504 ncf = npfctl_config_ref(); 505 loaded = true; 506 } 507 ctx->conf = ncf; 508 509 if (loaded) { 510 nl_rule_t *rl; 511 nl_rproc_t *rp; 512 nl_nat_t *nt; 513 nl_table_t *tl; 514 u_int level; 515 516 while ((tl = npf_table_iterate(ncf)) != NULL) { 517 npfctl_print_table(ctx, tl); 518 } 519 print_linesep(ctx); 520 521 while ((rp = npf_rproc_iterate(ncf)) != NULL) { 522 const char *rpname = npf_rproc_getname(rp); 523 fprintf(ctx->fp, "procedure \"%s\"\n", rpname); 524 } 525 print_linesep(ctx); 526 527 while ((nt = npf_nat_iterate(ncf)) != NULL) { 528 npfctl_print_nat(ctx, nt); 529 } 530 print_linesep(ctx); 531 532 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) { 533 print_indent(ctx, level); 534 npfctl_print_rule(ctx, rl); 535 } 536 print_linesep(ctx); 537 } 538 npf_config_destroy(ncf); 539 return 0; 540 } 541 542 int 543 npfctl_ruleset_show(int fd, const char *ruleset_name) 544 { 545 npf_conf_info_t *ctx = &stdout_ctx; 546 nl_config_t *ncf; 547 nl_rule_t *rl; 548 u_int level; 549 int error; 550 551 ncf = npf_config_create(); 552 ctx->conf = ncf; 553 554 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) { 555 return error; 556 } 557 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) { 558 npfctl_print_rule(ctx, rl); 559 } 560 npf_config_destroy(ncf); 561 return error; 562 } 563