* 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:
parent
95c3c1e25f
commit
4c0c07a552
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user