* Use different rule structures in kernel/userland.

* Switch kernel to use per-cpu counters for rules.
* Keep ABI/API.

Kernel changes:
* Each rules is now exported as TLV with optional extenable
  counter block (ip_fW_bcounter for base one) and
  ip_fw_rule for rule&cmd data.
* Counters needs to be explicitly requested by IPFW_CFG_GET_COUNTERS flag.
* Separate counters from rules in kernel and clean up ip_fw a bit.
* Pack each rule in IPFW_TLV_RULE_ENT tlv to ease parsing.
* Introduce versioning in container TLV (may be needed in future).
* Fix ipfw_cfg_lheader broken u64 alignment.

Userland changes:
* Use set_mask from cfg header when requesting config
* Fix incorrect read accouting in ipfw_show_config()
* Use IPFW_RULE_NOOPT flag instead of playing with _pad
* Fix "ipfw -d list": do not print counters for dynamic states
* Some small fixes
This commit is contained in:
melifaro 2014-07-08 23:11:15 +00:00
parent 7189aec01e
commit 3f7d90b385
7 changed files with 619 additions and 168 deletions

View File

@ -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);
}

View File

@ -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;

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/condvar.h>
#include <sys/counter.h>
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
@ -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);
}
/*

View File

@ -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);

View File

@ -69,6 +69,13 @@ __FBSDID("$FreeBSD$");
#include <security/mac/mac_framework.h>
#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;

View File

@ -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;

View File

@ -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,