* 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.
This commit is contained in:
Alexander V. Chernikov 2014-08-01 15:17:46 +00:00
parent 95c3c1e25f
commit 4c0c07a552
7 changed files with 107 additions and 33 deletions

View File

@ -580,11 +580,12 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
*
* 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 *op3, uintptr_t optlen)
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

View File

@ -255,8 +255,6 @@ char const *match_value(struct _s_x *p, int value);
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;

View File

@ -251,9 +251,10 @@ table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
}
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, char *av[])
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, void *arg)
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, int ac, char *av[], int add, int update
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, int ac, char *av[], int add, int update
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 (table_do_modify_record(cmd, oh, &tent, update) != 0)
err(EX_OSERR, "%s", texterr);
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);
}
errx(EX_OSERR, "%s: %s", texterr, etxt);
}
static int

View File

@ -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 */

View File

@ -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_config(struct ip_fw_chain *ch,
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_algo(struct tables_config *tableconf,
#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, struct tid_info *ti,
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, struct tid_info *ti,
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, struct tid_info *ti,
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, struct tid_info *ti,
/* 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, ip_fw3_opheader *op3,
}
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, ip_fw3_opheader *op3,
*/
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 *ch, struct tid_info *ti,
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, struct table_config *tc,
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));

View File

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

View File

@ -411,22 +411,12 @@ ta_add_cidr(void *ta_state, struct table_info *ti, struct tentry_info *tei,
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_info *ti, struct tentry_info *tei,
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 table_info *ti, struct tentry_info *tei,
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 table_info *ti, struct tentry_info *tei,
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 table_info *ti, struct tentry_info *tei,
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 table_info *ti, struct tentry_info *tei,
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;