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