* Add number:array algorithm lookup method.

Kernel changes:
* s/IPFW_TABLE_U32/IPFW_TABLE_NUMBER/
* Force "lookup <port|uid|gid|jid>" to be IPFW_TABLE_NUMBER
* Support "lookup" method for number tables
* Add number:array algorihm (i32 as key, auto-growing).

Userland changes:
* Support named tables in "lookup <tag> Table"
* Fix handling of "table(NAME,val)" case
* Support printing "number" table data.
This commit is contained in:
Alexander V. Chernikov 2014-07-30 14:52:26 +00:00
parent ce2817b51c
commit b23d5de9b6
6 changed files with 406 additions and 24 deletions

View File

@ -1096,6 +1096,7 @@ print_ip(struct format_opts *fo, ipfw_insn_ip *cmd, char const *s)
struct hostent *he = NULL;
uint32_t len = F_LEN((ipfw_insn *)cmd);
uint32_t *a = ((ipfw_insn_u32 *)cmd)->d;
char *t;
if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) {
uint32_t d = a[1];
@ -1103,8 +1104,9 @@ print_ip(struct format_opts *fo, ipfw_insn_ip *cmd, char const *s)
if (d < sizeof(lookup_key)/sizeof(lookup_key[0]))
arg = match_value(rule_options, lookup_key[d]);
printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "",
arg, cmd->o.arg1);
t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1);
printf("%s lookup %s %s", cmd->o.len & F_NOT ? " not": "",
arg, t);
return;
}
printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s);
@ -1115,7 +1117,6 @@ print_ip(struct format_opts *fo, ipfw_insn_ip *cmd, char const *s)
}
if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
cmd->o.opcode == O_IP_DST_LOOKUP) {
char *t;
t = table_search_ctlv(fo->tstate, ((ipfw_insn *)cmd)->arg1);
printf("table(%s", t);
if (len == F_INSN_SIZE(ipfw_insn_u32))
@ -2624,14 +2625,9 @@ struct tidx {
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);
@ -2694,6 +2690,9 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen, struct tidx *tstate)
}
if (strncmp(av, "table(", 6) == 0) {
if ((p = strchr(av + 6, ')')) == NULL)
errx(EX_DATAERR, "forgotten parenthesis: '%s'", av);
*p = '\0';
p = strchr(av + 6, ',');
if (p)
*p++ = '\0';
@ -2983,6 +2982,7 @@ ipfw_delete(char *av[])
static void
fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
{
char *p;
uint16_t uidx;
cmd->name[0] = '\0';
@ -2994,7 +2994,10 @@ fill_iface(ipfw_insn_if *cmd, char *arg, int cblen, struct tidx *tstate)
if (strcmp(arg, "any") == 0)
cmd->o.len = 0; /* effectively ignore this command */
else if (strncmp(arg, "table(", 6) == 0) {
char *p = strchr(arg + 6, ',');
if ((p = strchr(arg + 6, ')')) == NULL)
errx(EX_DATAERR, "forgotten parenthesis: '%s'", arg);
*p = '\0';
p = strchr(arg + 6, ',');
if (p)
*p++ = '\0';
if ((uidx = pack_table(tstate, arg + 6, 0)) == 0)
@ -4381,7 +4384,6 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
case TOK_LOOKUP: {
ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd;
char *p;
int j;
if (!av[0] || !av[1])
@ -4397,9 +4399,11 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
errx(EX_USAGE, "format: cannot lookup on %s", *av);
__PAST_END(c->d, 1) = j; // i converted to option
av++;
cmd->arg1 = strtoul(*av, &p, 0);
if (p && *p)
errx(EX_USAGE, "format: lookup argument tablenum");
if ((j = pack_table(tstate, *av, 0)) == 0)
errx(EX_DATAERR, "Invalid table name: %s", *av);
cmd->arg1 = j;
av++;
}
break;

View File

@ -82,7 +82,7 @@ static int tables_foreach(table_cb_t *f, void *arg, int sort);
static struct _s_x tabletypes[] = {
{ "cidr", IPFW_TABLE_CIDR },
{ "iface", IPFW_TABLE_INTERFACE },
{ "u32", IPFW_TABLE_U32 },
{ "number", IPFW_TABLE_NUMBER },
{ NULL, 0 }
};
@ -654,7 +654,7 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type)
/* Set mask to exact match */
masklen = 8 * IF_NAMESIZE;
break;
case IPFW_TABLE_U32:
case IPFW_TABLE_NUMBER:
/* Port or any other key */
key = strtol(arg, &p, 10);
if (*p != '\0')
@ -899,6 +899,16 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
inet_ntoa(*(struct in_addr *)&tval));
} else
printf("%s %u\n", tent->k.iface, tval);
break;
case IPFW_TABLE_NUMBER:
/* numbers */
if (co.do_value_as_ip) {
tval = htonl(tval);
printf("%u %s\n", tent->k.key,
inet_ntoa(*(struct in_addr *)&tval));
} else
printf("%u %u\n", tent->k.key, tval);
break;
}
}

View File

@ -674,7 +674,7 @@ struct _ipfw_dyn_rule {
#define IPFW_TABLE_CIDR 1 /* Table for holding IPv4/IPv6 prefixes */
#define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */
#define IPFW_TABLE_U32 3 /* Table for holidng ports/uid/gid/etc */
#define IPFW_TABLE_NUMBER 3 /* Table for holding ports/uid/gid/etc */
#define IPFW_TABLE_MAXTYPE 3 /* Maximum valid number */
#define IPFW_VTYPE_U32 1 /* Skipto/tablearg integer */

View File

@ -1473,9 +1473,9 @@ do { \
proto != IPPROTO_UDP)
break;
else if (v == 2)
key = htonl(dst_port);
key = dst_port;
else if (v == 3)
key = htonl(src_port);
key = src_port;
#ifndef USERSPACE
else if (v == 4 || v == 5) {
check_uidgid(
@ -1494,7 +1494,6 @@ do { \
else if (v == 5 /* O_JAIL */)
key = ucred_cache.xid;
#endif /* !__FreeBSD__ */
key = htonl(key);
} else
#endif /* !USERSPACE */
break;

View File

@ -593,6 +593,9 @@ ipfw_find_table_entry(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
IPFW_UH_RUNLOCK(ch);
return (EINVAL);
}
case IPFW_TABLE_NUMBER:
plen = sizeof(uint32_t);
break;
break;
default:
@ -1744,17 +1747,19 @@ classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
case 2:
case 3:
/* src/dst port */
//type = IPFW_TABLE_U16;
*ptype = IPFW_TABLE_NUMBER;
break;
case 4:
/* uid/gid */
//type = IPFW_TABLE_U32;
*ptype = IPFW_TABLE_NUMBER;
break;
case 5:
//type = IPFW_TABLE_U32;
/* jid */
*ptype = IPFW_TABLE_NUMBER;
break;
case 6:
//type = IPFW_TABLE_U16;
/* dscp */
*ptype = IPFW_TABLE_NUMBER;
break;
}
}

View File

@ -1376,7 +1376,7 @@ struct table_algo cidr_hash = {
*
* Runtime part:
* - sorted array of "struct ifidx" pointed by ti->state.
* Array is allocated with routing up to IFIDX_CHUNK. Only existing
* Array is allocated with rounding up to IFIDX_CHUNK. Only existing
* interfaces are stored in array, however its allocated size is
* sufficient to hold all table records if needed.
* - current array size is stored in ti->data
@ -2025,6 +2025,368 @@ struct table_algo iface_idx = {
.change_ti = ta_change_ti_ifidx,
};
/*
* Number array cmds.
*
* Implementation:
*
* Runtime part:
* - sorted array of "struct numarray" pointed by ti->state.
* Array is allocated with rounding up to NUMARRAY_CHUNK.
* - current array size is stored in ti->data
*
*/
struct numarray {
uint32_t number;
uint32_t value;
};
struct numarray_cfg {
void *main_ptr;
size_t size; /* Number of items allocated in array */
size_t used; /* Number of items _active_ now */
};
#define NUMARRAY_CHUNK 16
int compare_numarray(const void *k, const void *v);
int
compare_numarray(const void *k, const void *v)
{
struct numarray *na;
uint32_t key;
key = *((uint32_t *)k);
na = (struct numarray *)v;
if (key < na->number)
return (-1);
else if (key > na->number)
return (1);
return (0);
}
static struct numarray *
numarray_find(struct table_info *ti, void *key)
{
struct numarray *ri;
ri = bsearch(key, ti->state, ti->data, sizeof(struct numarray),
compare_ifidx);
return (ri);
}
static int
ta_lookup_numarray(struct table_info *ti, void *key, uint32_t keylen,
uint32_t *val)
{
struct numarray *ri;
ri = numarray_find(ti, key);
if (ri != NULL) {
*val = ri->value;
return (1);
}
return (0);
}
static int
ta_init_numarray(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
char *data)
{
struct numarray_cfg *cfg;
cfg = malloc(sizeof(*cfg), M_IPFW, M_WAITOK | M_ZERO);
cfg->size = NUMARRAY_CHUNK;
cfg->main_ptr = malloc(sizeof(struct numarray) * cfg->size, M_IPFW,
M_WAITOK | M_ZERO);
*ta_state = cfg;
ti->state = cfg->main_ptr;
ti->lookup = ta_lookup_numarray;
return (0);
}
/*
* Destroys table @ti
*/
static void
ta_destroy_numarray(void *ta_state, struct table_info *ti)
{
struct numarray_cfg *cfg;
cfg = (struct numarray_cfg *)ta_state;
if (cfg->main_ptr != NULL)
free(cfg->main_ptr, M_IPFW);
free(cfg, M_IPFW);
}
struct ta_buf_numarray
{
struct numarray na;
};
/*
* Prepare for addition/deletion to an array.
*/
static int
ta_prepare_add_numarray(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf)
{
struct ta_buf_numarray *tb;
tb = (struct ta_buf_numarray *)ta_buf;
memset(tb, 0, sizeof(*tb));
tb->na.number = *((uint32_t *)tei->paddr);
tb->na.value = tei->value;
return (0);
}
static int
ta_add_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
void *ta_buf, uint64_t *pflags, uint32_t *pnum)
{
struct numarray_cfg *cfg;
struct ta_buf_numarray *tb;
struct numarray *ri;
int res;
tb = (struct ta_buf_numarray*)ta_buf;
cfg = (struct numarray_cfg *)ta_state;
ri = numarray_find(ti, &tb->na.number);
if (ri != NULL) {
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
return (EEXIST);
/* We need to update value */
ri->value = tb->na.value;
/* Indicate that update has happened instead of addition */
tei->flags |= TEI_FLAGS_UPDATED;
*pnum = 0;
return (0);
}
res = badd(&tb->na.number, &tb->na, cfg->main_ptr, cfg->used,
sizeof(struct numarray), compare_numarray);
KASSERT(res == 1, ("number %d already exists", tb->na.number));
cfg->used++;
ti->data = cfg->used;
if (cfg->used + 1 == cfg->size) {
/* Notify core we need to grow */
*pflags = cfg->size + NUMARRAY_CHUNK;
}
*pnum = 1;
return (0);
}
/*
* Remove key from both configuration list and
* runtime array. Removed interface notification.
*/
static int
ta_del_numarray(void *ta_state, struct table_info *ti, struct tentry_info *tei,
void *ta_buf, uint64_t *pflags, uint32_t *pnum)
{
struct numarray_cfg *cfg;
struct ta_buf_numarray *tb;
struct numarray *ri;
int res;
tb = (struct ta_buf_numarray *)ta_buf;
cfg = (struct numarray_cfg *)ta_state;
ri = numarray_find(ti, &tb->na.number);
if (ri == NULL)
return (ENOENT);
res = bdel(&tb->na.number, cfg->main_ptr, cfg->used,
sizeof(struct numarray), compare_numarray);
KASSERT(res == 1, ("number %u does not exist", tb->na.number));
cfg->used--;
ti->data = cfg->used;
*pnum = 1;
return (0);
}
static void
ta_flush_numarray_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
void *ta_buf)
{
/* Do nothing */
}
/*
* Table growing callbacks.
*/
/*
* Allocate ned, larger runtime numarray array.
*/
static int
ta_prepare_mod_numarray(void *ta_buf, uint64_t *pflags)
{
struct mod_item *mi;
mi = (struct mod_item *)ta_buf;
memset(mi, 0, sizeof(struct mod_item));
mi->size = *pflags;
mi->main_ptr = malloc(sizeof(struct numarray) * mi->size, M_IPFW,
M_WAITOK | M_ZERO);
return (0);
}
/*
* Copy data from old runtime array to new one.
*/
static int
ta_fill_mod_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
uint64_t *pflags)
{
struct mod_item *mi;
struct numarray_cfg *cfg;
mi = (struct mod_item *)ta_buf;
cfg = (struct numarray_cfg *)ta_state;
/* Check if we still need to grow array */
if (cfg->size >= mi->size) {
*pflags = 0;
return (0);
}
memcpy(mi->main_ptr, cfg->main_ptr, cfg->used * sizeof(struct numarray));
return (0);
}
/*
* Switch old & new arrays.
*/
static int
ta_modify_numarray(void *ta_state, struct table_info *ti, void *ta_buf,
uint64_t pflags)
{
struct mod_item *mi;
struct numarray_cfg *cfg;
void *old_ptr;
mi = (struct mod_item *)ta_buf;
cfg = (struct numarray_cfg *)ta_state;
old_ptr = cfg->main_ptr;
cfg->main_ptr = mi->main_ptr;
cfg->size = mi->size;
ti->state = cfg->main_ptr;
mi->main_ptr = old_ptr;
return (0);
}
/*
* Free unneded array.
*/
static void
ta_flush_mod_numarray(void *ta_buf)
{
struct mod_item *mi;
mi = (struct mod_item *)ta_buf;
if (mi->main_ptr != NULL)
free(mi->main_ptr, M_IPFW);
}
static int
ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
ipfw_obj_tentry *tent)
{
struct numarray *na;
na = (struct numarray *)e;
tent->k.key = na->number;
tent->value = na->value;
return (0);
}
static int
ta_find_numarray_tentry(void *ta_state, struct table_info *ti, void *key,
uint32_t keylen, ipfw_obj_tentry *tent)
{
struct numarray_cfg *cfg;
struct numarray *ri;
cfg = (struct numarray_cfg *)ta_state;
ri = numarray_find(ti, key);
if (ri != NULL) {
ta_dump_numarray_tentry(ta_state, ti, ri, tent);
return (0);
}
return (ENOENT);
}
static void
ta_foreach_numarray(void *ta_state, struct table_info *ti, ta_foreach_f *f,
void *arg)
{
struct numarray_cfg *cfg;
struct numarray *array;
int i;
cfg = (struct numarray_cfg *)ta_state;
array = cfg->main_ptr;
for (i = 0; i < cfg->used; i++)
f(&array[i], arg);
}
struct table_algo number_array = {
.name = "number:array",
.type = IPFW_TABLE_NUMBER,
.init = ta_init_numarray,
.destroy = ta_destroy_numarray,
.prepare_add = ta_prepare_add_numarray,
.prepare_del = ta_prepare_add_numarray,
.add = ta_add_numarray,
.del = ta_del_numarray,
.flush_entry = ta_flush_numarray_entry,
.foreach = ta_foreach_numarray,
.dump_tentry = ta_dump_numarray_tentry,
.find_tentry = ta_find_numarray_tentry,
.prepare_mod = ta_prepare_mod_numarray,
.fill_mod = ta_fill_mod_numarray,
.modify = ta_modify_numarray,
.flush_mod = ta_flush_mod_numarray,
};
void
ipfw_table_algo_init(struct ip_fw_chain *ch)
{
@ -2037,6 +2399,7 @@ ipfw_table_algo_init(struct ip_fw_chain *ch)
ipfw_add_table_algo(ch, &cidr_radix, sz, &cidr_radix.idx);
ipfw_add_table_algo(ch, &cidr_hash, sz, &cidr_hash.idx);
ipfw_add_table_algo(ch, &iface_idx, sz, &iface_idx.idx);
ipfw_add_table_algo(ch, &number_array, sz, &number_array.idx);
}
void
@ -2046,6 +2409,7 @@ ipfw_table_algo_destroy(struct ip_fw_chain *ch)
ipfw_del_table_algo(ch, cidr_radix.idx);
ipfw_del_table_algo(ch, cidr_hash.idx);
ipfw_del_table_algo(ch, iface_idx.idx);
ipfw_del_table_algo(ch, number_array.idx);
}