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