14e592132Srmind /*- 2b899bfd9Srmind * Copyright (c) 2013-2020 The NetBSD Foundation, Inc. 34e592132Srmind * All rights reserved. 44e592132Srmind * 54e592132Srmind * This code is derived from software contributed to The NetBSD Foundation 64e592132Srmind * by Mindaugas Rasiukevicius. 74e592132Srmind * 84e592132Srmind * Redistribution and use in source and binary forms, with or without 94e592132Srmind * modification, are permitted provided that the following conditions 104e592132Srmind * are met: 114e592132Srmind * 1. Redistributions of source code must retain the above copyright 124e592132Srmind * notice, this list of conditions and the following disclaimer. 134e592132Srmind * 2. Redistributions in binary form must reproduce the above copyright 144e592132Srmind * notice, this list of conditions and the following disclaimer in the 154e592132Srmind * documentation and/or other materials provided with the distribution. 164e592132Srmind * 174e592132Srmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 184e592132Srmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 194e592132Srmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 204e592132Srmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 214e592132Srmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 224e592132Srmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 234e592132Srmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 244e592132Srmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 254e592132Srmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 264e592132Srmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 274e592132Srmind * POSSIBILITY OF SUCH DAMAGE. 284e592132Srmind */ 294e592132Srmind 304e592132Srmind /* 314e592132Srmind * NPF configuration printing. 324e592132Srmind * 334e592132Srmind * Each rule having BPF byte-code has a binary description. 344e592132Srmind */ 354e592132Srmind 364e592132Srmind #include <sys/cdefs.h> 37*cb624a4bSmlelstv __RCSID("$NetBSD: npf_show.c,v 1.34 2025/01/27 07:54:30 mlelstv Exp $"); 384e592132Srmind 394e592132Srmind #include <sys/socket.h> 40f75d79ebSchristos #define __FAVOR_BSD 414e592132Srmind #include <netinet/in.h> 424e592132Srmind #include <netinet/tcp.h> 434e592132Srmind #include <net/if.h> 444e592132Srmind 454e592132Srmind #include <stdio.h> 464e592132Srmind #include <stdlib.h> 474e592132Srmind #include <string.h> 484e592132Srmind #include <stdbool.h> 494e592132Srmind #include <inttypes.h> 504e592132Srmind #include <errno.h> 514e592132Srmind #include <err.h> 524e592132Srmind 534e592132Srmind #include "npfctl.h" 544e592132Srmind 55b899bfd9Srmind #define SEEN_PROTO 0x01 56b899bfd9Srmind 57b899bfd9Srmind typedef struct { 58b899bfd9Srmind char ** values; 59b899bfd9Srmind unsigned count; 60b899bfd9Srmind } elem_list_t; 61b899bfd9Srmind 62b899bfd9Srmind enum { 63b899bfd9Srmind LIST_PROTO = 0, LIST_SADDR, LIST_DADDR, LIST_SPORT, LIST_DPORT, 64b899bfd9Srmind LIST_COUNT, 65b899bfd9Srmind }; 663250dbf2Srmind 674e592132Srmind typedef struct { 681e7342c1Srmind nl_config_t * conf; 69b899bfd9Srmind bool validating; 70b899bfd9Srmind 714e592132Srmind FILE * fp; 724e592132Srmind long fpos; 73b899bfd9Srmind long fposln; 74b899bfd9Srmind int glevel; 754e592132Srmind 76b899bfd9Srmind unsigned flags; 77b899bfd9Srmind uint32_t curmark; 78b899bfd9Srmind uint64_t seen_marks; 79b899bfd9Srmind elem_list_t list[LIST_COUNT]; 80b899bfd9Srmind 81b899bfd9Srmind } npf_conf_info_t; 824e592132Srmind 834e592132Srmind static void print_linesep(npf_conf_info_t *); 844e592132Srmind 85b899bfd9Srmind static npf_conf_info_t * 86f75d79ebSchristos npfctl_show_init(void) 87f75d79ebSchristos { 88b899bfd9Srmind static npf_conf_info_t stdout_ctx; 89b899bfd9Srmind memset(&stdout_ctx, 0, sizeof(npf_conf_info_t)); 90b899bfd9Srmind stdout_ctx.glevel = -1; 91f75d79ebSchristos stdout_ctx.fp = stdout; 92b899bfd9Srmind return &stdout_ctx; 93b899bfd9Srmind } 94b899bfd9Srmind 95b899bfd9Srmind static void 96b899bfd9Srmind list_push(elem_list_t *list, char *val) 97b899bfd9Srmind { 98b899bfd9Srmind const unsigned n = list->count; 99b899bfd9Srmind char **values; 100b899bfd9Srmind 101b899bfd9Srmind if ((values = calloc(n + 1, sizeof(char *))) == NULL) { 102b899bfd9Srmind err(EXIT_FAILURE, "calloc"); 103b899bfd9Srmind } 104b899bfd9Srmind for (unsigned i = 0; i < n; i++) { 105b899bfd9Srmind values[i] = list->values[i]; 106b899bfd9Srmind } 107b899bfd9Srmind values[n] = val; 108b899bfd9Srmind free(list->values); 109b899bfd9Srmind list->values = values; 110b899bfd9Srmind list->count++; 111b899bfd9Srmind } 112b899bfd9Srmind 113b899bfd9Srmind static char * 114b899bfd9Srmind list_join_free(elem_list_t *list, const bool use_br, const char *sep) 115b899bfd9Srmind { 116b899bfd9Srmind char *s, buf[2048]; 117b899bfd9Srmind 118b899bfd9Srmind if (!join(buf, sizeof(buf), list->count, list->values, sep)) { 119b899bfd9Srmind errx(EXIT_FAILURE, "out of memory while parsing the rule"); 120b899bfd9Srmind } 121b899bfd9Srmind easprintf(&s, (use_br && list->count > 1) ? "{ %s }" : "%s", buf); 122b899bfd9Srmind for (unsigned i = 0; i < list->count; i++) { 123b899bfd9Srmind free(list->values[i]); 124b899bfd9Srmind } 125b899bfd9Srmind free(list->values); 126b899bfd9Srmind list->values = NULL; 127b899bfd9Srmind list->count = 0; 128b899bfd9Srmind return s; 129f75d79ebSchristos } 130f75d79ebSchristos 1314e592132Srmind /* 1324e592132Srmind * Helper routines to print various pieces of information. 1334e592132Srmind */ 1344e592132Srmind 1354e592132Srmind static void 1363d9a792dSrmind print_indent(npf_conf_info_t *ctx, unsigned level) 1374e592132Srmind { 138b899bfd9Srmind if (ctx->glevel >= 0 && level <= (unsigned)ctx->glevel) { 1393d9a792dSrmind /* 1403d9a792dSrmind * Level decrease -- end of the group. 1413d9a792dSrmind * Print the group closing curly bracket. 1423d9a792dSrmind */ 143b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "}\n\n"); 144b899bfd9Srmind ctx->glevel = -1; 1453d9a792dSrmind } 1463d9a792dSrmind while (level--) { 147b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "\t"); 1484e592132Srmind } 1493d9a792dSrmind } 1504e592132Srmind 1514e592132Srmind static void 1524e592132Srmind print_linesep(npf_conf_info_t *ctx) 1534e592132Srmind { 154b899bfd9Srmind if (ctx->fpos != ctx->fposln) { 155b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "\n"); 156b899bfd9Srmind ctx->fposln = ctx->fpos; 1574e592132Srmind } 1584e592132Srmind } 1594e592132Srmind 1604e592132Srmind static size_t 161b899bfd9Srmind tcpflags2string(char *buf, unsigned tfl) 1624e592132Srmind { 163b899bfd9Srmind unsigned i = 0; 1644e592132Srmind 1654e592132Srmind if (tfl & TH_FIN) buf[i++] = 'F'; 1664e592132Srmind if (tfl & TH_SYN) buf[i++] = 'S'; 1674e592132Srmind if (tfl & TH_RST) buf[i++] = 'R'; 1684e592132Srmind if (tfl & TH_PUSH) buf[i++] = 'P'; 1694e592132Srmind if (tfl & TH_ACK) buf[i++] = 'A'; 1704e592132Srmind if (tfl & TH_URG) buf[i++] = 'U'; 1714e592132Srmind if (tfl & TH_ECE) buf[i++] = 'E'; 17242e1450dSchristos if (tfl & TH_CWR) buf[i++] = 'W'; 1734e592132Srmind buf[i] = '\0'; 1744e592132Srmind return i; 1754e592132Srmind } 1764e592132Srmind 1774e592132Srmind static char * 17839013e66Srmind print_family(npf_conf_info_t *ctx __unused, const uint32_t *words) 1794e592132Srmind { 1804e592132Srmind const int af = words[0]; 1814e592132Srmind 1824e592132Srmind switch (af) { 1834e592132Srmind case AF_INET: 1848b83480dSrmind return estrdup("inet4"); 1854e592132Srmind case AF_INET6: 1864e592132Srmind return estrdup("inet6"); 1874e592132Srmind default: 1884e592132Srmind errx(EXIT_FAILURE, "invalid byte-code mark (family)"); 1894e592132Srmind } 1904e592132Srmind return NULL; 1914e592132Srmind } 1924e592132Srmind 1934e592132Srmind static char * 19439013e66Srmind print_address(npf_conf_info_t *ctx __unused, const uint32_t *words) 1954e592132Srmind { 1964e592132Srmind const int af = *words++; 197b899bfd9Srmind const unsigned mask = *words++; 1984e592132Srmind const npf_addr_t *addr; 1994e592132Srmind int alen = 0; 2004e592132Srmind 2014e592132Srmind switch (af) { 2024e592132Srmind case AF_INET: 2034e592132Srmind alen = 4; 2044e592132Srmind break; 2054e592132Srmind case AF_INET6: 2064e592132Srmind alen = 16; 2074e592132Srmind break; 2084e592132Srmind default: 2094e592132Srmind errx(EXIT_FAILURE, "invalid byte-code mark (address)"); 2104e592132Srmind } 2114e592132Srmind addr = (const npf_addr_t *)words; 212a0cedf0dSchristos return npfctl_print_addrmask(alen, "%a", addr, mask); 2134e592132Srmind } 2144e592132Srmind 2154e592132Srmind static char * 21639013e66Srmind print_number(npf_conf_info_t *ctx __unused, const uint32_t *words) 2174e592132Srmind { 2184e592132Srmind char *p; 2194e592132Srmind easprintf(&p, "%u", words[0]); 2204e592132Srmind return p; 2214e592132Srmind } 2224e592132Srmind 2234e592132Srmind static char * 2241e7342c1Srmind print_table(npf_conf_info_t *ctx, const uint32_t *words) 2251e7342c1Srmind { 2263d9a792dSrmind const unsigned tid = words[0]; 2273d9a792dSrmind const char *tname; 2283d9a792dSrmind char *s = NULL; 2293d9a792dSrmind bool ifaddr; 2301e7342c1Srmind 2313d9a792dSrmind tname = npfctl_table_getname(ctx->conf, tid, &ifaddr); 2323d9a792dSrmind easprintf(&s, ifaddr ? "ifaddrs(%s)" : "<%s>", tname); 2333d9a792dSrmind return s; 2341e7342c1Srmind } 2351e7342c1Srmind 2361e7342c1Srmind static char * 2371e7342c1Srmind print_proto(npf_conf_info_t *ctx, const uint32_t *words) 2384e592132Srmind { 239b899bfd9Srmind ctx->flags |= SEEN_PROTO; 2404e592132Srmind switch (words[0]) { 2414e592132Srmind case IPPROTO_TCP: 2424e592132Srmind return estrdup("tcp"); 2434e592132Srmind case IPPROTO_UDP: 2444e592132Srmind return estrdup("udp"); 2454e592132Srmind case IPPROTO_ICMP: 2464e592132Srmind return estrdup("icmp"); 2474e592132Srmind case IPPROTO_ICMPV6: 2484e592132Srmind return estrdup("ipv6-icmp"); 2494e592132Srmind } 2501e7342c1Srmind return print_number(ctx, words); 2514e592132Srmind } 2524e592132Srmind 2534e592132Srmind static char * 25439013e66Srmind print_tcpflags(npf_conf_info_t *ctx __unused, const uint32_t *words) 2554e592132Srmind { 256b899bfd9Srmind const unsigned tf = words[0], tf_mask = words[1]; 257b899bfd9Srmind char buf[32]; 258b899bfd9Srmind size_t n; 2594e592132Srmind 260b899bfd9Srmind if ((ctx->flags & SEEN_PROTO) == 0) { 261b899bfd9Srmind /* 262b899bfd9Srmind * Note: the TCP flag matching might be without 'proto tcp' 263b899bfd9Srmind * when using a plain 'stateful' rule. In such case, just 264b899bfd9Srmind * skip showing of the flags as they are implicit. 265b899bfd9Srmind */ 266b899bfd9Srmind return NULL; 267b899bfd9Srmind } 268b899bfd9Srmind n = tcpflags2string(buf, tf); 2694e592132Srmind if (tf != tf_mask) { 2704e592132Srmind buf[n++] = '/'; 2714e592132Srmind tcpflags2string(buf + n, tf_mask); 2724e592132Srmind } 2734e592132Srmind return estrdup(buf); 2744e592132Srmind } 2754e592132Srmind 2764e592132Srmind static char * 27764b4ec43Srmind print_portrange(npf_conf_info_t *ctx __unused, const uint32_t *words) 2784e592132Srmind { 279b899bfd9Srmind unsigned fport = words[0], tport = words[1]; 2804e592132Srmind char *p; 2814e592132Srmind 2824e592132Srmind if (fport != tport) { 28364b4ec43Srmind easprintf(&p, "%u-%u", fport, tport); 2844e592132Srmind } else { 28564b4ec43Srmind easprintf(&p, "%u", fport); 2864e592132Srmind } 2874e592132Srmind return p; 2884e592132Srmind } 2894e592132Srmind 2904e592132Srmind /* 2914e592132Srmind * The main keyword mapping tables defining the syntax: 2924e592132Srmind * - Mapping of rule attributes (flags) to the keywords. 2934e592132Srmind * - Mapping of the byte-code marks to the keywords. 2944e592132Srmind */ 2954e592132Srmind 2964e592132Srmind #define F(name) __CONCAT(NPF_RULE_, name) 297dadc88e3Srmind #define STATEFUL_ALL (NPF_RULE_STATEFUL | NPF_RULE_GSTATEFUL) 2984e592132Srmind #define NAME_AT 2 2994e592132Srmind 3004e592132Srmind static const struct attr_keyword_mapent { 3014e592132Srmind uint32_t mask; 3024e592132Srmind uint32_t flags; 3034e592132Srmind const char * val; 3044e592132Srmind } attr_keyword_map[] = { 3054e592132Srmind { F(GROUP)|F(DYNAMIC), F(GROUP), "group" }, 306b899bfd9Srmind { F(GROUP)|F(DYNAMIC), F(GROUP)|F(DYNAMIC), "ruleset" }, 3074e592132Srmind { F(GROUP)|F(PASS), 0, "block" }, 3084e592132Srmind { F(GROUP)|F(PASS), F(PASS), "pass" }, 3094e592132Srmind { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" }, 3104e592132Srmind { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" }, 3114e592132Srmind { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" }, 312dadc88e3Srmind { STATEFUL_ALL, F(STATEFUL), "stateful" }, 313dadc88e3Srmind { STATEFUL_ALL, STATEFUL_ALL, "stateful-all" }, 3144e592132Srmind { F(DIMASK), F(IN), "in" }, 3154e592132Srmind { F(DIMASK), F(OUT), "out" }, 3164e592132Srmind { F(FINAL), F(FINAL), "final" }, 3174e592132Srmind }; 3184e592132Srmind 3194e592132Srmind static const struct mark_keyword_mapent { 320b899bfd9Srmind unsigned mark; 321b899bfd9Srmind const char * format; 322b899bfd9Srmind int list_id; 3231e7342c1Srmind char * (*printfn)(npf_conf_info_t *, const uint32_t *); 324b899bfd9Srmind unsigned fwords; 3254e592132Srmind } mark_keyword_map[] = { 326b899bfd9Srmind { BM_IPVER, "family %s", LIST_PROTO, print_family, 1 }, 327b899bfd9Srmind { BM_PROTO, "proto %s", LIST_PROTO, print_proto, 1 }, 328b899bfd9Srmind { BM_TCPFL, "flags %s", LIST_PROTO, print_tcpflags, 2 }, 329b899bfd9Srmind { BM_ICMP_TYPE, "icmp-type %s", LIST_PROTO, print_number, 1 }, 330b899bfd9Srmind { BM_ICMP_CODE, "code %s", LIST_PROTO, print_number, 1 }, 3314e592132Srmind 332b899bfd9Srmind { BM_SRC_NEG, NULL, -1, NULL, 0 }, 333b899bfd9Srmind { BM_SRC_CIDR, NULL, LIST_SADDR, print_address, 6 }, 334b899bfd9Srmind { BM_SRC_TABLE, NULL, LIST_SADDR, print_table, 1 }, 335b899bfd9Srmind { BM_SRC_PORTS, NULL, LIST_SPORT, print_portrange,2 }, 3364e592132Srmind 337b899bfd9Srmind { BM_DST_NEG, NULL, -1, NULL, 0 }, 338b899bfd9Srmind { BM_DST_CIDR, NULL, LIST_DADDR, print_address, 6 }, 339b899bfd9Srmind { BM_DST_TABLE, NULL, LIST_DADDR, print_table, 1 }, 340b899bfd9Srmind { BM_DST_PORTS, NULL, LIST_DPORT, print_portrange,2 }, 3414e592132Srmind }; 3424e592132Srmind 3434e592132Srmind static const char * __attribute__((format_arg(2))) 3444e592132Srmind verified_fmt(const char *fmt, const char *t __unused) 3454e592132Srmind { 3464e592132Srmind return fmt; 3474e592132Srmind } 3484e592132Srmind 349b899bfd9Srmind static void 3504e592132Srmind scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk, 3514e592132Srmind const uint32_t *marks, size_t mlen) 3524e592132Srmind { 353b899bfd9Srmind elem_list_t sublist, *target_list; 354b899bfd9Srmind 355b899bfd9Srmind /* 356b899bfd9Srmind * If format is used for this mark, then collect multiple elements 357b899bfd9Srmind * in into the list, merge and re-push the set into the target list. 358b899bfd9Srmind * 359b899bfd9Srmind * Currently, this is applicable only for 'proto { tcp, udp }'. 360b899bfd9Srmind */ 361b899bfd9Srmind memset(&sublist, 0, sizeof(elem_list_t)); 362b899bfd9Srmind target_list = mk->format ? &sublist : &ctx->list[mk->list_id]; 3634e592132Srmind 3644e592132Srmind /* Scan for the marks and extract the values. */ 3654e592132Srmind mlen /= sizeof(uint32_t); 3664e592132Srmind while (mlen > 2) { 3674e592132Srmind const uint32_t m = *marks++; 368b899bfd9Srmind const unsigned nwords = *marks++; 3694e592132Srmind 3704e592132Srmind if ((mlen -= 2) < nwords) { 3714e592132Srmind errx(EXIT_FAILURE, "byte-code marking inconsistency"); 3724e592132Srmind } 3734e592132Srmind if (m == mk->mark) { 374b899bfd9Srmind /* 375b899bfd9Srmind * Set the current mark and note it as seen. 376b899bfd9Srmind * Value is processed by the print function, 377b899bfd9Srmind * otherwise we just need to note the mark. 378b899bfd9Srmind */ 379b899bfd9Srmind ctx->curmark = m; 380b899bfd9Srmind assert(BM_COUNT < (sizeof(uint64_t) * CHAR_BIT)); 381*cb624a4bSmlelstv ctx->seen_marks |= UINT64_C(1) << m; 382b899bfd9Srmind assert(mk->fwords == nwords); 383b899bfd9Srmind 384b899bfd9Srmind if (mk->printfn) { 38564b4ec43Srmind char *val; 38664b4ec43Srmind 38764b4ec43Srmind if ((val = mk->printfn(ctx, marks)) != NULL) { 388b899bfd9Srmind list_push(target_list, val); 389b899bfd9Srmind } 39064b4ec43Srmind } 3914e592132Srmind } 3924e592132Srmind marks += nwords; 3934e592132Srmind mlen -= nwords; 3944e592132Srmind } 3954e592132Srmind 396b899bfd9Srmind if (sublist.count) { 397b899bfd9Srmind char *val, *elements; 3984e592132Srmind 399b899bfd9Srmind elements = list_join_free(&sublist, true, ", "); 400b899bfd9Srmind easprintf(&val, verified_fmt(mk->format, "%s"), elements ); 401b899bfd9Srmind list_push(&ctx->list[mk->list_id], val); 402b899bfd9Srmind free(elements); 4034e592132Srmind } 4044e592132Srmind } 4054e592132Srmind 4064e592132Srmind static void 407f8006a40Schristos npfctl_print_id(npf_conf_info_t *ctx, nl_rule_t *rl) 408f8006a40Schristos { 409b899bfd9Srmind const uint64_t id = npf_rule_getid(rl); 410b899bfd9Srmind 411b899bfd9Srmind if (id) { 412b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "# id=\"%" PRIx64 "\" ", id); 413b899bfd9Srmind } 414f8006a40Schristos } 415f8006a40Schristos 416f8006a40Schristos static void 417b899bfd9Srmind npfctl_print_filter_generic(npf_conf_info_t *ctx) 418b899bfd9Srmind { 419b899bfd9Srmind elem_list_t *list = &ctx->list[LIST_PROTO]; 420b899bfd9Srmind 421b899bfd9Srmind if (list->count) { 422b899bfd9Srmind char *elements = list_join_free(list, false, " "); 423b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "%s ", elements); 424b899bfd9Srmind free(elements); 425b899bfd9Srmind } 426b899bfd9Srmind } 427b899bfd9Srmind 428b899bfd9Srmind static bool 429b899bfd9Srmind npfctl_print_filter_seg(npf_conf_info_t *ctx, unsigned which) 430b899bfd9Srmind { 431b899bfd9Srmind static const struct { 432b899bfd9Srmind const char * keyword; 433b899bfd9Srmind unsigned alist; 434b899bfd9Srmind unsigned plist; 435b899bfd9Srmind unsigned negbm; 436b899bfd9Srmind } refs[] = { 437b899bfd9Srmind [NPF_SRC] = { 438b899bfd9Srmind .keyword = "from", 439b899bfd9Srmind .alist = LIST_SADDR, 440b899bfd9Srmind .plist = LIST_SPORT, 441b899bfd9Srmind .negbm = UINT64_C(1) << BM_SRC_NEG, 442b899bfd9Srmind }, 443b899bfd9Srmind [NPF_DST] = { 444b899bfd9Srmind .keyword = "to", 445b899bfd9Srmind .alist = LIST_DADDR, 446b899bfd9Srmind .plist = LIST_DPORT, 447b899bfd9Srmind .negbm = UINT64_C(1) << BM_DST_NEG, 448b899bfd9Srmind } 449b899bfd9Srmind }; 450b899bfd9Srmind const char *neg = !!(ctx->seen_marks & refs[which].negbm) ? "! " : ""; 451b899bfd9Srmind const char *kwd = refs[which].keyword; 452b899bfd9Srmind bool seen_filter = false; 453b899bfd9Srmind elem_list_t *list; 454b899bfd9Srmind char *elements; 455b899bfd9Srmind 456b899bfd9Srmind list = &ctx->list[refs[which].alist]; 457b899bfd9Srmind if (list->count != 0) { 458b899bfd9Srmind seen_filter = true; 459b899bfd9Srmind elements = list_join_free(list, true, ", "); 460b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "%s %s%s ", kwd, neg, elements); 461b899bfd9Srmind free(elements); 462b899bfd9Srmind } 463b899bfd9Srmind 464b899bfd9Srmind list = &ctx->list[refs[which].plist]; 465b899bfd9Srmind if (list->count != 0) { 466b899bfd9Srmind if (!seen_filter) { 467b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "%s any ", kwd); 468b899bfd9Srmind seen_filter = true; 469b899bfd9Srmind } 470b899bfd9Srmind elements = list_join_free(list, true, ", "); 471b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "port %s ", elements); 472b899bfd9Srmind free(elements); 473b899bfd9Srmind } 474b899bfd9Srmind return seen_filter; 475b899bfd9Srmind } 476b899bfd9Srmind 477b899bfd9Srmind static bool 4784e592132Srmind npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl) 4794e592132Srmind { 4804e592132Srmind const void *marks; 481f56b8821Srmind size_t mlen, len; 482f56b8821Srmind const void *code; 483b899bfd9Srmind bool seenf = false; 484f56b8821Srmind int type; 4854e592132Srmind 4864e592132Srmind marks = npf_rule_getinfo(rl, &mlen); 487f56b8821Srmind if (!marks && (code = npf_rule_getcode(rl, &type, &len)) != NULL) { 488f56b8821Srmind /* 489f56b8821Srmind * No marks, but the byte-code is present. This must 490f56b8821Srmind * have been filled by libpcap(3) or possibly an unknown 491f56b8821Srmind * to us byte-code. 492f56b8821Srmind */ 493b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "%s ", type == NPF_CODE_BPF ? 494f56b8821Srmind "pcap-filter \"...\"" : "unrecognized-bytecode"); 495b899bfd9Srmind return true; 496f56b8821Srmind } 497d6bf72e9Srmind ctx->flags = 0; 498f56b8821Srmind 499f56b8821Srmind /* 500f56b8821Srmind * BPF filter criteria described by the byte-code marks. 501f56b8821Srmind */ 502*cb624a4bSmlelstv ctx->seen_marks = 0; 503b899bfd9Srmind for (unsigned i = 0; i < __arraycount(mark_keyword_map); i++) { 5044e592132Srmind const struct mark_keyword_mapent *mk = &mark_keyword_map[i]; 505b899bfd9Srmind scan_marks(ctx, mk, marks, mlen); 5064e592132Srmind } 507b899bfd9Srmind npfctl_print_filter_generic(ctx); 508b899bfd9Srmind seenf |= npfctl_print_filter_seg(ctx, NPF_SRC); 509b899bfd9Srmind seenf |= npfctl_print_filter_seg(ctx, NPF_DST); 510b899bfd9Srmind return seenf; 5114e592132Srmind } 5124e592132Srmind 5134e592132Srmind static void 514b899bfd9Srmind npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl, unsigned level) 5154e592132Srmind { 5164e592132Srmind const uint32_t attr = npf_rule_getattr(rl); 517a79812eaSrmind const char *rproc, *ifname, *name; 518b899bfd9Srmind bool dyn_ruleset; 5194e592132Srmind 5204e592132Srmind /* Rule attributes/flags. */ 521b899bfd9Srmind for (unsigned i = 0; i < __arraycount(attr_keyword_map); i++) { 5224e592132Srmind const struct attr_keyword_mapent *ak = &attr_keyword_map[i]; 5234e592132Srmind 5244e592132Srmind if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) { 525b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "\"%s\" ", name); 5264e592132Srmind } 5274e592132Srmind if ((attr & ak->mask) == ak->flags) { 528b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "%s ", ak->val); 5294e592132Srmind } 5304e592132Srmind } 531a79812eaSrmind if ((ifname = npf_rule_getinterface(rl)) != NULL) { 532b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "on %s ", ifname); 5334e592132Srmind } 5343d9a792dSrmind if (attr == (NPF_RULE_GROUP | NPF_RULE_IN | NPF_RULE_OUT) && !ifname) { 5353d9a792dSrmind /* The default group is a special case. */ 536b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "default "); 5373d9a792dSrmind } 538f56b8821Srmind if ((attr & NPF_DYNAMIC_GROUP) == NPF_RULE_GROUP) { 5394e592132Srmind /* Group; done. */ 540b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "{ "); 541b899bfd9Srmind ctx->glevel = level; 542f8006a40Schristos goto out; 5434e592132Srmind } 5444e592132Srmind 5454e592132Srmind /* Print filter criteria. */ 546b899bfd9Srmind dyn_ruleset = (attr & NPF_DYNAMIC_GROUP) == NPF_DYNAMIC_GROUP; 547b899bfd9Srmind if (!npfctl_print_filter(ctx, rl) && !dyn_ruleset) { 548b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "all "); 549b899bfd9Srmind } 5504e592132Srmind 5514e592132Srmind /* Rule procedure. */ 5524e592132Srmind if ((rproc = npf_rule_getproc(rl)) != NULL) { 553b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "apply \"%s\" ", rproc); 5544e592132Srmind } 555f8006a40Schristos out: 556f8006a40Schristos npfctl_print_id(ctx, rl); 557b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "\n"); 5584e592132Srmind } 5594e592132Srmind 5604e592132Srmind static void 5614e592132Srmind npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt) 5624e592132Srmind { 563dadc88e3Srmind const unsigned dynamic_natset = NPF_RULE_GROUP | NPF_RULE_DYNAMIC; 5644e592132Srmind nl_rule_t *rl = (nl_nat_t *)nt; 5653d9a792dSrmind const char *ifname, *algo, *seg1, *seg2, *arrow; 5663d9a792dSrmind const npf_addr_t *addr; 5673d9a792dSrmind npf_netmask_t mask; 5684e592132Srmind in_port_t port; 569a79812eaSrmind size_t alen; 570dadc88e3Srmind unsigned flags; 571a79812eaSrmind char *seg; 5724e592132Srmind 573dadc88e3Srmind /* Get flags and the interface. */ 574dadc88e3Srmind flags = npf_nat_getflags(nt); 575a79812eaSrmind ifname = npf_rule_getinterface(rl); 576a79812eaSrmind assert(ifname != NULL); 5774e592132Srmind 578dadc88e3Srmind if ((npf_rule_getattr(rl) & dynamic_natset) == dynamic_natset) { 579dadc88e3Srmind const char *name = npf_rule_getname(rl); 580b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, 581b899bfd9Srmind "map ruleset \"%s\" on %s\n", name, ifname); 582dadc88e3Srmind return; 583dadc88e3Srmind } 584dadc88e3Srmind 5853d9a792dSrmind /* Get the translation address or table (and port, if used). */ 5863d9a792dSrmind addr = npf_nat_getaddr(nt, &alen, &mask); 5873d9a792dSrmind if (addr) { 5883d9a792dSrmind seg = npfctl_print_addrmask(alen, "%a", addr, mask); 5893d9a792dSrmind } else { 5903d9a792dSrmind const unsigned tid = npf_nat_gettable(nt); 5913d9a792dSrmind const char *tname; 5923d9a792dSrmind bool ifaddr; 5933d9a792dSrmind 5943d9a792dSrmind tname = npfctl_table_getname(ctx->conf, tid, &ifaddr); 5953d9a792dSrmind easprintf(&seg, ifaddr ? "ifaddrs(%s)" : "<%s>", tname); 5963d9a792dSrmind } 5973d9a792dSrmind 5983d9a792dSrmind if ((port = npf_nat_getport(nt)) != 0) { 5994e592132Srmind char *p; 60027b83b3dSrmind easprintf(&p, "%s port %u", seg, ntohs(port)); 6014e592132Srmind free(seg), seg = p; 6024e592132Srmind } 6034e592132Srmind seg1 = seg2 = "any"; 6044e592132Srmind 6054e592132Srmind /* Get the NAT type and determine the translation segment. */ 6064e592132Srmind switch (npf_nat_gettype(nt)) { 6074e592132Srmind case NPF_NATIN: 6084e592132Srmind arrow = "<-"; 6094e592132Srmind seg1 = seg; 6104e592132Srmind break; 6114e592132Srmind case NPF_NATOUT: 6124e592132Srmind arrow = "->"; 6134e592132Srmind seg2 = seg; 6144e592132Srmind break; 6154e592132Srmind default: 6168274d601Srmind abort(); 6174e592132Srmind } 6184e592132Srmind 6193d9a792dSrmind /* NAT algorithm. */ 6203d9a792dSrmind switch (npf_nat_getalgo(nt)) { 6213d9a792dSrmind case NPF_ALGO_NETMAP: 6223d9a792dSrmind algo = "algo netmap "; 6233d9a792dSrmind break; 6243d9a792dSrmind case NPF_ALGO_IPHASH: 6253d9a792dSrmind algo = "algo ip-hash "; 6263d9a792dSrmind break; 6273d9a792dSrmind case NPF_ALGO_RR: 6283d9a792dSrmind algo = "algo round-robin "; 6293d9a792dSrmind break; 6303d9a792dSrmind case NPF_ALGO_NPT66: 6313d9a792dSrmind algo = "algo npt66 "; 6323d9a792dSrmind break; 6333d9a792dSrmind default: 6343d9a792dSrmind algo = ""; 6353d9a792dSrmind break; 6363d9a792dSrmind } 6373d9a792dSrmind 638b899bfd9Srmind /* XXX also handle "any" */ 6393d9a792dSrmind 6404e592132Srmind /* Print out the NAT policy with the filter criteria. */ 641b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "map %s %s %s%s%s %s %s pass ", 6428274d601Srmind ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic", 6433d9a792dSrmind algo, (flags & NPF_NAT_PORTS) ? "" : "no-ports ", 6448274d601Srmind seg1, arrow, seg2); 6454e592132Srmind npfctl_print_filter(ctx, rl); 646f8006a40Schristos npfctl_print_id(ctx, rl); 647b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "\n"); 6484e592132Srmind free(seg); 6494e592132Srmind } 6504e592132Srmind 6514e592132Srmind static void 6524e592132Srmind npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl) 6534e592132Srmind { 6541e7342c1Srmind const char *name = npf_table_getname(tl); 6551e2389edSrmind const unsigned type = npf_table_gettype(tl); 6561e2389edSrmind const char *table_types[] = { 6573d9a792dSrmind [NPF_TABLE_IPSET] = "ipset", 6583d9a792dSrmind [NPF_TABLE_LPM] = "lpm", 6593d9a792dSrmind [NPF_TABLE_CONST] = "const", 6601e2389edSrmind }; 6614e592132Srmind 662d116583eSrmind if (name[0] == '.') { 663d116583eSrmind /* Internal tables use dot and are hidden. */ 664d116583eSrmind return; 665d116583eSrmind } 6661e2389edSrmind assert(type < __arraycount(table_types)); 667b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, 668b899bfd9Srmind "table <%s> type %s\n", name, table_types[type]); 669b899bfd9Srmind } 670b899bfd9Srmind 671b899bfd9Srmind static void 672b899bfd9Srmind npfctl_print_params(npf_conf_info_t *ctx, nl_config_t *ncf) 673b899bfd9Srmind { 674b899bfd9Srmind nl_iter_t i = NPF_ITER_BEGIN; 675b899bfd9Srmind int val, defval, *dval; 676b899bfd9Srmind const char *name; 677b899bfd9Srmind 678b899bfd9Srmind dval = ctx->validating ? NULL : &defval; 679b899bfd9Srmind while ((name = npf_param_iterate(ncf, &i, &val, dval)) != NULL) { 680b899bfd9Srmind if (dval && val == *dval) { 681b899bfd9Srmind continue; 682b899bfd9Srmind } 683b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, "set %s %d\n", name, val); 684b899bfd9Srmind } 685b899bfd9Srmind print_linesep(ctx); 6864e592132Srmind } 6874e592132Srmind 6884e592132Srmind int 6894e592132Srmind npfctl_config_show(int fd) 6904e592132Srmind { 691b899bfd9Srmind npf_conf_info_t *ctx = npfctl_show_init(); 6924e592132Srmind nl_config_t *ncf; 693f75d79ebSchristos bool loaded; 6944e592132Srmind 6954e592132Srmind if (fd) { 696f75d79ebSchristos ncf = npf_config_retrieve(fd); 6974e592132Srmind if (ncf == NULL) { 6984e592132Srmind return errno; 6994e592132Srmind } 700f75d79ebSchristos loaded = npf_config_loaded_p(ncf); 701b899bfd9Srmind ctx->validating = false; 702b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, 703b899bfd9Srmind "# filtering:\t%s\n# config:\t%s\n", 704f75d79ebSchristos npf_config_active_p(ncf) ? "active" : "inactive", 7054e592132Srmind loaded ? "loaded" : "empty"); 7064e592132Srmind print_linesep(ctx); 7074e592132Srmind } else { 7084e592132Srmind ncf = npfctl_config_ref(); 70907861232Srmind npfctl_config_build(); 710b899bfd9Srmind ctx->validating = true; 7114e592132Srmind loaded = true; 7124e592132Srmind } 7131e7342c1Srmind ctx->conf = ncf; 7144e592132Srmind 7154e592132Srmind if (loaded) { 7164e592132Srmind nl_rule_t *rl; 7174e592132Srmind nl_rproc_t *rp; 7184e592132Srmind nl_nat_t *nt; 7194e592132Srmind nl_table_t *tl; 720dadc88e3Srmind nl_iter_t i; 7213d9a792dSrmind unsigned level; 7224e592132Srmind 723b899bfd9Srmind npfctl_print_params(ctx, ncf); 724b899bfd9Srmind 725dadc88e3Srmind i = NPF_ITER_BEGIN; 726dadc88e3Srmind while ((tl = npf_table_iterate(ncf, &i)) != NULL) { 7274e592132Srmind npfctl_print_table(ctx, tl); 7284e592132Srmind } 7294e592132Srmind print_linesep(ctx); 7304e592132Srmind 731dadc88e3Srmind i = NPF_ITER_BEGIN; 732dadc88e3Srmind while ((rp = npf_rproc_iterate(ncf, &i)) != NULL) { 7334e592132Srmind const char *rpname = npf_rproc_getname(rp); 734b899bfd9Srmind ctx->fpos += fprintf(ctx->fp, 735b899bfd9Srmind "procedure \"%s\"\n", rpname); 7364e592132Srmind } 7374e592132Srmind print_linesep(ctx); 7384e592132Srmind 739dadc88e3Srmind i = NPF_ITER_BEGIN; 740dadc88e3Srmind while ((nt = npf_nat_iterate(ncf, &i)) != NULL) { 7414e592132Srmind npfctl_print_nat(ctx, nt); 7424e592132Srmind } 7434e592132Srmind print_linesep(ctx); 7444e592132Srmind 745dadc88e3Srmind i = NPF_ITER_BEGIN; 746dadc88e3Srmind while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) { 7474e592132Srmind print_indent(ctx, level); 748b899bfd9Srmind npfctl_print_rule(ctx, rl, level); 7494e592132Srmind } 7503d9a792dSrmind print_indent(ctx, 0); 7514e592132Srmind } 7524e592132Srmind npf_config_destroy(ncf); 7534e592132Srmind return 0; 7544e592132Srmind } 7554e592132Srmind 7564e592132Srmind int 7574e592132Srmind npfctl_ruleset_show(int fd, const char *ruleset_name) 7584e592132Srmind { 759b899bfd9Srmind npf_conf_info_t *ctx = npfctl_show_init(); 7604e592132Srmind nl_config_t *ncf; 7614e592132Srmind nl_rule_t *rl; 7623d9a792dSrmind unsigned level; 763dadc88e3Srmind nl_iter_t i; 7644e592132Srmind int error; 7654e592132Srmind 7664e592132Srmind ncf = npf_config_create(); 7671e7342c1Srmind ctx->conf = ncf; 7681e7342c1Srmind 7694e592132Srmind if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) { 7704e592132Srmind return error; 7714e592132Srmind } 772dadc88e3Srmind i = NPF_ITER_BEGIN; 773dadc88e3Srmind while ((rl = npf_rule_iterate(ncf, &i, &level)) != NULL) { 774b899bfd9Srmind npfctl_print_rule(ctx, rl, 0); 7754e592132Srmind } 7764e592132Srmind npf_config_destroy(ncf); 7774e592132Srmind return error; 7784e592132Srmind } 779