Add support for multi-field values inside ipfw tables.

This is the last major change in given branch.

Kernel changes:
* Use 64-bytes structures to hold multi-value variables.
* Use shared array to hold values from all tables (assume
  each table algo is capable of holding 32-byte variables).
* Add some placeholders to support per-table value arrays in future.
* Use simple eventhandler-style API to ease the process of adding new
  table items. Currently table addition may required multiple UH drops/
  acquires which is quite tricky due to atomic table modificatio/swap
  support, shared array resize, etc. Deal with it by calling special
  notifier capable of rolling back state before actually performing
  swap/resize operations. Original operation then restarts itself after
  acquiring UH lock.
* Bump all objhash users default values to at least 64
* Fix custom hashing inside objhash.

Userland changes:
* Add support for dumping shared value array via "vlist" internal cmd.
* Some small print/fill_flags dixes to support u32 values.
* valtype is now bitmask of
  <skipto|pipe|fib|nat|dscp|tag|divert|netgraph|limit|ipv4|ipv6>.
  New values can hold distinct values for each of this types.
* Provide special "legacy" type which assumes all values are the same.
* More helpers/docs following..

Some examples:

3:41 [1] zfscurr0# ipfw table mimimi create valtype skipto,limit,ipv4,ipv6
3:41 [1] zfscurr0# ipfw table mimimi info
+++ table(mimimi), set(0) +++
 kindex: 2, type: addr
 references: 0, valtype: skipto,limit,ipv4,ipv6
 algorithm: addr:radix
 items: 0, size: 296
3:42 [1] zfscurr0# ipfw table mimimi add 10.0.0.5 3000,10,10.0.0.1,2a02:978:2::1
added: 10.0.0.5/32 3000,10,10.0.0.1,2a02:978:2::1
3:42 [1] zfscurr0# ipfw table mimimi list
+++ table(mimimi), set(0) +++
10.0.0.5/32 3000,0,10.0.0.1,2a02:978:2::1
This commit is contained in:
Alexander V. Chernikov 2014-08-31 23:51:09 +00:00
parent 1326363253
commit 0cba2b2802
15 changed files with 1528 additions and 276 deletions

View File

@ -712,12 +712,13 @@ concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter)
* helper function to process a set of flags and set bits in the
* appropriate masks.
*/
void
fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear)
int
fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
uint32_t *clear)
{
char *q; /* points to the separator */
int val;
uint8_t *which; /* mask we are working on */
uint32_t *which; /* mask we are working on */
while (p && *p) {
if (*p == '!') {
@ -729,15 +730,19 @@ fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear)
if (q)
*q++ = '\0';
val = match_token(flags, p);
if (val <= 0)
errx(EX_DATAERR, "invalid flag %s", p);
*which |= (uint8_t)val;
if (val <= 0) {
if (e != NULL)
*e = p;
return (-1);
}
*which |= (uint32_t)val;
p = q;
}
return (0);
}
void
print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set)
print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set)
{
char const *comma = "";
int i, l;
@ -2992,9 +2997,11 @@ static void
fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode,
struct _s_x *flags, char *p)
{
uint8_t set = 0, clear = 0;
char *e;
uint32_t set = 0, clear = 0;
fill_flags(flags, p, &set, &clear);
if (fill_flags(flags, p, &e, &set, &clear) != 0)
errx(EX_DATAERR, "invalid flag %s", e);
cmd->opcode = opcode;
cmd->len = (cmd->len & (F_NOT | F_OR)) | 1;
@ -4825,6 +4832,7 @@ ipfw_flush(int force)
static struct _s_x intcmds[] = {
{ "talist", TOK_TALIST },
{ "iflist", TOK_IFLIST },
{ "vlist", TOK_VLIST },
{ NULL, 0 }
};
@ -4846,6 +4854,9 @@ ipfw_internal_handler(int ac, char *av[])
case TOK_TALIST:
ipfw_list_ta(ac, av);
break;
case TOK_VLIST:
ipfw_list_values(ac, av);
break;
}
}

View File

@ -223,10 +223,10 @@ enum tokens {
TOK_VALTYPE,
TOK_ALGO,
TOK_TALIST,
TOK_FTYPE,
TOK_ATOMIC,
TOK_LOCK,
TOK_UNLOCK,
TOK_VLIST,
};
/*
@ -265,8 +265,9 @@ int match_token_relaxed(struct _s_x *table, char *string);
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_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set);
int fill_flags(struct _s_x *flags, char *p, char **e, uint32_t *set,
uint32_t *clear);
void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set);
struct _ip_fw3_opheader;
int do_cmd(int optname, void *optval, uintptr_t optlen);
@ -347,4 +348,5 @@ 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);
void ipfw_list_ta(int ac, char *av[]);
void ipfw_list_values(int ac, char *av[]);

View File

@ -67,9 +67,11 @@ static void table_show_list(ipfw_obj_header *oh, int need_header);
static void table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent);
static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
char *key, int add, uint8_t *ptype, uint8_t *pvtype, ipfw_xtable_info *xi);
char *key, int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi);
static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
char *arg, uint8_t type, uint8_t vtype);
char *arg, uint8_t type, uint32_t vmask);
static void table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
uint32_t vmask, int print_ip);
typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
static int tables_foreach(table_cb_t *f, void *arg, int sort);
@ -87,13 +89,17 @@ static struct _s_x tabletypes[] = {
};
static struct _s_x tablevaltypes[] = {
{ "number", IPFW_VTYPE_U32 },
{ NULL, 0 }
};
static struct _s_x tablefvaltypes[] = {
{ "ip", IPFW_VFTYPE_IP },
{ "number", IPFW_VFTYPE_U32 },
{ "skipto", IPFW_VTYPE_SKIPTO },
{ "pipe", IPFW_VTYPE_PIPE },
{ "fib", IPFW_VTYPE_FIB },
{ "nat", IPFW_VTYPE_NAT },
{ "dscp", IPFW_VTYPE_DSCP },
{ "tag", IPFW_VTYPE_TAG },
{ "divert", IPFW_VTYPE_DIVERT },
{ "netgraph", IPFW_VTYPE_NETGRAPH },
{ "limit", IPFW_VTYPE_LIMIT },
{ "ipv4", IPFW_VTYPE_NH4 },
{ "ipv6", IPFW_VTYPE_NH6 },
{ NULL, 0 }
};
@ -311,7 +317,6 @@ table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
static struct _s_x tablenewcmds[] = {
{ "type", TOK_TYPE },
{ "ftype", TOK_FTYPE },
{ "valtype", TOK_VALTYPE },
{ "algo", TOK_ALGO },
{ "limit", TOK_LIMIT },
@ -331,14 +336,16 @@ static struct _s_x flowtypecmds[] = {
int
table_parse_type(uint8_t ttype, char *p, uint8_t *tflags)
{
uint8_t fset, fclear;
uint32_t fset, fclear;
char *e;
/* Parse type options */
switch(ttype) {
case IPFW_TABLE_FLOW:
fset = fclear = 0;
fill_flags(flowtypecmds, p, &fset,
&fclear);
if (fill_flags(flowtypecmds, p, &e, &fset, &fclear) != 0)
errx(EX_USAGE,
"unable to parse flow option %s", e);
*tflags = fset;
break;
default:
@ -383,8 +390,9 @@ table_create(ipfw_obj_header *oh, int ac, char *av[])
{
ipfw_xtable_info xi;
int error, tcmd, val;
uint32_t fset, fclear;
size_t sz;
char *p;
char *e, *p;
char tbuf[128];
sz = sizeof(tbuf);
@ -424,27 +432,16 @@ table_create(ipfw_obj_header *oh, int ac, char *av[])
break;
case TOK_VALTYPE:
NEED1("table value type required");
val = match_token(tablevaltypes, *av);
fset = fclear = 0;
val = fill_flags(tablevaltypes, *av, &e, &fset, &fclear);
if (val != -1) {
xi.vtype = val;
xi.vmask = fset;
ac--; av++;
break;
}
concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
*av, tbuf);
break;
case TOK_FTYPE:
NEED1("table value format type required");
val = match_token(tablefvaltypes, *av);
if (val != -1) {
xi.vftype = val;
ac--; av++;
break;
}
concat_tokens(tbuf, sizeof(tbuf), tablefvaltypes, ", ");
errx(EX_USAGE, "Unknown format type: %s. Supported: %s",
*av, tbuf);
e, tbuf);
break;
case TOK_ALGO:
NEED1("table algorithm name required");
@ -462,8 +459,8 @@ table_create(ipfw_obj_header *oh, int ac, char *av[])
/* Set some defaults to preserve compability */
if (xi.algoname[0] == '\0' && xi.type == 0)
xi.type = IPFW_TABLE_ADDR;
if (xi.vtype == 0)
xi.vtype = IPFW_VTYPE_U32;
if (xi.vmask == 0)
xi.vmask = IPFW_VTYPE_LEGACY;
if ((error = table_do_create(oh, &xi)) != 0)
err(EX_OSERR, "Table creation failed");
@ -494,13 +491,13 @@ table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
/*
* Modifies existing table
*
* ipfw table NAME modify [ limit number ] [ ftype { number | ip } ]
* ipfw table NAME modify [ limit number ]
*/
static void
table_modify(ipfw_obj_header *oh, int ac, char *av[])
{
ipfw_xtable_info xi;
int error, tcmd, val;
int error, tcmd;
size_t sz;
char tbuf[128];
@ -518,19 +515,8 @@ table_modify(ipfw_obj_header *oh, int ac, char *av[])
xi.mflags |= IPFW_TMFLAGS_LIMIT;
ac--; av++;
break;
case TOK_FTYPE:
NEED1("table value format type required");
val = match_token(tablefvaltypes, *av);
if (val != -1) {
xi.vftype = val;
xi.mflags |= IPFW_TMFLAGS_FTYPE;
ac--; av++;
break;
}
concat_tokens(tbuf, sizeof(tbuf), tablefvaltypes, ", ");
errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
*av, tbuf);
break;
default:
errx(EX_USAGE, "cmd is not supported for modificatiob");
}
}
@ -726,34 +712,39 @@ table_show_tainfo(ipfw_xtable_info *i, struct ta_cldata *d,
}
}
static void
table_print_valheader(char *buf, size_t bufsize, uint32_t vmask)
{
if (vmask == IPFW_VTYPE_LEGACY) {
snprintf(buf, bufsize, "legacy");
return;
}
print_flags_buffer(buf, bufsize, tablevaltypes, vmask);
}
/*
* Prints table info struct @i in human-readable form.
*/
static int
table_show_info(ipfw_xtable_info *i, void *arg)
{
const char *vtype, *vftype;
const char *vtype;
ipfw_ta_tinfo *tainfo;
int afdata, afitem;
struct ta_cldata d;
char ttype[64], tvtype[64];
table_print_type(ttype, sizeof(ttype), i->type, i->tflags);
if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL)
vtype = "unknown";
if ((vftype = match_value(tablefvaltypes, i->vftype)) == NULL)
vftype = "unknown";
if (strcmp(vtype, vftype) != 0)
snprintf(tvtype, sizeof(tvtype), "%s(%s)", vtype, vftype);
else
snprintf(tvtype, sizeof(tvtype), "%s", vtype);
table_print_valheader(tvtype, sizeof(tvtype), i->vmask);
printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
if ((i->flags & IPFW_TGFLAGS_LOCKED) != 0)
printf(" kindex: %d, type: %s, locked\n", i->kidx, ttype);
else
printf(" kindex: %d, type: %s\n", i->kidx, ttype);
printf(" valtype: %s, references: %u\n", tvtype, i->refcnt);
printf(" references: %u, valtype: %s\n", i->refcnt, tvtype);
printf(" algorithm: %s\n", i->algoname);
printf(" items: %u, size: %u\n", i->count, i->size);
if (i->limit > 0)
@ -895,7 +886,8 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
{
ipfw_obj_tentry *ptent, tent, *tent_buf;
ipfw_xtable_info xi;
uint8_t type, vtype;
uint8_t type;
uint32_t vmask;
int cmd, count, error, i, ignored;
char *texterr, *etxt, *px;
@ -933,14 +925,14 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
memset(&xi, 0, sizeof(xi));
count = 0;
while (ac > 0) {
tentry_fill_key(oh, ptent, *av, add, &type, &vtype, &xi);
tentry_fill_key(oh, ptent, *av, add, &type, &vmask, &xi);
/*
* compability layer: auto-create table if not exists
*/
if (xi.tablename[0] == '\0') {
xi.type = type;
xi.vtype = vtype;
xi.vmask = vmask;
strlcpy(xi.tablename, oh->ntlv.name,
sizeof(xi.tablename));
fprintf(stderr, "DEPRECATED: inserting data info "
@ -953,7 +945,7 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
ac--; av++;
if (add != 0 && ac > 0) {
tentry_fill_value(oh, ptent, *av, type, vtype);
tentry_fill_value(oh, ptent, *av, type, vmask);
ac--; av++;
}
@ -966,6 +958,8 @@ table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add,
error = table_do_modify_record(cmd, oh, tent_buf, count, atomic);
quiet = 0;
/*
* Compatibility stuff: do not yell on duplicate keys or
* failed deletions.
@ -1062,7 +1056,8 @@ table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi,
{
char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
ipfw_obj_tentry *tent;
uint8_t type, vtype;
uint8_t type;
uint32_t vmask;
int error;
size_t sz;
@ -1074,7 +1069,7 @@ table_do_lookup(ipfw_obj_header *oh, char *key, ipfw_xtable_info *xi,
tent->head.length = sizeof(*tent);
tent->idx = 1;
tentry_fill_key(oh, tent, key, 0, &type, &vtype, xi);
tentry_fill_key(oh, tent, key, 0, &type, &vmask, xi);
oh->ntlv.type = type;
sz = sizeof(xbuf);
@ -1321,15 +1316,16 @@ tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
static void
tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
int add, uint8_t *ptype, uint8_t *pvtype, ipfw_xtable_info *xi)
int add, uint8_t *ptype, uint32_t *pvmask, ipfw_xtable_info *xi)
{
uint8_t type, tflags, vtype;
uint8_t type, tflags;
uint32_t vmask;
int error;
char *del;
type = 0;
tflags = 0;
vtype = 0;
vmask = 0;
if (xi->tablename[0] == '\0')
error = table_get_info(oh, xi);
@ -1340,7 +1336,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
/* Table found. */
type = xi->type;
tflags = xi->tflags;
vtype = xi->vtype;
vmask = xi->vmask;
} else {
if (error != ESRCH)
errx(EX_OSERR, "Error requesting table %s info",
@ -1359,11 +1355,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
/* OK Prepare and send */
type = IPFW_TABLE_ADDR;
/*
* XXX: Value type is forced to be u32.
* This should be changed for MFC.
*/
vtype = IPFW_VTYPE_U32;
vmask = IPFW_VTYPE_LEGACY;
} else {
/* Inknown key */
errx(EX_USAGE, "Table %s does not exist, cannot guess "
@ -1376,57 +1368,156 @@ tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
tentry_fill_key_type(key, tent, type, tflags);
*ptype = type;
*pvtype = vtype;
*pvmask = vmask;
}
static void
set_legacy_value(uint32_t val, ipfw_table_value *v)
{
v->tag = val;
v->pipe = val;
v->divert = val;
v->skipto = val;
v->netgraph = val;
v->fib = val;
v->nat = val;
v->nh4 = val;
v->dscp = (uint8_t)val;
v->limit = val;
}
static void
tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg,
uint8_t type, uint8_t vtype)
uint8_t type, uint32_t vmask)
{
uint32_t val;
char *p;
uint32_t a4, flag, val, vm;
ipfw_table_value *v;
uint32_t i;
char *comma, *e, *etype, *n, *p;
/* Try to interpret as number first */
tent->v.value = strtoul(arg, &p, 0);
if (*p == '\0')
return;
if (inet_pton(AF_INET, arg, &val) == 1) {
tent->v.value = ntohl(val);
return;
}
/* Try hostname */
if (lookup_host(arg, (struct in_addr *)&tent->v.value) == 0)
return;
errx(EX_OSERR, "Unable to parse value %s", arg);
#if 0
switch (vtype) {
case IPFW_VTYPE_U32:
tent->value = strtoul(arg, &p, 0);
if (*p != '\0')
errx(EX_USAGE, "Invalid number: %s", arg);
break;
case IPFW_VTYPE_IP:
if (inet_pton(AF_INET, arg, &tent->value) == 1)
break;
/* Try hostname */
if (lookup_host(arg, (struct in_addr *)&tent->value) != 0)
errx(EX_USAGE, "Invalid IPv4 address: %s", arg);
break;
case IPFW_VTYPE_DSCP:
if (isalpha(*arg)) {
if ((code = match_token(f_ipdscp, arg)) == -1)
errx(EX_DATAERR, "Unknown DSCP code");
} else {
code = strtoul(arg, NULL, 10);
if (code < 0 || code > 63)
errx(EX_DATAERR, "Invalid DSCP value");
v = &tent->v.value;
vm = vmask;
/* Compat layer: keep old behavior for legacy value types */
if (vmask == IPFW_VTYPE_LEGACY) {
/* Try to interpret as number first */
val = strtoul(arg, &p, 0);
if (*p == '\0') {
set_legacy_value(val, v);
return;
}
tent->value = code;
break;
default:
errx(EX_OSERR, "Unsupported format type %d", vtype);
if (inet_pton(AF_INET, arg, &val) == 1) {
set_legacy_value(ntohl(val), v);
return;
}
/* Try hostname */
if (lookup_host(arg, (struct in_addr *)&val) == 0) {
set_legacy_value(val, v);
return;
}
errx(EX_OSERR, "Unable to parse value %s", arg);
}
/*
* Shorthands: handle single value if vmask consists
* of numbers only. e.g.:
* vmask = "fib,skipto" -> treat input "1" as "1,1"
*/
n = arg;
etype = NULL;
for (i = 1; i < (1 << 31); i *= 2) {
if ((flag = (vmask & i)) == 0)
continue;
vmask &= ~flag;
if ((comma = strchr(n, ',')) != NULL)
*comma = '\0';
switch (flag) {
case IPFW_VTYPE_TAG:
v->tag = strtol(n, &e, 10);
if (*e != '\0')
etype = "tag";
break;
case IPFW_VTYPE_PIPE:
v->pipe = strtol(n, &e, 10);
if (*e != '\0')
etype = "pipe";
break;
case IPFW_VTYPE_DIVERT:
v->divert = strtol(n, &e, 10);
if (*e != '\0')
etype = "divert";
break;
case IPFW_VTYPE_SKIPTO:
v->skipto = strtol(n, &e, 10);
if (*e != '\0')
etype = "skipto";
break;
case IPFW_VTYPE_NETGRAPH:
v->netgraph = strtol(n, &e, 10);
if (*e != '\0')
etype = "netgraph";
break;
case IPFW_VTYPE_FIB:
v->fib = strtol(n, &e, 10);
if (*e != '\0')
etype = "fib";
break;
case IPFW_VTYPE_NAT:
v->nat = strtol(n, &e, 10);
if (*e != '\0')
etype = "nat";
break;
case IPFW_VTYPE_LIMIT:
v->limit = strtol(n, &e, 10);
if (*e != '\0')
etype = "limit";
break;
case IPFW_VTYPE_NH4:
if (strchr(n, '.') != NULL &&
inet_pton(AF_INET, n, &a4) == 1) {
v->nh4 = ntohl(a4);
break;
}
if (lookup_host(n, (struct in_addr *)&v->nh4) == 0)
break;
etype = "ipv4";
break;
case IPFW_VTYPE_DSCP:
if (isalpha(*n)) {
if ((v->dscp = match_token(f_ipdscp, n)) != -1)
break;
else
etype = "DSCP code";
} else {
v->dscp = strtol(n, &e, 10);
if (v->dscp > 63 || *e != '\0')
etype = "DSCP value";
}
break;
case IPFW_VTYPE_NH6:
if (strchr(n, ':') != NULL &&
inet_pton(AF_INET6, n, &v->nh6) == 1)
break;
etype = "ipv6";
break;
}
if (etype != NULL)
errx(EX_USAGE, "Unable to parse %s as %s", n, etype);
if (comma != NULL)
*comma++ = ',';
if ((n = comma) != NULL)
continue;
/* End of input. */
if (vmask != 0)
errx(EX_USAGE, "Not enough fields inside value");
}
#endif
}
/*
@ -1557,21 +1648,91 @@ table_show_list(ipfw_obj_header *oh, int need_header)
}
}
static void
table_show_value(char *buf, size_t bufsize, ipfw_table_value *v,
uint32_t vmask, int print_ip)
{
uint32_t flag, i, l;
size_t sz;
struct in_addr a4;
char abuf[INET6_ADDRSTRLEN];
sz = bufsize;
/*
* Some shorthands for printing values:
* legacy assumes all values are equal, so keep the first one.
*/
if (vmask == IPFW_VTYPE_LEGACY) {
if (print_ip != 0) {
flag = htonl(v->tag);
inet_ntop(AF_INET, &flag, buf, sz);
} else
snprintf(buf, sz, "%u", v->tag);
return;
}
for (i = 1; i < (1 << 31); i *= 2) {
if ((flag = (vmask & i)) == 0)
continue;
l = 0;
switch (flag) {
case IPFW_VTYPE_TAG:
l = snprintf(buf, sz, "%u,", v->tag);
break;
case IPFW_VTYPE_PIPE:
l = snprintf(buf, sz, "%u,", v->pipe);
break;
case IPFW_VTYPE_DIVERT:
l = snprintf(buf, sz, "%d,", v->divert);
break;
case IPFW_VTYPE_SKIPTO:
l = snprintf(buf, sz, "%d,", v->skipto);
break;
case IPFW_VTYPE_NETGRAPH:
l = snprintf(buf, sz, "%u,", v->netgraph);
break;
case IPFW_VTYPE_FIB:
l = snprintf(buf, sz, "%u,", v->fib);
break;
case IPFW_VTYPE_NAT:
l = snprintf(buf, sz, "%u,", v->nat);
break;
case IPFW_VTYPE_LIMIT:
l = snprintf(buf, sz, "%u,", v->limit);
break;
case IPFW_VTYPE_NH4:
a4.s_addr = htonl(v->nh4);
inet_ntop(AF_INET, &a4, abuf, sizeof(abuf));
l = snprintf(buf, sz, "%s,", abuf);
break;
case IPFW_VTYPE_DSCP:
l = snprintf(buf, sz, "%d,", v->dscp);
break;
case IPFW_VTYPE_NH6:
inet_ntop(AF_INET6, &v->nh6, abuf, sizeof(abuf));
l = snprintf(buf, sz, "%s,", abuf);
break;
}
buf += l;
sz -= l;
}
if (sz != bufsize)
*(buf - 1) = '\0';
}
static void
table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
{
char *comma, tbuf[128], pval[32];
char *comma, tbuf[128], pval[128];
void *paddr;
uint32_t tval;
struct tflow_entry *tfe;
tval = tent->v.value;
if (co.do_value_as_ip || i->vftype == IPFW_VFTYPE_IP) {
tval = htonl(tval);
inet_ntop(AF_INET, &tval, pval, sizeof(pval));
} else
snprintf(pval, sizeof(pval), "%u", tval);
table_show_value(pval, sizeof(pval), &tent->v.value, i->vmask,
co.do_value_as_ip);
switch (i->type) {
case IPFW_TABLE_ADDR:
@ -1633,7 +1794,7 @@ table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
}
static int
table_do_get_algolist(ipfw_obj_lheader **polh)
table_do_get_stdlist(uint16_t opcode, ipfw_obj_lheader **polh)
{
ipfw_obj_lheader req, *olh;
size_t sz;
@ -1642,7 +1803,7 @@ table_do_get_algolist(ipfw_obj_lheader **polh)
memset(&req, 0, sizeof(req));
sz = sizeof(req);
error = do_get3(IP_FW_TABLES_ALIST, &req.opheader, &sz);
error = do_get3(opcode, &req.opheader, &sz);
if (error != 0 && error != ENOMEM)
return (error);
@ -1651,7 +1812,7 @@ table_do_get_algolist(ipfw_obj_lheader **polh)
return (ENOMEM);
olh->size = sz;
if ((error = do_get3(IP_FW_TABLES_ALIST, &olh->opheader, &sz)) != 0) {
if ((error = do_get3(opcode, &olh->opheader, &sz)) != 0) {
free(olh);
return (error);
}
@ -1660,6 +1821,20 @@ table_do_get_algolist(ipfw_obj_lheader **polh)
return (0);
}
static int
table_do_get_algolist(ipfw_obj_lheader **polh)
{
return (table_do_get_stdlist(IP_FW_TABLES_ALIST, polh));
}
static int
table_do_get_vlist(ipfw_obj_lheader **polh)
{
return (table_do_get_stdlist(IP_FW_TABLE_VLIST, polh));
}
void
ipfw_list_ta(int ac, char *av[])
{
@ -1685,6 +1860,71 @@ ipfw_list_ta(int ac, char *av[])
free(olh);
}
/* Copy of current kernel table_value structure */
struct _table_value {
uint32_t tag; /* O_TAG/O_TAGGED */
uint32_t pipe; /* O_PIPE/O_QUEUE */
uint16_t divert; /* O_DIVERT/O_TEE */
uint16_t skipto; /* skipto, CALLRET */
uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */
uint32_t fib; /* O_SETFIB */
uint32_t nat; /* O_NAT */
uint32_t nh4;
uint8_t dscp;
uint8_t spare0[3];
/* -- 32 bytes -- */
struct in6_addr nh6;
uint32_t limit; /* O_LIMIT */
uint32_t spare1;
uint64_t refcnt; /* Number of references */
};
int
compare_values(const void *_a, const void *_b)
{
struct _table_value *a, *b;
a = (struct _table_value *)_a;
b = (struct _table_value *)_b;
if (a->spare1 < b->spare1)
return (-1);
else if (a->spare1 > b->spare1)
return (1);
return (0);
}
void
ipfw_list_values(int ac, char *av[])
{
ipfw_obj_lheader *olh;
struct _table_value *v;
int error, i;
uint32_t vmask;
char buf[128];
error = table_do_get_vlist(&olh);
if (error != 0)
err(EX_OSERR, "Unable to request value list");
vmask = 0x7FFFFFFF; /* Similar to IPFW_VTYPE_LEGACY */
table_print_valheader(buf, sizeof(buf), vmask);
printf("HEADER: %s\n", buf);
v = (struct _table_value *)(olh + 1);
qsort(v, olh->count, olh->objsize, compare_values);
for (i = 0; i < olh->count; i++) {
table_show_value(buf, sizeof(buf), (ipfw_table_value *)v,
vmask, 0);
printf("[%u] refs=%lu %s\n", v->spare1, v->refcnt, buf);
v = (struct _table_value *)((caddr_t)v + olh->objsize);
}
free(olh);
}
int
compare_ntlv(const void *_a, const void *_b)
{

View File

@ -3494,6 +3494,7 @@ netpfil/ipfw/ip_fw_pfil.c optional inet ipfirewall
netpfil/ipfw/ip_fw_sockopt.c optional inet ipfirewall
netpfil/ipfw/ip_fw_table.c optional inet ipfirewall
netpfil/ipfw/ip_fw_table_algo.c optional inet ipfirewall
netpfil/ipfw/ip_fw_table_value.c optional inet ipfirewall
netpfil/ipfw/ip_fw_iface.c optional inet ipfirewall
netpfil/ipfw/ip_fw_nat.c optional inet ipfirewall_nat
netpfil/pf/if_pflog.c optional pflog pf inet

View File

@ -8,6 +8,7 @@ KMOD= ipfw
SRCS= ip_fw2.c ip_fw_pfil.c
SRCS+= ip_fw_dynamic.c ip_fw_log.c
SRCS+= ip_fw_sockopt.c ip_fw_table.c ip_fw_table_algo.c ip_fw_iface.c
SRCS+= ip_fw_table_value.c
SRCS+= opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h opt_ipsec.h
CFLAGS+= -DIPFIREWALL

View File

@ -96,6 +96,7 @@ typedef struct _ip_fw3_opheader {
#define IP_FW_XIFLIST 107 /* list tracked interfaces */
#define IP_FW_TABLES_ALIST 108 /* list table algorithms */
#define IP_FW_TABLE_XSWAP 109 /* swap two tables */
#define IP_FW_TABLE_VLIST 110 /* dump table value hash */
/*
* The kernel representation of ipfw rules is made of a list of
@ -663,11 +664,18 @@ struct _ipfw_dyn_rule {
#define IPFW_TABLE_CIDR IPFW_TABLE_ADDR /* compat */
/* Value types */
#define IPFW_VTYPE_U32 1 /* Skipto/tablearg integer */
/* Value format types */
#define IPFW_VFTYPE_U32 0 /* Skipto/tablearg integer */
#define IPFW_VFTYPE_IP 1 /* Nexthop IP address */
#define IPFW_VTYPE_LEGACY 0xFFFFFFFF /* All data is filled in */
#define IPFW_VTYPE_SKIPTO 0x00000001 /* skipto/call/callreturn */
#define IPFW_VTYPE_PIPE 0x00000002 /* pipe/queue */
#define IPFW_VTYPE_FIB 0x00000004 /* setfib */
#define IPFW_VTYPE_NAT 0x00000008 /* nat */
#define IPFW_VTYPE_DSCP 0x00000010 /* dscp */
#define IPFW_VTYPE_TAG 0x00000020 /* tag/untag */
#define IPFW_VTYPE_DIVERT 0x00000040 /* divert/tee */
#define IPFW_VTYPE_NETGRAPH 0x00000080 /* netgraph/ngtee */
#define IPFW_VTYPE_LIMIT 0x00000100 /* IPv6 nexthop */
#define IPFW_VTYPE_NH4 0x00000200 /* IPv4 nexthop */
#define IPFW_VTYPE_NH6 0x00000400 /* IPv6 nexthop */
typedef struct _ipfw_table_entry {
in_addr_t addr; /* network address */
@ -751,6 +759,23 @@ struct tflow_entry {
} a;
};
typedef struct _ipfw_table_value {
uint32_t tag; /* O_TAG/O_TAGGED */
uint32_t pipe; /* O_PIPE/O_QUEUE */
uint16_t divert; /* O_DIVERT/O_TEE */
uint16_t skipto; /* skipto, CALLRET */
uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */
uint32_t fib; /* O_SETFIB */
uint32_t nat; /* O_NAT */
uint32_t nh4;
uint8_t dscp;
uint8_t spare0[3];
struct in6_addr nh6;
uint32_t limit; /* O_LIMIT */
uint32_t spare1;
uint64_t reserved;
} ipfw_table_value;
/* Table entry TLV */
typedef struct _ipfw_obj_tentry {
ipfw_obj_tlv head; /* TLV header */
@ -769,8 +794,8 @@ typedef struct _ipfw_obj_tentry {
struct tflow_entry flow;
} k;
union {
uint32_t value; /* 32-bit value */
char storage[64]; /* Future needs */
ipfw_table_value value; /* value data */
uint32_t kidx; /* value kernel index */
} v;
} ipfw_obj_tentry;
#define IPFW_TF_UPDATE 0x01 /* Update record if exists */
@ -839,10 +864,10 @@ typedef struct _ipfw_ta_tinfo {
typedef struct _ipfw_xtable_info {
uint8_t type; /* table type (addr,iface,..) */
uint8_t tflags; /* type flags */
uint8_t vtype; /* value type (u32) */
uint8_t vftype; /* value format type (ip,number)*/
uint16_t mflags; /* modification flags */
uint16_t flags; /* generic table flags */
uint16_t spare[3];
uint32_t vmask; /* bitmask with value types */
uint32_t set; /* set table is in */
uint32_t kidx; /* kernel index */
uint32_t refcnt; /* number of references */
@ -862,7 +887,6 @@ typedef struct _ipfw_xtable_info {
#define IPFW_TFFLAG_DSTPORT 0x08
#define IPFW_TFFLAG_PROTO 0x10
/* Table modification flags */
#define IPFW_TMFLAGS_FTYPE 0x0001 /* Change ftype field */
#define IPFW_TMFLAGS_LIMIT 0x0002 /* Change limit value */
#define IPFW_TMFLAGS_LOCK 0x0004 /* Change table lock state */

View File

@ -817,7 +817,7 @@ jump_fast(struct ip_fw_chain *chain, struct ip_fw *f, int num,
if (num != IP_FW_TARG && f->cached_id == chain->id)
f_pos = f->cached_pos;
else {
int i = IP_FW_ARG_TABLEARG(num);
int i = IP_FW_ARG_TABLEARG(chain, num, skipto);
/* make sure we do not jump backward */
if (jump_backwards == 0 && i <= f->rulenum)
i = f->rulenum + 1;
@ -844,7 +844,7 @@ jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num,
{
int f_pos;
num = IP_FW_ARG_TABLEARG(num);
num = IP_FW_ARG_TABLEARG(chain, num, skipto);
/* make sure we do not jump backward */
if (jump_backwards == 0 && num <= f->rulenum)
num = f->rulenum + 1;
@ -853,6 +853,7 @@ jump_linear(struct ip_fw_chain *chain, struct ip_fw *f, int num,
return (f_pos);
}
#define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f)
/*
* The main check routine for the firewall.
*
@ -1841,7 +1842,7 @@ do { \
}
case O_LOG:
ipfw_log(f, hlen, args, m,
ipfw_log(chain, f, hlen, args, m,
oif, offset | ip6f_mf, tablearg, ip);
match = 1;
break;
@ -1963,7 +1964,7 @@ do { \
case O_TAG: {
struct m_tag *mtag;
uint32_t tag = IP_FW_ARG_TABLEARG(cmd->arg1);
uint32_t tag = TARG(cmd->arg1, tag);
/* Packet is already tagged with this tag? */
mtag = m_tag_locate(m, MTAG_IPFW, tag, NULL);
@ -2044,7 +2045,7 @@ do { \
case O_TAGGED: {
struct m_tag *mtag;
uint32_t tag = IP_FW_ARG_TABLEARG(cmd->arg1);
uint32_t tag = TARG(cmd->arg1, tag);
if (cmdlen == 1) {
match = m_tag_locate(m, MTAG_IPFW,
@ -2115,7 +2116,7 @@ do { \
*/
case O_LIMIT:
case O_KEEP_STATE:
if (ipfw_install_state(f,
if (ipfw_install_state(chain, f,
(ipfw_insn_limit *)cmd, args, tablearg)) {
/* error or limit violation */
retval = IP_FW_DENY;
@ -2182,7 +2183,7 @@ do { \
case O_PIPE:
case O_QUEUE:
set_match(args, f_pos, chain);
args->rule.info = IP_FW_ARG_TABLEARG(cmd->arg1);
args->rule.info = TARG(cmd->arg1, pipe);
if (cmd->opcode == O_PIPE)
args->rule.info |= IPFW_IS_PIPE;
if (V_fw_one_pass)
@ -2202,7 +2203,7 @@ do { \
retval = (cmd->opcode == O_DIVERT) ?
IP_FW_DIVERT : IP_FW_TEE;
set_match(args, f_pos, chain);
args->rule.info = IP_FW_ARG_TABLEARG(cmd->arg1);
args->rule.info = TARG(cmd->arg1, divert);
break;
case O_COUNT:
@ -2409,7 +2410,7 @@ do { \
case O_NETGRAPH:
case O_NGTEE:
set_match(args, f_pos, chain);
args->rule.info = IP_FW_ARG_TABLEARG(cmd->arg1);
args->rule.info = TARG(cmd->arg1, netgraph);
if (V_fw_one_pass)
args->rule.info |= IPFW_ONEPASS;
retval = (cmd->opcode == O_NETGRAPH) ?
@ -2422,7 +2423,7 @@ do { \
uint32_t fib;
IPFW_INC_RULE_COUNTER(f, pktlen);
fib = IP_FW_ARG_TABLEARG(cmd->arg1) & 0x7FFFF;
fib = TARG(cmd->arg1, fib) & 0x7FFFF;
if (fib >= rt_numfibs)
fib = 0;
M_SETFIB(m, fib);
@ -2434,7 +2435,7 @@ do { \
case O_SETDSCP: {
uint16_t code;
code = IP_FW_ARG_TABLEARG(cmd->arg1) & 0x3F;
code = TARG(cmd->arg1, dscp) & 0x3F;
l = 0; /* exit inner loop */
if (is_ipv4) {
uint16_t a;
@ -2476,7 +2477,7 @@ do { \
}
t = ((ipfw_insn_nat *)cmd)->nat;
if (t == NULL) {
nat_id = IP_FW_ARG_TABLEARG(cmd->arg1);
nat_id = TARG(cmd->arg1, nat);
t = (*lookup_nat_ptr)(&chain->nat, nat_id);
if (t == NULL) {

View File

@ -672,8 +672,8 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule)
* session limitations are enforced.
*/
int
ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
struct ip_fw_args *args, uint32_t tablearg)
ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule,
ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg)
{
ipfw_dyn_rule *q;
int i;
@ -716,7 +716,7 @@ ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
uint16_t limit_mask = cmd->limit_mask;
int pindex;
conn_limit = IP_FW_ARG_TABLEARG(cmd->conn_limit);
conn_limit = IP_FW_ARG_TABLEARG(chain, cmd->conn_limit, limit);
DEB(
if (cmd->conn_limit == IP_FW_TARG)

View File

@ -240,14 +240,15 @@ ipfw_log_bpf(int onoff)
}
#endif /* !WITHOUT_BPF */
#define TARG(k, f) IP_FW_ARG_TABLEARG(chain, k, f)
/*
* We enter here when we have a rule with O_LOG.
* XXX this function alone takes about 2Kbytes of code!
*/
void
ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
struct ip *ip)
ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif,
u_short offset, uint32_t tablearg, struct ip *ip)
{
char *action;
int limit_reached = 0;
@ -343,27 +344,27 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
break;
case O_DIVERT:
snprintf(SNPARGS(action2, 0), "Divert %d",
cmd->arg1);
TARG(cmd->arg1, divert));
break;
case O_TEE:
snprintf(SNPARGS(action2, 0), "Tee %d",
cmd->arg1);
TARG(cmd->arg1, divert));
break;
case O_SETFIB:
snprintf(SNPARGS(action2, 0), "SetFib %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, fib));
break;
case O_SKIPTO:
snprintf(SNPARGS(action2, 0), "SkipTo %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, skipto));
break;
case O_PIPE:
snprintf(SNPARGS(action2, 0), "Pipe %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, pipe));
break;
case O_QUEUE:
snprintf(SNPARGS(action2, 0), "Queue %d",
IP_FW_ARG_TABLEARG(cmd->arg1));
TARG(cmd->arg1, pipe));
break;
case O_FORWARD_IP: {
ipfw_insn_sa *sa = (ipfw_insn_sa *)cmd;

View File

@ -152,10 +152,11 @@ void ipfw_nat_destroy(void);
/* In ip_fw_log.c */
struct ip;
struct ip_fw_chain;
void ipfw_log_bpf(int);
void ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
struct mbuf *m, struct ifnet *oif, u_short offset, uint32_t tablearg,
struct ip *ip);
void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif,
u_short offset, uint32_t tablearg, struct ip *ip);
VNET_DECLARE(u_int64_t, norule_counter);
#define V_norule_counter VNET(norule_counter)
VNET_DECLARE(int, verbose_limit);
@ -184,8 +185,8 @@ void ipfw_dyn_unlock(ipfw_dyn_rule *q);
struct tcphdr;
struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *,
u_int32_t, u_int32_t, int);
int ipfw_install_state(struct ip_fw *rule, ipfw_insn_limit *cmd,
struct ip_fw_args *args, uint32_t tablearg);
int ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule,
ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg);
ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt,
int *match_direction, struct tcphdr *tcp);
void ipfw_remove_dyn_children(struct ip_fw *rule);
@ -268,6 +269,7 @@ struct ip_fw_chain {
int n_rules; /* number of static rules */
LIST_HEAD(nat_list, cfg_nat) nat; /* list of nat entries */
void *tablestate; /* runtime table info */
void *valuestate; /* runtime table value info */
int *idxmap; /* skipto array of rules */
#if defined( __linux__ ) || defined( _WIN32 )
spinlock_t rwmtx;
@ -287,6 +289,25 @@ struct ip_fw_chain {
#endif
};
/* 64-byte structure representing multi-field table value */
struct table_value {
uint32_t tag; /* O_TAG/O_TAGGED */
uint32_t pipe; /* O_PIPE/O_QUEUE */
uint16_t divert; /* O_DIVERT/O_TEE */
uint16_t skipto; /* skipto, CALLRET */
uint32_t netgraph; /* O_NETGRAPH/O_NGTEE */
uint32_t fib; /* O_SETFIB */
uint32_t nat; /* O_NAT */
uint32_t nh4;
uint8_t dscp;
uint8_t spare0[3];
/* -- 32 bytes -- */
struct in6_addr nh6;
uint32_t limit; /* O_LIMIT */
uint32_t spare1;
uint64_t refcnt; /* Number of references */
};
struct namedobj_instance;
struct named_object {
@ -386,8 +407,9 @@ struct ipfw_ifc {
} while (0)
#endif
#define IP_FW_ARG_TABLEARG(a) (((a) == IP_FW_TARG) ? tablearg : (a))
#define TARG_VAL(ch, k, f) ((struct table_value *)((ch)->valuestate))[k].f
#define IP_FW_ARG_TABLEARG(ch, a, f) \
(((a) == IP_FW_TARG) ? TARG_VAL(ch, tablearg, f) : (a))
/*
* The lock is heavily used by ip_fw2.c (the main file) and ip_fw_nat.c
* so the variable and the macros must be here.

View File

@ -2509,6 +2509,10 @@ ipfw_ctl3(struct sockopt *sopt)
error = ipfw_list_table_algo(chain, &sdata);
break;
case IP_FW_TABLE_VLIST:
error = ipfw_list_table_values(chain, op3, &sdata);
break;
case IP_FW_TABLE_XGETSIZE:
{
uint32_t *tbl;
@ -2729,6 +2733,7 @@ ipfw_ctl(struct sockopt *sopt)
ipfw_table_entry ent;
struct tentry_info tei;
struct tid_info ti;
struct table_value v;
error = sooptcopyin(sopt, &ent,
sizeof(ent), sizeof(ent));
@ -2739,7 +2744,8 @@ ipfw_ctl(struct sockopt *sopt)
tei.paddr = &ent.addr;
tei.subtype = AF_INET;
tei.masklen = ent.masklen;
tei.value = ent.value;
ipfw_import_table_value_legacy(ent.value, &v);
tei.pvalue = &v;
memset(&ti, 0, sizeof(ti));
ti.uidx = ent.tbl;
ti.type = IPFW_TABLE_CIDR;
@ -2992,7 +2998,10 @@ ipfw_objhash_bitmap_alloc(uint32_t items, void **idx, int *pblocks)
int max_blocks;
u_long *idx_mask;
items = roundup2(items, BLOCK_ITEMS); /* Align to block size */
KASSERT((items % BLOCK_ITEMS) == 0,
("bitmask size needs to power of 2 and greater or equal to %d",
BLOCK_ITEMS));
max_blocks = items / BLOCK_ITEMS;
size = items / 8;
idx_mask = malloc(size * IPFW_MAX_SETS, M_IPFW, M_WAITOK);
@ -3111,11 +3120,8 @@ ipfw_objhash_set_funcs(struct namedobj_instance *ni, objhash_hash_f *hash_f,
static uint32_t
objhash_hash_name(struct namedobj_instance *ni, void *name, uint32_t set)
{
uint32_t v;
v = fnv_32_str((char *)name, FNV1_32_INIT);
return (v % ni->nn_size);
return (fnv_32_str((char *)name, FNV1_32_INIT));
}
static int
@ -3144,7 +3150,7 @@ ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name)
struct named_object *no;
uint32_t hash;
hash = ni->hash_f(ni, name, set);
hash = ni->hash_f(ni, name, set) % ni->nn_size;
TAILQ_FOREACH(no, &ni->names[hash], nn_next) {
if (ni->cmp_f(no, name, set) == 0)
@ -3186,7 +3192,7 @@ ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
{
uint32_t hash;
hash = ni->hash_f(ni, no->name, no->set);
hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
hash = objhash_hash_idx(ni, no->kidx);
@ -3200,7 +3206,7 @@ ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
{
uint32_t hash;
hash = ni->hash_f(ni, no->name, no->set);
hash = ni->hash_f(ni, no->name, no->set) % ni->nn_size;
TAILQ_REMOVE(&ni->names[hash], no, nn_next);
hash = objhash_hash_idx(ni, no->kidx);

View File

@ -62,44 +62,34 @@ __FBSDID("$FreeBSD$");
* Table has the following `type` concepts:
*
* `no.type` represents lookup key type (addr, ifp, uid, etc..)
* `vtype` represents table value type (currently U32)
* `ftype` (at the moment )is pure userland field helping to properly
* format value data e.g. "value is IPv4 nexthop" or "value is DSCP"
* or "value is port".
*
* vmask represents bitmask of table values which are present at the moment.
* Special IPFW_VTYPE_LEGACY ( (uint32_t)-1 ) represents old
* single-value-for-all approach.
*/
struct table_config {
struct named_object no;
uint8_t vtype; /* value type */
uint8_t vftype; /* value format type */
uint8_t tflags; /* type flags */
uint8_t locked; /* 1 if locked from changes */
uint32_t count; /* Number of records */
uint32_t limit; /* Max number of records */
uint8_t linked; /* 1 if already linked */
uint8_t ochanged; /* used by set swapping */
uint16_t spare1;
uint32_t spare2;
uint8_t vshared; /* 1 if using shared value array */
uint8_t spare[3];
uint32_t count; /* Number of records */
uint32_t limit; /* Max number of records */
uint32_t vmask; /* bitmask with supported values */
uint32_t ocount; /* used by set swapping */
uint64_t gencnt; /* generation count */
char tablename[64]; /* table name */
struct table_algo *ta; /* Callbacks for given algo */
void *astate; /* algorithm state */
struct table_info ti; /* data to put to table_info */
};
struct tables_config {
struct namedobj_instance *namehash;
int algo_count;
struct table_algo *algo[256];
struct table_algo *def_algo[IPFW_TABLE_MAXTYPE + 1];
struct namedobj_instance *vi;
};
static struct table_config *find_table(struct namedobj_instance *ni,
struct tid_info *ti);
static struct table_config *alloc_table_config(struct ip_fw_chain *ch,
struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags,
uint8_t vtype);
struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags);
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,
@ -126,8 +116,8 @@ static int ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3
static int swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
struct tid_info *b);
static int check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
struct table_info *ti, uint32_t count);
static int check_table_space(struct ip_fw_chain *ch, struct tableop_state *ts,
struct table_config *tc, struct table_info *ti, uint32_t count);
static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
static struct table_algo *find_table_algo(struct tables_config *tableconf,
@ -137,12 +127,65 @@ static void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
static void ntlv_to_ti(struct _ipfw_obj_ntlv *ntlv, struct tid_info *ti);
static int classify_table_opcode(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype);
#define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg)
#define CHAIN_TO_NI(chain) (CHAIN_TO_TCFG(chain)->namehash)
#define KIDX_TO_TI(ch, k) (&(((struct table_info *)(ch)->tablestate)[k]))
#define TA_BUF_SZ 128 /* On-stack buffer for add/delete state */
void
rollback_toperation_state(struct ip_fw_chain *ch, void *object)
{
struct tables_config *tcfg;
struct op_state *os;
tcfg = CHAIN_TO_TCFG(ch);
TAILQ_FOREACH(os, &tcfg->state_list, next)
os->func(os, object);
}
void
add_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts)
{
struct tables_config *tcfg;
tcfg = CHAIN_TO_TCFG(ch);
TAILQ_INSERT_HEAD(&tcfg->state_list, &ts->opstate, next);
}
void
del_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts)
{
struct tables_config *tcfg;
tcfg = CHAIN_TO_TCFG(ch);
TAILQ_REMOVE(&tcfg->state_list, &ts->opstate, next);
}
void
tc_ref(struct table_config *tc)
{
tc->no.refcnt++;
}
void
tc_unref(struct table_config *tc)
{
tc->no.refcnt--;
}
static struct table_value *
get_table_value(struct ip_fw_chain *ch, struct table_config *tc, uint32_t kidx)
{
struct table_value *pval;
pval = (struct table_value *)ch->valuestate;
return (&pval[kidx]);
}
/*
* Checks if we're able to insert/update entry @tei into table
* w.r.t @tc limits.
@ -222,8 +265,8 @@ create_table_compat(struct ip_fw_chain *ch, struct tid_info *ti,
int error;
memset(&xi, 0, sizeof(xi));
/* Set u32 as default value type for legacy clients */
xi.vtype = IPFW_VTYPE_U32;
/* Set default value mask for legacy clients */
xi.vmask = IPFW_VTYPE_LEGACY;
error = create_table_internal(ch, ti, NULL, &xi, pkidx, 1);
if (error != 0)
@ -410,6 +453,7 @@ flush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
caddr_t ta_buf_m, caddr_t ta_buf)
{
caddr_t v;
struct tentry_info *ptei;
size_t ta_buf_sz;
int i;
@ -417,8 +461,14 @@ flush_batch_buffer(struct ip_fw_chain *ch, struct table_algo *ta,
/* Run cleaning callback anyway */
v = ta_buf_m;
for (i = 0; i < count; i++, v += ta_buf_sz)
ta->flush_entry(ch, &tei[i], v);
for (i = 0; i < count; i++, v += ta_buf_sz) {
ptei = &tei[i];
ta->flush_entry(ch, ptei, v);
if (ptei->ptv != NULL) {
free(ptei->ptv, M_IPFW);
ptei->ptv = NULL;
}
}
/* Clean up "deleted" state in case of rollback */
if (rollback != 0) {
@ -453,23 +503,40 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
int error, first_error, i, rollback;
uint32_t num, numadd;
struct tentry_info *ptei;
struct tableop_state ts;
char ta_buf[TA_BUF_SZ];
caddr_t ta_buf_m, v;
memset(&ts, 0, sizeof(ts));
IPFW_UH_WLOCK(ch);
/*
* Find and reference existing table.
*/
IPFW_UH_WLOCK(ch);
restart:
if (ts.modified != 0) {
flush_batch_buffer(ch, ta, tei, count, rollback,
ta_buf_m, ta_buf);
memset(&ts, 0, sizeof(ts));
}
error = find_ref_table(ch, ti, tei, count, OP_ADD, &tc);
if (error != 0) {
IPFW_UH_WUNLOCK(ch);
return (error);
}
ta = tc->ta;
ts.ch = ch;
ts.tc = tc;
ts.vshared = tc->vshared;
ts.vmask = tc->vmask;
ts.ta = ta;
ts.tei = tei;
ts.count = count;
rollback = 0;
add_toperation_state(ch, &ts);
IPFW_UH_WUNLOCK(ch);
/* Allocate memory and prepare record(s) */
rollback = 0;
/* Pass stack buffer by default */
ta_buf_m = ta_buf;
error = prepare_batch_buffer(ch, ta, tei, count, OP_ADD, &ta_buf_m);
@ -477,32 +544,39 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
goto cleanup;
IPFW_UH_WLOCK(ch);
/* Drop reference we've used in first search */
tc->no.refcnt--;
/*
* Ensure we are able to add all entries without additional
* memory allocations. May release/reacquire UH_WLOCK.
* check_table_space() guarantees us @tc won't disappear
* by referencing it internally.
* Check if table swap has happened.
* (so table algo might be changed).
* Restart operation to achieve consistent behavior.
*/
kidx = tc->no.kidx;
error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), count);
if (error != 0) {
IPFW_UH_WUNLOCK(ch);
goto cleanup;
}
del_toperation_state(ch, &ts);
if (ts.modified != 0)
goto restart;
/*
* Check if table algo is still the same.
* (changed ta may be the result of table swap).
* Link all values values to shared/per-table value array.
*
* May release/reacquire UH_WLOCK.
*/
if (ta != tc->ta) {
IPFW_UH_WUNLOCK(ch);
error = EINVAL;
error = ipfw_link_table_values(ch, &ts);
if (error != 0)
goto cleanup;
}
if (ts.modified != 0)
goto restart;
/*
* Ensure we are able to add all entries without additional
* memory allocations. May release/reacquire UH_WLOCK.
*/
kidx = tc->no.kidx;
error = check_table_space(ch, &ts, tc, KIDX_TO_TI(ch, kidx), count);
if (error != 0)
goto cleanup;
if (ts.modified != 0)
goto restart;
/* We've got valid table in @tc. Let's try to add data */
kidx = tc->no.kidx;
@ -543,23 +617,27 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
rollback_added_entries(ch, tc, KIDX_TO_TI(ch, kidx),
tei, ta_buf_m, count, i);
rollback = 1;
break;
}
IPFW_WUNLOCK(ch);
ipfw_finalize_table_values(ch, tc, tei, count, rollback);
/* Permit post-add algorithm grow/rehash. */
if (numadd != 0)
check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
IPFW_UH_WUNLOCK(ch);
check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0);
/* Return first error to user, if any */
error = first_error;
cleanup:
flush_batch_buffer(ch, ta, tei, count, rollback, ta_buf_m, ta_buf);
IPFW_UH_WUNLOCK(ch);
flush_batch_buffer(ch, ta, tei, count, rollback, ta_buf_m, ta_buf);
return (error);
}
@ -635,9 +713,12 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
}
IPFW_WUNLOCK(ch);
/* Unlink non-used values */
ipfw_finalize_table_values(ch, tc, tei, count, 0);
if (numdel != 0) {
/* Run post-del hook to permit shrinking */
error = check_table_space(ch, tc, KIDX_TO_TI(ch, kidx), 0);
check_table_space(ch, NULL, tc, KIDX_TO_TI(ch, kidx), 0);
}
IPFW_UH_WUNLOCK(ch);
@ -666,8 +747,8 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
* Returns 0 on success.
*/
static int
check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
struct table_info *ti, uint32_t count)
check_table_space(struct ip_fw_chain *ch, struct tableop_state *ts,
struct table_config *tc, struct table_info *ti, uint32_t count)
{
struct table_algo *ta;
uint64_t pflags;
@ -697,15 +778,30 @@ check_table_space(struct ip_fw_chain *ch, struct table_config *tc,
}
/* We have to shrink/grow table */
if (ts != NULL)
add_toperation_state(ch, ts);
IPFW_UH_WUNLOCK(ch);
memset(&ta_buf, 0, sizeof(ta_buf));
if ((error = ta->prepare_mod(ta_buf, &pflags)) != 0) {
IPFW_UH_WLOCK(ch);
break;
}
error = ta->prepare_mod(ta_buf, &pflags);
IPFW_UH_WLOCK(ch);
if (ts != NULL)
del_toperation_state(ch, ts);
if (error != 0)
break;
if (ts != NULL && ts->modified != 0) {
/*
* Swap operation has happened
* so we're currently operating on other
* table data. Stop doing this.
*/
ta->flush_mod(ta_buf);
break;
}
/* Check if we still need to alter table */
ti = KIDX_TO_TI(ch, tc->no.kidx);
@ -774,6 +870,7 @@ ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
ipfw_table_xentry *xent;
struct tentry_info tei;
struct tid_info ti;
struct table_value v;
int error, hdrlen, read;
hdrlen = offsetof(ipfw_table_xentry, k);
@ -792,7 +889,8 @@ ipfw_manage_table_ent_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
memset(&tei, 0, sizeof(tei));
tei.paddr = &xent->k;
tei.masklen = xent->masklen;
tei.value = xent->value;
ipfw_import_table_value_legacy(xent->value, &v);
tei.pvalue = &v;
/* Old requests compability */
tei.flags = TEI_FLAGS_COMPAT;
if (xent->type == IPFW_TABLE_ADDR) {
@ -899,7 +997,9 @@ ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
ptei->masklen = ptent->masklen;
if (ptent->head.flags & IPFW_TF_UPDATE)
ptei->flags |= TEI_FLAGS_UPDATE;
ptei->value = ptent->v.value;
ipfw_import_table_value_v1(&ptent->v.value);
ptei->pvalue = (struct table_value *)&ptent->v.value;
}
error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
@ -924,6 +1024,7 @@ ipfw_manage_table_ent_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
ptent->result = IPFW_TR_NOTFOUND;
else if (ptei->flags & TEI_FLAGS_EXISTS)
ptent->result = IPFW_TR_EXISTS;
ipfw_export_table_value_v1(ptei->pvalue, &ptent->v.value);
}
if (tei_buf != &tei)
@ -1113,10 +1214,14 @@ flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
tc->count = 0;
tc->no.refcnt--;
/*
* Stage 4: unref values.
*/
ipfw_unref_table_values(ch, tc, ta, astate_old, &ti_old);
IPFW_UH_WUNLOCK(ch);
/*
* Stage 4: perform real flush.
* Stage 5: perform real flush/destroy.
*/
ta->destroy(astate_old, &ti_old);
@ -1212,8 +1317,7 @@ swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
}
/* Check type and value are the same */
if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags ||
tc_a->vtype != tc_b->vtype) {
if (tc_a->no.type != tc_b->no.type || tc_a->tflags != tc_b->tflags) {
IPFW_UH_WUNLOCK(ch);
return (EINVAL);
}
@ -1231,6 +1335,10 @@ swap_tables(struct ip_fw_chain *ch, struct tid_info *a,
return (EACCES);
}
/* Notify we're going to swap */
rollback_toperation_state(ch, tc_a);
rollback_toperation_state(ch, tc_b);
/* Everything is fine, prepare to swap */
tablestate = (struct table_info *)ch->tablestate;
ti = tablestate[tc_a->no.kidx];
@ -1302,6 +1410,9 @@ destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
printf("Error unlinking kidx %d from table %s\n",
tc->no.kidx, tc->tablename);
/* Unref values used in tables while holding UH lock */
ipfw_unref_table_values(ch, tc, tc->ta, tc->astate,
&((struct table_info *)ch->tablestate)[tc->no.kidx]);
IPFW_UH_WUNLOCK(ch);
free_table_config(ni, tc);
@ -1338,6 +1449,7 @@ ipfw_destroy_tables(struct ip_fw_chain *ch)
/* Free pointers itself */
free(ch->tablestate, M_IPFW);
ipfw_table_value_destroy(ch);
ipfw_table_algo_destroy(ch);
ipfw_objhash_destroy(CHAIN_TO_NI(ch));
@ -1360,6 +1472,7 @@ ipfw_init_tables(struct ip_fw_chain *ch)
tcfg->namehash = ipfw_objhash_create(V_fw_tables_max);
ch->tblcfg = tcfg;
ipfw_table_value_init(ch);
ipfw_table_algo_init(ch);
return (0);
@ -1654,8 +1767,6 @@ ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
return (EACCES);
}
if ((i->mflags & IPFW_TMFLAGS_FTYPE) != 0)
tc->vftype = i->vftype;
if ((i->mflags & IPFW_TMFLAGS_LIMIT) != 0)
tc->limit = i->limit;
if ((i->mflags & IPFW_TMFLAGS_LOCK) != 0)
@ -1744,11 +1855,11 @@ create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
if (ta == NULL)
return (ENOTSUP);
tc = alloc_table_config(ch, ti, ta, aname, i->tflags, i->vtype);
tc = alloc_table_config(ch, ti, ta, aname, i->tflags);
if (tc == NULL)
return (ENOMEM);
tc->vftype = i->vftype;
tc->vmask = i->vmask;
tc->limit = i->limit;
if (ta->flags & TA_FLAG_READONLY)
tc->locked = 1;
@ -1764,10 +1875,9 @@ create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
/*
* Compat: do not fail if we're
* requesting to create existing table
* which has the same type / vtype
* which has the same type
*/
if (compat == 0 || tc_new->no.type != tc->no.type ||
tc_new->vtype != tc->vtype) {
if (compat == 0 || tc_new->no.type != tc->no.type) {
IPFW_UH_WUNLOCK(ch);
free_table_config(ni, tc);
return (EEXIST);
@ -1891,6 +2001,7 @@ ipfw_mark_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule,
}
struct dump_args {
struct ip_fw_chain *ch;
struct table_info *ti;
struct table_config *tc;
struct sockopt_data *sd;
@ -1959,8 +2070,7 @@ export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
i->type = tc->no.type;
i->tflags = tc->tflags;
i->vtype = tc->vtype;
i->vftype = tc->vftype;
i->vmask = tc->vmask;
i->set = tc->no.set;
i->kidx = tc->no.kidx;
i->refcnt = tc->no.refcnt;
@ -2113,6 +2223,7 @@ ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
* Do the actual dump in eXtended format
*/
memset(&da, 0, sizeof(da));
da.ch = ch;
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
da.tc = tc;
da.sd = sd;
@ -2177,6 +2288,7 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
/* Do the actual dump in eXtended format */
memset(&da, 0, sizeof(da));
da.ch = ch;
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
da.tc = tc;
da.sd = sd;
@ -2231,6 +2343,7 @@ dump_table_entry(void *e, void *arg)
struct table_config *tc;
struct table_algo *ta;
ipfw_table_entry *ent;
struct table_value *pval;
int error;
da = (struct dump_args *)arg;
@ -2251,7 +2364,8 @@ dump_table_entry(void *e, void *arg)
ent->addr = da->tent.k.addr.s_addr;
ent->masklen = da->tent.masklen;
ent->value = da->tent.v.value;
pval = get_table_value(da->ch, da->tc, da->tent.v.kidx);
ent->value = ipfw_export_table_value_legacy(pval);
return (0);
}
@ -2279,6 +2393,7 @@ ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
return (0);
memset(&da, 0, sizeof(da));
da.ch = ch;
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
da.tc = tc;
da.ent = &tbl->ent[0];
@ -2300,7 +2415,9 @@ dump_table_tentry(void *e, void *arg)
struct dump_args *da;
struct table_config *tc;
struct table_algo *ta;
struct table_value *pval;
ipfw_obj_tentry *tent;
int error;
da = (struct dump_args *)arg;
@ -2316,7 +2433,14 @@ dump_table_tentry(void *e, void *arg)
tent->head.length = sizeof(ipfw_obj_tentry);
tent->idx = da->uidx;
return (ta->dump_tentry(tc->astate, da->ti, e, tent));
error = ta->dump_tentry(tc->astate, da->ti, e, tent);
if (error != 0)
return (error);
pval = get_table_value(da->ch, da->tc, tent->v.kidx);
ipfw_export_table_value_v1(pval, &tent->v.value);
return (0);
}
/*
@ -2330,6 +2454,7 @@ dump_table_xentry(void *e, void *arg)
struct table_algo *ta;
ipfw_table_xentry *xent;
ipfw_obj_tentry *tent;
struct table_value *pval;
int error;
da = (struct dump_args *)arg;
@ -2352,7 +2477,8 @@ dump_table_xentry(void *e, void *arg)
/* Convert current format to previous one */
xent->masklen = tent->masklen;
xent->value = tent->v.value;
pval = get_table_value(da->ch, da->tc, da->tent.v.kidx);
xent->value = ipfw_export_table_value_legacy(pval);
/* Apply some hacks */
if (tc->no.type == IPFW_TABLE_ADDR && tent->subtype == AF_INET) {
xent->k.addr6.s6_addr32[3] = tent->k.addr.s_addr;
@ -2412,6 +2538,7 @@ ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx,
ta = tc->ta;
memset(&da, 0, sizeof(da));
da.ch = ch;
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
da.tc = tc;
da.f = f;
@ -2822,7 +2949,7 @@ find_table(struct namedobj_instance *ni, struct tid_info *ti)
*/
static struct table_config *
alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
struct table_algo *ta, char *aname, uint8_t tflags, uint8_t vtype)
struct table_algo *ta, char *aname, uint8_t tflags)
{
char *name, bname[16];
struct table_config *tc;
@ -2849,7 +2976,8 @@ alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
tc->tflags = tflags;
tc->ta = ta;
strlcpy(tc->tablename, name, sizeof(tc->tablename));
tc->vtype = vtype;
/* Set "shared" value type by default */
tc->vshared = 1;
if (ti->tlvs == NULL) {
tc->no.compat = 1;

View File

@ -34,6 +34,18 @@
*/
#ifdef _KERNEL
struct table_algo;
struct tables_config {
struct namedobj_instance *namehash;
struct namedobj_instance *valhash;
uint32_t val_size;
uint32_t algo_count;
struct table_algo *algo[256];
struct table_algo *def_algo[IPFW_TABLE_MAXTYPE + 1];
TAILQ_HEAD(op_state_l,op_state) state_list;
};
#define CHAIN_TO_TCFG(chain) ((struct tables_config *)(chain)->tblcfg)
struct table_info {
table_lookup_t *lookup; /* Lookup function */
void *state; /* Lookup radix/other structure */
@ -51,12 +63,15 @@ struct tid_info {
int tlen; /* Total TLV size block */
};
struct table_value;
struct tentry_info {
void *paddr;
struct table_value *pvalue;
void *ptv; /* Temporary field to hold obj */
uint8_t masklen; /* mask length */
uint8_t subtype;
uint16_t flags; /* record flags */
uint32_t value; /* value */
uint32_t value; /* value index */
};
#define TEI_FLAGS_UPDATE 0x0001 /* Add or update rec if exists */
#define TEI_FLAGS_UPDATED 0x0002 /* Entry has been updated */
@ -113,6 +128,7 @@ struct table_algo {
uint32_t type;
uint32_t refcnt;
uint32_t flags;
uint32_t vlimit;
size_t ta_buf_size;
ta_init *init;
ta_destroy *destroy;
@ -171,6 +187,23 @@ int add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
int del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
struct tentry_info *tei, uint8_t flags, uint32_t count);
int flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
void ipfw_import_table_value_legacy(uint32_t value, struct table_value *v);
uint32_t ipfw_export_table_value_legacy(struct table_value *v);
/* ipfw_table_value.c functions */
int ipfw_list_table_values(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
struct table_config;
struct tableop_state;
void ipfw_table_value_init(struct ip_fw_chain *ch);
void ipfw_table_value_destroy(struct ip_fw_chain *ch);
int ipfw_link_table_values(struct ip_fw_chain *ch, struct tableop_state *ts);
void ipfw_finalize_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct tentry_info *tei, uint32_t count, int rollback);
void ipfw_import_table_value_v1(ipfw_table_value *iv);
void ipfw_export_table_value_v1(struct table_value *v, ipfw_table_value *iv);
void ipfw_unref_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct table_algo *ta, void *astate, struct table_info *ti);
int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
struct rule_check_info *ci);
@ -191,6 +224,33 @@ void ipfw_swap_tables_sets(struct ip_fw_chain *ch, uint32_t old_set,
int ipfw_foreach_table_tentry(struct ip_fw_chain *ch, uint16_t kidx,
ta_foreach_f f, void *arg);
/* internal functions */
void tc_ref(struct table_config *tc);
void tc_unref(struct table_config *tc);
struct op_state;
typedef void (op_rollback_f)(void *object, struct op_state *state);
struct op_state {
TAILQ_ENTRY(op_state) next; /* chain link */
op_rollback_f *func;
};
struct tableop_state {
struct op_state opstate;
struct ip_fw_chain *ch;
struct table_config *tc;
struct table_algo *ta;
struct tentry_info *tei;
uint32_t count;
uint32_t vmask;
int vshared;
int modified;
};
void add_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts);
void del_toperation_state(struct ip_fw_chain *ch, struct tableop_state *ts);
void rollback_toperation_state(struct ip_fw_chain *ch, void *object);
/* Legacy interfaces */
int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti,
uint32_t *cnt);

View File

@ -486,14 +486,14 @@ ta_dump_radix_tentry(void *ta_state, struct table_info *ti, void *e,
tent->k.addr.s_addr = n->addr.sin_addr.s_addr;
tent->masklen = n->masklen;
tent->subtype = AF_INET;
tent->v.value = n->value;
tent->v.kidx = n->value;
#ifdef INET6
} else {
xn = (struct radix_addr_xentry *)e;
memcpy(&tent->k, &xn->addr6.sin6_addr, sizeof(struct in6_addr));
tent->masklen = xn->masklen;
tent->subtype = AF_INET6;
tent->v.value = xn->value;
tent->v.kidx = xn->value;
#endif
}
@ -1263,13 +1263,13 @@ ta_dump_chash_tentry(void *ta_state, struct table_info *ti, void *e,
tent->k.addr.s_addr = htonl(ent->a.a4 << (32 - cfg->mask4));
tent->masklen = cfg->mask4;
tent->subtype = AF_INET;
tent->v.value = ent->value;
tent->v.kidx = ent->value;
#ifdef INET6
} else {
memcpy(&tent->k, &ent->a.a6, sizeof(struct in6_addr));
tent->masklen = cfg->mask6;
tent->subtype = AF_INET6;
tent->v.value = ent->value;
tent->v.kidx = ent->value;
#endif
}
@ -1784,6 +1784,7 @@ struct ifidx {
uint16_t spare;
uint32_t value;
};
#define DEFAULT_IFIDX_SIZE 64
struct iftable_cfg;
@ -1941,8 +1942,8 @@ ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
icfg->ii = ipfw_objhash_create(16);
icfg->size = 16;
icfg->ii = ipfw_objhash_create(DEFAULT_IFIDX_SIZE);
icfg->size = DEFAULT_IFIDX_SIZE;
icfg->main_ptr = malloc(sizeof(struct ifidx) * icfg->size, M_IPFW,
M_WAITOK | M_ZERO);
icfg->ch = ch;
@ -2363,7 +2364,7 @@ ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
tent->masklen = 8 * IF_NAMESIZE;
memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
tent->v.value = ife->value;
tent->v.kidx = ife->value;
return (0);
}
@ -2789,7 +2790,7 @@ ta_dump_numarray_tentry(void *ta_state, struct table_info *ti, void *e,
na = (struct numarray *)e;
tent->k.key = na->number;
tent->v.value = na->value;
tent->v.kidx = na->value;
return (0);
}
@ -3135,7 +3136,7 @@ ta_dump_fhash_tentry(void *ta_state, struct table_info *ti, void *e,
tfe->proto = ent->proto;
tfe->dport = htons(ent->dport);
tfe->sport = htons(ent->sport);
tent->v.value = ent->value;
tent->v.kidx = ent->value;
tent->subtype = ent->af;
if (ent->af == AF_INET) {
@ -3706,7 +3707,7 @@ ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
len = 0;
tent->masklen = len;
tent->subtype = AF_INET;
tent->v.value = 0; /* Do we need to put GW here? */
tent->v.kidx = 0; /* Do we need to put GW here? */
#ifdef INET6
} else if (addr->sin_family == AF_INET6) {
addr6 = (struct sockaddr_in6 *)addr;
@ -3719,7 +3720,7 @@ ta_dump_kfib_tentry(void *ta_state, struct table_info *ti, void *e,
len = 0;
tent->masklen = len;
tent->subtype = AF_INET6;
tent->v.value = 0;
tent->v.kidx = 0;
#endif
}

View File

@ -0,0 +1,754 @@
/*-
* Copyright (c) 2004 Ruslan Ermilov and Vsevolod Lobko.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 270407 2014-08-23 12:41:39Z melifaro $");
/*
* Multi-field value support for ipfw tables.
*
* This file contains necessary functions to convert
* large multi-field values into u32 indices suitable to be fed
* to various table algorithms. Other machinery like proper refcounting,
* internal structures resizing are also kept here.
*/
#include "opt_ipfw.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/hash.h>
#include <sys/lock.h>
#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/queue.h>
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
#include <netinet/in.h>
#include <netinet/ip_var.h> /* struct ipfw_rule_ref */
#include <netinet/ip_fw.h>
#include <netpfil/ipfw/ip_fw_private.h>
#include <netpfil/ipfw/ip_fw_table.h>
static uint32_t hash_table_value(struct namedobj_instance *ni, void *key,
uint32_t kopt);
static int cmp_table_value(struct named_object *no, void *key, uint32_t kopt);
#define CHAIN_TO_VI(chain) (CHAIN_TO_TCFG(chain)->valhash)
struct table_val_link
{
struct named_object no;
struct table_value *pval; /* Pointer to real table value */
};
#define VALDATA_START_SIZE 64 /* Allocate 64-items array by default */
struct vdump_args {
struct ip_fw_chain *ch;
struct sockopt_data *sd;
struct table_value *pval;
int error;
};
void
ipfw_table_value_init(struct ip_fw_chain *ch)
{
struct tables_config *tcfg;
ch->valuestate = malloc(VALDATA_START_SIZE * sizeof(struct table_value),
M_IPFW, M_WAITOK | M_ZERO);
tcfg = ch->tblcfg;
tcfg->val_size = VALDATA_START_SIZE;
tcfg->valhash = ipfw_objhash_create(tcfg->val_size);
ipfw_objhash_set_funcs(tcfg->valhash, hash_table_value,
cmp_table_value);
}
static void
destroy_value(struct namedobj_instance *ni, struct named_object *no,
void *arg)
{
free(no, M_IPFW);
}
void
ipfw_table_value_destroy(struct ip_fw_chain *ch)
{
free(ch->valuestate, M_IPFW);
ipfw_objhash_foreach(CHAIN_TO_VI(ch), destroy_value, ch);
ipfw_objhash_destroy(CHAIN_TO_VI(ch));
}
static uint32_t
hash_table_value(struct namedobj_instance *ni, void *key, uint32_t kopt)
{
return (hash32_buf(key, 56, 0));
}
static int
cmp_table_value(struct named_object *no, void *key, uint32_t kopt)
{
return (memcmp(((struct table_val_link *)no)->pval, key, 56));
}
static void
mask_table_value(struct table_value *src, struct table_value *dst,
uint32_t mask)
{
#define _MCPY(f, b) if ((mask & (b)) != 0) { dst->f = src->f; }
memset(dst, 0, sizeof(*dst));
_MCPY(tag, IPFW_VTYPE_TAG);
_MCPY(pipe, IPFW_VTYPE_PIPE);
_MCPY(divert, IPFW_VTYPE_DIVERT);
_MCPY(skipto, IPFW_VTYPE_SKIPTO);
_MCPY(netgraph, IPFW_VTYPE_NETGRAPH);
_MCPY(fib, IPFW_VTYPE_FIB);
_MCPY(nat, IPFW_VTYPE_NAT);
_MCPY(dscp, IPFW_VTYPE_DSCP);
_MCPY(nh4, IPFW_VTYPE_NH4);
_MCPY(nh6, IPFW_VTYPE_NH6);
#undef _MCPY
}
static void
get_value_ptrs(struct ip_fw_chain *ch, struct table_config *tc, int vshared,
struct table_value **ptv, struct namedobj_instance **pvi)
{
struct table_value *pval;
struct namedobj_instance *vi;
if (vshared != 0) {
pval = (struct table_value *)ch->valuestate;
vi = CHAIN_TO_VI(ch);
} else {
pval = NULL;
vi = NULL;
//pval = (struct table_value *)&tc->ti.data;
}
if (ptv != NULL)
*ptv = pval;
if (pvi != NULL)
*pvi = vi;
}
static void
update_tvalue(struct namedobj_instance *ni, struct named_object *no, void *arg)
{
struct vdump_args *da;
struct table_val_link *ptv;
struct table_value *pval;
da = (struct vdump_args *)arg;
ptv = (struct table_val_link *)no;
pval = da->pval;
ptv->pval = &pval[ptv->no.kidx];
}
/*
* Grows value storage shared among all tables.
* Drops/reacquires UH locks.
*
* Returns 0 on success.
* Note caller has to check @ts "modified" field.
*/
static int
resize_shared_value_storage(struct ip_fw_chain *ch)
{
struct tables_config *tcfg;
struct namedobj_instance *vi;
struct table_value *pval, *valuestate, *old_valuestate;
void *new_idx;
struct vdump_args da;
int new_blocks;
int val_size, val_size_old;
IPFW_UH_WLOCK_ASSERT(ch);
valuestate = NULL;
new_idx = NULL;
pval = (struct table_value *)ch->valuestate;
vi = CHAIN_TO_VI(ch);
tcfg = CHAIN_TO_TCFG(ch);
val_size = tcfg->val_size * 2;
if (val_size == (1 << 30))
return (ENOSPC);
IPFW_UH_WUNLOCK(ch);
valuestate = malloc(sizeof(struct table_value) * val_size, M_IPFW,
M_WAITOK | M_ZERO);
ipfw_objhash_bitmap_alloc(val_size, (void *)&new_idx,
&new_blocks);
IPFW_UH_WLOCK(ch);
/*
* Check if we still need to resize
*/
if (tcfg->val_size >= val_size)
goto done;
/* Update pointers and notify everyone we're changing @ch */
pval = (struct table_value *)ch->valuestate;
rollback_toperation_state(ch, ch);
/* Good. Let's merge */
memcpy(valuestate, pval, sizeof(struct table_value) * tcfg->val_size);
ipfw_objhash_bitmap_merge(CHAIN_TO_VI(ch), &new_idx, &new_blocks);
IPFW_WLOCK(ch);
/* Change pointers */
old_valuestate = ch->valuestate;
ch->valuestate = valuestate;
valuestate = old_valuestate;
ipfw_objhash_bitmap_swap(CHAIN_TO_VI(ch), &new_idx, &new_blocks);
val_size_old = tcfg->val_size;
tcfg->val_size = val_size;
val_size = val_size_old;
IPFW_WUNLOCK(ch);
/* Update pointers to reflect resize */
memset(&da, 0, sizeof(da));
da.pval = (struct table_value *)ch->valuestate;
ipfw_objhash_foreach(vi, update_tvalue, &da);
done:
free(valuestate, M_IPFW);
ipfw_objhash_bitmap_free(new_idx, new_blocks);
return (0);
}
static void
unref_table_value(struct namedobj_instance *vi, struct table_value *pval,
uint32_t kidx)
{
struct table_val_link *ptvl;
if (pval[kidx].refcnt > 1) {
pval[kidx].refcnt--;
return;
}
/* Last reference, delete item */
ptvl = (struct table_val_link *)ipfw_objhash_lookup_kidx(vi, kidx);
KASSERT(ptvl != NULL, ("lookup on value kidx %d failed", kidx));
ipfw_objhash_del(vi, &ptvl->no);
ipfw_objhash_free_idx(vi, kidx);
free(ptvl, M_IPFW);
}
struct flush_args {
struct ip_fw_chain *ch;
struct table_algo *ta;
struct table_info *ti;
void *astate;
ipfw_obj_tentry tent;
};
static int
unref_table_value_cb(void *e, void *arg)
{
struct flush_args *fa;
struct ip_fw_chain *ch;
struct table_algo *ta;
ipfw_obj_tentry *tent;
int error;
fa = (struct flush_args *)arg;
ta = fa->ta;
memset(&fa->tent, 0, sizeof(fa->tent));
tent = &fa->tent;
error = ta->dump_tentry(fa->astate, fa->ti, e, tent);
if (error != 0)
return (error);
ch = fa->ch;
unref_table_value(CHAIN_TO_VI(ch),
(struct table_value *)ch->valuestate, tent->v.kidx);
return (0);
}
/*
* Drop references for each value used in @tc.
*/
void
ipfw_unref_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct table_algo *ta, void *astate, struct table_info *ti)
{
struct flush_args fa;
memset(&fa, 0, sizeof(fa));
fa.ch = ch;
fa.ta = ta;
fa.astate = astate;
fa.ti = ti;
ta->foreach(astate, ti, unref_table_value_cb, &fa);
}
/*
* Table operation state handler.
* Called when we are going to change something in @tc which
* may lead to inconsistencies in on-going table data addition.
*
* Here we rollback all already committed state (table values, currently)
* and set "modified" field to non-zero value to indicate
* that we need to restart original operation.
*/
static void
rollback_table_values(void *object, struct op_state *_state)
{
struct ip_fw_chain *ch;
struct tableop_state *ts;
struct table_value *pval;
struct tentry_info *ptei;
struct namedobj_instance *vi;
int i;
ts = (struct tableop_state *)_state;
if (ts->tc != object && ts->ch != object)
return;
ch = ts->ch;
IPFW_UH_WLOCK_ASSERT(ch);
/* Get current table value pointer */
get_value_ptrs(ch, ts->tc, ts->vshared, &pval, &vi);
for (i = 0; i < ts->count; i++) {
ptei = &ts->tei[i];
if (ptei->value == 0)
continue;
unref_table_value(vi, pval, ptei->value);
}
ts->modified = 1;
}
/*
* Allocate new value index in either shared or per-table array.
* Function may drop/reacquire UH lock.
*
* Returns 0 on success.
* Note that called has to check @ts "modified" value.
*/
static int
alloc_table_vidx(struct ip_fw_chain *ch, struct tableop_state *ts,
struct namedobj_instance *vi, uint16_t *pvidx)
{
int error, vlimit;
uint16_t vidx;
IPFW_UH_WLOCK_ASSERT(ch);
error = ipfw_objhash_alloc_idx(vi, &vidx);
if (error != 0) {
/*
* We need to resize array. This involves
* lock/unlock, so we need to check "modified"
* state.
*/
rollback_table_values(ts->tc, &ts->opstate);
error = resize_shared_value_storage(ch);
return (error); /* ts->modified should be set, we will restart */
}
vlimit = ts->ta->vlimit;
if (vlimit != 0 && vidx >= vlimit) {
/*
* Algorithm is not able to store given index.
* We have to rollback state, start using
* per-table value array or return error
* if we're already using it.
*
* TODO: do not rollback state if
* atomicity is not required.
*/
if (ts->vshared != 0) {
/* shared -> per-table */
return (ENOSPC); /* TODO: proper error */
}
/* per-table. Fail for now. */
return (ENOSPC); /* TODO: proper error */
}
*pvidx = vidx;
return (0);
}
/*
* Drops value reference for unused values (updates, partially
* successful adds or rollbacks).
*/
void
ipfw_finalize_table_values(struct ip_fw_chain *ch, struct table_config *tc,
struct tentry_info *tei, uint32_t count, int rollback)
{
int i;
struct tentry_info *ptei;
struct table_value *pval;
struct namedobj_instance *vi;
/*
* We have two slightly different cases here:
* either (1) we are successful / partially successful,
* in that case we need
* * to ignore ADDED entries values
* * rollback every other values (either UPDATED since
* old value has been stored there, or some failure like
* EXISTS or LIMIT or simply "ignored" case.
*
* (2): atomic rollback of partially successful operation
* in that case we simply need to unref all entries.
*
*/
/*
* Get current table value pointers.
* XXX: Properly read vshared
*/
get_value_ptrs(ch, tc, 1, &pval, &vi);
for (i = 0; i < count; i++) {
ptei = &tei[i];
if (ptei->value == 0) {
/*
* We may be deleting non-existing record.
* Skip.
*/
continue;
}
if ((ptei->flags & TEI_FLAGS_ADDED) != 0 && rollback == 0) {
ptei->value = 0;
continue;
}
unref_table_value(vi, pval, ptei->value);
ptei->value = 0;
}
}
int
ipfw_link_table_values(struct ip_fw_chain *ch, struct tableop_state *ts)
{
int error, i, found;
struct namedobj_instance *vi;
struct table_config *tc;
struct tentry_info *tei, *ptei;
uint32_t count, vlimit;
uint16_t vidx;
struct table_val_link *ptv;
struct table_value tval, *pval;
/*
* Stage 1: reference all existing values and
* save them inside the bitmask.
*/
IPFW_UH_WLOCK_ASSERT(ch);
get_value_ptrs(ch, ts->tc, ts->vshared, &pval, &vi);
error = 0;
found = 0;
vlimit = ts->ta->vlimit;
tc = ts->tc;
tei = ts->tei;
count = ts->count;
for (i = 0; i < count; i++) {
ptei = &tei[i];
ptei->value = 0; /* Ensure value is always 0 in the beginnig */
mask_table_value(ptei->pvalue, &tval, ts->vmask);
ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0,
(char *)&tval);
if (ptv == NULL)
continue;
/* Deal with vlimit later */
if (vlimit > 0 && vlimit <= ptv->no.kidx)
continue;
/* Value found. Bump refcount */
ptv->pval->refcnt++;
found++;
ptei->value = ptv->no.kidx;
}
if (ts->count == found) {
/* We've found all values , no need ts create new ones */
return (0);
}
/*
* we have added some state here, let's attach operation
* state ts the list ts be able ts rollback if necessary.
*/
add_toperation_state(ch, ts);
/* Ensure table won't disappear */
tc_ref(tc);
IPFW_UH_WUNLOCK(ch);
/*
* Stage 2: allocate objects for non-existing values.
*/
for (i = 0; i < count; i++) {
ptei = &tei[i];
if (ptei->value != 0)
continue;
if (ptei->ptv != NULL)
continue;
ptei->ptv = malloc(sizeof(struct table_val_link), M_IPFW,
M_WAITOK | M_ZERO);
}
/*
* Stage 3: allocate index numbers for new values
* and link them to index.
*/
IPFW_UH_WLOCK(ch);
tc_unref(tc);
del_toperation_state(ch, ts);
if (ts->modified != 0)
return (0);
KASSERT(pval == ch->tablestate, ("resize_storage() notify failure"));
/* Let's try to link values */
for (i = 0; i < count; i++) {
ptei = &tei[i];
if (ptei->value != 0)
continue;
/* Check if record has appeared */
mask_table_value(ptei->pvalue, &tval, ts->vmask);
ptv = (struct table_val_link *)ipfw_objhash_lookup_name(vi, 0,
(char *)&tval);
if (ptv != NULL) {
ptei->value = ptv->no.kidx;
continue;
}
/* May perform UH unlock/lock */
error = alloc_table_vidx(ch, ts, vi, &vidx);
if (error != 0) {
rollback_table_values(tc, &ts->opstate);
return (error);
}
if (ts->modified != 0)
return (0);
/* Finally, we have allocated valid index, let's add entry */
ptei->value = vidx;
ptv = (struct table_val_link *)ptei->ptv;
ptei->ptv = NULL;
ptv->no.kidx = vidx;
ptv->no.name = (char *)&pval[vidx];
ptv->pval = &pval[vidx];
memcpy(ptv->pval, &tval, sizeof(struct table_value));
pval[vidx].refcnt = 1;
ipfw_objhash_add(vi, &ptv->no);
}
return (0);
}
/*
* Compability function used to import data from old
* IP_FW_TABLE_ADD / IP_FW_TABLE_XADD opcodes.
*/
void
ipfw_import_table_value_legacy(uint32_t value, struct table_value *v)
{
memset(v, 0, sizeof(*v));
v->tag = value;
v->pipe = value;
v->divert = value;
v->skipto = value;
v->netgraph = value;
v->fib = value;
v->nat = value;
v->nh4 = value; /* host format */
v->dscp = value;
v->limit = value;
}
uint32_t
ipfw_export_table_value_legacy(struct table_value *v)
{
/*
* TODO: provide more compatibility depending on
* vmask value.
*/
return (v->tag);
}
void
ipfw_import_table_value_v1(ipfw_table_value *iv)
{
struct table_value v;
memset(&v, 0, sizeof(v));
v.tag = iv->tag;
v.pipe = iv->pipe;
v.divert = iv->divert;
v.skipto = iv->skipto;
v.netgraph = iv->netgraph;
v.fib = iv->fib;
v.nat = iv->nat;
v.dscp = iv->dscp;
v.nh4 = iv->nh4;
v.nh6 = iv->nh6;
v.limit = iv->limit;
memcpy(iv, &v, sizeof(ipfw_table_value));
}
void
ipfw_export_table_value_v1(struct table_value *v, ipfw_table_value *piv)
{
ipfw_table_value iv;
memset(&iv, 0, sizeof(iv));
iv.tag = v->tag;
iv.pipe = v->pipe;
iv.divert = v->divert;
iv.skipto = v->skipto;
iv.netgraph = v->netgraph;
iv.fib = v->fib;
iv.nat = v->nat;
iv.dscp = v->dscp;
iv.limit = v->limit;
iv.nh4 = v->nh4;
iv.nh6 = v->nh6;
memcpy(piv, &iv, sizeof(iv));
}
static void
dump_tvalue(struct namedobj_instance *ni, struct named_object *no, void *arg)
{
struct vdump_args *da;
struct table_val_link *ptv;
struct table_value *v;
da = (struct vdump_args *)arg;
ptv = (struct table_val_link *)no;
v = (struct table_value *)ipfw_get_sopt_space(da->sd, sizeof(*v));
/* Out of memory, returning */
if (v == NULL) {
da->error = ENOMEM;
return;
}
memcpy(v, ptv->pval, sizeof(*v));
v->spare1 = ptv->no.kidx;
}
/*
* Dumps all shared/table value data
* Data layout (v1)(current):
* Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
* Reply: [ ipfw_obj_lheader ipfw_table_value x N ]
*
* Returns 0 on success
*/
int
ipfw_list_table_values(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
struct _ipfw_obj_lheader *olh;
struct namedobj_instance *vi;
struct vdump_args da;
uint32_t count, size;
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
if (olh == NULL)
return (EINVAL);
if (sd->valsize < olh->size)
return (EINVAL);
IPFW_UH_RLOCK(ch);
vi = CHAIN_TO_VI(ch);
count = ipfw_objhash_count(vi);
size = count * sizeof(ipfw_table_value) + sizeof(ipfw_obj_lheader);
/* Fill in header regadless of buffer size */
olh->count = count;
olh->objsize = sizeof(ipfw_table_value);
if (size > olh->size) {
olh->size = size;
IPFW_UH_RUNLOCK(ch);
return (ENOMEM);
}
olh->size = size;
/*
* Do the actual value dump
*/
memset(&da, 0, sizeof(da));
da.ch = ch;
da.sd = sd;
ipfw_objhash_foreach(vi, dump_tvalue, &da);
IPFW_UH_RUNLOCK(ch);
return (0);
}