1 /* $NetBSD: npf_show.c,v 1.25 2017/12/10 22:04:41 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.25 2017/12/10 22:04:41 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_id(npf_conf_info_t *ctx, nl_rule_t *rl) 342 { 343 uint64_t id = id = npf_rule_getid(rl); 344 fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id); 345 } 346 347 static void 348 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl) 349 { 350 const void *marks; 351 size_t mlen, len; 352 const void *code; 353 int type; 354 355 marks = npf_rule_getinfo(rl, &mlen); 356 if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) { 357 /* 358 * No marks, but the byte-code is present. This must 359 * have been filled by libpcap(3) or possibly an unknown 360 * to us byte-code. 361 */ 362 fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ? 363 "pcap-filter \"...\"" : "unrecognized-bytecode"); 364 return; 365 } 366 ctx->flags = 0; 367 368 /* 369 * BPF filter criteria described by the byte-code marks. 370 */ 371 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) { 372 const struct mark_keyword_mapent *mk = &mark_keyword_map[i]; 373 char *val; 374 375 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) { 376 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val); 377 fputs(" ", ctx->fp); 378 free(val); 379 } 380 } 381 if (!mlen) { 382 fputs("all ", ctx->fp); 383 } 384 } 385 386 static void 387 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl) 388 { 389 const uint32_t attr = npf_rule_getattr(rl); 390 const char *rproc, *ifname, *name; 391 392 /* Rule attributes/flags. */ 393 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) { 394 const struct attr_keyword_mapent *ak = &attr_keyword_map[i]; 395 396 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) { 397 fprintf(ctx->fp, "\"%s\" ", name); 398 } 399 if ((attr & ak->mask) == ak->flags) { 400 fprintf(ctx->fp, "%s ", ak->val); 401 } 402 } 403 if ((ifname = npf_rule_getinterface(rl)) != NULL) { 404 fprintf(ctx->fp, "on %s ", ifname); 405 } 406 407 if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) { 408 /* Group; done. */ 409 goto out; 410 } 411 412 /* Print filter criteria. */ 413 npfctl_print_filter(ctx, rl); 414 415 /* Rule procedure. */ 416 if ((rproc = npf_rule_getproc(rl)) != NULL) { 417 fprintf(ctx->fp, "apply \"%s\" ", rproc); 418 } 419 420 out: 421 npfctl_print_id(ctx, rl); 422 fputs("\n", ctx->fp); 423 } 424 425 static void 426 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt) 427 { 428 nl_rule_t *rl = (nl_nat_t *)nt; 429 const char *ifname, *seg1, *seg2, *arrow; 430 npf_addr_t addr; 431 in_port_t port; 432 size_t alen; 433 u_int flags; 434 char *seg; 435 436 /* Get the interface. */ 437 ifname = npf_rule_getinterface(rl); 438 assert(ifname != NULL); 439 440 /* Get the translation address (and port, if used). */ 441 npf_nat_getmap(nt, &addr, &alen, &port); 442 seg = npfctl_print_addrmask(alen, "%a", &addr, NPF_NO_NETMASK); 443 if (port) { 444 char *p; 445 easprintf(&p, "%s port %u", seg, ntohs(port)); 446 free(seg), seg = p; 447 } 448 seg1 = seg2 = "any"; 449 450 /* Get the NAT type and determine the translation segment. */ 451 switch (npf_nat_gettype(nt)) { 452 case NPF_NATIN: 453 arrow = "<-"; 454 seg1 = seg; 455 break; 456 case NPF_NATOUT: 457 arrow = "->"; 458 seg2 = seg; 459 break; 460 default: 461 abort(); 462 } 463 flags = npf_nat_getflags(nt); 464 465 /* Print out the NAT policy with the filter criteria. */ 466 fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ", 467 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic", 468 "" /* XXX algo, */, 469 (flags & NPF_NAT_PORTS) ? "" : "no-ports ", 470 seg1, arrow, seg2); 471 npfctl_print_filter(ctx, rl); 472 npfctl_print_id(ctx, rl); 473 fputs("\n", ctx->fp); 474 free(seg); 475 } 476 477 static void 478 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl) 479 { 480 const char *name = npf_table_getname(tl); 481 const unsigned type = npf_table_gettype(tl); 482 const char *table_types[] = { 483 [NPF_TABLE_HASH] = "hash", 484 [NPF_TABLE_TREE] = "tree", 485 [NPF_TABLE_CDB] = "cdb", 486 }; 487 488 if (name[0] == '.') { 489 /* Internal tables use dot and are hidden. */ 490 return; 491 } 492 assert(type < __arraycount(table_types)); 493 fprintf(ctx->fp, "table <%s> type %s\n", name, table_types[type]); 494 } 495 496 int 497 npfctl_config_show(int fd) 498 { 499 npf_conf_info_t *ctx = &stdout_ctx; 500 nl_config_t *ncf; 501 bool loaded; 502 503 if (fd) { 504 ncf = npf_config_retrieve(fd); 505 if (ncf == NULL) { 506 return errno; 507 } 508 loaded = npf_config_loaded_p(ncf); 509 fprintf(ctx->fp, "# filtering:\t%s\n# config:\t%s\n", 510 npf_config_active_p(ncf) ? "active" : "inactive", 511 loaded ? "loaded" : "empty"); 512 print_linesep(ctx); 513 } else { 514 ncf = npfctl_config_ref(); 515 (void)npf_config_build(ncf); 516 loaded = true; 517 } 518 ctx->conf = ncf; 519 520 if (loaded) { 521 nl_rule_t *rl; 522 nl_rproc_t *rp; 523 nl_nat_t *nt; 524 nl_table_t *tl; 525 u_int level; 526 527 while ((tl = npf_table_iterate(ncf)) != NULL) { 528 npfctl_print_table(ctx, tl); 529 } 530 print_linesep(ctx); 531 532 while ((rp = npf_rproc_iterate(ncf)) != NULL) { 533 const char *rpname = npf_rproc_getname(rp); 534 fprintf(ctx->fp, "procedure \"%s\"\n", rpname); 535 } 536 print_linesep(ctx); 537 538 while ((nt = npf_nat_iterate(ncf)) != NULL) { 539 npfctl_print_nat(ctx, nt); 540 } 541 print_linesep(ctx); 542 543 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) { 544 print_indent(ctx, level); 545 npfctl_print_rule(ctx, rl); 546 } 547 print_linesep(ctx); 548 } 549 npf_config_destroy(ncf); 550 return 0; 551 } 552 553 int 554 npfctl_ruleset_show(int fd, const char *ruleset_name) 555 { 556 npf_conf_info_t *ctx = &stdout_ctx; 557 nl_config_t *ncf; 558 nl_rule_t *rl; 559 u_int level; 560 int error; 561 562 ncf = npf_config_create(); 563 ctx->conf = ncf; 564 565 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) { 566 return error; 567 } 568 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) { 569 npfctl_print_rule(ctx, rl); 570 } 571 npf_config_destroy(ncf); 572 return error; 573 } 574