svn commit: r269386 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Alexander V. Chernikov
melifaro at FreeBSD.org
Fri Aug 1 15:17:48 UTC 2014
Author: melifaro
Date: Fri Aug 1 15:17:46 2014
New Revision: 269386
URL: http://svnweb.freebsd.org/changeset/base/269386
Log:
* Permit limiting number of items in table.
Kernel changes:
* Add TEI_FLAGS_DONTADD entry flag to indicate that insert is not possible
* Support given flag in all algorithms
* Add "limit" field to ipfw_xtable_info
* Add actual limiting code into add_table_entry()
Userland changes:
* Add "limit" option as "create" table sub-option. Limit modification
is currently impossible.
* Print human-readable errors in table enry addition/deletion code.
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_table.c
projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c
Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sbin/ipfw/ipfw2.c Fri Aug 1 15:17:46 2014 (r269386)
@@ -580,11 +580,12 @@ do_cmd(int optname, void *optval, uintpt
*
* Assumes op3 header is already embedded.
* Calls setsockopt() with IP_FW3 as kernel-visible opcode.
- * Returns 0 on success or -1 otherwise.
+ * Returns 0 on success or errno otherwise.
*/
int
do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen)
{
+ int errno;
if (co.test_only)
return (0);
@@ -596,7 +597,10 @@ do_set3(int optname, ip_fw3_opheader *op
op3->opcode = optname;
- return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
+ if (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen) != 0)
+ return (errno);
+
+ return (0);
}
int
Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sbin/ipfw/ipfw2.h Fri Aug 1 15:17:46 2014 (r269386)
@@ -255,8 +255,6 @@ char const *match_value(struct _s_x *p,
size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table,
char *delimiter);
void fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear);
-void print_flags(char const *name, struct _s_x *list, uint8_t set,
- uint8_t clear);
void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set);
struct _ip_fw3_opheader;
Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sbin/ipfw/tables.c Fri Aug 1 15:17:46 2014 (r269386)
@@ -251,9 +251,10 @@ table_fill_objheader(ipfw_obj_header *oh
}
static struct _s_x tablenewcmds[] = {
- { "type", TOK_TYPE},
+ { "type", TOK_TYPE },
{ "valtype", TOK_VALTYPE },
{ "algo", TOK_ALGO },
+ { "limit", TOK_LIMIT },
{ NULL, 0 }
};
@@ -341,6 +342,11 @@ table_create(ipfw_obj_header *oh, int ac
ac--; av++;
switch (tcmd) {
+ case TOK_LIMIT:
+ NEED1("limit value required");
+ xi.limit = strtol(*av, NULL, 10);
+ ac--; av++;
+ break;
case TOK_TYPE:
NEED1("table type required");
/* Type may have suboptions after ':' */
@@ -485,6 +491,8 @@ table_show_info(ipfw_xtable_info *i, voi
printf(" valtype: %s, references: %u\n", vtype, i->refcnt);
printf(" algorithm: %s\n", i->algoname);
printf(" items: %u, size: %u\n", i->count, i->size);
+ if (i->limit > 0)
+ printf(" limit: %u\n", i->limit);
return (0);
}
@@ -561,8 +569,8 @@ table_modify_record(ipfw_obj_header *oh,
ipfw_obj_tentry tent;
ipfw_xtable_info xi;
uint8_t type, vtype;
- int cmd;
- char *texterr;
+ int cmd, error;
+ char *texterr, *etxt;
if (ac == 0)
errx(EX_USAGE, "address required");
@@ -592,14 +600,34 @@ table_modify_record(ipfw_obj_header *oh,
if (ac > 0)
tentry_fill_value(oh, &tent, *av, type, vtype);
cmd = IP_FW_TABLE_XADD;
- texterr = "setsockopt(IP_FW_TABLE_XADD)";
+ texterr = "Adding record failed";
} else {
cmd = IP_FW_TABLE_XDEL;
- texterr = "setsockopt(IP_FW_TABLE_XDEL)";
+ texterr = "Deleting record failed";
+ }
+
+ if ((error = table_do_modify_record(cmd, oh, &tent, update)) == 0)
+ return;
+
+ /* Try to provide more human-readable error */
+ switch (error) {
+ case EEXIST:
+ etxt = "record already exists";
+ break;
+ case EFBIG:
+ etxt = "limit hit";
+ break;
+ case ESRCH:
+ etxt = "table not found";
+ break;
+ case ENOENT:
+ etxt = "record not found";
+ break;
+ default:
+ etxt = strerror(error);
}
- if (table_do_modify_record(cmd, oh, &tent, update) != 0)
- err(EX_OSERR, "%s", texterr);
+ errx(EX_OSERR, "%s: %s", texterr, etxt);
}
static int
Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sys/netinet/ip_fw.h Fri Aug 1 15:17:46 2014 (r269386)
@@ -827,6 +827,8 @@ typedef struct _ipfw_xtable_info {
uint32_t refcnt; /* number of references */
uint32_t count; /* Number of records */
uint32_t size; /* Total size of records(export)*/
+ uint32_t limit; /* Max number of records */
+ uint32_t spare;
char tablename[64]; /* table name */
char algoname[64]; /* algorithm name */
ifpw_ta_tinfo ta_info; /* additional algo stats */
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c Fri Aug 1 15:17:46 2014 (r269386)
@@ -78,8 +78,9 @@ struct table_config {
uint8_t vtype; /* format table type */
uint8_t linked; /* 1 if already linked */
uint8_t tflags; /* type flags */
- uint8_t spare;
+ uint8_t spare;
uint32_t count; /* Number of records */
+ uint32_t limit; /* Max number of records */
uint64_t flags; /* state flags */
char tablename[64]; /* table name */
struct table_algo *ta; /* Callbacks for given algo */
@@ -102,7 +103,7 @@ static struct table_config *alloc_table_
static void free_table_config(struct namedobj_instance *ni,
struct table_config *tc);
static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
- char *aname, uint8_t tflags, uint8_t vtype);
+ char *aname, ipfw_xtable_info *i);
static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
static void free_table_state(void **state, void **xstate, uint8_t type);
@@ -132,7 +133,6 @@ static struct table_algo *find_table_alg
#define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k]))
-
int
add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
struct tentry_info *tei)
@@ -144,6 +144,7 @@ add_table_entry(struct ip_fw_chain *ch,
int error;
uint32_t num;
uint64_t aflags;
+ ipfw_xtable_info xi;
char ta_buf[128];
IPFW_UH_WLOCK(ch);
@@ -160,6 +161,13 @@ add_table_entry(struct ip_fw_chain *ch,
return (EINVAL);
}
+ /* Try to exit early on limit hit */
+ if (tc->limit != 0 && tc->count == tc->limit &&
+ (tei->flags & TEI_FLAGS_UPDATE) == 0) {
+ IPFW_UH_WUNLOCK(ch);
+ return (EFBIG);
+ }
+
/* Reference and unlock */
tc->no.refcnt++;
ta = tc->ta;
@@ -172,7 +180,10 @@ add_table_entry(struct ip_fw_chain *ch,
if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
return (ESRCH);
- error = create_table_internal(ch, ti, NULL, 0, IPFW_VTYPE_U32);
+ memset(&xi, 0, sizeof(xi));
+ xi.vtype = IPFW_VTYPE_U32;
+
+ error = create_table_internal(ch, ti, NULL, &xi);
if (error != 0)
return (error);
@@ -223,6 +234,22 @@ add_table_entry(struct ip_fw_chain *ch,
/* Update aflags since it can be changed after previous read */
aflags = tc->flags;
+ /* Check limit before adding */
+ if (tc->limit != 0 && tc->count == tc->limit) {
+ if ((tei->flags & TEI_FLAGS_UPDATE) == 0) {
+ IPFW_UH_WUNLOCK(ch);
+ return (EFBIG);
+ }
+
+ /*
+ * We have UPDATE flag set.
+ * Permit updating record (if found),
+ * but restrict adding new one since we've
+ * already hit the limit.
+ */
+ tei->flags |= TEI_FLAGS_DONTADD;
+ }
+
/* We've got valid table in @tc. Let's add data */
kidx = tc->no.kidx;
ta = tc->ta;
@@ -1187,7 +1214,7 @@ ipfw_create_table(struct ip_fw_chain *ch
}
IPFW_UH_RUNLOCK(ch);
- return (create_table_internal(ch, &ti, aname, i->tflags, i->vtype));
+ return (create_table_internal(ch, &ti, aname, i));
}
/*
@@ -1200,7 +1227,7 @@ ipfw_create_table(struct ip_fw_chain *ch
*/
static int
create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
- char *aname, uint8_t tflags, uint8_t vtype)
+ char *aname, ipfw_xtable_info *i)
{
struct namedobj_instance *ni;
struct table_config *tc;
@@ -1212,10 +1239,13 @@ create_table_internal(struct ip_fw_chain
ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, aname);
if (ta == NULL)
return (ENOTSUP);
-
- if ((tc = alloc_table_config(ch, ti, ta, aname, tflags, vtype)) == NULL)
+
+ tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype);
+ if (tc == NULL)
return (ENOMEM);
+ tc->limit = i->limit;
+
IPFW_UH_WLOCK(ch);
/* Check if table has been already created */
@@ -1293,6 +1323,7 @@ export_table_info(struct ip_fw_chain *ch
i->kidx = tc->no.kidx;
i->refcnt = tc->no.refcnt;
i->count = tc->count;
+ i->limit = tc->limit;
i->size = tc->count * sizeof(ipfw_obj_tentry);
i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h Fri Aug 1 15:17:46 2014 (r269386)
@@ -58,9 +58,10 @@ struct tentry_info {
uint16_t flags; /* record flags */
uint32_t value; /* value */
};
-#define TEI_FLAGS_UPDATE 0x01 /* Update record if exists */
+#define TEI_FLAGS_UPDATE 0x01 /* Add or update rec if exists */
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
#define TEI_FLAGS_COMPAT 0x04 /* Called from old ABI */
+#define TEI_FLAGS_DONTADD 0x08 /* Do not create new rec */
typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
struct table_info *ti, char *data, uint8_t tflags);
Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c Fri Aug 1 15:10:55 2014 (r269385)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c Fri Aug 1 15:17:46 2014 (r269386)
@@ -411,22 +411,12 @@ ta_add_cidr(void *ta_state, struct table
else
rnh = ti->xstate;
- rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
-
- if (rn == NULL) {
+ /* Search for an entry first */
+ rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
+ if (rn != NULL) {
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
return (EEXIST);
/* Record already exists. Update value if we're asked to */
- rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
- if (rn == NULL) {
-
- /*
- * Radix may have failed addition for other reasons
- * like failure in mask allocation code.
- */
- return (EINVAL);
- }
-
if (tei->subtype == AF_INET) {
/* IPv4. */
value = ((struct radix_cidr_entry *)tb->ent_ptr)->value;
@@ -444,6 +434,15 @@ ta_add_cidr(void *ta_state, struct table
return (0);
}
+ if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+ return (EFBIG);
+
+ rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
+ if (rn == NULL) {
+ /* Unknown error */
+ return (EINVAL);
+ }
+
tb->ent_ptr = NULL;
*pnum = 1;
@@ -1167,6 +1166,8 @@ ta_add_chash(void *ta_state, struct tabl
tei->flags |= TEI_FLAGS_UPDATED;
*pnum = 0;
} else {
+ if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+ return (EFBIG);
SLIST_INSERT_HEAD(&head[hash], ent, next);
tb->ent_ptr = NULL;
*pnum = 1;
@@ -1715,6 +1716,9 @@ ta_add_ifidx(void *ta_state, struct tabl
return (0);
}
+ if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+ return (EFBIG);
+
/* Link to internal list */
ipfw_objhash_add(icfg->ii, &ife->no);
@@ -2206,6 +2210,9 @@ ta_add_numarray(void *ta_state, struct t
return (0);
}
+ if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+ return (EFBIG);
+
res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
sizeof(struct numarray), compare_numarray);
@@ -2891,6 +2898,9 @@ ta_add_fhash(void *ta_state, struct tabl
tei->flags |= TEI_FLAGS_UPDATED;
*pnum = 0;
} else {
+ if ((tei->flags & TEI_FLAGS_DONTADD) != 0)
+ return (EFBIG);
+
SLIST_INSERT_HEAD(&head[hash], ent, next);
tb->ent_ptr = NULL;
*pnum = 1;
More information about the svn-src-projects
mailing list