diff --git a/sbin/ipfw/altq.c b/sbin/ipfw/altq.c index 8dced11b72b3..8398ab611f23 100644 --- a/sbin/ipfw/altq.c +++ b/sbin/ipfw/altq.c @@ -137,15 +137,15 @@ altq_qid_to_name(u_int32_t qid) } void -print_altq_cmd(ipfw_insn_altq *altqptr) +print_altq_cmd(struct buf_pr *bp, ipfw_insn_altq *altqptr) { if (altqptr) { const char *qname; qname = altq_qid_to_name(altqptr->qid); if (qname == NULL) - printf(" altq ?<%u>", altqptr->qid); + bprintf(bp, " altq ?<%u>", altqptr->qid); else - printf(" altq %s", qname); + bprintf(bp, " altq %s", qname); } } diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c index cb6285323eed..dc95a1988ffb 100644 --- a/sbin/ipfw/dummynet.c +++ b/sbin/ipfw/dummynet.c @@ -174,48 +174,44 @@ print_header(struct ipfw_flow_id *id) } static void -list_flow(struct dn_flow *ni, int *print) +list_flow(struct buf_pr *bp, struct dn_flow *ni) { char buff[255]; struct protoent *pe = NULL; struct in_addr ina; struct ipfw_flow_id *id = &ni->fid; - if (*print) { - print_header(&ni->fid); - *print = 0; - } pe = getprotobynumber(id->proto); /* XXX: Should check for IPv4 flows */ - printf("%3u%c", (ni->oid.id) & 0xff, + bprintf(bp, "%3u%c", (ni->oid.id) & 0xff, id->extra ? '*' : ' '); if (!IS_IP6_FLOW_ID(id)) { if (pe) - printf("%-4s ", pe->p_name); + bprintf(bp, "%-4s ", pe->p_name); else - printf("%4u ", id->proto); + bprintf(bp, "%4u ", id->proto); ina.s_addr = htonl(id->src_ip); - printf("%15s/%-5d ", + bprintf(bp, "%15s/%-5d ", inet_ntoa(ina), id->src_port); ina.s_addr = htonl(id->dst_ip); - printf("%15s/%-5d ", + bprintf(bp, "%15s/%-5d ", inet_ntoa(ina), id->dst_port); } else { /* Print IPv6 flows */ if (pe != NULL) - printf("%9s ", pe->p_name); + bprintf(bp, "%9s ", pe->p_name); else - printf("%9u ", id->proto); - printf("%7d %39s/%-5d ", id->flow_id6, + bprintf(bp, "%9u ", id->proto); + bprintf(bp, "%7d %39s/%-5d ", id->flow_id6, inet_ntop(AF_INET6, &(id->src_ip6), buff, sizeof(buff)), id->src_port); - printf(" %39s/%-5d ", + bprintf(bp, " %39s/%-5d ", inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)), id->dst_port); } - pr_u64(&ni->tot_pkts, 4); - pr_u64(&ni->tot_bytes, 8); - printf("%2u %4u %3u\n", + pr_u64(bp, &ni->tot_pkts, 4); + pr_u64(bp, &ni->tot_bytes, 8); + bprintf(bp, "%2u %4u %3u", ni->length, ni->len_bytes, ni->drops); } @@ -303,8 +299,10 @@ list_pipes(struct dn_id *oid, struct dn_id *end) { char buf[160]; /* pending buffer */ int toPrint = 1; /* print header */ + struct buf_pr bp; buf[0] = '\0'; + bp_alloc(&bp, 4096); for (; oid != end; oid = O_NEXT(oid, oid->len)) { if (oid->len < sizeof(*oid)) errx(1, "invalid oid len %d\n", oid->len); @@ -346,7 +344,12 @@ list_pipes(struct dn_id *oid, struct dn_id *end) break; case DN_FLOW: - list_flow((struct dn_flow *)oid, &toPrint); + if (toPrint != 0) { + print_header(&((struct dn_flow *)oid)->fid); + toPrint = 0; + } + list_flow(&bp, (struct dn_flow *)oid); + printf("%s\n", bp.buf); break; case DN_LINK: { @@ -384,6 +387,8 @@ list_pipes(struct dn_id *oid, struct dn_id *end) } flush_buf(buf); // XXX does it really go here ? } + + bp_free(&bp); } /* diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index 0faa6f901ecb..9b7e02ade6ba 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,15 @@ struct cmdline_opts co; /* global options */ +struct format_opts { + int bcwidth; + int pcwidth; + int show_counters; + int first; + int last; + ipfw_obj_ctlv *tstate; +}; + int resvd_set_number = RESVD_SET; int ipfw_socket = -1; @@ -364,6 +374,102 @@ 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, uint32_t flags, + 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); + +/* + * 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_TABLEARG) + 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 @@ -371,7 +477,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" @@ -384,11 +490,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) { @@ -866,14 +973,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); } /* @@ -937,7 +1044,7 @@ print_flags(char const *name, ipfw_insn *cmd, struct _s_x *list) * Print the ip address contained in a command. */ static void -print_ip(ipfw_insn_ip *cmd, char const *s) +print_ip(struct format_opts *fo, ipfw_insn_ip *cmd, char const *s) { struct hostent *he = NULL; uint32_t len = F_LEN((ipfw_insn *)cmd); @@ -961,7 +1068,9 @@ print_ip(ipfw_insn_ip *cmd, char const *s) } if (cmd->o.opcode == O_IP_SRC_LOOKUP || cmd->o.opcode == O_IP_DST_LOOKUP) { - printf("table(%u", ((ipfw_insn *)cmd)->arg1); + char *t; + t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1); + printf("table(%s", t); if (len == F_INSN_SIZE(ipfw_insn_u32)) printf(",%u", *a); printf(")"); @@ -1162,7 +1271,8 @@ show_prerequisites(int *flags, int want, int cmd) } 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) { static int twidth = 0; int l; @@ -1178,21 +1288,21 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) bcopy(&rule->next_rule, &set_disable, sizeof(set_disable)); if (set_disable & (1 << rule->set)) { /* disabled */ - if (!co.show_sets) + 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); + if (fo->pcwidth > 0 || fo->bcwidth > 0) { + pr_u64(bp, &rule->pcnt, fo->pcwidth); + pr_u64(bp, &rule->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 ", rule->timestamp); + else if (co->do_time == 1) { char timestr[30]; time_t t = (time_t)0; @@ -1206,14 +1316,14 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 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" @@ -1225,7 +1335,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); } } @@ -1236,66 +1346,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: @@ -1303,12 +1413,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; @@ -1317,10 +1427,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; @@ -1338,13 +1448,13 @@ 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); break; case O_SETDSCP: @@ -1352,46 +1462,52 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) const char *code; if ((code = match_value(f_ipdscp, cmd->arg1)) != NULL) - printf("setdscp %s", code); + bprintf(bp, "setdscp %s", code); else - PRINT_UINT_ARG("setdscp ", cmd->arg1); + bprint_uint_arg(bp, "setdscp ", cmd->arg1); } 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); } + /* + * TODO: convert remainings to use @bp buffer + * + */ + printf("%s", bp->buf); + /* * then print the body. */ @@ -1408,7 +1524,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) } } if (rule->_pad & 1) { /* empty rules before options */ - if (!co.do_compact) { + if (!co->do_compact) { show_prerequisites(&flags, HAVE_PROTO, 0); printf(" from any to any"); } @@ -1416,7 +1532,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) HAVE_SRCIP | HAVE_DSTIP; } - if (co.comment_only) + if (co->comment_only) comment = "..."; for (l = rule->act_ofs, cmd = rule->cmd ; @@ -1424,7 +1540,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) /* 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)); @@ -1450,7 +1566,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) printf(" from"); if ((cmd->len & F_OR) && !or_block) printf(" {"); - print_ip((ipfw_insn_ip *)cmd, + print_ip(fo, (ipfw_insn_ip *)cmd, (flags & HAVE_OPTIONS) ? " src-ip" : ""); flags |= HAVE_SRCIP; break; @@ -1465,7 +1581,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) printf(" to"); if ((cmd->len & F_OR) && !or_block) printf(" {"); - print_ip((ipfw_insn_ip *)cmd, + print_ip(fo, (ipfw_insn_ip *)cmd, (flags & HAVE_OPTIONS) ? " dst-ip" : ""); flags |= HAVE_DSTIP; break; @@ -1610,7 +1726,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 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) @@ -1622,9 +1738,12 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) if (cmdif->name[0] == '\0') printf(" %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 + else if (cmdif->name[0] == '\1') { + /* interface table */ + t = table_search_ctlv(fo->tstate, + cmdif->p.glob); + printf(" %s table(%s)", s, t); + } else printf(" %s %s", s, cmdif->name); break; @@ -1825,57 +1944,56 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) } 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"); - - printf("\n"); + bprintf(bp, " UNKNOWN <-> UNKNOWN\n"); } /* @@ -2026,28 +2144,112 @@ ipfw_sysctl_handler(char *av[], int which) } } +static void +prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, + struct ip_fw *r, ipfw_dyn_rule *d, int rcnt, int dcnt) +{ + int bcwidth, pcwidth, width; + int n; + uint32_t set; +#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r))) + + bcwidth = 0; + pcwidth = 0; + if (fo->show_counters != 0) { + for (n = 0; n < rcnt; 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(NULL, &r->pcnt, 0); + if (width > pcwidth) + pcwidth = width; + + /* byte counter */ + width = pr_u64(NULL, &r->bcnt, 0); + if (width > bcwidth) + bcwidth = width; + } + } + if (co->do_dynamic && dcnt > 0) { + for (n = 0; n < dcnt; 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(NULL, &d->pcnt, 0); + if (width > pcwidth) + pcwidth = width; + + width = pr_u64(NULL, &d->bcnt, 0); + if (width > bcwidth) + bcwidth = width; + } + } + + fo->bcwidth = bcwidth; + fo->pcwidth = pcwidth; +} + +static int +ipfw_list_static_range(struct cmdline_opts *co, struct format_opts *fo, + struct buf_pr *bp, struct ip_fw *r, int nstat) +{ + int n, seen; + + for (n = seen = 0; n < nstat; n++, r = NEXT(r) ) { + 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); + bp_flush(bp); + seen++; + } + } + + return (seen); +} + +static void +ipfw_list_dyn_range(struct cmdline_opts *co, struct format_opts *fo, + struct buf_pr *bp, ipfw_dyn_rule *d, int ndyn) +{ + int n; + uint8_t set; + uint16_t rulenum; + + for (n = 0; n < ndyn; n++, d++) { + bcopy(&d->rule, &rulenum, sizeof(rulenum)); + if (rulenum > fo->last) + break; + if (co->use_set) { + bcopy((char *)&d->rule + sizeof(uint16_t), + &set, sizeof(uint8_t)); + if (set != co->use_set - 1) + continue; + } + if (rulenum >= fo->first) { + show_dyn_state(co, fo, bp, d); + printf("%s\n", bp->buf); + bp_flush(bp); + } + } +} + 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; - int lac; - char **lav; - u_long rnum, last; - 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; + ipfw_cfg_lheader *cfg; + struct format_opts sfo; + size_t sz; + int error; + uint32_t flags; if (co.test_only) { fprintf(stderr, "Testing only, list disabled\n"); @@ -2061,119 +2263,108 @@ ipfw_list(int ac, char *av[], int show_counters) ac--; av++; - /* get rules or pipes from kernel, resizing array as necessary */ - nbytes = nalloc; + /* get configuraion from kernel */ + cfg = NULL; + flags = IPFW_CFG_GET_STATIC; + if (co.do_dynamic != 0) + flags |= IPFW_CFG_GET_STATES; + if ((error = ipfw_get_config(&co, flags, &cfg, &sz)) != 0) + err(EX_OSERR, "retrieving config failed"); - 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"); - } + memset(&sfo, 0, sizeof(sfo)); + sfo.show_counters = show_counters; + error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av); + + free(cfg); + + if (error != EX_OK) + exit(error); +#undef NEXT +} + +static int +ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, + ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[]) +{ + struct ip_fw *rbase; + ipfw_dyn_rule *dynbase; + int rcnt, dcnt; + int exitval = EX_OK; + int lac; + char **lav; + u_long rnum; + char *endptr; + size_t read; + struct buf_pr bp; + ipfw_obj_ctlv *ctlv, *tstate; /* - * Count static rules. They have variable size so we - * need to scan the list to count them. + * Handle tablenames TLV first, if any */ - for (nstat = 1, r = data, lim = (char *)data + nbytes; - r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim; - ++nstat, r = NEXT(r) ) - ; /* nothing */ + tstate = NULL; + rbase = NULL; + dynbase = NULL; + read = 0; - /* - * Count dynamic rules. This is easier as they have - * fixed size. - */ - r = NEXT(r); - dynrules = (ipfw_dyn_rule *)r ; - n = (char *)r - (char *)data; - ndyn = (nbytes - n) / sizeof *dynrules; + ctlv = (ipfw_obj_ctlv *)(cfg + 1); - /* 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; + if (cfg->flags & IPFW_CFG_GET_STATIC) { + /* We've requested static rules */ + if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { + fo->tstate = ctlv; + read += ctlv->head.length; + ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + + ctlv->head.length); + } - /* packet counter */ - width = pr_u64(&r->pcnt, 0); - if (width > pcwidth) - pcwidth = width; - - /* byte counter */ - width = pr_u64(&r->bcnt, 0); - if (width > bcwidth) - bcwidth = width; + if (ctlv->head.type == IPFW_TLV_RULE_LIST) { + rbase = (struct ip_fw *)(ctlv + 1); + rcnt = ctlv->count; + read += 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) && (read != sz)) { + /* We may have some dynamic rules */ + read += sizeof(ipfw_obj_ctlv); + dcnt = ((sz - read) / ctlv->objsize); + if (dcnt != 0) + dynbase = (ipfw_dyn_rule *)(ctlv + 1); } + + prepare_format_opts(co, fo, rbase, dynbase, rcnt, dcnt); + 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; + ipfw_list_static_range(co, fo, &bp, rbase, rcnt); + + if (co->do_dynamic && dcnt) { + printf("## Dynamic rules (%d):\n", dcnt); + ipfw_list_dyn_range(co, fo, &bp, dynbase, dcnt); } - 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); - } - } - 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 (ipfw_list_static_range(co, fo, &bp, rbase, rcnt) == 0) { /* give precedence to other error(s) */ if (exitval == EX_OK) exitval = EX_UNAVAILABLE; @@ -2181,41 +2372,78 @@ ipfw_list(int ac, char *av[], int show_counters) } } - if (co.do_dynamic && ndyn) { + if (co->do_dynamic && dcnt > 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); - } + ipfw_list_dyn_range(co, fo, &bp, dynbase, dcnt); } } - 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, uint32_t flags, + ipfw_cfg_lheader **pcfg, size_t *psize) +{ + ipfw_cfg_lheader *cfg; + size_t sz; + int error, 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 < 10; i++) { + if (cfg != NULL) + free(cfg); + if ((cfg = calloc(1, sz)) == NULL) + return (ENOMEM); + + cfg->flags = flags; + + if (do_get3(IP_FW_XGET, &cfg->opheader, &sz) < 0) { + if (error != ENOMEM) { + free(cfg); + return (error); + } + + /* Buffer size is not enough. Try to increase */ + sz = sz * 2 + 200; + if (sz < cfg->size) + sz = cfg->size + 200; + } + + *pcfg = cfg; + *psize = sz; + return (0); + } + + free(cfg); + return (ENOMEM); } static int @@ -4064,8 +4292,14 @@ ipfw_add(char *av[]) 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); + if (!co.do_quiet) { + struct format_opts sfo; + struct buf_pr bp; + memset(&sfo, 0, sizeof(sfo)); + bp_alloc(&bp, 4096); + show_static_rule(&co, &sfo, &bp, rule); + bp_free(&bp); + } } /* diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 2729aaf49530..a6a7e2c78e4b 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -213,7 +213,19 @@ enum tokens { #define NEED(_p, msg) {if (!_p) errx(EX_USAGE, msg);} #define NEED1(msg) {if (!(*av)) errx(EX_USAGE, msg);} -int pr_u64(uint64_t *pd, int width); +struct buf_pr { + char *buf; /* allocated buffer */ + char *ptr; /* current pointer */ + size_t size; /* total buffer size */ + size_t avail; /* available storage */ + size_t needed; /* length needed */ +}; + +int pr_u64(struct buf_pr *bp, uint64_t *pd, int width); +int bp_alloc(struct buf_pr *b, size_t size); +void bp_free(struct buf_pr *b); +int bprintf(struct buf_pr *b, char *format, ...); + /* memory allocation support */ void *safe_calloc(size_t number, size_t size); @@ -274,7 +286,7 @@ void ipfw_list(int ac, char *av[], int show_counters); /* altq.c */ void altq_set_enabled(int enabled); u_int32_t altq_name_to_qid(const char *name); -void print_altq_cmd(struct _ipfw_insn_altq *altqptr); +void print_altq_cmd(struct buf_pr *bp, struct _ipfw_insn_altq *altqptr); #else #define NO_ALTQ #endif @@ -298,3 +310,8 @@ void fill_flow6(struct _ipfw_insn_u32 *cmd, char *av, int cblen); void fill_unreach6_code(u_short *codep, char *str); void fill_icmp6types(struct _ipfw_insn_icmp6 *cmd, char *av, int cblen); int fill_ext6hdr(struct _ipfw_insn *cmd, char *av); + +/* tables.c */ +struct _ipfw_obj_ctlv; +char *table_search_ctlv(struct _ipfw_obj_ctlv *ctlv, uint16_t idx); + diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c index 491ac86ec88e..6b4eff2e2bc6 100644 --- a/sbin/ipfw/tables.c +++ b/sbin/ipfw/tables.c @@ -305,7 +305,7 @@ static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx) { - ntlv->head.type = IPFW_TLV_NAME; + ntlv->head.type = IPFW_TLV_TBL_NAME; ntlv->head.length = sizeof(ipfw_obj_ntlv); ntlv->idx = uidx; strlcpy(ntlv->name, name, sizeof(ntlv->name)); @@ -593,3 +593,43 @@ table_show_list(ipfw_obj_header *oh, int need_header) } } + +int +compare_ntlv(const void *k, const void *v) +{ + ipfw_obj_ntlv *ntlv; + uint16_t key; + + key = *((uint16_t *)k); + ntlv = (ipfw_obj_ntlv *)v; + + if (key < ntlv->idx) + return (-1); + else if (key > ntlv->idx) + return (1); + + return (0); +} + +/* + * Finds table name in @ctlv by @idx. + * Uses the following facts: + * 1) All TLVs are the same size + * 2) Kernel implementation provides already sorted list. + * + * Returns table name or NULL. + */ +char * +table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx) +{ + ipfw_obj_ntlv *ntlv; + + ntlv = bsearch(&idx, (ctlv + 1), ctlv->count, ctlv->objsize, + compare_ntlv); + + if (ntlv != 0) + return (ntlv->name); + + return (NULL); +} + diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index efd23010818e..51def3ba2b86 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -87,6 +87,7 @@ typedef struct _ip_fw3_opheader { #define IP_FW_TABLE_XFLUSH 94 /* flush table data */ #define IP_FW_TABLE_XCREATE 95 /* create new table */ #define IP_FW_TABLE_XMODIFY 96 /* modify existing table */ +#define IP_FW_XGET 97 /* Retrieve configuration */ /* * Usage guidelines: @@ -681,18 +682,30 @@ typedef struct _ipfw_xtable { typedef struct _ipfw_obj_tlv { uint16_t type; /* TLV type */ - uint16_t length; /* Total length, aligned to u32 */ + uint16_t flags; /* unused */ + uint32_t length; /* Total length, aligned to u64 */ } ipfw_obj_tlv; +#define IPFW_TLV_TBL_NAME 1 +#define IPFW_TLV_TBLNAME_LIST 2 +#define IPFW_TLV_RULE_LIST 3 +#define IPFW_TLV_STATE_LIST 4 -#define IPFW_TLV_NAME 1 /* Object name TLV */ typedef struct _ipfw_obj_ntlv { - ipfw_obj_tlv head; /* TLV header */ - uint16_t idx; /* Name index */ - uint16_t spare; /* unused */ - char name[64]; /* Null-terminated name */ + ipfw_obj_tlv head; /* TLV header */ + uint16_t idx; /* Name index */ + uint16_t spare0; /* unused */ + uint32_t spare1; /* unused */ + char name[64]; /* Null-terminated name */ } ipfw_obj_ntlv; +/* Containter TLVs */ +typedef struct _ipfw_obj_ctlv { + ipfw_obj_tlv head; /* TLV header */ + uint32_t count; /* Number of sub-TLVs */ + uint32_t objsize; /* Single object size */ +} ipfw_obj_ctlv; + typedef struct _ipfw_xtable_info { uint8_t type; /* table type (cidr,iface,..) */ uint8_t ftype; /* table value format type */ @@ -725,4 +738,15 @@ typedef struct _ipfw_obj_lheader { uint32_t objsize; /* Size of one object */ } ipfw_obj_lheader; +#define IPFW_CFG_GET_STATIC 1 +#define IPFW_CFG_GET_STATES 2 +typedef struct _ipfw_cfg_lheader { + ip_fw3_opheader opheader; /* IP_FW3 opcode */ + uint32_t set_mask; /* disabled set mask */ + uint32_t flags; /* Request flags */ + uint32_t size; /* neded buffer size */ + uint32_t start_rule; + uint32_t end_rule; +} ipfw_cfg_lheader; + #endif /* _IPFW2_H */ diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c index af546f43fc20..a59afaa6636a 100644 --- a/sys/netpfil/ipfw/ip_fw_dynamic.c +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -1454,6 +1454,74 @@ ipfw_dyn_len(void) (DYN_COUNT * sizeof(ipfw_dyn_rule)); } +static void +export_dyn_rule(ipfw_dyn_rule *src, ipfw_dyn_rule *dst) +{ + + memcpy(dst, src, sizeof(*src)); + memcpy(&(dst->rule), &(src->rule->rulenum), sizeof(src->rule->rulenum)); + /* + * store set number into high word of + * dst->rule pointer. + */ + memcpy((char *)&dst->rule + sizeof(src->rule->rulenum), + &(src->rule->set), sizeof(src->rule->set)); + /* + * store a non-null value in "next". + * The userland code will interpret a + * NULL here as a marker + * for the last dynamic rule. + */ + memcpy(&dst->next, &dst, sizeof(dst)); + dst->expire = + TIME_LEQ(dst->expire, time_uptime) ? 0 : dst->expire - time_uptime; +} + +/* + * Fills int buffer given by @sd with dynamic states. + * + * Returns 0 on success. + */ +int +ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd) +{ + ipfw_dyn_rule *p, *dst, *last = NULL; + ipfw_obj_ctlv *ctlv; + int i; + + if (V_ipfw_dyn_v == NULL) + return (0); + + IPFW_UH_RLOCK_ASSERT(chain); + + ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv)); + if (ctlv == NULL) + return (ENOMEM); + ctlv->head.type = IPFW_TLV_TBLNAME_LIST; + ctlv->objsize = sizeof(ipfw_dyn_rule); + + for (i = 0 ; i < V_curr_dyn_buckets; i++) { + IPFW_BUCK_LOCK(i); + for (p = V_ipfw_dyn_v[i].head ; p != NULL; p = p->next) { + dst = (ipfw_dyn_rule *)ipfw_get_sopt_space(sd, + sizeof(*dst)); + if (dst == NULL) { + IPFW_BUCK_UNLOCK(i); + return (ENOMEM); + } + + export_dyn_rule(p, dst); + last = dst; + } + IPFW_BUCK_UNLOCK(i); + } + + if (last != NULL) /* mark last dynamic rule */ + bzero(&last->next, sizeof(last)); + + return (0); +} + /* * Fill given buffer with dynamic states. * IPFW_UH_RLOCK has to be held while calling. @@ -1477,28 +1545,9 @@ ipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep) if (bp + sizeof *p <= ep) { ipfw_dyn_rule *dst = (ipfw_dyn_rule *)bp; - bcopy(p, dst, sizeof *p); - bcopy(&(p->rule->rulenum), &(dst->rule), - sizeof(p->rule->rulenum)); - /* - * store set number into high word of - * dst->rule pointer. - */ - bcopy(&(p->rule->set), - (char *)&dst->rule + - sizeof(p->rule->rulenum), - sizeof(p->rule->set)); - /* - * store a non-null value in "next". - * The userland code will interpret a - * NULL here as a marker - * for the last dynamic rule. - */ - bcopy(&dst, &dst->next, sizeof(dst)); + + export_dyn_rule(p, dst); last = dst; - dst->expire = - TIME_LEQ(dst->expire, time_uptime) ? - 0 : dst->expire - time_uptime ; bp += sizeof(ipfw_dyn_rule); } } diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index 6daf89aba370..480e1208bb7a 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -176,6 +176,7 @@ enum { /* result for matching dynamic rules */ * Eventually we may implement it with a callback on the function. */ struct ip_fw_chain; +struct sockopt_data; void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int); void ipfw_dyn_unlock(ipfw_dyn_rule *q); @@ -188,6 +189,7 @@ ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, struct tcphdr *tcp); void ipfw_remove_dyn_children(struct ip_fw *rule); void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep); +int ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd); void ipfw_dyn_init(struct ip_fw_chain *); /* per-vnet initialization */ void ipfw_dyn_uninit(int); /* per-vnet deinitialization */ diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index fa869f0469f0..cd4eb6f656d6 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -989,6 +989,178 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) } +struct dump_args { + uint32_t b; /* start rule */ + uint32_t e; /* end rule */ + uint32_t rcount; /* number of rules */ + uint32_t rsize; /* rules size */ + uint32_t tcount; /* number of tables */ +}; + +/* + * Dumps static rules with table TLVs in buffer @sd. + * + * Returns 0 on success. + */ +static int +dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da, + uint32_t *bmask, struct sockopt_data *sd) +{ + int error; + int i, l; + uint32_t tcount; + ipfw_obj_ctlv *ctlv; + struct ip_fw *dst, *rule; + time_t boot_seconds; + + /* Dump table names first (if any) */ + if (da->tcount > 0) { + /* Header first */ + ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv)); + if (ctlv == NULL) + return (ENOMEM); + ctlv->head.type = IPFW_TLV_TBLNAME_LIST; + ctlv->head.length = da->tcount * sizeof(ipfw_obj_ntlv) + + sizeof(*ctlv); + ctlv->count = da->tcount; + ctlv->objsize = sizeof(ipfw_obj_ntlv); + } + + i = 0; + tcount = da->tcount; + while (tcount > 0) { + if ((bmask[i / 32] & (1 << (i % 32))) == 0) { + i++; + continue; + } + + if ((error = ipfw_export_table_ntlv(chain, i, sd)) != 0) + return (error); + + i++; + tcount--; + } + + /* Dump rules */ + ctlv = (ipfw_obj_ctlv *)ipfw_get_sopt_space(sd, sizeof(*ctlv)); + if (ctlv == NULL) + return (ENOMEM); + ctlv->head.type = IPFW_TLV_RULE_LIST; + ctlv->head.length = da->rsize + sizeof(*ctlv); + ctlv->count = da->rcount; + + boot_seconds = boottime.tv_sec; + for (i = da->b; i < da->e; i++) { + rule = chain->map[i]; + + l = RULESIZE(rule); + /* XXX: align to u64 */ + dst = (struct ip_fw *)ipfw_get_sopt_space(sd, l); + if (rule == NULL) + return (ENOMEM); + + bcopy(rule, dst, l); + if (dst->timestamp != 0) + dst->timestamp += boot_seconds; + } + + return (0); +} + +/* + * Dumps requested objects data + * Data layout (version 0)(current): + * Request: [ ipfw_cfg_lheader ] + IPFW_CFG_GET_* flags + * size = ipfw_cfg_lheader.size + * Reply: [ ipfw_rules_lheader + * [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional) + * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (optional) + * [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_dyn_rule x N ] (optional) + * ] + * * NOTE IPFW_TLV_STATE_LIST has the single valid field: objsize. + * The rest (size, count) are set to zero and needs to be ignored. + * + * Returns 0 on success. + */ +static int +dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd) +{ + ipfw_cfg_lheader *hdr; + struct ip_fw *rule; + uint32_t sz; + int error, i; + struct dump_args da; + uint32_t *bmask; + + hdr = (ipfw_cfg_lheader *)ipfw_get_sopt_header(sd, sizeof(*hdr)); + if (hdr == NULL) + return (EINVAL); + + /* Allocate needed state */ + error = 0; + bmask = NULL; + if (hdr->flags & IPFW_CFG_GET_STATIC) + bmask = malloc(IPFW_TABLES_MAX / 8, M_TEMP, M_WAITOK | M_ZERO); + + IPFW_UH_RLOCK(chain); + + /* + * STAGE 1: Determine size/count for objects in range. + * Prepare used tables bitmask. + */ + sz = 0; + memset(&da, 0, sizeof(da)); + + da.b = 0; + da.e = chain->n_rules; + + if (hdr->flags & IPFW_CFG_GET_STATIC) { + for (i = da.b; i < da.e; i++) { + rule = chain->map[i]; + da.rsize += RULESIZE(rule); + da.rcount++; + da.tcount += ipfw_mark_table_kidx(chain, rule, bmask); + } + + if (da.tcount > 0) + sz += da.tcount * sizeof(ipfw_obj_ntlv) + + sizeof(ipfw_obj_ctlv); + sz += da.rsize + sizeof(ipfw_obj_ctlv); + } + + if (hdr->flags & IPFW_CFG_GET_STATES) { + sz += ipfw_dyn_len(); + } + + /* Fill header anyway */ + hdr->size = sz; + hdr->set_mask = V_set_disable; + + if (sd->valsize < sz) { + IPFW_UH_RUNLOCK(chain); + return (ENOMEM); + } + + /* STAGE2: Store actual data */ + if (hdr->flags & IPFW_CFG_GET_STATIC) { + error = dump_static_rules(chain, &da, bmask, sd); + if (error != 0) { + IPFW_UH_RUNLOCK(chain); + return (error); + } + } + + if (hdr->flags & IPFW_CFG_GET_STATES) + error = ipfw_dump_states(chain, sd); + + IPFW_UH_RUNLOCK(chain); + + if (bmask != NULL) + free(bmask, M_TEMP); + + return (error); +} + #define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader)) #define IP_FW3_OPTBUF 4096 /* page-size */ /** @@ -1101,6 +1273,10 @@ ipfw_ctl(struct sockopt *sopt) } break; + case IP_FW_XGET: /* IP_FW3 */ + error = dump_config(chain, &sdata); + break; + case IP_FW_FLUSH: /* locking is done within del_entry() */ error = del_entry(chain, 0); /* special case, rule=0, cmd=0 means all */ diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index a53f189c41db..8b816095ca38 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -27,16 +27,15 @@ __FBSDID("$FreeBSD$"); /* - * Lookup table support for ipfw + * Lookup table support for ipfw. * - * Lookup tables are implemented (at the moment) using the radix - * tree used for routing tables. Tables store key-value entries, where - * keys are network prefixes (addr/masklen), and values are integers. - * As a degenerate case we can interpret keys as 32-bit integers - * (with a /32 mask). + * This file containg handlers for all generic tables operations: + * add/del/flush entries, list/dump tables etc.. * - * The table is protected by the IPFW lock even for manipulation coming - * from userland, because operations are typically fast. + * Table data modification is protected by both UH and runtimg lock + * while reading configuration/data is protected by UH lock. + * + * Lookup algorithms for all table types are located in ip_fw_table_algo.c */ #include "opt_ipfw.h" @@ -901,6 +900,31 @@ objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti) ti->tlen = oh->ntlv.head.length; } +int +ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, + struct sockopt_data *sd) +{ + struct namedobj_instance *ni; + struct named_object *no; + ipfw_obj_ntlv *ntlv; + + ni = CHAIN_TO_NI(ch); + + no = ipfw_objhash_lookup_idx(ni, 0, kidx); + KASSERT(no != NULL, ("invalid table kidx passed")); + + ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv)); + if (ntlv == NULL) + return (ENOMEM); + + ntlv->head.type = IPFW_TLV_TBL_NAME; + ntlv->head.length = sizeof(*ntlv); + ntlv->idx = no->kidx; + strlcpy(ntlv->name, no->name, sizeof(ntlv->name)); + + return (0); +} + static void export_table_info(struct table_config *tc, ipfw_xtable_info *i) { @@ -1253,7 +1277,7 @@ find_name_tlv(void *tlvs, int len, uint16_t uidx) for (; pa < pe; pa += l) { ntlv = (ipfw_obj_ntlv *)pa; l = ntlv->head.length; - if (ntlv->head.type != IPFW_TLV_NAME) + if (ntlv->head.type != IPFW_TLV_TBL_NAME) continue; if (ntlv->idx != uidx) continue; @@ -1515,6 +1539,40 @@ ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule) return (0); } +/* + * Sets every table kidx in @bmask which is used in rule @rule. + * + * Returns number of newly-referenced tables. + */ +int +ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, + uint32_t *bmask) +{ + int cmdlen, l, count; + ipfw_insn *cmd; + uint16_t kidx; + uint8_t type; + + l = rule->cmd_len; + cmd = rule->cmd; + cmdlen = 0; + count = 0; + for ( ; l > 0 ; l -= cmdlen, cmd += cmdlen) { + cmdlen = F_LEN(cmd); + + if (classify_table_opcode(cmd, &kidx, &type) != 0) + continue; + + if ((bmask[kidx / 32] & (1 << (kidx % 32))) == 0) + count++; + + bmask[kidx / 32] |= 1 << (kidx % 32); + } + + return (count); +} + + /* * Checks is opcode is referencing table of appropriate type. diff --git a/sys/netpfil/ipfw/ip_fw_table.h b/sys/netpfil/ipfw/ip_fw_table.h index e9599d8f0acc..4d0d3cedcc34 100644 --- a/sys/netpfil/ipfw/ip_fw_table.h +++ b/sys/netpfil/ipfw/ip_fw_table.h @@ -111,6 +111,10 @@ int ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain, struct rule_check_info *ci); int ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule); +int ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule, + uint32_t *bmask); +int ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx, + struct sockopt_data *sd); void ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule); void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);