1/*- 2 * Copyright (c) 2013-2020 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Mindaugas Rasiukevicius. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30/* 31 * NPF configuration printing. 32 * 33 * Each rule having BPF byte-code has a binary description. 34 */ 35 36#include <sys/cdefs.h> 37__RCSID("$NetBSD: npf_show.c,v 1.33 2023/08/01 20:09:12 andvar Exp $"); 38 39#include <sys/socket.h> 40#define __FAVOR_BSD 41#include <netinet/in.h> 42#include <netinet/tcp.h> 43#include <net/if.h> 44 45#include <stdio.h> 46#include <stdlib.h> 47#include <string.h> 48#include <stdbool.h> 49#include <inttypes.h> 50#include <errno.h> 51#include <err.h> 52 53#include "npfctl.h" 54 55#define SEEN_PROTO 0x01 56 57typedef struct { 58 char ** values; 59 unsigned count; 60} elem_list_t; 61 62enum { 63 LIST_PROTO = 0, LIST_SADDR, LIST_DADDR, LIST_SPORT, LIST_DPORT, 64 LIST_COUNT, 65}; 66 67typedef struct { 68 nl_config_t * conf; 69 bool validating; 70 71 FILE * fp; 72 long fpos; 73 long fposln; 74 int glevel; 75 76 unsigned flags; 77 uint32_t curmark; 78 uint64_t seen_marks; 79 elem_list_t list[LIST_COUNT]; 80 81} npf_conf_info_t; 82 83static void print_linesep(npf_conf_info_t *); 84 85static npf_conf_info_t * 86npfctl_show_init(void) 87{ 88 static npf_conf_info_t stdout_ctx; 89 memset(&stdout_ctx, 0, sizeof(npf_conf_info_t)); 90 stdout_ctx.glevel = -1; 91 stdout_ctx.fp = stdout; 92 return &stdout_ctx; 93} 94 95static void 96list_push(elem_list_t *list, char *val) 97{ 98 const unsigned n = list->count; 99 char **values; 100 101 if ((values = calloc(n + 1, sizeof(char *))) == NULL) { 102 err(EXIT_FAILURE, "calloc"); 103 } 104 for (unsigned i = 0; i < n; i++) { 105 values[i] = list->values[i]; 106 } 107 values[n] = val; 108 free(list->values); 109 list->values = values; 110 list->count++; 111} 112 113static char * 114list_join_free(elem_list_t *list, const bool use_br, const char *sep) 115{ 116 char *s, buf[2048]; 117 118 if (!join(buf, sizeof(buf), list->count, list->values, sep)) { 119 errx(EXIT_FAILURE, "out of memory while parsing the rule"); 120 } 121 easprintf(&s, (use_br && list->count > 1) ? "{ %s }" : "%s", buf); 122 for (unsigned i = 0; i < list->count; i++) { 123 free(list->values[i]); 124 } 125 free(list->values); 126 list->values = NULL; 127 list->count = 0; 128 return s; 129} 130 131/* 132 * Helper routines to print various pieces of information. 133 */ 134 135static void 136print_indent(npf_conf_info_t *ctx, unsigned level) 137{ 138 if (ctx->glevel >= 0 && level <= (unsigned)ctx->glevel) { 139 /* 140 * Level decrease -- end of the group. 141 * Print the group closing curly bracket. 142 */ 143 ctx->fpos += fprintf(ctx->fp, "}\n\n"); 144 ctx->glevel = -1; 145 } 146 while (level--) { 147 ctx->fpos += fprintf(ctx->fp, "\t"); 148 } 149} 150 151static void 152print_linesep(npf_conf_info_t *ctx) 153{ 154 if (ctx->fpos != ctx->fposln) { 155 ctx->fpos += fprintf(ctx->fp, "\n"); 156 ctx->fposln = ctx->fpos; 157 } 158} 159 160static size_t 161tcpflags2string(char *buf, unsigned tfl) 162{ 163 unsigned i = 0; 164 165 if (tfl & TH_FIN) buf[i++] = 'F'; 166 if (tfl & TH_SYN) buf[i++] = 'S'; 167 if (tfl & TH_RST) buf[i++] = 'R'; 168 if (tfl & TH_PUSH) buf[i++] = 'P'; 169 if (tfl & TH_ACK) buf[i++] = 'A'; 170 if (tfl & TH_URG) buf[i++] = 'U'; 171 if (tfl & TH_ECE) buf[i++] = 'E'; 172 if (tfl & TH_CWR) buf[i++] = 'W'; 173 buf[i] = '\0'; 174 return i; 175} 176 177static char * 178print_family(npf_conf_info_t *ctx __unused, const uint32_t *words) 179{ 180 const int af = words[0]; 181 182 switch (af) { 183 case AF_INET: 184 return estrdup("inet4"); 185 case AF_INET6: 186 return estrdup("inet6"); 187 default: 188 errx(EXIT_FAILURE, "invalid byte-code mark (family)"); 189 } 190 return NULL; 191} 192 193static char * 194print_address(npf_conf_info_t *ctx __unused, const uint32_t *words) 195{ 196 const int af = *words++; 197 const unsigned mask = *words++; 198 const npf_addr_t *addr; 199 int alen = 0; 200 201 switch (af) { 202 case AF_INET: 203 alen = 4; 204 break; 205 case AF_INET6: 206 alen = 16; 207 break; 208 default: 209 errx(EXIT_FAILURE, "invalid byte-code mark (address)"); 210 } 211 addr = (const npf_addr_t *)words; 212 return npfctl_print_addrmask(alen, "%a", addr, mask); 213} 214 215static char * 216print_number(npf_conf_info_t *ctx __unused, const uint32_t *words) 217{ 218 char *p; 219 easprintf(&p, "%u", words[0]); 220 return p; 221} 222 223static char * 224print_table(npf_conf_info_t *ctx, const uint32_t *words) 225{ 226 const unsigned tid = words[0]; 227 const char *tname; 228 char *s = NULL; 229 bool ifaddr; 230 231 tname = npfctl_table_getname(ctx->conf, tid, &ifaddr); 232 easprintf(&s, ifaddr ? "ifaddrs(%s)" : "<%s>", tname); 233 return s; 234} 235 236static char * 237print_proto(npf_conf_info_t *ctx, const uint32_t *words) 238{ 239 ctx->flags |= SEEN_PROTO; 240 switch (words[0]) { 241 case IPPROTO_TCP: 242 return estrdup("tcp"); 243 case IPPROTO_UDP: 244 return estrdup("udp"); 245 case IPPROTO_ICMP: 246 return estrdup("icmp"); 247 case IPPROTO_ICMPV6: 248 return estrdup("ipv6-icmp"); 249 } 250 return print_number(ctx, words); 251} 252 253static char * 254print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words) 255{ 256 const unsigned tf = words[0], tf_mask = words[1]; 257 char buf[32]; 258 size_t n; 259 260 if ((ctx->flags & SEEN_PROTO) == 0) { 261 /* 262 * Note: the TCP flag matching might be without 'proto tcp' 263 * when using a plain 'stateful' rule. In such case, just 264 * skip showing of the flags as they are implicit. 265 */ 266 return NULL; 267 } 268 n = tcpflags2string(buf, tf); 269 if (tf != tf_mask) { 270 buf[n++] = '/'; 271 tcpflags2string(buf + n, tf_mask); 272 } 273 return estrdup(buf); 274} 275 276static char * 277print_portrange(npf_conf_info_t *ctx __unused, const uint32_t *words) 278{ 279 unsigned fport = words[0], tport = words[1]; 280 char *p; 281 282 if (fport != tport) { 283 easprintf(&p, "%u-%u", fport, tport); 284 } else { 285 easprintf(&p, "%u", fport); 286 } 287 return p; 288} 289 290/* 291 * The main keyword mapping tables defining the syntax: 292 * - Mapping of rule attributes (flags) to the keywords. 293 * - Mapping of the byte-code marks to the keywords. 294 */ 295 296#define F(name) __CONCAT(NPF_RULE_, name) 297#define STATEFUL_ALL (NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL) 298#define NAME_AT 2 299 300static const struct attr_keyword_mapent { 301 uint32_t mask; 302 uint32_t flags; 303 const char * val; 304} attr_keyword_map[] = { 305 { F(GROUP)|F(DYNAMIC), F(GROUP), "group" }, 306 { F(GROUP)|F(DYNAMIC), F(GROUP)|F(DYNAMIC), "ruleset" }, 307 { F(GROUP)|F(PASS), 0, "block" }, 308 { F(GROUP)|F(PASS), F(PASS), "pass" }, 309 { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" }, 310 { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" }, 311 { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" }, 312 { STATEFUL_ALL, F(STATEFUL), "stateful" }, 313 { STATEFUL_ALL, STATEFUL_ALL, "stateful-all" }, 314 { F(DIMASK), F(IN), "in" }, 315 { F(DIMASK), F(OUT), "out" }, 316 { F(FINAL), F(FINAL), "final" }, 317}; 318 319static const struct mark_keyword_mapent { 320 unsigned mark; 321 const char * format; 322 int list_id; 323 char * (*printfn)(npf_conf_info_t *, const uint32_t *); 324 unsigned fwords; 325} mark_keyword_map[] = { 326 { BM_IPVER, "family %s", LIST_PROTO, print_family, 1 }, 327 { BM_PROTO, "proto %s", LIST_PROTO, print_proto, 1 }, 328 { BM_TCPFL, "flags %s", LIST_PROTO, print_tcpflags, 2 }, 329 { BM_ICMP_TYPE, "icmp-type %s", LIST_PROTO, print_number, 1 }, 330 { BM_ICMP_CODE, "code %s", LIST_PROTO, print_number, 1 }, 331 332 { BM_SRC_NEG, NULL, -1, NULL, 0 }, 333 { BM_SRC_CIDR, NULL, LIST_SADDR, print_address, 6 }, 334 { BM_SRC_TABLE, NULL, LIST_SADDR, print_table, 1 }, 335 { BM_SRC_PORTS, NULL, LIST_SPORT, print_portrange,2 }, 336 337 { BM_DST_NEG, NULL, -1, NULL, 0 }, 338 { BM_DST_CIDR, NULL, LIST_DADDR, print_address, 6 }, 339 { BM_DST_TABLE, NULL, LIST_DADDR, print_table, 1 }, 340 { BM_DST_PORTS, NULL, LIST_DPORT, print_portrange,2 }, 341}; 342 343static const char * __attribute__((format_arg(2))) 344verified_fmt(const char *fmt, const char *t __unused) 345{ 346 return fmt; 347} 348 349static void 350scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk, 351 const uint32_t *marks, size_t mlen) 352{ 353 elem_list_t sublist, *target_list; 354 355 /* 356 * If format is used for this mark, then collect multiple elements 357 * in into the list, merge and re-push the set into the target list. 358 * 359 * Currently, this is applicable only for 'proto { tcp, udp }'. 360 */ 361 memset(&sublist, 0, sizeof(elem_list_t)); 362 target_list = mk->format ? &sublist : &ctx->list[mk->list_id]; 363 364 /* Scan for the marks and extract the values. */ 365 mlen /= sizeof(uint32_t); 366 while (mlen > 2) { 367 const uint32_t m = *marks++; 368 const unsigned nwords = *marks++; 369 370 if ((mlen -= 2) < nwords) { 371 errx(EXIT_FAILURE, "byte-code marking inconsistency"); 372 } 373 if (m == mk->mark) { 374 /* 375 * Set the current mark and note it as seen. 376 * Value is processed by the print function, 377 * otherwise we just need to note the mark. 378 */ 379 ctx->curmark = m; 380 assert(BM_COUNT < (sizeof(uint64_t) * CHAR_BIT)); 381 ctx->seen_marks = UINT64_C(1) << m; 382 assert(mk->fwords == nwords); 383 384 if (mk->printfn) { 385 char *val; 386 387 if ((val = mk->printfn(ctx, marks)) != NULL) { 388 list_push(target_list, val); 389 } 390 } 391 } 392 marks += nwords; 393 mlen -= nwords; 394 } 395 396 if (sublist.count) { 397 char *val, *elements; 398 399 elements = list_join_free(&sublist, true, ", "); 400 easprintf(&val, verified_fmt(mk->format, "%s"), elements ); 401 list_push(&ctx->list[mk->list_id], val); 402 free(elements); 403 } 404} 405 406static void 407npfctl_print_id(npf_conf_info_t *ctx, nl_rule_t *rl) 408{ 409 const uint64_t id = npf_rule_getid(rl); 410 411 if (id) { 412 ctx->fpos += fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id); 413 } 414} 415 416static void 417npfctl_print_filter_generic(npf_conf_info_t *ctx) 418{ 419 elem_list_t *list = &ctx->list[LIST_PROTO]; 420 421 if (list->count) { 422 char *elements = list_join_free(list, false, " "); 423 ctx->fpos += fprintf(ctx->fp, "%s ", elements); 424 free(elements); 425 } 426} 427 428static bool 429npfctl_print_filter_seg(npf_conf_info_t *ctx, unsigned which) 430{ 431 static const struct { 432 const char * keyword; 433 unsigned alist; 434 unsigned plist; 435 unsigned negbm; 436 } refs[] = { 437 [NPF_SRC] = { 438 .keyword = "from", 439 .alist = LIST_SADDR, 440 .plist = LIST_SPORT, 441 .negbm = UINT64_C(1) << BM_SRC_NEG, 442 }, 443 [NPF_DST] = { 444 .keyword = "to", 445 .alist = LIST_DADDR, 446 .plist = LIST_DPORT, 447 .negbm = UINT64_C(1) << BM_DST_NEG, 448 } 449 }; 450 const char *neg = !!(ctx->seen_marks & refs[which].negbm) ? "! " : ""; 451 const char *kwd = refs[which].keyword; 452 bool seen_filter = false; 453 elem_list_t *list; 454 char *elements; 455 456 list = &ctx->list[refs[which].alist]; 457 if (list->count != 0) { 458 seen_filter = true; 459 elements = list_join_free(list, true, ", "); 460 ctx->fpos += fprintf(ctx->fp, "%s %s%s ", kwd, neg, elements); 461 free(elements); 462 } 463 464 list = &ctx->list[refs[which].plist]; 465 if (list->count != 0) { 466 if (!seen_filter) { 467 ctx->fpos += fprintf(ctx->fp, "%s any ", kwd); 468 seen_filter = true; 469 } 470 elements = list_join_free(list, true, ", "); 471 ctx->fpos += fprintf(ctx->fp, "port %s ", elements); 472 free(elements); 473 } 474 return seen_filter; 475} 476 477static bool 478npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl) 479{ 480 const void *marks; 481 size_t mlen, len; 482 const void *code; 483 bool seenf = false; 484 int type; 485 486 marks = npf_rule_getinfo(rl, &mlen); 487 if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) { 488 /* 489 * No marks, but the byte-code is present. This must 490 * have been filled by libpcap(3) or possibly an unknown 491 * to us byte-code. 492 */ 493 ctx->fpos += fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ? 494 "pcap-filter \"...\"" : "unrecognized-bytecode"); 495 return true; 496 } 497 ctx->flags = 0; 498 499 /* 500 * BPF filter criteria described by the byte-code marks. 501 */ 502 for (unsigned i = 0; i < __arraycount(mark_keyword_map); i++) { 503 const struct mark_keyword_mapent *mk = &mark_keyword_map[i]; 504 scan_marks(ctx, mk, marks, mlen); 505 } 506 npfctl_print_filter_generic(ctx); 507 seenf |= npfctl_print_filter_seg(ctx, NPF_SRC); 508 seenf |= npfctl_print_filter_seg(ctx, NPF_DST); 509 return seenf; 510} 511 512static void 513npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl, unsigned level) 514{ 515 const uint32_t attr = npf_rule_getattr(rl); 516 const char *rproc, *ifname, *name; 517 bool dyn_ruleset; 518 519 /* Rule attributes/flags. */ 520 for (unsigned i = 0; i < __arraycount(attr_keyword_map); i++) { 521 const struct attr_keyword_mapent *ak = &attr_keyword_map[i]; 522 523 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) { 524 ctx->fpos += fprintf(ctx->fp, "\"%s\" ", name); 525 } 526 if ((attr & ak->mask) == ak->flags) { 527 ctx->fpos += fprintf(ctx->fp, "%s ", ak->val); 528 } 529 } 530 if ((ifname = npf_rule_getinterface(rl)) != NULL) { 531 ctx->fpos += fprintf(ctx->fp, "on %s ", ifname); 532 } 533 if (attr == (NPF_RULE_GROUP | NPF_RULE_IN | NPF_RULE_OUT) && !ifname) { 534 /* The default group is a special case. */ 535 ctx->fpos += fprintf(ctx->fp, "default "); 536 } 537 if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) { 538 /* Group; done. */ 539 ctx->fpos += fprintf(ctx->fp, "{ "); 540 ctx->glevel = level; 541 goto out; 542 } 543 544 /* Print filter criteria. */ 545 dyn_ruleset = (attr & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP; 546 if (!npfctl_print_filter(ctx, rl) && !dyn_ruleset) { 547 ctx->fpos += fprintf(ctx->fp, "all "); 548 } 549 550 /* Rule procedure. */ 551 if ((rproc = npf_rule_getproc(rl)) != NULL) { 552 ctx->fpos += fprintf(ctx->fp, "apply \"%s\" ", rproc); 553 } 554out: 555 npfctl_print_id(ctx, rl); 556 ctx->fpos += fprintf(ctx->fp, "\n"); 557} 558 559static void 560npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt) 561{ 562 const unsigned dynamic_natset = NPF_RULE_GROUP | NPF_RULE_DYNAMIC; 563 nl_rule_t *rl = (nl_nat_t *)nt; 564 const char *ifname, *algo, *seg1, *seg2, *arrow; 565 const npf_addr_t *addr; 566 npf_netmask_t mask; 567 in_port_t port; 568 size_t alen; 569 unsigned flags; 570 char *seg; 571 572 /* Get flags and the interface. */ 573 flags = npf_nat_getflags(nt); 574 ifname = npf_rule_getinterface(rl); 575 assert(ifname != NULL); 576 577 if ((npf_rule_getattr(rl) & dynamic_natset) == dynamic_natset) { 578 const char *name = npf_rule_getname(rl); 579 ctx->fpos += fprintf(ctx->fp, 580 "map ruleset \"%s\" on %s\n", name, ifname); 581 return; 582 } 583 584 /* Get the translation address or table (and port, if used). */ 585 addr = npf_nat_getaddr(nt, &alen, &mask); 586 if (addr) { 587 seg = npfctl_print_addrmask(alen, "%a", addr, mask); 588 } else { 589 const unsigned tid = npf_nat_gettable(nt); 590 const char *tname; 591 bool ifaddr; 592 593 tname = npfctl_table_getname(ctx->conf, tid, &ifaddr); 594 easprintf(&seg, ifaddr ? "ifaddrs(%s)" : "<%s>", tname); 595 } 596 597 if ((port = npf_nat_getport(nt)) != 0) { 598 char *p; 599 easprintf(&p, "%s port %u", seg, ntohs(port)); 600 free(seg), seg = p; 601 } 602 seg1 = seg2 = "any"; 603 604 /* Get the NAT type and determine the translation segment. */ 605 switch (npf_nat_gettype(nt)) { 606 case NPF_NATIN: 607 arrow = "<-"; 608 seg1 = seg; 609 break; 610 case NPF_NATOUT: 611 arrow = "->"; 612 seg2 = seg; 613 break; 614 default: 615 abort(); 616 } 617 618 /* NAT algorithm. */ 619 switch (npf_nat_getalgo(nt)) { 620 case NPF_ALGO_NETMAP: 621 algo = "algo netmap "; 622 break; 623 case NPF_ALGO_IPHASH: 624 algo = "algo ip-hash "; 625 break; 626 case NPF_ALGO_RR: 627 algo = "algo round-robin "; 628 break; 629 case NPF_ALGO_NPT66: 630 algo = "algo npt66 "; 631 break; 632 default: 633 algo = ""; 634 break; 635 } 636 637 /* XXX also handle "any" */ 638 639 /* Print out the NAT policy with the filter criteria. */ 640 ctx->fpos += fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ", 641 ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic", 642 algo, (flags & NPF_NAT_PORTS) ? "" : "no-ports ", 643 seg1, arrow, seg2); 644 npfctl_print_filter(ctx, rl); 645 npfctl_print_id(ctx, rl); 646 ctx->fpos += fprintf(ctx->fp, "\n"); 647 free(seg); 648} 649 650static void 651npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl) 652{ 653 const char *name = npf_table_getname(tl); 654 const unsigned type = npf_table_gettype(tl); 655 const char *table_types[] = { 656 [NPF_TABLE_IPSET] = "ipset", 657 [NPF_TABLE_LPM] = "lpm", 658 [NPF_TABLE_CONST] = "const", 659 }; 660 661 if (name[0] == '.') { 662 /* Internal tables use dot and are hidden. */ 663 return; 664 } 665 assert(type < __arraycount(table_types)); 666 ctx->fpos += fprintf(ctx->fp, 667 "table <%s> type %s\n", name, table_types[type]); 668} 669 670static void 671npfctl_print_params(npf_conf_info_t *ctx, nl_config_t *ncf) 672{ 673 nl_iter_t i = NPF_ITER_BEGIN; 674 int val, defval, *dval; 675 const char *name; 676 677 dval = ctx->validating ? NULL : &defval; 678 while ((name = npf_param_iterate(ncf, &i, &val, dval)) != NULL) { 679 if (dval && val == *dval) { 680 continue; 681 } 682 ctx->fpos += fprintf(ctx->fp, "set %s %d\n", name, val); 683 } 684 print_linesep(ctx); 685} 686 687int 688npfctl_config_show(int fd) 689{ 690 npf_conf_info_t *ctx = npfctl_show_init(); 691 nl_config_t *ncf; 692 bool loaded; 693 694 if (fd) { 695 ncf = npf_config_retrieve(fd); 696 if (ncf == NULL) { 697 return errno; 698 } 699 loaded = npf_config_loaded_p(ncf); 700 ctx->validating = false; 701 ctx->fpos += fprintf(ctx->fp, 702 "# filtering:\t%s\n# config:\t%s\n", 703 npf_config_active_p(ncf) ? "active" : "inactive", 704 loaded ? "loaded" : "empty"); 705 print_linesep(ctx); 706 } else { 707 ncf = npfctl_config_ref(); 708 npfctl_config_build(); 709 ctx->validating = true; 710 loaded = true; 711 } 712 ctx->conf = ncf; 713 714 if (loaded) { 715 nl_rule_t *rl; 716 nl_rproc_t *rp; 717 nl_nat_t *nt; 718 nl_table_t *tl; 719 nl_iter_t i; 720 unsigned level; 721 722 npfctl_print_params(ctx, ncf); 723 724 i = NPF_ITER_BEGIN; 725 while ((tl = npf_table_iterate(ncf, &i)) != NULL) { 726 npfctl_print_table(ctx, tl); 727 } 728 print_linesep(ctx); 729 730 i = NPF_ITER_BEGIN; 731 while ((rp = npf_rproc_iterate(ncf, &i)) != NULL) { 732 const char *rpname = npf_rproc_getname(rp); 733 ctx->fpos += fprintf(ctx->fp, 734 "procedure \"%s\"\n", rpname); 735 } 736 print_linesep(ctx); 737 738 i = NPF_ITER_BEGIN; 739 while ((nt = npf_nat_iterate(ncf, &i)) != NULL) { 740 npfctl_print_nat(ctx, nt); 741 } 742 print_linesep(ctx); 743 744 i = NPF_ITER_BEGIN; 745 while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) { 746 print_indent(ctx, level); 747 npfctl_print_rule(ctx, rl, level); 748 } 749 print_indent(ctx, 0); 750 } 751 npf_config_destroy(ncf); 752 return 0; 753} 754 755int 756npfctl_ruleset_show(int fd, const char *ruleset_name) 757{ 758 npf_conf_info_t *ctx = npfctl_show_init(); 759 nl_config_t *ncf; 760 nl_rule_t *rl; 761 unsigned level; 762 nl_iter_t i; 763 int error; 764 765 ncf = npf_config_create(); 766 ctx->conf = ncf; 767 768 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) { 769 return error; 770 } 771 i = NPF_ITER_BEGIN; 772 while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) { 773 npfctl_print_rule(ctx, rl, 0); 774 } 775 npf_config_destroy(ncf); 776 return error; 777} 778