1 /*- 2 * Copyright (c) 2002-2003 Luigi Rizzo 3 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 4 * Copyright (c) 1994 Ugen J.S.Antsilevich 5 * 6 * Idea and grammar partially left from: 7 * Copyright (c) 1993 Daniel Boulet 8 * 9 * Redistribution and use in source forms, with and without modification, 10 * are permitted provided that this entire comment appears intact. 11 * 12 * Redistribution in binary form may occur without any restrictions. 13 * Obviously, it would be nice if you gave credit where credit is due 14 * but requiring it would be too onerous. 15 * 16 * This software is provided ``AS IS'' without any warranties of any kind. 17 * 18 * NEW command line interface for IP firewall facility 19 */ 20 21 #include <sys/types.h> 22 #include <sys/param.h> 23 #include <sys/socket.h> 24 #include <sys/sockio.h> 25 #include <sys/sysctl.h> 26 27 #include "ipfw2.h" 28 29 #include <ctype.h> 30 #include <err.h> 31 #include <errno.h> 32 #include <grp.h> 33 #include <jail.h> 34 #include <netdb.h> 35 #include <pwd.h> 36 #include <stdio.h> 37 #include <stdarg.h> 38 #include <stdint.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <sysexits.h> 42 #include <time.h> /* ctime */ 43 #include <timeconv.h> /* _long_to_time */ 44 #include <unistd.h> 45 #include <fcntl.h> 46 #include <stddef.h> /* offsetof */ 47 48 #include <net/ethernet.h> 49 #include <net/if.h> /* only IFNAMSIZ */ 50 #include <netinet/in.h> 51 #include <netinet/in_systm.h> /* only n_short, n_long */ 52 #include <netinet/ip.h> 53 #include <netinet/ip_icmp.h> 54 #include <netinet/ip_fw.h> 55 #include <netinet/tcp.h> 56 #include <arpa/inet.h> 57 58 struct cmdline_opts g_co; /* global options */ 59 60 struct format_opts { 61 int bcwidth; 62 int pcwidth; 63 int show_counters; 64 int show_time; /* show timestamp */ 65 uint32_t set_mask; /* enabled sets mask */ 66 uint32_t flags; /* request flags */ 67 uint32_t first; /* first rule to request */ 68 uint32_t last; /* last rule to request */ 69 uint32_t dcnt; /* number of dynamic states */ 70 ipfw_obj_ctlv *tstate; /* table state data */ 71 }; 72 73 int resvd_set_number = RESVD_SET; 74 75 static int ipfw_socket = -1; 76 77 #define CHECK_LENGTH(v, len) do { \ 78 if ((v) < (len)) \ 79 errx(EX_DATAERR, "Rule too long"); \ 80 } while (0) 81 /* 82 * Check if we have enough space in cmd buffer. Note that since 83 * first 8? u32 words are reserved by reserved header, full cmd 84 * buffer can't be used, so we need to protect from buffer overrun 85 * only. At the beginning, cblen is less than actual buffer size by 86 * size of ipfw_insn_u32 instruction + 1 u32 work. This eliminates need 87 * for checking small instructions fitting in given range. 88 * We also (ab)use the fact that ipfw_insn is always the first field 89 * for any custom instruction. 90 */ 91 #define CHECK_CMDLEN CHECK_LENGTH(cblen, F_LEN((ipfw_insn *)cmd)) 92 93 #define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ 94 if (!av[0]) \ 95 errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ 96 if (_substrcmp(*av, "tablearg") == 0) { \ 97 arg = IP_FW_TARG; \ 98 break; \ 99 } \ 100 \ 101 { \ 102 long _xval; \ 103 char *end; \ 104 \ 105 _xval = strtol(*av, &end, 10); \ 106 \ 107 if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \ 108 errx(EX_DATAERR, "%s: invalid argument: %s", \ 109 match_value(s_x, tok), *av); \ 110 \ 111 if (errno == ERANGE || _xval < min || _xval > max) \ 112 errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ 113 match_value(s_x, tok), min, max, *av); \ 114 \ 115 if (_xval == IP_FW_TARG) \ 116 errx(EX_DATAERR, "%s: illegal argument value: %s", \ 117 match_value(s_x, tok), *av); \ 118 arg = _xval; \ 119 } \ 120 } while (0) 121 122 static struct _s_x f_tcpflags[] = { 123 { "syn", TH_SYN }, 124 { "fin", TH_FIN }, 125 { "ack", TH_ACK }, 126 { "psh", TH_PUSH }, 127 { "rst", TH_RST }, 128 { "urg", TH_URG }, 129 { "tcp flag", 0 }, 130 { NULL, 0 } 131 }; 132 133 static struct _s_x f_tcpopts[] = { 134 { "mss", IP_FW_TCPOPT_MSS }, 135 { "maxseg", IP_FW_TCPOPT_MSS }, 136 { "window", IP_FW_TCPOPT_WINDOW }, 137 { "sack", IP_FW_TCPOPT_SACK }, 138 { "ts", IP_FW_TCPOPT_TS }, 139 { "timestamp", IP_FW_TCPOPT_TS }, 140 { "cc", IP_FW_TCPOPT_CC }, 141 { "tcp option", 0 }, 142 { NULL, 0 } 143 }; 144 145 /* 146 * IP options span the range 0 to 255 so we need to remap them 147 * (though in fact only the low 5 bits are significant). 148 */ 149 static struct _s_x f_ipopts[] = { 150 { "ssrr", IP_FW_IPOPT_SSRR}, 151 { "lsrr", IP_FW_IPOPT_LSRR}, 152 { "rr", IP_FW_IPOPT_RR}, 153 { "ts", IP_FW_IPOPT_TS}, 154 { "ip option", 0 }, 155 { NULL, 0 } 156 }; 157 158 static struct _s_x f_iptos[] = { 159 { "lowdelay", IPTOS_LOWDELAY}, 160 { "throughput", IPTOS_THROUGHPUT}, 161 { "reliability", IPTOS_RELIABILITY}, 162 { "mincost", IPTOS_MINCOST}, 163 { "congestion", IPTOS_ECN_CE}, 164 { "ecntransport", IPTOS_ECN_ECT0}, 165 { "ip tos option", 0}, 166 { NULL, 0 } 167 }; 168 169 static struct _s_x f_ipoff[] = { 170 { "rf", IP_RF >> 8 }, 171 { "df", IP_DF >> 8 }, 172 { "mf", IP_MF >> 8 }, 173 { "offset", 0x1 }, 174 { NULL, 0} 175 }; 176 177 struct _s_x f_ipdscp[] = { 178 { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */ 179 { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */ 180 { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */ 181 { "af21", IPTOS_DSCP_AF21 >> 2 }, /* 010010 */ 182 { "af22", IPTOS_DSCP_AF22 >> 2 }, /* 010100 */ 183 { "af23", IPTOS_DSCP_AF23 >> 2 }, /* 010110 */ 184 { "af31", IPTOS_DSCP_AF31 >> 2 }, /* 011010 */ 185 { "af32", IPTOS_DSCP_AF32 >> 2 }, /* 011100 */ 186 { "af33", IPTOS_DSCP_AF33 >> 2 }, /* 011110 */ 187 { "af41", IPTOS_DSCP_AF41 >> 2 }, /* 100010 */ 188 { "af42", IPTOS_DSCP_AF42 >> 2 }, /* 100100 */ 189 { "af43", IPTOS_DSCP_AF43 >> 2 }, /* 100110 */ 190 { "be", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */ 191 { "va", IPTOS_DSCP_VA >> 2 }, /* 101100 */ 192 { "ef", IPTOS_DSCP_EF >> 2 }, /* 101110 */ 193 { "cs0", IPTOS_DSCP_CS0 >> 2 }, /* 000000 */ 194 { "cs1", IPTOS_DSCP_CS1 >> 2 }, /* 001000 */ 195 { "cs2", IPTOS_DSCP_CS2 >> 2 }, /* 010000 */ 196 { "cs3", IPTOS_DSCP_CS3 >> 2 }, /* 011000 */ 197 { "cs4", IPTOS_DSCP_CS4 >> 2 }, /* 100000 */ 198 { "cs5", IPTOS_DSCP_CS5 >> 2 }, /* 101000 */ 199 { "cs6", IPTOS_DSCP_CS6 >> 2 }, /* 110000 */ 200 { "cs7", IPTOS_DSCP_CS7 >> 2 }, /* 100000 */ 201 { NULL, 0 } 202 }; 203 204 static struct _s_x limit_masks[] = { 205 {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, 206 {"src-addr", DYN_SRC_ADDR}, 207 {"src-port", DYN_SRC_PORT}, 208 {"dst-addr", DYN_DST_ADDR}, 209 {"dst-port", DYN_DST_PORT}, 210 {NULL, 0} 211 }; 212 213 /* 214 * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines 215 * This is only used in this code. 216 */ 217 #define IPPROTO_ETHERTYPE 0x1000 218 static struct _s_x ether_types[] = { 219 /* 220 * Note, we cannot use "-:&/" in the names because they are field 221 * separators in the type specifications. Also, we use s = NULL as 222 * end-delimiter, because a type of 0 can be legal. 223 */ 224 { "ip", 0x0800 }, 225 { "ipv4", 0x0800 }, 226 { "ipv6", 0x86dd }, 227 { "arp", 0x0806 }, 228 { "rarp", 0x8035 }, 229 { "vlan", 0x8100 }, 230 { "loop", 0x9000 }, 231 { "trail", 0x1000 }, 232 { "at", 0x809b }, 233 { "atalk", 0x809b }, 234 { "aarp", 0x80f3 }, 235 { "pppoe_disc", 0x8863 }, 236 { "pppoe_sess", 0x8864 }, 237 { "ipx_8022", 0x00E0 }, 238 { "ipx_8023", 0x0000 }, 239 { "ipx_ii", 0x8137 }, 240 { "ipx_snap", 0x8137 }, 241 { "ipx", 0x8137 }, 242 { "ns", 0x0600 }, 243 { NULL, 0 } 244 }; 245 246 static struct _s_x rule_eactions[] = { 247 { "nat64clat", TOK_NAT64CLAT }, 248 { "nat64lsn", TOK_NAT64LSN }, 249 { "nat64stl", TOK_NAT64STL }, 250 { "nptv6", TOK_NPTV6 }, 251 { "tcp-setmss", TOK_TCPSETMSS }, 252 { NULL, 0 } /* terminator */ 253 }; 254 255 static struct _s_x rule_actions[] = { 256 { "abort6", TOK_ABORT6 }, 257 { "abort", TOK_ABORT }, 258 { "accept", TOK_ACCEPT }, 259 { "pass", TOK_ACCEPT }, 260 { "allow", TOK_ACCEPT }, 261 { "permit", TOK_ACCEPT }, 262 { "count", TOK_COUNT }, 263 { "pipe", TOK_PIPE }, 264 { "queue", TOK_QUEUE }, 265 { "divert", TOK_DIVERT }, 266 { "tee", TOK_TEE }, 267 { "netgraph", TOK_NETGRAPH }, 268 { "ngtee", TOK_NGTEE }, 269 { "fwd", TOK_FORWARD }, 270 { "forward", TOK_FORWARD }, 271 { "skipto", TOK_SKIPTO }, 272 { "deny", TOK_DENY }, 273 { "drop", TOK_DENY }, 274 { "reject", TOK_REJECT }, 275 { "reset6", TOK_RESET6 }, 276 { "reset", TOK_RESET }, 277 { "unreach6", TOK_UNREACH6 }, 278 { "unreach", TOK_UNREACH }, 279 { "check-state", TOK_CHECKSTATE }, 280 { "//", TOK_COMMENT }, 281 { "nat", TOK_NAT }, 282 { "reass", TOK_REASS }, 283 { "setfib", TOK_SETFIB }, 284 { "setdscp", TOK_SETDSCP }, 285 { "call", TOK_CALL }, 286 { "return", TOK_RETURN }, 287 { "eaction", TOK_EACTION }, 288 { "tcp-setmss", TOK_TCPSETMSS }, 289 { "setmark", TOK_SETMARK }, 290 { NULL, 0 } /* terminator */ 291 }; 292 293 static struct _s_x return_types[] = { 294 { "next-rulenum", RETURN_NEXT_RULENUM }, 295 { "next-rule", RETURN_NEXT_RULE }, 296 { NULL, 0 } /* terminator */ 297 }; 298 299 static struct _s_x rule_action_params[] = { 300 { "altq", TOK_ALTQ }, 301 { "log", TOK_LOG }, 302 { "tag", TOK_TAG }, 303 { "untag", TOK_UNTAG }, 304 { NULL, 0 } /* terminator */ 305 }; 306 307 /* 308 * The 'lookup' instruction accepts one of the following arguments. 309 * Arguments are passed as o.arg1 and o->value in O_DST_LOOKUP option. 310 */ 311 static struct _s_x lookup_keys[] = { 312 { "dst-ip", LOOKUP_DST_IP }, 313 { "src-ip", LOOKUP_SRC_IP }, 314 { "dst-port", LOOKUP_DST_PORT }, 315 { "src-port", LOOKUP_SRC_PORT }, 316 { "dst-mac", LOOKUP_DST_MAC }, 317 { "src-mac", LOOKUP_SRC_MAC }, 318 { "uid", LOOKUP_UID }, 319 { "jail", LOOKUP_JAIL }, 320 { "dscp", LOOKUP_DSCP }, 321 { "mark", LOOKUP_MARK }, 322 { "rulenum", LOOKUP_RULENUM }, 323 { NULL, 0 }, 324 }; 325 326 /* 327 * table(name,valuename=value) instruction accepts one of the 328 * following arguments as valuename. 329 */ 330 static struct _s_x tvalue_names[] = { 331 { "tag", TVALUE_TAG }, 332 { "pipe", TVALUE_PIPE }, 333 { "divert", TVALUE_DIVERT }, 334 { "skipto", TVALUE_SKIPTO }, 335 { "netgraph", TVALUE_NETGRAPH }, 336 { "fib", TVALUE_FIB }, 337 { "nat", TVALUE_NAT }, 338 { "nh4", TVALUE_NH4 }, 339 { "dscp", TVALUE_DSCP }, 340 { "limit", TVALUE_LIMIT }, 341 { "mark", TVALUE_MARK }, 342 { NULL, 0 } 343 }; 344 345 static struct _s_x rule_options[] = { 346 { "tagged", TOK_TAGGED }, 347 { "uid", TOK_UID }, 348 { "gid", TOK_GID }, 349 { "jail", TOK_JAIL }, 350 { "in", TOK_IN }, 351 { "limit", TOK_LIMIT }, 352 { "set-limit", TOK_SETLIMIT }, 353 { "keep-state", TOK_KEEPSTATE }, 354 { "record-state", TOK_RECORDSTATE }, 355 { "bridged", TOK_LAYER2 }, 356 { "layer2", TOK_LAYER2 }, 357 { "out", TOK_OUT }, 358 { "diverted", TOK_DIVERTED }, 359 { "diverted-loopback", TOK_DIVERTEDLOOPBACK }, 360 { "diverted-output", TOK_DIVERTEDOUTPUT }, 361 { "xmit", TOK_XMIT }, 362 { "recv", TOK_RECV }, 363 { "via", TOK_VIA }, 364 { "fragment", TOK_FRAG }, 365 { "frag", TOK_FRAG }, 366 { "fib", TOK_FIB }, 367 { "ipoptions", TOK_IPOPTS }, 368 { "ipopts", TOK_IPOPTS }, 369 { "iplen", TOK_IPLEN }, 370 { "ipid", TOK_IPID }, 371 { "ipprecedence", TOK_IPPRECEDENCE }, 372 { "dscp", TOK_DSCP }, 373 { "iptos", TOK_IPTOS }, 374 { "ipttl", TOK_IPTTL }, 375 { "ipversion", TOK_IPVER }, 376 { "ipver", TOK_IPVER }, 377 { "estab", TOK_ESTAB }, 378 { "established", TOK_ESTAB }, 379 { "setup", TOK_SETUP }, 380 { "sockarg", TOK_SOCKARG }, 381 { "tcpdatalen", TOK_TCPDATALEN }, 382 { "tcpflags", TOK_TCPFLAGS }, 383 { "tcpflgs", TOK_TCPFLAGS }, 384 { "tcpmss", TOK_TCPMSS }, 385 { "tcpoptions", TOK_TCPOPTS }, 386 { "tcpopts", TOK_TCPOPTS }, 387 { "tcpseq", TOK_TCPSEQ }, 388 { "tcpack", TOK_TCPACK }, 389 { "tcpwin", TOK_TCPWIN }, 390 { "icmptype", TOK_ICMPTYPES }, 391 { "icmptypes", TOK_ICMPTYPES }, 392 { "dst-ip", TOK_DSTIP }, 393 { "src-ip", TOK_SRCIP }, 394 { "dst-port", TOK_DSTPORT }, 395 { "src-port", TOK_SRCPORT }, 396 { "dst-mac", TOK_DSTMAC }, 397 { "src-mac", TOK_SRCMAC }, 398 { "proto", TOK_PROTO }, 399 { "MAC", TOK_MAC }, 400 { "mac", TOK_MAC }, 401 { "mac-type", TOK_MACTYPE }, 402 { "verrevpath", TOK_VERREVPATH }, 403 { "versrcreach", TOK_VERSRCREACH }, 404 { "antispoof", TOK_ANTISPOOF }, 405 { "ipsec", TOK_IPSEC }, 406 { "icmp6type", TOK_ICMP6TYPES }, 407 { "icmp6types", TOK_ICMP6TYPES }, 408 { "ext6hdr", TOK_EXT6HDR }, 409 { "flow-id", TOK_FLOWID }, 410 { "ipv6", TOK_IPV6 }, 411 { "ip6", TOK_IPV6 }, 412 { "ipv4", TOK_IPV4 }, 413 { "ip4", TOK_IPV4 }, 414 { "dst-ipv6", TOK_DSTIP6 }, 415 { "dst-ip6", TOK_DSTIP6 }, 416 { "src-ipv6", TOK_SRCIP6 }, 417 { "src-ip6", TOK_SRCIP6 }, 418 { "lookup", TOK_LOOKUP }, 419 { "flow", TOK_FLOW }, 420 { "mark", TOK_MARK }, 421 { "defer-action", TOK_SKIPACTION }, 422 { "defer-immediate-action", TOK_SKIPACTION }, 423 { "//", TOK_COMMENT }, 424 425 { "not", TOK_NOT }, /* pseudo option */ 426 { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ 427 { "or", TOK_OR }, /* pseudo option */ 428 { "|", /* escape */ TOK_OR }, /* pseudo option */ 429 { "{", TOK_STARTBRACE }, /* pseudo option */ 430 { "(", TOK_STARTBRACE }, /* pseudo option */ 431 { "}", TOK_ENDBRACE }, /* pseudo option */ 432 { ")", TOK_ENDBRACE }, /* pseudo option */ 433 { NULL, 0 } /* terminator */ 434 }; 435 436 void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg); 437 static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, 438 ipfw_cfg_lheader **pcfg, size_t *psize); 439 static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, 440 ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av); 441 static void ipfw_list_tifaces(void); 442 443 struct tidx; 444 static uint32_t pack_object(struct tidx *tstate, const char *name, int otype); 445 static uint32_t pack_table(struct tidx *tstate, const char *name); 446 447 static char *table_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx); 448 static void object_sort_ctlv(ipfw_obj_ctlv *ctlv); 449 static char *object_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx, 450 uint16_t type); 451 452 int 453 is_ipfw(void) 454 { 455 return (g_co.prog == cmdline_prog_ipfw); 456 } 457 458 /* 459 * Simple string buffer API. 460 * Used to simplify buffer passing between function and for 461 * transparent overrun handling. 462 */ 463 464 /* 465 * Allocates new buffer of given size @sz. 466 * 467 * Returns 0 on success. 468 */ 469 int 470 bp_alloc(struct buf_pr *b, size_t size) 471 { 472 memset(b, 0, sizeof(struct buf_pr)); 473 474 if ((b->buf = calloc(1, size)) == NULL) 475 return (ENOMEM); 476 477 b->ptr = b->buf; 478 b->size = size; 479 b->avail = b->size; 480 481 return (0); 482 } 483 484 void 485 bp_free(struct buf_pr *b) 486 { 487 488 free(b->buf); 489 } 490 491 /* 492 * Flushes buffer so new writer start from beginning. 493 */ 494 void 495 bp_flush(struct buf_pr *b) 496 { 497 498 b->ptr = b->buf; 499 b->avail = b->size; 500 b->buf[0] = '\0'; 501 } 502 503 /* 504 * Print message specified by @format and args. 505 * Automatically manage buffer space and transparently handle 506 * buffer overruns. 507 * 508 * Returns number of bytes that should have been printed. 509 */ 510 int 511 bprintf(struct buf_pr *b, const char *format, ...) 512 { 513 va_list args; 514 int i; 515 516 va_start(args, format); 517 518 i = vsnprintf(b->ptr, b->avail, format, args); 519 va_end(args); 520 521 if (i < 0 || (size_t)i > b->avail) { 522 /* Overflow or print error */ 523 b->avail = 0; 524 } else { 525 b->ptr += i; 526 b->avail -= i; 527 } 528 529 b->needed += i; 530 531 return (i); 532 } 533 534 /* 535 * Special values printer for tablearg-aware opcodes. 536 */ 537 void 538 bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg) 539 { 540 541 if (str != NULL) 542 bprintf(bp, "%s", str); 543 if (arg == IP_FW_TARG) 544 bprintf(bp, "tablearg"); 545 else 546 bprintf(bp, "%u", arg); 547 } 548 549 /* 550 * Helper routine to print a possibly unaligned uint64_t on 551 * various platform. If width > 0, print the value with 552 * the desired width, followed by a space; 553 * otherwise, return the required width. 554 */ 555 int 556 pr_u64(struct buf_pr *b, void *pd, int width) 557 { 558 #ifdef TCC 559 #define U64_FMT "I64" 560 #else 561 #define U64_FMT "llu" 562 #endif 563 uint64_t u; 564 unsigned long long d; 565 566 bcopy (pd, &u, sizeof(u)); 567 d = u; 568 return (width > 0) ? 569 bprintf(b, "%*" U64_FMT " ", width, d) : 570 snprintf(NULL, 0, "%" U64_FMT, d) ; 571 #undef U64_FMT 572 } 573 574 575 void * 576 safe_calloc(size_t number, size_t size) 577 { 578 void *ret = calloc(number, size); 579 580 if (ret == NULL) 581 err(EX_OSERR, "calloc"); 582 return ret; 583 } 584 585 void * 586 safe_realloc(void *ptr, size_t size) 587 { 588 void *ret = realloc(ptr, size); 589 590 if (ret == NULL) 591 err(EX_OSERR, "realloc"); 592 return ret; 593 } 594 595 /* 596 * Compare things like interface or table names. 597 */ 598 int 599 stringnum_cmp(const char *a, const char *b) 600 { 601 int la, lb; 602 603 la = strlen(a); 604 lb = strlen(b); 605 606 if (la > lb) 607 return (1); 608 else if (la < lb) 609 return (-01); 610 611 return (strcmp(a, b)); 612 } 613 614 struct debug_header { 615 uint16_t cmd_type; 616 uint16_t spare1; 617 uint32_t opt_name; 618 uint32_t total_len; 619 uint32_t spare2; 620 }; 621 622 /* 623 * conditionally runs the command. 624 * Selected options or negative -> getsockopt 625 */ 626 int 627 do_cmd(int optname, void *optval, uintptr_t optlen) 628 { 629 int i; 630 631 if (g_co.debug_only) { 632 struct debug_header dbg = { 633 .cmd_type = 1, 634 .opt_name = optname, 635 .total_len = optlen + sizeof(struct debug_header), 636 }; 637 write(1, &dbg, sizeof(dbg)); 638 write(1, optval, optlen); 639 } 640 641 if (g_co.test_only) 642 return (0); 643 644 if (ipfw_socket == -1) 645 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 646 if (ipfw_socket < 0) 647 err(EX_UNAVAILABLE, "socket"); 648 649 if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET || 650 optname == IP_FW_ADD || optname == IP_FW3 || 651 optname == IP_FW_NAT_GET_CONFIG || 652 optname < 0 || 653 optname == IP_FW_NAT_GET_LOG) { 654 if (optname < 0) 655 optname = -optname; 656 i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval, 657 (socklen_t *)optlen); 658 } else { 659 i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen); 660 } 661 return (i); 662 } 663 664 /* 665 * do_set3 - pass ipfw control cmd to kernel 666 * @optname: option name 667 * @optval: pointer to option data 668 * @optlen: option length 669 * 670 * Assumes op3 header is already embedded. 671 * Calls setsockopt() with IP_FW3 as kernel-visible opcode. 672 * Returns 0 on success or errno otherwise. 673 */ 674 int 675 do_set3(int optname, ip_fw3_opheader *op3, size_t optlen) 676 { 677 678 op3->opcode = optname; 679 op3->version = IP_FW3_OPVER; /* use last version */ 680 681 if (g_co.debug_only) { 682 struct debug_header dbg = { 683 .cmd_type = 2, 684 .opt_name = optname, 685 .total_len = optlen, sizeof(struct debug_header), 686 }; 687 write(1, &dbg, sizeof(dbg)); 688 write(1, op3, optlen); 689 } 690 691 if (g_co.test_only) 692 return (0); 693 694 if (ipfw_socket == -1) 695 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 696 if (ipfw_socket < 0) 697 err(EX_UNAVAILABLE, "socket"); 698 699 700 return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen)); 701 } 702 703 /* 704 * do_get3 - pass ipfw control cmd to kernel 705 * @optname: option name 706 * @optval: pointer to option data 707 * @optlen: pointer to option length 708 * 709 * Assumes op3 header is already embedded. 710 * Calls getsockopt() with IP_FW3 as kernel-visible opcode. 711 * Returns 0 on success or errno otherwise. 712 */ 713 int 714 do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) 715 { 716 int error; 717 socklen_t len; 718 719 op3->opcode = optname; 720 op3->version = IP_FW3_OPVER; /* use last version */ 721 722 if (g_co.debug_only) { 723 struct debug_header dbg = { 724 .cmd_type = 3, 725 .opt_name = optname, 726 .total_len = *optlen + sizeof(struct debug_header), 727 }; 728 write(1, &dbg, sizeof(dbg)); 729 write(1, op3, *optlen); 730 } 731 732 if (g_co.test_only) 733 return (0); 734 735 if (ipfw_socket == -1) 736 ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 737 if (ipfw_socket < 0) 738 err(EX_UNAVAILABLE, "socket"); 739 740 741 len = *optlen; 742 error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len); 743 *optlen = len; 744 745 return (error); 746 } 747 748 /** 749 * match_token takes a table and a string, returns the value associated 750 * with the string (-1 in case of failure). 751 */ 752 int 753 match_token(struct _s_x *table, const char *string) 754 { 755 struct _s_x *pt; 756 uint i = strlen(string); 757 758 for (pt = table ; i && pt->s != NULL ; pt++) 759 if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) 760 return pt->x; 761 return (-1); 762 } 763 764 /** 765 * match_token_relaxed takes a table and a string, returns the value associated 766 * with the string for the best match. 767 * 768 * Returns: 769 * value from @table for matched records 770 * -1 for non-matched records 771 * -2 if more than one records match @string. 772 */ 773 int 774 match_token_relaxed(struct _s_x *table, const char *string) 775 { 776 struct _s_x *pt, *m; 777 int i, c; 778 779 i = strlen(string); 780 c = 0; 781 782 for (pt = table ; i != 0 && pt->s != NULL ; pt++) { 783 if (strncmp(pt->s, string, i) != 0) 784 continue; 785 m = pt; 786 c++; 787 } 788 789 if (c == 1) 790 return (m->x); 791 792 return (c > 0 ? -2: -1); 793 } 794 795 int 796 get_token(struct _s_x *table, const char *string, const char *errbase) 797 { 798 int tcmd; 799 800 if ((tcmd = match_token_relaxed(table, string)) < 0) 801 errx(EX_USAGE, "%s %s %s", 802 (tcmd == 0) ? "invalid" : "ambiguous", errbase, string); 803 804 return (tcmd); 805 } 806 807 /** 808 * match_value takes a table and a value, returns the string associated 809 * with the value (NULL in case of failure). 810 */ 811 char const * 812 match_value(struct _s_x *p, int value) 813 { 814 for (; p->s != NULL; p++) 815 if (p->x == value) 816 return p->s; 817 return NULL; 818 } 819 820 size_t 821 concat_tokens(char *buf, size_t bufsize, struct _s_x *table, 822 const char *delimiter) 823 { 824 struct _s_x *pt; 825 int l; 826 size_t sz; 827 828 for (sz = 0, pt = table ; pt->s != NULL; pt++) { 829 l = snprintf(buf + sz, bufsize - sz, "%s%s", 830 (sz == 0) ? "" : delimiter, pt->s); 831 sz += l; 832 bufsize += l; 833 if (sz > bufsize) 834 return (bufsize); 835 } 836 837 return (sz); 838 } 839 840 /* 841 * helper function to process a set of flags and set bits in the 842 * appropriate masks. 843 */ 844 int 845 fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set, 846 uint32_t *clear) 847 { 848 char *q; /* points to the separator */ 849 int val; 850 uint32_t *which; /* mask we are working on */ 851 852 while (p && *p) { 853 if (*p == '!') { 854 p++; 855 which = clear; 856 } else 857 which = set; 858 q = strchr(p, ','); 859 if (q) 860 *q++ = '\0'; 861 val = match_token(flags, p); 862 if (val <= 0) { 863 if (e != NULL) 864 *e = p; 865 return (-1); 866 } 867 *which |= (uint32_t)val; 868 p = q; 869 } 870 return (0); 871 } 872 873 void 874 print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set) 875 { 876 char const *comma = ""; 877 int i, l; 878 879 for (i = 0; list[i].x != 0; i++) { 880 if ((set & list[i].x) == 0) 881 continue; 882 883 set &= ~list[i].x; 884 l = snprintf(buf, sz, "%s%s", comma, list[i].s); 885 if (l < 0 || (size_t)l >= sz) 886 return; 887 comma = ","; 888 buf += l; 889 sz -=l; 890 } 891 } 892 893 /* 894 * _substrcmp takes two strings and returns 1 if they do not match, 895 * and 0 if they match exactly or the first string is a sub-string 896 * of the second. A warning is printed to stderr in the case that the 897 * first string is a sub-string of the second. 898 * 899 * This function will be removed in the future through the usual 900 * deprecation process. 901 */ 902 int 903 _substrcmp(const char *str1, const char* str2) 904 { 905 906 if (strncmp(str1, str2, strlen(str1)) != 0) 907 return 1; 908 909 if (strlen(str1) != strlen(str2)) 910 warnx("DEPRECATED: '%s' matched '%s' as a sub-string", 911 str1, str2); 912 return 0; 913 } 914 915 /* 916 * _substrcmp2 takes three strings and returns 1 if the first two do not match, 917 * and 0 if they match exactly or the second string is a sub-string 918 * of the first. A warning is printed to stderr in the case that the 919 * first string does not match the third. 920 * 921 * This function exists to warn about the bizarre construction 922 * strncmp(str, "by", 2) which is used to allow people to use a shortcut 923 * for "bytes". The problem is that in addition to accepting "by", 924 * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any 925 * other string beginning with "by". 926 * 927 * This function will be removed in the future through the usual 928 * deprecation process. 929 */ 930 int 931 _substrcmp2(const char *str1, const char* str2, const char* str3) 932 { 933 934 if (strncmp(str1, str2, strlen(str2)) != 0) 935 return 1; 936 937 if (strcmp(str1, str3) != 0) 938 warnx("DEPRECATED: '%s' matched '%s'", 939 str1, str3); 940 return 0; 941 } 942 943 /* 944 * prints one port, symbolic or numeric 945 */ 946 static void 947 print_port(struct buf_pr *bp, int proto, uint16_t port) 948 { 949 950 if (proto == IPPROTO_ETHERTYPE) { 951 char const *s; 952 953 if (g_co.do_resolv && (s = match_value(ether_types, port)) ) 954 bprintf(bp, "%s", s); 955 else 956 bprintf(bp, "0x%04x", port); 957 } else { 958 struct servent *se = NULL; 959 if (g_co.do_resolv) { 960 struct protoent *pe = getprotobynumber(proto); 961 962 se = getservbyport(htons(port), pe ? pe->p_name : NULL); 963 } 964 if (se) 965 bprintf(bp, "%s", se->s_name); 966 else 967 bprintf(bp, "%d", port); 968 } 969 } 970 971 static struct _s_x _port_name[] = { 972 {"dst-port", O_IP_DSTPORT}, 973 {"src-port", O_IP_SRCPORT}, 974 {"ipid", O_IPID}, 975 {"iplen", O_IPLEN}, 976 {"ipttl", O_IPTTL}, 977 {"mac-type", O_MAC_TYPE}, 978 {"tcpdatalen", O_TCPDATALEN}, 979 {"tcpmss", O_TCPMSS}, 980 {"tcpwin", O_TCPWIN}, 981 {"tagged", O_TAGGED}, 982 {NULL, 0} 983 }; 984 985 /* 986 * Print the values in a list 16-bit items of the types above. 987 * XXX todo: add support for mask. 988 */ 989 static void 990 print_newports(struct buf_pr *bp, const ipfw_insn_u16 *cmd, int proto, int opcode) 991 { 992 const uint16_t *p = cmd->ports; 993 int i; 994 char const *sep; 995 996 if (opcode != 0) { 997 sep = match_value(_port_name, opcode); 998 if (sep == NULL) 999 sep = "???"; 1000 bprintf(bp, " %s", sep); 1001 } 1002 sep = " "; 1003 for (i = F_LEN((const ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { 1004 bprintf(bp, "%s", sep); 1005 print_port(bp, proto, p[0]); 1006 if (p[0] != p[1]) { 1007 bprintf(bp, "-"); 1008 print_port(bp, proto, p[1]); 1009 } 1010 sep = ","; 1011 } 1012 } 1013 1014 /* 1015 * Like strtol, but also translates service names into port numbers 1016 * for some protocols. 1017 * In particular: 1018 * proto == -1 disables the protocol check; 1019 * proto == IPPROTO_ETHERTYPE looks up an internal table 1020 * proto == <some value in /etc/protocols> matches the values there. 1021 * Returns *end == s in case the parameter is not found. 1022 */ 1023 static int 1024 strtoport(char *s, char **end, int base, int proto) 1025 { 1026 char *p, *buf; 1027 char *s1; 1028 int i; 1029 1030 *end = s; /* default - not found */ 1031 if (*s == '\0') 1032 return 0; /* not found */ 1033 1034 if (isdigit(*s)) 1035 return strtol(s, end, base); 1036 1037 /* 1038 * find separator. '\\' escapes the next char. 1039 */ 1040 for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\' || 1041 *s1 == '_' || *s1 == '.') ; s1++) 1042 if (*s1 == '\\' && s1[1] != '\0') 1043 s1++; 1044 1045 buf = safe_calloc(s1 - s + 1, 1); 1046 1047 /* 1048 * copy into a buffer skipping backslashes 1049 */ 1050 for (p = s, i = 0; p != s1 ; p++) 1051 if (*p != '\\') 1052 buf[i++] = *p; 1053 buf[i++] = '\0'; 1054 1055 if (proto == IPPROTO_ETHERTYPE) { 1056 i = match_token(ether_types, buf); 1057 free(buf); 1058 if (i != -1) { /* found */ 1059 *end = s1; 1060 return i; 1061 } 1062 } else { 1063 struct protoent *pe = NULL; 1064 struct servent *se; 1065 1066 if (proto != 0) 1067 pe = getprotobynumber(proto); 1068 setservent(1); 1069 se = getservbyname(buf, pe ? pe->p_name : NULL); 1070 free(buf); 1071 if (se != NULL) { 1072 *end = s1; 1073 return ntohs(se->s_port); 1074 } 1075 } 1076 return 0; /* not found */ 1077 } 1078 1079 /* 1080 * Fill the body of the command with the list of port ranges. 1081 */ 1082 static int 1083 fill_newports(ipfw_insn_u16 *cmd, char *av, int proto, int cblen) 1084 { 1085 uint16_t a, b, *p = cmd->ports; 1086 int i = 0; 1087 char *s = av; 1088 1089 while (*s) { 1090 a = strtoport(av, &s, 0, proto); 1091 if (s == av) /* empty or invalid argument */ 1092 return (0); 1093 1094 CHECK_LENGTH(cblen, i + 2); 1095 1096 switch (*s) { 1097 case '-': /* a range */ 1098 av = s + 1; 1099 b = strtoport(av, &s, 0, proto); 1100 /* Reject expressions like '1-abc' or '1-2-3'. */ 1101 if (s == av || (*s != ',' && *s != '\0')) 1102 return (0); 1103 p[0] = a; 1104 p[1] = b; 1105 break; 1106 case ',': /* comma separated list */ 1107 case '\0': 1108 p[0] = p[1] = a; 1109 break; 1110 default: 1111 warnx("port list: invalid separator <%c> in <%s>", 1112 *s, av); 1113 return (0); 1114 } 1115 1116 i++; 1117 p += 2; 1118 av = s + 1; 1119 } 1120 if (i > 0) { 1121 if (i + 1 > F_LEN_MASK) 1122 errx(EX_DATAERR, "too many ports/ranges\n"); 1123 cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */ 1124 } 1125 return (i); 1126 } 1127 1128 /* 1129 * Fill the body of the command with the list of DiffServ codepoints. 1130 */ 1131 static void 1132 fill_dscp(ipfw_insn *cmd, char *av, int cblen) 1133 { 1134 uint32_t *low, *high; 1135 char *s = av, *a; 1136 int code; 1137 1138 cmd->opcode = O_DSCP; 1139 cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1; 1140 1141 CHECK_CMDLEN; 1142 1143 low = (uint32_t *)(cmd + 1); 1144 high = low + 1; 1145 1146 *low = 0; 1147 *high = 0; 1148 1149 while (s != NULL) { 1150 a = strchr(s, ','); 1151 1152 if (a != NULL) 1153 *a++ = '\0'; 1154 1155 if (isalpha(*s)) { 1156 if ((code = match_token(f_ipdscp, s)) == -1) 1157 errx(EX_DATAERR, "Unknown DSCP code"); 1158 } else { 1159 code = strtoul(s, NULL, 10); 1160 if (code < 0 || code > 63) 1161 errx(EX_DATAERR, "Invalid DSCP value"); 1162 } 1163 1164 if (code >= 32) 1165 *high |= 1 << (code - 32); 1166 else 1167 *low |= 1 << code; 1168 1169 s = a; 1170 } 1171 } 1172 1173 /* 1174 * Fill the body of the command with mark value and mask. 1175 */ 1176 static void 1177 fill_mark(ipfw_insn *cmd, char *av, int cblen) 1178 { 1179 uint32_t *value, *mask; 1180 char *value_str; 1181 1182 cmd->opcode = O_MARK; 1183 cmd->len |= F_INSN_SIZE(ipfw_insn_u32) + 1; 1184 1185 CHECK_CMDLEN; 1186 1187 value = (uint32_t *)(cmd + 1); 1188 mask = value + 1; 1189 1190 value_str = strsep(&av, ":"); 1191 1192 if (strcmp(value_str, "tablearg") == 0) { 1193 cmd->arg1 = IP_FW_TARG; 1194 *value = 0; 1195 } else { 1196 /* This is not a tablearg */ 1197 cmd->arg1 |= 0x8000; 1198 *value = strtoul(value_str, NULL, 0); 1199 } 1200 if (av) 1201 *mask = strtoul(av, NULL, 0); 1202 else 1203 *mask = 0xFFFFFFFF; 1204 1205 if ((*value & *mask) != *value) 1206 errx(EX_DATAERR, "Static mark value: some bits in value are" 1207 " set that will be masked out by mask " 1208 "(%#x & %#x) = %#x != %#x", 1209 *value, *mask, (*value & *mask), *value); 1210 } 1211 1212 static struct _s_x icmpcodes[] = { 1213 { "net", ICMP_UNREACH_NET }, 1214 { "host", ICMP_UNREACH_HOST }, 1215 { "protocol", ICMP_UNREACH_PROTOCOL }, 1216 { "port", ICMP_UNREACH_PORT }, 1217 { "needfrag", ICMP_UNREACH_NEEDFRAG }, 1218 { "srcfail", ICMP_UNREACH_SRCFAIL }, 1219 { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, 1220 { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, 1221 { "isolated", ICMP_UNREACH_ISOLATED }, 1222 { "net-prohib", ICMP_UNREACH_NET_PROHIB }, 1223 { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, 1224 { "tosnet", ICMP_UNREACH_TOSNET }, 1225 { "toshost", ICMP_UNREACH_TOSHOST }, 1226 { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, 1227 { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, 1228 { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, 1229 { NULL, 0 } 1230 }; 1231 1232 static uint16_t 1233 get_reject_code(const char *str) 1234 { 1235 int val; 1236 char *s; 1237 1238 val = strtoul(str, &s, 0); 1239 if (s == str || *s != '\0' || val >= 0x100) 1240 val = match_token(icmpcodes, str); 1241 if (val < 0) 1242 errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); 1243 return (val); 1244 } 1245 1246 static void 1247 print_reject_code(struct buf_pr *bp, uint16_t code) 1248 { 1249 char const *s; 1250 1251 if ((s = match_value(icmpcodes, code)) != NULL) 1252 bprintf(bp, "unreach %s", s); 1253 else 1254 bprintf(bp, "unreach %u", code); 1255 } 1256 1257 /* 1258 * Returns the number of bits set (from left) in a contiguous bitmask, 1259 * or -1 if the mask is not contiguous. 1260 * XXX this needs a proper fix. 1261 * This effectively works on masks in big-endian (network) format. 1262 * when compiled on little endian architectures. 1263 * 1264 * First bit is bit 7 of the first byte -- note, for MAC addresses, 1265 * the first bit on the wire is bit 0 of the first byte. 1266 * len is the max length in bits. 1267 */ 1268 int 1269 contigmask(const uint8_t *p, int len) 1270 { 1271 int i, n; 1272 1273 for (i=0; i<len ; i++) 1274 if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */ 1275 break; 1276 for (n=i+1; n < len; n++) 1277 if ( (p[n/8] & (1 << (7 - (n%8)))) != 0) 1278 return -1; /* mask not contiguous */ 1279 return i; 1280 } 1281 1282 /* 1283 * print flags set/clear in the two bitmasks passed as parameters. 1284 * There is a specialized check for f_tcpflags. 1285 */ 1286 static void 1287 print_flags(struct buf_pr *bp, char const *name, const ipfw_insn *cmd, 1288 struct _s_x *list) 1289 { 1290 char const *comma = ""; 1291 int i; 1292 uint8_t set = cmd->arg1 & 0xff; 1293 uint8_t clear = (cmd->arg1 >> 8) & 0xff; 1294 1295 if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { 1296 bprintf(bp, " setup"); 1297 return; 1298 } 1299 1300 bprintf(bp, " %s ", name); 1301 for (i=0; list[i].x != 0; i++) { 1302 if (set & list[i].x) { 1303 set &= ~list[i].x; 1304 bprintf(bp, "%s%s", comma, list[i].s); 1305 comma = ","; 1306 } 1307 if (clear & list[i].x) { 1308 clear &= ~list[i].x; 1309 bprintf(bp, "%s!%s", comma, list[i].s); 1310 comma = ","; 1311 } 1312 } 1313 } 1314 1315 static void 1316 print_tvalue(struct buf_pr *bp, const ipfw_insn_table *cmd) 1317 { 1318 const char *name; 1319 1320 name = match_value(tvalue_names, IPFW_TVALUE_TYPE(&cmd->o)); 1321 bprintf(bp, ",%s=%u", name != NULL ? name: "<invalid>", cmd->value); 1322 } 1323 1324 1325 /* 1326 * Print the ip address contained in a command. 1327 */ 1328 static void 1329 print_ip(struct buf_pr *bp, const struct format_opts *fo, 1330 const ipfw_insn_ip *cmd) 1331 { 1332 struct hostent *he = NULL; 1333 const struct in_addr *ia; 1334 const uint32_t *a = ((const ipfw_insn_u32 *)cmd)->d; 1335 uint32_t len = F_LEN(&cmd->o); 1336 char *t; 1337 1338 bprintf(bp, " "); 1339 switch (cmd->o.opcode) { 1340 case O_IP_SRC_ME: 1341 case O_IP_DST_ME: 1342 bprintf(bp, "me"); 1343 return; 1344 1345 case O_IP_DST_LOOKUP: 1346 if ((len == F_INSN_SIZE(ipfw_insn_kidx) || 1347 len == F_INSN_SIZE(ipfw_insn_table)) && 1348 IPFW_LOOKUP_TYPE(&cmd->o) != LOOKUP_NONE) { 1349 const char *key; 1350 1351 key = match_value(lookup_keys, 1352 IPFW_LOOKUP_TYPE(&cmd->o)); 1353 t = table_search_ctlv(fo->tstate, 1354 insntoc(&cmd->o, kidx)->kidx); 1355 if (len == F_INSN_SIZE(ipfw_insn_table)) { 1356 bprintf(bp, "lookup %s:%#x %s", 1357 (key != NULL ? key : "<invalid>"), 1358 insntoc(&cmd->o, table)->value, t); 1359 } else 1360 bprintf(bp, "lookup %s %s", key != NULL ? key: 1361 "<invalid>", t); 1362 return; 1363 } 1364 /* FALLTHROUGH */ 1365 case O_IP_SRC_LOOKUP: 1366 t = table_search_ctlv(fo->tstate, 1367 insntoc(&cmd->o, kidx)->kidx); 1368 bprintf(bp, "table(%s", t); 1369 if (len == F_INSN_SIZE(ipfw_insn_table)) 1370 print_tvalue(bp, insntoc(&cmd->o, table)); 1371 bprintf(bp, ")"); 1372 return; 1373 } 1374 1375 if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { 1376 const uint32_t *map = (const uint32_t *)&cmd->mask; 1377 struct in_addr addr; 1378 uint32_t x; 1379 int i, j; 1380 char comma = '{'; 1381 1382 x = cmd->o.arg1 - 1; 1383 x = htonl(~x); 1384 addr.s_addr = htonl(cmd->addr.s_addr); 1385 bprintf(bp, "%s/%d", inet_ntoa(addr), 1386 contigmask((uint8_t *)&x, 32)); 1387 x = cmd->addr.s_addr; 1388 x &= 0xff; /* base */ 1389 /* 1390 * Print bits and ranges. 1391 * Locate first bit set (i), then locate first bit unset (j). 1392 * If we have 3+ consecutive bits set, then print them as a 1393 * range, otherwise only print the initial bit and rescan. 1394 */ 1395 for (i=0; i < cmd->o.arg1; i++) 1396 if (map[i/32] & (1<<(i & 31))) { 1397 for (j=i+1; j < cmd->o.arg1; j++) 1398 if (!(map[ j/32] & (1<<(j & 31)))) 1399 break; 1400 bprintf(bp, "%c%d", comma, i+x); 1401 if (j>i+2) { /* range has at least 3 elements */ 1402 bprintf(bp, "-%d", j-1+x); 1403 i = j-1; 1404 } 1405 comma = ','; 1406 } 1407 bprintf(bp, "}"); 1408 return; 1409 } 1410 /* 1411 * len == 2 indicates a single IP, whereas lists of 1 or more 1412 * addr/mask pairs have len = (2n+1). We convert len to n so we 1413 * use that to count the number of entries. 1414 */ 1415 for (len = len / 2; len > 0; len--, a += 2) { 1416 int mb = /* mask length */ 1417 (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ? 1418 32 : contigmask((const uint8_t *)&(a[1]), 32); 1419 if (mb == 32 && g_co.do_resolv) 1420 he = gethostbyaddr((const char *)&(a[0]), sizeof(in_addr_t), 1421 AF_INET); 1422 if (he != NULL) /* resolved to name */ 1423 bprintf(bp, "%s", he->h_name); 1424 else if (mb == 0) /* any */ 1425 bprintf(bp, "any"); 1426 else { /* numeric IP followed by some kind of mask */ 1427 ia = (const struct in_addr *)&a[0]; 1428 bprintf(bp, "%s", inet_ntoa(*ia)); 1429 if (mb < 0) { 1430 ia = (const struct in_addr *)&a[1]; 1431 bprintf(bp, ":%s", inet_ntoa(*ia)); 1432 } else if (mb < 32) 1433 bprintf(bp, "/%d", mb); 1434 } 1435 if (len > 1) 1436 bprintf(bp, ","); 1437 } 1438 } 1439 1440 /* 1441 * prints a MAC address/mask pair 1442 */ 1443 static void 1444 format_mac(struct buf_pr *bp, const uint8_t *addr, const uint8_t *mask) 1445 { 1446 int l = contigmask(mask, 48); 1447 1448 if (l == 0) 1449 bprintf(bp, " any"); 1450 else { 1451 bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x", 1452 addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 1453 if (l == -1) 1454 bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x", 1455 mask[0], mask[1], mask[2], 1456 mask[3], mask[4], mask[5]); 1457 else if (l < 48) 1458 bprintf(bp, "/%d", l); 1459 } 1460 } 1461 1462 static void 1463 print_mac(struct buf_pr *bp, const ipfw_insn_mac *mac) 1464 { 1465 1466 bprintf(bp, " MAC"); 1467 format_mac(bp, mac->addr, mac->mask); 1468 format_mac(bp, mac->addr + 6, mac->mask + 6); 1469 } 1470 1471 static void 1472 print_mac_lookup(struct buf_pr *bp, const struct format_opts *fo, 1473 const ipfw_insn *cmd) 1474 { 1475 uint32_t len = F_LEN(cmd); 1476 char *t; 1477 1478 bprintf(bp, " "); 1479 1480 t = table_search_ctlv(fo->tstate, insntoc(cmd, kidx)->kidx); 1481 bprintf(bp, "table(%s", t); 1482 if (len == F_INSN_SIZE(ipfw_insn_table)) 1483 print_tvalue(bp, insntoc(cmd, table)); 1484 bprintf(bp, ")"); 1485 } 1486 1487 static void 1488 fill_icmptypes(ipfw_insn_u32 *cmd, char *av) 1489 { 1490 uint8_t type; 1491 1492 cmd->d[0] = 0; 1493 while (*av) { 1494 if (*av == ',') 1495 av++; 1496 1497 type = strtoul(av, &av, 0); 1498 1499 if (*av != ',' && *av != '\0') 1500 errx(EX_DATAERR, "invalid ICMP type"); 1501 1502 if (type > 31) 1503 errx(EX_DATAERR, "ICMP type out of range"); 1504 1505 cmd->d[0] |= 1 << type; 1506 } 1507 cmd->o.opcode = O_ICMPTYPE; 1508 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 1509 } 1510 1511 static void 1512 print_icmptypes(struct buf_pr *bp, const ipfw_insn_u32 *cmd) 1513 { 1514 int i; 1515 char sep= ' '; 1516 1517 bprintf(bp, " icmptypes"); 1518 for (i = 0; i < 32; i++) { 1519 if ( (cmd->d[0] & (1 << (i))) == 0) 1520 continue; 1521 bprintf(bp, "%c%d", sep, i); 1522 sep = ','; 1523 } 1524 } 1525 1526 static void 1527 print_dscp(struct buf_pr *bp, const ipfw_insn_u32 *cmd) 1528 { 1529 const uint32_t *v; 1530 const char *code; 1531 int i = 0; 1532 char sep= ' '; 1533 1534 bprintf(bp, " dscp"); 1535 v = cmd->d; 1536 while (i < 64) { 1537 if (*v & (1 << i)) { 1538 if ((code = match_value(f_ipdscp, i)) != NULL) 1539 bprintf(bp, "%c%s", sep, code); 1540 else 1541 bprintf(bp, "%c%d", sep, i); 1542 sep = ','; 1543 } 1544 1545 if ((++i % 32) == 0) 1546 v++; 1547 } 1548 } 1549 1550 struct show_state { 1551 struct ip_fw_rule *rule; 1552 const ipfw_insn_kidx *eaction; 1553 uint8_t *printed; 1554 int flags; 1555 #define HAVE_PROTO 0x0001 1556 #define HAVE_SRCIP 0x0002 1557 #define HAVE_DSTIP 0x0004 1558 #define HAVE_PROBE_STATE 0x0008 1559 int proto; 1560 int or_block; 1561 }; 1562 1563 static int 1564 init_show_state(struct show_state *state, struct ip_fw_rule *rule) 1565 { 1566 1567 state->printed = calloc(rule->cmd_len, sizeof(uint8_t)); 1568 if (state->printed == NULL) 1569 return (ENOMEM); 1570 state->rule = rule; 1571 state->eaction = NULL; 1572 state->flags = 0; 1573 state->proto = 0; 1574 state->or_block = 0; 1575 return (0); 1576 } 1577 1578 static void 1579 free_show_state(struct show_state *state) 1580 { 1581 1582 free(state->printed); 1583 } 1584 1585 static uint8_t 1586 is_printed_opcode(struct show_state *state, const ipfw_insn *cmd) 1587 { 1588 1589 return (state->printed[cmd - state->rule->cmd]); 1590 } 1591 1592 static void 1593 mark_printed(struct show_state *state, const ipfw_insn *cmd) 1594 { 1595 1596 state->printed[cmd - state->rule->cmd] = 1; 1597 } 1598 1599 static void 1600 print_limit_mask(struct buf_pr *bp, const ipfw_insn_limit *limit) 1601 { 1602 struct _s_x *p = limit_masks; 1603 char const *comma = " "; 1604 uint8_t x; 1605 1606 for (x = limit->limit_mask; p->x != 0; p++) { 1607 if ((x & p->x) == p->x) { 1608 x &= ~p->x; 1609 bprintf(bp, "%s%s", comma, p->s); 1610 comma = ","; 1611 } 1612 } 1613 bprint_uint_arg(bp, " ", limit->conn_limit); 1614 } 1615 1616 static int 1617 print_instruction(struct buf_pr *bp, const struct format_opts *fo, 1618 struct show_state *state, const ipfw_insn *cmd) 1619 { 1620 struct protoent *pe; 1621 struct passwd *pwd; 1622 struct group *grp; 1623 const char *s; 1624 double d; 1625 1626 if (is_printed_opcode(state, cmd)) 1627 return (0); 1628 if ((cmd->len & F_OR) != 0 && state->or_block == 0) 1629 bprintf(bp, " {"); 1630 if (cmd->opcode != O_IN && (cmd->len & F_NOT) != 0) 1631 bprintf(bp, " not"); 1632 1633 switch (cmd->opcode) { 1634 case O_PROB: 1635 d = 1.0 * insntoc(cmd, u32)->d[0] / 0x7fffffff; 1636 bprintf(bp, "prob %f ", d); 1637 break; 1638 case O_PROBE_STATE: /* no need to print anything here */ 1639 state->flags |= HAVE_PROBE_STATE; 1640 break; 1641 case O_IP_SRC: 1642 case O_IP_SRC_LOOKUP: 1643 case O_IP_SRC_MASK: 1644 case O_IP_SRC_ME: 1645 case O_IP_SRC_SET: 1646 if (state->flags & HAVE_SRCIP) 1647 bprintf(bp, " src-ip"); 1648 print_ip(bp, fo, insntoc(cmd, ip)); 1649 break; 1650 case O_IP_DST: 1651 case O_IP_DST_MASK: 1652 case O_IP_DST_ME: 1653 case O_IP_DST_SET: 1654 case O_IP_DST_LOOKUP: 1655 /* 1656 * Special handling for O_IP_DST_LOOKUP when 1657 * lookup type is not LOOKUP_NONE. 1658 */ 1659 if ((state->flags & HAVE_DSTIP) != 0 && ( 1660 cmd->opcode != O_IP_DST_LOOKUP || 1661 IPFW_LOOKUP_TYPE(cmd) == LOOKUP_NONE)) 1662 bprintf(bp, " dst-ip"); 1663 print_ip(bp, fo, insntoc(cmd, ip)); 1664 break; 1665 case O_IP6_SRC: 1666 case O_IP6_SRC_MASK: 1667 case O_IP6_SRC_ME: 1668 if (state->flags & HAVE_SRCIP) 1669 bprintf(bp, " src-ip6"); 1670 print_ip6(bp, insntoc(cmd, ip6)); 1671 break; 1672 case O_IP6_DST: 1673 case O_IP6_DST_MASK: 1674 case O_IP6_DST_ME: 1675 if (state->flags & HAVE_DSTIP) 1676 bprintf(bp, " dst-ip6"); 1677 print_ip6(bp, insntoc(cmd, ip6)); 1678 break; 1679 case O_MAC_SRC_LOOKUP: 1680 bprintf(bp, " src-mac"); 1681 print_mac_lookup(bp, fo, cmd); 1682 break; 1683 case O_MAC_DST_LOOKUP: 1684 bprintf(bp, " dst-mac"); 1685 print_mac_lookup(bp, fo, cmd); 1686 break; 1687 case O_FLOW6ID: 1688 print_flow6id(bp, insntoc(cmd, u32)); 1689 break; 1690 case O_IP_DSTPORT: 1691 case O_IP_SRCPORT: 1692 print_newports(bp, insntoc(cmd, u16), state->proto, 1693 (state->flags & (HAVE_SRCIP | HAVE_DSTIP)) == 1694 (HAVE_SRCIP | HAVE_DSTIP) ? cmd->opcode: 0); 1695 break; 1696 case O_PROTO: 1697 pe = getprotobynumber(cmd->arg1); 1698 if (state->flags & HAVE_PROTO) 1699 bprintf(bp, " proto"); 1700 if (pe != NULL) 1701 bprintf(bp, " %s", pe->p_name); 1702 else 1703 bprintf(bp, " %u", cmd->arg1); 1704 state->proto = cmd->arg1; 1705 break; 1706 case O_MACADDR2: 1707 print_mac(bp, insntoc(cmd, mac)); 1708 break; 1709 case O_MAC_TYPE: 1710 print_newports(bp, insntoc(cmd, u16), 1711 IPPROTO_ETHERTYPE, cmd->opcode); 1712 break; 1713 case O_FRAG: 1714 print_flags(bp, "frag", cmd, f_ipoff); 1715 break; 1716 case O_FIB: 1717 bprintf(bp, " fib %u", cmd->arg1); 1718 break; 1719 case O_SOCKARG: 1720 bprintf(bp, " sockarg"); 1721 break; 1722 case O_IN: 1723 bprintf(bp, cmd->len & F_NOT ? " out" : " in"); 1724 break; 1725 case O_DIVERTED: 1726 switch (cmd->arg1) { 1727 case 3: 1728 bprintf(bp, " diverted"); 1729 break; 1730 case 2: 1731 bprintf(bp, " diverted-output"); 1732 break; 1733 case 1: 1734 bprintf(bp, " diverted-loopback"); 1735 break; 1736 default: 1737 bprintf(bp, " diverted-?<%u>", cmd->arg1); 1738 break; 1739 } 1740 break; 1741 case O_LAYER2: 1742 bprintf(bp, " layer2"); 1743 break; 1744 case O_XMIT: 1745 case O_RECV: 1746 case O_VIA: 1747 if (cmd->opcode == O_XMIT) 1748 s = "xmit"; 1749 else if (cmd->opcode == O_RECV) 1750 s = "recv"; 1751 else /* if (cmd->opcode == O_VIA) */ 1752 s = "via"; 1753 switch (insntoc(cmd, if)->name[0]) { 1754 case '\0': 1755 bprintf(bp, " %s %s", s, 1756 inet_ntoa(insntoc(cmd, if)->p.ip)); 1757 break; 1758 case '\1': 1759 bprintf(bp, " %s table(%s)", s, 1760 table_search_ctlv(fo->tstate, 1761 insntoc(cmd, if)->p.kidx)); 1762 break; 1763 default: 1764 bprintf(bp, " %s %s", s, 1765 insntoc(cmd, if)->name); 1766 } 1767 break; 1768 case O_IP_FLOW_LOOKUP: 1769 s = table_search_ctlv(fo->tstate, 1770 insntoc(cmd, kidx)->kidx); 1771 bprintf(bp, " flow table(%s", s); 1772 if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_table)) 1773 print_tvalue(bp, insntoc(cmd, table)); 1774 bprintf(bp, ")"); 1775 break; 1776 case O_IPID: 1777 case O_IPTTL: 1778 case O_IPLEN: 1779 case O_TCPDATALEN: 1780 case O_TCPMSS: 1781 case O_TCPWIN: 1782 if (F_LEN(cmd) == 1) { 1783 switch (cmd->opcode) { 1784 case O_IPID: 1785 s = "ipid"; 1786 break; 1787 case O_IPTTL: 1788 s = "ipttl"; 1789 break; 1790 case O_IPLEN: 1791 s = "iplen"; 1792 break; 1793 case O_TCPDATALEN: 1794 s = "tcpdatalen"; 1795 break; 1796 case O_TCPMSS: 1797 s = "tcpmss"; 1798 break; 1799 case O_TCPWIN: 1800 s = "tcpwin"; 1801 break; 1802 default: 1803 s = "<unknown>"; 1804 break; 1805 } 1806 bprintf(bp, " %s %u", s, cmd->arg1); 1807 } else 1808 print_newports(bp, insntoc(cmd, u16), 0, 1809 cmd->opcode); 1810 break; 1811 case O_IPVER: 1812 bprintf(bp, " ipver %u", cmd->arg1); 1813 break; 1814 case O_IPPRECEDENCE: 1815 bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5); 1816 break; 1817 case O_DSCP: 1818 print_dscp(bp, insntoc(cmd, u32)); 1819 break; 1820 case O_IPOPT: 1821 print_flags(bp, "ipoptions", cmd, f_ipopts); 1822 break; 1823 case O_IPTOS: 1824 print_flags(bp, "iptos", cmd, f_iptos); 1825 break; 1826 case O_ICMPTYPE: 1827 print_icmptypes(bp, insntoc(cmd, u32)); 1828 break; 1829 case O_ESTAB: 1830 bprintf(bp, " established"); 1831 break; 1832 case O_TCPFLAGS: 1833 print_flags(bp, "tcpflags", cmd, f_tcpflags); 1834 break; 1835 case O_TCPOPTS: 1836 print_flags(bp, "tcpoptions", cmd, f_tcpopts); 1837 break; 1838 case O_TCPACK: 1839 bprintf(bp, " tcpack %d", 1840 ntohl(insntoc(cmd, u32)->d[0])); 1841 break; 1842 case O_TCPSEQ: 1843 bprintf(bp, " tcpseq %d", 1844 ntohl(insntoc(cmd, u32)->d[0])); 1845 break; 1846 case O_UID: 1847 pwd = getpwuid(insntoc(cmd, u32)->d[0]); 1848 if (pwd != NULL) 1849 bprintf(bp, " uid %s", pwd->pw_name); 1850 else 1851 bprintf(bp, " uid %u", 1852 insntoc(cmd, u32)->d[0]); 1853 break; 1854 case O_GID: 1855 grp = getgrgid(insntoc(cmd, u32)->d[0]); 1856 if (grp != NULL) 1857 bprintf(bp, " gid %s", grp->gr_name); 1858 else 1859 bprintf(bp, " gid %u", 1860 insntoc(cmd, u32)->d[0]); 1861 break; 1862 case O_JAIL: 1863 bprintf(bp, " jail %d", insntoc(cmd, u32)->d[0]); 1864 break; 1865 case O_VERREVPATH: 1866 bprintf(bp, " verrevpath"); 1867 break; 1868 case O_VERSRCREACH: 1869 bprintf(bp, " versrcreach"); 1870 break; 1871 case O_ANTISPOOF: 1872 bprintf(bp, " antispoof"); 1873 break; 1874 case O_IPSEC: 1875 bprintf(bp, " ipsec"); 1876 break; 1877 case O_NOP: 1878 bprintf(bp, " // %s", (const char *)(cmd + 1)); 1879 break; 1880 case O_KEEP_STATE: 1881 if (state->flags & HAVE_PROBE_STATE) 1882 bprintf(bp, " keep-state"); 1883 else 1884 bprintf(bp, " record-state"); 1885 bprintf(bp, " :%s", 1886 object_search_ctlv(fo->tstate, 1887 insntoc(cmd, kidx)->kidx, 1888 IPFW_TLV_STATE_NAME)); 1889 break; 1890 case O_LIMIT: 1891 if (state->flags & HAVE_PROBE_STATE) 1892 bprintf(bp, " limit"); 1893 else 1894 bprintf(bp, " set-limit"); 1895 print_limit_mask(bp, insntoc(cmd, limit)); 1896 bprintf(bp, " :%s", 1897 object_search_ctlv(fo->tstate, 1898 insntoc(cmd, kidx)->kidx, 1899 IPFW_TLV_STATE_NAME)); 1900 break; 1901 case O_IP6: 1902 if (state->flags & HAVE_PROTO) 1903 bprintf(bp, " proto"); 1904 bprintf(bp, " ip6"); 1905 break; 1906 case O_IP4: 1907 if (state->flags & HAVE_PROTO) 1908 bprintf(bp, " proto"); 1909 bprintf(bp, " ip4"); 1910 break; 1911 case O_ICMP6TYPE: 1912 print_icmp6types(bp, insntoc(cmd, u32)); 1913 break; 1914 case O_EXT_HDR: 1915 print_ext6hdr(bp, cmd); 1916 break; 1917 case O_TAGGED: 1918 if (F_LEN(cmd) == 1) 1919 bprint_uint_arg(bp, " tagged ", cmd->arg1); 1920 else 1921 print_newports(bp, insntoc(cmd, u16), 1922 0, O_TAGGED); 1923 break; 1924 case O_SKIP_ACTION: 1925 bprintf(bp, " defer-immediate-action"); 1926 break; 1927 case O_MARK: 1928 bprintf(bp, " mark"); 1929 if (cmd->arg1 == IP_FW_TARG) 1930 bprintf(bp, " tablearg"); 1931 else 1932 bprintf(bp, " %#x", insntoc(cmd, u32)->d[0]); 1933 1934 if (insntoc(cmd, u32)->d[1] != 0xFFFFFFFF) 1935 bprintf(bp, ":%#x", insntoc(cmd, u32)->d[1]); 1936 break; 1937 1938 default: 1939 bprintf(bp, " [opcode %d len %d]", cmd->opcode, 1940 cmd->len); 1941 } 1942 if (cmd->len & F_OR) { 1943 bprintf(bp, " or"); 1944 state->or_block = 1; 1945 } else if (state->or_block != 0) { 1946 bprintf(bp, " }"); 1947 state->or_block = 0; 1948 } 1949 mark_printed(state, cmd); 1950 1951 return (1); 1952 } 1953 1954 static ipfw_insn * 1955 print_opcode(struct buf_pr *bp, struct format_opts *fo, 1956 struct show_state *state, int opcode) 1957 { 1958 ipfw_insn *cmd; 1959 int l; 1960 1961 for (l = state->rule->act_ofs, cmd = state->rule->cmd; 1962 l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 1963 /* We use zero opcode to print the rest of options */ 1964 if (opcode >= 0 && cmd->opcode != opcode) 1965 continue; 1966 /* 1967 * Skip O_NOP, when we printing the rest 1968 * of options, it will be handled separately. 1969 */ 1970 if (cmd->opcode == O_NOP && opcode != O_NOP) 1971 continue; 1972 if (!print_instruction(bp, fo, state, cmd)) 1973 continue; 1974 return (cmd); 1975 } 1976 return (NULL); 1977 } 1978 1979 static void 1980 print_fwd(struct buf_pr *bp, const ipfw_insn *cmd) 1981 { 1982 char buf[INET6_ADDRSTRLEN + IF_NAMESIZE + 2]; 1983 const ipfw_insn_sa6 *sa6; 1984 const ipfw_insn_sa *sa; 1985 uint16_t port; 1986 1987 if (cmd->opcode == O_FORWARD_IP) { 1988 sa = insntoc(cmd, sa); 1989 port = sa->sa.sin_port; 1990 if (sa->sa.sin_addr.s_addr == INADDR_ANY) 1991 bprintf(bp, "fwd tablearg"); 1992 else 1993 bprintf(bp, "fwd %s", inet_ntoa(sa->sa.sin_addr)); 1994 } else { 1995 sa6 = insntoc(cmd, sa6); 1996 port = sa6->sa.sin6_port; 1997 bprintf(bp, "fwd "); 1998 if (getnameinfo((const struct sockaddr *)&sa6->sa, 1999 sizeof(struct sockaddr_in6), buf, sizeof(buf), NULL, 0, 2000 NI_NUMERICHOST) == 0) 2001 bprintf(bp, "%s", buf); 2002 } 2003 if (port != 0) 2004 bprintf(bp, ",%u", port); 2005 } 2006 2007 static void 2008 print_logdst(struct buf_pr *bp, uint16_t arg1) 2009 { 2010 char const *comma = ""; 2011 2012 bprintf(bp, " logdst ", arg1); 2013 if (arg1 & IPFW_LOG_SYSLOG) { 2014 bprintf(bp, "%ssyslog", comma); 2015 comma = ","; 2016 } 2017 if (arg1 & IPFW_LOG_IPFW0) { 2018 bprintf(bp, "%sipfw0", comma); 2019 comma = ","; 2020 } 2021 if (arg1 & IPFW_LOG_RTSOCK) { 2022 bprintf(bp, "%srtsock", comma); 2023 comma = ","; 2024 } 2025 } 2026 2027 static int 2028 print_action_instruction(struct buf_pr *bp, const struct format_opts *fo, 2029 struct show_state *state, const ipfw_insn *cmd) 2030 { 2031 const char *s; 2032 2033 if (is_printed_opcode(state, cmd)) 2034 return (0); 2035 switch (cmd->opcode) { 2036 case O_CHECK_STATE: 2037 bprintf(bp, "check-state"); 2038 if (insntoc(cmd, kidx)->kidx != 0) 2039 s = object_search_ctlv(fo->tstate, 2040 insntoc(cmd, kidx)->kidx, 2041 IPFW_TLV_STATE_NAME); 2042 else 2043 s = NULL; 2044 bprintf(bp, " :%s", s ? s: "any"); 2045 break; 2046 case O_ACCEPT: 2047 bprintf(bp, "allow"); 2048 break; 2049 case O_COUNT: 2050 bprintf(bp, "count"); 2051 break; 2052 case O_DENY: 2053 bprintf(bp, "deny"); 2054 break; 2055 case O_REJECT: 2056 if (cmd->arg1 == ICMP_REJECT_RST) 2057 bprintf(bp, "reset"); 2058 else if (cmd->arg1 == ICMP_REJECT_ABORT) 2059 bprintf(bp, "abort"); 2060 else if (cmd->arg1 == ICMP_UNREACH_HOST) 2061 bprintf(bp, "reject"); 2062 else if (cmd->arg1 == ICMP_UNREACH_NEEDFRAG && 2063 cmd->len == F_INSN_SIZE(ipfw_insn_u16)) 2064 bprintf(bp, "needfrag %u", 2065 insntoc(cmd, u16)->ports[0]); 2066 else 2067 print_reject_code(bp, cmd->arg1); 2068 break; 2069 case O_UNREACH6: 2070 if (cmd->arg1 == ICMP6_UNREACH_RST) 2071 bprintf(bp, "reset6"); 2072 else if (cmd->arg1 == ICMP6_UNREACH_ABORT) 2073 bprintf(bp, "abort6"); 2074 else 2075 print_unreach6_code(bp, cmd->arg1); 2076 break; 2077 case O_SKIPTO: 2078 bprint_uint_arg(bp, "skipto ", insntoc(cmd, u32)->d[0]); 2079 break; 2080 case O_PIPE: 2081 bprint_uint_arg(bp, "pipe ", cmd->arg1); 2082 break; 2083 case O_QUEUE: 2084 bprint_uint_arg(bp, "queue ", cmd->arg1); 2085 break; 2086 case O_DIVERT: 2087 bprint_uint_arg(bp, "divert ", cmd->arg1); 2088 break; 2089 case O_TEE: 2090 bprint_uint_arg(bp, "tee ", cmd->arg1); 2091 break; 2092 case O_NETGRAPH: 2093 bprint_uint_arg(bp, "netgraph ", cmd->arg1); 2094 break; 2095 case O_NGTEE: 2096 bprint_uint_arg(bp, "ngtee ", cmd->arg1); 2097 break; 2098 case O_FORWARD_IP: 2099 case O_FORWARD_IP6: 2100 print_fwd(bp, cmd); 2101 break; 2102 case O_LOG: 2103 bprintf(bp, " log"); 2104 if (insntoc(cmd, log)->max_log > 0) 2105 bprintf(bp, " logamount %d", 2106 insntoc(cmd, log)->max_log); 2107 if (cmd->arg1 != IPFW_LOG_DEFAULT) 2108 print_logdst(bp, cmd->arg1); 2109 break; 2110 case O_ALTQ: 2111 #ifndef NO_ALTQ 2112 print_altq_cmd(bp, insntoc(cmd, altq)); 2113 #endif 2114 break; 2115 case O_TAG: 2116 bprint_uint_arg(bp, cmd->len & F_NOT ? " untag ": 2117 " tag ", cmd->arg1); 2118 break; 2119 case O_NAT: 2120 if (cmd->arg1 != IP_FW_NAT44_GLOBAL) 2121 bprint_uint_arg(bp, "nat ", cmd->arg1); 2122 else 2123 bprintf(bp, "nat global"); 2124 break; 2125 case O_SETFIB: 2126 if (cmd->arg1 == IP_FW_TARG) 2127 bprint_uint_arg(bp, "setfib ", cmd->arg1); 2128 else 2129 bprintf(bp, "setfib %u", cmd->arg1 & 0x7FFF); 2130 break; 2131 case O_EXTERNAL_ACTION: 2132 /* 2133 * The external action can consists of two following 2134 * each other opcodes - O_EXTERNAL_ACTION and 2135 * O_EXTERNAL_INSTANCE. The first contains the ID of 2136 * name of external action. The second contains the ID 2137 * of name of external action instance. 2138 * NOTE: in case when external action has no named 2139 * instances support, the second opcode isn't needed. 2140 */ 2141 state->eaction = insntoc(cmd, kidx); 2142 s = object_search_ctlv(fo->tstate, 2143 state->eaction->kidx, 2144 IPFW_TLV_EACTION); 2145 if (match_token(rule_eactions, s) != -1) 2146 bprintf(bp, "%s", s); 2147 else 2148 bprintf(bp, "eaction %s", s); 2149 break; 2150 case O_EXTERNAL_INSTANCE: 2151 if (state->eaction == NULL) 2152 break; 2153 /* 2154 * XXX: we need to teach ipfw(9) to rewrite opcodes 2155 * in the user buffer on rule addition. When we add 2156 * the rule, we specify zero TLV type for 2157 * O_EXTERNAL_INSTANCE object. To show correct 2158 * rule after `ipfw add` we need to search instance 2159 * name with zero type. But when we do `ipfw show` 2160 * we calculate TLV type using IPFW_TLV_EACTION_NAME() 2161 * macro. 2162 */ 2163 s = object_search_ctlv(fo->tstate, 2164 insntoc(cmd, kidx)->kidx, 0); 2165 if (s == NULL) 2166 s = object_search_ctlv(fo->tstate, 2167 insntoc(cmd, kidx)->kidx, IPFW_TLV_EACTION_NAME( 2168 state->eaction->kidx)); 2169 bprintf(bp, " %s", s); 2170 break; 2171 case O_EXTERNAL_DATA: 2172 if (state->eaction == NULL) 2173 break; 2174 /* 2175 * Currently we support data formatting only for 2176 * external data with datalen u16. For unknown data 2177 * print its size in bytes. 2178 */ 2179 if (cmd->len == F_INSN_SIZE(ipfw_insn)) 2180 bprintf(bp, " %u", cmd->arg1); 2181 else 2182 bprintf(bp, " %ubytes", 2183 cmd->len * sizeof(uint32_t)); 2184 break; 2185 case O_SETDSCP: 2186 if (cmd->arg1 == IP_FW_TARG) { 2187 bprintf(bp, "setdscp tablearg"); 2188 break; 2189 } 2190 s = match_value(f_ipdscp, cmd->arg1 & 0x3F); 2191 if (s != NULL) 2192 bprintf(bp, "setdscp %s", s); 2193 else 2194 bprintf(bp, "setdscp %u", cmd->arg1 & 0x3F); 2195 break; 2196 case O_REASS: 2197 bprintf(bp, "reass"); 2198 break; 2199 case O_CALLRETURN: 2200 if (cmd->len & F_NOT) { 2201 s = match_value(return_types, cmd->arg1); 2202 bprintf(bp, "return %s", s ? s: "<invalid>"); 2203 } else 2204 bprint_uint_arg(bp, "call ", insntoc(cmd, u32)->d[0]); 2205 break; 2206 case O_SETMARK: 2207 if (cmd->arg1 == IP_FW_TARG) { 2208 bprintf(bp, "setmark tablearg"); 2209 break; 2210 } 2211 bprintf(bp, "setmark %#x", insntoc(cmd, u32)->d[0]); 2212 break; 2213 default: 2214 bprintf(bp, "** unrecognized action %d len %d ", 2215 cmd->opcode, cmd->len); 2216 } 2217 mark_printed(state, cmd); 2218 2219 return (1); 2220 } 2221 2222 2223 static ipfw_insn * 2224 print_action(struct buf_pr *bp, struct format_opts *fo, 2225 struct show_state *state, uint8_t opcode) 2226 { 2227 ipfw_insn *cmd; 2228 int l; 2229 2230 for (l = state->rule->cmd_len - state->rule->act_ofs, 2231 cmd = ACTION_PTR(state->rule); l > 0; 2232 l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 2233 if (cmd->opcode != opcode) 2234 continue; 2235 if (!print_action_instruction(bp, fo, state, cmd)) 2236 continue; 2237 return (cmd); 2238 } 2239 return (NULL); 2240 } 2241 2242 static void 2243 print_proto(struct buf_pr *bp, struct format_opts *fo, 2244 struct show_state *state) 2245 { 2246 ipfw_insn *cmd; 2247 int l, proto, ip4, ip6; 2248 2249 /* Count all O_PROTO, O_IP4, O_IP6 instructions. */ 2250 proto = ip4 = ip6 = 0; 2251 for (l = state->rule->act_ofs, cmd = state->rule->cmd; 2252 l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 2253 switch (cmd->opcode) { 2254 case O_PROTO: 2255 proto++; 2256 break; 2257 case O_IP4: 2258 ip4 = 1; 2259 if (cmd->len & F_OR) 2260 ip4++; 2261 break; 2262 case O_IP6: 2263 ip6 = 1; 2264 if (cmd->len & F_OR) 2265 ip6++; 2266 break; 2267 default: 2268 continue; 2269 } 2270 } 2271 if (proto == 0 && ip4 == 0 && ip6 == 0) { 2272 state->proto = IPPROTO_IP; 2273 state->flags |= HAVE_PROTO; 2274 bprintf(bp, " ip"); 2275 return; 2276 } 2277 /* To handle the case { ip4 or ip6 }, print opcode with F_OR first */ 2278 cmd = NULL; 2279 if (ip4 || ip6) 2280 cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP4: O_IP6); 2281 if (cmd != NULL && (cmd->len & F_OR)) 2282 cmd = print_opcode(bp, fo, state, ip4 > ip6 ? O_IP6: O_IP4); 2283 if (cmd == NULL || (cmd->len & F_OR)) 2284 for (l = proto; l > 0; l--) { 2285 cmd = print_opcode(bp, fo, state, O_PROTO); 2286 if (cmd == NULL || (cmd->len & F_OR) == 0) 2287 break; 2288 } 2289 /* Initialize proto, it is used by print_newports() */ 2290 state->flags |= HAVE_PROTO; 2291 if (state->proto == 0 && ip6 != 0) 2292 state->proto = IPPROTO_IPV6; 2293 } 2294 2295 static int 2296 match_opcode(int opcode, const int opcodes[], size_t nops) 2297 { 2298 size_t i; 2299 2300 for (i = 0; i < nops; i++) 2301 if (opcode == opcodes[i]) 2302 return (1); 2303 return (0); 2304 } 2305 2306 static void 2307 print_address(struct buf_pr *bp, struct format_opts *fo, 2308 struct show_state *state, const int opcodes[], size_t nops, int portop, 2309 int flag) 2310 { 2311 ipfw_insn *cmd; 2312 int count, l, portcnt, pf; 2313 2314 count = portcnt = 0; 2315 for (l = state->rule->act_ofs, cmd = state->rule->cmd; 2316 l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 2317 if (match_opcode(cmd->opcode, opcodes, nops)) { 2318 /* 2319 * Special handling for O_IP_DST_LOOKUP when 2320 * lookup type is not LOOKUP_NONE. 2321 */ 2322 if (cmd->opcode == O_IP_DST_LOOKUP && 2323 IPFW_LOOKUP_TYPE(cmd) != LOOKUP_NONE) 2324 continue; 2325 count++; 2326 } else if (cmd->opcode == portop) 2327 portcnt++; 2328 } 2329 if (count == 0) 2330 bprintf(bp, " any"); 2331 for (l = state->rule->act_ofs, cmd = state->rule->cmd; 2332 l > 0 && count > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 2333 if (!match_opcode(cmd->opcode, opcodes, nops)) 2334 continue; 2335 print_instruction(bp, fo, state, cmd); 2336 if ((cmd->len & F_OR) == 0) 2337 break; 2338 count--; 2339 } 2340 /* 2341 * If several O_IP_?PORT opcodes specified, leave them to the 2342 * options section. 2343 */ 2344 if (portcnt == 1) { 2345 for (l = state->rule->act_ofs, cmd = state->rule->cmd, pf = 0; 2346 l > 0; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 2347 if (cmd->opcode != portop) { 2348 pf = (cmd->len & F_OR); 2349 continue; 2350 } 2351 /* Print opcode iff it is not in OR block. */ 2352 if (pf == 0 && (cmd->len & F_OR) == 0) 2353 print_instruction(bp, fo, state, cmd); 2354 break; 2355 } 2356 } 2357 state->flags |= flag; 2358 } 2359 2360 static const int action_opcodes[] = { 2361 O_CHECK_STATE, O_ACCEPT, O_COUNT, O_DENY, O_REJECT, 2362 O_UNREACH6, O_SKIPTO, O_PIPE, O_QUEUE, O_DIVERT, O_TEE, 2363 O_NETGRAPH, O_NGTEE, O_FORWARD_IP, O_FORWARD_IP6, O_NAT, 2364 O_SETFIB, O_SETDSCP, O_REASS, O_CALLRETURN, O_SETMARK, 2365 /* keep the following opcodes at the end of the list */ 2366 O_EXTERNAL_ACTION, O_EXTERNAL_INSTANCE, O_EXTERNAL_DATA 2367 }; 2368 2369 static const int modifier_opcodes[] = { 2370 O_LOG, O_ALTQ, O_TAG 2371 }; 2372 2373 static const int src_opcodes[] = { 2374 O_IP_SRC, O_IP_SRC_LOOKUP, O_IP_SRC_MASK, O_IP_SRC_ME, 2375 O_IP_SRC_SET, O_IP6_SRC, O_IP6_SRC_MASK, O_IP6_SRC_ME 2376 }; 2377 2378 static const int dst_opcodes[] = { 2379 O_IP_DST, O_IP_DST_LOOKUP, O_IP_DST_MASK, O_IP_DST_ME, 2380 O_IP_DST_SET, O_IP6_DST, O_IP6_DST_MASK, O_IP6_DST_ME 2381 }; 2382 2383 #if IPFW_DEFAULT_RULE > 65535 2384 #define RULENUM_FORMAT "%06d" 2385 #else 2386 #define RULENUM_FORMAT "%05d" 2387 #endif 2388 2389 static void 2390 show_static_rule(struct cmdline_opts *co, struct format_opts *fo, 2391 struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) 2392 { 2393 static int twidth = 0; 2394 struct show_state state; 2395 ipfw_insn *cmd; 2396 size_t i; 2397 2398 /* Print # DISABLED or skip the rule */ 2399 if ((fo->set_mask & (1 << rule->set)) == 0) { 2400 /* disabled mask */ 2401 if (!co->show_sets) 2402 return; 2403 else 2404 bprintf(bp, "# DISABLED "); 2405 } 2406 if (init_show_state(&state, rule) != 0) { 2407 warn("init_show_state() failed"); 2408 return; 2409 } 2410 2411 bprintf(bp, RULENUM_FORMAT " ", rule->rulenum); 2412 2413 /* only if counters are available */ 2414 if (cntr != NULL) { 2415 /* Print counters if enabled */ 2416 if (fo->pcwidth > 0 || fo->bcwidth > 0) { 2417 pr_u64(bp, &cntr->pcnt, fo->pcwidth); 2418 pr_u64(bp, &cntr->bcnt, fo->bcwidth); 2419 } 2420 2421 /* Print timestamp */ 2422 if (co->do_time == TIMESTAMP_NUMERIC) 2423 bprintf(bp, "%10u ", cntr->timestamp); 2424 else if (co->do_time == TIMESTAMP_STRING) { 2425 char timestr[30]; 2426 time_t t = (time_t)0; 2427 2428 if (twidth == 0) { 2429 strcpy(timestr, ctime(&t)); 2430 *strchr(timestr, '\n') = '\0'; 2431 twidth = strlen(timestr); 2432 } 2433 if (cntr->timestamp > 0) { 2434 t = _long_to_time(cntr->timestamp); 2435 2436 strcpy(timestr, ctime(&t)); 2437 *strchr(timestr, '\n') = '\0'; 2438 bprintf(bp, "%s ", timestr); 2439 } else { 2440 bprintf(bp, "%*s ", twidth, ""); 2441 } 2442 } 2443 } 2444 2445 /* Print set number */ 2446 if (co->show_sets) 2447 bprintf(bp, "set %d ", rule->set); 2448 2449 /* Print the optional "match probability" */ 2450 cmd = print_opcode(bp, fo, &state, O_PROB); 2451 /* Print rule action */ 2452 for (i = 0; i < nitems(action_opcodes); i++) { 2453 cmd = print_action(bp, fo, &state, action_opcodes[i]); 2454 if (cmd == NULL) 2455 continue; 2456 /* Handle special cases */ 2457 switch (cmd->opcode) { 2458 case O_CHECK_STATE: 2459 goto end; 2460 case O_EXTERNAL_ACTION: 2461 case O_EXTERNAL_INSTANCE: 2462 /* External action can have several instructions */ 2463 continue; 2464 } 2465 break; 2466 } 2467 /* Print rule modifiers */ 2468 for (i = 0; i < nitems(modifier_opcodes); i++) 2469 print_action(bp, fo, &state, modifier_opcodes[i]); 2470 /* 2471 * Print rule body 2472 */ 2473 if (co->comment_only != 0) 2474 goto end; 2475 2476 if (rule->flags & IPFW_RULE_JUSTOPTS) { 2477 state.flags |= HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP; 2478 /* 2479 * Print `proto ip` if all opcodes has been already printed. 2480 */ 2481 if (memchr(state.printed, 0, rule->act_ofs) == NULL) { 2482 bprintf(bp, " proto ip"); 2483 goto end; 2484 } 2485 goto justopts; 2486 } 2487 2488 print_proto(bp, fo, &state); 2489 if (co->do_compact != 0 && (rule->flags & IPFW_RULE_NOOPT)) 2490 goto justopts; 2491 2492 /* Print source */ 2493 bprintf(bp, " from"); 2494 print_address(bp, fo, &state, src_opcodes, nitems(src_opcodes), 2495 O_IP_SRCPORT, HAVE_SRCIP); 2496 2497 /* Print destination */ 2498 bprintf(bp, " to"); 2499 print_address(bp, fo, &state, dst_opcodes, nitems(dst_opcodes), 2500 O_IP_DSTPORT, HAVE_DSTIP); 2501 2502 justopts: 2503 /* Print the rest of options */ 2504 while (print_opcode(bp, fo, &state, -1)) 2505 ; 2506 end: 2507 /* Print comment at the end */ 2508 cmd = print_opcode(bp, fo, &state, O_NOP); 2509 if (co->comment_only != 0 && cmd == NULL) 2510 bprintf(bp, " // ..."); 2511 bprintf(bp, "\n"); 2512 free_show_state(&state); 2513 } 2514 2515 static void 2516 show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, 2517 struct buf_pr *bp, ipfw_dyn_rule *d) 2518 { 2519 struct protoent *pe; 2520 struct in_addr a; 2521 char buf[INET6_ADDRSTRLEN]; 2522 2523 if (!d->expire && !(d->type == O_LIMIT_PARENT)) 2524 return; 2525 2526 bprintf(bp, RULENUM_FORMAT, d->rulenum); 2527 if (fo->pcwidth > 0 || fo->bcwidth > 0) { 2528 bprintf(bp, " "); 2529 pr_u64(bp, &d->pcnt, fo->pcwidth); 2530 pr_u64(bp, &d->bcnt, fo->bcwidth); 2531 bprintf(bp, "(%ds)", d->expire); 2532 } 2533 switch (d->type) { 2534 case O_LIMIT_PARENT: 2535 bprintf(bp, " PARENT %u", d->count); 2536 break; 2537 case O_LIMIT: 2538 bprintf(bp, " LIMIT"); 2539 break; 2540 case O_KEEP_STATE: /* bidir, no mask */ 2541 bprintf(bp, " STATE"); 2542 break; 2543 } 2544 2545 if ((pe = getprotobynumber(d->id.proto)) != NULL) 2546 bprintf(bp, " %s", pe->p_name); 2547 else 2548 bprintf(bp, " proto %u", d->id.proto); 2549 2550 if (d->id.addr_type == 4) { 2551 a.s_addr = htonl(d->id.src_ip); 2552 bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port); 2553 2554 a.s_addr = htonl(d->id.dst_ip); 2555 bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port); 2556 } else if (d->id.addr_type == 6) { 2557 bprintf(bp, " %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, 2558 sizeof(buf)), d->id.src_port); 2559 bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, 2560 buf, sizeof(buf)), d->id.dst_port); 2561 } else 2562 bprintf(bp, " UNKNOWN <-> UNKNOWN"); 2563 if (d->kidx != 0) 2564 bprintf(bp, " :%s", object_search_ctlv(fo->tstate, 2565 d->kidx, IPFW_TLV_STATE_NAME)); 2566 2567 #define BOTH_SYN (TH_SYN | (TH_SYN << 8)) 2568 #define BOTH_FIN (TH_FIN | (TH_FIN << 8)) 2569 if (co->verbose) { 2570 bprintf(bp, " state 0x%08x%s", d->state, 2571 d->state ? " ": ","); 2572 if (d->state & IPFW_DYN_ORPHANED) 2573 bprintf(bp, "ORPHANED,"); 2574 if ((d->state & BOTH_SYN) == BOTH_SYN) 2575 bprintf(bp, "BOTH_SYN,"); 2576 else { 2577 if (d->state & TH_SYN) 2578 bprintf(bp, "F_SYN,"); 2579 if (d->state & (TH_SYN << 8)) 2580 bprintf(bp, "R_SYN,"); 2581 } 2582 if ((d->state & BOTH_FIN) == BOTH_FIN) 2583 bprintf(bp, "BOTH_FIN,"); 2584 else { 2585 if (d->state & TH_FIN) 2586 bprintf(bp, "F_FIN,"); 2587 if (d->state & (TH_FIN << 8)) 2588 bprintf(bp, "R_FIN,"); 2589 } 2590 bprintf(bp, " f_ack 0x%x, r_ack 0x%x", d->ack_fwd, 2591 d->ack_rev); 2592 } 2593 } 2594 2595 static int 2596 do_range_cmd(int cmd, ipfw_range_tlv *rt) 2597 { 2598 ipfw_range_header rh; 2599 size_t sz; 2600 2601 memset(&rh, 0, sizeof(rh)); 2602 memcpy(&rh.range, rt, sizeof(*rt)); 2603 rh.range.head.length = sizeof(*rt); 2604 rh.range.head.type = IPFW_TLV_RANGE; 2605 sz = sizeof(rh); 2606 2607 if (do_get3(cmd, &rh.opheader, &sz) != 0) 2608 return (-1); 2609 /* Save number of matched objects */ 2610 rt->new_set = rh.range.new_set; 2611 return (0); 2612 } 2613 2614 /* 2615 * This one handles all set-related commands 2616 * ipfw set { show | enable | disable } 2617 * ipfw set swap X Y 2618 * ipfw set move X to Y 2619 * ipfw set move rule X to Y 2620 */ 2621 void 2622 ipfw_sets_handler(char *av[]) 2623 { 2624 ipfw_range_tlv rt; 2625 const char *msg; 2626 size_t size; 2627 uint32_t masks[2], rulenum; 2628 int i; 2629 uint8_t cmd; 2630 2631 av++; 2632 memset(&rt, 0, sizeof(rt)); 2633 2634 if (av[0] == NULL) 2635 errx(EX_USAGE, "set needs command"); 2636 if (_substrcmp(*av, "show") == 0) { 2637 struct format_opts fo; 2638 ipfw_cfg_lheader *cfg; 2639 2640 memset(&fo, 0, sizeof(fo)); 2641 if (ipfw_get_config(&g_co, &fo, &cfg, &size) != 0) 2642 err(EX_OSERR, "requesting config failed"); 2643 2644 for (i = 0, msg = "disable"; i < RESVD_SET; i++) 2645 if ((cfg->set_mask & (1<<i)) == 0) { 2646 printf("%s %d", msg, i); 2647 msg = ""; 2648 } 2649 msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable"; 2650 for (i = 0; i < RESVD_SET; i++) 2651 if ((cfg->set_mask & (1<<i)) != 0) { 2652 printf("%s %d", msg, i); 2653 msg = ""; 2654 } 2655 printf("\n"); 2656 free(cfg); 2657 } else if (_substrcmp(*av, "swap") == 0) { 2658 av++; 2659 if ( av[0] == NULL || av[1] == NULL ) 2660 errx(EX_USAGE, "set swap needs 2 set numbers\n"); 2661 rt.set = atoi(av[0]); 2662 rt.new_set = atoi(av[1]); 2663 if (!isdigit(*(av[0])) || rt.set > RESVD_SET) 2664 errx(EX_DATAERR, "invalid set number %s\n", av[0]); 2665 if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET) 2666 errx(EX_DATAERR, "invalid set number %s\n", av[1]); 2667 i = do_range_cmd(IP_FW_SET_SWAP, &rt); 2668 } else if (_substrcmp(*av, "move") == 0) { 2669 av++; 2670 if (av[0] && _substrcmp(*av, "rule") == 0) { 2671 rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */ 2672 cmd = IP_FW_XMOVE; 2673 av++; 2674 } else 2675 cmd = IP_FW_SET_MOVE; /* Move set to new one */ 2676 if (av[0] == NULL || av[1] == NULL || av[2] == NULL || 2677 av[3] != NULL || _substrcmp(av[1], "to") != 0) 2678 errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); 2679 rulenum = (uint32_t)strtoul(av[0], NULL, 10); 2680 rt.new_set = atoi(av[2]); 2681 if (cmd == IP_FW_XMOVE) { 2682 rt.start_rule = rulenum; 2683 rt.end_rule = rulenum; 2684 } else 2685 rt.set = rulenum; 2686 rt.new_set = atoi(av[2]); 2687 if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) || 2688 (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) ) 2689 errx(EX_DATAERR, "invalid source number %s\n", av[0]); 2690 if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET) 2691 errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); 2692 i = do_range_cmd(cmd, &rt); 2693 if (i < 0) 2694 err(EX_OSERR, "failed to move %s", 2695 cmd == IP_FW_SET_MOVE ? "set": "rule"); 2696 } else if (_substrcmp(*av, "disable") == 0 || 2697 _substrcmp(*av, "enable") == 0 ) { 2698 int which = _substrcmp(*av, "enable") == 0 ? 1 : 0; 2699 2700 av++; 2701 masks[0] = masks[1] = 0; 2702 2703 while (av[0]) { 2704 if (isdigit(**av)) { 2705 i = atoi(*av); 2706 if (i < 0 || i > RESVD_SET) 2707 errx(EX_DATAERR, 2708 "invalid set number %d\n", i); 2709 masks[which] |= (1<<i); 2710 } else if (_substrcmp(*av, "disable") == 0) 2711 which = 0; 2712 else if (_substrcmp(*av, "enable") == 0) 2713 which = 1; 2714 else 2715 errx(EX_DATAERR, 2716 "invalid set command %s\n", *av); 2717 av++; 2718 } 2719 if ( (masks[0] & masks[1]) != 0 ) 2720 errx(EX_DATAERR, 2721 "cannot enable and disable the same set\n"); 2722 2723 rt.set = masks[0]; 2724 rt.new_set = masks[1]; 2725 i = do_range_cmd(IP_FW_SET_ENABLE, &rt); 2726 if (i) 2727 warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)"); 2728 } else 2729 errx(EX_USAGE, "invalid set command %s\n", *av); 2730 } 2731 2732 static void 2733 manage_skipto_cache(int op) 2734 { 2735 ipfw_cmd_header req; 2736 2737 memset(&req, 0, sizeof(req)); 2738 req.size = sizeof(req); 2739 req.cmd = op ? SKIPTO_CACHE_ENABLE : SKIPTO_CACHE_DISABLE; 2740 2741 do_set3(IP_FW_SKIPTO_CACHE, &req.opheader, sizeof(req)); 2742 } 2743 2744 void 2745 ipfw_sysctl_handler(char *av[], int which) 2746 { 2747 av++; 2748 2749 if (av[0] == NULL) { 2750 warnx("missing keyword to enable/disable\n"); 2751 } else if (_substrcmp(*av, "firewall") == 0) { 2752 sysctlbyname("net.inet.ip.fw.enable", NULL, 0, 2753 &which, sizeof(which)); 2754 sysctlbyname("net.inet6.ip6.fw.enable", NULL, 0, 2755 &which, sizeof(which)); 2756 } else if (_substrcmp(*av, "one_pass") == 0) { 2757 sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0, 2758 &which, sizeof(which)); 2759 } else if (_substrcmp(*av, "debug") == 0) { 2760 sysctlbyname("net.inet.ip.fw.debug", NULL, 0, 2761 &which, sizeof(which)); 2762 } else if (_substrcmp(*av, "verbose") == 0) { 2763 sysctlbyname("net.inet.ip.fw.verbose", NULL, 0, 2764 &which, sizeof(which)); 2765 } else if (_substrcmp(*av, "dyn_keepalive") == 0) { 2766 sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, 2767 &which, sizeof(which)); 2768 } else if (_substrcmp(*av, "skipto_cache") == 0) { 2769 manage_skipto_cache(which); 2770 #ifndef NO_ALTQ 2771 } else if (_substrcmp(*av, "altq") == 0) { 2772 altq_set_enabled(which); 2773 #endif 2774 } else { 2775 warnx("unrecognize enable/disable keyword: %s\n", *av); 2776 } 2777 } 2778 2779 typedef void state_cb(struct cmdline_opts *co, struct format_opts *fo, 2780 void *arg, void *state); 2781 2782 static void 2783 prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo, 2784 void *arg __unused, void *_state) 2785 { 2786 ipfw_dyn_rule *d; 2787 int width; 2788 2789 d = (ipfw_dyn_rule *)_state; 2790 /* Count _ALL_ states */ 2791 fo->dcnt++; 2792 2793 if (fo->show_counters == 0) 2794 return; 2795 2796 /* skip states from another set */ 2797 if (co->use_set != 0 && d->set != co->use_set - 1) 2798 return; 2799 2800 width = pr_u64(NULL, &d->pcnt, 0); 2801 if (width > fo->pcwidth) 2802 fo->pcwidth = width; 2803 2804 width = pr_u64(NULL, &d->bcnt, 0); 2805 if (width > fo->bcwidth) 2806 fo->bcwidth = width; 2807 } 2808 2809 static int 2810 foreach_state(struct cmdline_opts *co, struct format_opts *fo, 2811 caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg) 2812 { 2813 int ttype; 2814 state_cb *fptr; 2815 void *farg; 2816 ipfw_obj_tlv *tlv; 2817 ipfw_obj_ctlv *ctlv; 2818 2819 fptr = NULL; 2820 ttype = 0; 2821 2822 while (sz > 0) { 2823 ctlv = (ipfw_obj_ctlv *)base; 2824 switch (ctlv->head.type) { 2825 case IPFW_TLV_DYNSTATE_LIST: 2826 base += sizeof(*ctlv); 2827 sz -= sizeof(*ctlv); 2828 ttype = IPFW_TLV_DYN_ENT; 2829 fptr = dyn_bc; 2830 farg = dyn_arg; 2831 break; 2832 default: 2833 return (sz); 2834 } 2835 2836 while (sz > 0) { 2837 tlv = (ipfw_obj_tlv *)base; 2838 if (tlv->type != ttype) 2839 break; 2840 2841 fptr(co, fo, farg, tlv + 1); 2842 sz -= tlv->length; 2843 base += tlv->length; 2844 } 2845 } 2846 2847 return (sz); 2848 } 2849 2850 static void 2851 prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, 2852 ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz) 2853 { 2854 int bcwidth, pcwidth, width; 2855 int n; 2856 struct ip_fw_bcounter *cntr; 2857 struct ip_fw_rule *r; 2858 2859 bcwidth = 0; 2860 pcwidth = 0; 2861 if (fo->show_counters != 0) { 2862 for (n = 0; n < rcnt; n++, 2863 rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { 2864 cntr = (struct ip_fw_bcounter *)(rtlv + 1); 2865 r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); 2866 /* skip rules from another set */ 2867 if (co->use_set && r->set != co->use_set - 1) 2868 continue; 2869 2870 /* packet counter */ 2871 width = pr_u64(NULL, &cntr->pcnt, 0); 2872 if (width > pcwidth) 2873 pcwidth = width; 2874 2875 /* byte counter */ 2876 width = pr_u64(NULL, &cntr->bcnt, 0); 2877 if (width > bcwidth) 2878 bcwidth = width; 2879 } 2880 } 2881 fo->bcwidth = bcwidth; 2882 fo->pcwidth = pcwidth; 2883 2884 fo->dcnt = 0; 2885 if (co->do_dynamic && dynsz > 0) 2886 foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL); 2887 } 2888 2889 static int 2890 list_static_range(struct cmdline_opts *co, struct format_opts *fo, 2891 struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt) 2892 { 2893 int n, seen; 2894 struct ip_fw_rule *r; 2895 struct ip_fw_bcounter *cntr; 2896 2897 for (n = seen = 0; n < rcnt; n++, 2898 rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { 2899 2900 if ((fo->show_counters | fo->show_time) != 0) { 2901 cntr = (struct ip_fw_bcounter *)(rtlv + 1); 2902 r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); 2903 } else { 2904 cntr = NULL; 2905 r = (struct ip_fw_rule *)(rtlv + 1); 2906 } 2907 if (r->rulenum > fo->last) 2908 break; 2909 if (co->use_set && r->set != co->use_set - 1) 2910 continue; 2911 if (r->rulenum >= fo->first && r->rulenum <= fo->last) { 2912 show_static_rule(co, fo, bp, r, cntr); 2913 printf("%s", bp->buf); 2914 bp_flush(bp); 2915 seen++; 2916 } 2917 } 2918 2919 return (seen); 2920 } 2921 2922 static void 2923 list_dyn_state(struct cmdline_opts *co, struct format_opts *fo, 2924 void *_arg, void *_state) 2925 { 2926 ipfw_dyn_rule *d; 2927 struct buf_pr *bp; 2928 2929 d = (ipfw_dyn_rule *)_state; 2930 bp = (struct buf_pr *)_arg; 2931 2932 if (d->rulenum > fo->last) 2933 return; 2934 if (co->use_set != 0 && d->set != co->use_set - 1) 2935 return; 2936 if (d->rulenum >= fo->first) { 2937 show_dyn_state(co, fo, bp, d); 2938 printf("%s\n", bp->buf); 2939 bp_flush(bp); 2940 } 2941 } 2942 2943 static int 2944 list_dyn_range(struct cmdline_opts *co, struct format_opts *fo, 2945 struct buf_pr *bp, caddr_t base, size_t sz) 2946 { 2947 2948 sz = foreach_state(co, fo, base, sz, list_dyn_state, bp); 2949 return (sz); 2950 } 2951 2952 void 2953 ipfw_list(int ac, char *av[], int show_counters) 2954 { 2955 ipfw_cfg_lheader *cfg; 2956 struct format_opts sfo; 2957 size_t sz; 2958 int error; 2959 int lac; 2960 char **lav; 2961 uint32_t rnum; 2962 char *endptr; 2963 2964 if (g_co.test_only) { 2965 fprintf(stderr, "Testing only, list disabled\n"); 2966 return; 2967 } 2968 if (g_co.do_pipe) { 2969 dummynet_list(ac, av, show_counters); 2970 return; 2971 } 2972 2973 ac--; 2974 av++; 2975 memset(&sfo, 0, sizeof(sfo)); 2976 2977 /* Determine rule range to request */ 2978 if (ac > 0) { 2979 for (lac = ac, lav = av; lac != 0; lac--) { 2980 rnum = strtoul(*lav++, &endptr, 10); 2981 if (sfo.first == 0 || rnum < sfo.first) 2982 sfo.first = rnum; 2983 2984 if (*endptr == '-') 2985 rnum = strtoul(endptr + 1, &endptr, 10); 2986 if (sfo.last == 0 || rnum > sfo.last) 2987 sfo.last = rnum; 2988 } 2989 } 2990 2991 /* get configuration from kernel */ 2992 cfg = NULL; 2993 sfo.show_counters = show_counters; 2994 sfo.show_time = g_co.do_time; 2995 if (g_co.do_dynamic != 2) 2996 sfo.flags |= IPFW_CFG_GET_STATIC; 2997 if (g_co.do_dynamic != 0) 2998 sfo.flags |= IPFW_CFG_GET_STATES; 2999 if ((sfo.show_counters | sfo.show_time) != 0) 3000 sfo.flags |= IPFW_CFG_GET_COUNTERS; 3001 if (ipfw_get_config(&g_co, &sfo, &cfg, &sz) != 0) 3002 err(EX_OSERR, "retrieving config failed"); 3003 3004 error = ipfw_show_config(&g_co, &sfo, cfg, sz, ac, av); 3005 3006 free(cfg); 3007 3008 if (error != EX_OK) 3009 exit(error); 3010 } 3011 3012 static int 3013 ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, 3014 ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[]) 3015 { 3016 caddr_t dynbase; 3017 size_t dynsz; 3018 int rcnt; 3019 int exitval = EX_OK; 3020 int lac; 3021 char **lav; 3022 char *endptr; 3023 size_t readsz; 3024 struct buf_pr bp; 3025 ipfw_obj_ctlv *ctlv; 3026 ipfw_obj_tlv *rbase; 3027 3028 /* 3029 * Handle tablenames TLV first, if any 3030 */ 3031 rbase = NULL; 3032 dynbase = NULL; 3033 dynsz = 0; 3034 readsz = sizeof(*cfg); 3035 rcnt = 0; 3036 3037 fo->set_mask = cfg->set_mask; 3038 3039 ctlv = (ipfw_obj_ctlv *)(cfg + 1); 3040 if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { 3041 object_sort_ctlv(ctlv); 3042 fo->tstate = ctlv; 3043 readsz += ctlv->head.length; 3044 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); 3045 } 3046 3047 if (cfg->flags & IPFW_CFG_GET_STATIC) { 3048 /* We've requested static rules */ 3049 if (ctlv->head.type == IPFW_TLV_RULE_LIST) { 3050 rbase = (ipfw_obj_tlv *)(ctlv + 1); 3051 rcnt = ctlv->count; 3052 readsz += ctlv->head.length; 3053 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + 3054 ctlv->head.length); 3055 } 3056 } 3057 3058 if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz)) { 3059 /* We may have some dynamic states */ 3060 dynsz = sz - readsz; 3061 /* Skip empty header */ 3062 if (dynsz != sizeof(ipfw_obj_ctlv)) 3063 dynbase = (caddr_t)ctlv; 3064 else 3065 dynsz = 0; 3066 } 3067 3068 prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz); 3069 bp_alloc(&bp, 4096); 3070 3071 /* if no rule numbers were specified, list all rules */ 3072 if (ac == 0) { 3073 fo->first = 0; 3074 fo->last = IPFW_DEFAULT_RULE; 3075 if (cfg->flags & IPFW_CFG_GET_STATIC) 3076 list_static_range(co, fo, &bp, rbase, rcnt); 3077 3078 if (co->do_dynamic && dynsz > 0) { 3079 printf("## Dynamic rules (%d %zu):\n", fo->dcnt, 3080 dynsz); 3081 list_dyn_range(co, fo, &bp, dynbase, dynsz); 3082 } 3083 3084 bp_free(&bp); 3085 return (EX_OK); 3086 } 3087 3088 /* display specific rules requested on command line */ 3089 for (lac = ac, lav = av; lac != 0; lac--) { 3090 /* convert command line rule # */ 3091 fo->last = fo->first = strtoul(*lav++, &endptr, 10); 3092 if (*endptr == '-') 3093 fo->last = strtoul(endptr + 1, &endptr, 10); 3094 if (*endptr) { 3095 exitval = EX_USAGE; 3096 warnx("invalid rule number: %s", *(lav - 1)); 3097 continue; 3098 } 3099 3100 if ((cfg->flags & IPFW_CFG_GET_STATIC) == 0) 3101 continue; 3102 3103 if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) { 3104 /* give precedence to other error(s) */ 3105 if (exitval == EX_OK) 3106 exitval = EX_UNAVAILABLE; 3107 if (fo->first == fo->last) 3108 warnx("rule %u does not exist", fo->first); 3109 else 3110 warnx("no rules in range %u-%u", 3111 fo->first, fo->last); 3112 } 3113 } 3114 3115 if (co->do_dynamic && dynsz > 0) { 3116 printf("## Dynamic rules:\n"); 3117 for (lac = ac, lav = av; lac != 0; lac--) { 3118 fo->last = fo->first = strtoul(*lav++, &endptr, 10); 3119 if (*endptr == '-') 3120 fo->last = strtoul(endptr+1, &endptr, 10); 3121 if (*endptr) 3122 /* already warned */ 3123 continue; 3124 list_dyn_range(co, fo, &bp, dynbase, dynsz); 3125 } 3126 } 3127 3128 bp_free(&bp); 3129 return (exitval); 3130 } 3131 3132 3133 /* 3134 * Retrieves current ipfw configuration of given type 3135 * and stores its pointer to @pcfg. 3136 * 3137 * Caller is responsible for freeing @pcfg. 3138 * 3139 * Returns 0 on success. 3140 */ 3141 3142 static int 3143 ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, 3144 ipfw_cfg_lheader **pcfg, size_t *psize) 3145 { 3146 ipfw_cfg_lheader *cfg; 3147 size_t sz; 3148 int i; 3149 3150 3151 if (co->test_only != 0) { 3152 fprintf(stderr, "Testing only, list disabled\n"); 3153 return (0); 3154 } 3155 3156 /* Start with some data size */ 3157 sz = 4096; 3158 cfg = NULL; 3159 3160 for (i = 0; i < 16; i++) { 3161 if (cfg != NULL) 3162 free(cfg); 3163 if ((cfg = calloc(1, sz)) == NULL) 3164 return (ENOMEM); 3165 3166 cfg->flags = fo->flags; 3167 cfg->start_rule = fo->first; 3168 cfg->end_rule = fo->last; 3169 3170 if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) { 3171 if (errno != ENOMEM) { 3172 free(cfg); 3173 return (errno); 3174 } 3175 3176 /* Buffer size is not enough. Try to increase */ 3177 sz = sz * 2; 3178 if (sz < cfg->size) 3179 sz = cfg->size; 3180 continue; 3181 } 3182 3183 *pcfg = cfg; 3184 *psize = sz; 3185 return (0); 3186 } 3187 3188 free(cfg); 3189 return (ENOMEM); 3190 } 3191 3192 static int 3193 lookup_host (char *host, struct in_addr *ipaddr) 3194 { 3195 struct hostent *he; 3196 3197 if (!inet_aton(host, ipaddr)) { 3198 if ((he = gethostbyname(host)) == NULL) 3199 return(-1); 3200 *ipaddr = *(struct in_addr *)he->h_addr_list[0]; 3201 } 3202 return(0); 3203 } 3204 3205 struct tidx { 3206 ipfw_obj_ntlv *idx; 3207 uint32_t count; 3208 uint32_t size; 3209 uint16_t counter; 3210 uint8_t set; 3211 }; 3212 3213 int 3214 ipfw_check_object_name(const char *name) 3215 { 3216 int c, i, l; 3217 3218 /* 3219 * Check that name is null-terminated and contains 3220 * valid symbols only. Valid mask is: 3221 * [a-zA-Z0-9\-_\.]{1,63} 3222 */ 3223 l = strlen(name); 3224 if (l == 0 || l >= 64) 3225 return (EINVAL); 3226 for (i = 0; i < l; i++) { 3227 c = name[i]; 3228 if (isalpha(c) || isdigit(c) || c == '_' || 3229 c == '-' || c == '.') 3230 continue; 3231 return (EINVAL); 3232 } 3233 return (0); 3234 } 3235 3236 static const char *default_state_name = "default"; 3237 3238 static int 3239 state_check_name(const char *name) 3240 { 3241 3242 if (ipfw_check_object_name(name) != 0) 3243 return (EINVAL); 3244 if (strcmp(name, "any") == 0) 3245 return (EINVAL); 3246 return (0); 3247 } 3248 3249 static int 3250 eaction_check_name(const char *name) 3251 { 3252 3253 if (ipfw_check_object_name(name) != 0) 3254 return (EINVAL); 3255 /* Restrict some 'special' names */ 3256 if (match_token(rule_actions, name) != -1 && 3257 match_token(rule_action_params, name) != -1) 3258 return (EINVAL); 3259 return (0); 3260 } 3261 3262 static uint32_t 3263 pack_object(struct tidx *tstate, const char *name, int otype) 3264 { 3265 ipfw_obj_ntlv *ntlv; 3266 uint32_t i; 3267 3268 for (i = 0; i < tstate->count; i++) { 3269 if (strcmp(tstate->idx[i].name, name) != 0) 3270 continue; 3271 if (tstate->idx[i].set != tstate->set) 3272 continue; 3273 if (tstate->idx[i].head.type != otype) 3274 continue; 3275 3276 return (tstate->idx[i].idx); 3277 } 3278 3279 if (tstate->count + 1 > tstate->size) { 3280 tstate->size += 4; 3281 tstate->idx = realloc(tstate->idx, tstate->size * 3282 sizeof(ipfw_obj_ntlv)); 3283 if (tstate->idx == NULL) 3284 return (0); 3285 } 3286 3287 ntlv = &tstate->idx[i]; 3288 memset(ntlv, 0, sizeof(ipfw_obj_ntlv)); 3289 strlcpy(ntlv->name, name, sizeof(ntlv->name)); 3290 ntlv->head.type = otype; 3291 ntlv->head.length = sizeof(ipfw_obj_ntlv); 3292 ntlv->set = tstate->set; 3293 ntlv->idx = ++tstate->counter; 3294 tstate->count++; 3295 3296 return (ntlv->idx); 3297 } 3298 3299 static uint32_t 3300 pack_table(struct tidx *tstate, const char *name) 3301 { 3302 3303 if (table_check_name(name) != 0) 3304 return (0); 3305 3306 return (pack_object(tstate, name, IPFW_TLV_TBL_NAME)); 3307 } 3308 3309 static void 3310 fill_table_value(ipfw_insn *cmd, char *s) 3311 { 3312 char *p; 3313 int i; 3314 3315 p = strchr(s, '='); 3316 if (p != NULL) { 3317 *p++ = '\0'; 3318 i = match_token(tvalue_names, s); 3319 if (i == -1) 3320 errx(EX_USAGE, 3321 "format: unknown table value name %s", s); 3322 } else { 3323 i = TVALUE_TAG; 3324 p = s; 3325 } 3326 3327 IPFW_SET_TVALUE_TYPE(cmd, i); 3328 insntod(cmd, table)->value = strtoul(p, NULL, 0); 3329 } 3330 3331 void 3332 fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) 3333 { 3334 ipfw_insn_kidx *c = insntod(cmd, kidx); 3335 char *p; 3336 3337 if ((p = strchr(av + 6, ')')) == NULL) 3338 errx(EX_DATAERR, "forgotten parenthesis: '%s'", av); 3339 *p = '\0'; 3340 p = strchr(av + 6, ','); 3341 if (p) 3342 *p++ = '\0'; 3343 3344 if ((c->kidx = pack_table(tstate, av + 6)) == 0) 3345 errx(EX_DATAERR, "Invalid table name: %s", av + 6); 3346 3347 cmd->opcode = opcode; 3348 if (p) { 3349 cmd->len |= F_INSN_SIZE(ipfw_insn_table); 3350 fill_table_value(cmd, p); 3351 } else { 3352 IPFW_SET_LOOKUP_TYPE(cmd, LOOKUP_NONE); 3353 cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); 3354 } 3355 } 3356 3357 /* 3358 * fills the addr and mask fields in the instruction as appropriate from av. 3359 * Update length as appropriate. 3360 * The following formats are allowed: 3361 * me returns O_IP_*_ME 3362 * 1.2.3.4 single IP address 3363 * 1.2.3.4:5.6.7.8 address:mask 3364 * 1.2.3.4/24 address/mask 3365 * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet 3366 * We can have multiple comma-separated address/mask entries. 3367 */ 3368 static void 3369 fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate) 3370 { 3371 int len = 0; 3372 uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; 3373 3374 cmd->o.len &= ~F_LEN_MASK; /* zero len */ 3375 3376 if (_substrcmp(av, "any") == 0) 3377 return; 3378 3379 if (_substrcmp(av, "me") == 0) { 3380 cmd->o.len |= F_INSN_SIZE(ipfw_insn); 3381 return; 3382 } 3383 3384 if (strncmp(av, "table(", 6) == 0) { 3385 fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate); 3386 return; 3387 } 3388 3389 while (av) { 3390 /* 3391 * After the address we can have '/' or ':' indicating a mask, 3392 * ',' indicating another address follows, '{' indicating a 3393 * set of addresses of unspecified size. 3394 */ 3395 char *t = NULL, *p = strpbrk(av, "/:,{"); 3396 int masklen; 3397 char md, nd = '\0'; 3398 3399 CHECK_LENGTH(cblen, (int)F_INSN_SIZE(ipfw_insn) + 2 + len); 3400 3401 if (p) { 3402 md = *p; 3403 *p++ = '\0'; 3404 if ((t = strpbrk(p, ",{")) != NULL) { 3405 nd = *t; 3406 *t = '\0'; 3407 } 3408 } else 3409 md = '\0'; 3410 3411 if (lookup_host(av, (struct in_addr *)&d[0]) != 0) 3412 errx(EX_NOHOST, "hostname ``%s'' unknown", av); 3413 switch (md) { 3414 case ':': 3415 if (!inet_aton(p, (struct in_addr *)&d[1])) 3416 errx(EX_DATAERR, "bad netmask ``%s''", p); 3417 break; 3418 case '/': 3419 masklen = atoi(p); 3420 if (masklen == 0) 3421 d[1] = htonl(0U); /* mask */ 3422 else if (masklen > 32) 3423 errx(EX_DATAERR, "bad width ``%s''", p); 3424 else 3425 d[1] = htonl(~0U << (32 - masklen)); 3426 break; 3427 case '{': /* no mask, assume /24 and put back the '{' */ 3428 d[1] = htonl(~0U << (32 - 24)); 3429 *(--p) = md; 3430 break; 3431 3432 case ',': /* single address plus continuation */ 3433 *(--p) = md; 3434 /* FALLTHROUGH */ 3435 case 0: /* initialization value */ 3436 default: 3437 d[1] = htonl(~0U); /* force /32 */ 3438 break; 3439 } 3440 d[0] &= d[1]; /* mask base address with mask */ 3441 if (t) 3442 *t = nd; 3443 /* find next separator */ 3444 if (p) 3445 p = strpbrk(p, ",{"); 3446 if (p && *p == '{') { 3447 /* 3448 * We have a set of addresses. They are stored as follows: 3449 * arg1 is the set size (powers of 2, 2..256) 3450 * addr is the base address IN HOST FORMAT 3451 * mask.. is an array of arg1 bits (rounded up to 3452 * the next multiple of 32) with bits set 3453 * for each host in the map. 3454 */ 3455 uint32_t *map = (uint32_t *)&cmd->mask; 3456 int low, high; 3457 int i = contigmask((uint8_t *)&(d[1]), 32); 3458 3459 if (len > 0) 3460 errx(EX_DATAERR, "address set cannot be in a list"); 3461 if (i < 24 || i > 31) 3462 errx(EX_DATAERR, "invalid set with mask %d\n", i); 3463 cmd->o.arg1 = 1<<(32-i); /* map length */ 3464 d[0] = ntohl(d[0]); /* base addr in host format */ 3465 cmd->o.opcode = O_IP_DST_SET; /* default */ 3466 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; 3467 for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) 3468 map[i] = 0; /* clear map */ 3469 3470 av = p + 1; 3471 low = d[0] & 0xff; 3472 high = low + cmd->o.arg1 - 1; 3473 /* 3474 * Here, i stores the previous value when we specify a range 3475 * of addresses within a mask, e.g. 45-63. i = -1 means we 3476 * have no previous value. 3477 */ 3478 i = -1; /* previous value in a range */ 3479 while (isdigit(*av)) { 3480 char *s; 3481 int a = strtol(av, &s, 0); 3482 3483 if (s == av) { /* no parameter */ 3484 if (*av != '}') 3485 errx(EX_DATAERR, "set not closed\n"); 3486 if (i != -1) 3487 errx(EX_DATAERR, "incomplete range %d-", i); 3488 break; 3489 } 3490 if (a < low || a > high) 3491 errx(EX_DATAERR, "addr %d out of range [%d-%d]\n", 3492 a, low, high); 3493 a -= low; 3494 if (i == -1) /* no previous in range */ 3495 i = a; 3496 else { /* check that range is valid */ 3497 if (i > a) 3498 errx(EX_DATAERR, "invalid range %d-%d", 3499 i+low, a+low); 3500 if (*s == '-') 3501 errx(EX_DATAERR, "double '-' in range"); 3502 } 3503 for (; i <= a; i++) 3504 map[i/32] |= 1<<(i & 31); 3505 i = -1; 3506 if (*s == '-') 3507 i = a; 3508 else if (*s == '}') 3509 break; 3510 av = s+1; 3511 } 3512 return; 3513 } 3514 av = p; 3515 if (av) /* then *av must be a ',' */ 3516 av++; 3517 3518 /* Check this entry */ 3519 if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */ 3520 /* 3521 * 'any' turns the entire list into a NOP. 3522 * 'not any' never matches, so it is removed from the 3523 * list unless it is the only item, in which case we 3524 * report an error. 3525 */ 3526 if (cmd->o.len & F_NOT) { /* "not any" never matches */ 3527 if (av == NULL && len == 0) /* only this entry */ 3528 errx(EX_DATAERR, "not any never matches"); 3529 } 3530 /* else do nothing and skip this entry */ 3531 return; 3532 } 3533 /* A single IP can be stored in an optimized format */ 3534 if (d[1] == (uint32_t)~0 && av == NULL && len == 0) { 3535 cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 3536 return; 3537 } 3538 len += 2; /* two words... */ 3539 d += 2; 3540 } /* end while */ 3541 if (len + 1 > F_LEN_MASK) 3542 errx(EX_DATAERR, "address list too long"); 3543 cmd->o.len |= len+1; 3544 } 3545 3546 3547 /* n2mask sets n bits of the mask */ 3548 void 3549 n2mask(struct in6_addr *mask, int n) 3550 { 3551 static int minimask[9] = 3552 { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; 3553 u_char *p; 3554 3555 memset(mask, 0, sizeof(struct in6_addr)); 3556 p = (u_char *) mask; 3557 for (; n > 0; p++, n -= 8) { 3558 if (n >= 8) 3559 *p = 0xff; 3560 else 3561 *p = minimask[n]; 3562 } 3563 return; 3564 } 3565 3566 static void 3567 fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, 3568 struct _s_x *flags, char *p) 3569 { 3570 char *e; 3571 uint32_t set = 0, clear = 0; 3572 3573 if (fill_flags(flags, p, &e, &set, &clear) != 0) 3574 errx(EX_DATAERR, "invalid flag %s", e); 3575 3576 cmd->opcode = opcode; 3577 cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; 3578 cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); 3579 } 3580 3581 3582 void 3583 ipfw_delete(char *av[]) 3584 { 3585 ipfw_range_tlv rt; 3586 char *sep; 3587 uint32_t i, j; 3588 int exitval = EX_OK; 3589 int do_set = 0; 3590 3591 av++; 3592 NEED1("missing rule specification"); 3593 if ( *av && _substrcmp(*av, "set") == 0) { 3594 /* Do not allow using the following syntax: 3595 * ipfw set N delete set M 3596 */ 3597 if (g_co.use_set) 3598 errx(EX_DATAERR, "invalid syntax"); 3599 do_set = 1; /* delete set */ 3600 av++; 3601 } 3602 3603 /* Rule number */ 3604 while (*av && isdigit(**av)) { 3605 i = strtol(*av, &sep, 10); 3606 j = i; 3607 if (*sep== '-') 3608 j = strtol(sep + 1, NULL, 10); 3609 av++; 3610 if (g_co.do_nat) { 3611 exitval = ipfw_delete_nat(i); 3612 } else if (g_co.do_pipe) { 3613 exitval = ipfw_delete_pipe(g_co.do_pipe, i); 3614 } else { 3615 memset(&rt, 0, sizeof(rt)); 3616 if (do_set != 0) { 3617 rt.set = i & 31; 3618 rt.flags = IPFW_RCFLAG_SET; 3619 } else { 3620 rt.start_rule = i; 3621 rt.end_rule = j; 3622 if (rt.start_rule == 0 && rt.end_rule == 0) 3623 rt.flags |= IPFW_RCFLAG_ALL; 3624 else 3625 rt.flags |= IPFW_RCFLAG_RANGE; 3626 if (g_co.use_set != 0) { 3627 rt.set = g_co.use_set - 1; 3628 rt.flags |= IPFW_RCFLAG_SET; 3629 } 3630 } 3631 if (g_co.do_dynamic == 2) 3632 rt.flags |= IPFW_RCFLAG_DYNAMIC; 3633 i = do_range_cmd(IP_FW_XDEL, &rt); 3634 if (i != 0) { 3635 exitval = EX_UNAVAILABLE; 3636 if (g_co.do_quiet) 3637 continue; 3638 warn("rule %u: setsockopt(IP_FW_XDEL)", 3639 rt.start_rule); 3640 } else if (rt.new_set == 0 && do_set == 0 && 3641 g_co.do_dynamic != 2) { 3642 exitval = EX_UNAVAILABLE; 3643 if (g_co.do_quiet) 3644 continue; 3645 if (rt.start_rule != rt.end_rule) 3646 warnx("no rules in %u-%u range", 3647 rt.start_rule, rt.end_rule); 3648 else 3649 warnx("rule %u not found", 3650 rt.start_rule); 3651 } 3652 } 3653 } 3654 if (exitval != EX_OK && g_co.do_force == 0) 3655 exit(exitval); 3656 } 3657 3658 /* 3659 * fill the interface structure. We do not check the name as we can 3660 * create interfaces dynamically, so checking them at insert time 3661 * makes relatively little sense. 3662 * Interface names containing '*', '?', or '[' are assumed to be shell 3663 * patterns which match interfaces. 3664 */ 3665 static void 3666 fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate) 3667 { 3668 char *p; 3669 uint32_t uidx; 3670 3671 cmd->name[0] = '\0'; 3672 cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); 3673 3674 CHECK_CMDLEN; 3675 3676 /* Parse the interface or address */ 3677 if (strcmp(arg, "any") == 0) 3678 cmd->o.len = 0; /* effectively ignore this command */ 3679 else if (strncmp(arg, "table(", 6) == 0) { 3680 if ((p = strchr(arg + 6, ')')) == NULL) 3681 errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg); 3682 *p = '\0'; 3683 p = strchr(arg + 6, ','); 3684 if (p) 3685 *p++ = '\0'; 3686 if ((uidx = pack_table(tstate, arg + 6)) == 0) 3687 errx(EX_DATAERR, "Invalid table name: %s", arg + 6); 3688 3689 cmd->name[0] = '\1'; /* Special value indicating table */ 3690 cmd->p.kidx = uidx; 3691 } else if (!isdigit(*arg)) { 3692 strlcpy(cmd->name, arg, sizeof(cmd->name)); 3693 cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; 3694 } else if (!inet_aton(arg, &cmd->p.ip)) 3695 errx(EX_DATAERR, "bad ip address ``%s''", arg); 3696 } 3697 3698 static void 3699 get_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask) 3700 { 3701 int i; 3702 size_t l; 3703 char *ap, *ptr, *optr; 3704 struct ether_addr *mac; 3705 const char *macset = "0123456789abcdefABCDEF:"; 3706 3707 if (strcmp(p, "any") == 0) { 3708 for (i = 0; i < ETHER_ADDR_LEN; i++) 3709 addr[i] = mask[i] = 0; 3710 return; 3711 } 3712 3713 optr = ptr = strdup(p); 3714 if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) { 3715 l = strlen(ap); 3716 if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL) 3717 errx(EX_DATAERR, "Incorrect MAC address"); 3718 bcopy(mac, addr, ETHER_ADDR_LEN); 3719 } else 3720 errx(EX_DATAERR, "Incorrect MAC address"); 3721 3722 if (ptr != NULL) { /* we have mask? */ 3723 if (p[ptr - optr - 1] == '/') { /* mask len */ 3724 long ml = strtol(ptr, &ap, 10); 3725 if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0) 3726 errx(EX_DATAERR, "Incorrect mask length"); 3727 for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++) 3728 mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml); 3729 } else { /* mask */ 3730 l = strlen(ptr); 3731 if (strspn(ptr, macset) != l || 3732 (mac = ether_aton(ptr)) == NULL) 3733 errx(EX_DATAERR, "Incorrect mask"); 3734 bcopy(mac, mask, ETHER_ADDR_LEN); 3735 } 3736 } else { /* default mask: ff:ff:ff:ff:ff:ff */ 3737 for (i = 0; i < ETHER_ADDR_LEN; i++) 3738 mask[i] = 0xff; 3739 } 3740 for (i = 0; i < ETHER_ADDR_LEN; i++) 3741 addr[i] &= mask[i]; 3742 3743 free(optr); 3744 } 3745 3746 /* 3747 * helper function, updates the pointer to cmd with the length 3748 * of the current command, and also cleans up the first word of 3749 * the new command in case it has been clobbered before. 3750 */ 3751 static ipfw_insn * 3752 next_cmd(ipfw_insn *cmd, int *len) 3753 { 3754 *len -= F_LEN(cmd); 3755 CHECK_LENGTH(*len, 0); 3756 cmd += F_LEN(cmd); 3757 bzero(cmd, sizeof(*cmd)); 3758 return cmd; 3759 } 3760 3761 /* 3762 * Takes arguments and copies them into a comment 3763 */ 3764 static void 3765 fill_comment(ipfw_insn *cmd, char **av, int cblen) 3766 { 3767 int i, l; 3768 char *p = (char *)(cmd + 1); 3769 3770 cmd->opcode = O_NOP; 3771 cmd->len = (cmd->len & (F_NOT | F_OR)); 3772 3773 /* Compute length of comment string. */ 3774 for (i = 0, l = 0; av[i] != NULL; i++) 3775 l += strlen(av[i]) + 1; 3776 if (l == 0) 3777 return; 3778 if (l > 84) 3779 errx(EX_DATAERR, 3780 "comment too long (max 80 chars)"); 3781 l = 1 + (l+3)/4; 3782 cmd->len = (cmd->len & (F_NOT | F_OR)) | l; 3783 CHECK_CMDLEN; 3784 3785 for (i = 0; av[i] != NULL; i++) { 3786 strcpy(p, av[i]); 3787 p += strlen(av[i]); 3788 *p++ = ' '; 3789 } 3790 *(--p) = '\0'; 3791 } 3792 3793 /* 3794 * A function to fill simple commands of size 1. 3795 * Existing flags are preserved. 3796 */ 3797 static void 3798 fill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) 3799 { 3800 cmd->opcode = opcode; 3801 cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; 3802 cmd->arg1 = arg; 3803 } 3804 3805 /* 3806 * Fetch and add the MAC address and type, with masks. This generates one or 3807 * two microinstructions, and returns the pointer to the last one. 3808 */ 3809 static ipfw_insn * 3810 add_mac(ipfw_insn *cmd, char *av[], int cblen) 3811 { 3812 ipfw_insn_mac *mac; 3813 3814 if ( ( av[0] == NULL ) || ( av[1] == NULL ) ) 3815 errx(EX_DATAERR, "MAC dst src"); 3816 3817 cmd->opcode = O_MACADDR2; 3818 cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); 3819 CHECK_CMDLEN; 3820 3821 mac = (ipfw_insn_mac *)cmd; 3822 get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ 3823 get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]), 3824 &(mac->mask[ETHER_ADDR_LEN])); /* src */ 3825 return cmd; 3826 } 3827 3828 static ipfw_insn * 3829 add_mactype(ipfw_insn *cmd, char *av, int cblen) 3830 { 3831 if (!av) 3832 errx(EX_DATAERR, "missing MAC type"); 3833 if (strcmp(av, "any") != 0) { /* we have a non-null type */ 3834 fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE, 3835 cblen); 3836 cmd->opcode = O_MAC_TYPE; 3837 return cmd; 3838 } else 3839 return NULL; 3840 } 3841 3842 static ipfw_insn * 3843 add_proto0(ipfw_insn *cmd, char *av, u_char *protop) 3844 { 3845 struct protoent *pe; 3846 char *ep; 3847 int proto; 3848 3849 proto = strtol(av, &ep, 10); 3850 if (*ep != '\0' || proto <= 0) { 3851 if ((pe = getprotobyname(av)) == NULL) 3852 return NULL; 3853 proto = pe->p_proto; 3854 } 3855 3856 fill_cmd(cmd, O_PROTO, 0, proto); 3857 *protop = proto; 3858 return cmd; 3859 } 3860 3861 static ipfw_insn * 3862 add_proto(ipfw_insn *cmd, char *av, u_char *protop) 3863 { 3864 u_char proto = IPPROTO_IP; 3865 3866 if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) 3867 ; /* do not set O_IP4 nor O_IP6 */ 3868 else if (strcmp(av, "ip4") == 0) 3869 /* explicit "just IPv4" rule */ 3870 fill_cmd(cmd, O_IP4, 0, 0); 3871 else if (strcmp(av, "ip6") == 0) { 3872 /* explicit "just IPv6" rule */ 3873 proto = IPPROTO_IPV6; 3874 fill_cmd(cmd, O_IP6, 0, 0); 3875 } else 3876 return add_proto0(cmd, av, protop); 3877 3878 *protop = proto; 3879 return cmd; 3880 } 3881 3882 static ipfw_insn * 3883 add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop) 3884 { 3885 u_char proto = IPPROTO_IP; 3886 3887 if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) 3888 ; /* do not set O_IP4 nor O_IP6 */ 3889 else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0) 3890 /* explicit "just IPv4" rule */ 3891 fill_cmd(cmd, O_IP4, 0, 0); 3892 else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) { 3893 /* explicit "just IPv6" rule */ 3894 proto = IPPROTO_IPV6; 3895 fill_cmd(cmd, O_IP6, 0, 0); 3896 } else 3897 return add_proto0(cmd, av, protop); 3898 3899 *protop = proto; 3900 return cmd; 3901 } 3902 3903 static ipfw_insn * 3904 add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) 3905 { 3906 fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); 3907 if (cmd->opcode == O_IP_DST_SET) /* set */ 3908 cmd->opcode = O_IP_SRC_SET; 3909 else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 3910 cmd->opcode = O_IP_SRC_LOOKUP; 3911 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 3912 cmd->opcode = O_IP_SRC_ME; 3913 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 3914 cmd->opcode = O_IP_SRC; 3915 else /* addr/mask */ 3916 cmd->opcode = O_IP_SRC_MASK; 3917 return cmd; 3918 } 3919 3920 static ipfw_insn * 3921 add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) 3922 { 3923 fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); 3924 if (cmd->opcode == O_IP_DST_SET) /* set */ 3925 ; 3926 else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 3927 ; 3928 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 3929 cmd->opcode = O_IP_DST_ME; 3930 else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 3931 cmd->opcode = O_IP_DST; 3932 else /* addr/mask */ 3933 cmd->opcode = O_IP_DST_MASK; 3934 return cmd; 3935 } 3936 3937 static ipfw_insn * 3938 add_srcmac(ipfw_insn *cmd, char *av, struct tidx *tstate) 3939 { 3940 3941 if (strncmp(av, "table(", 6) == 0) 3942 fill_table(cmd, av, O_MAC_SRC_LOOKUP, tstate); 3943 else 3944 errx(EX_DATAERR, "only mac table lookup is supported %s", av); 3945 return cmd; 3946 } 3947 3948 static ipfw_insn * 3949 add_dstmac(ipfw_insn *cmd, char *av, struct tidx *tstate) 3950 { 3951 3952 if (strncmp(av, "table(", 6) == 0) 3953 fill_table(cmd, av, O_MAC_DST_LOOKUP, tstate); 3954 else 3955 errx(EX_DATAERR, "only mac table lookup is supported %s", av); 3956 return cmd; 3957 } 3958 3959 3960 static struct _s_x f_reserved_keywords[] = { 3961 { "altq", TOK_OR }, 3962 { "//", TOK_OR }, 3963 { "diverted", TOK_OR }, 3964 { "dst-port", TOK_OR }, 3965 { "src-port", TOK_OR }, 3966 { "established", TOK_OR }, 3967 { "keep-state", TOK_OR }, 3968 { "frag", TOK_OR }, 3969 { "icmptypes", TOK_OR }, 3970 { "in", TOK_OR }, 3971 { "out", TOK_OR }, 3972 { "ip6", TOK_OR }, 3973 { "any", TOK_OR }, 3974 { "to", TOK_OR }, 3975 { "via", TOK_OR }, 3976 { "{", TOK_OR }, 3977 { "lookup", TOK_OR }, 3978 { "tagged", TOK_OR }, 3979 { NULL, 0 } /* terminator */ 3980 }; 3981 3982 static ipfw_insn * 3983 add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen) 3984 { 3985 3986 if (match_token(f_reserved_keywords, av) != -1) 3987 return (NULL); 3988 3989 if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) { 3990 /* XXX todo: check that we have a protocol with ports */ 3991 cmd->opcode = opcode; 3992 return cmd; 3993 } 3994 return NULL; 3995 } 3996 3997 static ipfw_insn * 3998 add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) 3999 { 4000 struct in6_addr a; 4001 char *host, *ch, buf[INET6_ADDRSTRLEN]; 4002 ipfw_insn *ret = NULL; 4003 size_t len; 4004 4005 /* Copy first address in set if needed */ 4006 if ((ch = strpbrk(av, "/,")) != NULL) { 4007 len = ch - av; 4008 strlcpy(buf, av, sizeof(buf)); 4009 if (len < sizeof(buf)) 4010 buf[len] = '\0'; 4011 host = buf; 4012 } else 4013 host = av; 4014 4015 if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || 4016 inet_pton(AF_INET6, host, &a) == 1) 4017 ret = add_srcip6(cmd, av, cblen, tstate); 4018 /* XXX: should check for IPv4, not !IPv6 */ 4019 if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || 4020 inet_pton(AF_INET6, host, &a) != 1)) 4021 ret = add_srcip(cmd, av, cblen, tstate); 4022 if (ret == NULL && strcmp(av, "any") != 0) 4023 ret = cmd; 4024 4025 return ret; 4026 } 4027 4028 static ipfw_insn * 4029 add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) 4030 { 4031 struct in6_addr a; 4032 char *host, *ch, buf[INET6_ADDRSTRLEN]; 4033 ipfw_insn *ret = NULL; 4034 size_t len; 4035 4036 /* Copy first address in set if needed */ 4037 if ((ch = strpbrk(av, "/,")) != NULL) { 4038 len = ch - av; 4039 strlcpy(buf, av, sizeof(buf)); 4040 if (len < sizeof(buf)) 4041 buf[len] = '\0'; 4042 host = buf; 4043 } else 4044 host = av; 4045 4046 if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || 4047 inet_pton(AF_INET6, host, &a) == 1) 4048 ret = add_dstip6(cmd, av, cblen, tstate); 4049 /* XXX: should check for IPv4, not !IPv6 */ 4050 if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || 4051 inet_pton(AF_INET6, host, &a) != 1)) 4052 ret = add_dstip(cmd, av, cblen, tstate); 4053 if (ret == NULL && strcmp(av, "any") != 0) 4054 ret = cmd; 4055 4056 return ret; 4057 } 4058 4059 static uint16_t 4060 parse_logdst(char *logdst_iter) 4061 { 4062 char *token; 4063 uint16_t ret; 4064 4065 ret = IPFW_LOG_DEFAULT; 4066 while ((token = strsep(&logdst_iter, ",")) != NULL) { 4067 if (_substrcmp(token, "syslog") == 0) { 4068 ret |= IPFW_LOG_SYSLOG; 4069 continue; 4070 } 4071 if (_substrcmp(token, "ipfw0") == 0) { 4072 /* XXX add multiple ipfw* */ 4073 ret |= IPFW_LOG_IPFW0; 4074 continue; 4075 } 4076 if (_substrcmp(token, "rtsock") == 0) { 4077 ret |= IPFW_LOG_RTSOCK; 4078 continue; 4079 } 4080 errx(EX_DATAERR, 4081 "unsupported logdst token"); 4082 } 4083 return (ret); 4084 } 4085 4086 static inline uint32_t 4087 arg_or_targ_relaxed(const char *arg, const char *action, uint32_t maxarg) 4088 { 4089 uint32_t arg1 = (uint32_t)(-1); 4090 4091 if (arg == NULL) 4092 errx(EX_USAGE, "missing argument for %s", action); 4093 if (isdigit(arg[0])) { 4094 arg1 = strtoul(arg, NULL, 10); 4095 if (arg1 < IPFW_ARG_MIN || arg1 > maxarg) 4096 errx(EX_DATAERR, "illegal argument %s(%u) for %s", 4097 arg, arg1, action); 4098 } else if (_substrcmp(arg, "tablearg") == 0) 4099 arg1 = IP_FW_TARG; 4100 return (arg1); 4101 } 4102 4103 static inline uint32_t 4104 arg_or_targ(const char *arg, const char *action) 4105 { 4106 uint32_t arg1 = arg_or_targ_relaxed(arg, action, IPFW_ARG_MAX); 4107 4108 if (arg1 == (uint32_t)(-1)) 4109 errx(EX_DATAERR, "illegal argument %s(%u) for %s", 4110 arg, arg1, action); 4111 return (arg1); 4112 } 4113 4114 static uint16_t 4115 get_divert_port(const char *arg, const char *action) 4116 { 4117 uint32_t arg1 = arg_or_targ_relaxed(arg, action, IPFW_ARG_MAX); 4118 4119 if (arg1 != (uint32_t)(-1)) 4120 return (arg1); 4121 4122 struct servent *s; 4123 setservent(1); 4124 s = getservbyname(arg, "divert"); 4125 if (s == NULL) 4126 errx(EX_DATAERR, "illegal divert/tee port"); 4127 return (ntohs(s->s_port)); 4128 } 4129 4130 /* 4131 * Parse arguments and assemble the microinstructions which make up a rule. 4132 * Rules are added into the 'rulebuf' and then copied in the correct order 4133 * into the actual rule. 4134 * 4135 * The syntax for a rule starts with the action, followed by 4136 * optional action parameters, and the various match patterns. 4137 * In the assembled microcode, the first opcode must be an O_PROBE_STATE 4138 * (generated if the rule includes a keep-state option), then the 4139 * various match patterns, log/altq actions, and the actual action. 4140 * 4141 */ 4142 static void 4143 compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) 4144 { 4145 /* 4146 * rules are added into the 'rulebuf' and then copied in 4147 * the correct order into the actual rule. 4148 * Some things that need to go out of order (prob, action etc.) 4149 * go into actbuf[]. 4150 */ 4151 static uint32_t actbuf[255], cmdbuf[255]; 4152 int rblen, ablen, cblen; 4153 4154 ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; 4155 ipfw_insn *first_cmd; /* first match pattern */ 4156 4157 struct ip_fw_rule *rule; 4158 4159 /* 4160 * various flags used to record that we entered some fields. 4161 */ 4162 ipfw_insn *have_state = NULL; /* any state-related option */ 4163 int have_rstate = 0; 4164 ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL; 4165 ipfw_insn *have_skipcmd = NULL; 4166 size_t len; 4167 4168 int i; 4169 4170 int open_par = 0; /* open parenthesis ( */ 4171 4172 /* proto is here because it is used to fetch ports */ 4173 u_char proto = IPPROTO_IP; /* default protocol */ 4174 4175 double match_prob = 1; /* match probability, default is always match */ 4176 4177 bzero(actbuf, sizeof(actbuf)); /* actions go here */ 4178 bzero(cmdbuf, sizeof(cmdbuf)); 4179 bzero(rbuf, *rbufsize); 4180 4181 rule = (struct ip_fw_rule *)rbuf; 4182 cmd = (ipfw_insn *)cmdbuf; 4183 action = (ipfw_insn *)actbuf; 4184 4185 rblen = *rbufsize / sizeof(uint32_t); 4186 rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t); 4187 ablen = nitems(actbuf); 4188 cblen = nitems(cmdbuf); 4189 cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1; 4190 4191 #define CHECK_RBUFLEN(len) { CHECK_LENGTH(rblen, len); rblen -= len; } 4192 #define CHECK_ACTLEN CHECK_LENGTH(ablen, action->len) 4193 4194 av++; 4195 4196 /* [rule N] -- Rule number optional */ 4197 if (av[0] && isdigit(**av)) { 4198 rule->rulenum = atoi(*av); 4199 av++; 4200 } 4201 4202 /* [set N] -- set number (0..RESVD_SET), optional */ 4203 if (av[0] && av[1] && _substrcmp(*av, "set") == 0) { 4204 int set = strtoul(av[1], NULL, 10); 4205 if (set < 0 || set > RESVD_SET) 4206 errx(EX_DATAERR, "illegal set %s", av[1]); 4207 rule->set = set; 4208 tstate->set = set; 4209 av += 2; 4210 } 4211 4212 /* [prob D] -- match probability, optional */ 4213 if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) { 4214 match_prob = strtod(av[1], NULL); 4215 4216 if (match_prob <= 0 || match_prob > 1) 4217 errx(EX_DATAERR, "illegal match prob. %s", av[1]); 4218 av += 2; 4219 } 4220 4221 /* action -- mandatory */ 4222 NEED1("missing action"); 4223 i = match_token(rule_actions, *av); 4224 av++; 4225 action->len = 1; /* default */ 4226 CHECK_ACTLEN; 4227 switch(i) { 4228 case TOK_CHECKSTATE: 4229 have_state = action; 4230 action->opcode = O_CHECK_STATE; 4231 action->len = F_INSN_SIZE(ipfw_insn_kidx); 4232 CHECK_ACTLEN; 4233 if (*av == NULL || 4234 match_token(rule_options, *av) == TOK_COMMENT) { 4235 insntod(have_state, kidx)->kidx = pack_object(tstate, 4236 default_state_name, IPFW_TLV_STATE_NAME); 4237 break; 4238 } 4239 if (*av[0] == ':') { 4240 if (strcmp(*av + 1, "any") == 0) 4241 insntod(have_state, kidx)->kidx = 0; 4242 else if (state_check_name(*av + 1) == 0) 4243 insntod(have_state, kidx)->kidx = pack_object( 4244 tstate, *av + 1, IPFW_TLV_STATE_NAME); 4245 else 4246 errx(EX_DATAERR, "Invalid state name %s", 4247 *av); 4248 av++; 4249 break; 4250 } 4251 errx(EX_DATAERR, "Invalid state name %s", *av); 4252 break; 4253 4254 case TOK_ABORT: 4255 action->opcode = O_REJECT; 4256 action->arg1 = ICMP_REJECT_ABORT; 4257 break; 4258 4259 case TOK_ABORT6: 4260 action->opcode = O_UNREACH6; 4261 action->arg1 = ICMP6_UNREACH_ABORT; 4262 break; 4263 4264 case TOK_ACCEPT: 4265 action->opcode = O_ACCEPT; 4266 break; 4267 4268 case TOK_DENY: 4269 action->opcode = O_DENY; 4270 action->arg1 = 0; 4271 break; 4272 4273 case TOK_REJECT: 4274 action->opcode = O_REJECT; 4275 action->arg1 = ICMP_UNREACH_HOST; 4276 break; 4277 4278 case TOK_RESET: 4279 action->opcode = O_REJECT; 4280 action->arg1 = ICMP_REJECT_RST; 4281 break; 4282 4283 case TOK_RESET6: 4284 action->opcode = O_UNREACH6; 4285 action->arg1 = ICMP6_UNREACH_RST; 4286 break; 4287 4288 case TOK_UNREACH: 4289 action->opcode = O_REJECT; 4290 NEED1("missing reject code"); 4291 action->arg1 = get_reject_code(*av); 4292 av++; 4293 if (action->arg1 == ICMP_UNREACH_NEEDFRAG && isdigit(**av)) { 4294 uint16_t mtu; 4295 4296 mtu = strtoul(*av, NULL, 10); 4297 if (mtu < 68 || mtu >= IP_MAXPACKET) 4298 errx(EX_DATAERR, "illegal argument for %s", 4299 *(av - 1)); 4300 action->len = F_INSN_SIZE(ipfw_insn_u16); 4301 insntod(action, u16)->ports[0] = mtu; 4302 av++; 4303 } 4304 break; 4305 4306 case TOK_UNREACH6: 4307 action->opcode = O_UNREACH6; 4308 NEED1("missing unreach code"); 4309 action->arg1 = get_unreach6_code(*av); 4310 av++; 4311 break; 4312 4313 case TOK_COUNT: 4314 action->opcode = O_COUNT; 4315 break; 4316 4317 case TOK_NAT: 4318 action->opcode = O_NAT; 4319 action->len = F_INSN_SIZE(ipfw_insn_nat); 4320 CHECK_ACTLEN; 4321 if (*av != NULL && _substrcmp(*av, "global") == 0) 4322 action->arg1 = IP_FW_NAT44_GLOBAL; 4323 else 4324 action->arg1 = arg_or_targ(av[0], *(av - 1)); 4325 av++; 4326 break; 4327 case TOK_QUEUE: 4328 action->opcode = O_QUEUE; 4329 action->arg1 = arg_or_targ(av[0], *(av - 1)); 4330 av++; 4331 break; 4332 case TOK_PIPE: 4333 action->opcode = O_PIPE; 4334 action->arg1 = arg_or_targ(av[0], *(av - 1)); 4335 av++; 4336 break; 4337 case TOK_SKIPTO: 4338 action->opcode = O_SKIPTO; 4339 action->len = F_INSN_SIZE(ipfw_insn_u32); 4340 CHECK_ACTLEN; 4341 insntod(action, u32)->d[0] = 4342 arg_or_targ_relaxed(av[0], *(av - 1), IPFW_DEFAULT_RULE); 4343 av++; 4344 break; 4345 case TOK_NETGRAPH: 4346 action->opcode = O_NETGRAPH; 4347 action->arg1 = arg_or_targ(av[0], *(av - 1)); 4348 av++; 4349 break; 4350 case TOK_NGTEE: 4351 action->opcode = O_NGTEE; 4352 action->arg1 = arg_or_targ(av[0], *(av - 1)); 4353 av++; 4354 break; 4355 case TOK_DIVERT: 4356 action->opcode = O_DIVERT; 4357 action->arg1 = get_divert_port(av[0], *(av - 1)); 4358 av++; 4359 break; 4360 case TOK_TEE: 4361 action->opcode = O_TEE; 4362 action->arg1 = get_divert_port(av[0], *(av - 1)); 4363 av++; 4364 break; 4365 case TOK_CALL: 4366 action->opcode = O_CALLRETURN; 4367 action->len = F_INSN_SIZE(ipfw_insn_u32); 4368 CHECK_ACTLEN; 4369 insntod(action, u32)->d[0] = 4370 arg_or_targ_relaxed(av[0], *(av - 1), IPFW_DEFAULT_RULE); 4371 av++; 4372 break; 4373 4374 case TOK_FORWARD: { 4375 /* 4376 * Locate the address-port separator (':' or ','). 4377 * Could be one of the following: 4378 * hostname:port 4379 * IPv4 a.b.c.d,port 4380 * IPv4 a.b.c.d:port 4381 * IPv6 w:x:y::z,port 4382 * IPv6 [w:x:y::z]:port 4383 */ 4384 struct sockaddr_storage result; 4385 struct addrinfo *res; 4386 char *s, *end; 4387 int family; 4388 u_short port_number = 0; 4389 4390 NEED1("missing forward address[:port]"); 4391 4392 if (strncmp(*av, "tablearg", 8) == 0 && 4393 ((*av)[8] == '\0' || (*av)[8] == ',' || (*av)[8] == ':')) 4394 memcpy(++(*av), "0.0.0.0", 7); 4395 4396 /* 4397 * Are we an bracket-enclosed IPv6 address? 4398 */ 4399 if (strchr(*av, '[')) 4400 (*av)++; 4401 4402 /* 4403 * locate the address-port separator (':' or ',') 4404 */ 4405 s = strchr(*av, ','); 4406 if (s == NULL) { 4407 s = strchr(*av, ']'); 4408 /* Prevent erroneous parsing on brackets. */ 4409 if (s != NULL) 4410 *(s++) = '\0'; 4411 else 4412 s = *av; 4413 4414 /* Distinguish between IPv4:port and IPv6 cases. */ 4415 s = strchr(s, ':'); 4416 if (s && strchr(s+1, ':')) 4417 s = NULL; /* no port */ 4418 } 4419 4420 if (s != NULL) { 4421 /* Terminate host portion and set s to start of port. */ 4422 *(s++) = '\0'; 4423 i = strtoport(s, &end, 0 /* base */, 0 /* proto */); 4424 if (s == end) 4425 errx(EX_DATAERR, 4426 "illegal forwarding port ``%s''", s); 4427 port_number = (u_short)i; 4428 } 4429 4430 /* 4431 * Resolve the host name or address to a family and a 4432 * network representation of the address. 4433 */ 4434 if (getaddrinfo(*av, NULL, NULL, &res)) 4435 errx(EX_DATAERR, NULL); 4436 /* Just use the first host in the answer. */ 4437 family = res->ai_family; 4438 memcpy(&result, res->ai_addr, res->ai_addrlen); 4439 freeaddrinfo(res); 4440 4441 if (family == PF_INET) { 4442 ipfw_insn_sa *p = (ipfw_insn_sa *)action; 4443 4444 action->opcode = O_FORWARD_IP; 4445 action->len = F_INSN_SIZE(ipfw_insn_sa); 4446 CHECK_ACTLEN; 4447 4448 /* 4449 * In the kernel we assume AF_INET and use only 4450 * sin_port and sin_addr. Remember to set sin_len as 4451 * the routing code seems to use it too. 4452 */ 4453 p->sa.sin_len = sizeof(struct sockaddr_in); 4454 p->sa.sin_family = AF_INET; 4455 p->sa.sin_port = port_number; 4456 p->sa.sin_addr.s_addr = 4457 ((struct sockaddr_in *)&result)->sin_addr.s_addr; 4458 } else if (family == PF_INET6) { 4459 ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action; 4460 4461 action->opcode = O_FORWARD_IP6; 4462 action->len = F_INSN_SIZE(ipfw_insn_sa6); 4463 CHECK_ACTLEN; 4464 4465 p->sa.sin6_len = sizeof(struct sockaddr_in6); 4466 p->sa.sin6_family = AF_INET6; 4467 p->sa.sin6_port = port_number; 4468 p->sa.sin6_flowinfo = 0; 4469 p->sa.sin6_scope_id = 4470 ((struct sockaddr_in6 *)&result)->sin6_scope_id; 4471 bcopy(&((struct sockaddr_in6*)&result)->sin6_addr, 4472 &p->sa.sin6_addr, sizeof(p->sa.sin6_addr)); 4473 } else { 4474 errx(EX_DATAERR, "Invalid address family in forward action"); 4475 } 4476 av++; 4477 break; 4478 } 4479 case TOK_COMMENT: 4480 /* pretend it is a 'count' rule followed by the comment */ 4481 action->opcode = O_COUNT; 4482 av--; /* go back... */ 4483 break; 4484 4485 case TOK_SETFIB: 4486 { 4487 int numfibs; 4488 size_t intsize = sizeof(int); 4489 4490 action->opcode = O_SETFIB; 4491 NEED1("missing fib number"); 4492 if (_substrcmp(*av, "tablearg") == 0) { 4493 action->arg1 = IP_FW_TARG; 4494 } else { 4495 action->arg1 = strtoul(*av, NULL, 10); 4496 if (sysctlbyname("net.fibs", &numfibs, &intsize, 4497 NULL, 0) == -1) 4498 errx(EX_DATAERR, "fibs not supported.\n"); 4499 if (action->arg1 >= numfibs) /* Temporary */ 4500 errx(EX_DATAERR, "fib too large.\n"); 4501 /* Add high-order bit to fib to make room for tablearg*/ 4502 action->arg1 |= 0x8000; 4503 } 4504 av++; 4505 break; 4506 } 4507 4508 case TOK_SETDSCP: 4509 { 4510 int code; 4511 4512 action->opcode = O_SETDSCP; 4513 NEED1("missing DSCP code"); 4514 if (_substrcmp(*av, "tablearg") == 0) { 4515 action->arg1 = IP_FW_TARG; 4516 } else { 4517 if (isalpha(*av[0])) { 4518 if ((code = match_token(f_ipdscp, *av)) == -1) 4519 errx(EX_DATAERR, "Unknown DSCP code"); 4520 action->arg1 = code; 4521 } else 4522 action->arg1 = strtoul(*av, NULL, 10); 4523 /* 4524 * Add high-order bit to DSCP to make room 4525 * for tablearg 4526 */ 4527 action->arg1 |= 0x8000; 4528 } 4529 av++; 4530 break; 4531 } 4532 4533 case TOK_REASS: 4534 action->opcode = O_REASS; 4535 break; 4536 4537 case TOK_RETURN: 4538 action->opcode = O_CALLRETURN; 4539 action->len = F_INSN_SIZE(ipfw_insn_u32) | F_NOT; 4540 CHECK_ACTLEN; 4541 if (*av != NULL) { 4542 /* 4543 * Return type is optional. 4544 * By default we use RETURN_NEXT_RULENUM. 4545 */ 4546 i = match_token(return_types, *av); 4547 if (i >= 0) { 4548 action->arg1 = i; 4549 av++; 4550 } else 4551 action->arg1 = RETURN_NEXT_RULENUM; 4552 } 4553 break; 4554 4555 case TOK_SETMARK: { 4556 action->opcode = O_SETMARK; 4557 action->len = F_INSN_SIZE(ipfw_insn_u32); 4558 NEED1("missing mark"); 4559 if (strcmp(*av, "tablearg") == 0) { 4560 action->arg1 = IP_FW_TARG; 4561 } else { 4562 insntod(action, u32)->d[0] = strtoul(*av, NULL, 0); 4563 /* This is not a tablearg */ 4564 action->arg1 |= 0x8000; 4565 } 4566 av++; 4567 CHECK_CMDLEN; 4568 break; 4569 } 4570 4571 case TOK_TCPSETMSS: { 4572 u_long mss; 4573 uint32_t idx; 4574 4575 idx = pack_object(tstate, "tcp-setmss", IPFW_TLV_EACTION); 4576 if (idx == 0) 4577 errx(EX_DATAERR, "pack_object failed"); 4578 action->opcode = O_EXTERNAL_ACTION; 4579 action->len = F_INSN_SIZE(ipfw_insn_kidx); 4580 CHECK_ACTLEN; 4581 insntod(action, kidx)->kidx = idx; 4582 4583 NEED1("Missing MSS value"); 4584 action = next_cmd(action, &ablen); 4585 action->len = 1; 4586 CHECK_ACTLEN; 4587 mss = strtoul(*av, NULL, 10); 4588 if (mss == 0 || mss > UINT16_MAX) 4589 errx(EX_USAGE, "invalid MSS value %s", *av); 4590 fill_cmd(action, O_EXTERNAL_DATA, 0, (uint16_t)mss); 4591 av++; 4592 break; 4593 } 4594 4595 default: 4596 av--; 4597 if (match_token(rule_eactions, *av) == -1) 4598 errx(EX_DATAERR, "invalid action %s\n", *av); 4599 /* 4600 * External actions support. 4601 * XXX: we support only syntax with instance name. 4602 * For known external actions (from rule_eactions list) 4603 * we can handle syntax directly. But with `eaction' 4604 * keyword we can use only `eaction <name> <instance>' 4605 * syntax. 4606 */ 4607 case TOK_EACTION: { 4608 uint32_t idx; 4609 4610 NEED1("Missing eaction name"); 4611 if (eaction_check_name(*av) != 0) 4612 errx(EX_DATAERR, "Invalid eaction name %s", *av); 4613 idx = pack_object(tstate, *av, IPFW_TLV_EACTION); 4614 if (idx == 0) 4615 errx(EX_DATAERR, "pack_object failed"); 4616 action->opcode = O_EXTERNAL_ACTION; 4617 action->len = F_INSN_SIZE(ipfw_insn_kidx); 4618 CHECK_ACTLEN; 4619 insntod(action, kidx)->kidx = idx; 4620 4621 av++; 4622 NEED1("Missing eaction instance name"); 4623 if (eaction_check_name(*av) != 0) 4624 errx(EX_DATAERR, "Invalid eaction instance name %s", 4625 *av); 4626 /* 4627 * External action instance object has TLV type depended 4628 * from the external action name object index. Since we 4629 * currently don't know this index, use zero as TLV type. 4630 */ 4631 idx = pack_object(tstate, *av, 0); 4632 if (idx == 0) 4633 errx(EX_DATAERR, "pack_object failed"); 4634 action = next_cmd(action, &ablen); 4635 action->opcode = O_EXTERNAL_INSTANCE; 4636 action->len = F_INSN_SIZE(ipfw_insn_kidx); 4637 CHECK_ACTLEN; 4638 insntod(action, kidx)->kidx = idx; 4639 av++; 4640 } 4641 } 4642 action = next_cmd(action, &ablen); 4643 4644 /* 4645 * [altq queuename] -- altq tag, optional 4646 * [log [logamount N]] -- log, optional 4647 * 4648 * If they exist, it go first in the cmdbuf, but then it is 4649 * skipped in the copy section to the end of the buffer. 4650 */ 4651 while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) { 4652 av++; 4653 switch (i) { 4654 case TOK_LOG: 4655 { 4656 ipfw_insn_log *c = (ipfw_insn_log *)cmd; 4657 int l; 4658 4659 if (have_log) 4660 errx(EX_DATAERR, 4661 "log cannot be specified more than once"); 4662 have_log = (ipfw_insn *)c; 4663 cmd->len = F_INSN_SIZE(ipfw_insn_log); 4664 CHECK_CMDLEN; 4665 cmd->opcode = O_LOG; 4666 cmd->arg1 = IPFW_LOG_DEFAULT; 4667 /* logdst before logamount */ 4668 if (av[0] && _substrcmp(*av, "logdst") == 0) { 4669 av++; 4670 NEED1("logdst requires argument"); 4671 cmd->arg1 = parse_logdst(*av); 4672 av++; 4673 } 4674 4675 if (av[0] && _substrcmp(*av, "logamount") == 0) { 4676 av++; 4677 NEED1("logamount requires argument"); 4678 l = atoi(*av); 4679 if (l < 0) 4680 errx(EX_DATAERR, 4681 "logamount must be positive"); 4682 c->max_log = l; 4683 av++; 4684 } else { 4685 len = sizeof(c->max_log); 4686 if (sysctlbyname("net.inet.ip.fw.verbose_limit", 4687 &c->max_log, &len, NULL, 0) == -1) { 4688 if (g_co.test_only) { 4689 c->max_log = 0; 4690 break; 4691 } 4692 errx(1, "sysctlbyname(\"%s\")", 4693 "net.inet.ip.fw.verbose_limit"); 4694 } 4695 } 4696 4697 /* logdst after logamount */ 4698 if (av[0] && _substrcmp(*av, "logdst") == 0) { 4699 av++; 4700 NEED1("logdst requires argument"); 4701 cmd->arg1 = parse_logdst(*av); 4702 av++; 4703 } 4704 } 4705 break; 4706 4707 #ifndef NO_ALTQ 4708 case TOK_ALTQ: 4709 { 4710 ipfw_insn_altq *a = (ipfw_insn_altq *)cmd; 4711 4712 NEED1("missing altq queue name"); 4713 if (have_altq) 4714 errx(EX_DATAERR, 4715 "altq cannot be specified more than once"); 4716 have_altq = (ipfw_insn *)a; 4717 cmd->len = F_INSN_SIZE(ipfw_insn_altq); 4718 CHECK_CMDLEN; 4719 cmd->opcode = O_ALTQ; 4720 a->qid = altq_name_to_qid(*av); 4721 av++; 4722 } 4723 break; 4724 #endif 4725 4726 case TOK_TAG: 4727 case TOK_UNTAG: { 4728 uint16_t tag; 4729 4730 if (have_tag) 4731 errx(EX_USAGE, "tag and untag cannot be " 4732 "specified more than once"); 4733 GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i, 4734 rule_action_params); 4735 have_tag = cmd; 4736 fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag); 4737 av++; 4738 break; 4739 } 4740 4741 default: 4742 abort(); 4743 } 4744 cmd = next_cmd(cmd, &cblen); 4745 } 4746 4747 if (have_state) { /* must be a check-state, we are done */ 4748 if (*av != NULL && 4749 match_token(rule_options, *av) == TOK_COMMENT) { 4750 /* check-state has a comment */ 4751 av++; 4752 fill_comment(cmd, av, cblen); 4753 cmd = next_cmd(cmd, &cblen); 4754 av[0] = NULL; 4755 } 4756 goto done; 4757 } 4758 4759 #define OR_START(target) \ 4760 if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \ 4761 if (open_par) \ 4762 errx(EX_USAGE, "nested \"(\" not allowed\n"); \ 4763 prev = NULL; \ 4764 open_par = 1; \ 4765 if ( (av[0])[1] == '\0') { \ 4766 av++; \ 4767 } else \ 4768 (*av)++; \ 4769 } \ 4770 target: \ 4771 4772 4773 #define CLOSE_PAR \ 4774 if (open_par) { \ 4775 if (av[0] && ( \ 4776 strcmp(*av, ")") == 0 || \ 4777 strcmp(*av, "}") == 0)) { \ 4778 prev = NULL; \ 4779 open_par = 0; \ 4780 av++; \ 4781 } else \ 4782 errx(EX_USAGE, "missing \")\"\n"); \ 4783 } 4784 4785 #define NOT_BLOCK \ 4786 if (av[0] && _substrcmp(*av, "not") == 0) { \ 4787 if (cmd->len & F_NOT) \ 4788 errx(EX_USAGE, "double \"not\" not allowed\n"); \ 4789 cmd->len |= F_NOT; \ 4790 av++; \ 4791 } 4792 4793 #define OR_BLOCK(target) \ 4794 if (av[0] && _substrcmp(*av, "or") == 0) { \ 4795 if (prev == NULL || open_par == 0) \ 4796 errx(EX_DATAERR, "invalid OR block"); \ 4797 prev->len |= F_OR; \ 4798 av++; \ 4799 goto target; \ 4800 } \ 4801 CLOSE_PAR; 4802 4803 first_cmd = cmd; 4804 4805 #if 0 4806 /* 4807 * MAC addresses, optional. 4808 * If we have this, we skip the part "proto from src to dst" 4809 * and jump straight to the option parsing. 4810 */ 4811 NOT_BLOCK; 4812 NEED1("missing protocol"); 4813 if (_substrcmp(*av, "MAC") == 0 || 4814 _substrcmp(*av, "mac") == 0) { 4815 av++; /* the "MAC" keyword */ 4816 add_mac(cmd, av); /* exits in case of errors */ 4817 cmd = next_cmd(cmd); 4818 av += 2; /* dst-mac and src-mac */ 4819 NOT_BLOCK; 4820 NEED1("missing mac type"); 4821 if (add_mactype(cmd, av[0])) 4822 cmd = next_cmd(cmd); 4823 av++; /* any or mac-type */ 4824 goto read_options; 4825 } 4826 #endif 4827 4828 /* 4829 * protocol, mandatory 4830 */ 4831 OR_START(get_proto); 4832 NOT_BLOCK; 4833 NEED1("missing protocol"); 4834 if (add_proto_compat(cmd, *av, &proto)) { 4835 av++; 4836 if (F_LEN(cmd) != 0) { 4837 prev = cmd; 4838 cmd = next_cmd(cmd, &cblen); 4839 } 4840 } else if (first_cmd != cmd) { 4841 errx(EX_DATAERR, "invalid protocol ``%s''", *av); 4842 } else { 4843 rule->flags |= IPFW_RULE_JUSTOPTS; 4844 goto read_options; 4845 } 4846 OR_BLOCK(get_proto); 4847 4848 first_cmd = cmd; /* update pointer to use in compact form */ 4849 4850 /* 4851 * "from", mandatory 4852 */ 4853 if ((av[0] == NULL) || _substrcmp(*av, "from") != 0) 4854 errx(EX_USAGE, "missing ``from''"); 4855 av++; 4856 4857 /* 4858 * source IP, mandatory 4859 */ 4860 OR_START(source_ip); 4861 NOT_BLOCK; /* optional "not" */ 4862 NEED1("missing source address"); 4863 if (add_src(cmd, *av, proto, cblen, tstate)) { 4864 av++; 4865 if (F_LEN(cmd) != 0) { /* ! any */ 4866 prev = cmd; 4867 cmd = next_cmd(cmd, &cblen); 4868 } 4869 } else 4870 errx(EX_USAGE, "bad source address %s", *av); 4871 OR_BLOCK(source_ip); 4872 4873 /* 4874 * source ports, optional 4875 */ 4876 NOT_BLOCK; /* optional "not" */ 4877 if ( av[0] != NULL ) { 4878 if (_substrcmp(*av, "any") == 0 || 4879 add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { 4880 av++; 4881 if (F_LEN(cmd) != 0) 4882 cmd = next_cmd(cmd, &cblen); 4883 } 4884 } 4885 4886 /* 4887 * "to", mandatory 4888 */ 4889 if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 ) 4890 errx(EX_USAGE, "missing ``to''"); 4891 av++; 4892 4893 /* 4894 * destination, mandatory 4895 */ 4896 OR_START(dest_ip); 4897 NOT_BLOCK; /* optional "not" */ 4898 NEED1("missing dst address"); 4899 if (add_dst(cmd, *av, proto, cblen, tstate)) { 4900 av++; 4901 if (F_LEN(cmd) != 0) { /* ! any */ 4902 prev = cmd; 4903 cmd = next_cmd(cmd, &cblen); 4904 } 4905 } else 4906 errx( EX_USAGE, "bad destination address %s", *av); 4907 OR_BLOCK(dest_ip); 4908 4909 /* 4910 * dest. ports, optional 4911 */ 4912 NOT_BLOCK; /* optional "not" */ 4913 if (av[0]) { 4914 if (_substrcmp(*av, "any") == 0 || 4915 add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { 4916 av++; 4917 if (F_LEN(cmd) != 0) 4918 cmd = next_cmd(cmd, &cblen); 4919 } 4920 } 4921 if (first_cmd == cmd) 4922 rule->flags |= IPFW_RULE_NOOPT; 4923 4924 read_options: 4925 prev = NULL; 4926 while ( av[0] != NULL ) { 4927 char *s; 4928 ipfw_insn_u32 *cmd32; /* alias for cmd */ 4929 4930 s = *av; 4931 cmd32 = (ipfw_insn_u32 *)cmd; 4932 4933 if (*s == '!') { /* alternate syntax for NOT */ 4934 if (cmd->len & F_NOT) 4935 errx(EX_USAGE, "double \"not\" not allowed\n"); 4936 cmd->len = F_NOT; 4937 s++; 4938 } 4939 i = match_token(rule_options, s); 4940 av++; 4941 switch(i) { 4942 case TOK_NOT: 4943 if (cmd->len & F_NOT) 4944 errx(EX_USAGE, "double \"not\" not allowed\n"); 4945 cmd->len = F_NOT; 4946 break; 4947 4948 case TOK_OR: 4949 if (open_par == 0 || prev == NULL) 4950 errx(EX_USAGE, "invalid \"or\" block\n"); 4951 prev->len |= F_OR; 4952 break; 4953 4954 case TOK_STARTBRACE: 4955 if (open_par) 4956 errx(EX_USAGE, "+nested \"(\" not allowed\n"); 4957 open_par = 1; 4958 break; 4959 4960 case TOK_ENDBRACE: 4961 if (!open_par) 4962 errx(EX_USAGE, "+missing \")\"\n"); 4963 open_par = 0; 4964 prev = NULL; 4965 break; 4966 4967 case TOK_IN: 4968 fill_cmd(cmd, O_IN, 0, 0); 4969 break; 4970 4971 case TOK_OUT: 4972 cmd->len ^= F_NOT; /* toggle F_NOT */ 4973 fill_cmd(cmd, O_IN, 0, 0); 4974 break; 4975 4976 case TOK_DIVERTED: 4977 fill_cmd(cmd, O_DIVERTED, 0, 3); 4978 break; 4979 4980 case TOK_DIVERTEDLOOPBACK: 4981 fill_cmd(cmd, O_DIVERTED, 0, 1); 4982 break; 4983 4984 case TOK_DIVERTEDOUTPUT: 4985 fill_cmd(cmd, O_DIVERTED, 0, 2); 4986 break; 4987 4988 case TOK_FRAG: { 4989 uint32_t set = 0, clear = 0; 4990 4991 if (*av != NULL && fill_flags(f_ipoff, *av, NULL, 4992 &set, &clear) == 0) 4993 av++; 4994 else { 4995 /* 4996 * Compatibility: no argument after "frag" 4997 * keyword equals to "frag offset". 4998 */ 4999 set = 0x01; 5000 clear = 0; 5001 } 5002 fill_cmd(cmd, O_FRAG, 0, 5003 (set & 0xff) | ( (clear & 0xff) << 8)); 5004 break; 5005 } 5006 5007 case TOK_LAYER2: 5008 fill_cmd(cmd, O_LAYER2, 0, 0); 5009 break; 5010 5011 case TOK_XMIT: 5012 case TOK_RECV: 5013 case TOK_VIA: 5014 NEED1("recv, xmit, via require interface name" 5015 " or address"); 5016 fill_iface(insntod(cmd, if), av[0], cblen, tstate); 5017 av++; 5018 if (F_LEN(cmd) == 0) /* not a valid address */ 5019 break; 5020 if (i == TOK_XMIT) 5021 cmd->opcode = O_XMIT; 5022 else if (i == TOK_RECV) 5023 cmd->opcode = O_RECV; 5024 else if (i == TOK_VIA) 5025 cmd->opcode = O_VIA; 5026 break; 5027 5028 case TOK_ICMPTYPES: 5029 NEED1("icmptypes requires list of types"); 5030 fill_icmptypes((ipfw_insn_u32 *)cmd, *av); 5031 av++; 5032 break; 5033 5034 case TOK_ICMP6TYPES: 5035 NEED1("icmptypes requires list of types"); 5036 fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av, cblen); 5037 av++; 5038 break; 5039 5040 case TOK_IPTTL: 5041 NEED1("ipttl requires TTL"); 5042 if (strpbrk(*av, "-,")) { 5043 if (!add_ports(cmd, *av, 0, O_IPTTL, cblen)) 5044 errx(EX_DATAERR, "invalid ipttl %s", *av); 5045 } else 5046 fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); 5047 av++; 5048 break; 5049 5050 case TOK_IPID: 5051 NEED1("ipid requires id"); 5052 if (strpbrk(*av, "-,")) { 5053 if (!add_ports(cmd, *av, 0, O_IPID, cblen)) 5054 errx(EX_DATAERR, "invalid ipid %s", *av); 5055 } else 5056 fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); 5057 av++; 5058 break; 5059 5060 case TOK_IPLEN: 5061 NEED1("iplen requires length"); 5062 if (strpbrk(*av, "-,")) { 5063 if (!add_ports(cmd, *av, 0, O_IPLEN, cblen)) 5064 errx(EX_DATAERR, "invalid ip len %s", *av); 5065 } else 5066 fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); 5067 av++; 5068 break; 5069 5070 case TOK_IPVER: 5071 NEED1("ipver requires version"); 5072 fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); 5073 av++; 5074 break; 5075 5076 case TOK_IPPRECEDENCE: 5077 NEED1("ipprecedence requires value"); 5078 fill_cmd(cmd, O_IPPRECEDENCE, 0, 5079 (strtoul(*av, NULL, 0) & 7) << 5); 5080 av++; 5081 break; 5082 5083 case TOK_DSCP: 5084 NEED1("missing DSCP code"); 5085 fill_dscp(cmd, *av, cblen); 5086 av++; 5087 break; 5088 5089 case TOK_IPOPTS: 5090 NEED1("missing argument for ipoptions"); 5091 fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av); 5092 av++; 5093 break; 5094 5095 case TOK_IPTOS: 5096 NEED1("missing argument for iptos"); 5097 fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av); 5098 av++; 5099 break; 5100 5101 case TOK_UID: 5102 NEED1("uid requires argument"); 5103 { 5104 char *end; 5105 uid_t uid; 5106 struct passwd *pwd; 5107 5108 cmd->opcode = O_UID; 5109 uid = strtoul(*av, &end, 0); 5110 pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); 5111 if (pwd == NULL) 5112 errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); 5113 cmd32->d[0] = pwd->pw_uid; 5114 cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 5115 av++; 5116 } 5117 break; 5118 5119 case TOK_GID: 5120 NEED1("gid requires argument"); 5121 { 5122 char *end; 5123 gid_t gid; 5124 struct group *grp; 5125 5126 cmd->opcode = O_GID; 5127 gid = strtoul(*av, &end, 0); 5128 grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); 5129 if (grp == NULL) 5130 errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); 5131 cmd32->d[0] = grp->gr_gid; 5132 cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 5133 av++; 5134 } 5135 break; 5136 5137 case TOK_JAIL: 5138 NEED1("jail requires argument"); 5139 { 5140 char *end; 5141 int jid; 5142 5143 cmd->opcode = O_JAIL; 5144 /* 5145 * If av is a number, then we'll just pass it as-is. If 5146 * it's a name, try to resolve that to a jid. 5147 * 5148 * We save the jail_getid(3) call for a fallback because 5149 * it entails an unconditional trip to the kernel to 5150 * either validate a jid or resolve a name to a jid. 5151 * This specific token doesn't currently require a 5152 * jid to be an active jail, so we save a transition 5153 * by simply using a number that we're given. 5154 */ 5155 jid = strtoul(*av, &end, 10); 5156 if (*end != '\0') { 5157 jid = jail_getid(*av); 5158 if (jid < 0) 5159 errx(EX_DATAERR, "%s", jail_errmsg); 5160 } 5161 cmd32->d[0] = (uint32_t)jid; 5162 cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 5163 av++; 5164 } 5165 break; 5166 5167 case TOK_ESTAB: 5168 fill_cmd(cmd, O_ESTAB, 0, 0); 5169 break; 5170 5171 case TOK_SETUP: 5172 fill_cmd(cmd, O_TCPFLAGS, 0, 5173 (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); 5174 break; 5175 5176 case TOK_TCPDATALEN: 5177 NEED1("tcpdatalen requires length"); 5178 if (strpbrk(*av, "-,")) { 5179 if (!add_ports(cmd, *av, 0, O_TCPDATALEN, cblen)) 5180 errx(EX_DATAERR, "invalid tcpdata len %s", *av); 5181 } else 5182 fill_cmd(cmd, O_TCPDATALEN, 0, 5183 strtoul(*av, NULL, 0)); 5184 av++; 5185 break; 5186 5187 case TOK_TCPOPTS: 5188 NEED1("missing argument for tcpoptions"); 5189 fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av); 5190 av++; 5191 break; 5192 5193 case TOK_TCPSEQ: 5194 case TOK_TCPACK: 5195 NEED1("tcpseq/tcpack requires argument"); 5196 cmd->len = F_INSN_SIZE(ipfw_insn_u32); 5197 cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; 5198 cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); 5199 av++; 5200 break; 5201 5202 case TOK_TCPMSS: 5203 case TOK_TCPWIN: 5204 NEED1("tcpmss/tcpwin requires size"); 5205 if (strpbrk(*av, "-,")) { 5206 if (add_ports(cmd, *av, 0, 5207 i == TOK_TCPWIN ? O_TCPWIN : O_TCPMSS, 5208 cblen) == NULL) 5209 errx(EX_DATAERR, "invalid %s size %s", 5210 s, *av); 5211 } else 5212 fill_cmd(cmd, i == TOK_TCPWIN ? O_TCPWIN : 5213 O_TCPMSS, 0, strtoul(*av, NULL, 0)); 5214 av++; 5215 break; 5216 5217 case TOK_TCPFLAGS: 5218 NEED1("missing argument for tcpflags"); 5219 cmd->opcode = O_TCPFLAGS; 5220 fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av); 5221 av++; 5222 break; 5223 5224 case TOK_KEEPSTATE: 5225 case TOK_RECORDSTATE: { 5226 uint32_t uidx; 5227 5228 if (open_par) 5229 errx(EX_USAGE, "keep-state or record-state cannot be part " 5230 "of an or block"); 5231 if (have_state) 5232 errx(EX_USAGE, "only one of keep-state, record-state, " 5233 " limit and set-limit is allowed"); 5234 if (*av != NULL && *av[0] == ':') { 5235 if (state_check_name(*av + 1) != 0) 5236 errx(EX_DATAERR, 5237 "Invalid state name %s", *av); 5238 uidx = pack_object(tstate, *av + 1, 5239 IPFW_TLV_STATE_NAME); 5240 av++; 5241 } else 5242 uidx = pack_object(tstate, default_state_name, 5243 IPFW_TLV_STATE_NAME); 5244 have_state = cmd; 5245 have_rstate = i == TOK_RECORDSTATE; 5246 cmd->opcode = O_KEEP_STATE; 5247 cmd->len = F_INSN_SIZE(ipfw_insn_kidx); 5248 CHECK_CMDLEN; 5249 insntod(have_state, kidx)->kidx = uidx; 5250 break; 5251 } 5252 5253 case TOK_LIMIT: 5254 case TOK_SETLIMIT: { 5255 ipfw_insn_limit *c = insntod(cmd, limit); 5256 int val; 5257 5258 if (open_par) 5259 errx(EX_USAGE, 5260 "limit or set-limit cannot be part of an or block"); 5261 if (have_state) 5262 errx(EX_USAGE, "only one of keep-state, record-state, " 5263 " limit and set-limit is allowed"); 5264 have_state = cmd; 5265 have_rstate = i == TOK_SETLIMIT; 5266 5267 cmd->opcode = O_LIMIT; 5268 cmd->len = F_INSN_SIZE(ipfw_insn_limit); 5269 CHECK_CMDLEN; 5270 c->limit_mask = c->conn_limit = c->kidx = 0; 5271 5272 while ( av[0] != NULL ) { 5273 if ((val = match_token(limit_masks, *av)) <= 0) 5274 break; 5275 c->limit_mask |= val; 5276 av++; 5277 } 5278 5279 if (c->limit_mask == 0) 5280 errx(EX_USAGE, "limit: missing limit mask"); 5281 5282 GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX, 5283 TOK_LIMIT, rule_options); 5284 av++; 5285 5286 if (*av != NULL && *av[0] == ':') { 5287 if (state_check_name(*av + 1) != 0) 5288 errx(EX_DATAERR, 5289 "Invalid state name %s", *av); 5290 c->kidx = pack_object(tstate, *av + 1, 5291 IPFW_TLV_STATE_NAME); 5292 av++; 5293 } else 5294 c->kidx = pack_object(tstate, 5295 default_state_name, IPFW_TLV_STATE_NAME); 5296 break; 5297 } 5298 5299 case TOK_PROTO: 5300 NEED1("missing protocol"); 5301 if (add_proto(cmd, *av, &proto)) { 5302 av++; 5303 } else 5304 errx(EX_DATAERR, "invalid protocol ``%s''", 5305 *av); 5306 break; 5307 5308 case TOK_SRCIP: 5309 NEED1("missing source IP"); 5310 if (add_srcip(cmd, *av, cblen, tstate)) { 5311 av++; 5312 } 5313 break; 5314 5315 case TOK_DSTIP: 5316 NEED1("missing destination IP"); 5317 if (add_dstip(cmd, *av, cblen, tstate)) { 5318 av++; 5319 } 5320 break; 5321 5322 case TOK_SRCIP6: 5323 NEED1("missing source IP6"); 5324 if (add_srcip6(cmd, *av, cblen, tstate)) { 5325 av++; 5326 } 5327 break; 5328 5329 case TOK_DSTIP6: 5330 NEED1("missing destination IP6"); 5331 if (add_dstip6(cmd, *av, cblen, tstate)) { 5332 av++; 5333 } 5334 break; 5335 5336 5337 case TOK_SRCMAC: 5338 NEED1("missing source MAC"); 5339 if (add_srcmac(cmd, *av, tstate)) { 5340 av++; 5341 } 5342 break; 5343 5344 case TOK_DSTMAC: 5345 NEED1("missing destination MAC"); 5346 if (add_dstmac(cmd, *av, tstate)) { 5347 av++; 5348 } 5349 break; 5350 5351 case TOK_SRCPORT: 5352 NEED1("missing source port"); 5353 if (_substrcmp(*av, "any") == 0 || 5354 add_ports(cmd, *av, proto, O_IP_SRCPORT, cblen)) { 5355 av++; 5356 } else 5357 errx(EX_DATAERR, "invalid source port %s", *av); 5358 break; 5359 5360 case TOK_DSTPORT: 5361 NEED1("missing destination port"); 5362 if (_substrcmp(*av, "any") == 0 || 5363 add_ports(cmd, *av, proto, O_IP_DSTPORT, cblen)) { 5364 av++; 5365 } else 5366 errx(EX_DATAERR, "invalid destination port %s", 5367 *av); 5368 break; 5369 5370 case TOK_MAC: 5371 if (add_mac(cmd, av, cblen)) 5372 av += 2; 5373 break; 5374 5375 case TOK_MACTYPE: 5376 NEED1("missing mac type"); 5377 if (!add_mactype(cmd, *av, cblen)) 5378 errx(EX_DATAERR, "invalid mac type %s", *av); 5379 av++; 5380 break; 5381 5382 case TOK_VERREVPATH: 5383 fill_cmd(cmd, O_VERREVPATH, 0, 0); 5384 break; 5385 5386 case TOK_VERSRCREACH: 5387 fill_cmd(cmd, O_VERSRCREACH, 0, 0); 5388 break; 5389 5390 case TOK_ANTISPOOF: 5391 fill_cmd(cmd, O_ANTISPOOF, 0, 0); 5392 break; 5393 5394 case TOK_IPSEC: 5395 fill_cmd(cmd, O_IPSEC, 0, 0); 5396 break; 5397 5398 case TOK_IPV6: 5399 fill_cmd(cmd, O_IP6, 0, 0); 5400 break; 5401 5402 case TOK_IPV4: 5403 fill_cmd(cmd, O_IP4, 0, 0); 5404 break; 5405 5406 case TOK_EXT6HDR: 5407 NEED1("missing extension header"); 5408 fill_ext6hdr( cmd, *av ); 5409 av++; 5410 break; 5411 5412 case TOK_FLOWID: 5413 if (proto != IPPROTO_IPV6 ) 5414 errx( EX_USAGE, "flow-id filter is active " 5415 "only for ipv6 protocol\n"); 5416 fill_flow6( (ipfw_insn_u32 *) cmd, *av, cblen); 5417 av++; 5418 break; 5419 5420 case TOK_COMMENT: 5421 fill_comment(cmd, av, cblen); 5422 av[0]=NULL; 5423 break; 5424 5425 case TOK_TAGGED: 5426 if (av[0] && strpbrk(*av, "-,")) { 5427 if (!add_ports(cmd, *av, 0, O_TAGGED, cblen)) 5428 errx(EX_DATAERR, "tagged: invalid tag" 5429 " list: %s", *av); 5430 } 5431 else { 5432 uint16_t tag; 5433 5434 GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, 5435 TOK_TAGGED, rule_options); 5436 fill_cmd(cmd, O_TAGGED, 0, tag); 5437 } 5438 av++; 5439 break; 5440 5441 case TOK_FIB: 5442 NEED1("fib requires fib number"); 5443 fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0)); 5444 av++; 5445 break; 5446 case TOK_SOCKARG: 5447 fill_cmd(cmd, O_SOCKARG, 0, 0); 5448 break; 5449 5450 case TOK_LOOKUP: { 5451 /* optional mask for some LOOKUP types */ 5452 ipfw_insn_table *c = insntod(cmd, table); 5453 char *lkey; 5454 5455 if (!av[0] || !av[1]) 5456 errx(EX_USAGE, 5457 "format: lookup argument tablenum"); 5458 cmd->opcode = O_IP_DST_LOOKUP; 5459 5460 lkey = strsep(av, ":"); 5461 i = match_token(lookup_keys, lkey); 5462 if (i == -1) 5463 errx(EX_USAGE, 5464 "format: cannot lookup on %s", lkey); 5465 /* masked lookup key */ 5466 if (*av != NULL) { 5467 switch (i) { 5468 case LOOKUP_DST_PORT: 5469 case LOOKUP_SRC_PORT: 5470 case LOOKUP_UID: 5471 case LOOKUP_JAIL: 5472 case LOOKUP_DSCP: 5473 case LOOKUP_MARK: 5474 case LOOKUP_RULENUM: 5475 break; 5476 default: 5477 errx(EX_USAGE, 5478 "masked lookup is not supported " 5479 "for %s", lkey); 5480 } 5481 cmd->len |= F_INSN_SIZE(ipfw_insn_table); 5482 c->value = strtoul(*av, NULL, 0); 5483 if (c->value == 0) 5484 errx(EX_USAGE, 5485 "all-zeroes bitmask for lookup " 5486 "is meaningless"); 5487 } else { 5488 cmd->len |= F_INSN_SIZE(ipfw_insn_kidx); 5489 } 5490 CHECK_CMDLEN; 5491 5492 IPFW_SET_LOOKUP_TYPE(cmd, i); 5493 av++; 5494 c->kidx = pack_table(tstate, *av); 5495 if (c->kidx == 0) 5496 errx(EX_DATAERR, 5497 "Invalid table name: %s", *av); 5498 av++; 5499 } 5500 break; 5501 case TOK_FLOW: 5502 NEED1("missing table name"); 5503 if (strncmp(*av, "table(", 6) != 0) 5504 errx(EX_DATAERR, 5505 "enclose table name into \"table()\""); 5506 fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate); 5507 av++; 5508 break; 5509 5510 case TOK_SKIPACTION: 5511 if (have_skipcmd) 5512 errx(EX_USAGE, "only one defer-action " 5513 "is allowed"); 5514 have_skipcmd = cmd; 5515 fill_cmd(cmd, O_SKIP_ACTION, 0, 0); 5516 break; 5517 5518 case TOK_MARK: 5519 NEED1("missing mark value:mask"); 5520 fill_mark(cmd, *av, cblen); 5521 av++; 5522 break; 5523 5524 default: 5525 errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); 5526 } 5527 if (F_LEN(cmd) > 0) { /* prepare to advance */ 5528 prev = cmd; 5529 cmd = next_cmd(cmd, &cblen); 5530 } 5531 } 5532 5533 done: 5534 5535 if (!have_state && have_skipcmd) 5536 warnx("Rule contains \"defer-immediate-action\" " 5537 "and doesn't contain any state-related options."); 5538 5539 /* 5540 * Now copy stuff into the rule. 5541 * If we have a keep-state option, the first instruction 5542 * must be a PROBE_STATE (which is generated here). 5543 * If we have a LOG option, it was stored as the first command, 5544 * and now must be moved to the top of the action part. 5545 */ 5546 dst = (ipfw_insn *)rule->cmd; 5547 5548 /* 5549 * First thing to write into the command stream is the match probability. 5550 */ 5551 if (match_prob != 1) { /* 1 means always match */ 5552 dst->opcode = O_PROB; 5553 dst->len = 2; 5554 *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); 5555 dst += dst->len; 5556 } 5557 5558 /* 5559 * generate O_PROBE_STATE if necessary 5560 */ 5561 if (have_state && have_state->opcode != O_CHECK_STATE && !have_rstate) { 5562 dst->opcode = O_PROBE_STATE; 5563 dst->len = F_INSN_SIZE(ipfw_insn_kidx); 5564 insntod(dst, kidx)->kidx = insntod(have_state, kidx)->kidx; 5565 dst = next_cmd(dst, &rblen); 5566 } 5567 5568 /* 5569 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG, 5570 * O_SKIP_ACTION 5571 */ 5572 for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { 5573 i = F_LEN(src); 5574 CHECK_RBUFLEN(i); 5575 5576 switch (src->opcode) { 5577 case O_LOG: 5578 case O_KEEP_STATE: 5579 case O_LIMIT: 5580 case O_ALTQ: 5581 case O_TAG: 5582 case O_SKIP_ACTION: 5583 break; 5584 default: 5585 bcopy(src, dst, i * sizeof(uint32_t)); 5586 dst += i; 5587 } 5588 } 5589 5590 /* 5591 * put back the have_state command as last opcode 5592 */ 5593 if (have_state && have_state->opcode != O_CHECK_STATE) { 5594 i = F_LEN(have_state); 5595 CHECK_RBUFLEN(i); 5596 bcopy(have_state, dst, i * sizeof(uint32_t)); 5597 dst += i; 5598 } 5599 5600 /* 5601 * put back the have_skipcmd command as very last opcode 5602 */ 5603 if (have_skipcmd) { 5604 i = F_LEN(have_skipcmd); 5605 CHECK_RBUFLEN(i); 5606 bcopy(have_skipcmd, dst, i * sizeof(uint32_t)); 5607 dst += i; 5608 } 5609 5610 /* 5611 * start action section 5612 */ 5613 rule->act_ofs = dst - rule->cmd; 5614 5615 /* put back O_LOG, O_ALTQ, O_TAG if necessary */ 5616 if (have_log) { 5617 i = F_LEN(have_log); 5618 CHECK_RBUFLEN(i); 5619 bcopy(have_log, dst, i * sizeof(uint32_t)); 5620 dst += i; 5621 } 5622 if (have_altq) { 5623 i = F_LEN(have_altq); 5624 CHECK_RBUFLEN(i); 5625 bcopy(have_altq, dst, i * sizeof(uint32_t)); 5626 dst += i; 5627 } 5628 if (have_tag) { 5629 i = F_LEN(have_tag); 5630 CHECK_RBUFLEN(i); 5631 bcopy(have_tag, dst, i * sizeof(uint32_t)); 5632 dst += i; 5633 } 5634 5635 /* 5636 * copy all other actions 5637 */ 5638 for (src = (ipfw_insn *)actbuf; src != action; src += i) { 5639 i = F_LEN(src); 5640 CHECK_RBUFLEN(i); 5641 bcopy(src, dst, i * sizeof(uint32_t)); 5642 dst += i; 5643 } 5644 5645 rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); 5646 *rbufsize = (char *)dst - (char *)rule; 5647 } 5648 5649 static int 5650 compare_ntlv(const void *_a, const void *_b) 5651 { 5652 const ipfw_obj_ntlv *a, *b; 5653 5654 a = (const ipfw_obj_ntlv *)_a; 5655 b = (const ipfw_obj_ntlv *)_b; 5656 5657 if (a->set < b->set) 5658 return (-1); 5659 else if (a->set > b->set) 5660 return (1); 5661 5662 if (a->idx < b->idx) 5663 return (-1); 5664 else if (a->idx > b->idx) 5665 return (1); 5666 5667 if (a->head.type < b->head.type) 5668 return (-1); 5669 else if (a->head.type > b->head.type) 5670 return (1); 5671 5672 return (0); 5673 } 5674 5675 /* 5676 * Provide kernel with sorted list of referenced objects 5677 */ 5678 static void 5679 object_sort_ctlv(ipfw_obj_ctlv *ctlv) 5680 { 5681 5682 qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv); 5683 } 5684 5685 struct object_kt { 5686 uint32_t uidx; 5687 uint16_t type; 5688 }; 5689 static int 5690 compare_object_kntlv(const void *k, const void *v) 5691 { 5692 const ipfw_obj_ntlv *ntlv; 5693 struct object_kt key; 5694 5695 key = *((const struct object_kt *)k); 5696 ntlv = (const ipfw_obj_ntlv *)v; 5697 5698 if (key.uidx < ntlv->idx) 5699 return (-1); 5700 else if (key.uidx > ntlv->idx) 5701 return (1); 5702 5703 if (key.type < ntlv->head.type) 5704 return (-1); 5705 else if (key.type > ntlv->head.type) 5706 return (1); 5707 5708 return (0); 5709 } 5710 5711 /* 5712 * Finds object name in @ctlv by @idx and @type. 5713 * Uses the following facts: 5714 * 1) All TLVs are the same size 5715 * 2) Kernel implementation provides already sorted list. 5716 * 5717 * Returns table name or NULL. 5718 */ 5719 static char * 5720 object_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx, uint16_t type) 5721 { 5722 ipfw_obj_ntlv *ntlv; 5723 struct object_kt key; 5724 5725 key.uidx = idx; 5726 key.type = type; 5727 5728 ntlv = bsearch(&key, (ctlv + 1), ctlv->count, ctlv->objsize, 5729 compare_object_kntlv); 5730 5731 if (ntlv != NULL) 5732 return (ntlv->name); 5733 5734 return (NULL); 5735 } 5736 5737 static char * 5738 table_search_ctlv(ipfw_obj_ctlv *ctlv, uint32_t idx) 5739 { 5740 5741 return (object_search_ctlv(ctlv, idx, IPFW_TLV_TBL_NAME)); 5742 } 5743 5744 /* 5745 * Adds one or more rules to ipfw chain. 5746 * Data layout: 5747 * Request: 5748 * [ 5749 * ip_fw3_opheader 5750 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) 5751 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3) 5752 * ] 5753 * Reply: 5754 * [ 5755 * ip_fw3_opheader 5756 * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) 5757 * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] 5758 * ] 5759 * 5760 * Rules in reply are modified to store their actual ruleset number. 5761 * 5762 * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending 5763 * according to their idx field and there has to be no duplicates. 5764 * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. 5765 * (*3) Each ip_fw structure needs to be aligned to u64 boundary. 5766 */ 5767 void 5768 ipfw_add(char *av[]) 5769 { 5770 uint32_t rulebuf[1024]; 5771 int rbufsize, default_off, tlen, rlen; 5772 size_t sz; 5773 struct tidx ts; 5774 struct ip_fw_rule *rule; 5775 caddr_t tbuf; 5776 ip_fw3_opheader *op3; 5777 ipfw_obj_ctlv *ctlv, *tstate; 5778 5779 rbufsize = sizeof(rulebuf); 5780 memset(rulebuf, 0, rbufsize); 5781 memset(&ts, 0, sizeof(ts)); 5782 5783 /* Optimize case with no tables */ 5784 default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader); 5785 op3 = (ip_fw3_opheader *)rulebuf; 5786 ctlv = (ipfw_obj_ctlv *)(op3 + 1); 5787 rule = (struct ip_fw_rule *)(ctlv + 1); 5788 rbufsize -= default_off; 5789 5790 compile_rule(av, (uint32_t *)rule, &rbufsize, &ts); 5791 /* Align rule size to u64 boundary */ 5792 rlen = roundup2(rbufsize, sizeof(uint64_t)); 5793 5794 tbuf = NULL; 5795 sz = 0; 5796 tstate = NULL; 5797 if (ts.count != 0) { 5798 /* Some tables. We have to alloc more data */ 5799 tlen = ts.count * sizeof(ipfw_obj_ntlv); 5800 sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen; 5801 5802 if ((tbuf = calloc(1, sz)) == NULL) 5803 err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD"); 5804 op3 = (ip_fw3_opheader *)tbuf; 5805 /* Tables first */ 5806 ctlv = (ipfw_obj_ctlv *)(op3 + 1); 5807 ctlv->head.type = IPFW_TLV_TBLNAME_LIST; 5808 ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen; 5809 ctlv->count = ts.count; 5810 ctlv->objsize = sizeof(ipfw_obj_ntlv); 5811 memcpy(ctlv + 1, ts.idx, tlen); 5812 object_sort_ctlv(ctlv); 5813 tstate = ctlv; 5814 /* Rule next */ 5815 ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); 5816 ctlv->head.type = IPFW_TLV_RULE_LIST; 5817 ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; 5818 ctlv->count = 1; 5819 memcpy(ctlv + 1, rule, rbufsize); 5820 } else { 5821 /* Simply add header */ 5822 sz = rlen + default_off; 5823 memset(ctlv, 0, sizeof(*ctlv)); 5824 ctlv->head.type = IPFW_TLV_RULE_LIST; 5825 ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; 5826 ctlv->count = 1; 5827 } 5828 5829 if (do_get3(IP_FW_XADD, op3, &sz) != 0) 5830 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD"); 5831 5832 if (!g_co.do_quiet) { 5833 struct format_opts sfo; 5834 struct buf_pr bp; 5835 memset(&sfo, 0, sizeof(sfo)); 5836 sfo.tstate = tstate; 5837 sfo.set_mask = (uint32_t)(-1); 5838 bp_alloc(&bp, 4096); 5839 show_static_rule(&g_co, &sfo, &bp, rule, NULL); 5840 printf("%s", bp.buf); 5841 bp_free(&bp); 5842 } 5843 5844 if (tbuf != NULL) 5845 free(tbuf); 5846 5847 if (ts.idx != NULL) 5848 free(ts.idx); 5849 } 5850 5851 /* 5852 * clear the counters or the log counters. 5853 * optname has the following values: 5854 * 0 (zero both counters and logging) 5855 * 1 (zero logging only) 5856 */ 5857 void 5858 ipfw_zero(int ac, char *av[], int optname) 5859 { 5860 ipfw_range_tlv rt; 5861 char const *errstr; 5862 char const *name = optname ? "RESETLOG" : "ZERO"; 5863 uint32_t arg; 5864 int failed = EX_OK; 5865 5866 optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO; 5867 av++; ac--; 5868 5869 if (ac == 0) { 5870 /* clear all entries */ 5871 memset(&rt, 0, sizeof(rt)); 5872 rt.flags = IPFW_RCFLAG_ALL; 5873 if (do_range_cmd(optname, &rt) < 0) 5874 err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name); 5875 if (!g_co.do_quiet) 5876 printf("%s.\n", optname == IP_FW_XZERO ? 5877 "Accounting cleared":"Logging counts reset"); 5878 5879 return; 5880 } 5881 5882 while (ac) { 5883 /* Rule number */ 5884 if (isdigit(**av)) { 5885 arg = strtonum(*av, 0, 0xffff, &errstr); 5886 if (errstr) 5887 errx(EX_DATAERR, 5888 "invalid rule number %s\n", *av); 5889 memset(&rt, 0, sizeof(rt)); 5890 rt.start_rule = arg; 5891 rt.end_rule = arg; 5892 rt.flags |= IPFW_RCFLAG_RANGE; 5893 if (g_co.use_set != 0) { 5894 rt.set = g_co.use_set - 1; 5895 rt.flags |= IPFW_RCFLAG_SET; 5896 } 5897 if (do_range_cmd(optname, &rt) != 0) { 5898 warn("rule %u: setsockopt(IP_FW_X%s)", 5899 arg, name); 5900 failed = EX_UNAVAILABLE; 5901 } else if (rt.new_set == 0) { 5902 printf("Entry %d not found\n", arg); 5903 failed = EX_UNAVAILABLE; 5904 } else if (!g_co.do_quiet) 5905 printf("Entry %d %s.\n", arg, 5906 optname == IP_FW_XZERO ? 5907 "cleared" : "logging count reset"); 5908 } else { 5909 errx(EX_USAGE, "invalid rule number ``%s''", *av); 5910 } 5911 av++; ac--; 5912 } 5913 if (failed != EX_OK) 5914 exit(failed); 5915 } 5916 5917 void 5918 ipfw_flush(int force) 5919 { 5920 ipfw_range_tlv rt; 5921 5922 if (!force && !g_co.do_quiet) { /* need to ask user */ 5923 int c; 5924 5925 printf("Are you sure? [yn] "); 5926 fflush(stdout); 5927 do { 5928 c = toupper(getc(stdin)); 5929 while (c != '\n' && getc(stdin) != '\n') 5930 if (feof(stdin)) 5931 return; /* and do not flush */ 5932 } while (c != 'Y' && c != 'N'); 5933 printf("\n"); 5934 if (c == 'N') /* user said no */ 5935 return; 5936 } 5937 if (g_co.do_pipe) { 5938 dummynet_flush(); 5939 return; 5940 } 5941 /* `ipfw set N flush` - is the same that `ipfw delete set N` */ 5942 memset(&rt, 0, sizeof(rt)); 5943 if (g_co.use_set != 0) { 5944 rt.set = g_co.use_set - 1; 5945 rt.flags = IPFW_RCFLAG_SET; 5946 } else 5947 rt.flags = IPFW_RCFLAG_ALL; 5948 if (do_range_cmd(IP_FW_XDEL, &rt) != 0) 5949 err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)"); 5950 if (!g_co.do_quiet) 5951 printf("Flushed all %s.\n", g_co.do_pipe ? "pipes" : "rules"); 5952 } 5953 5954 static struct _s_x intcmds[] = { 5955 { "talist", TOK_TALIST }, 5956 { "iflist", TOK_IFLIST }, 5957 { "olist", TOK_OLIST }, 5958 { "vlist", TOK_VLIST }, 5959 { NULL, 0 } 5960 }; 5961 5962 static struct _s_x otypes[] = { 5963 { "EACTION", IPFW_TLV_EACTION }, 5964 { "DYNSTATE", IPFW_TLV_STATE_NAME }, 5965 { NULL, 0 } 5966 }; 5967 5968 static const char* 5969 lookup_eaction_name(ipfw_obj_ntlv *ntlv, int cnt, uint16_t type) 5970 { 5971 const char *name; 5972 int i; 5973 5974 name = NULL; 5975 for (i = 0; i < cnt; i++) { 5976 if (ntlv[i].head.type != IPFW_TLV_EACTION) 5977 continue; 5978 if (IPFW_TLV_EACTION_NAME(ntlv[i].idx) != type) 5979 continue; 5980 name = ntlv[i].name; 5981 break; 5982 } 5983 return (name); 5984 } 5985 5986 static void 5987 ipfw_list_objects(int ac __unused, char *av[] __unused) 5988 { 5989 ipfw_obj_lheader req, *olh; 5990 ipfw_obj_ntlv *ntlv; 5991 const char *name; 5992 size_t sz; 5993 uint32_t i; 5994 5995 memset(&req, 0, sizeof(req)); 5996 sz = sizeof(req); 5997 if (do_get3(IP_FW_DUMP_SRVOBJECTS, &req.opheader, &sz) != 0) 5998 if (errno != ENOMEM) 5999 return; 6000 6001 sz = req.size; 6002 if ((olh = calloc(1, sz)) == NULL) 6003 return; 6004 6005 olh->size = sz; 6006 if (do_get3(IP_FW_DUMP_SRVOBJECTS, &olh->opheader, &sz) != 0) { 6007 free(olh); 6008 return; 6009 } 6010 6011 if (olh->count > 0) 6012 printf("Objects list:\n"); 6013 else 6014 printf("There are no objects\n"); 6015 ntlv = (ipfw_obj_ntlv *)(olh + 1); 6016 for (i = 0; i < olh->count; i++) { 6017 name = match_value(otypes, ntlv->head.type); 6018 if (name == NULL) 6019 name = lookup_eaction_name( 6020 (ipfw_obj_ntlv *)(olh + 1), olh->count, 6021 ntlv->head.type); 6022 if (name == NULL) 6023 printf(" kidx: %4d\ttype: %10d\tname: %s\n", 6024 ntlv->idx, ntlv->head.type, ntlv->name); 6025 else 6026 printf(" kidx: %4d\ttype: %10s\tname: %s\n", 6027 ntlv->idx, name, ntlv->name); 6028 ntlv++; 6029 } 6030 free(olh); 6031 } 6032 6033 void 6034 ipfw_internal_handler(int ac, char *av[]) 6035 { 6036 int tcmd; 6037 6038 ac--; av++; 6039 NEED1("internal cmd required"); 6040 6041 if ((tcmd = match_token(intcmds, *av)) == -1) 6042 errx(EX_USAGE, "invalid internal sub-cmd: %s", *av); 6043 6044 switch (tcmd) { 6045 case TOK_IFLIST: 6046 ipfw_list_tifaces(); 6047 break; 6048 case TOK_TALIST: 6049 ipfw_list_ta(ac, av); 6050 break; 6051 case TOK_OLIST: 6052 ipfw_list_objects(ac, av); 6053 break; 6054 case TOK_VLIST: 6055 ipfw_list_values(ac, av); 6056 break; 6057 } 6058 } 6059 6060 static int 6061 ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh) 6062 { 6063 ipfw_obj_lheader req, *olh; 6064 size_t sz; 6065 6066 memset(&req, 0, sizeof(req)); 6067 sz = sizeof(req); 6068 6069 if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) { 6070 if (errno != ENOMEM) 6071 return (errno); 6072 } 6073 6074 sz = req.size; 6075 if ((olh = calloc(1, sz)) == NULL) 6076 return (ENOMEM); 6077 6078 olh->size = sz; 6079 if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) { 6080 free(olh); 6081 return (errno); 6082 } 6083 6084 *polh = olh; 6085 return (0); 6086 } 6087 6088 static int 6089 ifinfo_cmp(const void *a, const void *b) 6090 { 6091 const ipfw_iface_info *ia, *ib; 6092 6093 ia = (const ipfw_iface_info *)a; 6094 ib = (const ipfw_iface_info *)b; 6095 6096 return (stringnum_cmp(ia->ifname, ib->ifname)); 6097 } 6098 6099 /* 6100 * Retrieves table list from kernel, 6101 * optionally sorts it and calls requested function for each table. 6102 * Returns 0 on success. 6103 */ 6104 static void 6105 ipfw_list_tifaces(void) 6106 { 6107 ipfw_obj_lheader *olh = NULL; 6108 ipfw_iface_info *info; 6109 uint32_t i; 6110 int error; 6111 6112 if ((error = ipfw_get_tracked_ifaces(&olh)) != 0) 6113 err(EX_OSERR, "Unable to request ipfw tracked interface list"); 6114 6115 qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp); 6116 6117 info = (ipfw_iface_info *)(olh + 1); 6118 for (i = 0; i < olh->count; i++) { 6119 if (info->flags & IPFW_IFFLAG_RESOLVED) 6120 printf("%s ifindex: %d refcount: %u changes: %u\n", 6121 info->ifname, info->ifindex, info->refcnt, 6122 info->gencnt); 6123 else 6124 printf("%s ifindex: unresolved refcount: %u changes: %u\n", 6125 info->ifname, info->refcnt, info->gencnt); 6126 info = (ipfw_iface_info *)((caddr_t)info + olh->objsize); 6127 } 6128 6129 free(olh); 6130 } 6131