diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index ee03f4e1f9df..8cb8d8bd5df1 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -61,6 +61,7 @@ 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 */ @@ -1298,7 +1299,7 @@ show_prerequisites(int *flags, int want, int cmd) static void show_static_rule(struct cmdline_opts *co, struct format_opts *fo, - struct buf_pr *bp, struct ip_fw *rule) + struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr) { static int twidth = 0; int l; @@ -1309,11 +1310,9 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, 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; - bcopy(&rule->next_rule, &set_disable, sizeof(set_disable)); - - if (set_disable & (1 << rule->set)) { /* disabled */ + if ((fo->set_mask & (1 << rule->set)) == 0) { + /* disabled mask */ if (!co->show_sets) return; else @@ -1321,13 +1320,14 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, } bprintf(bp, "%05u ", rule->rulenum); + /* Print counters if enabled */ if (fo->pcwidth > 0 || fo->bcwidth > 0) { - pr_u64(bp, &rule->pcnt, fo->pcwidth); - pr_u64(bp, &rule->bcnt, fo->bcwidth); + pr_u64(bp, &cntr->pcnt, fo->pcwidth); + pr_u64(bp, &cntr->bcnt, fo->bcwidth); } if (co->do_time == 2) - bprintf(bp, "%10u ", rule->timestamp); + bprintf(bp, "%10u ", cntr->timestamp); else if (co->do_time == 1) { char timestr[30]; time_t t = (time_t)0; @@ -1337,8 +1337,8 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, *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'; @@ -1537,7 +1537,7 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, /* * 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; @@ -1549,7 +1549,7 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, break; } } - if (rule->_pad & 1) { /* empty rules before options */ + if (rule->flags & IPFW_RULE_NOOPT) { /* empty rules before options */ if (!co->do_compact) { show_prerequisites(&flags, HAVE_PROTO, 0); printf(" from any to any"); @@ -1561,7 +1561,7 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, 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; @@ -2177,6 +2177,9 @@ prepare_format_dyn(struct cmdline_opts *co, struct format_opts *fo, /* 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, @@ -2237,27 +2240,31 @@ foreach_state(struct cmdline_opts *co, struct format_opts *fo, static void prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, - struct ip_fw *r, int rcnt, caddr_t base, size_t sz) + ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz) { int bcwidth, pcwidth, width; int n; -#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r))) + 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++, r = NEXT(r)) { + 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, &r->pcnt, 0); + width = pr_u64(NULL, &cntr->pcnt, 0); if (width > pcwidth) pcwidth = width; /* byte counter */ - width = pr_u64(NULL, &r->bcnt, 0); + width = pr_u64(NULL, &cntr->bcnt, 0); if (width > bcwidth) bcwidth = width; } @@ -2266,23 +2273,36 @@ prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo, fo->pcwidth = pcwidth; fo->dcnt = 0; - if (co->do_dynamic && sz > 0) - sz = foreach_state(co, fo, base, sz, prepare_format_dyn, NULL); + 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, struct ip_fw *r, int rcnt) + 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++, r = NEXT(r) ) { + 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); + show_static_rule(co, fo, bp, r, cntr); + c += rtlv->length; bp_flush(bp); seen++; } @@ -2369,27 +2389,27 @@ ipfw_list(int ac, char *av[], int show_counters) /* 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 ((error = ipfw_get_config(&co, &sfo, &cfg, &sz)) != 0) err(EX_OSERR, "retrieving config failed"); - 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; caddr_t dynbase; size_t dynsz; int rcnt; @@ -2400,6 +2420,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, size_t read; struct buf_pr bp; ipfw_obj_ctlv *ctlv, *tstate; + ipfw_obj_tlv *rbase; /* * Handle tablenames TLV first, if any @@ -2408,7 +2429,9 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, rbase = NULL; dynbase = NULL; dynsz = 0; - read = 0; + read = sizeof(*cfg); + + fo->set_mask = cfg->set_mask; ctlv = (ipfw_obj_ctlv *)(cfg + 1); @@ -2422,7 +2445,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, } if (ctlv->head.type == IPFW_TLV_RULE_LIST) { - rbase = (struct ip_fw *)(ctlv + 1); + rbase = (ipfw_obj_tlv *)(ctlv + 1); rcnt = ctlv->count; read += ctlv->head.length; ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + @@ -2432,8 +2455,12 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, if ((cfg->flags & IPFW_CFG_GET_STATES) && (read != sz)) { /* We may have some dynamic states */ - dynbase = (caddr_t)ctlv; dynsz = sz - read; + /* 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); @@ -2446,7 +2473,7 @@ ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo, list_static_range(co, fo, &bp, rbase, rcnt); if (co->do_dynamic && dynsz > 0) { - printf("## Dynamic rules (%d):\n", fo->dcnt); + printf("## Dynamic rules (%d %lu):\n", fo->dcnt, dynsz); list_dyn_range(co, fo, &bp, dynbase, dynsz); } @@ -3304,7 +3331,7 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) 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. @@ -3326,12 +3353,12 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) bzero(cmdbuf, sizeof(cmdbuf)); bzero(rbuf, *rbufsize); - rule = (struct ip_fw *)rbuf; + rule = (struct ip_fw_rule *)rbuf; cmd = (ipfw_insn *)cmdbuf; action = (ipfw_insn *)actbuf; rblen = *rbufsize / sizeof(uint32_t); - rblen -= offsetof(struct ip_fw, cmd) / 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; @@ -3884,7 +3911,7 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) * 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 ) { @@ -4467,13 +4494,13 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) * [ * 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 x N ] (*2) (*3) + * [ 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 x N ] + * [ 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. @@ -4490,7 +4517,7 @@ ipfw_add(char *av[]) int rbufsize, default_off, tlen, rlen; size_t sz; struct tidx ts; - struct ip_fw *rule; + struct ip_fw_rule *rule; caddr_t tbuf; ip_fw3_opheader *op3; ipfw_obj_ctlv *ctlv, *tstate; @@ -4502,7 +4529,7 @@ ipfw_add(char *av[]) 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 *)(ctlv + 1); + rule = (struct ip_fw_rule *)(ctlv + 1); rbufsize -= default_off; compile_rule(av, (uint32_t *)rule, &rbufsize, &ts); @@ -4552,8 +4579,9 @@ ipfw_add(char *av[]) 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); + show_static_rule(&co, &sfo, &bp, rule, NULL); bp_free(&bp); } diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index bce86de70429..463353a958c1 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -36,10 +36,8 @@ */ #define IPFW_DEFAULT_RULE 65535 -/* - * Number of sets supported by ipfw - */ -#define IPFW_MAX_SETS 32 +#define RESVD_SET 31 /*set for default and persistent rules*/ +#define IPFW_MAX_SETS 32 /* Number of sets supported by ipfw*/ /* * Default number of ipfw tables. @@ -513,15 +511,17 @@ typedef struct _ipfw_insn_icmp6 { /* * Here we have the structure representing an ipfw rule. * - * It starts with a general area (with link fields and counters) + * Layout: + * struct ip_fw_rule + * [ counter block, size = rule->cntr_len ] + * [ one or more instructions, size = rule->cmd_len * 4 ] + * + * It starts with a general area (with link fields). + * Counter block may be next (if rule->cntr_len > 0), * followed by an array of one or more instructions, which the code - * accesses as an array of 32-bit values. - * - * Given a rule pointer r: - * - * r->cmd is the start of the first instruction. - * ACTION_PTR(r) is the start of the first action (things to do - * once a rule matched). + * accesses as an array of 32-bit values. rule->cmd_len represents + * the total instructions legth in u32 worrd, while act_ofs represents + * rule action offset in u32 words. * * When assembling instruction, remember the following: * @@ -532,11 +532,41 @@ typedef struct _ipfw_insn_icmp6 { * + if a rule has an "altq" option, it comes after "log" * + if a rule has an O_TAG option, it comes after "log" and "altq" * - * NOTE: we use a simple linked list of rules because we never need - * to delete a rule without scanning the list. We do not use - * queue(3) macros for portability and readability. + * + * All structures (excluding instructions) are u64-aligned. + * Please keep this. */ +struct ip_fw_rule { + uint16_t act_ofs; /* offset of action in 32-bit units */ + uint16_t cmd_len; /* # of 32-bit words in cmd */ + uint16_t spare; + uint8_t set; /* rule set (0..31) */ + uint8_t flags; /* rule flags */ + uint32_t rulenum; /* rule number */ + uint32_t id; /* rule id */ + + ipfw_insn cmd[1]; /* storage for commands */ +}; +#define IPFW_RULE_NOOPT 0x01 /* Has no options in body */ + +/* Unaligned version */ + +/* Base ipfw rule counter block. */ +struct ip_fw_bcounter { + uint16_t size; /* Size of counter block, bytes */ + uint8_t flags; /* flags for given block */ + uint8_t spare; + uint32_t timestamp; /* tv_sec of last match */ + uint64_t pcnt; /* Packet counter */ + uint64_t bcnt; /* Byte counter */ +}; + + +#ifndef _KERNEL +/* + * Legacy rule format + */ struct ip_fw { struct ip_fw *x_next; /* linked list of rules */ struct ip_fw *next_rule; /* ptr to next [skipto] rule */ @@ -546,7 +576,6 @@ struct ip_fw { uint16_t cmd_len; /* # of 32-bit words in cmd */ uint16_t rulenum; /* rule number */ uint8_t set; /* rule set (0..31) */ -#define RESVD_SET 31 /* set for default and persistent rules */ uint8_t _pad; /* padding */ uint32_t id; /* rule id */ @@ -557,12 +586,13 @@ struct ip_fw { ipfw_insn cmd[1]; /* storage for commands */ }; +#endif #define ACTION_PTR(rule) \ (ipfw_insn *)( (u_int32_t *)((rule)->cmd) + ((rule)->act_ofs) ) -#define RULESIZE(rule) (sizeof(struct ip_fw) + \ - ((struct ip_fw *)(rule))->cmd_len * 4 - 4) +#define RULESIZE(rule) (sizeof(*(rule)) + (rule)->cmd_len * 4 - 4) + #if 1 // should be moved to in.h /* @@ -698,6 +728,7 @@ typedef struct _ipfw_obj_tlv { #define IPFW_TLV_DYNSTATE_LIST 4 #define IPFW_TLV_TBL_ENT 5 #define IPFW_TLV_DYN_ENT 6 +#define IPFW_TLV_RULE_ENT 7 /* Object name TLV */ typedef struct _ipfw_obj_ntlv { @@ -737,7 +768,9 @@ typedef struct _ipfw_obj_dyntlv { typedef struct _ipfw_obj_ctlv { ipfw_obj_tlv head; /* TLV header */ uint32_t count; /* Number of sub-TLVs */ - uint32_t objsize; /* Single object size */ + uint16_t objsize; /* Single object size */ + uint8_t version; /* TLV version */ + uint8_t spare; } ipfw_obj_ctlv; typedef struct _ipfw_xtable_info { @@ -772,11 +805,13 @@ 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 +#define IPFW_CFG_GET_STATIC 0x01 +#define IPFW_CFG_GET_STATES 0x02 +#define IPFW_CFG_GET_COUNTERS 0x04 typedef struct _ipfw_cfg_lheader { ip_fw3_opheader opheader; /* IP_FW3 opcode */ uint32_t set_mask; /* enabled set mask */ + uint32_t spare; uint32_t flags; /* Request flags */ uint32_t size; /* neded buffer size */ uint32_t start_rule; diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index f69e885ca646..d20156f375aa 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -2647,12 +2648,11 @@ vnet_ipfw_init(const void *unused) LIST_INIT(&chain->nat); #endif + ipfw_init_counters(); /* insert the default rule and create the initial map */ chain->n_rules = 1; - chain->static_len = sizeof(struct ip_fw); chain->map = malloc(sizeof(struct ip_fw *), M_IPFW, M_WAITOK | M_ZERO); - if (chain->map) - rule = malloc(chain->static_len, M_IPFW, M_WAITOK | M_ZERO); + rule = ipfw_alloc_rule(chain, sizeof(struct ip_fw)); /* Set initial number of tables */ V_fw_tables_max = default_fw_tables; @@ -2673,6 +2673,8 @@ vnet_ipfw_init(const void *unused) rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY; chain->default_rule = chain->map[0] = rule; chain->id = rule->id = 1; + /* Pre-calculate rules length for legacy dump format */ + chain->static_len = sizeof(struct ip_fw_rule0); IPFW_LOCK_INIT(chain); ipfw_dyn_init(chain); @@ -2740,7 +2742,8 @@ vnet_ipfw_uninit(const void *unused) ipfw_reap_rules(reap); IPFW_LOCK_DESTROY(chain); ipfw_dyn_uninit(1); /* free the remaining parts */ - return 0; + ipfw_destroy_counters(); + return (0); } /* diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index aaae4ae3f638..a7e578c0fd12 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -220,6 +220,44 @@ VNET_DECLARE(unsigned int, fw_tables_sets); struct tables_config; +#ifdef _KERNEL +typedef struct ip_fw_cntr { + uint64_t pcnt; /* Packet counter */ + uint64_t bcnt; /* Byte counter */ + uint64_t timestamp; /* tv_sec of last match */ +} ip_fw_cntr; + +/* + * Here we have the structure representing an ipfw rule. + * + * It starts with a general area + * followed by an array of one or more instructions, which the code + * accesses as an array of 32-bit values. + * + * Given a rule pointer r: + * + * r->cmd is the start of the first instruction. + * ACTION_PTR(r) is the start of the first action (things to do + * once a rule matched). + */ + +struct ip_fw { + uint16_t act_ofs; /* offset of action in 32-bit units */ + uint16_t cmd_len; /* # of 32-bit words in cmd */ + uint16_t rulenum; /* rule number */ + uint8_t set; /* rule set (0..31) */ + uint8_t flags; /* currently unused */ + counter_u64_t cntr; /* Pointer to rule counters */ + uint32_t timestamp; /* tv_sec of last match */ + uint32_t id; /* rule id */ + struct ip_fw *x_next; /* linked list of rules */ + struct ip_fw *next_rule; /* ptr to next [skipto] rule */ + + ipfw_insn cmd[1]; /* storage for commands */ +}; + +#endif + struct ip_fw_chain { struct ip_fw **map; /* array of rule ptrs to ease lookup */ uint32_t id; /* ruleset id */ @@ -231,7 +269,7 @@ struct ip_fw_chain { #else struct rwlock rwmtx; #endif - int static_len; /* total len of static rules */ + int static_len; /* total len of static rules (v0) */ uint32_t gencnt; /* NAT generation count */ struct ip_fw *reap; /* list of rules to reap */ struct ip_fw *default_rule; @@ -255,6 +293,7 @@ struct sockopt_data { }; /* Macro for working with various counters */ +#ifdef USERSPACE #define IPFW_INC_RULE_COUNTER(_cntr, _bytes) do { \ (_cntr)->pcnt++; \ (_cntr)->bcnt += _bytes; \ @@ -276,6 +315,31 @@ struct sockopt_data { (_cntr)->pcnt = 0; \ (_cntr)->bcnt = 0; \ } while (0) +#else +#define IPFW_INC_RULE_COUNTER(_cntr, _bytes) do { \ + counter_u64_add((_cntr)->cntr, 1); \ + counter_u64_add((_cntr)->cntr + 1, _bytes); \ + if ((_cntr)->timestamp != time_uptime) \ + (_cntr)->timestamp = time_uptime; \ + } while (0) + +#define IPFW_INC_DYN_COUNTER(_cntr, _bytes) do { \ + (_cntr)->pcnt++; \ + (_cntr)->bcnt += _bytes; \ + } while (0) + +#define IPFW_ZERO_RULE_COUNTER(_cntr) do { \ + counter_u64_zero((_cntr)->cntr); \ + counter_u64_zero((_cntr)->cntr + 1); \ + (_cntr)->timestamp = 0; \ + } while (0) + +#define IPFW_ZERO_DYN_COUNTER(_cntr) do { \ + (_cntr)->pcnt = 0; \ + (_cntr)->bcnt = 0; \ + } while (0) +#endif + #define IP_FW_ARG_TABLEARG(a) (((a) == IP_FW_TABLEARG) ? tablearg : (a)) /* @@ -322,17 +386,70 @@ struct obj_idx { struct rule_check_info { uint16_t table_opcodes; /* count of opcodes referencing table */ uint16_t new_tables; /* count of opcodes referencing table */ + uint16_t urule_numoff; /* offset of rulenum in bytes */ + uint8_t version; /* rule version */ ipfw_obj_ctlv *ctlv; /* name TLV containter */ struct ip_fw *krule; /* resulting rule pointer */ - struct ip_fw *urule; /* original rule pointer */ + caddr_t urule; /* original rule pointer */ struct obj_idx obuf[8]; /* table references storage */ }; +/* Legacy interface support */ +/* + * FreeBSD 8 export rule format + */ +struct ip_fw_rule0 { + struct ip_fw *x_next; /* linked list of rules */ + struct ip_fw *next_rule; /* ptr to next [skipto] rule */ + /* 'next_rule' is used to pass up 'set_disable' status */ + + uint16_t act_ofs; /* offset of action in 32-bit units */ + uint16_t cmd_len; /* # of 32-bit words in cmd */ + uint16_t rulenum; /* rule number */ + uint8_t set; /* rule set (0..31) */ + uint8_t _pad; /* padding */ + uint32_t id; /* rule id */ + + /* These fields are present in all rules. */ + uint64_t pcnt; /* Packet counter */ + uint64_t bcnt; /* Byte counter */ + uint32_t timestamp; /* tv_sec of last match */ + + ipfw_insn cmd[1]; /* storage for commands */ +}; + +struct ip_fw_bcounter0 { + uint64_t pcnt; /* Packet counter */ + uint64_t bcnt; /* Byte counter */ + uint32_t timestamp; /* tv_sec of last match */ +}; + +/* Kernel rule length */ +/* + * RULE _K_ SIZE _V_ -> + * get kernel size from userland rool version _V_. + * RULE _U_ SIZE _V_ -> + * get user size version _V_ from kernel rule + * RULESIZE _V_ -> + * get user size rule length + */ +/* FreeBSD8 <> current kernel format */ +#define RULEUSIZE0(r) (sizeof(struct ip_fw_rule0) + (r)->cmd_len * 4 - 4) +#define RULEKSIZE0(r) roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8) +/* FreeBSD11 <> current kernel format */ +#define RULEUSIZE1(r) (roundup2(sizeof(struct ip_fw_rule) + \ + (r)->cmd_len * 4 - 4, 8)) +#define RULEKSIZE1(r) roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8) + + /* In ip_fw_sockopt.c */ int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id); int ipfw_ctl(struct sockopt *sopt); int ipfw_chk(struct ip_fw_args *args); void ipfw_reap_rules(struct ip_fw *head); +void ipfw_init_counters(void); +void ipfw_destroy_counters(void); +struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize); caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed); caddr_t ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed); diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index 542be9805b6d..8d60ca41ef8d 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -69,6 +69,13 @@ __FBSDID("$FreeBSD$"); #include #endif +static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, + struct rule_check_info *ci); +static int check_ipfw_rule1(struct ip_fw_rule *rule, int size, + struct rule_check_info *ci); +static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, + struct rule_check_info *ci); + #define NAMEDOBJ_HASH_SIZE 32 struct namedobj_instance { @@ -92,9 +99,79 @@ static int ipfw_flush_sopt_data(struct sockopt_data *sd); MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's"); /* - * static variables followed by global ones (none in this file) + * static variables followed by global ones */ +#ifndef USERSPACE + +static VNET_DEFINE(uma_zone_t, ipfw_cntr_zone); +#define V_ipfw_cntr_zone VNET(ipfw_cntr_zone) + +void +ipfw_init_counters() +{ + + V_ipfw_cntr_zone = uma_zcreate("IPFW counters", + sizeof(ip_fw_cntr), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, UMA_ZONE_PCPU); +} + +void +ipfw_destroy_counters() +{ + + uma_zdestroy(V_ipfw_cntr_zone); +} + +struct ip_fw * +ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize) +{ + struct ip_fw *rule; + + rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO); + rule->cntr = uma_zalloc(V_ipfw_cntr_zone, M_WAITOK | M_ZERO); + + return (rule); +} + +static void +free_rule(struct ip_fw *rule) +{ + + uma_zfree(V_ipfw_cntr_zone, rule->cntr); + free(rule, M_IPFW); +} +#else +void +ipfw_init_counters() +{ +} + +void +ipfw_destroy_counters() +{ +} + +struct ip_fw * +ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize) +{ + struct ip_fw *rule; + + rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO); + + return (rule); +} + +static void +free_rule(struct ip_fw *rule) +{ + + free(rule, M_IPFW); +} + +#endif + + /* * Find the smallest rule >= key, id. * We could use bsearch but it is so simple that we code it directly @@ -167,20 +244,151 @@ swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len) return old_map; } + +static void +export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr) +{ + + cntr->size = sizeof(*cntr); + + if (krule->cntr != NULL) { + cntr->pcnt = counter_u64_fetch(krule->cntr); + cntr->bcnt = counter_u64_fetch(krule->cntr + 1); + cntr->timestamp = krule->timestamp; + } + if (cntr->timestamp > 0) + cntr->timestamp += boottime.tv_sec; +} + +static void +export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr) +{ + + if (krule->cntr != NULL) { + cntr->pcnt = counter_u64_fetch(krule->cntr); + cntr->bcnt = counter_u64_fetch(krule->cntr + 1); + cntr->timestamp = krule->timestamp; + } + if (cntr->timestamp > 0) + cntr->timestamp += boottime.tv_sec; +} + /* - * Copies rule @urule from userland format to kernel @krule. + * Copies rule @urule from v1 userland format + * to kernel @krule. + * Assume @krule is zeroed. */ static void -copy_rule(struct ip_fw *urule, struct ip_fw *krule) +import_rule1(struct rule_check_info *ci) { - int l; + struct ip_fw_rule *urule; + struct ip_fw *krule; - l = RULESIZE(urule); - bcopy(urule, krule, l); - /* clear fields not settable from userland */ - krule->x_next = NULL; - krule->next_rule = NULL; - IPFW_ZERO_RULE_COUNTER(krule); + urule = (struct ip_fw_rule *)ci->urule; + krule = (struct ip_fw *)ci->krule; + + /* copy header */ + krule->act_ofs = urule->act_ofs; + krule->cmd_len = urule->cmd_len; + krule->rulenum = urule->rulenum; + krule->set = urule->set; + krule->flags = urule->flags; + + /* Save rulenum offset */ + ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum); + + /* Copy opcodes */ + memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); +} + +/* + * Export rule into v1 format (Current). + * Layout: + * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT) + * [ ip_fw_rule ] OR + * [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs). + * ] + * Assume @data is zeroed. + */ +static void +export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs) +{ + struct ip_fw_bcounter *cntr; + struct ip_fw_rule *urule; + ipfw_obj_tlv *tlv; + + /* Fill in TLV header */ + tlv = (ipfw_obj_tlv *)data; + tlv->type = IPFW_TLV_RULE_ENT; + tlv->length = len; + + if (rcntrs != 0) { + /* Copy counters */ + cntr = (struct ip_fw_bcounter *)(tlv + 1); + urule = (struct ip_fw_rule *)(cntr + 1); + export_cntr1_base(krule, cntr); + } else + urule = (struct ip_fw_rule *)(tlv + 1); + + /* copy header */ + urule->act_ofs = krule->act_ofs; + urule->cmd_len = krule->cmd_len; + urule->rulenum = krule->rulenum; + urule->set = krule->set; + urule->flags = krule->flags; + urule->id = krule->id; + + /* Copy opcodes */ + memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); +} + + +/* + * Copies rule @urule from FreeBSD8 userland format (v0) + * to kernel @krule. + * Assume @krule is zeroed. + */ +static void +import_rule0(struct rule_check_info *ci) +{ + struct ip_fw_rule0 *urule; + struct ip_fw *krule; + + urule = (struct ip_fw_rule0 *)ci->urule; + krule = (struct ip_fw *)ci->krule; + + /* copy header */ + krule->act_ofs = urule->act_ofs; + krule->cmd_len = urule->cmd_len; + krule->rulenum = urule->rulenum; + krule->set = urule->set; + if ((urule->_pad & 1) != 0) + krule->flags |= IPFW_RULE_NOOPT; + + /* Save rulenum offset */ + ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum); + + /* Copy opcodes */ + memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t)); +} + +static void +export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len) +{ + /* copy header */ + memset(urule, 0, len); + urule->act_ofs = krule->act_ofs; + urule->cmd_len = krule->cmd_len; + urule->rulenum = krule->rulenum; + urule->set = krule->set; + if ((krule->flags & IPFW_RULE_NOOPT) != 0) + urule->_pad |= 1; + + /* Copy opcodes */ + memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t)); + + /* Export counters */ + export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt); } /* @@ -191,9 +399,10 @@ copy_rule(struct ip_fw *urule, struct ip_fw *krule) static int commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count) { - int error, i, l, insert_before, tcount; + int error, i, insert_before, tcount; + uint16_t rulenum, *pnum; struct rule_check_info *ci; - struct ip_fw *rule, *urule; + struct ip_fw *krule; struct ip_fw **map; /* the new array of pointers */ /* Check if we need to do table remap */ @@ -264,31 +473,33 @@ commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count) /* FIXME: Handle count > 1 */ ci = rci; - rule = ci->krule; - urule = ci->urule; - l = RULESIZE(rule); + krule = ci->krule; + rulenum = krule->rulenum; /* find the insertion point, we will insert before */ - insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE; + insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE; i = ipfw_find_rule(chain, insert_before, 0); /* duplicate first part */ if (i > 0) bcopy(chain->map, map, i * sizeof(struct ip_fw *)); - map[i] = rule; + map[i] = krule; /* duplicate remaining part, we always have the default rule */ bcopy(chain->map + i, map + i + 1, sizeof(struct ip_fw *) *(chain->n_rules - i)); - if (rule->rulenum == 0) { - /* write back the number */ - rule->rulenum = i > 0 ? map[i-1]->rulenum : 0; - if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) - rule->rulenum += V_autoinc_step; - urule->rulenum = rule->rulenum; + if (rulenum == 0) { + /* Compute rule number and write it back */ + rulenum = i > 0 ? map[i-1]->rulenum : 0; + if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step) + rulenum += V_autoinc_step; + krule->rulenum = rulenum; + /* Save number to userland rule */ + pnum = (uint16_t *)((caddr_t)ci->urule + ci->urule_numoff); + *pnum = rulenum; } - rule->id = chain->id + 1; + krule->id = chain->id + 1; map = swap_map(chain, map, chain->n_rules + 1); - chain->static_len += l; + chain->static_len += RULEUSIZE0(krule); IPFW_UH_WUNLOCK(chain); if (map) free(map, M_IPFW); @@ -307,7 +518,7 @@ ipfw_reap_rules(struct ip_fw *head) while ((rule = head) != NULL) { head = head->x_next; - free(rule, M_IPFW); + free_rule(rule); } } @@ -466,7 +677,7 @@ del_entry(struct ip_fw_chain *chain, uint32_t arg) rule = map[i]; if (keep_rule(rule, cmd, new_set, num)) continue; - chain->static_len -= RULESIZE(rule); + chain->static_len -= RULEUSIZE0(rule); if (cmd != 1) ipfw_expire_dyn_rules(chain, rule, RESVD_SET); rule->x_next = chain->reap; @@ -600,23 +811,24 @@ zero_entry(struct ip_fw_chain *chain, u_int32_t arg, int log_only) return (0); } + /* - * Check validity of the structure before insert. - * Rules are simple, so this mostly need to check rule sizes. + * Check rule head in FreeBSD11 format + * */ static int -check_ipfw_struct(struct ip_fw *rule, int size, struct rule_check_info *ci) +check_ipfw_rule1(struct ip_fw_rule *rule, int size, + struct rule_check_info *ci) { - int l, cmdlen = 0; - int have_action=0; - ipfw_insn *cmd; + int l; if (size < sizeof(*rule)) { printf("ipfw: rule too short\n"); return (EINVAL); } - /* first, check for valid size */ - l = RULESIZE(rule); + + /* Check for valid cmd_len */ + l = roundup2(RULESIZE(rule), sizeof(uint64_t)); if (l != size) { printf("ipfw: size mismatch (have %d want %d)\n", size, l); return (EINVAL); @@ -630,12 +842,55 @@ check_ipfw_struct(struct ip_fw *rule, int size, struct rule_check_info *ci) if (rule->rulenum > IPFW_DEFAULT_RULE - 1) return (EINVAL); + return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); +} + +/* + * Check rule head in FreeBSD8 format + * + */ +static int +check_ipfw_rule0(struct ip_fw_rule0 *rule, int size, + struct rule_check_info *ci) +{ + int l; + + if (size < sizeof(*rule)) { + printf("ipfw: rule too short\n"); + return (EINVAL); + } + + /* Check for valid cmd_len */ + l = sizeof(*rule) + rule->cmd_len * 4 - 4; + if (l != size) { + printf("ipfw: size mismatch (have %d want %d)\n", size, l); + return (EINVAL); + } + if (rule->act_ofs >= rule->cmd_len) { + printf("ipfw: bogus action offset (%u > %u)\n", + rule->act_ofs, rule->cmd_len - 1); + return (EINVAL); + } + + if (rule->rulenum > IPFW_DEFAULT_RULE - 1) + return (EINVAL); + + return (check_ipfw_rule_body(rule->cmd, rule->cmd_len, ci)); +} + +static int +check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) +{ + int cmdlen, l; + int have_action; + + have_action = 0; + /* * Now go for the individual checks. Very simple ones, basically only * instruction sizes. */ - for (l = rule->cmd_len, cmd = rule->cmd ; - l > 0 ; l -= cmdlen, cmd += cmdlen) { + for (l = cmd_len; l > 0 ; l -= cmdlen, cmd += cmdlen) { cmdlen = F_LEN(cmd); if (cmdlen > l) { printf("ipfw: opcode %d size truncated\n", @@ -854,14 +1109,14 @@ check_ipfw_struct(struct ip_fw *rule, int size, struct rule_check_info *ci) printf("ipfw: opcode %d, multiple actions" " not allowed\n", cmd->opcode); - return EINVAL; + return (EINVAL); } have_action = 1; if (l != cmdlen) { printf("ipfw: opcode %d, action must be" " last opcode\n", cmd->opcode); - return EINVAL; + return (EINVAL); } break; #ifdef INET6 @@ -904,25 +1159,25 @@ check_ipfw_struct(struct ip_fw *rule, int size, struct rule_check_info *ci) case O_IP6_DST_MASK: case O_ICMP6TYPE: printf("ipfw: no IPv6 support in kernel\n"); - return EPROTONOSUPPORT; + return (EPROTONOSUPPORT); #endif default: printf("ipfw: opcode %d, unknown opcode\n", cmd->opcode); - return EINVAL; + return (EINVAL); } } } if (have_action == 0) { printf("ipfw: missing action\n"); - return EINVAL; + return (EINVAL); } return 0; bad_size: printf("ipfw: opcode %d size %d wrong\n", cmd->opcode, cmdlen); - return EINVAL; + return (EINVAL); } @@ -954,8 +1209,8 @@ struct ip_fw7 { ipfw_insn cmd[1]; /* storage for commands */ }; - int convert_rule_to_7(struct ip_fw *rule); -int convert_rule_to_8(struct ip_fw *rule); +static int convert_rule_to_7(struct ip_fw_rule0 *rule); +static int convert_rule_to_8(struct ip_fw_rule0 *rule); #ifndef RULESIZE7 #define RULESIZE7(rule) (sizeof(struct ip_fw7) + \ @@ -973,7 +1228,8 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) { char *bp = buf; char *ep = bp + space; - struct ip_fw *rule, *dst; + struct ip_fw *rule; + struct ip_fw_rule0 *dst; int error, i, l, warnflag; time_t boot_seconds; @@ -989,10 +1245,10 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) if (bp + l + sizeof(uint32_t) <= ep) { bcopy(rule, bp, l + sizeof(uint32_t)); error = ipfw_rewrite_table_kidx(chain, - (struct ip_fw *)bp); + (struct ip_fw_rule0 *)bp); if (error != 0) return (0); - error = convert_rule_to_7((struct ip_fw *) bp); + error = convert_rule_to_7((struct ip_fw_rule0 *) bp); if (error) return 0; /*XXX correct? */ /* @@ -1010,14 +1266,13 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space) continue; /* go to next rule */ } - /* normal mode, don't touch rules */ - l = RULESIZE(rule); + l = RULEUSIZE0(rule); if (bp + l > ep) { /* should not happen */ printf("overflow dumping static rules\n"); break; } - dst = (struct ip_fw *)bp; - bcopy(rule, dst, l); + dst = (struct ip_fw_rule0 *)bp; + export_rule0(rule, dst, l); error = ipfw_rewrite_table_kidx(chain, dst); /* @@ -1058,6 +1313,7 @@ struct dump_args { uint32_t rcount; /* number of rules */ uint32_t rsize; /* rules size */ uint32_t tcount; /* number of tables */ + int rcounters; /* counters */ }; /* @@ -1073,8 +1329,8 @@ dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da, int i, l; uint32_t tcount; ipfw_obj_ctlv *ctlv; - struct ip_fw *dst, *rule; - time_t boot_seconds; + struct ip_fw *krule; + caddr_t dst; /* Dump table names first (if any) */ if (da->tcount > 0) { @@ -1112,19 +1368,17 @@ dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da, 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]; + krule = chain->map[i]; - l = RULESIZE(rule); - /* XXX: align to u64 */ - dst = (struct ip_fw *)ipfw_get_sopt_space(sd, l); - if (rule == NULL) + l = RULEUSIZE1(krule) + sizeof(ipfw_obj_tlv); + if (da->rcounters != 0) + l += sizeof(struct ip_fw_bcounter); + dst = (caddr_t)ipfw_get_sopt_space(sd, l); + if (dst == NULL) return (ENOMEM); - bcopy(rule, dst, l); - if (dst->timestamp != 0) - dst->timestamp += boot_seconds; + export_rule1(krule, dst, l, da->rcounters); } return (0); @@ -1137,8 +1391,10 @@ dump_static_rules(struct ip_fw_chain *chain, struct dump_args *da, * 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) + * [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) + * ipfw_obj_tlv(IPFW_TLV_RULE_ENT) [ ip_fw_bcounter (optional) ip_fw_rule ] + * ] (optional) + * [ ipfw_obj_ctlv(IPFW_TLV_STATE_LIST) ipfw_obj_dyntlv 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. @@ -1190,10 +1446,15 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd) if (hdr->flags & IPFW_CFG_GET_STATIC) { for (i = da.b; i < da.e; i++) { rule = chain->map[i]; - da.rsize += RULESIZE(rule); + da.rsize += RULEUSIZE1(rule) + sizeof(ipfw_obj_tlv); da.rcount++; da.tcount += ipfw_mark_table_kidx(chain, rule, bmask); } + /* Add counters if requested */ + if (hdr->flags & IPFW_CFG_GET_COUNTERS) { + da.rsize += sizeof(struct ip_fw_bcounter) * da.rcount; + da.rcounters = 1; + } if (da.tcount > 0) sz += da.tcount * sizeof(ipfw_obj_ntlv) + @@ -1202,7 +1463,8 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd) } if (hdr->flags & IPFW_CFG_GET_STATES) - sz += ipfw_dyn_get_count() * sizeof(ipfw_obj_dyntlv); + sz += ipfw_dyn_get_count() * sizeof(ipfw_obj_dyntlv) + + sizeof(ipfw_obj_ctlv); /* Fill header anyway */ hdr->size = sz; @@ -1286,7 +1548,7 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd) ipfw_obj_ntlv *ntlv; int clen, error, idx; uint32_t count, read; - struct ip_fw *r; + struct ip_fw_rule *r; struct rule_check_info rci, *ci, *cbuf; ip_fw3_opheader *op3; int i, rsize; @@ -1308,8 +1570,11 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd) if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) { clen = ctlv->head.length; + /* Check size and alignment */ if (clen > sd->valsize || clen < sizeof(*ctlv)) return (EINVAL); + if ((clen % sizeof(uint64_t)) != 0) + return (EINVAL); /* * Some table names or other named objects. @@ -1354,6 +1619,8 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd) clen = ctlv->head.length; if (clen + read > sd->valsize || clen < sizeof(*ctlv)) return (EINVAL); + if ((clen % sizeof(uint64_t)) != 0) + return (EINVAL); /* * TODO: Permit adding multiple rules at once @@ -1363,7 +1630,7 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd) clen -= sizeof(*ctlv); - if (ctlv->count > clen / sizeof(struct ip_fw)) + if (ctlv->count > clen / sizeof(struct ip_fw_rule)) return (EINVAL); /* Allocate state for each rule or use stack */ @@ -1377,36 +1644,38 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd) /* * Check each rule for validness. - * Ensure numbered rules are sorted ascending. + * Ensure numbered rules are sorted ascending + * and properly aligned */ - idx = -1; - r = (struct ip_fw *)(ctlv + 1); + idx = 0; + r = (struct ip_fw_rule *)(ctlv + 1); count = 0; error = 0; while (clen > 0) { - rsize = RULESIZE(r); + rsize = roundup2(RULESIZE(r), sizeof(uint64_t)); if (rsize > clen || ctlv->count <= count) { error = EINVAL; break; } ci->ctlv = tstate; - error = check_ipfw_struct(r, rsize, ci); + error = check_ipfw_rule1(r, rsize, ci); if (error != 0) break; /* Check sorting */ if (r->rulenum != 0 && r->rulenum < idx) { + printf("rulenum %d idx %d\n", r->rulenum, idx); error = EINVAL; break; } idx = r->rulenum; - ci->urule = r; + ci->urule = (caddr_t)r; rsize = roundup2(rsize, sizeof(uint64_t)); clen -= rsize; - r = (struct ip_fw *)((caddr_t)r + rsize); + r = (struct ip_fw_rule *)((caddr_t)r + rsize); count++; ci++; } @@ -1433,8 +1702,9 @@ add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd) * Allocate storage and try to add them to chain. */ for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++) { - ci->krule = malloc(RULESIZE(ci->urule), M_IPFW, M_WAITOK); - copy_rule(ci->urule, ci->krule); + clen = RULEKSIZE1((struct ip_fw_rule *)ci->urule); + ci->krule = ipfw_alloc_rule(chain, clen); + import_rule1(ci); } if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) { @@ -1458,7 +1728,8 @@ ipfw_ctl(struct sockopt *sopt) #define RULE_MAXSIZE (256*sizeof(u_int32_t)) int error; size_t bsize_max, size, valsize; - struct ip_fw *buf, *rule; + struct ip_fw *buf; + struct ip_fw_rule0 *rule; struct ip_fw_chain *chain; u_int32_t rulenum[2]; uint32_t opt; @@ -1569,7 +1840,7 @@ ipfw_ctl(struct sockopt *sopt) size += ipfw_dyn_len(); if (size >= sopt->sopt_valsize) break; - buf = malloc(size, M_TEMP, M_WAITOK); + buf = malloc(size, M_TEMP, M_WAITOK | M_ZERO); IPFW_UH_RLOCK(chain); /* check again how much space we need */ want = chain->static_len + ipfw_dyn_len(); @@ -1613,29 +1884,27 @@ ipfw_ctl(struct sockopt *sopt) * the first ipfw command is 'ipfw [pipe] list') * the ipfw binary may crash or loop infinitly... */ - if (sopt->sopt_valsize == RULESIZE7(rule)) { + size = sopt->sopt_valsize; + if (size == RULESIZE7(rule)) { is7 = 1; error = convert_rule_to_8(rule); if (error) { free(rule, M_TEMP); return error; } - if (error == 0) - error = check_ipfw_struct(rule, RULESIZE(rule), &ci); - } else { + size = RULESIZE(rule); + } else is7 = 0; if (error == 0) - error = check_ipfw_struct(rule, sopt->sopt_valsize,&ci); - } + error = check_ipfw_rule0(rule, size, &ci); if (error == 0) { /* locking is done within add_rule() */ struct ip_fw *krule; - krule = malloc(RULESIZE(rule), M_IPFW, M_WAITOK); - copy_rule(rule, krule); - ci.urule = rule; + krule = ipfw_alloc_rule(chain, RULEKSIZE0(rule)); + ci.urule = (caddr_t)rule; ci.krule = krule; + import_rule0(&ci); error = commit_rules(chain, &ci, 1); - size = RULESIZE(rule); if (!error && sopt->sopt_dir == SOPT_GET) { if (is7) { error = convert_rule_to_7(rule); @@ -1971,8 +2240,8 @@ ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed) #define RULE_MAXSIZE (256*sizeof(u_int32_t)) /* Functions to convert rules 7.2 <==> 8.0 */ -int -convert_rule_to_7(struct ip_fw *rule) +static int +convert_rule_to_7(struct ip_fw_rule0 *rule) { /* Used to modify original rule */ struct ip_fw7 *rule7 = (struct ip_fw7 *)rule; @@ -1990,7 +2259,7 @@ convert_rule_to_7(struct ip_fw *rule) bcopy(rule, tmp, RULE_MAXSIZE); /* Copy fields */ - rule7->_pad = tmp->_pad; + //rule7->_pad = tmp->_pad; rule7->set = tmp->set; rule7->rulenum = tmp->rulenum; rule7->cmd_len = tmp->cmd_len; @@ -1998,9 +2267,7 @@ convert_rule_to_7(struct ip_fw *rule) rule7->next_rule = (struct ip_fw7 *)tmp->next_rule; rule7->next = (struct ip_fw7 *)tmp->x_next; rule7->cmd_len = tmp->cmd_len; - rule7->pcnt = tmp->pcnt; - rule7->bcnt = tmp->bcnt; - rule7->timestamp = tmp->timestamp; + export_cntr1_base(tmp, (struct ip_fw_bcounter *)&rule7->pcnt); /* Copy commands */ for (ll = tmp->cmd_len, ccmd = tmp->cmd, dst = rule7->cmd ; @@ -2026,8 +2293,8 @@ convert_rule_to_7(struct ip_fw *rule) return 0; } -int -convert_rule_to_8(struct ip_fw *rule) +static int +convert_rule_to_8(struct ip_fw_rule0 *rule) { /* Used to modify original rule */ struct ip_fw7 *rule7 = (struct ip_fw7 *) rule; diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c index a36dc00e601a..c75a398d3c34 100644 --- a/sys/netpfil/ipfw/ip_fw_table.c +++ b/sys/netpfil/ipfw/ip_fw_table.c @@ -1893,7 +1893,7 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule, * Raises error on any other tables. */ int -ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule) +ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw_rule0 *rule) { int cmdlen, error, l; ipfw_insn *cmd; diff --git a/sys/netpfil/ipfw/ip_fw_table.h b/sys/netpfil/ipfw/ip_fw_table.h index 89907cfb78be..4db0837dc6de 100644 --- a/sys/netpfil/ipfw/ip_fw_table.h +++ b/sys/netpfil/ipfw/ip_fw_table.h @@ -118,6 +118,8 @@ int ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); int ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, struct sockopt_data *sd); +int ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, + struct sockopt_data *sd); /* Exported to support legacy opcodes */ int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, struct tentry_info *tei); @@ -125,11 +127,10 @@ int del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti, struct tentry_info *tei); int flush_table(struct ip_fw_chain *ch, struct tid_info *ti); -int ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3, - struct sockopt_data *sd); 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_rewrite_table_kidx(struct ip_fw_chain *chain, + struct ip_fw_rule0 *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,