diff options
Diffstat (limited to 'sbin/ipfw/ipfw2.c')
-rw-r--r-- | sbin/ipfw/ipfw2.c | 2232 |
1 files changed, 1388 insertions, 844 deletions
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 25d6afd5febe..2c9846628977 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -35,6 +35,7 @@ #include <netdb.h> #include <pwd.h> #include <stdio.h> +#include <stdarg.h> #include <stdlib.h> #include <string.h> #include <sysexits.h> @@ -56,16 +57,22 @@ struct cmdline_opts co; /* global options */ +struct format_opts { + int bcwidth; + int pcwidth; + int show_counters; + uint32_t set_mask; /* enabled sets mask */ + uint32_t flags; /* request flags */ + uint32_t first; /* first rule to request */ + uint32_t last; /* last rule to request */ + uint32_t dcnt; /* number of dynamic states */ + ipfw_obj_ctlv *tstate; /* table state data */ +}; + int resvd_set_number = RESVD_SET; int ipfw_socket = -1; -uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */ - -#ifndef s6_addr32 -#define s6_addr32 __u6_addr.__u6_addr32 -#endif - #define CHECK_LENGTH(v, len) do { \ if ((v) < (len)) \ errx(EX_DATAERR, "Rule too long"); \ @@ -86,7 +93,7 @@ uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */ if (!av[0]) \ errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ if (_substrcmp(*av, "tablearg") == 0) { \ - arg = IP_FW_TABLEARG; \ + arg = IP_FW_TARG; \ break; \ } \ \ @@ -104,24 +111,13 @@ uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */ errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ match_value(s_x, tok), min, max, *av); \ \ - if (_xval == IP_FW_TABLEARG) \ + if (_xval == IP_FW_TARG) \ errx(EX_DATAERR, "%s: illegal argument value: %s", \ match_value(s_x, tok), *av); \ arg = _xval; \ } \ } while (0) -static void -PRINT_UINT_ARG(const char *str, uint32_t arg) -{ - if (str != NULL) - printf("%s",str); - if (arg == IP_FW_TABLEARG) - printf("tablearg"); - else - printf("%u", arg); -} - static struct _s_x f_tcpflags[] = { { "syn", TH_SYN }, { "fin", TH_FIN }, @@ -169,7 +165,7 @@ static struct _s_x f_iptos[] = { { NULL, 0 } }; -static struct _s_x f_ipdscp[] = { +struct _s_x f_ipdscp[] = { { "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */ { "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */ { "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */ @@ -357,6 +353,7 @@ static struct _s_x rule_options[] = { { "src-ipv6", TOK_SRCIP6}, { "src-ip6", TOK_SRCIP6}, { "lookup", TOK_LOOKUP}, + { "flow", TOK_FLOW}, { "//", TOK_COMMENT }, { "not", TOK_NOT }, /* pseudo option */ @@ -370,6 +367,103 @@ static struct _s_x rule_options[] = { { NULL, 0 } /* terminator */ }; +void bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg); +static int ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, + ipfw_cfg_lheader **pcfg, size_t *psize); +static int ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, + ipfw_cfg_lheader *cfg, size_t sz, int ac, char **av); +static void ipfw_list_tifaces(void); + +/* + * Simple string buffer API. + * Used to simplify buffer passing between function and for + * transparent overrun handling. + */ + +/* + * Allocates new buffer of given size @sz. + * + * Returns 0 on success. + */ +int +bp_alloc(struct buf_pr *b, size_t size) +{ + memset(b, 0, sizeof(struct buf_pr)); + + if ((b->buf = calloc(1, size)) == NULL) + return (ENOMEM); + + b->ptr = b->buf; + b->size = size; + b->avail = b->size; + + return (0); +} + +void +bp_free(struct buf_pr *b) +{ + + free(b->buf); +} + +/* + * Flushes buffer so new writer start from beginning. + */ +void +bp_flush(struct buf_pr *b) +{ + + b->ptr = b->buf; + b->avail = b->size; +} + +/* + * Print message specified by @format and args. + * Automatically manage buffer space and transparently handle + * buffer overruns. + * + * Returns number of bytes that should have been printed. + */ +int +bprintf(struct buf_pr *b, char *format, ...) +{ + va_list args; + int i; + + va_start(args, format); + + i = vsnprintf(b->ptr, b->avail, format, args); + va_end(args); + + if (i > b->avail || i < 0) { + /* Overflow or print error */ + b->avail = 0; + } else { + b->ptr += i; + b->avail -= i; + } + + b->needed += i; + + return (i); +} + +/* + * Special values printer for tablearg-aware opcodes. + */ +void +bprint_uint_arg(struct buf_pr *bp, const char *str, uint32_t arg) +{ + + if (str != NULL) + bprintf(bp, "%s", str); + if (arg == IP_FW_TARG) + bprintf(bp, "tablearg"); + else + bprintf(bp, "%u", arg); +} + /* * Helper routine to print a possibly unaligned uint64_t on * various platform. If width > 0, print the value with @@ -377,7 +471,7 @@ static struct _s_x rule_options[] = { * otherwise, return the required width. */ int -pr_u64(uint64_t *pd, int width) +pr_u64(struct buf_pr *b, uint64_t *pd, int width) { #ifdef TCC #define U64_FMT "I64" @@ -390,11 +484,12 @@ pr_u64(uint64_t *pd, int width) bcopy (pd, &u, sizeof(u)); d = u; return (width > 0) ? - printf("%*" U64_FMT " ", width, d) : + bprintf(b, "%*" U64_FMT " ", width, d) : snprintf(NULL, 0, "%" U64_FMT, d) ; #undef U64_FMT } + void * safe_calloc(size_t number, size_t size) { @@ -416,6 +511,26 @@ safe_realloc(void *ptr, size_t size) } /* + * Compare things like interface or table names. + */ +int +stringnum_cmp(const char *a, const char *b) +{ + int la, lb; + + la = strlen(a); + lb = strlen(b); + + if (la > lb) + return (1); + else if (la < lb) + return (-01); + + return (strcmp(a, b)); +} + + +/* * conditionally runs the command. * Selected options or negative -> getsockopt */ @@ -448,20 +563,18 @@ do_cmd(int optname, void *optval, uintptr_t optlen) } /* - * do_setcmd3 - pass ipfw control cmd to kernel + * do_set3 - pass ipfw control cmd to kernel * @optname: option name * @optval: pointer to option data * @optlen: option length * - * Function encapsulates option value in IP_FW3 socket option - * and calls setsockopt(). - * Function returns 0 on success or -1 otherwise. + * Assumes op3 header is already embedded. + * Calls setsockopt() with IP_FW3 as kernel-visible opcode. + * Returns 0 on success or errno otherwise. */ -static int -do_setcmd3(int optname, void *optval, socklen_t optlen) +int +do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen) { - socklen_t len; - ip_fw3_opheader *op3; if (co.test_only) return (0); @@ -471,14 +584,40 @@ do_setcmd3(int optname, void *optval, socklen_t optlen) if (ipfw_socket < 0) err(EX_UNAVAILABLE, "socket"); - len = sizeof(ip_fw3_opheader) + optlen; - op3 = alloca(len); - /* Zero reserved fields */ - memset(op3, 0, sizeof(ip_fw3_opheader)); - memcpy(op3 + 1, optval, optlen); op3->opcode = optname; - return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len); + return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen)); +} + +/* + * do_get3 - pass ipfw control cmd to kernel + * @optname: option name + * @optval: pointer to option data + * @optlen: pointer to option length + * + * Assumes op3 header is already embedded. + * Calls getsockopt() with IP_FW3 as kernel-visible opcode. + * Returns 0 on success or errno otherwise. + */ +int +do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen) +{ + int error; + + if (co.test_only) + return (0); + + if (ipfw_socket == -1) + ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (ipfw_socket < 0) + err(EX_UNAVAILABLE, "socket"); + + op3->opcode = optname; + + error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, + (socklen_t *)optlen); + + return (error); } /** @@ -494,7 +633,38 @@ match_token(struct _s_x *table, char *string) for (pt = table ; i && pt->s != NULL ; pt++) if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) return pt->x; - return -1; + return (-1); +} + +/** + * match_token takes a table and a string, returns the value associated + * with the string for the best match. + * + * Returns: + * value from @table for matched records + * -1 for non-matched records + * -2 if more than one records match @string. + */ +int +match_token_relaxed(struct _s_x *table, char *string) +{ + struct _s_x *pt, *m; + int i, c; + + i = strlen(string); + c = 0; + + for (pt = table ; i != 0 && pt->s != NULL ; pt++) { + if (strncmp(pt->s, string, i) != 0) + continue; + m = pt; + c++; + } + + if (c == 1) + return (m->x); + + return (c > 0 ? -2: -1); } /** @@ -510,6 +680,78 @@ match_value(struct _s_x *p, int value) return NULL; } +size_t +concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter) +{ + struct _s_x *pt; + int l; + size_t sz; + + for (sz = 0, pt = table ; pt->s != NULL; pt++) { + l = snprintf(buf + sz, bufsize - sz, "%s%s", + (sz == 0) ? "" : delimiter, pt->s); + sz += l; + bufsize += l; + if (sz > bufsize) + return (bufsize); + } + + return (sz); +} + +/* + * helper function to process a set of flags and set bits in the + * appropriate masks. + */ +int +fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set, + uint32_t *clear) +{ + char *q; /* points to the separator */ + int val; + uint32_t *which; /* mask we are working on */ + + while (p && *p) { + if (*p == '!') { + p++; + which = clear; + } else + which = set; + q = strchr(p, ','); + if (q) + *q++ = '\0'; + val = match_token(flags, p); + if (val <= 0) { + if (e != NULL) + *e = p; + return (-1); + } + *which |= (uint32_t)val; + p = q; + } + return (0); +} + +void +print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set) +{ + char const *comma = ""; + int i, l; + + for (i = 0; list[i].x != 0; i++) { + if ((set & list[i].x) == 0) + continue; + + set &= ~list[i].x; + l = snprintf(buf, sz, "%s%s", comma, list[i].s); + if (l >= sz) + return; + comma = ","; + buf += l; + sz -=l; + } +} + /* * _substrcmp takes two strings and returns 1 if they do not match, * and 0 if they match exactly or the first string is a sub-string @@ -564,16 +806,16 @@ _substrcmp2(const char *str1, const char* str2, const char* str3) * prints one port, symbolic or numeric */ static void -print_port(int proto, uint16_t port) +print_port(struct buf_pr *bp, int proto, uint16_t port) { if (proto == IPPROTO_ETHERTYPE) { char const *s; if (co.do_resolv && (s = match_value(ether_types, port)) ) - printf("%s", s); + bprintf(bp, "%s", s); else - printf("0x%04x", port); + bprintf(bp, "0x%04x", port); } else { struct servent *se = NULL; if (co.do_resolv) { @@ -582,9 +824,9 @@ print_port(int proto, uint16_t port) se = getservbyport(htons(port), pe ? pe->p_name : NULL); } if (se) - printf("%s", se->s_name); + bprintf(bp, "%s", se->s_name); else - printf("%d", port); + bprintf(bp, "%d", port); } } @@ -606,7 +848,7 @@ static struct _s_x _port_name[] = { * XXX todo: add support for mask. */ static void -print_newports(ipfw_insn_u16 *cmd, int proto, int opcode) +print_newports(struct buf_pr *bp, ipfw_insn_u16 *cmd, int proto, int opcode) { uint16_t *p = cmd->ports; int i; @@ -616,15 +858,15 @@ print_newports(ipfw_insn_u16 *cmd, int proto, int opcode) sep = match_value(_port_name, opcode); if (sep == NULL) sep = "???"; - printf (" %s", sep); + bprintf(bp, " %s", sep); } sep = " "; for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { - printf("%s", sep); - print_port(proto, p[0]); + bprintf(bp, "%s", sep); + print_port(bp, proto, p[0]); if (p[0] != p[1]) { - printf("-"); - print_port(proto, p[1]); + bprintf(bp, "-"); + print_port(bp, proto, p[1]); } sep = ","; } @@ -824,14 +1066,14 @@ fill_reject_code(u_short *codep, char *str) } static void -print_reject_code(uint16_t code) +print_reject_code(struct buf_pr *bp, uint16_t code) { - char const *s = match_value(icmpcodes, code); + char const *s; - if (s != NULL) - printf("unreach %s", s); + if ((s = match_value(icmpcodes, code)) != NULL) + bprintf(bp, "unreach %s", s); else - printf("unreach %u", code); + bprintf(bp, "unreach %u", code); } /* @@ -864,7 +1106,8 @@ contigmask(uint8_t *p, int len) * There is a specialized check for f_tcpflags. */ static void -print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list) +print_flags(struct buf_pr *bp, char const *name, ipfw_insn *cmd, + struct _s_x *list) { char const *comma = ""; int i; @@ -872,34 +1115,38 @@ print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list) uint8_t clear = (cmd->arg1 >> 8) & 0xff; if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { - printf(" setup"); + bprintf(bp, " setup"); return; } - printf(" %s ", name); + bprintf(bp, " %s ", name); for (i=0; list[i].x != 0; i++) { if (set & list[i].x) { set &= ~list[i].x; - printf("%s%s", comma, list[i].s); + bprintf(bp, "%s%s", comma, list[i].s); comma = ","; } if (clear & list[i].x) { clear &= ~list[i].x; - printf("%s!%s", comma, list[i].s); + bprintf(bp, "%s!%s", comma, list[i].s); comma = ","; } } } + /* * Print the ip address contained in a command. */ static void -print_ip(ipfw_insn_ip *cmd, char const *s) +print_ip(struct buf_pr *bp, struct format_opts *fo, ipfw_insn_ip *cmd, + char const *s) { struct hostent *he = NULL; + struct in_addr *ia; uint32_t len = F_LEN((ipfw_insn *)cmd); uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; + char *t; if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { uint32_t d = a[1]; @@ -907,22 +1154,24 @@ print_ip(ipfw_insn_ip *cmd, char const *s) if (d < sizeof(lookup_key)/sizeof(lookup_key[0])) arg = match_value(rule_options, lookup_key[d]); - printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "", - arg, cmd->o.arg1); + t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); + bprintf(bp, "%s lookup %s %s", cmd->o.len & F_NOT ? " not": "", + arg, t); return; } - printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); + bprintf(bp, "%s%s ", cmd->o.len & F_NOT ? " not": "", s); if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { - printf("me"); + bprintf(bp, "me"); return; } if (cmd->o.opcode == O_IP_SRC_LOOKUP || cmd->o.opcode == O_IP_DST_LOOKUP) { - printf("table(%u", ((ipfw_insn *)cmd)->arg1); + t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); + bprintf(bp, "table(%s", t); if (len == F_INSN_SIZE(ipfw_insn_u32)) - printf(",%u", *a); - printf(")"); + bprintf(bp, ",%u", *a); + bprintf(bp, ")"); return; } if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { @@ -933,7 +1182,7 @@ print_ip(ipfw_insn_ip *cmd, char const *s) x = cmd->o.arg1 - 1; x = htonl( ~x ); cmd->addr.s_addr = htonl(cmd->addr.s_addr); - printf("%s/%d", inet_ntoa(cmd->addr), + bprintf(bp, "%s/%d", inet_ntoa(cmd->addr), contigmask((uint8_t *)&x, 32)); x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); x &= 0xff; /* base */ @@ -948,14 +1197,14 @@ print_ip(ipfw_insn_ip *cmd, char const *s) for (j=i+1; j < cmd->o.arg1; j++) if (!(map[ j/32] & (1<<(j & 31)))) break; - printf("%c%d", comma, i+x); + bprintf(bp, "%c%d", comma, i+x); if (j>i+2) { /* range has at least 3 elements */ - printf("-%d", j-1+x); + bprintf(bp, "-%d", j-1+x); i = j-1; } comma = ','; } - printf("}"); + bprintf(bp, "}"); return; } /* @@ -970,18 +1219,20 @@ print_ip(ipfw_insn_ip *cmd, char const *s) if (mb == 32 && co.do_resolv) he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET); if (he != NULL) /* resolved to name */ - printf("%s", he->h_name); + bprintf(bp, "%s", he->h_name); else if (mb == 0) /* any */ - printf("any"); + bprintf(bp, "any"); else { /* numeric IP followed by some kind of mask */ - printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) ); - if (mb < 0) - printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) ); - else if (mb < 32) - printf("/%d", mb); + ia = (struct in_addr *)&a[0]; + bprintf(bp, "%s", inet_ntoa(*ia)); + if (mb < 0) { + ia = (struct in_addr *)&a[1]; + bprintf(bp, ":%s", inet_ntoa(*ia)); + } else if (mb < 32) + bprintf(bp, "/%d", mb); } if (len > 1) - printf(","); + bprintf(bp, ","); } } @@ -989,21 +1240,21 @@ print_ip(ipfw_insn_ip *cmd, char const *s) * prints a MAC address/mask pair */ static void -print_mac(uint8_t *addr, uint8_t *mask) +print_mac(struct buf_pr *bp, uint8_t *addr, uint8_t *mask) { int l = contigmask(mask, 48); if (l == 0) - printf(" any"); + bprintf(bp, " any"); else { - printf(" %02x:%02x:%02x:%02x:%02x:%02x", + bprintf(bp, " %02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); if (l == -1) - printf("&%02x:%02x:%02x:%02x:%02x:%02x", + bprintf(bp, "&%02x:%02x:%02x:%02x:%02x:%02x", mask[0], mask[1], mask[2], mask[3], mask[4], mask[5]); else if (l < 48) - printf("/%d", l); + bprintf(bp, "/%d", l); } } @@ -1032,38 +1283,38 @@ fill_icmptypes(ipfw_insn_u32 *cmd, char *av) } static void -print_icmptypes(ipfw_insn_u32 *cmd) +print_icmptypes(struct buf_pr *bp, ipfw_insn_u32 *cmd) { int i; char sep= ' '; - printf(" icmptypes"); + bprintf(bp, " icmptypes"); for (i = 0; i < 32; i++) { if ( (cmd->d[0] & (1 << (i))) == 0) continue; - printf("%c%d", sep, i); + bprintf(bp, "%c%d", sep, i); sep = ','; } } static void -print_dscp(ipfw_insn_u32 *cmd) +print_dscp(struct buf_pr *bp, ipfw_insn_u32 *cmd) { int i, c; uint32_t *v; char sep= ' '; const char *code; - printf(" dscp"); + bprintf(bp, " dscp"); i = 0; c = 0; v = cmd->d; while (i < 64) { if (*v & (1 << i)) { if ((code = match_value(f_ipdscp, i)) != NULL) - printf("%c%s", sep, code); + bprintf(bp, "%c%s", sep, code); else - printf("%c%d", sep, i); + bprintf(bp, "%c%d", sep, i); sep = ','; } @@ -1094,7 +1345,7 @@ print_dscp(ipfw_insn_u32 *cmd) #define HAVE_OPTIONS 0x8000 static void -show_prerequisites(int *flags, int want, int cmd) +show_prerequisites(struct buf_pr *bp, int *flags, int want, int cmd) { (void)cmd; /* UNUSED */ if (co.comment_only) @@ -1105,22 +1356,23 @@ show_prerequisites(int *flags, int want, int cmd) if ( !(*flags & HAVE_OPTIONS)) { if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) { if ( (*flags & HAVE_PROTO4)) - printf(" ip4"); + bprintf(bp, " ip4"); else if ( (*flags & HAVE_PROTO6)) - printf(" ip6"); + bprintf(bp, " ip6"); else - printf(" ip"); + bprintf(bp, " ip"); } if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) - printf(" from any"); + bprintf(bp, " from any"); if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP)) - printf(" to any"); + bprintf(bp, " to any"); } *flags |= want; } static void -show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) +show_static_rule(struct cmdline_opts *co, struct format_opts *fo, + struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) { static int twidth = 0; int l; @@ -1131,26 +1383,26 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */ int or_block = 0; /* we are in an or block */ - uint32_t set_disable; + uint32_t uval; - bcopy(&rule->next_rule, &set_disable, sizeof(set_disable)); - - if (set_disable & (1 << rule->set)) { /* disabled */ - if (!co.show_sets) + if ((fo->set_mask & (1 << rule->set)) == 0) { + /* disabled mask */ + if (!co->show_sets) return; else - printf("# DISABLED "); + bprintf(bp, "# DISABLED "); } - printf("%05u ", rule->rulenum); + bprintf(bp, "%05u ", rule->rulenum); - if (pcwidth > 0 || bcwidth > 0) { - pr_u64(&rule->pcnt, pcwidth); - pr_u64(&rule->bcnt, bcwidth); + /* Print counters if enabled */ + if (fo->pcwidth > 0 || fo->bcwidth > 0) { + pr_u64(bp, &cntr->pcnt, fo->pcwidth); + pr_u64(bp, &cntr->bcnt, fo->bcwidth); } - if (co.do_time == 2) - printf("%10u ", rule->timestamp); - else if (co.do_time == 1) { + if (co->do_time == 2) + bprintf(bp, "%10u ", cntr->timestamp); + else if (co->do_time == 1) { char timestr[30]; time_t t = (time_t)0; @@ -1159,19 +1411,19 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) *strchr(timestr, '\n') = '\0'; twidth = strlen(timestr); } - if (rule->timestamp) { - t = _long_to_time(rule->timestamp); + if (cntr->timestamp > 0) { + t = _long_to_time(cntr->timestamp); strcpy(timestr, ctime(&t)); *strchr(timestr, '\n') = '\0'; - printf("%s ", timestr); + bprintf(bp, "%s ", timestr); } else { - printf("%*s", twidth, " "); + bprintf(bp, "%*s", twidth, " "); } } - if (co.show_sets) - printf("set %d ", rule->set); + if (co->show_sets) + bprintf(bp, "set %d ", rule->set); /* * print the optional "match probability" @@ -1183,7 +1435,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) double d = 1.0 * p->d[0]; d = (d / 0x7fffffff); - printf("prob %f ", d); + bprintf(bp, "prob %f ", d); } } @@ -1194,66 +1446,66 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { switch(cmd->opcode) { case O_CHECK_STATE: - printf("check-state"); + bprintf(bp, "check-state"); /* avoid printing anything else */ flags = HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP | HAVE_IP; break; case O_ACCEPT: - printf("allow"); + bprintf(bp, "allow"); break; case O_COUNT: - printf("count"); + bprintf(bp, "count"); break; case O_DENY: - printf("deny"); + bprintf(bp, "deny"); break; case O_REJECT: if (cmd->arg1 == ICMP_REJECT_RST) - printf("reset"); + bprintf(bp, "reset"); else if (cmd->arg1 == ICMP_UNREACH_HOST) - printf("reject"); + bprintf(bp, "reject"); else - print_reject_code(cmd->arg1); + print_reject_code(bp, cmd->arg1); break; case O_UNREACH6: if (cmd->arg1 == ICMP6_UNREACH_RST) - printf("reset6"); + bprintf(bp, "reset6"); else print_unreach6_code(cmd->arg1); break; case O_SKIPTO: - PRINT_UINT_ARG("skipto ", cmd->arg1); + bprint_uint_arg(bp, "skipto ", cmd->arg1); break; case O_PIPE: - PRINT_UINT_ARG("pipe ", cmd->arg1); + bprint_uint_arg(bp, "pipe ", cmd->arg1); break; case O_QUEUE: - PRINT_UINT_ARG("queue ", cmd->arg1); + bprint_uint_arg(bp, "queue ", cmd->arg1); break; case O_DIVERT: - PRINT_UINT_ARG("divert ", cmd->arg1); + bprint_uint_arg(bp, "divert ", cmd->arg1); break; case O_TEE: - PRINT_UINT_ARG("tee ", cmd->arg1); + bprint_uint_arg(bp, "tee ", cmd->arg1); break; case O_NETGRAPH: - PRINT_UINT_ARG("netgraph ", cmd->arg1); + bprint_uint_arg(bp, "netgraph ", cmd->arg1); break; case O_NGTEE: - PRINT_UINT_ARG("ngtee ", cmd->arg1); + bprint_uint_arg(bp, "ngtee ", cmd->arg1); break; case O_FORWARD_IP: @@ -1261,12 +1513,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) ipfw_insn_sa *s = (ipfw_insn_sa *)cmd; if (s->sa.sin_addr.s_addr == INADDR_ANY) { - printf("fwd tablearg"); + bprintf(bp, "fwd tablearg"); } else { - printf("fwd %s", inet_ntoa(s->sa.sin_addr)); + bprintf(bp, "fwd %s",inet_ntoa(s->sa.sin_addr)); } if (s->sa.sin_port) - printf(",%d", s->sa.sin_port); + bprintf(bp, ",%d", s->sa.sin_port); } break; @@ -1275,10 +1527,10 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) char buf[4 + INET6_ADDRSTRLEN + 1]; ipfw_insn_sa6 *s = (ipfw_insn_sa6 *)cmd; - printf("fwd %s", inet_ntop(AF_INET6, &s->sa.sin6_addr, - buf, sizeof(buf))); + bprintf(bp, "fwd %s", inet_ntop(AF_INET6, + &s->sa.sin6_addr, buf, sizeof(buf))); if (s->sa.sin6_port) - printf(",%d", s->sa.sin6_port); + bprintf(bp, ",%d", s->sa.sin6_port); } break; @@ -1296,64 +1548,69 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_NAT: if (cmd->arg1 != 0) - PRINT_UINT_ARG("nat ", cmd->arg1); + bprint_uint_arg(bp, "nat ", cmd->arg1); else - printf("nat global"); + bprintf(bp, "nat global"); break; case O_SETFIB: - PRINT_UINT_ARG("setfib ", cmd->arg1); + bprint_uint_arg(bp, "setfib ", cmd->arg1 & 0x7FFF); break; case O_SETDSCP: { const char *code; - if ((code = match_value(f_ipdscp, cmd->arg1)) != NULL) - printf("setdscp %s", code); + if (cmd->arg1 == IP_FW_TARG) { + bprint_uint_arg(bp, "setdscp ", cmd->arg1); + break; + } + uval = cmd->arg1 & 0x3F; + if ((code = match_value(f_ipdscp, uval)) != NULL) + bprintf(bp, "setdscp %s", code); else - PRINT_UINT_ARG("setdscp ", cmd->arg1); + bprint_uint_arg(bp, "setdscp ", uval); } break; case O_REASS: - printf("reass"); + bprintf(bp, "reass"); break; case O_CALLRETURN: if (cmd->len & F_NOT) - printf("return"); + bprintf(bp, "return"); else - PRINT_UINT_ARG("call ", cmd->arg1); + bprint_uint_arg(bp, "call ", cmd->arg1); break; default: - printf("** unrecognized action %d len %d ", + bprintf(bp, "** unrecognized action %d len %d ", cmd->opcode, cmd->len); } } if (logptr) { if (logptr->max_log > 0) - printf(" log logamount %d", logptr->max_log); + bprintf(bp, " log logamount %d", logptr->max_log); else - printf(" log"); + bprintf(bp, " log"); } #ifndef NO_ALTQ if (altqptr) { - print_altq_cmd(altqptr); + print_altq_cmd(bp, altqptr); } #endif if (tagptr) { if (tagptr->len & F_NOT) - PRINT_UINT_ARG(" untag ", tagptr->arg1); + bprint_uint_arg(bp, " untag ", tagptr->arg1); else - PRINT_UINT_ARG(" tag ", tagptr->arg1); + bprint_uint_arg(bp, " tag ", tagptr->arg1); } /* * then print the body. */ - for (l = rule->act_ofs, cmd = rule->cmd ; + for (l = rule->act_ofs, cmd = rule->cmd; l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { if ((cmd->len & F_OR) || (cmd->len & F_NOT)) continue; @@ -1365,31 +1622,31 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) break; } } - if (rule->_pad & 1) { /* empty rules before options */ - if (!co.do_compact) { - show_prerequisites(&flags, HAVE_PROTO, 0); - printf(" from any to any"); + if (rule->flags & IPFW_RULE_NOOPT) { /* empty rules before options */ + if (!co->do_compact) { + show_prerequisites(bp, &flags, HAVE_PROTO, 0); + bprintf(bp, " from any to any"); } flags |= HAVE_IP | HAVE_OPTIONS | HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP; } - if (co.comment_only) + if (co->comment_only) comment = "..."; - for (l = rule->act_ofs, cmd = rule->cmd ; + for (l = rule->act_ofs, cmd = rule->cmd; l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { /* useful alias */ ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; - if (co.comment_only) { + if (co->comment_only) { if (cmd->opcode != O_NOP) continue; - printf(" // %s\n", (char *)(cmd + 1)); + bprintf(bp, " // %s\n", (char *)(cmd + 1)); return; } - show_prerequisites(&flags, 0, cmd->opcode); + show_prerequisites(bp, &flags, 0, cmd->opcode); switch(cmd->opcode) { case O_PROB: @@ -1403,12 +1660,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_IP_SRC_MASK: case O_IP_SRC_ME: case O_IP_SRC_SET: - show_prerequisites(&flags, HAVE_PROTO, 0); + show_prerequisites(bp, &flags, HAVE_PROTO, 0); if (!(flags & HAVE_SRCIP)) - printf(" from"); + bprintf(bp, " from"); if ((cmd->len & F_OR) && !or_block) - printf(" {"); - print_ip((ipfw_insn_ip *)cmd, + bprintf(bp, " {"); + print_ip(bp, fo, (ipfw_insn_ip *)cmd, (flags & HAVE_OPTIONS) ? " src-ip" : ""); flags |= HAVE_SRCIP; break; @@ -1418,12 +1675,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_IP_DST_MASK: case O_IP_DST_ME: case O_IP_DST_SET: - show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); + show_prerequisites(bp, &flags, HAVE_PROTO|HAVE_SRCIP, 0); if (!(flags & HAVE_DSTIP)) - printf(" to"); + bprintf(bp, " to"); if ((cmd->len & F_OR) && !or_block) - printf(" {"); - print_ip((ipfw_insn_ip *)cmd, + bprintf(bp, " {"); + print_ip(bp, fo, (ipfw_insn_ip *)cmd, (flags & HAVE_OPTIONS) ? " dst-ip" : ""); flags |= HAVE_DSTIP; break; @@ -1431,12 +1688,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_IP6_SRC: case O_IP6_SRC_MASK: case O_IP6_SRC_ME: - show_prerequisites(&flags, HAVE_PROTO, 0); + show_prerequisites(bp, &flags, HAVE_PROTO, 0); if (!(flags & HAVE_SRCIP)) - printf(" from"); + bprintf(bp, " from"); if ((cmd->len & F_OR) && !or_block) - printf(" {"); - print_ip6((ipfw_insn_ip6 *)cmd, + bprintf(bp, " {"); + print_ip6(bp, (ipfw_insn_ip6 *)cmd, (flags & HAVE_OPTIONS) ? " src-ip6" : ""); flags |= HAVE_SRCIP | HAVE_PROTO; break; @@ -1444,35 +1701,35 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) case O_IP6_DST: case O_IP6_DST_MASK: case O_IP6_DST_ME: - show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); + show_prerequisites(bp, &flags, HAVE_PROTO|HAVE_SRCIP, 0); if (!(flags & HAVE_DSTIP)) - printf(" to"); + bprintf(bp, " to"); if ((cmd->len & F_OR) && !or_block) - printf(" {"); - print_ip6((ipfw_insn_ip6 *)cmd, + bprintf(bp, " {"); + print_ip6(bp, (ipfw_insn_ip6 *)cmd, (flags & HAVE_OPTIONS) ? " dst-ip6" : ""); flags |= HAVE_DSTIP; break; case O_FLOW6ID: - print_flow6id( (ipfw_insn_u32 *) cmd ); - flags |= HAVE_OPTIONS; - break; + print_flow6id(bp, (ipfw_insn_u32 *) cmd ); + flags |= HAVE_OPTIONS; + break; case O_IP_DSTPORT: - show_prerequisites(&flags, + show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP | HAVE_IP, 0); case O_IP_SRCPORT: if (flags & HAVE_DSTIP) flags |= HAVE_IP; - show_prerequisites(&flags, + show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_SRCIP, 0); if ((cmd->len & F_OR) && !or_block) - printf(" {"); + bprintf(bp, " {"); if (cmd->len & F_NOT) - printf(" not"); - print_newports((ipfw_insn_u16 *)cmd, proto, + bprintf(bp, " not"); + print_newports(bp, (ipfw_insn_u16 *)cmd, proto, (flags & HAVE_OPTIONS) ? cmd->opcode : 0); break; @@ -1480,22 +1737,22 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) struct protoent *pe = NULL; if ((cmd->len & F_OR) && !or_block) - printf(" {"); + bprintf(bp, " {"); if (cmd->len & F_NOT) - printf(" not"); + bprintf(bp, " not"); proto = cmd->arg1; pe = getprotobynumber(cmd->arg1); if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) && !(flags & HAVE_PROTO)) - show_prerequisites(&flags, + show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_IP | HAVE_SRCIP | HAVE_DSTIP | HAVE_OPTIONS, 0); if (flags & HAVE_OPTIONS) - printf(" proto"); + bprintf(bp, " proto"); if (pe) - printf(" %s", pe->p_name); + bprintf(bp, " %s", pe->p_name); else - printf(" %u", cmd->arg1); + bprintf(bp, " %u", cmd->arg1); } flags |= HAVE_PROTO; break; @@ -1507,68 +1764,68 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) ((cmd->opcode == O_IP4) && (flags & HAVE_PROTO4))) break; - show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | + show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP | HAVE_IP | HAVE_OPTIONS, 0); if ((cmd->len & F_OR) && !or_block) - printf(" {"); + bprintf(bp, " {"); if (cmd->len & F_NOT && cmd->opcode != O_IN) - printf(" not"); + bprintf(bp, " not"); switch(cmd->opcode) { case O_MACADDR2: { ipfw_insn_mac *m = (ipfw_insn_mac *)cmd; - printf(" MAC"); - print_mac(m->addr, m->mask); - print_mac(m->addr + 6, m->mask + 6); + bprintf(bp, " MAC"); + print_mac(bp, m->addr, m->mask); + print_mac(bp, m->addr + 6, m->mask + 6); } break; case O_MAC_TYPE: - print_newports((ipfw_insn_u16 *)cmd, + print_newports(bp, (ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE, cmd->opcode); break; case O_FRAG: - printf(" frag"); + bprintf(bp, " frag"); break; case O_FIB: - printf(" fib %u", cmd->arg1 ); + bprintf(bp, " fib %u", cmd->arg1 ); break; case O_SOCKARG: - printf(" sockarg"); + bprintf(bp, " sockarg"); break; case O_IN: - printf(cmd->len & F_NOT ? " out" : " in"); + bprintf(bp, cmd->len & F_NOT ? " out" : " in"); break; case O_DIVERTED: switch (cmd->arg1) { case 3: - printf(" diverted"); + bprintf(bp, " diverted"); break; case 1: - printf(" diverted-loopback"); + bprintf(bp, " diverted-loopback"); break; case 2: - printf(" diverted-output"); + bprintf(bp, " diverted-output"); break; default: - printf(" diverted-?<%u>", cmd->arg1); + bprintf(bp, " diverted-?<%u>", cmd->arg1); break; } break; case O_LAYER2: - printf(" layer2"); + bprintf(bp, " layer2"); break; case O_XMIT: case O_RECV: case O_VIA: { - char const *s; + char const *s, *t; ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd; if (cmd->opcode == O_XMIT) @@ -1578,97 +1835,112 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) else /* if (cmd->opcode == O_VIA) */ s = "via"; if (cmdif->name[0] == '\0') - printf(" %s %s", s, + bprintf(bp, " %s %s", s, inet_ntoa(cmdif->p.ip)); - else if (cmdif->name[0] == '\1') /* interface table */ - printf(" %s table(%d)", s, cmdif->p.glob); - else - printf(" %s %s", s, cmdif->name); + else if (cmdif->name[0] == '\1') { + /* interface table */ + t = table_search_ctlv(fo->tstate, + cmdif->p.kidx); + bprintf(bp, " %s table(%s)", s, t); + } else + bprintf(bp, " %s %s", s, cmdif->name); break; } + case O_IP_FLOW_LOOKUP: + { + char *t; + + t = table_search_ctlv(fo->tstate, cmd->arg1); + bprintf(bp, " flow table(%s", t); + if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) + bprintf(bp, ",%u", + ((ipfw_insn_u32 *)cmd)->d[0]); + bprintf(bp, ")"); + break; + } case O_IPID: if (F_LEN(cmd) == 1) - printf(" ipid %u", cmd->arg1 ); + bprintf(bp, " ipid %u", cmd->arg1 ); else - print_newports((ipfw_insn_u16 *)cmd, 0, + print_newports(bp, (ipfw_insn_u16 *)cmd, 0, O_IPID); break; case O_IPTTL: if (F_LEN(cmd) == 1) - printf(" ipttl %u", cmd->arg1 ); + bprintf(bp, " ipttl %u", cmd->arg1 ); else - print_newports((ipfw_insn_u16 *)cmd, 0, + print_newports(bp, (ipfw_insn_u16 *)cmd, 0, O_IPTTL); break; case O_IPVER: - printf(" ipver %u", cmd->arg1 ); + bprintf(bp, " ipver %u", cmd->arg1 ); break; case O_IPPRECEDENCE: - printf(" ipprecedence %u", (cmd->arg1) >> 5 ); + bprintf(bp, " ipprecedence %u", cmd->arg1 >> 5); break; case O_DSCP: - print_dscp((ipfw_insn_u32 *)cmd); + print_dscp(bp, (ipfw_insn_u32 *)cmd); break; case O_IPLEN: if (F_LEN(cmd) == 1) - printf(" iplen %u", cmd->arg1 ); + bprintf(bp, " iplen %u", cmd->arg1 ); else - print_newports((ipfw_insn_u16 *)cmd, 0, + print_newports(bp, (ipfw_insn_u16 *)cmd, 0, O_IPLEN); break; case O_IPOPT: - print_flags("ipoptions", cmd, f_ipopts); + print_flags(bp, "ipoptions", cmd, f_ipopts); break; case O_IPTOS: - print_flags("iptos", cmd, f_iptos); + print_flags(bp, "iptos", cmd, f_iptos); break; case O_ICMPTYPE: - print_icmptypes((ipfw_insn_u32 *)cmd); + print_icmptypes(bp, (ipfw_insn_u32 *)cmd); break; case O_ESTAB: - printf(" established"); + bprintf(bp, " established"); break; case O_TCPDATALEN: if (F_LEN(cmd) == 1) - printf(" tcpdatalen %u", cmd->arg1 ); + bprintf(bp, " tcpdatalen %u", cmd->arg1 ); else - print_newports((ipfw_insn_u16 *)cmd, 0, + print_newports(bp, (ipfw_insn_u16 *)cmd, 0, O_TCPDATALEN); break; case O_TCPFLAGS: - print_flags("tcpflags", cmd, f_tcpflags); + print_flags(bp, "tcpflags", cmd, f_tcpflags); break; case O_TCPOPTS: - print_flags("tcpoptions", cmd, f_tcpopts); + print_flags(bp, "tcpoptions", cmd, f_tcpopts); break; case O_TCPWIN: if (F_LEN(cmd) == 1) - printf(" tcpwin %u", cmd->arg1); + bprintf(bp, " tcpwin %u", cmd->arg1); else - print_newports((ipfw_insn_u16 *)cmd, 0, + print_newports(bp, (ipfw_insn_u16 *)cmd, 0, O_TCPWIN); break; case O_TCPACK: - printf(" tcpack %d", ntohl(cmd32->d[0])); + bprintf(bp, " tcpack %d", ntohl(cmd32->d[0])); break; case O_TCPSEQ: - printf(" tcpseq %d", ntohl(cmd32->d[0])); + bprintf(bp, " tcpseq %d", ntohl(cmd32->d[0])); break; case O_UID: @@ -1676,9 +1948,9 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) struct passwd *pwd = getpwuid(cmd32->d[0]); if (pwd) - printf(" uid %s", pwd->pw_name); + bprintf(bp, " uid %s", pwd->pw_name); else - printf(" uid %u", cmd32->d[0]); + bprintf(bp, " uid %u", cmd32->d[0]); } break; @@ -1687,30 +1959,30 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) struct group *grp = getgrgid(cmd32->d[0]); if (grp) - printf(" gid %s", grp->gr_name); + bprintf(bp, " gid %s", grp->gr_name); else - printf(" gid %u", cmd32->d[0]); + bprintf(bp, " gid %u", cmd32->d[0]); } break; case O_JAIL: - printf(" jail %d", cmd32->d[0]); + bprintf(bp, " jail %d", cmd32->d[0]); break; case O_VERREVPATH: - printf(" verrevpath"); + bprintf(bp, " verrevpath"); break; case O_VERSRCREACH: - printf(" versrcreach"); + bprintf(bp, " versrcreach"); break; case O_ANTISPOOF: - printf(" antispoof"); + bprintf(bp, " antispoof"); break; case O_IPSEC: - printf(" ipsec"); + bprintf(bp, " ipsec"); break; case O_NOP: @@ -1718,7 +1990,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) break; case O_KEEP_STATE: - printf(" keep-state"); + bprintf(bp, " keep-state"); break; case O_LIMIT: { @@ -1727,113 +1999,132 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) uint8_t x = c->limit_mask; char const *comma = " "; - printf(" limit"); + bprintf(bp, " limit"); for (; p->x != 0 ; p++) if ((x & p->x) == p->x) { x &= ~p->x; - printf("%s%s", comma, p->s); + bprintf(bp, "%s%s", comma,p->s); comma = ","; } - PRINT_UINT_ARG(" ", c->conn_limit); + bprint_uint_arg(bp, " ", c->conn_limit); break; } case O_IP6: - printf(" ip6"); + bprintf(bp, " ip6"); break; case O_IP4: - printf(" ip4"); + bprintf(bp, " ip4"); break; case O_ICMP6TYPE: - print_icmp6types((ipfw_insn_u32 *)cmd); + print_icmp6types(bp, (ipfw_insn_u32 *)cmd); break; case O_EXT_HDR: - print_ext6hdr( (ipfw_insn *) cmd ); + print_ext6hdr(bp, (ipfw_insn *)cmd); break; case O_TAGGED: if (F_LEN(cmd) == 1) - PRINT_UINT_ARG(" tagged ", cmd->arg1); + bprint_uint_arg(bp, " tagged ", + cmd->arg1); else - print_newports((ipfw_insn_u16 *)cmd, 0, - O_TAGGED); + print_newports(bp, (ipfw_insn_u16 *)cmd, + 0, O_TAGGED); break; default: - printf(" [opcode %d len %d]", + bprintf(bp, " [opcode %d len %d]", cmd->opcode, cmd->len); } } if (cmd->len & F_OR) { - printf(" or"); + bprintf(bp, " or"); or_block = 1; } else if (or_block) { - printf(" }"); + bprintf(bp, " }"); or_block = 0; } } - show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP + show_prerequisites(bp, &flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP | HAVE_IP, 0); if (comment) - printf(" // %s", comment); - printf("\n"); + bprintf(bp, " // %s", comment); + bprintf(bp, "\n"); } static void -show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth) +show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, + struct buf_pr *bp, ipfw_dyn_rule *d) { struct protoent *pe; struct in_addr a; uint16_t rulenum; char buf[INET6_ADDRSTRLEN]; - if (!co.do_expired) { + if (!co->do_expired) { if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT)) return; } bcopy(&d->rule, &rulenum, sizeof(rulenum)); - printf("%05d", rulenum); - if (pcwidth > 0 || bcwidth > 0) { - printf(" "); - pr_u64(&d->pcnt, pcwidth); - pr_u64(&d->bcnt, bcwidth); - printf("(%ds)", d->expire); + bprintf(bp, "%05d", rulenum); + if (fo->pcwidth > 0 || fo->bcwidth > 0) { + bprintf(bp, " "); + pr_u64(bp, &d->pcnt, fo->pcwidth); + pr_u64(bp, &d->bcnt, fo->bcwidth); + bprintf(bp, "(%ds)", d->expire); } switch (d->dyn_type) { case O_LIMIT_PARENT: - printf(" PARENT %d", d->count); + bprintf(bp, " PARENT %d", d->count); break; case O_LIMIT: - printf(" LIMIT"); + bprintf(bp, " LIMIT"); break; case O_KEEP_STATE: /* bidir, no mask */ - printf(" STATE"); + bprintf(bp, " STATE"); break; } if ((pe = getprotobynumber(d->id.proto)) != NULL) - printf(" %s", pe->p_name); + bprintf(bp, " %s", pe->p_name); else - printf(" proto %u", d->id.proto); + bprintf(bp, " proto %u", d->id.proto); if (d->id.addr_type == 4) { a.s_addr = htonl(d->id.src_ip); - printf(" %s %d", inet_ntoa(a), d->id.src_port); + bprintf(bp, " %s %d", inet_ntoa(a), d->id.src_port); a.s_addr = htonl(d->id.dst_ip); - printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port); + bprintf(bp, " <-> %s %d", inet_ntoa(a), d->id.dst_port); } else if (d->id.addr_type == 6) { - printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, + bprintf(bp, " %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, sizeof(buf)), d->id.src_port); - printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, - sizeof(buf)), d->id.dst_port); + bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, + buf, sizeof(buf)), d->id.dst_port); } else - printf(" UNKNOWN <-> UNKNOWN\n"); + bprintf(bp, " UNKNOWN <-> UNKNOWN\n"); +} - printf("\n"); +static int +do_range_cmd(int cmd, ipfw_range_tlv *rt) +{ + ipfw_range_header rh; + size_t sz; + + memset(&rh, 0, sizeof(rh)); + memcpy(&rh.range, rt, sizeof(*rt)); + rh.range.head.length = sizeof(*rt); + rh.range.head.type = IPFW_TLV_RANGE; + sz = sizeof(rh); + + if (do_get3(cmd, &rh.opheader, &sz) != 0) + return (-1); + /* Save number of matched objects */ + rt->new_set = rh.range.new_set; + return (0); } /* @@ -1846,77 +2137,75 @@ show_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth) void ipfw_sets_handler(char *av[]) { - uint32_t set_disable, masks[2]; - int i, nbytes; - uint16_t rulenum; - uint8_t cmd, new_set; + uint32_t masks[2]; + int i; + uint8_t cmd, rulenum; + ipfw_range_tlv rt; + char *msg; + size_t size; av++; + memset(&rt, 0, sizeof(rt)); if (av[0] == NULL) errx(EX_USAGE, "set needs command"); if (_substrcmp(*av, "show") == 0) { - void *data = NULL; - char const *msg; - int nalloc; - - nalloc = nbytes = sizeof(struct ip_fw); - while (nbytes >= nalloc) { - if (data) - free(data); - nalloc = nalloc * 2 + 200; - nbytes = nalloc; - data = safe_calloc(1, nbytes); - if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0) - err(EX_OSERR, "getsockopt(IP_FW_GET)"); - } + struct format_opts fo; + ipfw_cfg_lheader *cfg; - bcopy(&((struct ip_fw *)data)->next_rule, - &set_disable, sizeof(set_disable)); + memset(&fo, 0, sizeof(fo)); + if (ipfw_get_config(&co, &fo, &cfg, &size) != 0) + err(EX_OSERR, "requesting config failed"); - for (i = 0, msg = "disable" ; i < RESVD_SET; i++) - if ((set_disable & (1<<i))) { + for (i = 0, msg = "disable"; i < RESVD_SET; i++) + if ((cfg->set_mask & (1<<i)) == 0) { printf("%s %d", msg, i); msg = ""; } - msg = (set_disable) ? " enable" : "enable"; + msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable"; for (i = 0; i < RESVD_SET; i++) - if (!(set_disable & (1<<i))) { + if ((cfg->set_mask & (1<<i)) != 0) { printf("%s %d", msg, i); msg = ""; } printf("\n"); + free(cfg); } else if (_substrcmp(*av, "swap") == 0) { av++; if ( av[0] == NULL || av[1] == NULL ) errx(EX_USAGE, "set swap needs 2 set numbers\n"); - rulenum = atoi(av[0]); - new_set = atoi(av[1]); - if (!isdigit(*(av[0])) || rulenum > RESVD_SET) + rt.set = atoi(av[0]); + rt.new_set = atoi(av[1]); + if (!isdigit(*(av[0])) || rt.set > RESVD_SET) errx(EX_DATAERR, "invalid set number %s\n", av[0]); - if (!isdigit(*(av[1])) || new_set > RESVD_SET) + if (!isdigit(*(av[1])) || rt.new_set > RESVD_SET) errx(EX_DATAERR, "invalid set number %s\n", av[1]); - masks[0] = (4 << 24) | (new_set << 16) | (rulenum); - i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); + i = do_range_cmd(IP_FW_SET_SWAP, &rt); } else if (_substrcmp(*av, "move") == 0) { av++; if (av[0] && _substrcmp(*av, "rule") == 0) { - cmd = 2; + rt.flags = IPFW_RCFLAG_RANGE; /* move rules to new set */ + cmd = IP_FW_XMOVE; av++; } else - cmd = 3; + cmd = IP_FW_SET_MOVE; /* Move set to new one */ if (av[0] == NULL || av[1] == NULL || av[2] == NULL || av[3] != NULL || _substrcmp(av[1], "to") != 0) errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); rulenum = atoi(av[0]); - new_set = atoi(av[2]); - if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) || - (cmd == 2 && rulenum == IPFW_DEFAULT_RULE) ) + rt.new_set = atoi(av[2]); + if (cmd == IP_FW_XMOVE) { + rt.start_rule = rulenum; + rt.end_rule = rulenum; + } else + rt.set = rulenum; + rt.new_set = atoi(av[2]); + if (!isdigit(*(av[0])) || (cmd == 3 && rt.set > RESVD_SET) || + (cmd == 2 && rt.start_rule == IPFW_DEFAULT_RULE) ) errx(EX_DATAERR, "invalid source number %s\n", av[0]); - if (!isdigit(*(av[2])) || new_set > RESVD_SET) + if (!isdigit(*(av[2])) || rt.new_set > RESVD_SET) errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); - masks[0] = (cmd << 24) | (new_set << 16) | (rulenum); - i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); + i = do_range_cmd(cmd, &rt); } else if (_substrcmp(*av, "disable") == 0 || _substrcmp(*av, "enable") == 0 ) { int which = _substrcmp(*av, "enable") == 0 ? 1 : 0; @@ -1944,9 +2233,11 @@ ipfw_sets_handler(char *av[]) errx(EX_DATAERR, "cannot enable and disable the same set\n"); - i = do_cmd(IP_FW_DEL, masks, sizeof(masks)); + rt.set = masks[0]; + rt.new_set = masks[1]; + i = do_range_cmd(IP_FW_SET_ENABLE, &rt); if (i) - warn("set enable/disable: setsockopt(IP_FW_DEL)"); + warn("set enable/disable: setsockopt(IP_FW_SET_ENABLE)"); } else errx(EX_USAGE, "invalid set command %s\n", *av); } @@ -1984,28 +2275,204 @@ ipfw_sysctl_handler(char *av[], int which) } } +typedef void state_cb(struct cmdline_opts *co, struct format_opts *fo, + void *arg, void *state); + +static void +prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo, + void *arg, void *_state) +{ + ipfw_dyn_rule *d; + int width; + uint8_t set; + + d = (ipfw_dyn_rule *)_state; + /* Count _ALL_ states */ + fo->dcnt++; + + if (fo->show_counters == 0) + return; + + if (co->use_set) { + /* skip states from another set */ + bcopy((char *)&d->rule + sizeof(uint16_t), &set, + sizeof(uint8_t)); + if (set != co->use_set - 1) + return; + } + + width = pr_u64(NULL, &d->pcnt, 0); + if (width > fo->pcwidth) + fo->pcwidth = width; + + width = pr_u64(NULL, &d->bcnt, 0); + if (width > fo->bcwidth) + fo->bcwidth = width; +} + +static int +foreach_state(struct cmdline_opts *co, struct format_opts *fo, + caddr_t base, size_t sz, state_cb dyn_bc, void *dyn_arg) +{ + int ttype; + state_cb *fptr; + void *farg; + ipfw_obj_tlv *tlv; + ipfw_obj_ctlv *ctlv; + + fptr = NULL; + ttype = 0; + + while (sz > 0) { + ctlv = (ipfw_obj_ctlv *)base; + switch (ctlv->head.type) { + case IPFW_TLV_DYNSTATE_LIST: + base += sizeof(*ctlv); + sz -= sizeof(*ctlv); + ttype = IPFW_TLV_DYN_ENT; + fptr = dyn_bc; + farg = dyn_arg; + break; + default: + return (sz); + } + + while (sz > 0) { + tlv = (ipfw_obj_tlv *)base; + if (tlv->type != ttype) + break; + + fptr(co, fo, farg, tlv + 1); + sz -= tlv->length; + base += tlv->length; + } + } + + return (sz); +} + +static void +prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, + ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz) +{ + int bcwidth, pcwidth, width; + int n; + struct ip_fw_bcounter *cntr; + struct ip_fw_rule *r; + + bcwidth = 0; + pcwidth = 0; + if (fo->show_counters != 0) { + for (n = 0; n < rcnt; n++, + rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { + cntr = (struct ip_fw_bcounter *)(rtlv + 1); + r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); + /* skip rules from another set */ + if (co->use_set && r->set != co->use_set - 1) + continue; + + /* packet counter */ + width = pr_u64(NULL, &cntr->pcnt, 0); + if (width > pcwidth) + pcwidth = width; + + /* byte counter */ + width = pr_u64(NULL, &cntr->bcnt, 0); + if (width > bcwidth) + bcwidth = width; + } + } + fo->bcwidth = bcwidth; + fo->pcwidth = pcwidth; + + fo->dcnt = 0; + if (co->do_dynamic && dynsz > 0) + foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL); +} + +static int +list_static_range(struct cmdline_opts *co, struct format_opts *fo, + struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt) +{ + int n, seen; + struct ip_fw_rule *r; + struct ip_fw_bcounter *cntr; + int c = 0; + + for (n = seen = 0; n < rcnt; n++, + rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) { + + if (fo->show_counters != 0) { + cntr = (struct ip_fw_bcounter *)(rtlv + 1); + r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size); + } else { + cntr = NULL; + r = (struct ip_fw_rule *)(rtlv + 1); + } + if (r->rulenum > fo->last) + break; + if (co->use_set && r->set != co->use_set - 1) + continue; + if (r->rulenum >= fo->first && r->rulenum <= fo->last) { + show_static_rule(co, fo, bp, r, cntr); + printf("%s", bp->buf); + c += rtlv->length; + bp_flush(bp); + seen++; + } + } + + return (seen); +} + +static void +list_dyn_state(struct cmdline_opts *co, struct format_opts *fo, + void *_arg, void *_state) +{ + uint16_t rulenum; + uint8_t set; + ipfw_dyn_rule *d; + struct buf_pr *bp; + + d = (ipfw_dyn_rule *)_state; + bp = (struct buf_pr *)_arg; + + bcopy(&d->rule, &rulenum, sizeof(rulenum)); + if (rulenum > fo->last) + return; + if (co->use_set) { + bcopy((char *)&d->rule + sizeof(uint16_t), + &set, sizeof(uint8_t)); + if (set != co->use_set - 1) + return; + } + if (rulenum >= fo->first) { + show_dyn_state(co, fo, bp, d); + printf("%s\n", bp->buf); + bp_flush(bp); + } +} + +static int +list_dyn_range(struct cmdline_opts *co, struct format_opts *fo, + struct buf_pr *bp, caddr_t base, size_t sz) +{ + + sz = foreach_state(co, fo, base, sz, list_dyn_state, bp); + return (sz); +} + void ipfw_list(int ac, char *av[], int show_counters) { - struct ip_fw *r; - ipfw_dyn_rule *dynrules, *d; - -#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r))) - char *lim; - void *data = NULL; - int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width; - int exitval = EX_OK; + ipfw_cfg_lheader *cfg; + struct format_opts sfo; + size_t sz; + int error; int lac; char **lav; - u_long rnum, last; + uint32_t rnum; char *endptr; - int seen = 0; - uint8_t set; - - const int ocmd = co.do_pipe ? IP_DUMMYNET_GET : IP_FW_GET; - int nalloc = 1024; /* start somewhere... */ - - last = 0; if (co.test_only) { fprintf(stderr, "Testing only, list disabled\n"); @@ -2018,162 +2485,216 @@ ipfw_list(int ac, char *av[], int show_counters) ac--; av++; + memset(&sfo, 0, sizeof(sfo)); - /* get rules or pipes from kernel, resizing array as necessary */ - nbytes = nalloc; + /* Determine rule range to request */ + if (ac > 0) { + for (lac = ac, lav = av; lac != 0; lac--) { + rnum = strtoul(*lav++, &endptr, 10); + if (sfo.first == 0 || rnum < sfo.first) + sfo.first = rnum; - while (nbytes >= nalloc) { - nalloc = nalloc * 2 + 200; - nbytes = nalloc; - data = safe_realloc(data, nbytes); - if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0) - err(EX_OSERR, "getsockopt(IP_%s_GET)", - co.do_pipe ? "DUMMYNET" : "FW"); + if (*endptr == '-') + rnum = strtoul(endptr + 1, &endptr, 10); + if (sfo.last == 0 || rnum > sfo.last) + sfo.last = rnum; + } } - /* - * Count static rules. They have variable size so we - * need to scan the list to count them. - */ - for (nstat = 1, r = data, lim = (char *)data + nbytes; - r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim; - ++nstat, r = NEXT(r) ) - ; /* nothing */ + /* get configuraion from kernel */ + cfg = NULL; + sfo.show_counters = show_counters; + sfo.flags = IPFW_CFG_GET_STATIC; + if (co.do_dynamic != 0) + sfo.flags |= IPFW_CFG_GET_STATES; + if (sfo.show_counters != 0) + sfo.flags |= IPFW_CFG_GET_COUNTERS; + if (ipfw_get_config(&co, &sfo, &cfg, &sz) != 0) + err(EX_OSERR, "retrieving config failed"); + + error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av); + + free(cfg); + + if (error != EX_OK) + exit(error); +} + +static int +ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, + ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[]) +{ + caddr_t dynbase; + size_t dynsz; + int rcnt; + int exitval = EX_OK; + int lac; + char **lav; + char *endptr; + size_t readsz; + struct buf_pr bp; + ipfw_obj_ctlv *ctlv, *tstate; + ipfw_obj_tlv *rbase; /* - * Count dynamic rules. This is easier as they have - * fixed size. + * Handle tablenames TLV first, if any */ - r = NEXT(r); - dynrules = (ipfw_dyn_rule *)r ; - n = (char *)r - (char *)data; - ndyn = (nbytes - n) / sizeof *dynrules; - - /* if showing stats, figure out column widths ahead of time */ - bcwidth = pcwidth = 0; - if (show_counters) { - for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { - /* skip rules from another set */ - if (co.use_set && r->set != co.use_set - 1) - continue; - - /* packet counter */ - width = pr_u64(&r->pcnt, 0); - if (width > pcwidth) - pcwidth = width; + tstate = NULL; + rbase = NULL; + dynbase = NULL; + dynsz = 0; + readsz = sizeof(*cfg); + rcnt = 0; + + fo->set_mask = cfg->set_mask; + + ctlv = (ipfw_obj_ctlv *)(cfg + 1); + + if (cfg->flags & IPFW_CFG_GET_STATIC) { + /* We've requested static rules */ + if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { + fo->tstate = ctlv; + readsz += ctlv->head.length; + ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + + ctlv->head.length); + } - /* byte counter */ - width = pr_u64(&r->bcnt, 0); - if (width > bcwidth) - bcwidth = width; + if (ctlv->head.type == IPFW_TLV_RULE_LIST) { + rbase = (ipfw_obj_tlv *)(ctlv + 1); + rcnt = ctlv->count; + readsz += ctlv->head.length; + ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + + ctlv->head.length); } } - if (co.do_dynamic && ndyn) { - for (n = 0, d = dynrules; n < ndyn; n++, d++) { - if (co.use_set) { - /* skip rules from another set */ - bcopy((char *)&d->rule + sizeof(uint16_t), - &set, sizeof(uint8_t)); - if (set != co.use_set - 1) - continue; - } - width = pr_u64(&d->pcnt, 0); - if (width > pcwidth) - pcwidth = width; - width = pr_u64(&d->bcnt, 0); - if (width > bcwidth) - bcwidth = width; - } + if ((cfg->flags & IPFW_CFG_GET_STATES) && (readsz != sz)) { + /* We may have some dynamic states */ + dynsz = sz - readsz; + /* Skip empty header */ + if (dynsz != sizeof(ipfw_obj_ctlv)) + dynbase = (caddr_t)ctlv; + else + dynsz = 0; } + + prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz); + bp_alloc(&bp, 4096); + /* if no rule numbers were specified, list all rules */ if (ac == 0) { - for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { - if (co.use_set && r->set != co.use_set - 1) - continue; - show_ipfw(r, pcwidth, bcwidth); - } + fo->first = 0; + fo->last = IPFW_DEFAULT_RULE; + list_static_range(co, fo, &bp, rbase, rcnt); - if (co.do_dynamic && ndyn) { - printf("## Dynamic rules (%d):\n", ndyn); - for (n = 0, d = dynrules; n < ndyn; n++, d++) { - if (co.use_set) { - bcopy((char *)&d->rule + sizeof(uint16_t), - &set, sizeof(uint8_t)); - if (set != co.use_set - 1) - continue; - } - show_dyn_ipfw(d, pcwidth, bcwidth); - } + if (co->do_dynamic && dynsz > 0) { + printf("## Dynamic rules (%d %zu):\n", fo->dcnt, dynsz); + list_dyn_range(co, fo, &bp, dynbase, dynsz); } - goto done; + + bp_free(&bp); + return (EX_OK); } /* display specific rules requested on command line */ - for (lac = ac, lav = av; lac != 0; lac--) { /* convert command line rule # */ - last = rnum = strtoul(*lav++, &endptr, 10); + fo->last = fo->first = strtoul(*lav++, &endptr, 10); if (*endptr == '-') - last = strtoul(endptr+1, &endptr, 10); + fo->last = strtoul(endptr + 1, &endptr, 10); if (*endptr) { exitval = EX_USAGE; warnx("invalid rule number: %s", *(lav - 1)); continue; } - for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) { - if (r->rulenum > last) - break; - if (co.use_set && r->set != co.use_set - 1) - continue; - if (r->rulenum >= rnum && r->rulenum <= last) { - show_ipfw(r, pcwidth, bcwidth); - seen = 1; - } - } - if (!seen) { + + if (list_static_range(co, fo, &bp, rbase, rcnt) == 0) { /* give precedence to other error(s) */ if (exitval == EX_OK) exitval = EX_UNAVAILABLE; - warnx("rule %lu does not exist", rnum); + if (fo->first == fo->last) + warnx("rule %u does not exist", fo->first); + else + warnx("no rules in range %u-%u", + fo->first, fo->last); } } - if (co.do_dynamic && ndyn) { + if (co->do_dynamic && dynsz > 0) { printf("## Dynamic rules:\n"); for (lac = ac, lav = av; lac != 0; lac--) { - last = rnum = strtoul(*lav++, &endptr, 10); + fo->last = fo->first = strtoul(*lav++, &endptr, 10); if (*endptr == '-') - last = strtoul(endptr+1, &endptr, 10); + fo->last = strtoul(endptr+1, &endptr, 10); if (*endptr) /* already warned */ continue; - for (n = 0, d = dynrules; n < ndyn; n++, d++) { - uint16_t rulenum; - - bcopy(&d->rule, &rulenum, sizeof(rulenum)); - if (rulenum > rnum) - break; - if (co.use_set) { - bcopy((char *)&d->rule + sizeof(uint16_t), - &set, sizeof(uint8_t)); - if (set != co.use_set - 1) - continue; - } - if (r->rulenum >= rnum && r->rulenum <= last) - show_dyn_ipfw(d, pcwidth, bcwidth); - } + list_dyn_range(co, fo, &bp, dynbase, dynsz); } } - ac = 0; + bp_free(&bp); + return (exitval); +} -done: - free(data); - if (exitval != EX_OK) - exit(exitval); -#undef NEXT +/* + * Retrieves current ipfw configuration of given type + * and stores its pointer to @pcfg. + * + * Caller is responsible for freeing @pcfg. + * + * Returns 0 on success. + */ + +static int +ipfw_get_config(struct cmdline_opts *co, struct format_opts *fo, + ipfw_cfg_lheader **pcfg, size_t *psize) +{ + ipfw_cfg_lheader *cfg; + size_t sz; + int i; + + + if (co->test_only != 0) { + fprintf(stderr, "Testing only, list disabled\n"); + return (0); + } + + /* Start with some data size */ + sz = 4096; + cfg = NULL; + + for (i = 0; i < 16; i++) { + if (cfg != NULL) + free(cfg); + if ((cfg = calloc(1, sz)) == NULL) + return (ENOMEM); + + cfg->flags = fo->flags; + cfg->start_rule = fo->first; + cfg->end_rule = fo->last; + + if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) != 0) { + if (errno != ENOMEM) { + free(cfg); + return (errno); + } + + /* Buffer size is not enough. Try to increase */ + sz = sz * 2; + if (sz < cfg->size) + sz = cfg->size; + continue; + } + + *pcfg = cfg; + *psize = sz; + return (0); + } + + free(cfg); + return (ENOMEM); } static int @@ -2189,6 +2710,79 @@ lookup_host (char *host, struct in_addr *ipaddr) return(0); } +struct tidx { + ipfw_obj_ntlv *idx; + uint32_t count; + uint32_t size; + uint16_t counter; + uint8_t set; +}; + +static uint16_t +pack_table(struct tidx *tstate, char *name) +{ + int i; + ipfw_obj_ntlv *ntlv; + + if (table_check_name(name) != 0) + return (0); + + for (i = 0; i < tstate->count; i++) { + if (strcmp(tstate->idx[i].name, name) != 0) + continue; + if (tstate->idx[i].set != tstate->set) + continue; + + return (tstate->idx[i].idx); + } + + if (tstate->count + 1 > tstate->size) { + tstate->size += 4; + tstate->idx = realloc(tstate->idx, tstate->size * + sizeof(ipfw_obj_ntlv)); + if (tstate->idx == NULL) + return (0); + } + + ntlv = &tstate->idx[i]; + memset(ntlv, 0, sizeof(ipfw_obj_ntlv)); + strlcpy(ntlv->name, name, sizeof(ntlv->name)); + ntlv->head.type = IPFW_TLV_TBL_NAME; + ntlv->head.length = sizeof(ipfw_obj_ntlv); + ntlv->set = tstate->set; + ntlv->idx = ++tstate->counter; + tstate->count++; + + return (ntlv->idx); +} + +static void +fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate) +{ + uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; + uint16_t uidx; + char *p; + + if ((p = strchr(av + 6, ')')) == NULL) + errx(EX_DATAERR, "forgotten parenthesis: '%s'", av); + *p = '\0'; + p = strchr(av + 6, ','); + if (p) + *p++ = '\0'; + + if ((uidx = pack_table(tstate, av + 6)) == 0) + errx(EX_DATAERR, "Invalid table name: %s", av + 6); + + cmd->opcode = opcode; + cmd->arg1 = uidx; + if (p) { + cmd->len |= F_INSN_SIZE(ipfw_insn_u32); + d[0] = strtoul(p, NULL, 0); + } else + cmd->len |= F_INSN_SIZE(ipfw_insn); +} + + /* * fills the addr and mask fields in the instruction as appropriate from av. * Update length as appropriate. @@ -2201,11 +2795,10 @@ lookup_host (char *host, struct in_addr *ipaddr) * We can have multiple comma-separated address/mask entries. */ static void -fill_ip(ipfw_insn_ip *cmd, char *av, int cblen) +fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate) { int len = 0; uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; - uint32_t tables_max; cmd->o.len &= ~F_LEN_MASK; /* zero len */ @@ -2218,21 +2811,7 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen) } if (strncmp(av, "table(", 6) == 0) { - char *p = strchr(av + 6, ','); - - if (p) - *p++ = '\0'; - cmd->o.opcode = O_IP_DST_LOOKUP; - cmd->o.arg1 = strtoul(av + 6, NULL, 0); - tables_max = ipfw_get_tables_max(); - if (cmd->o.arg1 > tables_max) - errx(EX_USAGE, "The table number exceeds the maximum " - "allowed value (%u)", tables_max - 1); - if (p) { - cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); - d[0] = strtoul(p, NULL, 0); - } else - cmd->o.len |= F_INSN_SIZE(ipfw_insn); + fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate); return; } @@ -2413,35 +2992,16 @@ n2mask(struct in6_addr *mask, int n) return; } -/* - * helper function to process a set of flags and set bits in the - * appropriate masks. - */ static void -fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, +fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, struct _s_x *flags, char *p) { - uint8_t set=0, clear=0; + char *e; + uint32_t set = 0, clear = 0; - while (p && *p) { - char *q; /* points to the separator */ - int val; - uint8_t *which; /* mask we are working on */ + if (fill_flags(flags, p, &e, &set, &clear) != 0) + errx(EX_DATAERR, "invalid flag %s", e); - if (*p == '!') { - p++; - which = &clear; - } else - which = &set; - q = strchr(p, ','); - if (q) - *q++ = '\0'; - val = match_token(flags, p); - if (val <= 0) - errx(EX_DATAERR, "invalid flag %s", p); - *which |= (uint8_t)val; - p = q; - } cmd->opcode = opcode; cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); @@ -2451,13 +3011,14 @@ fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, void ipfw_delete(char *av[]) { - uint32_t rulenum; int i; int exitval = EX_OK; int do_set = 0; + ipfw_range_tlv rt; av++; NEED1("missing rule specification"); + memset(&rt, 0, sizeof(rt)); if ( *av && _substrcmp(*av, "set") == 0) { /* Do not allow using the following syntax: * ipfw set N delete set M @@ -2480,16 +3041,34 @@ ipfw_delete(char *av[]) } else if (co.do_pipe) { exitval = ipfw_delete_pipe(co.do_pipe, i); } else { - if (co.use_set) - rulenum = (i & 0xffff) | (5 << 24) | - ((co.use_set - 1) << 16); - else - rulenum = (i & 0xffff) | (do_set << 24); - i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum); - if (i) { + if (do_set != 0) { + rt.set = i & 31; + rt.flags = IPFW_RCFLAG_SET; + } else { + rt.start_rule = i & 0xffff; + rt.end_rule = i & 0xffff; + if (rt.start_rule == 0 && rt.end_rule == 0) + rt.flags |= IPFW_RCFLAG_ALL; + else + rt.flags |= IPFW_RCFLAG_RANGE; + if (co.use_set != 0) { + rt.set = co.use_set - 1; + rt.flags |= IPFW_RCFLAG_SET; + } + } + i = do_range_cmd(IP_FW_XDEL, &rt); + if (i != 0) { exitval = EX_UNAVAILABLE; - warn("rule %u: setsockopt(IP_FW_DEL)", - rulenum); + warn("rule %u: setsockopt(IP_FW_XDEL)", + rt.start_rule); + } else if (rt.new_set == 0) { + exitval = EX_UNAVAILABLE; + if (rt.start_rule != rt.end_rule) + warnx("no rules rules in %u-%u range", + rt.start_rule, rt.end_rule); + else + warnx("rule %u not found", + rt.start_rule); } } } @@ -2506,8 +3085,11 @@ ipfw_delete(char *av[]) * patterns which match interfaces. */ static void -fill_iface(ipfw_insn_if *cmd, char *arg, int cblen) +fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate) { + char *p; + uint16_t uidx; + cmd->name[0] = '\0'; cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); @@ -2517,11 +3099,17 @@ fill_iface(ipfw_insn_if *cmd, char *arg, int cblen) if (strcmp(arg, "any") == 0) cmd->o.len = 0; /* effectively ignore this command */ else if (strncmp(arg, "table(", 6) == 0) { - char *p = strchr(arg + 6, ','); + if ((p = strchr(arg + 6, ')')) == NULL) + errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg); + *p = '\0'; + p = strchr(arg + 6, ','); if (p) *p++ = '\0'; + if ((uidx = pack_table(tstate, arg + 6)) == 0) + errx(EX_DATAERR, "Invalid table name: %s", arg + 6); + cmd->name[0] = '\1'; /* Special value indicating table */ - cmd->p.glob = strtoul(arg + 6, NULL, 0); + cmd->p.kidx = uidx; } else if (!isdigit(*arg)) { strlcpy(cmd->name, arg, sizeof(cmd->name)); cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; @@ -2735,9 +3323,9 @@ add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop) } static ipfw_insn * -add_srcip(ipfw_insn *cmd, char *av, int cblen) +add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { - fill_ip((ipfw_insn_ip *)cmd, av, cblen); + fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ cmd->opcode = O_IP_SRC_SET; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ @@ -2752,9 +3340,9 @@ add_srcip(ipfw_insn *cmd, char *av, int cblen) } static ipfw_insn * -add_dstip(ipfw_insn *cmd, char *av, int cblen) +add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate) { - fill_ip((ipfw_insn_ip *)cmd, av, cblen); + fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate); if (cmd->opcode == O_IP_DST_SET) /* set */ ; else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ @@ -2768,13 +3356,34 @@ add_dstip(ipfw_insn *cmd, char *av, int cblen) return cmd; } +static struct _s_x f_reserved_keywords[] = { + { "altq", TOK_OR }, + { "//", TOK_OR }, + { "diverted", TOK_OR }, + { "dst-port", TOK_OR }, + { "src-port", TOK_OR }, + { "established", TOK_OR }, + { "keep-state", TOK_OR }, + { "frag", TOK_OR }, + { "icmptypes", TOK_OR }, + { "in", TOK_OR }, + { "out", TOK_OR }, + { "ip6", TOK_OR }, + { "any", TOK_OR }, + { "to", TOK_OR }, + { "via", TOK_OR }, + { "{", TOK_OR }, + { NULL, 0 } /* terminator */ +}; + static ipfw_insn * add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen) { - /* XXX "any" is trapped before. Perhaps "to" */ - if (_substrcmp(av, "any") == 0) { - return NULL; - } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) { + + if (match_token(f_reserved_keywords, av) != -1) + return (NULL); + + if (fill_newports((ipfw_insn_u16 *)cmd, av, proto, cblen)) { /* XXX todo: check that we have a protocol with ports */ cmd->opcode = opcode; return cmd; @@ -2783,7 +3392,7 @@ add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen) } static ipfw_insn * -add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen) +add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) { struct in6_addr a; char *host, *ch, buf[INET6_ADDRSTRLEN]; @@ -2806,7 +3415,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen) /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) - ret = add_srcip(cmd, av, cblen); + ret = add_srcip(cmd, av, cblen, tstate); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; @@ -2814,7 +3423,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen) } static ipfw_insn * -add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen) +add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate) { struct in6_addr a; char *host, *ch, buf[INET6_ADDRSTRLEN]; @@ -2837,7 +3446,7 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen) /* XXX: should check for IPv4, not !IPv6 */ if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || inet_pton(AF_INET6, host, &a) != 1)) - ret = add_dstip(cmd, av, cblen); + ret = add_dstip(cmd, av, cblen, tstate); if (ret == NULL && strcmp(av, "any") != 0) ret = cmd; @@ -2857,7 +3466,7 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen) * */ void -ipfw_add(char *av[]) +compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) { /* * rules are added into the 'rulebuf' and then copied in @@ -2865,13 +3474,13 @@ ipfw_add(char *av[]) * Some things that need to go out of order (prob, action etc.) * go into actbuf[]. */ - static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; + static uint32_t actbuf[255], cmdbuf[255]; int rblen, ablen, cblen; ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; ipfw_insn *first_cmd; /* first match pattern */ - struct ip_fw *rule; + struct ip_fw_rule *rule; /* * various flags used to record that we entered some fields. @@ -2891,14 +3500,14 @@ ipfw_add(char *av[]) bzero(actbuf, sizeof(actbuf)); /* actions go here */ bzero(cmdbuf, sizeof(cmdbuf)); - bzero(rulebuf, sizeof(rulebuf)); + bzero(rbuf, *rbufsize); - rule = (struct ip_fw *)rulebuf; + rule = (struct ip_fw_rule *)rbuf; cmd = (ipfw_insn *)cmdbuf; action = (ipfw_insn *)actbuf; - rblen = sizeof(rulebuf) / sizeof(rulebuf[0]); - rblen -= offsetof(struct ip_fw, cmd) / sizeof(rulebuf[0]); + rblen = *rbufsize / sizeof(uint32_t); + rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t); ablen = sizeof(actbuf) / sizeof(actbuf[0]); cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]); cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1; @@ -2920,6 +3529,7 @@ ipfw_add(char *av[]) if (set < 0 || set > RESVD_SET) errx(EX_DATAERR, "illegal set %s", av[1]); rule->set = set; + tstate->set = set; av += 2; } @@ -3029,7 +3639,7 @@ chkarg: errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); } else if (_substrcmp(*av, "tablearg") == 0) { - action->arg1 = IP_FW_TABLEARG; + action->arg1 = IP_FW_TARG; } else if (i == TOK_DIVERT || i == TOK_TEE) { struct servent *s; setservent(1); @@ -3153,7 +3763,7 @@ chkarg: action->opcode = O_SETFIB; NEED1("missing fib number"); if (_substrcmp(*av, "tablearg") == 0) { - action->arg1 = IP_FW_TABLEARG; + action->arg1 = IP_FW_TARG; } else { action->arg1 = strtoul(*av, NULL, 10); if (sysctlbyname("net.fibs", &numfibs, &intsize, @@ -3161,6 +3771,8 @@ chkarg: errx(EX_DATAERR, "fibs not suported.\n"); if (action->arg1 >= numfibs) /* Temporary */ errx(EX_DATAERR, "fib too large.\n"); + /* Add high-order bit to fib to make room for tablearg*/ + action->arg1 |= 0x8000; } av++; break; @@ -3173,13 +3785,16 @@ chkarg: action->opcode = O_SETDSCP; NEED1("missing DSCP code"); if (_substrcmp(*av, "tablearg") == 0) { - action->arg1 = IP_FW_TABLEARG; + action->arg1 = IP_FW_TARG; } else if (isalpha(*av[0])) { if ((code = match_token(f_ipdscp, *av)) == -1) errx(EX_DATAERR, "Unknown DSCP code"); action->arg1 = code; } else action->arg1 = strtoul(*av, NULL, 10); + /* Add high-order bit to DSCP to make room for tablearg */ + if (action->arg1 != IP_FW_TARG) + action->arg1 |= 0x8000; av++; break; } @@ -3386,7 +4001,7 @@ chkarg: OR_START(source_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing source address"); - if (add_src(cmd, *av, proto, cblen)) { + if (add_src(cmd, *av, proto, cblen, tstate)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; @@ -3422,7 +4037,7 @@ chkarg: OR_START(dest_ip); NOT_BLOCK; /* optional "not" */ NEED1("missing dst address"); - if (add_dst(cmd, *av, proto, cblen)) { + if (add_dst(cmd, *av, proto, cblen, tstate)) { av++; if (F_LEN(cmd) != 0) { /* ! any */ prev = cmd; @@ -3451,7 +4066,7 @@ read_options: * nothing specified so far, store in the rule to ease * printout later. */ - rule->_pad = 1; + rule->flags |= IPFW_RULE_NOOPT; } prev = NULL; while ( av[0] != NULL ) { @@ -3529,7 +4144,7 @@ read_options: case TOK_VIA: NEED1("recv, xmit, via require interface name" " or address"); - fill_iface((ipfw_insn_if *)cmd, av[0], cblen); + fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate); av++; if (F_LEN(cmd) == 0) /* not a valid address */ break; @@ -3604,13 +4219,13 @@ read_options: case TOK_IPOPTS: NEED1("missing argument for ipoptions"); - fill_flags(cmd, O_IPOPT, f_ipopts, *av); + fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av); av++; break; case TOK_IPTOS: NEED1("missing argument for iptos"); - fill_flags(cmd, O_IPTOS, f_iptos, *av); + fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av); av++; break; @@ -3688,7 +4303,7 @@ read_options: case TOK_TCPOPTS: NEED1("missing argument for tcpoptions"); - fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); + fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av); av++; break; @@ -3715,7 +4330,7 @@ read_options: case TOK_TCPFLAGS: NEED1("missing argument for tcpflags"); cmd->opcode = O_TCPFLAGS; - fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av); + fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av); av++; break; @@ -3775,14 +4390,14 @@ read_options: case TOK_SRCIP: NEED1("missing source IP"); - if (add_srcip(cmd, *av, cblen)) { + if (add_srcip(cmd, *av, cblen, tstate)) { av++; } break; case TOK_DSTIP: NEED1("missing destination IP"); - if (add_dstip(cmd, *av, cblen)) { + if (add_dstip(cmd, *av, cblen, tstate)) { av++; } break; @@ -3901,7 +4516,6 @@ read_options: case TOK_LOOKUP: { ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; - char *p; int j; if (!av[0] || !av[1]) @@ -3917,12 +4531,22 @@ read_options: errx(EX_USAGE, "format: cannot lookup on %s", *av); __PAST_END(c->d, 1) = j; // i converted to option av++; - cmd->arg1 = strtoul(*av, &p, 0); - if (p && *p) - errx(EX_USAGE, "format: lookup argument tablenum"); + + if ((j = pack_table(tstate, *av)) == 0) + errx(EX_DATAERR, "Invalid table name: %s", *av); + + cmd->arg1 = j; av++; } break; + case TOK_FLOW: + NEED1("missing table name"); + if (strncmp(*av, "table(", 6) != 0) + errx(EX_DATAERR, + "enclose table name into \"table()\""); + fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate); + av++; + break; default: errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); @@ -4024,34 +4648,143 @@ done: } rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); - i = (char *)dst - (char *)rule; - if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1) - err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD"); - if (!co.do_quiet) - show_ipfw(rule, 0, 0); + *rbufsize = (char *)dst - (char *)rule; +} + +/* + * Adds one or more rules to ipfw chain. + * Data layout: + * Request: + * [ + * ip_fw3_opheader + * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1) + * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3) + * ] + * Reply: + * [ + * ip_fw3_opheader + * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) + * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] + * ] + * + * Rules in reply are modified to store their actual ruleset number. + * + * (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending + * accoring to their idx field and there has to be no duplicates. + * (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending. + * (*3) Each ip_fw structure needs to be aligned to u64 boundary. + */ +void +ipfw_add(char *av[]) +{ + uint32_t rulebuf[1024]; + int rbufsize, default_off, tlen, rlen; + size_t sz; + struct tidx ts; + struct ip_fw_rule *rule; + caddr_t tbuf; + ip_fw3_opheader *op3; + ipfw_obj_ctlv *ctlv, *tstate; + + rbufsize = sizeof(rulebuf); + memset(rulebuf, 0, rbufsize); + memset(&ts, 0, sizeof(ts)); + + /* Optimize case with no tables */ + default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader); + op3 = (ip_fw3_opheader *)rulebuf; + ctlv = (ipfw_obj_ctlv *)(op3 + 1); + rule = (struct ip_fw_rule *)(ctlv + 1); + rbufsize -= default_off; + + compile_rule(av, (uint32_t *)rule, &rbufsize, &ts); + /* Align rule size to u64 boundary */ + rlen = roundup2(rbufsize, sizeof(uint64_t)); + + tbuf = NULL; + sz = 0; + tstate = NULL; + if (ts.count != 0) { + /* Some tables. We have to alloc more data */ + tlen = ts.count * sizeof(ipfw_obj_ntlv); + sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen; + + if ((tbuf = calloc(1, sz)) == NULL) + err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD"); + op3 = (ip_fw3_opheader *)tbuf; + /* Tables first */ + ctlv = (ipfw_obj_ctlv *)(op3 + 1); + ctlv->head.type = IPFW_TLV_TBLNAME_LIST; + ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen; + ctlv->count = ts.count; + ctlv->objsize = sizeof(ipfw_obj_ntlv); + memcpy(ctlv + 1, ts.idx, tlen); + table_sort_ctlv(ctlv); + tstate = ctlv; + /* Rule next */ + ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length); + ctlv->head.type = IPFW_TLV_RULE_LIST; + ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; + ctlv->count = 1; + memcpy(ctlv + 1, rule, rbufsize); + } else { + /* Simply add header */ + sz = rlen + default_off; + memset(ctlv, 0, sizeof(*ctlv)); + ctlv->head.type = IPFW_TLV_RULE_LIST; + ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen; + ctlv->count = 1; + } + + if (do_get3(IP_FW_XADD, op3, &sz) != 0) + err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD"); + + if (!co.do_quiet) { + struct format_opts sfo; + struct buf_pr bp; + memset(&sfo, 0, sizeof(sfo)); + sfo.tstate = tstate; + sfo.set_mask = (uint32_t)(-1); + bp_alloc(&bp, 4096); + show_static_rule(&co, &sfo, &bp, rule, NULL); + printf("%s", bp.buf); + bp_free(&bp); + } + + if (tbuf != NULL) + free(tbuf); + + if (ts.idx != NULL) + free(ts.idx); } /* * clear the counters or the log counters. + * optname has the following values: + * 0 (zero both counters and logging) + * 1 (zero logging only) */ void -ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */) +ipfw_zero(int ac, char *av[], int optname) { - uint32_t arg, saved_arg; + ipfw_range_tlv rt; + uint32_t arg; int failed = EX_OK; char const *errstr; char const *name = optname ? "RESETLOG" : "ZERO"; - optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO; + optname = optname ? IP_FW_XRESETLOG : IP_FW_XZERO; + memset(&rt, 0, sizeof(rt)); av++; ac--; - if (!ac) { + if (ac == 0) { /* clear all entries */ - if (do_cmd(optname, NULL, 0) < 0) - err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name); + rt.flags = IPFW_RCFLAG_ALL; + if (do_range_cmd(optname, &rt) < 0) + err(EX_UNAVAILABLE, "setsockopt(IP_FW_X%s)", name); if (!co.do_quiet) - printf("%s.\n", optname == IP_FW_ZERO ? + printf("%s.\n", optname == IP_FW_XZERO ? "Accounting cleared":"Logging counts reset"); return; @@ -4064,22 +4797,28 @@ ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG if (errstr) errx(EX_DATAERR, "invalid rule number %s\n", *av); - saved_arg = arg; - if (co.use_set) - arg |= (1 << 24) | ((co.use_set - 1) << 16); - av++; - ac--; - if (do_cmd(optname, &arg, sizeof(arg))) { - warn("rule %u: setsockopt(IP_FW_%s)", - saved_arg, name); + rt.start_rule = arg; + rt.end_rule = arg; + rt.flags |= IPFW_RCFLAG_RANGE; + if (co.use_set != 0) { + rt.set = co.use_set - 1; + rt.flags |= IPFW_RCFLAG_SET; + } + if (do_range_cmd(optname, &rt) != 0) { + warn("rule %u: setsockopt(IP_FW_X%s)", + arg, name); + failed = EX_UNAVAILABLE; + } else if (rt.new_set == 0) { + printf("Entry %d not found\n", arg); failed = EX_UNAVAILABLE; } else if (!co.do_quiet) - printf("Entry %d %s.\n", saved_arg, - optname == IP_FW_ZERO ? + printf("Entry %d %s.\n", arg, + optname == IP_FW_XZERO ? "cleared" : "logging count reset"); } else { errx(EX_USAGE, "invalid rule number ``%s''", *av); } + av++; ac--; } if (failed != EX_OK) exit(failed); @@ -4088,7 +4827,7 @@ ipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG void ipfw_flush(int force) { - int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH; + ipfw_range_tlv rt; if (!force && !co.do_quiet) { /* need to ask user */ int c; @@ -4110,316 +4849,121 @@ ipfw_flush(int force) return; } /* `ipfw set N flush` - is the same that `ipfw delete set N` */ - if (co.use_set) { - uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24); - if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0) - err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)"); - } else if (do_cmd(cmd, NULL, 0) < 0) - err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)", - co.do_pipe ? "DUMMYNET" : "FW"); + memset(&rt, 0, sizeof(rt)); + if (co.use_set != 0) { + rt.set = co.use_set - 1; + rt.flags = IPFW_RCFLAG_SET; + } else + rt.flags = IPFW_RCFLAG_ALL; + if (do_range_cmd(IP_FW_XDEL, &rt) != 0) + err(EX_UNAVAILABLE, "setsockopt(IP_FW_XDEL)"); if (!co.do_quiet) printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules"); } +static struct _s_x intcmds[] = { + { "talist", TOK_TALIST }, + { "iflist", TOK_IFLIST }, + { "vlist", TOK_VLIST }, + { NULL, 0 } +}; -static void table_list(uint16_t num, int need_header); -static void table_fill_xentry(char *arg, ipfw_table_xentry *xent); - -/* - * Retrieve maximum number of tables supported by ipfw(4) module. - */ -uint32_t -ipfw_get_tables_max() -{ - size_t len; - uint32_t tables_max; - - if (ipfw_tables_max != 0) - return (ipfw_tables_max); - - len = sizeof(tables_max); - if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, - NULL, 0) == -1) { - if (co.test_only) - tables_max = 128; /* Old conservative default */ - else - errx(1, "Can't determine maximum number of ipfw tables." - " Perhaps you forgot to load ipfw module?"); - } - - ipfw_tables_max = tables_max; - - return (ipfw_tables_max); -} - -/* - * This one handles all table-related commands - * ipfw table N add addr[/masklen] [value] - * ipfw table N delete addr[/masklen] - * ipfw table {N | all} flush - * ipfw table {N | all} list - */ void -ipfw_table_handler(int ac, char *av[]) +ipfw_internal_handler(int ac, char *av[]) { - ipfw_table_xentry xent; - int do_add; - int is_all; - uint32_t a; - uint32_t tables_max; + int tcmd; - tables_max = ipfw_get_tables_max(); + ac--; av++; + NEED1("internal cmd required"); - memset(&xent, 0, sizeof(xent)); + if ((tcmd = match_token(intcmds, *av)) == -1) + errx(EX_USAGE, "invalid internal sub-cmd: %s", *av); - ac--; av++; - if (ac && isdigit(**av)) { - xent.tbl = atoi(*av); - is_all = 0; - ac--; av++; - } else if (ac && _substrcmp(*av, "all") == 0) { - xent.tbl = 0; - is_all = 1; - ac--; av++; - } else - errx(EX_USAGE, "table number or 'all' keyword required"); - if (xent.tbl >= tables_max) - errx(EX_USAGE, "The table number exceeds the maximum allowed " - "value (%d)", tables_max - 1); - NEED1("table needs command"); - if (is_all && _substrcmp(*av, "list") != 0 - && _substrcmp(*av, "flush") != 0) - errx(EX_USAGE, "table number required"); - - if (_substrcmp(*av, "add") == 0 || - _substrcmp(*av, "delete") == 0) { - do_add = **av == 'a'; - ac--; av++; - if (!ac) - errx(EX_USAGE, "address required"); - - table_fill_xentry(*av, &xent); - - ac--; av++; - if (do_add && ac) { - unsigned int tval; - /* isdigit is a bit of a hack here.. */ - if (strchr(*av, (int)'.') == NULL && isdigit(**av)) { - xent.value = strtoul(*av, NULL, 0); - } else { - if (lookup_host(*av, (struct in_addr *)&tval) == 0) { - /* The value must be stored in host order * - * so that the values < 65k can be distinguished */ - xent.value = ntohl(tval); - } else { - errx(EX_NOHOST, "hostname ``%s'' unknown", *av); - } - } - } else - xent.value = 0; - if (do_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL, - &xent, xent.len) < 0) { - /* If running silent, don't bomb out on these errors. */ - if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH)))) - err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)", - do_add ? "XADD" : "XDEL"); - /* In silent mode, react to a failed add by deleting */ - if (do_add) { - do_setcmd3(IP_FW_TABLE_XDEL, &xent, xent.len); - if (do_setcmd3(IP_FW_TABLE_XADD, &xent, xent.len) < 0) - err(EX_OSERR, - "setsockopt(IP_FW_TABLE_XADD)"); - } - } - } else if (_substrcmp(*av, "flush") == 0) { - a = is_all ? tables_max : (uint32_t)(xent.tbl + 1); - do { - if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl, - sizeof(xent.tbl)) < 0) - err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)"); - } while (++xent.tbl < a); - } else if (_substrcmp(*av, "list") == 0) { - a = is_all ? tables_max : (uint32_t)(xent.tbl + 1); - do { - table_list(xent.tbl, is_all); - } while (++xent.tbl < a); - } else - errx(EX_USAGE, "invalid table command %s", *av); + switch (tcmd) { + case TOK_IFLIST: + ipfw_list_tifaces(); + break; + case TOK_TALIST: + ipfw_list_ta(ac, av); + break; + case TOK_VLIST: + ipfw_list_values(ac, av); + break; + } } -static void -table_fill_xentry(char *arg, ipfw_table_xentry *xent) +static int +ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh) { - int addrlen, mask, masklen, type; - struct in6_addr *paddr; - uint32_t *pkey; - char *p; - uint32_t key; - - mask = 0; - type = 0; - addrlen = 0; - masklen = 0; - - /* - * Let's try to guess type by agrument. - * Possible types: - * 1) IPv4[/mask] - * 2) IPv6[/mask] - * 3) interface name - * 4) port, uid/gid or other u32 key (base 10 format) - * 5) hostname - */ - paddr = &xent->k.addr6; - if (ishexnumber(*arg) != 0 || *arg == ':') { - /* Remove / if exists */ - if ((p = strchr(arg, '/')) != NULL) { - *p = '\0'; - mask = atoi(p + 1); - } - - if (inet_pton(AF_INET, arg, paddr) == 1) { - if (p != NULL && mask > 32) - errx(EX_DATAERR, "bad IPv4 mask width: %s", - p + 1); + ipfw_obj_lheader req, *olh; + size_t sz; - type = IPFW_TABLE_CIDR; - masklen = p ? mask : 32; - addrlen = sizeof(struct in_addr); - } else if (inet_pton(AF_INET6, arg, paddr) == 1) { - if (IN6_IS_ADDR_V4COMPAT(paddr)) - errx(EX_DATAERR, - "Use IPv4 instead of v4-compatible"); - if (p != NULL && mask > 128) - errx(EX_DATAERR, "bad IPv6 mask width: %s", - p + 1); - - type = IPFW_TABLE_CIDR; - masklen = p ? mask : 128; - addrlen = sizeof(struct in6_addr); - } else { - /* Port or any other key */ - /* Skip non-base 10 entries like 'fa1' */ - key = strtol(arg, &p, 10); - if (*p == '\0') { - pkey = (uint32_t *)paddr; - *pkey = htonl(key); - type = IPFW_TABLE_CIDR; - masklen = 32; - addrlen = sizeof(uint32_t); - } else if ((p != arg) && (*p == '.')) { - /* - * Warn on IPv4 address strings - * which are "valid" for inet_aton() but not - * in inet_pton(). - * - * Typical examples: '10.5' or '10.0.0.05' - */ - errx(EX_DATAERR, - "Invalid IPv4 address: %s", arg); - } - } - } + memset(&req, 0, sizeof(req)); + sz = sizeof(req); - if (type == 0 && strchr(arg, '.') == NULL) { - /* Assume interface name. Copy significant data only */ - mask = MIN(strlen(arg), IF_NAMESIZE - 1); - memcpy(xent->k.iface, arg, mask); - /* Set mask to exact match */ - masklen = 8 * IF_NAMESIZE; - type = IPFW_TABLE_INTERFACE; - addrlen = IF_NAMESIZE; + if (do_get3(IP_FW_XIFLIST, &req.opheader, &sz) != 0) { + if (errno != ENOMEM) + return (errno); } - if (type == 0) { - if (lookup_host(arg, (struct in_addr *)paddr) != 0) - errx(EX_NOHOST, "hostname ``%s'' unknown", arg); + sz = req.size; + if ((olh = calloc(1, sz)) == NULL) + return (ENOMEM); - masklen = 32; - type = IPFW_TABLE_CIDR; - addrlen = sizeof(struct in_addr); + olh->size = sz; + if (do_get3(IP_FW_XIFLIST, &olh->opheader, &sz) != 0) { + free(olh); + return (errno); } - xent->type = type; - xent->masklen = masklen; - xent->len = offsetof(ipfw_table_xentry, k) + addrlen; + *polh = olh; + return (0); } -static void -table_list(uint16_t num, int need_header) +static int +ifinfo_cmp(const void *a, const void *b) { - ipfw_xtable *tbl; - ipfw_table_xentry *xent; - socklen_t l; - uint32_t *a, sz, tval; - char tbuf[128]; - struct in6_addr *addr6; - ip_fw3_opheader *op3; + ipfw_iface_info *ia, *ib; - /* Prepend value with IP_FW3 header */ - l = sizeof(ip_fw3_opheader) + sizeof(uint32_t); - op3 = alloca(l); - /* Zero reserved fields */ - memset(op3, 0, sizeof(ip_fw3_opheader)); - a = (uint32_t *)(op3 + 1); - *a = num; - op3->opcode = IP_FW_TABLE_XGETSIZE; - if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0) - err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)"); - - /* If a is zero we have nothing to do, the table is empty. */ - if (*a == 0) - return; + ia = (ipfw_iface_info *)a; + ib = (ipfw_iface_info *)b; - l = *a; - tbl = safe_calloc(1, l); - tbl->opheader.opcode = IP_FW_TABLE_XLIST; - tbl->tbl = num; - if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0) - err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)"); - if (tbl->cnt && need_header) - printf("---table(%d)---\n", tbl->tbl); - sz = tbl->size - sizeof(ipfw_xtable); - xent = &tbl->xent[0]; - while (sz > 0) { - switch (tbl->type) { - case IPFW_TABLE_CIDR: - /* IPv4 or IPv6 prefixes */ - tval = xent->value; - addr6 = &xent->k.addr6; + return (stringnum_cmp(ia->ifname, ib->ifname)); +} +/* + * Retrieves table list from kernel, + * optionally sorts it and calls requested function for each table. + * Returns 0 on success. + */ +static void +ipfw_list_tifaces() +{ + ipfw_obj_lheader *olh; + ipfw_iface_info *info; + int i, error; - if ((xent->flags & IPFW_TCF_INET) != 0) { - /* IPv4 address */ - inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf)); - } else { - /* IPv6 address */ - inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf)); - } + if ((error = ipfw_get_tracked_ifaces(&olh)) != 0) + err(EX_OSERR, "Unable to request ipfw tracked interface list"); - if (co.do_value_as_ip) { - tval = htonl(tval); - printf("%s/%u %s\n", tbuf, xent->masklen, - inet_ntoa(*(struct in_addr *)&tval)); - } else - printf("%s/%u %u\n", tbuf, xent->masklen, tval); - break; - case IPFW_TABLE_INTERFACE: - /* Interface names */ - tval = xent->value; - if (co.do_value_as_ip) { - tval = htonl(tval); - printf("%s %s\n", xent->k.iface, - inet_ntoa(*(struct in_addr *)&tval)); - } else - printf("%s %u\n", xent->k.iface, tval); - } - if (sz < xent->len) - break; - sz -= xent->len; - xent = (ipfw_table_xentry *)((char *)xent + xent->len); + qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp); + + info = (ipfw_iface_info *)(olh + 1); + for (i = 0; i < olh->count; i++) { + if (info->flags & IPFW_IFFLAG_RESOLVED) + printf("%s ifindex: %d refcount: %u changes: %u\n", + info->ifname, info->ifindex, info->refcnt, + info->gencnt); + else + printf("%s ifindex: unresolved refcount: %u changes: %u\n", + info->ifname, info->refcnt, info->gencnt); + info = (ipfw_iface_info *)((caddr_t)info + olh->objsize); } - free(tbl); + free(olh); } + + + + |