1 /* $NetBSD: npf_show.c,v 1.8 2013/11/22 18:42:02 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.8 2013/11/22 18:42:02 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("inet"); 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 NAME_AT 2 222 223 static const struct attr_keyword_mapent { 224 uint32_t mask; 225 uint32_t flags; 226 const char * val; 227 } attr_keyword_map[] = { 228 { F(GROUP)|F(DYNAMIC), F(GROUP), "group" }, 229 { F(DYNAMIC), F(DYNAMIC), "ruleset" }, 230 { F(GROUP)|F(PASS), 0, "block" }, 231 { F(GROUP)|F(PASS), F(PASS), "pass" }, 232 { F(RETRST)|F(RETICMP), F(RETRST)|F(RETICMP), "return" }, 233 { F(RETRST)|F(RETICMP), F(RETRST), "return-rst" }, 234 { F(RETRST)|F(RETICMP), F(RETICMP), "return-icmp" }, 235 { F(STATEFUL), F(STATEFUL), "stateful" }, 236 { F(DIMASK), F(IN), "in" }, 237 { F(DIMASK), F(OUT), "out" }, 238 { F(FINAL), F(FINAL), "final" }, 239 }; 240 241 static const struct mark_keyword_mapent { 242 u_int mark; 243 const char * token; 244 const char * sep; 245 char * (*printfn)(npf_conf_info_t *, const uint32_t *); 246 u_int fwords; 247 } mark_keyword_map[] = { 248 { BM_IPVER, "family %s", NULL, print_family, 1 }, 249 { BM_PROTO, "proto %s", NULL, print_proto, 1 }, 250 { BM_TCPFL, "flags %s", NULL, print_tcpflags, 2 }, 251 { BM_ICMP_TYPE, "icmp-type %s", NULL, print_number, 1 }, 252 { BM_ICMP_CODE, "code %s", NULL, print_number, 1 }, 253 254 { BM_SRC_CIDR, "from %s", ", ", print_address, 6 }, 255 { BM_SRC_TABLE, "from <%s>", NULL, print_table, 1 }, 256 { BM_SRC_PORTS, "port %s", ", ", print_portrange,2 }, 257 258 { BM_DST_CIDR, "to %s", ", ", print_address, 6 }, 259 { BM_DST_TABLE, "to <%s>", NULL, print_table, 1 }, 260 { BM_DST_PORTS, "port %s", ", ", print_portrange,2 }, 261 }; 262 263 static const char * __attribute__((format_arg(2))) 264 verified_fmt(const char *fmt, const char *t __unused) 265 { 266 return fmt; 267 } 268 269 static char * 270 scan_marks(npf_conf_info_t *ctx, const struct mark_keyword_mapent *mk, 271 const uint32_t *marks, size_t mlen) 272 { 273 char buf[2048], *vals[256], *p; 274 size_t nvals = 0; 275 276 /* Scan for the marks and extract the values. */ 277 mlen /= sizeof(uint32_t); 278 while (mlen > 2) { 279 const uint32_t m = *marks++; 280 const u_int nwords = *marks++; 281 282 if ((mlen -= 2) < nwords) { 283 errx(EXIT_FAILURE, "byte-code marking inconsistency"); 284 } 285 if (m == mk->mark) { 286 /* Value is processed by the print function. */ 287 assert(mk->fwords == nwords); 288 vals[nvals++] = mk->printfn(ctx, marks); 289 } 290 marks += nwords; 291 mlen -= nwords; 292 } 293 if (nvals == 0) { 294 return NULL; 295 } 296 assert(nvals == 1 || mk->sep != NULL); 297 298 /* 299 * Join all the values and print. Add curly brackets if there 300 * is more than value and it can be a set. 301 */ 302 if (!join(buf, sizeof(buf), nvals, vals, mk->sep ? mk->sep : "")) { 303 errx(EXIT_FAILURE, "out of memory while parsing the rule"); 304 } 305 easprintf(&p, nvals > 1 ? "{ %s }" : "%s", buf); 306 307 for (u_int i = 0; i < nvals; i++) { 308 free(vals[i]); 309 } 310 return p; 311 } 312 313 static void 314 npfctl_print_filter(npf_conf_info_t *ctx, nl_rule_t *rl) 315 { 316 const void *marks; 317 size_t mlen; 318 319 /* BPF filter criteria described by the byte-code marks. */ 320 marks = npf_rule_getinfo(rl, &mlen); 321 for (u_int i = 0; i < __arraycount(mark_keyword_map); i++) { 322 const struct mark_keyword_mapent *mk = &mark_keyword_map[i]; 323 char *val; 324 325 if ((val = scan_marks(ctx, mk, marks, mlen)) != NULL) { 326 fprintf(ctx->fp, verified_fmt(mk->token, "%s"), val); 327 fputs(" ", ctx->fp); 328 free(val); 329 } 330 } 331 if (!mlen) { 332 fputs("all ", ctx->fp); 333 } 334 } 335 336 static void 337 npfctl_print_rule(npf_conf_info_t *ctx, nl_rule_t *rl) 338 { 339 const uint32_t attr = npf_rule_getattr(rl); 340 const char *rproc, *ifname, *name; 341 342 /* Rule attributes/flags. */ 343 for (u_int i = 0; i < __arraycount(attr_keyword_map); i++) { 344 const struct attr_keyword_mapent *ak = &attr_keyword_map[i]; 345 346 if (i == NAME_AT && (name = npf_rule_getname(rl)) != NULL) { 347 fprintf(ctx->fp, "\"%s\" ", name); 348 } 349 if ((attr & ak->mask) == ak->flags) { 350 fprintf(ctx->fp, "%s ", ak->val); 351 } 352 } 353 if ((ifname = npf_rule_getinterface(rl)) != NULL) { 354 fprintf(ctx->fp, "on %s ", ifname); 355 } 356 357 if ((attr & (NPF_RULE_GROUP | NPF_RULE_DYNAMIC)) == NPF_RULE_GROUP) { 358 /* Group; done. */ 359 fputs("\n", ctx->fp); 360 return; 361 } 362 363 /* Print filter criteria. */ 364 npfctl_print_filter(ctx, rl); 365 366 /* Rule procedure. */ 367 if ((rproc = npf_rule_getproc(rl)) != NULL) { 368 fprintf(ctx->fp, "apply \"%s\"", rproc); 369 } 370 fputs("\n", ctx->fp); 371 } 372 373 static void 374 npfctl_print_nat(npf_conf_info_t *ctx, nl_nat_t *nt) 375 { 376 nl_rule_t *rl = (nl_nat_t *)nt; 377 const char *ifname, *seg1, *seg2, *arrow; 378 npf_addr_t addr; 379 in_port_t port; 380 size_t alen; 381 char *seg; 382 383 /* Get the interface. */ 384 ifname = npf_rule_getinterface(rl); 385 assert(ifname != NULL); 386 387 /* Get the translation address (and port, if used). */ 388 npf_nat_getmap(nt, &addr, &alen, &port); 389 seg = npfctl_print_addrmask(alen, &addr, NPF_NO_NETMASK); 390 if (port) { 391 char *p; 392 easprintf(&p, "%s port %u", seg, port); 393 free(seg), seg = p; 394 } 395 seg1 = seg2 = "any"; 396 397 /* Get the NAT type and determine the translation segment. */ 398 switch (npf_nat_gettype(nt)) { 399 case NPF_NATIN: 400 arrow = "<-"; 401 seg1 = seg; 402 break; 403 case NPF_NATOUT: 404 arrow = "->"; 405 seg2 = seg; 406 break; 407 default: 408 assert(false); 409 } 410 411 /* Print out the NAT policy with the filter criteria. */ 412 fprintf(ctx->fp, "map %s dynamic %s %s %s pass ", 413 ifname, seg1, arrow, seg2); 414 npfctl_print_filter(ctx, rl); 415 fputs("\n", ctx->fp); 416 free(seg); 417 } 418 419 static void 420 npfctl_print_table(npf_conf_info_t *ctx, nl_table_t *tl) 421 { 422 const char *name = npf_table_getname(tl); 423 const int type = npf_table_gettype(tl); 424 425 if (name[0] == '.') { 426 /* Internal tables use dot and are hidden. */ 427 return; 428 } 429 430 fprintf(ctx->fp, "table <%s> type %s\n", name, 431 (type == NPF_TABLE_HASH) ? "hash" : 432 (type == NPF_TABLE_TREE) ? "tree" : 433 "unknown"); 434 } 435 436 int 437 npfctl_config_show(int fd) 438 { 439 npf_conf_info_t *ctx = &stdout_ctx; 440 nl_config_t *ncf; 441 bool active, loaded; 442 443 if (fd) { 444 ncf = npf_config_retrieve(fd, &active, &loaded); 445 if (ncf == NULL) { 446 return errno; 447 } 448 fprintf(ctx->fp, "Filtering:\t%s\nConfiguration:\t%s\n", 449 active ? "active" : "inactive", 450 loaded ? "loaded" : "empty"); 451 print_linesep(ctx); 452 } else { 453 npfctl_config_send(0, NULL); 454 ncf = npfctl_config_ref(); 455 loaded = true; 456 } 457 ctx->conf = ncf; 458 459 if (loaded) { 460 nl_rule_t *rl; 461 nl_rproc_t *rp; 462 nl_nat_t *nt; 463 nl_table_t *tl; 464 u_int level; 465 466 while ((tl = npf_table_iterate(ncf)) != NULL) { 467 npfctl_print_table(ctx, tl); 468 } 469 print_linesep(ctx); 470 471 while ((rp = npf_rproc_iterate(ncf)) != NULL) { 472 const char *rpname = npf_rproc_getname(rp); 473 fprintf(ctx->fp, "procedure \"%s\"\n", rpname); 474 } 475 print_linesep(ctx); 476 477 while ((nt = npf_nat_iterate(ncf)) != NULL) { 478 npfctl_print_nat(ctx, nt); 479 } 480 print_linesep(ctx); 481 482 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) { 483 print_indent(ctx, level); 484 npfctl_print_rule(ctx, rl); 485 } 486 print_linesep(ctx); 487 } 488 npf_config_destroy(ncf); 489 return 0; 490 } 491 492 int 493 npfctl_ruleset_show(int fd, const char *ruleset_name) 494 { 495 npf_conf_info_t *ctx = &stdout_ctx; 496 nl_config_t *ncf; 497 nl_rule_t *rl; 498 u_int level; 499 int error; 500 501 ncf = npf_config_create(); 502 ctx->conf = ncf; 503 504 if ((error = _npf_ruleset_list(fd, ruleset_name, ncf)) != 0) { 505 return error; 506 } 507 while ((rl = npf_rule_iterate(ncf, &level)) != NULL) { 508 npfctl_print_rule(ctx, rl); 509 } 510 npf_config_destroy(ncf); 511 return error; 512 } 513