Fully switch to named tables:

Kernel changes:
* Introduce ipfw_obj_tentry table entry structure to force u64 alignment.
* Support "update-on-existing-key" "add" bahavior (TEI_FLAGS_UPDATED).
* Use "subtype" field to distingush between IPv4 and IPv6 table records
  instead of previous hack.
* Add value type (vtype) field for kernel tables. Current types are
  number,ip and dscp
* Fix sets mask retrieval for old binaries
* Fix crash while using interface tables

Userland changes:
* Switch ipfw_table_handler() to use named-only tables.
* Add "table NAME create [type {cidr|iface|u32} [valtype {number|ip|dscp}] ..."
* Switch ipfw_table_handler to match_token()-based parser.
* Switch ipfw_sets_handler to use new ipfw_get_config() for mask  retrieval.
* Allow ipfw set X table ... syntax to permit using per-set table namespaces.
This commit is contained in:
Alexander V. Chernikov 2014-07-03 22:25:59 +00:00
parent 6c2997ffec
commit ac35ff1784
11 changed files with 1028 additions and 509 deletions

View File

@ -174,7 +174,7 @@ static struct _s_x f_iptos[] = {
{ NULL, 0 }
};
static struct _s_x f_ipdscp[] = {
struct _s_x f_ipdscp[] = {
{ "af11", IPTOS_DSCP_AF11 >> 2 }, /* 001010 */
{ "af12", IPTOS_DSCP_AF12 >> 2 }, /* 001100 */
{ "af13", IPTOS_DSCP_AF13 >> 2 }, /* 001110 */
@ -649,7 +649,7 @@ match_token(struct _s_x *table, char *string)
for (pt = table ; i && pt->s != NULL ; pt++)
if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
return pt->x;
return -1;
return (-1);
}
/**
@ -665,6 +665,25 @@ match_value(struct _s_x *p, int value)
return NULL;
}
size_t
concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter)
{
struct _s_x *pt;
int l;
size_t sz;
for (sz = 0, pt = table ; pt->s != NULL; pt++) {
l = snprintf(buf + sz, bufsize - sz, "%s%s",
(sz == 0) ? "" : delimiter, pt->s);
sz += l;
bufsize += l;
if (sz > bufsize)
return (bufsize);
}
return (sz);
}
/*
* _substrcmp takes two strings and returns 1 if they do not match,
* and 0 if they match exactly or the first string is a sub-string
@ -2012,46 +2031,38 @@ show_dyn_state(struct cmdline_opts *co, struct format_opts *fo,
void
ipfw_sets_handler(char *av[])
{
uint32_t set_disable, masks[2];
int i, nbytes;
uint32_t masks[2];
int i;
uint16_t rulenum;
uint8_t cmd, new_set;
char *msg;
size_t size;
av++;
if (av[0] == NULL)
errx(EX_USAGE, "set needs command");
if (_substrcmp(*av, "show") == 0) {
void *data = NULL;
char const *msg;
int nalloc;
struct format_opts fo;
ipfw_cfg_lheader *cfg;
nalloc = nbytes = sizeof(struct ip_fw);
while (nbytes >= nalloc) {
if (data)
free(data);
nalloc = nalloc * 2 + 200;
nbytes = nalloc;
data = safe_calloc(1, nbytes);
if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
err(EX_OSERR, "getsockopt(IP_FW_GET)");
}
memset(&fo, 0, sizeof(fo));
if (ipfw_get_config(&co, &fo, &cfg, &size) != 0)
err(EX_OSERR, "requesting config failed");
bcopy(&((struct ip_fw *)data)->next_rule,
&set_disable, sizeof(set_disable));
for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
if ((set_disable & (1<<i))) {
for (i = 0, msg = "disable"; i < RESVD_SET; i++)
if ((cfg->set_mask & (1<<i)) == 0) {
printf("%s %d", msg, i);
msg = "";
}
msg = (set_disable) ? " enable" : "enable";
msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable";
for (i = 0; i < RESVD_SET; i++)
if (!(set_disable & (1<<i))) {
if ((cfg->set_mask & (1<<i)) != 0) {
printf("%s %d", msg, i);
msg = "";
}
printf("\n");
free(cfg);
} else if (_substrcmp(*av, "swap") == 0) {
av++;
if ( av[0] == NULL || av[1] == NULL )

View File

@ -71,6 +71,8 @@ struct _s_x {
int x;
};
extern struct _s_x f_ipdscp[];
enum tokens {
TOK_NULL=0,
@ -205,6 +207,16 @@ enum tokens {
TOK_LOOKUP,
TOK_SOCKARG,
TOK_SETDSCP,
/* Table tokens */
TOK_CREATE,
TOK_DESTROY,
TOK_LIST,
TOK_INFO,
TOK_FLUSH,
TOK_ADD,
TOK_DEL,
TOK_VALTYPE,
TOK_ALGO,
};
/*
* the following macro returns an error message if we run out of
@ -238,6 +250,8 @@ int _substrcmp2(const char *str1, const char* str2, const char* str3);
/* utility functions */
int match_token(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);
struct _ip_fw3_opheader;
int do_cmd(int optname, void *optval, uintptr_t optlen);

View File

@ -436,6 +436,8 @@ ipfw_main(int oldac, char **oldav)
ipfw_list(ac, av, do_acct);
else if (_substrcmp(*av, "show") == 0)
ipfw_list(ac, av, 1 /* show counters */);
else if (_substrcmp(*av, "table") == 0)
ipfw_table_handler(ac, av);
else
errx(EX_USAGE, "bad command `%s'", *av);
}

View File

@ -49,18 +49,27 @@
#include "ipfw2.h"
static void table_list(ipfw_xtable_info *i, int need_header);
static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
static int table_flush(char *name, uint32_t set);
static int table_destroy(char *name, uint32_t set);
static int table_get_info(char *name, uint32_t set, ipfw_xtable_info *i);
static void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
int add, int update);
static int table_flush(ipfw_obj_header *oh);
static int table_destroy(ipfw_obj_header *oh);
static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
static int table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i);
static int table_show_info(ipfw_xtable_info *i, void *arg);
static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx);
static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set,
uint16_t uidx);
static int table_flush_one(ipfw_xtable_info *i, void *arg);
static int table_show_one(ipfw_xtable_info *i, void *arg);
static int table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh);
static void table_show_list(ipfw_obj_header *oh, int need_header);
static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
char *key, uint8_t *ptype, uint8_t *pvtype);
static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
char *arg, uint8_t type, uint8_t vtype);
typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
static int tables_foreach(table_cb_t *f, void *arg, int sort);
@ -68,6 +77,31 @@ static int tables_foreach(table_cb_t *f, void *arg, int sort);
#define s6_addr32 __u6_addr.__u6_addr32
#endif
static struct _s_x tabletypes[] = {
{ "cidr", IPFW_TABLE_CIDR },
{ "iface", IPFW_TABLE_INTERFACE },
{ "u32", IPFW_TABLE_U32 },
{ NULL, 0 }
};
static struct _s_x tablevaltypes[] = {
{ "dscp", IPFW_VTYPE_DSCP },
{ "ip", IPFW_VTYPE_IP },
{ "number", IPFW_VTYPE_U32 },
{ NULL, 0 }
};
static struct _s_x tablecmds[] = {
{ "add", TOK_ADD },
{ "create", TOK_CREATE },
{ "delete", TOK_DEL },
{ "destroy", TOK_DESTROY },
{ "flush", TOK_FLUSH },
{ "info", TOK_INFO },
{ "list", TOK_LIST },
{ NULL, 0 }
};
static int
lookup_host (char *host, struct in_addr *ipaddr)
{
@ -83,115 +117,89 @@ lookup_host (char *host, struct in_addr *ipaddr)
/*
* This one handles all table-related commands
* ipfw table N add addr[/masklen] [value]
* ipfw table N delete addr[/masklen]
* ipfw table {N | all} flush
* ipfw table {N | all} list
* ipfw table {N | all} info
* ipfw table NAME create ...
* ipfw table NAME destroy
* ipfw table NAME add addr[/masklen] [value]
* ipfw table NAME delete addr[/masklen]
* ipfw table {NAME | all} flush
* ipfw table {NAME | all} list
* ipfw table {NAME | all} info
*/
void
ipfw_table_handler(int ac, char *av[])
{
ipfw_table_xentry *xent;
int do_add;
int is_all;
uint32_t set;
int error;
char xbuf[sizeof(ip_fw3_opheader) + sizeof(ipfw_table_xentry)];
ip_fw3_opheader *op3;
int do_add, is_all;
int error, tcmd;
ipfw_xtable_info i;
ipfw_obj_header oh;
char *tablename;
uint32_t set;
memset(xbuf, 0, sizeof(xbuf));
op3 = (ip_fw3_opheader *)xbuf;
xent = (ipfw_table_xentry *)(op3 + 1);
memset(&oh, 0, sizeof(oh));
is_all = 0;
if (co.use_set != 0)
set = co.use_set - 1;
else
set = 0;
ac--; av++;
tablename = *av;
set = 0;
if (ac && isdigit(**av)) {
xent->tbl = atoi(*av);
is_all = 0;
ac--; av++;
} else if (ac && _substrcmp(*av, "all") == 0) {
xent->tbl = 0;
is_all = 1;
ac--; av++;
} else
errx(EX_USAGE, "table number or 'all' keyword required");
NEED1("table needs command");
if (is_all && _substrcmp(*av, "list") != 0
&& _substrcmp(*av, "info") != 0
&& _substrcmp(*av, "flush") != 0)
errx(EX_USAGE, "table number required");
if (_substrcmp(*av, "add") == 0 ||
_substrcmp(*av, "delete") == 0) {
if (table_check_name(tablename) == 0) {
table_fill_ntlv(&oh.ntlv, *av, set, 1);
//oh->set = set;
oh.idx = 1;
} else {
if (strcmp(tablename, "all") == 0)
is_all = 1;
else
errx(EX_USAGE, "table name %s is invalid", tablename);
}
ac--; av++;
if ((tcmd = match_token(tablecmds, *av)) == -1)
errx(EX_USAGE, "invalid table command %s", *av);
NEED1("table needs command");
switch (tcmd) {
case TOK_LIST:
case TOK_INFO:
case TOK_FLUSH:
break;
default:
if (is_all != 0)
errx(EX_USAGE, "table name required");
}
switch (tcmd) {
case TOK_ADD:
case TOK_DEL:
do_add = **av == 'a';
ac--; av++;
if (!ac)
errx(EX_USAGE, "address required");
table_fill_xentry(*av, xent);
table_modify_record(&oh, ac, av, do_add, co.do_quiet);
break;
case TOK_CREATE:
ac--; av++;
if (do_add && ac) {
unsigned int tval;
/* isdigit is a bit of a hack here.. */
if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
xent->value = strtoul(*av, NULL, 0);
} else {
if (lookup_host(*av, (struct in_addr *)&tval) == 0) {
/* The value must be stored in host order *
* so that the values < 65k can be distinguished */
xent->value = ntohl(tval);
} else {
errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
}
}
} else
xent->value = 0;
if (do_set3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL,
op3, sizeof(xbuf)) < 0) {
/* If running silent, don't bomb out on these errors. */
if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
do_add ? "XADD" : "XDEL");
/* In silent mode, react to a failed add by deleting */
if (do_add) {
do_set3(IP_FW_TABLE_XDEL, op3, sizeof(xbuf));
if (do_set3(IP_FW_TABLE_XADD, op3, sizeof(xbuf)) < 0)
err(EX_OSERR,
"setsockopt(IP_FW_TABLE_XADD)");
}
}
} else if (_substrcmp(*av, "flush") == 0) {
table_create(&oh, ac, av);
break;
case TOK_DESTROY:
if (table_destroy(&oh) != 0)
err(EX_OSERR, "failed to destroy table %s", tablename);
break;
case TOK_FLUSH:
if (is_all == 0) {
if ((error = table_flush(tablename, set)) != 0)
if ((error = table_flush(&oh)) != 0)
err(EX_OSERR, "failed to flush table %s info",
tablename);
} else {
error = tables_foreach(table_flush_one, NULL, 1);
error = tables_foreach(table_flush_one, &oh, 1);
if (error != 0)
err(EX_OSERR, "failed to flush tables list");
}
} else if (_substrcmp(*av, "list") == 0) {
break;
case TOK_INFO:
if (is_all == 0) {
ipfw_xtable_info i;
if ((error = table_get_info(tablename, set, &i)) != 0)
err(EX_OSERR, "failed to request table info");
table_show_one(&i, NULL);
} else {
error = tables_foreach(table_show_one, NULL, 1);
if (error != 0)
err(EX_OSERR, "failed to request tables list");
}
} else if (_substrcmp(*av, "destroy") == 0) {
if (table_destroy(tablename, set) != 0)
err(EX_OSERR, "failed to destroy table %s", tablename);
} else if (_substrcmp(*av, "info") == 0) {
if (is_all == 0) {
ipfw_xtable_info i;
if ((error = table_get_info(tablename, set, &i)) != 0)
if ((error = table_get_info(&oh, &i)) != 0)
err(EX_OSERR, "failed to request table info");
table_show_info(&i, NULL);
} else {
@ -199,115 +207,30 @@ ipfw_table_handler(int ac, char *av[])
if (error != 0)
err(EX_OSERR, "failed to request tables list");
}
} else
errx(EX_USAGE, "invalid table command %s", *av);
}
static void
table_fill_xentry(char *arg, ipfw_table_xentry *xent)
{
int addrlen, mask, masklen, type;
struct in6_addr *paddr;
uint32_t *pkey;
char *p;
uint32_t key;
mask = 0;
type = 0;
addrlen = 0;
masklen = 0;
/*
* Let's try to guess type by agrument.
* Possible types:
* 1) IPv4[/mask]
* 2) IPv6[/mask]
* 3) interface name
* 4) port, uid/gid or other u32 key (base 10 format)
* 5) hostname
*/
paddr = &xent->k.addr6;
if (ishexnumber(*arg) != 0 || *arg == ':') {
/* Remove / if exists */
if ((p = strchr(arg, '/')) != NULL) {
*p = '\0';
mask = atoi(p + 1);
}
if (inet_pton(AF_INET, arg, paddr) == 1) {
if (p != NULL && mask > 32)
errx(EX_DATAERR, "bad IPv4 mask width: %s",
p + 1);
type = IPFW_TABLE_CIDR;
masklen = p ? mask : 32;
addrlen = sizeof(struct in_addr);
} else if (inet_pton(AF_INET6, arg, paddr) == 1) {
if (IN6_IS_ADDR_V4COMPAT(paddr))
errx(EX_DATAERR,
"Use IPv4 instead of v4-compatible");
if (p != NULL && mask > 128)
errx(EX_DATAERR, "bad IPv6 mask width: %s",
p + 1);
type = IPFW_TABLE_CIDR;
masklen = p ? mask : 128;
addrlen = sizeof(struct in6_addr);
break;
case TOK_LIST:
if (is_all == 0) {
ipfw_xtable_info i;
if ((error = table_get_info(&oh, &i)) != 0)
err(EX_OSERR, "failed to request table info");
table_show_one(&i, NULL);
} else {
/* Port or any other key */
/* Skip non-base 10 entries like 'fa1' */
key = strtol(arg, &p, 10);
if (*p == '\0') {
pkey = (uint32_t *)paddr;
*pkey = htonl(key);
type = IPFW_TABLE_CIDR;
masklen = 32;
addrlen = sizeof(uint32_t);
} else if ((p != arg) && (*p == '.')) {
/*
* Warn on IPv4 address strings
* which are "valid" for inet_aton() but not
* in inet_pton().
*
* Typical examples: '10.5' or '10.0.0.05'
*/
errx(EX_DATAERR,
"Invalid IPv4 address: %s", arg);
error = tables_foreach(table_show_one, NULL, 1);
if (error != 0)
err(EX_OSERR, "failed to request tables list");
}
break;
}
}
if (type == 0 && strchr(arg, '.') == NULL) {
/* Assume interface name. Copy significant data only */
mask = MIN(strlen(arg), IF_NAMESIZE - 1);
memcpy(xent->k.iface, arg, mask);
/* Set mask to exact match */
masklen = 8 * IF_NAMESIZE;
type = IPFW_TABLE_INTERFACE;
addrlen = IF_NAMESIZE;
}
if (type == 0) {
if (lookup_host(arg, (struct in_addr *)paddr) != 0)
errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
masklen = 32;
type = IPFW_TABLE_CIDR;
addrlen = sizeof(struct in_addr);
}
xent->type = type;
xent->masklen = masklen;
xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
}
static void
table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx)
table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx)
{
ntlv->head.type = IPFW_TLV_TBL_NAME;
ntlv->head.length = sizeof(ipfw_obj_ntlv);
ntlv->idx = uidx;
ntlv->set = set;
strlcpy(ntlv->name, name, sizeof(ntlv->name));
}
@ -317,71 +240,157 @@ table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
oh->set = i->set;
oh->idx = 1;
table_fill_ntlv(&oh->ntlv, i->tablename, 1);
table_fill_ntlv(&oh->ntlv, i->tablename, oh->set, 1);
}
static struct _s_x tablenewcmds[] = {
{ "type", TOK_TYPE},
{ "valtype", TOK_VALTYPE },
{ "algo", TOK_ALGO },
{ NULL, 0 }
};
/*
* Creates new table
*
* ipfw table NAME create [ type { cidr | iface | u32 } ]
* [ valtype { number | ip | dscp } ]
* [ algo algoname ]
*
* Request: [ ipfw_obj_header ipfw_xtable_info ]
*/
static void
table_create(ipfw_obj_header *oh, int ac, char *av[])
{
ipfw_xtable_info xi;
int error, tcmd, val;
size_t sz;
char tbuf[128];
sz = sizeof(tbuf);
memset(&xi, 0, sizeof(xi));
/* Set some defaults to preserve compability */
xi.type = IPFW_TABLE_CIDR;
xi.vtype = IPFW_VTYPE_U32;
while (ac > 0) {
if ((tcmd = match_token(tablenewcmds, *av)) == -1)
errx(EX_USAGE, "unknown option: %s", *av);
ac--; av++;
switch (tcmd) {
case TOK_TYPE:
NEED1("table type required");
val = match_token(tabletypes, *av);
if (val != -1) {
printf("av %s type %d\n", *av, xi.type);
xi.type = val;
ac--; av++;
break;
}
concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", ");
errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s",
*av, tbuf);
break;
case TOK_VALTYPE:
NEED1("table value type required");
val = match_token(tablevaltypes, *av);
if (val != -1) {
xi.vtype = val;
ac--; av++;
break;
}
concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
*av, tbuf);
break;
case TOK_ALGO:
NEED1("table algorithm name required");
if (strlen(*av) > sizeof(xi.algoname))
errx(EX_USAGE, "algorithm name too long");
strlcpy(xi.algoname, *av, sizeof(xi.algoname));
ac--; av++;
break;
}
}
if ((error = table_do_create(oh, &xi)) != 0)
err(EX_OSERR, "Table creation failed");
}
/*
* Destroys given table @name in given @set.
* Creates new table
*
* Request: [ ipfw_obj_header ipfw_xtable_info ]
*
* Returns 0 on success.
*/
static int
table_destroy(char *name, uint32_t set)
table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
{
ipfw_obj_header oh;
char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
int error;
memset(&oh, 0, sizeof(oh));
oh.idx = 1;
table_fill_ntlv(&oh.ntlv, name, 1);
if (do_set3(IP_FW_TABLE_XDESTROY, &oh.opheader, sizeof(oh)) != 0)
memcpy(tbuf, oh, sizeof(*oh));
memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
oh = (ipfw_obj_header *)tbuf;
error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf));
return (error);
}
/*
* Destroys given table specified by @oh->ntlv.
* Returns 0 on success.
*/
static int
table_destroy(ipfw_obj_header *oh)
{
if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0)
return (-1);
return (0);
}
/*
* Flushes given table @name in given @set.
* Flushes given table specified by @oh->ntlv.
* Returns 0 on success.
*/
static int
table_flush(char *name, uint32_t set)
table_flush(ipfw_obj_header *oh)
{
ipfw_obj_header oh;
memset(&oh, 0, sizeof(oh));
oh.idx = 1;
table_fill_ntlv(&oh.ntlv, name, 1);
if (do_set3(IP_FW_TABLE_XFLUSH, &oh.opheader, sizeof(oh)) != 0)
if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0)
return (-1);
return (0);
}
/*
* Retrieves info for given table @name in given @set and stores
* Retrieves table in given table specified by @oh->ntlv.
* it inside @i.
* Returns 0 on success.
*/
static int
table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i)
{
char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
ipfw_obj_header *oh;
char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
int error;
size_t sz;
sz = sizeof(tbuf);
memset(tbuf, 0, sizeof(tbuf));
memcpy(tbuf, oh, sizeof(*oh));
oh = (ipfw_obj_header *)tbuf;
i->set = set;
strlcpy(i->tablename, name, sizeof(i->tablename));
table_fill_objheader(oh, i);
if (do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz) < 0)
return (-1);
if ((error = do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz)) != 0)
return (error);
if (sz < sizeof(tbuf))
return (-1);
return (EINVAL);
*i = *(ipfw_xtable_info *)(oh + 1);
@ -394,21 +403,16 @@ table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
static int
table_show_info(ipfw_xtable_info *i, void *arg)
{
char *type;
const char *ttype, *vtype;
printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
switch (i->type) {
case IPFW_TABLE_CIDR:
type = "cidr";
break;
case IPFW_TABLE_INTERFACE:
type = "iface";
break;
default:
type = "unknown";
}
printf(" type: %s, kindex: %d\n", type, i->kidx);
printf(" ftype: %d, algorithm: %d\n", i->ftype, i->atype);
if ((ttype = match_value(tabletypes, i->type)) == NULL)
ttype = "unknown";
if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL)
vtype = "unknown";
printf(" type: %s, kindex: %d\n", ttype, i->kidx);
printf(" valtype: %s, algorithm: %s\n", vtype, i->algoname);
printf(" references: %u\n", i->refcnt);
printf(" items: %u, size: %u\n", i->count, i->size);
@ -426,7 +430,7 @@ table_show_one(ipfw_xtable_info *i, void *arg)
{
ipfw_obj_header *oh;
if ((oh = malloc(i->size)) == NULL)
if ((oh = calloc(1, i->size)) == NULL)
return (ENOMEM);
if (table_get_list(i, oh) == 0)
@ -439,10 +443,243 @@ table_show_one(ipfw_xtable_info *i, void *arg)
static int
table_flush_one(ipfw_xtable_info *i, void *arg)
{
ipfw_obj_header *oh;
return (table_flush(i->tablename, i->set));
oh = (ipfw_obj_header *)arg;
table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
return (table_flush(oh));
}
static int
table_do_modify_record(int cmd, ipfw_obj_header *oh,
ipfw_obj_tentry *tent, int update)
{
char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
int error;
memset(xbuf, 0, sizeof(xbuf));
memcpy(xbuf, oh, sizeof(*oh));
oh = (ipfw_obj_header *)xbuf;
oh->opheader.version = 1;
memcpy(oh + 1, tent, sizeof(*tent));
tent = (ipfw_obj_tentry *)(oh + 1);
if (update != 0)
tent->flags |= IPFW_TF_UPDATE;
tent->head.length = sizeof(ipfw_obj_tentry);
error = do_set3(cmd, &oh->opheader, sizeof(xbuf));
return (error);
}
static void
table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int update)
{
ipfw_obj_tentry tent;
uint8_t type, vtype;
int cmd;
char *texterr;
if (ac == 0)
errx(EX_USAGE, "address required");
memset(&tent, 0, sizeof(tent));
tent.head.length = sizeof(tent);
tent.idx = 1;
tentry_fill_key(oh, &tent, *av, &type, &vtype);
oh->ntlv.type = type;
ac--; av++;
if (add != 0) {
if (ac > 0)
tentry_fill_value(oh, &tent, *av, type, vtype);
cmd = IP_FW_TABLE_XADD;
texterr = "setsockopt(IP_FW_TABLE_XADD)";
} else {
cmd = IP_FW_TABLE_XDEL;
texterr = "setsockopt(IP_FW_TABLE_XDEL)";
}
if (table_do_modify_record(cmd, oh, &tent, update) != 0)
err(EX_OSERR, "%s", texterr);
}
static void
tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type)
{
char *p;
int mask, af;
struct in6_addr *paddr;
uint32_t key, *pkey;
int masklen;
masklen = 0;
af = 0;
paddr = (struct in6_addr *)&tentry->k;
switch (type) {
case IPFW_TABLE_CIDR:
/* Remove / if exists */
if ((p = strchr(arg, '/')) != NULL) {
*p = '\0';
mask = atoi(p + 1);
}
if (inet_pton(AF_INET, arg, paddr) == 1) {
if (p != NULL && mask > 32)
errx(EX_DATAERR, "bad IPv4 mask width: %s",
p + 1);
masklen = p ? mask : 32;
af = AF_INET;
} else if (inet_pton(AF_INET6, arg, paddr) == 1) {
if (IN6_IS_ADDR_V4COMPAT(paddr))
errx(EX_DATAERR,
"Use IPv4 instead of v4-compatible");
if (p != NULL && mask > 128)
errx(EX_DATAERR, "bad IPv6 mask width: %s",
p + 1);
masklen = p ? mask : 128;
af = AF_INET6;
} else {
/* Assume FQDN */
if (lookup_host(arg, (struct in_addr *)paddr) != 0)
errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
masklen = 32;
type = IPFW_TABLE_CIDR;
af = AF_INET;
}
break;
case IPFW_TABLE_INTERFACE:
/* Assume interface name. Copy significant data only */
mask = MIN(strlen(arg), IF_NAMESIZE - 1);
memcpy(paddr, arg, mask);
/* Set mask to exact match */
masklen = 8 * IF_NAMESIZE;
break;
case IPFW_TABLE_U32:
/* Port or any other key */
key = strtol(arg, &p, 10);
if (*p != '\0')
errx(EX_DATAERR, "Invalid number: %s", arg);
pkey = (uint32_t *)paddr;
*pkey = key;
masklen = 32;
break;
default:
errx(EX_DATAERR, "Unsupported table type: %d", type);
}
tentry->subtype = af;
tentry->masklen = masklen;
}
static void
tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
uint8_t *ptype, uint8_t *pvtype)
{
ipfw_xtable_info xi;
uint8_t type, vtype;
int error;
type = 0;
vtype = 0;
/*
* Compability layer. Try to interpret data as CIDR first.
*/
if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 ||
inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
/* OK Prepare and send */
type = IPFW_TABLE_CIDR;
} else {
/*
* Non-CIDR of FQDN hostname. Ask kernel
* about given table.
*/
error = table_get_info(oh, &xi);
if (error == ESRCH)
errx(EX_USAGE, "Table %s does not exist, cannot intuit "
"key type", oh->ntlv.name);
else if (error != 0)
errx(EX_OSERR, "Error requesting table %s info",
oh->ntlv.name);
/* Table found. */
type = xi.type;
vtype = xi.vtype;
}
tentry_fill_key_type(key, tent, type);
*ptype = type;
*pvtype = vtype;
}
static void
tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg,
uint8_t type, uint8_t vtype)
{
ipfw_xtable_info xi;
int error;
int code;
char *p;
if (vtype == 0) {
/* Format type is unknown, ask kernel */
error = table_get_info(oh, &xi);
if (error == ESRCH) {
/*
* XXX: This one may break some scripts.
* Change this behavior for MFC.
*/
errx(EX_USAGE, "Table %s does not exist. Unable to "
"guess value format.", oh->ntlv.name);
} else if (error != 0)
errx(EX_OSERR, "Error requesting table %s info",
oh->ntlv.name);
vtype = xi.vtype;
}
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");
}
tent->value = code;
break;
default:
errx(EX_OSERR, "Unsupported format type %d", vtype);
}
}
/*
* Compare table names.
@ -668,14 +905,11 @@ table_check_name(char *tablename)
/*
* Check if tablename is null-terminated and contains
* valid symbols only. Valid mask is:
* [a-zA-Z\-\.][a-zA-Z0-9\-_\.]{0,62}
* [a-zA-Z0-9\-_\.]{1,63}
*/
l = strlen(tablename);
if (l == 0 || l >= 64)
return (EINVAL);
/* Restrict first symbol to non-digit */
if (isdigit(tablename[0]))
return (EINVAL);
for (i = 0; i < l; i++) {
c = tablename[i];
if (isalpha(c) || isdigit(c) || c == '_' ||
@ -684,6 +918,10 @@ table_check_name(char *tablename)
return (EINVAL);
}
/* Restrict some 'special' names */
if (strcmp(tablename, "all") == 0)
return (EINVAL);
return (0);
}

View File

@ -86,7 +86,7 @@ typedef struct _ip_fw3_opheader {
#define IP_FW_TABLE_XINFO 93 /* request info for one table */
#define IP_FW_TABLE_XFLUSH 94 /* flush table data */
#define IP_FW_TABLE_XCREATE 95 /* create new table */
#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */
//#define IP_FW_TABLE_XMODIFY 96 /* modify existing table */
#define IP_FW_XGET 97 /* Retrieve configuration */
#define IP_FW_XADD 98 /* add entry */
@ -641,7 +641,12 @@ struct _ipfw_dyn_rule {
#define IPFW_TABLE_CIDR 1 /* Table for holding IPv4/IPv6 prefixes */
#define IPFW_TABLE_INTERFACE 2 /* Table for holding interface names */
#define IPFW_TABLE_MAXTYPE 2 /* Maximum valid number */
#define IPFW_TABLE_U32 3 /* Table for holidng ports/uid/gid/etc */
#define IPFW_TABLE_MAXTYPE 3 /* Maximum valid number */
#define IPFW_VTYPE_U32 1 /* Skipto/tablearg integer */
#define IPFW_VTYPE_IP 2 /* Nexthop IP address */
#define IPFW_VTYPE_DSCP 3 /* DiffServ codepoints */
typedef struct _ipfw_table_entry {
in_addr_t addr; /* network address */
@ -683,23 +688,45 @@ typedef struct _ipfw_xtable {
typedef struct _ipfw_obj_tlv {
uint16_t type; /* TLV type */
uint16_t flags; /* unused */
uint16_t flags; /* TLV-specific flags */
uint32_t length; /* Total length, aligned to u64 */
} ipfw_obj_tlv;
#define IPFW_TLV_TBL_NAME 1
#define IPFW_TLV_TBLNAME_LIST 2
#define IPFW_TLV_RULE_LIST 3
#define IPFW_TLV_STATE_LIST 4
#define IPFW_TLV_TBL_ENT 5
/* Object name TLV */
typedef struct _ipfw_obj_ntlv {
ipfw_obj_tlv head; /* TLV header */
uint16_t idx; /* Name index */
uint16_t spare0; /* unused */
uint8_t spare; /* unused */
uint8_t type; /* object type, if applicable */
uint32_t set; /* set, if applicable */
char name[64]; /* Null-terminated name */
} ipfw_obj_ntlv;
/* Table entry TLV */
typedef struct _ipfw_obj_tentry {
ipfw_obj_tlv head; /* TLV header */
uint8_t subtype; /* subtype (IPv4,IPv6) */
uint8_t masklen; /* mask length */
uint16_t idx; /* Table name index */
uint16_t flags; /* Entry flags */
uint16_t spare0;
uint32_t spare1;
uint32_t value; /* value */
union {
/* Longest field needs to be aligned by 8-byte boundary */
struct in_addr addr; /* IPv4 address */
uint32_t key; /* uid/gid/port */
struct in6_addr addr6; /* IPv6 address */
char iface[IF_NAMESIZE]; /* interface name */
} k;
} ipfw_obj_tentry;
#define IPFW_TF_UPDATE 0x01 /* Update record if exists */
/* Containter TLVs */
typedef struct _ipfw_obj_ctlv {
ipfw_obj_tlv head; /* TLV header */
@ -710,8 +737,8 @@ typedef struct _ipfw_obj_ctlv {
typedef struct _ipfw_xtable_info {
uint8_t type; /* table type (cidr,iface,..) */
uint8_t ftype; /* table value format type */
uint8_t atype; /* algorithm type */
uint8_t spare0;
uint8_t vtype; /* value type */
uint16_t spare0;
uint32_t set; /* set table is in */
uint32_t kidx; /* kernel index */
uint32_t refcnt; /* number of references */
@ -733,7 +760,7 @@ typedef struct _ipfw_obj_header {
typedef struct _ipfw_obj_lheader {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t spare;
uint32_t set_mask; /* disabled set mask */
uint32_t count; /* Total objects count */
uint32_t size; /* Total objects size */
uint32_t objsize; /* Size of one object */
@ -743,7 +770,7 @@ typedef struct _ipfw_obj_lheader {
#define IPFW_CFG_GET_STATES 2
typedef struct _ipfw_cfg_lheader {
ip_fw3_opheader opheader; /* IP_FW3 opcode */
uint32_t set_mask; /* disabled set mask */
uint32_t set_mask; /* enabled set mask */
uint32_t flags; /* Request flags */
uint32_t size; /* neded buffer size */
uint32_t start_rule;

View File

@ -178,7 +178,10 @@ SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD,
"The default/max possible rule number.");
SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, tables_max,
CTLTYPE_UINT|CTLFLAG_RW, 0, 0, sysctl_ipfw_table_num, "IU",
"Maximum number of tables");
"Maximum number of concurrently used tables");
SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, tables_sets,
CTLFLAG_RW, &VNET_NAME(fw_tables_sets), 0,
"Use per-set namespace for tables");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
&default_to_accept, 0,
"Make the default rule accept all packets.");

View File

@ -310,15 +310,6 @@ struct sockopt_data {
#define IPFW_UH_WLOCK(p) rw_wlock(&(p)->uh_lock)
#define IPFW_UH_WUNLOCK(p) rw_wunlock(&(p)->uh_lock)
struct tid_info {
uint32_t set; /* table set */
uint16_t uidx; /* table index */
uint8_t type; /* table type */
uint8_t atype;
void *tlvs; /* Pointer to first TLV */
int tlen; /* Total TLV size block */
};
struct obj_idx {
uint16_t uidx; /* internal index supplied by userland */
uint16_t kidx; /* kernel object index */
@ -330,7 +321,6 @@ struct obj_idx {
struct rule_check_info {
uint16_t table_opcodes; /* count of opcodes referencing table */
uint16_t new_tables; /* count of opcodes referencing table */
uint32_t tableset; /* ipfw set id for table */
ipfw_obj_ctlv *ctlv; /* name TLV containter */
struct ip_fw *krule; /* resulting rule pointer */
struct ip_fw *urule; /* original rule pointer */
@ -373,8 +363,8 @@ void ipfw_objhash_bitmap_swap(struct namedobj_instance *ni,
void ipfw_objhash_bitmap_free(void *idx, int blocks);
struct named_object *ipfw_objhash_lookup_name(struct namedobj_instance *ni,
uint32_t set, char *name);
struct named_object *ipfw_objhash_lookup_idx(struct namedobj_instance *ni,
uint32_t set, uint16_t idx);
struct named_object *ipfw_objhash_lookup_kidx(struct namedobj_instance *ni,
uint16_t idx);
int ipfw_objhash_same_name(struct namedobj_instance *ni, struct named_object *a,
struct named_object *b);
void ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no);
@ -382,9 +372,8 @@ void ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no);
uint32_t ipfw_objhash_count(struct namedobj_instance *ni);
void ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f,
void *arg);
int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set,
uint16_t idx);
int ipfw_objhash_alloc_idx(void *n, uint32_t set, uint16_t *pidx);
int ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx);
int ipfw_objhash_alloc_idx(void *n, uint16_t *pidx);
/* In ip_fw_table.c */
struct table_info;

View File

@ -85,8 +85,7 @@ struct namedobj_instance {
static uint32_t objhash_hash_name(struct namedobj_instance *ni, uint32_t set,
char *name);
static uint32_t objhash_hash_val(struct namedobj_instance *ni, uint32_t set,
uint32_t val);
static uint32_t objhash_hash_val(struct namedobj_instance *ni, uint32_t val);
static int ipfw_flush_sopt_data(struct sockopt_data *sd);
@ -1018,21 +1017,25 @@ ipfw_getrules(struct ip_fw_chain *chain, void *buf, size_t space)
dst = (struct ip_fw *)bp;
bcopy(rule, dst, l);
error = ipfw_rewrite_table_kidx(chain, dst);
if (error != 0) {
printf("Stop on rule %d. Fail to convert table\n",
rule->rulenum);
break;
}
/*
* XXX HACK. Store the disable mask in the "next"
* pointer in a wild attempt to keep the ABI the same.
* Why do we do this on EVERY rule?
*
* XXX: "ipfw set show" (ab)uses IP_FW_GET to read disabled mask
* so we need to fail _after_ saving at least one mask.
*/
bcopy(&V_set_disable, &dst->next_rule, sizeof(V_set_disable));
if (dst->timestamp)
dst->timestamp += boot_seconds;
bp += l;
if (error != 0) {
printf("Stop on rule %d. Fail to convert table\n",
rule->rulenum);
break;
}
}
ipfw_get_dynamic(chain, &bp, ep); /* protected by the dynamic lock */
return (bp - (char *)buf);
@ -1146,9 +1149,9 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd)
if (hdr == NULL)
return (EINVAL);
/* Allocate needed state */
error = 0;
bmask = NULL;
/* Allocate needed state */
if (hdr->flags & IPFW_CFG_GET_STATIC)
bmask = malloc(IPFW_TABLES_MAX / 8, M_TEMP, M_WAITOK | M_ZERO);
@ -1194,7 +1197,7 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd)
/* Fill header anyway */
hdr->size = sz;
hdr->set_mask = V_set_disable;
hdr->set_mask = ~V_set_disable;
if (sd->valsize < sz) {
IPFW_UH_RUNLOCK(chain);
@ -1229,13 +1232,15 @@ dump_config(struct ip_fw_chain *chain, struct sockopt_data *sd)
static int
check_object_name(ipfw_obj_ntlv *ntlv)
{
int error;
if (strnlen(ntlv->name, sizeof(ntlv->name)) == sizeof(ntlv->name))
return (EINVAL);
/*
* TODO: do some more complicated checks
*/
switch (ntlv->head.type) {
case IPFW_TLV_TBL_NAME:
error = ipfw_check_table_name(ntlv->name);
break;
default:
error = ENOTSUP;
}
return (0);
}
@ -1443,7 +1448,7 @@ ipfw_ctl(struct sockopt *sopt)
{
#define RULE_MAXSIZE (256*sizeof(u_int32_t))
int error;
size_t bsize_max, size, len, valsize;
size_t bsize_max, size, valsize;
struct ip_fw *buf, *rule;
struct ip_fw_chain *chain;
u_int32_t rulenum[2];
@ -1683,36 +1688,13 @@ ipfw_ctl(struct sockopt *sopt)
/*--- TABLE opcodes ---*/
case IP_FW_TABLE_XCREATE: /* IP_FW3 */
case IP_FW_TABLE_XMODIFY: /* IP_FW3 */
if (opt == IP_FW_TABLE_XCREATE)
error = ipfw_create_table(chain, sopt, op3);
else
error= ipfw_modify_table(chain, sopt, op3);
error = ipfw_create_table(chain, op3, &sdata);
break;
case IP_FW_TABLE_XDESTROY: /* IP_FW3 */
case IP_FW_TABLE_XFLUSH: /* IP_FW3 */
{
struct _ipfw_obj_header *oh;
struct tid_info ti;
if (sopt->sopt_valsize < sizeof(*oh)) {
error = EINVAL;
error = ipfw_flush_table(chain, op3, &sdata);
break;
}
oh = (struct _ipfw_obj_header *)op3;
objheader_to_ti(oh, &ti);
if (opt == IP_FW_TABLE_XDESTROY)
error = ipfw_destroy_table(chain, &ti);
else if (opt == IP_FW_TABLE_XFLUSH)
error = ipfw_flush_table(chain, &ti);
else
error = ENOTSUP;
break;
}
case IP_FW_TABLE_XINFO: /* IP_FW3 */
error = ipfw_describe_table(chain, &sdata);
@ -1732,39 +1714,7 @@ ipfw_ctl(struct sockopt *sopt)
case IP_FW_TABLE_XADD: /* IP_FW3 */
case IP_FW_TABLE_XDEL: /* IP_FW3 */
{
ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1);
struct tentry_info tei;
struct tid_info ti;
/* Check minimum header size */
if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) {
error = EINVAL;
break;
}
/* Check if len field is valid */
if (xent->len > sizeof(ipfw_table_xentry)) {
error = EINVAL;
break;
}
len = xent->len - offsetof(ipfw_table_xentry, k);
memset(&tei, 0, sizeof(tei));
tei.paddr = &xent->k;
tei.plen = len;
tei.masklen = xent->masklen;
tei.value = xent->value;
memset(&ti, 0, sizeof(ti));
ti.set = 0; /* XXX: No way to specify set */
ti.uidx = xent->tbl;
ti.type = xent->type;
error = (opt == IP_FW_TABLE_XADD) ?
ipfw_add_table_entry(chain, &ti, &tei) :
ipfw_del_table_entry(chain, &ti, &tei);
}
error = ipfw_modify_table(chain, op3, &sdata);
break;
/*--- LEGACY API ---*/
@ -1782,11 +1732,10 @@ ipfw_ctl(struct sockopt *sopt)
memset(&tei, 0, sizeof(tei));
tei.paddr = &ent.addr;
tei.plen = sizeof(ent.addr);
tei.subtype = AF_INET;
tei.masklen = ent.masklen;
tei.value = ent.value;
memset(&ti, 0, sizeof(ti));
ti.set = RESVD_SET;
ti.uidx = ent.tbl;
ti.type = IPFW_TABLE_CIDR;
@ -1807,7 +1756,6 @@ ipfw_ctl(struct sockopt *sopt)
if (error)
break;
memset(&ti, 0, sizeof(ti));
ti.set = 0; /* XXX: No way to specify set */
ti.uidx = tbl;
error = ipfw_flush_table(chain, &ti);
}
@ -1822,7 +1770,6 @@ ipfw_ctl(struct sockopt *sopt)
sizeof(tbl))))
break;
memset(&ti, 0, sizeof(ti));
ti.set = 0; /* XXX: No way to specify set */
ti.uidx = tbl;
IPFW_RLOCK(chain);
error = ipfw_count_table(chain, &ti, &cnt);
@ -1852,7 +1799,6 @@ ipfw_ctl(struct sockopt *sopt)
tbl->size = (size - sizeof(*tbl)) /
sizeof(ipfw_table_entry);
memset(&ti, 0, sizeof(ti));
ti.set = 0; /* XXX: No way to specify set */
ti.uidx = tbl->tbl;
IPFW_RLOCK(chain);
error = ipfw_dump_table_legacy(chain, &ti, tbl);
@ -1879,7 +1825,6 @@ ipfw_ctl(struct sockopt *sopt)
tbl = (uint32_t *)(op3 + 1);
memset(&ti, 0, sizeof(ti));
ti.set = 0; /* XXX: No way to specify set */
ti.uidx = *tbl;
IPFW_UH_RLOCK(chain);
error = ipfw_count_xtable(chain, &ti, tbl);
@ -2249,7 +2194,7 @@ objhash_hash_name(struct namedobj_instance *ni, uint32_t set, char *name)
}
static uint32_t
objhash_hash_val(struct namedobj_instance *ni, uint32_t set, uint32_t val)
objhash_hash_val(struct namedobj_instance *ni, uint32_t val)
{
uint32_t v;
@ -2275,16 +2220,15 @@ ipfw_objhash_lookup_name(struct namedobj_instance *ni, uint32_t set, char *name)
}
struct named_object *
ipfw_objhash_lookup_idx(struct namedobj_instance *ni, uint32_t set,
uint16_t idx)
ipfw_objhash_lookup_kidx(struct namedobj_instance *ni, uint16_t kidx)
{
struct named_object *no;
uint32_t hash;
hash = objhash_hash_val(ni, set, idx);
hash = objhash_hash_val(ni, kidx);
TAILQ_FOREACH(no, &ni->values[hash], nv_next) {
if ((no->kidx == idx) && (no->set == set))
if (no->kidx == kidx)
return (no);
}
@ -2310,7 +2254,7 @@ ipfw_objhash_add(struct namedobj_instance *ni, struct named_object *no)
hash = objhash_hash_name(ni, no->set, no->name);
TAILQ_INSERT_HEAD(&ni->names[hash], no, nn_next);
hash = objhash_hash_val(ni, no->set, no->kidx);
hash = objhash_hash_val(ni, no->kidx);
TAILQ_INSERT_HEAD(&ni->values[hash], no, nv_next);
ni->count++;
@ -2324,7 +2268,7 @@ ipfw_objhash_del(struct namedobj_instance *ni, struct named_object *no)
hash = objhash_hash_name(ni, no->set, no->name);
TAILQ_REMOVE(&ni->names[hash], no, nn_next);
hash = objhash_hash_val(ni, no->set, no->kidx);
hash = objhash_hash_val(ni, no->kidx);
TAILQ_REMOVE(&ni->values[hash], no, nv_next);
ni->count--;
@ -2358,7 +2302,7 @@ ipfw_objhash_foreach(struct namedobj_instance *ni, objhash_cb_t *f, void *arg)
* Returns 0 on success.
*/
int
ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx)
ipfw_objhash_free_idx(struct namedobj_instance *ni, uint16_t idx)
{
u_long *mask;
int i, v;
@ -2366,10 +2310,10 @@ ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx)
i = idx / BLOCK_ITEMS;
v = idx % BLOCK_ITEMS;
if ((i >= ni->max_blocks) || set >= IPFW_MAX_SETS)
if (i >= ni->max_blocks)
return (1);
mask = &ni->idx_mask[set * ni->max_blocks + i];
mask = &ni->idx_mask[i];
if ((*mask & ((u_long)1 << v)) != 0)
return (1);
@ -2378,8 +2322,8 @@ ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx)
*mask |= (u_long)1 << v;
/* Update free offset */
if (ni->free_off[set] > i)
ni->free_off[set] = i;
if (ni->free_off[0] > i)
ni->free_off[0] = i;
return (0);
}
@ -2389,19 +2333,16 @@ ipfw_objhash_free_idx(struct namedobj_instance *ni, uint32_t set, uint16_t idx)
* Returns 0 on success.
*/
int
ipfw_objhash_alloc_idx(void *n, uint32_t set, uint16_t *pidx)
ipfw_objhash_alloc_idx(void *n, uint16_t *pidx)
{
struct namedobj_instance *ni;
u_long *mask;
int i, off, v;
if (set >= IPFW_MAX_SETS)
return (-1);
ni = (struct namedobj_instance *)n;
off = ni->free_off[set];
mask = &ni->idx_mask[set * ni->max_blocks + off];
off = ni->free_off[0];
mask = &ni->idx_mask[off];
for (i = off; i < ni->max_blocks; i++, mask++) {
if ((v = ffsl(*mask)) == 0)
@ -2410,7 +2351,7 @@ ipfw_objhash_alloc_idx(void *n, uint32_t set, uint16_t *pidx)
/* Mark as busy */
*mask &= ~ ((u_long)1 << (v - 1));
ni->free_off[set] = i;
ni->free_off[0] = i;
v = BLOCK_ITEMS * i + v - 1;

View File

@ -29,7 +29,7 @@ __FBSDID("$FreeBSD$");
/*
* Lookup table support for ipfw.
*
* This file containg handlers for all generic tables operations:
* This file contains handlers for all generic tables' operations:
* add/del/flush entries, list/dump tables etc..
*
* Table data modification is protected by both UH and runtimg lock
@ -75,7 +75,7 @@ __FBSDID("$FreeBSD$");
*/
struct table_config {
struct named_object no;
uint8_t ftype; /* format table type */
uint8_t vtype; /* format table type */
uint8_t linked; /* 1 if already linked */
uint16_t spare0;
uint32_t count; /* Number of records */
@ -103,11 +103,19 @@ static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
static void free_table_state(void **state, void **xstate, uint8_t type);
static int export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
struct sockopt_data *sd);
static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
static void export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
ipfw_xtable_info *i);
static int dump_table_xentry(void *e, void *arg);
static int ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd);
static int ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd);
static int ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
static int ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
static int destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
static int flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
static struct table_algo *find_table_algo(struct tables_config *tableconf,
struct tid_info *ti, char *name);
@ -129,11 +137,6 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
int error;
char ta_buf[128];
#if 0
if (ti->uidx >= V_fw_tables_max)
return (EINVAL);
#endif
IPFW_UH_WLOCK(ch);
ni = CHAIN_TO_NI(ch);
@ -155,7 +158,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
IPFW_UH_WUNLOCK(ch);
tc_new = NULL;
if (ta == NULL) {
if (tc == NULL) {
/* Table not found. We have to create new one */
if ((ta = find_table_algo(CHAIN_TO_TCFG(ch), ti, NULL)) == NULL)
return (ENOTSUP);
@ -179,7 +182,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
ni = CHAIN_TO_NI(ch);
if (tc == NULL) {
/* Check if another table was allocated by other thread */
/* Check if another table has been allocated by other thread */
if ((tc = find_table(ni, ti)) != NULL) {
/*
@ -189,28 +192,27 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
*/
if (tc->ta != ta) {
IPFW_UH_WUNLOCK(ch);
free_table_config(ni, tc);
return (EINVAL);
error = EINVAL;
goto done;
}
} else {
/*
* We're first to create this table.
* Set tc_new to zero not to free it afterwards.
*/
tc = tc_new;
tc_new = NULL;
/* Table still does not exists */
/* Allocate table index. */
if (ipfw_objhash_alloc_idx(ni, ti->set, &kidx) != 0) {
if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
/* Index full. */
IPFW_UH_WUNLOCK(ch);
printf("Unable to allocate index for table %s."
" Consider increasing "
printf("Unable to allocate index for table %s"
"in set %u. Consider increasing "
"net.inet.ip.fw.tables_max",
tc->no.name);
free_table_config(ni, tc);
return (EBUSY);
tc_new->no.name, ti->set);
error = EBUSY;
goto done;
}
/* Set tc_new to zero not to free it afterwards. */
tc = tc_new;
tc_new = NULL;
/* Save kidx */
tc->no.kidx = kidx;
}
@ -225,23 +227,23 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
IPFW_WLOCK(ch);
if (tc->linked == 0) {
if (tc->linked == 0)
link_table(ch, tc);
}
error = ta->add(tc->astate, KIDX_TO_TI(ch, kidx), tei, &ta_buf);
IPFW_WUNLOCK(ch);
if (error == 0)
/* Update number of records. */
if (error == 0 && (tei->flags & TEI_FLAGS_UPDATED) == 0)
tc->count++;
IPFW_UH_WUNLOCK(ch);
done:
if (tc_new != NULL)
free_table_config(ni, tc);
if (error != 0)
free_table_config(ni, tc_new);
/* Run cleaning callback anyway */
ta->flush_entry(tei, &ta_buf);
return (error);
@ -289,18 +291,176 @@ ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
IPFW_UH_WUNLOCK(ch);
if (error != 0)
return (error);
ta->flush_entry(tei, &ta_buf);
return (0);
return (error);
}
int
ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
int error;
switch (op3->version) {
case 0:
error = ipfw_modify_table_v0(ch, op3, sd);
break;
case 1:
error = ipfw_modify_table_v1(ch, op3, sd);
break;
default:
error = ENOTSUP;
}
return (error);
}
/*
* Adds or deletes record in table.
* Data layout (v0):
* Request: [ ip_fw3_opheader ipfw_table_xentry ]
*
* Returns 0 on success
*/
static int
ipfw_modify_table_v0(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_table_xentry *xent;
struct tentry_info tei;
struct tid_info ti;
int error, hdrlen, read;
hdrlen = offsetof(ipfw_table_xentry, k);
/* Check minimum header size */
if (sd->valsize < (sizeof(*op3) + hdrlen))
return (EINVAL);
read = sizeof(ip_fw3_opheader);
/* Check if xentry len field is valid */
xent = (ipfw_table_xentry *)(op3 + 1);
if (xent->len < hdrlen || xent->len + read > sd->valsize)
return (EINVAL);
memset(&tei, 0, sizeof(tei));
tei.paddr = &xent->k;
tei.masklen = xent->masklen;
tei.value = xent->value;
/* Old requests compability */
if (xent->type == IPFW_TABLE_CIDR) {
if (xent->len - hdrlen == sizeof(in_addr_t))
tei.subtype = AF_INET;
else
tei.subtype = AF_INET6;
}
memset(&ti, 0, sizeof(ti));
ti.uidx = xent->tbl;
ti.type = xent->type;
error = (op3->opcode == IP_FW_TABLE_XADD) ?
ipfw_add_table_entry(ch, &ti, &tei) :
ipfw_del_table_entry(ch, &ti, &tei);
return (error);
}
/*
* Adds or deletes record in table.
* Data layout (v1)(current):
* Request: [ ipfw_obj_header ipfw_obj_tentry ]
*
* Returns 0 on success
*/
static int
ipfw_modify_table_v1(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
ipfw_obj_tentry *tent;
ipfw_obj_header *oh;
struct tentry_info tei;
struct tid_info ti;
int error, read;
/* Check minimum header size */
if (sd->valsize < (sizeof(*oh) + sizeof(*tent)))
return (EINVAL);
/* Check if passed data is too long */
if (sd->valsize != sd->kavail)
return (EINVAL);
oh = (ipfw_obj_header *)sd->kbuf;
/* Basic length checks for TLVs */
if (oh->ntlv.head.length != sizeof(oh->ntlv))
return (EINVAL);
read = sizeof(*oh);
/* Assume tentry may grow to support larger keys */
tent = (ipfw_obj_tentry *)(oh + 1);
if (tent->head.length < sizeof(*tent) ||
tent->head.length + read > sd->valsize)
return (EINVAL);
memset(&tei, 0, sizeof(tei));
tei.paddr = &tent->k;
tei.subtype = tent->subtype;
tei.masklen = tent->masklen;
if (tent->flags & IPFW_TF_UPDATE)
tei.flags |= TEI_FLAGS_UPDATE;
tei.value = tent->value;
memset(&ti, 0, sizeof(ti));
ti.uidx = tent->idx;
ti.type = oh->ntlv.type;
ti.tlvs = &oh->ntlv;
ti.tlen = oh->ntlv.head.length;
error = (oh->opheader.opcode == IP_FW_TABLE_XADD) ?
ipfw_add_table_entry(ch, &ti, &tei) :
ipfw_del_table_entry(ch, &ti, &tei);
return (error);
}
int
ipfw_flush_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
int error;
struct _ipfw_obj_header *oh;
struct tid_info ti;
if (sd->valsize != sizeof(*oh))
return (EINVAL);
oh = (struct _ipfw_obj_header *)op3;
objheader_to_ti(oh, &ti);
if (opt == IP_FW_TABLE_XDESTROY)
error = destroy_table(ch, &ti);
else if (opt == IP_FW_TABLE_XFLUSH)
error = flush_table(ch, &ti);
else
return (ENOTSUP);
return (error);
}
/*
* Flushes all entries in given table.
* Data layout (v0)(current):
* Request: [ ip_fw3_opheader ]
*
* Returns 0 on success
*/
int
ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
static int
flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
{
struct namedobj_instance *ni;
struct table_config *tc;
@ -369,15 +529,17 @@ ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
/*
* Destroys table specified by @ti.
* Data layout (v0)(current):
* Request: [ ip_fw3_opheader ]
*
* Returns 0 on success
*/
int
ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
static int
destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
{
struct namedobj_instance *ni;
struct table_config *tc;
ti->set = TABLE_SET(ti->set);
IPFW_UH_WLOCK(ch);
ni = CHAIN_TO_NI(ch);
@ -397,7 +559,7 @@ ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti)
IPFW_WUNLOCK(ch);
/* Free obj index */
if (ipfw_objhash_free_idx(ni, tc->no.set, tc->no.kidx) != 0)
if (ipfw_objhash_free_idx(ni, tc->no.kidx) != 0)
printf("Error unlinking kidx %d from table %s\n",
tc->no.kidx, tc->tablename);
@ -414,7 +576,7 @@ destroy_table_locked(struct namedobj_instance *ni, struct named_object *no,
{
unlink_table((struct ip_fw_chain *)arg, (struct table_config *)no);
if (ipfw_objhash_free_idx(ni, no->set, no->kidx) != 0)
if (ipfw_objhash_free_idx(ni, no->kidx) != 0)
printf("Error unlinking kidx %d from table %s\n",
no->kidx, no->name);
free_table_config(ni, (struct table_config *)no);
@ -548,7 +710,7 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
/*
* Get buffer size needed to list info for all tables.
* Data layout:
* Data layout (v0)(current):
* Request: [ empty ], size = sizeof(ipfw_obj_lheader)
* Reply: [ ipfw_obj_lheader ]
*
@ -574,7 +736,7 @@ ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
/*
* Lists all tables currently available in kernel.
* Data layout:
* Data layout (v0)(current):
* Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
* Reply: [ ipfw_obj_lheader ipfw_xtable_info x N ]
*
@ -607,7 +769,7 @@ ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
/*
* Store table info to buffer provided by @sd.
* Data layout:
* Data layout (v0)(current):
* Request: [ ipfw_obj_header ipfw_xtable_info(empty)]
* Reply: [ ipfw_obj_header ipfw_xtable_info ]
*
@ -634,7 +796,7 @@ ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd)
return (ESRCH);
}
export_table_info(tc, (ipfw_xtable_info *)(oh + 1));
export_table_info(ch, tc, (ipfw_xtable_info *)(oh + 1));
IPFW_UH_RUNLOCK(ch);
return (0);
@ -672,7 +834,7 @@ ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
/*
* Dumps all table data
* Data layout (version 1)(current):
* Data layout (v1)(current):
* Request: [ ipfw_obj_header ], size = ipfw_xtable_info.size
* Reply: [ ipfw_obj_header ipfw_xtable_info ipfw_table_xentry x N ]
*
@ -702,7 +864,7 @@ ipfw_dump_table_v1(struct ip_fw_chain *ch, struct sockopt_data *sd)
IPFW_UH_RUNLOCK(ch);
return (ESRCH);
}
export_table_info(tc, i);
export_table_info(ch, tc, i);
sz = tc->count;
if (sd->valsize < sz + tc->count * sizeof(ipfw_table_xentry)) {
@ -756,7 +918,6 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
return (EINVAL);
memset(&ti, 0, sizeof(ti));
ti.set = 0; /* XXX: No way to specify set */
ti.uidx = xtbl->tbl;
IPFW_UH_RLOCK(ch);
@ -797,27 +958,16 @@ ipfw_dump_table_v0(struct ip_fw_chain *ch, struct sockopt_data *sd)
return (0);
}
/*
* High-level setsockopt cmds
*/
int
ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt,
ip_fw3_opheader *op3)
{
return (ENOTSUP);
}
/*
* Creates new table.
* Data layout:
* Data layout (v0)(current):
* Request: [ ipfw_obj_header ipfw_xtable_info ]
*
* Returns 0 on success
*/
int
ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
ip_fw3_opheader *op3)
ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd)
{
struct _ipfw_obj_header *oh;
ipfw_xtable_info *i;
@ -828,20 +978,19 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
struct table_algo *ta;
uint16_t kidx;
if (sopt->sopt_valsize < sizeof(*oh) + sizeof(ipfw_xtable_info))
if (sd->valsize != sizeof(*oh) + sizeof(ipfw_xtable_info))
return (EINVAL);
oh = (struct _ipfw_obj_header *)op3;
oh = (struct _ipfw_obj_header *)sd->kbuf;
i = (ipfw_xtable_info *)(oh + 1);
/*
* Verify user-supplied strings.
* Check for null-terminated/zero-length strings/
*/
tname = i->tablename;
tname = oh->ntlv.name;
aname = i->algoname;
if (strnlen(tname, sizeof(i->tablename)) == sizeof(i->tablename) ||
tname[0] == '\0' ||
if (ipfw_check_table_name(tname) != 0 ||
strnlen(aname, sizeof(i->algoname)) == sizeof(i->algoname))
return (EINVAL);
@ -851,6 +1000,9 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
}
objheader_to_ti(oh, &ti);
/* Create table in set 0 by default */
ti->set = TABLE_SET(ti->set);
ti.type = i->type;
ni = CHAIN_TO_NI(ch);
@ -867,9 +1019,11 @@ ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
if ((tc = alloc_table_config(ni, &ti, ta, aname)) == NULL)
return (ENOMEM);
/* TODO: move inside alloc_table_config() */
tc->vtype = i->vtype;
IPFW_UH_WLOCK(ch);
if (ipfw_objhash_alloc_idx(ni, ti.set, &kidx) != 0) {
if (ipfw_objhash_alloc_idx(ni, &kidx) != 0) {
IPFW_UH_WUNLOCK(ch);
printf("Unable to allocate table index for table %s in set %u."
" Consider increasing net.inet.ip.fw.tables_max",
@ -910,7 +1064,7 @@ ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
ni = CHAIN_TO_NI(ch);
no = ipfw_objhash_lookup_idx(ni, 0, kidx);
no = ipfw_objhash_lookup_kidx(ni, kidx);
KASSERT(no != NULL, ("invalid table kidx passed"));
ntlv = (ipfw_obj_ntlv *)ipfw_get_sopt_space(sd, sizeof(*ntlv));
@ -926,12 +1080,13 @@ ipfw_export_table_ntlv(struct ip_fw_chain *ch, uint16_t kidx,
}
static void
export_table_info(struct table_config *tc, ipfw_xtable_info *i)
export_table_info(struct ip_fw_chain *ch, struct table_config *tc,
ipfw_xtable_info *i)
{
struct table_info *ti;
i->type = tc->no.type;
i->ftype = tc->ftype;
i->atype = tc->ta->idx;
i->vtype = tc->vtype;
i->set = tc->no.set;
i->kidx = tc->no.kidx;
i->refcnt = tc->no.refcnt;
@ -939,20 +1094,33 @@ export_table_info(struct table_config *tc, ipfw_xtable_info *i)
i->size = tc->count * sizeof(ipfw_table_xentry);
i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
if (tc->ta->print_config != NULL) {
/* Use algo function to print table config to string */
ti = KIDX_TO_TI(ch, tc->no.kidx);
tc->ta->print_config(tc->astate, ti, i->algoname,
sizeof(i->algoname));
} else
strlcpy(i->algoname, tc->ta->name, sizeof(i->algoname));
}
struct dump_table_args {
struct ip_fw_chain *ch;
struct sockopt_data *sd;
};
static void
export_table_internal(struct namedobj_instance *ni, struct named_object *no,
void *arg)
{
ipfw_xtable_info *i;
struct sockopt_data *sd;
struct dump_table_args *dta;
sd = (struct sockopt_data *)arg;
i = (ipfw_xtable_info *)ipfw_get_sopt_space(sd, sizeof(*i));
dta = (struct dump_table_args *)arg;
i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
KASSERT(i == 0, ("previously checked buffer is not enough"));
export_table_info((struct table_config *)no, i);
export_table_info(dta->ch, (struct table_config *)no, i);
}
/*
@ -966,6 +1134,7 @@ export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
{
uint32_t size;
uint32_t count;
struct dump_table_args dta;
count = ipfw_objhash_count(CHAIN_TO_NI(ch));
size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
@ -981,7 +1150,10 @@ export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
}
olh->size = size;
ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, sd);
dta.ch = ch;
dta.sd = sd;
ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, &dta);
return (0);
}
@ -1264,7 +1436,41 @@ update_table_opcode(ipfw_insn *cmd, uint16_t idx)
}
}
static char *
/*
* Checks table name for validity.
* Enforce basic length checks, the rest
* should be done in userland.
*
* Returns 0 if name is considered valid.
*/
int
ipfw_check_table_name(char *name)
{
int nsize;
ipfw_obj_ntlv *ntlv = NULL;
nsize = sizeof(ntlv->name);
if (strnlen(name, nsize) == nsize)
return (EINVAL);
if (name[0] == '\0')
return (EINVAL);
/*
* TODO: do some more complicated checks
*/
return (0);
}
/*
* Find tablename TLV by @uid.
* Check @tlvs for valid data inside.
*
* Returns pointer to found TLV or NULL.
*/
static ipfw_obj_ntlv *
find_name_tlv(void *tlvs, int len, uint16_t uidx)
{
ipfw_obj_ntlv *ntlv;
@ -1277,33 +1483,53 @@ find_name_tlv(void *tlvs, int len, uint16_t uidx)
for (; pa < pe; pa += l) {
ntlv = (ipfw_obj_ntlv *)pa;
l = ntlv->head.length;
if (l != sizeof(*ntlv))
return (NULL);
if (ntlv->head.type != IPFW_TLV_TBL_NAME)
continue;
if (ntlv->idx != uidx)
continue;
return (ntlv->name);
if (ipfw_check_table_name(ntlv->name) != 0)
return (NULL);
return (ntlv);
}
return (NULL);
}
/*
* Finds table config based on either legacy index
* or name in ntlv.
* Note @ti structure contains unchecked data from userland.
*
* Returns pointer to table_config or NULL.
*/
static struct table_config *
find_table(struct namedobj_instance *ni, struct tid_info *ti)
{
char *name, bname[16];
struct named_object *no;
ipfw_obj_ntlv *ntlv;
uint32_t set;
if (ti->tlvs != NULL) {
name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
if (name == NULL)
ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
if (ntlv == NULL)
return (NULL);
name = ntlv->name;
set = ntlv->set;
} else {
snprintf(bname, sizeof(bname), "%d", ti->uidx);
name = bname;
set = 0;
}
no = ipfw_objhash_lookup_name(ni, ti->set, name);
no = ipfw_objhash_lookup_name(ni, set, name);
return ((struct table_config *)no);
}
@ -1315,22 +1541,29 @@ alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
char *name, bname[16];
struct table_config *tc;
int error;
ipfw_obj_ntlv *ntlv;
uint32_t set;
if (ti->tlvs != NULL) {
name = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
if (name == NULL)
ntlv = find_name_tlv(ti->tlvs, ti->tlen, ti->uidx);
if (ntlv == NULL)
return (NULL);
name = ntlv->name;
set = ntlv->set;
} else {
snprintf(bname, sizeof(bname), "%d", ti->uidx);
name = bname;
set = 0;
}
tc = malloc(sizeof(struct table_config), M_IPFW, M_WAITOK | M_ZERO);
tc->no.name = tc->tablename;
tc->no.type = ti->type;
tc->no.set = ti->set;
tc->no.set = set;
tc->ta = ta;
strlcpy(tc->tablename, name, sizeof(tc->tablename));
/* Set default value type to u32 for compability reasons */
tc->vtype = IPFW_VTYPE_U32;
if (ti->tlvs == NULL) {
tc->no.compat = 1;
@ -1458,10 +1691,10 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
}
/* Table not found. Allocate new index and save for later */
if (ipfw_objhash_alloc_idx(ni, ti->set, &pidx->kidx) != 0) {
printf("Unable to allocate table index in set %u."
if (ipfw_objhash_alloc_idx(ni, &pidx->kidx) != 0) {
printf("Unable to allocate table %s index in set %u."
" Consider increasing net.inet.ip.fw.tables_max",
ti->set);
"", ti->set);
error = EBUSY;
break;
}
@ -1475,12 +1708,12 @@ bind_table_rule(struct ip_fw_chain *ch, struct ip_fw *rule,
/* Unref everything we have already done */
for (p = *oib; p < pidx; p++) {
if (p->new != 0) {
ipfw_objhash_free_idx(ni, ci->tableset,p->kidx);
ipfw_objhash_free_idx(ni, p->kidx);
continue;
}
/* Find & unref by existing idx */
no = ipfw_objhash_lookup_idx(ni, ci->tableset, p->kidx);
no = ipfw_objhash_lookup_kidx(ni, p->kidx);
KASSERT(no != NULL, ("Ref'd table %d disappeared",
p->kidx));
@ -1527,7 +1760,7 @@ ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule)
if (classify_table_opcode(cmd, &kidx, &type) != 0)
continue;
if ((no = ipfw_objhash_lookup_idx(ni, set, kidx)) == NULL)
if ((no = ipfw_objhash_lookup_kidx(ni, kidx)) == NULL)
return (1);
if (no->compat == 0)
@ -1619,10 +1852,8 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
type = 0;
ftype = 0;
ci->tableset = TABLE_SET(ci->krule->set);
memset(&ti, 0, sizeof(ti));
ti.set = ci->tableset;
ti.set = TABLE_SET(ci->krule->set);
if (ci->ctlv != NULL) {
ti.tlvs = (void *)(ci->ctlv + 1);
ti.tlen = ci->ctlv->head.length - sizeof(ipfw_obj_ctlv);
@ -1650,7 +1881,6 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
if (p->new == 0)
continue;
/* TODO: get name from TLV */
ti.uidx = p->uidx;
ti.type = p->type;
ti.atype = 0;
@ -1785,7 +2015,7 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
/* Free indexes first */
IPFW_UH_WLOCK(chain);
TAILQ_FOREACH_SAFE(no, &nh, nn_next, no_tmp) {
ipfw_objhash_free_idx(ni, ci->tableset, no->kidx);
ipfw_objhash_free_idx(ni, no->kidx);
}
IPFW_UH_WUNLOCK(chain);
/* Free configs */
@ -1826,7 +2056,7 @@ ipfw_unbind_table_rule(struct ip_fw_chain *chain, struct ip_fw *rule)
if (classify_table_opcode(cmd, &kidx, &type) != 0)
continue;
no = ipfw_objhash_lookup_idx(ni, set, kidx);
no = ipfw_objhash_lookup_kidx(ni, kidx);
KASSERT(no != NULL, ("table id %d not found", kidx));
KASSERT(no->type == type, ("wrong type %d (%d) for table id %d",

View File

@ -41,14 +41,26 @@ struct table_info {
u_long data; /* Hints for given func */
};
/* Internal structures for handling sockopt data */
struct tid_info {
uint32_t set; /* table set */
uint16_t uidx; /* table index */
uint8_t type; /* table type */
uint8_t atype;
void *tlvs; /* Pointer to first TLV */
int tlen; /* Total TLV size block */
};
struct tentry_info {
void *paddr;
int plen; /* Total entry length */
uint8_t masklen; /* mask length */
uint8_t spare;
uint8_t subtype;
uint16_t flags; /* record flags */
uint32_t value; /* value */
};
#define TEI_FLAGS_UPDATE 0x01 /* Update record if exists */
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
typedef int (ta_init)(void **ta_state, struct table_info *ti, char *data);
typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
@ -59,6 +71,8 @@ typedef int (ta_add)(void *ta_state, struct table_info *ti,
typedef int (ta_del)(void *ta_state, struct table_info *ti,
struct tentry_info *tei, void *ta_buf);
typedef void (ta_flush_entry)(struct tentry_info *tei, void *ta_buf);
typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf,
size_t bufsize);
typedef int ta_foreach_f(void *node, void *arg);
typedef void ta_foreach(void *ta_state, struct table_info *ti, ta_foreach_f *f,
@ -82,6 +96,7 @@ struct table_algo {
ta_foreach *foreach;
ta_dump_entry *dump_entry;
ta_dump_xentry *dump_xentry;
ta_print_config *print_config;
};
void ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta);
extern struct table_algo radix_cidr, radix_iface;
@ -97,17 +112,17 @@ int ipfw_dump_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
int ipfw_describe_table(struct ip_fw_chain *ch, struct sockopt_data *sd);
int ipfw_create_table(struct ip_fw_chain *ch, struct sockopt *sopt,
ip_fw3_opheader *op3);
int ipfw_modify_table(struct ip_fw_chain *ch, struct sockopt *sopt,
ip_fw3_opheader *op3);
int ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
int ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
int ipfw_create_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
int ipfw_modify_table(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
struct sockopt_data *sd);
int ipfw_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
struct tentry_info *tei);
int ipfw_del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
struct tentry_info *tei);
int ipfw_destroy_table(struct ip_fw_chain *ch, struct tid_info *ti);
int ipfw_flush_table(struct ip_fw_chain *ch, struct tid_info *ti);
int ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
struct rule_check_info *ci);
int ipfw_rewrite_table_kidx(struct ip_fw_chain *chain, struct ip_fw *rule);
@ -120,6 +135,7 @@ void ipfw_unbind_table_list(struct ip_fw_chain *chain, struct ip_fw *head);
/* utility functions */
void objheader_to_ti(struct _ipfw_obj_header *oh, struct tid_info *ti);
int ipfw_check_table_name(char *name);
/* Legacy interfaces */
int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti,

View File

@ -325,11 +325,12 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
mlen = tei->masklen;
if (tei->plen == sizeof(in_addr_t)) {
if (tei->subtype == AF_INET) {
#ifdef INET
/* IPv4 case */
if (mlen > 32)
return (EINVAL);
ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
ent->value = tei->value;
/* Set 'total' structure length */
@ -346,7 +347,7 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
tb->mask_ptr = (struct sockaddr *)&ent->mask;
#endif
#ifdef INET6
} else if (tei->plen == sizeof(struct in6_addr)) {
} else if (tei->subtype == AF_INET6) {
/* IPv6 case */
if (mlen > 128)
return (EINVAL);
@ -380,18 +381,46 @@ ta_add_cidr(void *ta_state, struct table_info *ti,
struct radix_node_head *rnh;
struct radix_node *rn;
struct ta_buf_cidr *tb;
uint32_t value;
tb = (struct ta_buf_cidr *)ta_buf;
if (tei->plen == sizeof(in_addr_t))
if (tei->subtype == AF_INET)
rnh = ti->state;
else
rnh = ti->xstate;
rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
if (rn == NULL)
if (rn == NULL) {
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
return (EEXIST);
/* Record already exists. Update value if we're asked to */
rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
if (rn == NULL) {
/*
* Radix may have failed addition for other reasons
* like failure in mask allocation code.
*/
return (EINVAL);
}
if (tei->subtype == AF_INET) {
/* IPv4. */
value = ((struct table_entry *)tb->ent_ptr)->value;
((struct table_entry *)rn)->value = value;
} else {
/* IPv6 */
value = ((struct table_xentry *)tb->ent_ptr)->value;
((struct table_xentry *)rn)->value = value;
}
/* Indicate that update has happened instead of addition */
tei->flags |= TEI_FLAGS_UPDATED;
}
tb->ent_ptr = NULL;
return (0);
}
@ -408,7 +437,7 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
mlen = tei->masklen;
if (tei->plen == sizeof(in_addr_t)) {
if (tei->subtype == AF_INET) {
/* Set 'total' structure length */
struct sockaddr_in sa, mask;
KEY_LEN(sa) = KEY_LEN_INET;
@ -421,7 +450,7 @@ ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
tb->addr_ptr = (struct sockaddr *)&tb->addr.a4.sa;
tb->mask_ptr = (struct sockaddr *)&tb->addr.a4.ma;
#ifdef INET6
} else if (tei->plen == sizeof(struct in6_addr)) {
} else if (tei->subtype == AF_INET6) {
/* IPv6 case */
if (mlen > 128)
return (EINVAL);
@ -456,7 +485,7 @@ ta_del_cidr(void *ta_state, struct table_info *ti,
tb = (struct ta_buf_cidr *)ta_buf;
if (tei->plen == sizeof(in_addr_t))
if (tei->subtype == AF_INET)
rnh = ti->state;
else
rnh = ti->xstate;
@ -478,11 +507,12 @@ ta_flush_cidr_entry(struct tentry_info *tei, void *ta_buf)
tb = (struct ta_buf_cidr *)ta_buf;
if (tb->ent_ptr != NULL)
free(tb->ent_ptr, M_IPFW_TBL);
}
struct table_algo radix_cidr = {
.name = "cidr",
.name = "radix_cidr",
.lookup = ta_lookup_radix,
.init = ta_init_radix,
.destroy = ta_destroy_radix,
@ -557,7 +587,7 @@ ta_destroy_iface(void *ta_state, struct table_info *ti)
rnh = (struct radix_node_head *)(ti->xstate);
rnh->rnh_walktree(rnh, flush_iface_entry, rnh);
rn_detachhead(&ti->state);
rn_detachhead(&ti->xstate);
}
struct ta_buf_iface
@ -613,14 +643,31 @@ ta_add_iface(void *ta_state, struct table_info *ti,
struct radix_node_head *rnh;
struct radix_node *rn;
struct ta_buf_iface *tb;
uint32_t value;
tb = (struct ta_buf_iface *)ta_buf;
rnh = ti->xstate;
rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
if (rn == NULL)
return (1);
if (rn == NULL) {
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
return (EEXIST);
/* Record already exists. Update value if we're asked to */
rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
if (rn == NULL) {
/* Radix may have failed addition for other reasons */
return (EINVAL);
}
value = ((struct table_xentry *)tb->ent_ptr)->value;
((struct table_xentry *)rn)->value = value;
/* Indicate that update has happened instead of addition */
tei->flags |= TEI_FLAGS_UPDATED;
}
tb->ent_ptr = NULL;
return (0);
}
@ -689,6 +736,7 @@ ta_flush_iface_entry(struct tentry_info *tei, void *ta_buf)
tb = (struct ta_buf_iface *)ta_buf;
if (tb->ent_ptr != NULL)
free(tb->ent_ptr, M_IPFW_TBL);
}
@ -717,7 +765,7 @@ ta_foreach_iface(void *ta_state, struct table_info *ti, ta_foreach_f *f,
}
struct table_algo radix_iface = {
.name = "iface",
.name = "radix_iface",
.lookup = ta_lookup_iface,
.init = ta_init_iface,
.destroy = ta_destroy_iface,