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:
parent
6c2997ffec
commit
ac35ff1784
@ -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 )
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
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;
|
||||
}
|
||||
error = ipfw_flush_table(chain, op3, &sdata);
|
||||
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;
|
||||
|
||||
|
@ -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,24 +227,24 @@ 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)
|
||||
ta->flush_entry(tei, &ta_buf);
|
||||
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;
|
||||
|
||||
if (ipfw_check_table_name(ntlv->name) != 0)
|
||||
return (NULL);
|
||||
|
||||
return (ntlv->name);
|
||||
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",
|
||||
|
@ -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,
|
||||
|
@ -324,12 +324,13 @@ ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
|
||||
memset(tb, 0, sizeof(struct ta_buf_cidr));
|
||||
|
||||
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)
|
||||
return (EEXIST);
|
||||
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;
|
||||
|
||||
free(tb->ent_ptr, M_IPFW_TBL);
|
||||
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,7 +736,8 @@ ta_flush_iface_entry(struct tentry_info *tei, void *ta_buf)
|
||||
|
||||
tb = (struct ta_buf_iface *)ta_buf;
|
||||
|
||||
free(tb->ent_ptr, M_IPFW_TBL);
|
||||
if (tb->ent_ptr != NULL)
|
||||
free(tb->ent_ptr, M_IPFW_TBL);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user