svn commit: r268021 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Alexander V. Chernikov
melifaro at FreeBSD.org
Sun Jun 29 22:35:49 UTC 2014
Author: melifaro
Date: Sun Jun 29 22:35:47 2014
New Revision: 268021
URL: http://svnweb.freebsd.org/changeset/base/268021
Log:
* 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.
Modified:
projects/ipfw/sbin/ipfw/ipfw2.c
projects/ipfw/sbin/ipfw/ipfw2.h
projects/ipfw/sbin/ipfw/tables.c
projects/ipfw/sys/netinet/ip_fw.h
projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sbin/ipfw/ipfw2.c Sun Jun 29 22:35:47 2014 (r268021)
@@ -2493,6 +2493,56 @@ lookup_host (char *host, struct in_addr
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
* 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
}
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,
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 *a
}
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
}
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_ch
}
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
/* 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
}
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
/* 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
*
*/
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 @@ chkarg:
OR_START(source_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing source address");
- if (add_src(cmd, *av, proto, cblen)) {
+ if (add_src(cmd, *av, proto, cblen, tstate)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
@@ -3721,7 +3781,7 @@ chkarg:
OR_START(dest_ip);
NOT_BLOCK; /* optional "not" */
NEED1("missing dst address");
- if (add_dst(cmd, *av, proto, cblen)) {
+ if (add_dst(cmd, *av, proto, cblen, tstate)) {
av++;
if (F_LEN(cmd) != 0) { /* ! any */
prev = cmd;
@@ -3828,7 +3888,7 @@ read_options:
case TOK_VIA:
NEED1("recv, xmit, via require interface name"
" or address");
- fill_iface((ipfw_insn_if *)cmd, av[0], cblen);
+ fill_iface((ipfw_insn_if *)cmd, av[0], cblen, tstate);
av++;
if (F_LEN(cmd) == 0) /* not a valid address */
break;
@@ -4074,14 +4134,14 @@ read_options:
case TOK_SRCIP:
NEED1("missing source IP");
- if (add_srcip(cmd, *av, cblen)) {
+ if (add_srcip(cmd, *av, cblen, tstate)) {
av++;
}
break;
case TOK_DSTIP:
NEED1("missing destination IP");
- if (add_dstip(cmd, *av, cblen)) {
+ if (add_dstip(cmd, *av, cblen, tstate)) {
av++;
}
break;
@@ -4323,17 +4383,111 @@ done:
}
rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd);
- i = (char *)dst - (char *)rule;
- if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1)
- err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
+ *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);
}
/*
Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sbin/ipfw/ipfw2.h Sun Jun 29 22:35:47 2014 (r268021)
@@ -314,4 +314,6 @@ int fill_ext6hdr(struct _ipfw_insn *cmd,
/* 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);
Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sbin/ipfw/tables.c Sun Jun 29 22:35:47 2014 (r268021)
@@ -593,9 +593,29 @@ table_show_list(ipfw_obj_header *oh, int
}
}
+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, u
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, u
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);
+}
+
Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sys/netinet/ip_fw.h Sun Jun 29 22:35:47 2014 (r268021)
@@ -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;
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h Sun Jun 29 22:35:47 2014 (r268021)
@@ -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 */
};
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c Sun Jun 29 22:35:47 2014 (r268021)
@@ -169,53 +169,92 @@ swap_map(struct ip_fw_chain *chain, stru
}
/*
- * 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);
+ /* Check if we need to do table remap */
+ tcount = 0;
+ for (ci = rci, i = 0; i < count; ci++, i++) {
+ if (ci->table_opcodes == 0)
+ continue;
- 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);
+ /*
+ * Rule has some table opcodes.
+ * Reference & allocate needed tables/
+ */
+ error = ipfw_rewrite_table_uidx(chain, ci);
+ if (error != 0) {
- /* 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);
+ /*
+ * 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, stru
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, stru
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, in
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, s
}
#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);
+ 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 (sopt->sopt_name == IP_FW_ADD ||
- (sopt->sopt_dir == SOPT_SET && sopt->sopt_name != IP_FW_RESETLOG)) {
+ 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)
+ if (error != 0) {
+ if (sdata.kbuf != xbuf)
+ free(sdata.kbuf, M_TEMP);
return (error);
+ }
}
- chain = &V_layer3_chain;
- error = 0;
+ 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;
- /* Save original valsize before it is altered via sooptcopyin() */
- valsize = sopt->sopt_valsize;
- memset(&sdata, 0, sizeof(sdata));
- if ((opt = sopt->sopt_name) == IP_FW3) {
/*
* 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) {
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Sun Jun 29 19:22:49 2014 (r268020)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Sun Jun 29 22:35:47 2014 (r268021)
@@ -1623,8 +1623,10 @@ ipfw_rewrite_table_uidx(struct ip_fw_cha
memset(&ti, 0, sizeof(ti));
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-projects
mailing list