* Add new IP_FW_XADD opcode which permits to

a) specify table ids as names
  b) add multiple rules at once.
Partially convert current code for atomic addition of multiple rules.
This commit is contained in:
Alexander V. Chernikov 2014-06-29 22:35:47 +00:00
parent 2aa75134b7
commit 6c2997ffec
7 changed files with 594 additions and 87 deletions

View File

@ -2493,6 +2493,56 @@ lookup_host (char *host, struct in_addr *ipaddr)
return(0);
}
struct tidx {
ipfw_obj_ntlv *idx;
uint32_t count;
uint32_t size;
uint16_t counter;
};
static uint16_t
pack_table(struct tidx *tstate, char *name, uint32_t set)
{
char *p;
int i;
ipfw_obj_ntlv *ntlv;
if ((p = strchr(name, ')')) == NULL)
return (0);
*p = '\0';
if (table_check_name(name) != 0)
return (0);
for (i = 0; i < tstate->count; i++) {
if (strcmp(tstate->idx[i].name, name) != 0)
continue;
if (tstate->idx[i].set != set)
continue;
return (tstate->idx[i].idx);
}
if (tstate->count + 1 > tstate->size) {
tstate->size += 4;
tstate->idx = realloc(tstate->idx, tstate->size *
sizeof(ipfw_obj_ntlv));
if (tstate->idx == NULL)
return (0);
}
ntlv = &tstate->idx[i];
memset(ntlv, 0, sizeof(ipfw_obj_ntlv));
strlcpy(ntlv->name, name, sizeof(ntlv->name));
ntlv->head.type = IPFW_TLV_TBL_NAME;
ntlv->head.length = sizeof(ipfw_obj_ntlv);
ntlv->set = set;
ntlv->idx = ++tstate->counter;
tstate->count++;
return (ntlv->idx);
}
/*
* fills the addr and mask fields in the instruction as appropriate from av.
* Update length as appropriate.
@ -2505,10 +2555,12 @@ lookup_host (char *host, struct in_addr *ipaddr)
* We can have multiple comma-separated address/mask entries.
*/
static void
fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate)
{
int len = 0;
uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
uint16_t uidx;
char *p;
cmd->o.len &= ~F_LEN_MASK; /* zero len */
@ -2521,12 +2573,15 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
}
if (strncmp(av, "table(", 6) == 0) {
char *p = strchr(av + 6, ',');
p = strchr(av + 6, ',');
if (p)
*p++ = '\0';
if ((uidx = pack_table(tstate, av + 6, 0)) == 0)
errx(EX_DATAERR, "Invalid table name: %s", av + 6);
cmd->o.opcode = O_IP_DST_LOOKUP;
cmd->o.arg1 = strtoul(av + 6, NULL, 0);
cmd->o.arg1 = uidx;
if (p) {
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
d[0] = strtoul(p, NULL, 0);
@ -2805,8 +2860,10 @@ ipfw_delete(char *av[])
* patterns which match interfaces.
*/
static void
fill_iface(ipfw_insn_if *cmd, char *arg, int cblen)
fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
{
uint16_t uidx;
cmd->name[0] = '\0';
cmd->o.len |= F_INSN_SIZE(ipfw_insn_if);
@ -2819,8 +2876,11 @@ fill_iface(ipfw_insn_if *cmd, char *arg, int cblen)
char *p = strchr(arg + 6, ',');
if (p)
*p++ = '\0';
if ((uidx = pack_table(tstate, arg + 6, 0)) == 0)
errx(EX_DATAERR, "Invalid table name: %s", arg + 6);
cmd->name[0] = '\1'; /* Special value indicating table */
cmd->p.glob = strtoul(arg + 6, NULL, 0);
cmd->p.glob = uidx;
} else if (!isdigit(*arg)) {
strlcpy(cmd->name, arg, sizeof(cmd->name));
cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
@ -3034,9 +3094,9 @@ add_proto_compat(ipfw_insn *cmd, char *av, u_char *protop)
}
static ipfw_insn *
add_srcip(ipfw_insn *cmd, char *av, int cblen)
add_srcip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
{
fill_ip((ipfw_insn_ip *)cmd, av, cblen);
fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
if (cmd->opcode == O_IP_DST_SET) /* set */
cmd->opcode = O_IP_SRC_SET;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@ -3051,9 +3111,9 @@ add_srcip(ipfw_insn *cmd, char *av, int cblen)
}
static ipfw_insn *
add_dstip(ipfw_insn *cmd, char *av, int cblen)
add_dstip(ipfw_insn *cmd, char *av, int cblen, struct tidx *tstate)
{
fill_ip((ipfw_insn_ip *)cmd, av, cblen);
fill_ip((ipfw_insn_ip *)cmd, av, cblen, tstate);
if (cmd->opcode == O_IP_DST_SET) /* set */
;
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
@ -3082,7 +3142,7 @@ add_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode, int cblen)
}
static ipfw_insn *
add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
{
struct in6_addr a;
char *host, *ch, buf[INET6_ADDRSTRLEN];
@ -3105,7 +3165,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
/* XXX: should check for IPv4, not !IPv6 */
if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
inet_pton(AF_INET6, host, &a) != 1))
ret = add_srcip(cmd, av, cblen);
ret = add_srcip(cmd, av, cblen, tstate);
if (ret == NULL && strcmp(av, "any") != 0)
ret = cmd;
@ -3113,7 +3173,7 @@ add_src(ipfw_insn *cmd, char *av, u_char proto, int cblen)
}
static ipfw_insn *
add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen, struct tidx *tstate)
{
struct in6_addr a;
char *host, *ch, buf[INET6_ADDRSTRLEN];
@ -3136,7 +3196,7 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
/* XXX: should check for IPv4, not !IPv6 */
if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 ||
inet_pton(AF_INET6, host, &a) != 1))
ret = add_dstip(cmd, av, cblen);
ret = add_dstip(cmd, av, cblen, tstate);
if (ret == NULL && strcmp(av, "any") != 0)
ret = cmd;
@ -3156,7 +3216,7 @@ add_dst(ipfw_insn *cmd, char *av, u_char proto, int cblen)
*
*/
void
ipfw_add(char *av[])
compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
{
/*
* rules are added into the 'rulebuf' and then copied in
@ -3164,7 +3224,7 @@ ipfw_add(char *av[])
* Some things that need to go out of order (prob, action etc.)
* go into actbuf[].
*/
static uint32_t rulebuf[255], actbuf[255], cmdbuf[255];
static uint32_t actbuf[255], cmdbuf[255];
int rblen, ablen, cblen;
ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
@ -3190,14 +3250,14 @@ ipfw_add(char *av[])
bzero(actbuf, sizeof(actbuf)); /* actions go here */
bzero(cmdbuf, sizeof(cmdbuf));
bzero(rulebuf, sizeof(rulebuf));
bzero(rbuf, *rbufsize);
rule = (struct ip_fw *)rulebuf;
rule = (struct ip_fw *)rbuf;
cmd = (ipfw_insn *)cmdbuf;
action = (ipfw_insn *)actbuf;
rblen = sizeof(rulebuf) / sizeof(rulebuf[0]);
rblen -= offsetof(struct ip_fw, cmd) / sizeof(rulebuf[0]);
rblen = *rbufsize / sizeof(uint32_t);
rblen -= offsetof(struct ip_fw, cmd) / sizeof(uint32_t);
ablen = sizeof(actbuf) / sizeof(actbuf[0]);
cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
@ -3685,7 +3745,7 @@ ipfw_add(char *av[])
OR_START(source_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing source address");
if (add_src(cmd, *av, proto, cblen)) {
if (add_src(cmd, *av, proto, cblen, tstate)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
@ -3721,7 +3781,7 @@ ipfw_add(char *av[])
OR_START(dest_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing dst address");
if (add_dst(cmd, *av, proto, cblen)) {
if (add_dst(cmd, *av, proto, cblen, tstate)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
@ -3828,7 +3888,7 @@ ipfw_add(char *av[])
case TOK_VIA:
NEED1("recv, xmit, via require interface name"
" or address");
fill_iface((ipfw_insn_if *)cmd, av[0], cblen);
fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate);
av++;
if (F_LEN(cmd) == 0) /* not a valid address */
break;
@ -4074,14 +4134,14 @@ ipfw_add(char *av[])
case TOK_SRCIP:
NEED1("missing source IP");
if (add_srcip(cmd, *av, cblen)) {
if (add_srcip(cmd, *av, cblen, tstate)) {
av++;
}
break;
case TOK_DSTIP:
NEED1("missing destination IP");
if (add_dstip(cmd, *av, cblen)) {
if (add_dstip(cmd, *av, cblen, tstate)) {
av++;
}
break;
@ -4323,17 +4383,111 @@ ipfw_add(char *av[])
}
rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
i = (char *)dst - (char *)rule;
if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
*rbufsize = (char *)dst - (char *)rule;
}
/*
* Adds one or more rules to ipfw chain.
* Data layout:
* Request:
* [
* ip_fw3_opheader
* [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
* [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw 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 ]
* ]
*
* Rules in reply are modified to store their actual ruleset number.
*
* (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
* accoring to their idx field and there has to be no duplicates.
* (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
* (*3) Each ip_fw structure needs to be aligned to u64 boundary.
*/
void
ipfw_add(char *av[])
{
uint32_t rulebuf[1024];
int rbufsize, default_off, tlen, rlen;
size_t sz;
struct tidx ts;
struct ip_fw *rule;
caddr_t tbuf;
ip_fw3_opheader *op3;
ipfw_obj_ctlv *ctlv, *tstate;
rbufsize = sizeof(rulebuf);
memset(&ts, 0, sizeof(ts));
/* Optimize case with no tables */
default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader);
op3 = (ip_fw3_opheader *)rulebuf;
ctlv = (ipfw_obj_ctlv *)(op3 + 1);
rule = (struct ip_fw *)(ctlv + 1);
rbufsize -= default_off;
compile_rule(av, (uint32_t *)rule, &rbufsize, &ts);
/* Align rule size to u64 boundary */
rlen = roundup2(rbufsize, sizeof(uint64_t));
tbuf = NULL;
sz = 0;
tstate = NULL;
if (ts.count != 0) {
/* Some tables. We have to alloc more data */
tlen = ts.count * sizeof(ipfw_obj_ntlv);
sz = default_off + sizeof(ipfw_obj_ctlv) + tlen + rlen;
if ((tbuf = calloc(1, sz)) == NULL)
err(EX_UNAVAILABLE, "malloc() failed for IP_FW_ADD");
op3 = (ip_fw3_opheader *)tbuf;
/* Tables first */
ctlv = (ipfw_obj_ctlv *)(op3 + 1);
ctlv->head.type = IPFW_TLV_TBLNAME_LIST;
ctlv->head.length = sizeof(ipfw_obj_ctlv) + tlen;
ctlv->count = ts.count;
ctlv->objsize = sizeof(ipfw_obj_ntlv);
memcpy(ctlv + 1, ts.idx, tlen);
table_sort_ctlv(ctlv);
tstate = ctlv;
/* Rule next */
ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
ctlv->head.type = IPFW_TLV_RULE_LIST;
ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
ctlv->count = 1;
memcpy(ctlv + 1, rule, rbufsize);
} else {
/* Simply add header */
sz = rlen + default_off;
memset(ctlv, 0, sizeof(*ctlv));
ctlv->head.type = IPFW_TLV_RULE_LIST;
ctlv->head.length = sizeof(ipfw_obj_ctlv) + rlen;
ctlv->count = 1;
}
if (do_get3(IP_FW_XADD, op3, &sz) != 0)
err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_XADD");
if (!co.do_quiet) {
struct format_opts sfo;
struct buf_pr bp;
memset(&sfo, 0, sizeof(sfo));
sfo.tstate = tstate;
bp_alloc(&bp, 4096);
show_static_rule(&co, &sfo, &bp, rule);
bp_free(&bp);
}
if (tbuf != NULL)
free(tbuf);
if (ts.idx != NULL)
free(ts.idx);
}
/*

View File

@ -314,4 +314,6 @@ 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);
void table_sort_ctlv(struct _ipfw_obj_ctlv *ctlv);
int table_check_name(char *tablename);

View File

@ -593,9 +593,29 @@ table_show_list(ipfw_obj_header *oh, int need_header)
}
}
int
compare_ntlv(const void *_a, const void *_b)
{
ipfw_obj_ntlv *a, *b;
a = (ipfw_obj_ntlv *)_a;
b = (ipfw_obj_ntlv *)_b;
if (a->set < b->set)
return (-1);
else if (a->set > b->set)
return (1);
if (a->idx < b->idx)
return (-1);
else if (a->idx > b->idx)
return (1);
return (0);
}
int
compare_ntlv(const void *k, const void *v)
compare_kntlv(const void *k, const void *v)
{
ipfw_obj_ntlv *ntlv;
uint16_t key;
@ -625,7 +645,7 @@ 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);
compare_kntlv);
if (ntlv != 0)
return (ntlv->name);
@ -633,3 +653,37 @@ table_search_ctlv(ipfw_obj_ctlv *ctlv, uint16_t idx)
return (NULL);
}
void
table_sort_ctlv(ipfw_obj_ctlv *ctlv)
{
qsort(ctlv + 1, ctlv->count, ctlv->objsize, compare_ntlv);
}
int
table_check_name(char *tablename)
{
int c, i, l;
/*
* Check if tablename is null-terminated and contains
* valid symbols only. Valid mask is:
* [a-zA-Z\-\.][a-zA-Z0-9\-_\.]{0,62}
*/
l = strlen(tablename);
if (l == 0 || l >= 64)
return (EINVAL);
/* Restrict first symbol to non-digit */
if (isdigit(tablename[0]))
return (EINVAL);
for (i = 0; i < l; i++) {
c = tablename[i];
if (isalpha(c) || isdigit(c) || c == '_' ||
c == '-' || c == '.')
continue;
return (EINVAL);
}
return (0);
}

View File

@ -88,6 +88,7 @@ typedef struct _ip_fw3_opheader {
#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 */
#define IP_FW_XADD 98 /* add entry */
/*
* Usage guidelines:
@ -695,7 +696,7 @@ typedef struct _ipfw_obj_ntlv {
ipfw_obj_tlv head; /* TLV header */
uint16_t idx; /* Name index */
uint16_t spare0; /* unused */
uint32_t spare1; /* unused */
uint32_t set; /* set, if applicable */
char name[64]; /* Null-terminated name */
} ipfw_obj_ntlv;

View File

@ -331,10 +331,9 @@ struct rule_check_info {
uint16_t table_opcodes; /* count of opcodes referencing table */
uint16_t new_tables; /* count of opcodes referencing table */
uint32_t tableset; /* ipfw set id for table */
void *tlvs; /* Pointer to first TLV if any */
int tlen; /* *Total TLV size block */
uint8_t fw3; /* opcode is new */
ipfw_obj_ctlv *ctlv; /* name TLV containter */
struct ip_fw *krule; /* resulting rule pointer */
struct ip_fw *urule; /* original rule pointer */
struct obj_idx obuf[8]; /* table references storage */
};

View File

@ -169,53 +169,92 @@ swap_map(struct ip_fw_chain *chain, struct ip_fw **new_map, int new_len)
}
/*
* Add a new rule to the list. Copy the rule into a malloc'ed area, then
* possibly create a rule number and add the rule to the list.
* Copies rule @urule from userland format to kernel @krule.
*/
static void
copy_rule(struct ip_fw *urule, struct ip_fw *krule)
{
int l;
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);
}
/*
* Add new rule(s) to the list possibly creating rule number for each.
* Update the rule_number in the input struct so the caller knows it as well.
* XXX DO NOT USE FOR THE DEFAULT RULE.
* Must be called without IPFW_UH held
*/
static int
add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule,
struct rule_check_info *ci)
commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count)
{
struct ip_fw *rule;
int i, l, insert_before;
int error, i, l, insert_before, tcount;
struct rule_check_info *ci;
struct ip_fw *rule, *urule;
struct ip_fw **map; /* the new array of pointers */
if (chain->map == NULL || input_rule->rulenum > IPFW_DEFAULT_RULE - 1)
return (EINVAL);
l = RULESIZE(input_rule);
rule = malloc(l, M_IPFW, M_WAITOK | M_ZERO);
bcopy(input_rule, rule, l);
/* clear fields not settable from userland */
rule->x_next = NULL;
rule->next_rule = NULL;
IPFW_ZERO_RULE_COUNTER(rule);
/* Check if we need to do table remap */
if (ci->table_opcodes > 0) {
ci->krule = rule;
i = ipfw_rewrite_table_uidx(chain, ci);
if (i != 0) {
/* rewrite failed, return error */
free(rule, M_IPFW);
return (i);
tcount = 0;
for (ci = rci, i = 0; i < count; ci++, i++) {
if (ci->table_opcodes == 0)
continue;
/*
* Rule has some table opcodes.
* Reference & allocate needed tables/
*/
error = ipfw_rewrite_table_uidx(chain, ci);
if (error != 0) {
/*
* rewrite failed, state for current rule
* has been reverted. Check if we need to
* revert more.
*/
if (tcount > 0) {
/*
* We have some more table rules
* we need to rollback.
*/
IPFW_UH_WLOCK(chain);
while (ci != rci) {
ci--;
if (ci->table_opcodes == 0)
continue;
ipfw_unbind_table_rule(chain,ci->krule);
}
IPFW_UH_WUNLOCK(chain);
}
return (error);
}
tcount++;
}
/* get_map returns with IPFW_UH_WLOCK if successful */
map = get_map(chain, 1, 0 /* not locked */);
map = get_map(chain, count, 0 /* not locked */);
if (map == NULL) {
if (ci->table_opcodes > 0) {
/* We need to unbind tables */
if (tcount > 0) {
/* Unbind tables */
IPFW_UH_WLOCK(chain);
ipfw_unbind_table_rule(chain, rule);
for (ci = rci, i = 0; i < count; ci++, i++) {
if (ci->table_opcodes == 0)
continue;
ipfw_unbind_table_rule(chain, ci->krule);
}
IPFW_UH_WUNLOCK(chain);
}
free(rule, M_IPFW);
return (ENOSPC);
}
@ -223,6 +262,13 @@ add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule,
V_autoinc_step = 1;
else if (V_autoinc_step > 1000)
V_autoinc_step = 1000;
/* FIXME: Handle count > 1 */
ci = rci;
rule = ci->krule;
urule = ci->urule;
l = RULESIZE(rule);
/* find the insertion point, we will insert before */
insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE;
i = ipfw_find_rule(chain, insert_before, 0);
@ -238,7 +284,7 @@ add_rule(struct ip_fw_chain *chain, struct ip_fw *input_rule,
rule->rulenum = i > 0 ? map[i-1]->rulenum : 0;
if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
rule->rulenum += V_autoinc_step;
input_rule->rulenum = rule->rulenum;
urule->rulenum = rule->rulenum;
}
rule->id = chain->id + 1;
@ -581,6 +627,10 @@ check_ipfw_struct(struct ip_fw *rule, int size, struct rule_check_info *ci)
rule->act_ofs, rule->cmd_len - 1);
return (EINVAL);
}
if (rule->rulenum > IPFW_DEFAULT_RULE - 1)
return (EINVAL);
/*
* Now go for the individual checks. Very simple ones, basically only
* instruction sizes.
@ -1172,7 +1222,219 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd)
}
#define IP_FW3_OPLENGTH(x) ((x)->sopt_valsize - sizeof(ip_fw3_opheader))
#define IP_FW3_OPTBUF 4096 /* page-size */
#define IP_FW3_WRITEBUF 4096 /* small page-size write buffer */
#define IP_FW3_READBUF 16 * 1024 * 1024 /* handle large rulesets */
static int
check_object_name(ipfw_obj_ntlv *ntlv)
{
if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
return (EINVAL);
/*
* TODO: do some more complicated checks
*/
return (0);
}
/*
* Adds one or more rules to ipfw @chain.
* Data layout (version 0)(current):
* Request:
* [
* ip_fw3_opheader
* [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
* [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw 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 ]
* ]
*
* Rules in reply are modified to store their actual ruleset number.
*
* (*1) TLVs inside IPFW_TLV_TBL_LIST needs to be sorted ascending
* accoring to their idx field and there has to be no duplicates.
* (*2) Numbered rules inside IPFW_TLV_RULE_LIST needs to be sorted ascending.
* (*3) Each ip_fw structure needs to be aligned to u64 boundary.
*
* Returns 0 on success.
*/
static int
add_entry(struct ip_fw_chain *chain, struct sockopt_data *sd)
{
ipfw_obj_ctlv *ctlv, *rtlv, *tstate;
ipfw_obj_ntlv *ntlv;
int clen, error, idx;
uint32_t count, read;
struct ip_fw *r;
struct rule_check_info rci, *ci, *cbuf;
ip_fw3_opheader *op3;
int i, rsize;
if (sd->valsize > IP_FW3_READBUF)
return (EINVAL);
op3 = (ip_fw3_opheader *)ipfw_get_sopt_space(sd, sd->valsize);
ctlv = (ipfw_obj_ctlv *)(op3 + 1);
read = sizeof(ip_fw3_opheader);
rtlv = NULL;
tstate = NULL;
cbuf = NULL;
memset(&rci, 0, sizeof(struct rule_check_info));
if (read + sizeof(*ctlv) > sd->valsize)
return (EINVAL);
if (ctlv->head.type == IPFW_TLV_TBLNAME_LIST) {
clen = ctlv->head.length;
if (clen > sd->valsize || clen < sizeof(*ctlv))
return (EINVAL);
/*
* Some table names or other named objects.
* Check for validness.
*/
count = (ctlv->head.length - sizeof(*ctlv)) / sizeof(*ntlv);
if (ctlv->count != count || ctlv->objsize != sizeof(*ntlv))
return (EINVAL);
/*
* Check each TLV.
* Ensure TLVs are sorted ascending and
* there are no duplicates.
*/
idx = -1;
ntlv = (ipfw_obj_ntlv *)(ctlv + 1);
while (count > 0) {
if (ntlv->head.length != sizeof(ipfw_obj_ntlv))
return (EINVAL);
error = check_object_name(ntlv);
if (error != 0)
return (error);
if (ntlv->idx <= idx)
return (EINVAL);
idx = ntlv->idx;
count--;
ntlv++;
}
tstate = ctlv;
read += ctlv->head.length;
ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
}
if (read + sizeof(*ctlv) > sd->valsize)
return (EINVAL);
if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
clen = ctlv->head.length;
if (clen + read > sd->valsize || clen < sizeof(*ctlv))
return (EINVAL);
/*
* TODO: Permit adding multiple rules at once
*/
if (ctlv->count != 1)
return (ENOTSUP);
clen -= sizeof(*ctlv);
if (ctlv->count > clen / sizeof(struct ip_fw))
return (EINVAL);
/* Allocate state for each rule or use stack */
if (ctlv->count == 1) {
memset(&rci, 0, sizeof(struct rule_check_info));
cbuf = &rci;
} else
cbuf = malloc(ctlv->count * sizeof(*ci), M_TEMP,
M_WAITOK | M_ZERO);
ci = cbuf;
/*
* Check each rule for validness.
* Ensure numbered rules are sorted ascending.
*/
idx = -1;
r = (struct ip_fw *)(ctlv + 1);
count = 0;
error = 0;
while (clen > 0) {
rsize = RULESIZE(r);
if (rsize > clen || ctlv->count <= count) {
error = EINVAL;
break;
}
ci->ctlv = tstate;
error = check_ipfw_struct(r, rsize, ci);
if (error != 0)
break;
/* Check sorting */
if (r->rulenum != 0 && r->rulenum < idx) {
error = EINVAL;
break;
}
idx = r->rulenum;
ci->urule = r;
rsize = roundup2(rsize, sizeof(uint64_t));
clen -= rsize;
r = (struct ip_fw *)((caddr_t)r + rsize);
count++;
ci++;
}
if (ctlv->count != count || error != 0) {
if (cbuf != &rci)
free(cbuf, M_TEMP);
return (EINVAL);
}
rtlv = ctlv;
read += ctlv->head.length;
ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv + ctlv->head.length);
}
if (read != sd->valsize || rtlv == NULL || rtlv->count == 0) {
if (cbuf != NULL && cbuf != &rci)
free(cbuf, M_TEMP);
return (EINVAL);
}
/*
* Passed rules seems to be valid.
* 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);
}
if ((error = commit_rules(chain, cbuf, rtlv->count)) != 0) {
/* Free allocate krules */
for (i = 0, ci = cbuf; i < rtlv->count; i++, ci++)
free(ci->krule, M_IPFW);
}
if (cbuf != NULL && cbuf != &rci)
free(cbuf, M_TEMP);
return (error);
}
/**
* {set|get}sockopt parser.
*/
@ -1181,7 +1443,7 @@ ipfw_ctl(struct sockopt *sopt)
{
#define RULE_MAXSIZE (256*sizeof(u_int32_t))
int error;
size_t size, len, valsize;
size_t bsize_max, size, len, valsize;
struct ip_fw *buf, *rule;
struct ip_fw_chain *chain;
u_int32_t rulenum[2];
@ -1195,37 +1457,62 @@ ipfw_ctl(struct sockopt *sopt)
if (error)
return (error);
/*
* Disallow modifications in really-really secure mode, but still allow
* the logging counters to be reset.
*/
if (sopt->sopt_name == IP_FW_ADD ||
(sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) {
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
if (error)
return (error);
}
chain = &V_layer3_chain;
error = 0;
/* Save original valsize before it is altered via sooptcopyin() */
valsize = sopt->sopt_valsize;
memset(&sdata, 0, sizeof(sdata));
/* Read op3 header first to determine actual operation */
if ((opt = sopt->sopt_name) == IP_FW3) {
op3 = (ip_fw3_opheader *)xbuf;
error = sooptcopyin(sopt, op3, sizeof(*op3), sizeof(*op3));
if (error != 0)
return (error);
opt = op3->opcode;
sopt->sopt_valsize = valsize;
}
/*
* Disallow modifications in really-really secure mode, but still allow
* the logging counters to be reset.
*/
if (opt == IP_FW_ADD ||
(sopt->sopt_dir == SOPT_SET && opt != IP_FW_RESETLOG)) {
error = securelevel_ge(sopt->sopt_td->td_ucred, 3);
if (error != 0) {
if (sdata.kbuf != xbuf)
free(sdata.kbuf, M_TEMP);
return (error);
}
}
if (op3 != NULL) {
/*
* Determine buffer size:
* use on-stack xbuf for short request,
* allocate sliding-window buf for data export or
* contigious buffer for special ops.
*/
bsize_max = IP_FW3_WRITEBUF;
if (opt == IP_FW_ADD)
bsize_max = IP_FW3_READBUF;
/*
* Fill in sockopt_data structure that may be useful for
* IP_FW3 get requests
* IP_FW3 get requests.
*/
if (valsize <= sizeof(xbuf)) {
sdata.kbuf = xbuf;
sdata.ksize = sizeof(xbuf);
sdata.kavail = valsize;
} else {
if (valsize < IP_FW3_OPTBUF)
if (valsize < bsize_max)
size = valsize;
else
size = IP_FW3_OPTBUF;
size = bsize_max;
sdata.kbuf = malloc(size, M_TEMP, M_WAITOK | M_ZERO);
sdata.ksize = size;
@ -1236,8 +1523,8 @@ ipfw_ctl(struct sockopt *sopt)
sdata.valsize = valsize;
/*
* Copy either all request (if valsize < IP_FW3_OPTBUF)
* or first IP_FW3_OPTBUF bytes to guarantee most consumers
* Copy either all request (if valsize < bsize_max)
* or first bsize_max bytes to guarantee most consumers
* that all necessary data has been copied).
* Anyway, copy not less than sizeof(ip_fw3_opheader).
*/
@ -1287,6 +1574,10 @@ ipfw_ctl(struct sockopt *sopt)
error = dump_config(chain, &sdata);
break;
case IP_FW_XADD: /* IP_FW3 */
error = add_entry(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 */
@ -1324,7 +1615,12 @@ ipfw_ctl(struct sockopt *sopt)
}
if (error == 0) {
/* locking is done within add_rule() */
error = add_rule(chain, rule, &ci);
struct ip_fw *krule;
krule = malloc(RULESIZE(rule), M_IPFW, M_WAITOK);
copy_rule(rule, krule);
ci.urule = rule;
ci.krule = krule;
error = commit_rules(chain, &ci, 1);
size = RULESIZE(rule);
if (!error && sopt->sopt_dir == SOPT_GET) {
if (is7) {

View File

@ -1623,8 +1623,10 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
memset(&ti, 0, sizeof(ti));
ti.set = ci->tableset;
ti.tlvs = ci->tlvs;
ti.tlen = ci->tlen;
if (ci->ctlv != NULL) {
ti.tlvs = (void *)(ci->ctlv + 1);
ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
}
/*
* Stage 1: reference existing tables, determine number
@ -1723,7 +1725,6 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
goto free;
}
/*
* Attach new tables.
* We need to set table pointers for each new table,