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:
parent
631be4d79a
commit
a1eca3cc0c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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[]);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
754
sys/netpfil/ipfw/ip_fw_table_value.c
Normal file
754
sys/netpfil/ipfw/ip_fw_table_value.c
Normal 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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user