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