Move further to eliminate next pieces of number-assuming code inside tables.
Kernel changes: * Add IP_FW_OBJ_FLUSH opcode (flush table based on its name/set) * Add IP_FW_OBJ_DUMP opcode (dumps table data based on its names/set) * Add IP_FW_OBJ_LISTSIZE / IP_FW_OBJ_LIST opcodes (get list of kernel tables) Userland changes: * move tables code to separate tables.c file * get rid of tables_max * switch "all"/list handling to new opcodes
This commit is contained in:
parent
0001953a35
commit
fe9646e6ff
@ -3,7 +3,7 @@
|
||||
.include <src.opts.mk>
|
||||
|
||||
PROG= ipfw
|
||||
SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c
|
||||
SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c
|
||||
WARNS?= 2
|
||||
|
||||
.if ${MK_PF} != "no"
|
||||
|
@ -60,12 +60,6 @@ int resvd_set_number = RESVD_SET;
|
||||
|
||||
int ipfw_socket = -1;
|
||||
|
||||
uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */
|
||||
|
||||
#ifndef s6_addr32
|
||||
#define s6_addr32 __u6_addr.__u6_addr32
|
||||
#endif
|
||||
|
||||
#define CHECK_LENGTH(v, len) do { \
|
||||
if ((v) < (len)) \
|
||||
errx(EX_DATAERR, "Rule too long"); \
|
||||
@ -457,8 +451,8 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
|
||||
* Calls setsockopt() with IP_FW3 as kernel-visible opcode.
|
||||
* Returns 0 on success or -1 otherwise.
|
||||
*/
|
||||
static int
|
||||
do_set3(int optname, ip_fw3_opheader *op3, socklen_t optlen)
|
||||
int
|
||||
do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen)
|
||||
{
|
||||
|
||||
if (co.test_only)
|
||||
@ -474,6 +468,27 @@ do_set3(int optname, ip_fw3_opheader *op3, socklen_t optlen)
|
||||
return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
|
||||
}
|
||||
|
||||
int
|
||||
do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (co.test_only)
|
||||
return (0);
|
||||
|
||||
if (ipfw_socket == -1)
|
||||
ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (ipfw_socket < 0)
|
||||
err(EX_UNAVAILABLE, "socket");
|
||||
|
||||
op3->opcode = optname;
|
||||
|
||||
error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3,
|
||||
(socklen_t *)optlen);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* do_setcmd3 - pass ipfw control cmd to kernel
|
||||
* @optname: option name
|
||||
@ -2232,7 +2247,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
|
||||
{
|
||||
int len = 0;
|
||||
uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
|
||||
uint32_t tables_max;
|
||||
|
||||
cmd->o.len &= ~F_LEN_MASK; /* zero len */
|
||||
|
||||
@ -2251,10 +2265,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
|
||||
*p++ = '\0';
|
||||
cmd->o.opcode = O_IP_DST_LOOKUP;
|
||||
cmd->o.arg1 = strtoul(av + 6, NULL, 0);
|
||||
tables_max = ipfw_get_tables_max();
|
||||
if (cmd->o.arg1 > tables_max)
|
||||
errx(EX_USAGE, "The table number exceeds the maximum "
|
||||
"allowed value (%u)", tables_max - 1);
|
||||
if (p) {
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
|
||||
d[0] = strtoul(p, NULL, 0);
|
||||
@ -4148,409 +4158,3 @@ ipfw_flush(int force)
|
||||
printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
|
||||
}
|
||||
|
||||
|
||||
static void table_list(uint16_t num, int need_header);
|
||||
static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
|
||||
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_show_info(ipfw_xtable_info *i);
|
||||
static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx);
|
||||
|
||||
/*
|
||||
* Retrieve maximum number of tables supported by ipfw(4) module.
|
||||
*/
|
||||
uint32_t
|
||||
ipfw_get_tables_max()
|
||||
{
|
||||
size_t len;
|
||||
uint32_t tables_max;
|
||||
|
||||
if (ipfw_tables_max != 0)
|
||||
return (ipfw_tables_max);
|
||||
|
||||
len = sizeof(tables_max);
|
||||
if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
|
||||
NULL, 0) == -1) {
|
||||
if (co.test_only)
|
||||
tables_max = 128; /* Old conservative default */
|
||||
else
|
||||
errx(1, "Can't determine maximum number of ipfw tables."
|
||||
" Perhaps you forgot to load ipfw module?");
|
||||
}
|
||||
|
||||
ipfw_tables_max = tables_max;
|
||||
|
||||
return (ipfw_tables_max);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
void
|
||||
ipfw_table_handler(int ac, char *av[])
|
||||
{
|
||||
ipfw_table_xentry xent;
|
||||
int do_add;
|
||||
int is_all;
|
||||
uint32_t a;
|
||||
uint32_t tables_max;
|
||||
uint32_t set;
|
||||
int error;
|
||||
char *tablename;
|
||||
|
||||
tables_max = ipfw_get_tables_max();
|
||||
|
||||
memset(&xent, 0, sizeof(xent));
|
||||
|
||||
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");
|
||||
if (xent.tbl >= tables_max)
|
||||
errx(EX_USAGE, "The table number exceeds the maximum allowed "
|
||||
"value (%d)", tables_max - 1);
|
||||
NEED1("table needs command");
|
||||
if (is_all && _substrcmp(*av, "list") != 0
|
||||
&& _substrcmp(*av, "flush") != 0)
|
||||
errx(EX_USAGE, "table number required");
|
||||
|
||||
if (_substrcmp(*av, "add") == 0 ||
|
||||
_substrcmp(*av, "delete") == 0) {
|
||||
do_add = **av == 'a';
|
||||
ac--; av++;
|
||||
if (!ac)
|
||||
errx(EX_USAGE, "address required");
|
||||
|
||||
table_fill_xentry(*av, &xent);
|
||||
|
||||
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_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL,
|
||||
&xent, xent.len) < 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_setcmd3(IP_FW_TABLE_XDEL, &xent, xent.len);
|
||||
if (do_setcmd3(IP_FW_TABLE_XADD, &xent, xent.len) < 0)
|
||||
err(EX_OSERR,
|
||||
"setsockopt(IP_FW_TABLE_XADD)");
|
||||
}
|
||||
}
|
||||
} else if (_substrcmp(*av, "flush") == 0) {
|
||||
a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
|
||||
do {
|
||||
if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl,
|
||||
sizeof(xent.tbl)) < 0)
|
||||
err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
|
||||
} while (++xent.tbl < a);
|
||||
} else if (_substrcmp(*av, "list") == 0) {
|
||||
a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
|
||||
do {
|
||||
table_list(xent.tbl, is_all);
|
||||
} while (++xent.tbl < a);
|
||||
} 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) {
|
||||
ipfw_xtable_info i;
|
||||
if ((error = table_get_info(tablename, set, &i)) != 0)
|
||||
err(EX_OSERR, "failed to request table info");
|
||||
table_show_info(&i);
|
||||
} 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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_NAME;
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = uidx;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys given table @name in given @set.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
table_destroy(char *name, uint32_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
oh.idx = 1;
|
||||
oh.objtype = IPFW_OBJTYPE_TABLE;
|
||||
table_fill_ntlv(&oh.ntlv, name, 1);
|
||||
if (do_set3(IP_FW_OBJ_DEL, &oh.opheader, sizeof(oh)) != 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves info for given table @name in given @set and stores
|
||||
* it inside @i.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
|
||||
{
|
||||
char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
|
||||
ipfw_obj_header *oh;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(tbuf);
|
||||
memset(tbuf, 0, sizeof(tbuf));
|
||||
oh = (ipfw_obj_header *)tbuf;
|
||||
|
||||
oh->opheader.opcode = IP_FW_OBJ_INFO;
|
||||
oh->set = set;
|
||||
oh->idx = 1;
|
||||
oh->objtype = IPFW_OBJTYPE_TABLE;
|
||||
table_fill_ntlv(&oh->ntlv, name, 1);
|
||||
|
||||
if (do_cmd(IP_FW3, oh, (uintptr_t)&sz) < 0)
|
||||
return (-1);
|
||||
|
||||
if (sz < sizeof(tbuf))
|
||||
return (-1);
|
||||
|
||||
*i = *(ipfw_xtable_info *)(oh + 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prints table info struct @i in human-readable form.
|
||||
*/
|
||||
static void
|
||||
table_show_info(ipfw_xtable_info *i)
|
||||
{
|
||||
char *type;
|
||||
|
||||
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);
|
||||
printf("references: %u\n", i->refcnt);
|
||||
printf("items: %u, size: %u\n", i->count, i->size);
|
||||
}
|
||||
|
||||
static void
|
||||
table_list(uint16_t num, int need_header)
|
||||
{
|
||||
ipfw_xtable *tbl;
|
||||
ipfw_table_xentry *xent;
|
||||
socklen_t l;
|
||||
uint32_t *a, sz, tval;
|
||||
char tbuf[128];
|
||||
struct in6_addr *addr6;
|
||||
ip_fw3_opheader *op3;
|
||||
|
||||
/* Prepend value with IP_FW3 header */
|
||||
l = sizeof(ip_fw3_opheader) + sizeof(uint32_t);
|
||||
op3 = alloca(l);
|
||||
/* Zero reserved fields */
|
||||
memset(op3, 0, sizeof(ip_fw3_opheader));
|
||||
a = (uint32_t *)(op3 + 1);
|
||||
*a = num;
|
||||
op3->opcode = IP_FW_TABLE_XGETSIZE;
|
||||
if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0)
|
||||
err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)");
|
||||
|
||||
/* If a is zero we have nothing to do, the table is empty. */
|
||||
if (*a == 0)
|
||||
return;
|
||||
|
||||
l = *a;
|
||||
tbl = safe_calloc(1, l);
|
||||
tbl->opheader.opcode = IP_FW_TABLE_XLIST;
|
||||
tbl->tbl = num;
|
||||
if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0)
|
||||
err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)");
|
||||
if (tbl->cnt && need_header)
|
||||
printf("---table(%d)---\n", tbl->tbl);
|
||||
sz = tbl->size - sizeof(ipfw_xtable);
|
||||
xent = &tbl->xent[0];
|
||||
while (sz > 0) {
|
||||
switch (tbl->type) {
|
||||
case IPFW_TABLE_CIDR:
|
||||
/* IPv4 or IPv6 prefixes */
|
||||
tval = xent->value;
|
||||
addr6 = &xent->k.addr6;
|
||||
|
||||
|
||||
if ((xent->flags & IPFW_TCF_INET) != 0) {
|
||||
/* IPv4 address */
|
||||
inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf));
|
||||
} else {
|
||||
/* IPv6 address */
|
||||
inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf));
|
||||
}
|
||||
|
||||
if (co.do_value_as_ip) {
|
||||
tval = htonl(tval);
|
||||
printf("%s/%u %s\n", tbuf, xent->masklen,
|
||||
inet_ntoa(*(struct in_addr *)&tval));
|
||||
} else
|
||||
printf("%s/%u %u\n", tbuf, xent->masklen, tval);
|
||||
break;
|
||||
case IPFW_TABLE_INTERFACE:
|
||||
/* Interface names */
|
||||
tval = xent->value;
|
||||
if (co.do_value_as_ip) {
|
||||
tval = htonl(tval);
|
||||
printf("%s %s\n", xent->k.iface,
|
||||
inet_ntoa(*(struct in_addr *)&tval));
|
||||
} else
|
||||
printf("%s %u\n", xent->k.iface, tval);
|
||||
}
|
||||
|
||||
if (sz < xent->len)
|
||||
break;
|
||||
sz -= xent->len;
|
||||
xent = (ipfw_table_xentry *)((char *)xent + xent->len);
|
||||
}
|
||||
|
||||
free(tbl);
|
||||
}
|
||||
|
@ -227,9 +227,10 @@ int _substrcmp2(const char *str1, const char* str2, const char* str3);
|
||||
int match_token(struct _s_x *table, char *string);
|
||||
char const *match_value(struct _s_x *p, int value);
|
||||
|
||||
struct _ip_fw3_opheader;
|
||||
int do_cmd(int optname, void *optval, uintptr_t optlen);
|
||||
|
||||
uint32_t ipfw_get_tables_max(void);
|
||||
int do_set3(int optname, struct _ip_fw3_opheader *op3, uintptr_t optlen);
|
||||
int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen);
|
||||
|
||||
struct in6_addr;
|
||||
void n2mask(struct in6_addr *mask, int n);
|
||||
|
597
sbin/ipfw/tables.c
Normal file
597
sbin/ipfw/tables.c
Normal file
@ -0,0 +1,597 @@
|
||||
/*
|
||||
* Copyright (c) 2002-2003 Luigi Rizzo
|
||||
* Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
|
||||
* Copyright (c) 1994 Ugen J.S.Antsilevich
|
||||
*
|
||||
* Idea and grammar partially left from:
|
||||
* Copyright (c) 1993 Daniel Boulet
|
||||
*
|
||||
* Redistribution and use in source forms, with and without modification,
|
||||
* are permitted provided that this entire comment appears intact.
|
||||
*
|
||||
* Redistribution in binary form may occur without any restrictions.
|
||||
* Obviously, it would be nice if you gave credit where credit is due
|
||||
* but requiring it would be too onerous.
|
||||
*
|
||||
* This software is provided ``AS IS'' without any warranties of any kind.
|
||||
*
|
||||
* in-kernel tables support
|
||||
*
|
||||
* $FreeBSD: projects/ipfw/sbin/ipfw/ipfw2.c 267467 2014-06-14 10:58:39Z melifaro $
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <stddef.h> /* offsetof */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/route.h> /* def. of struct route */
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <alias.h>
|
||||
|
||||
#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 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 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);
|
||||
|
||||
typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
|
||||
static int tables_foreach(table_cb_t *f, void *arg, int sort);
|
||||
|
||||
#ifndef s6_addr32
|
||||
#define s6_addr32 __u6_addr.__u6_addr32
|
||||
#endif
|
||||
|
||||
static int
|
||||
lookup_host (char *host, struct in_addr *ipaddr)
|
||||
{
|
||||
struct hostent *he;
|
||||
|
||||
if (!inet_aton(host, ipaddr)) {
|
||||
if ((he = gethostbyname(host)) == NULL)
|
||||
return(-1);
|
||||
*ipaddr = *(struct in_addr *)he->h_addr_list[0];
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
char *tablename;
|
||||
|
||||
memset(xbuf, 0, sizeof(xbuf));
|
||||
op3 = (ip_fw3_opheader *)xbuf;
|
||||
xent = (ipfw_table_xentry *)(op3 + 1);
|
||||
|
||||
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) {
|
||||
do_add = **av == 'a';
|
||||
ac--; av++;
|
||||
if (!ac)
|
||||
errx(EX_USAGE, "address required");
|
||||
|
||||
table_fill_xentry(*av, xent);
|
||||
|
||||
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) {
|
||||
if (is_all == 0) {
|
||||
if ((error = table_flush(tablename, set)) != 0)
|
||||
err(EX_OSERR, "failed to flush table %s info",
|
||||
tablename);
|
||||
} else {
|
||||
error = tables_foreach(table_flush_one, NULL, 1);
|
||||
if (error != 0)
|
||||
err(EX_OSERR, "failed to flush tables list");
|
||||
}
|
||||
} else if (_substrcmp(*av, "list") == 0) {
|
||||
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)
|
||||
err(EX_OSERR, "failed to request table info");
|
||||
table_show_info(&i, NULL);
|
||||
} else {
|
||||
error = tables_foreach(table_show_info, NULL, 1);
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_NAME;
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = uidx;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
static void
|
||||
table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
|
||||
{
|
||||
|
||||
oh->set = i->set;
|
||||
oh->idx = 1;
|
||||
oh->objtype = IPFW_OBJTYPE_TABLE;
|
||||
table_fill_ntlv(&oh->ntlv, i->tablename, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys given table @name in given @set.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
table_destroy(char *name, uint32_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
oh.idx = 1;
|
||||
oh.objtype = IPFW_OBJTYPE_TABLE;
|
||||
table_fill_ntlv(&oh.ntlv, name, 1);
|
||||
if (do_set3(IP_FW_OBJ_DEL, &oh.opheader, sizeof(oh)) != 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flushes given table @name in given @set.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
table_flush(char *name, uint32_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
oh.idx = 1;
|
||||
oh.objtype = IPFW_OBJTYPE_TABLE;
|
||||
table_fill_ntlv(&oh.ntlv, name, 1);
|
||||
if (do_set3(IP_FW_OBJ_FLUSH, &oh.opheader, sizeof(oh)) != 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves info for given table @name in given @set and stores
|
||||
* it inside @i.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
|
||||
{
|
||||
char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
|
||||
ipfw_obj_header *oh;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(tbuf);
|
||||
memset(tbuf, 0, sizeof(tbuf));
|
||||
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_OBJ_INFO, &oh->opheader, &sz) < 0)
|
||||
return (-1);
|
||||
|
||||
if (sz < sizeof(tbuf))
|
||||
return (-1);
|
||||
|
||||
*i = *(ipfw_xtable_info *)(oh + 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prints table info struct @i in human-readable form.
|
||||
*/
|
||||
static int
|
||||
table_show_info(ipfw_xtable_info *i, void *arg)
|
||||
{
|
||||
char *type;
|
||||
|
||||
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);
|
||||
printf(" references: %u\n", i->refcnt);
|
||||
printf(" items: %u, size: %u\n", i->count, i->size);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Function wrappers which can be used either
|
||||
* as is or as foreach function parameter.
|
||||
*/
|
||||
|
||||
static int
|
||||
table_show_one(ipfw_xtable_info *i, void *arg)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
|
||||
if ((oh = malloc(i->size)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
if (table_get_list(i, oh) == 0)
|
||||
table_show_list(oh, 1);
|
||||
|
||||
free(oh);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
table_flush_one(ipfw_xtable_info *i, void *arg)
|
||||
{
|
||||
|
||||
return (table_flush(i->tablename, i->set));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare table names.
|
||||
* Honor number comparison.
|
||||
*/
|
||||
static int
|
||||
tablename_cmp(const void *a, const void *b)
|
||||
{
|
||||
ipfw_xtable_info *ia, *ib;
|
||||
int la, lb;
|
||||
|
||||
ia = (ipfw_xtable_info *)a;
|
||||
ib = (ipfw_xtable_info *)b;
|
||||
la = strlen(ia->tablename);
|
||||
lb = strlen(ib->tablename);
|
||||
|
||||
if (la > lb)
|
||||
return (1);
|
||||
else if (la < lb)
|
||||
return (-01);
|
||||
|
||||
return (strcmp(ia->tablename, ib->tablename));
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves table list from kernel,
|
||||
* optionally sorts it and calls requested function for each table.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
tables_foreach(table_cb_t *f, void *arg, int sort)
|
||||
{
|
||||
ipfw_obj_lheader req, *olh;
|
||||
ipfw_xtable_info *info;
|
||||
size_t sz;
|
||||
int i, error;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
sz = sizeof(req);
|
||||
|
||||
req.objtype = IPFW_OBJTYPE_TABLE;
|
||||
if ((error = do_get3(IP_FW_OBJ_LISTSIZE, &req.opheader, &sz)) != 0)
|
||||
return (errno);
|
||||
|
||||
sz = req.size;
|
||||
if ((olh = calloc(1, sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
olh->objtype = IPFW_OBJTYPE_TABLE;
|
||||
olh->size = sz;
|
||||
if ((error = do_get3(IP_FW_OBJ_LIST, &olh->opheader, &sz)) != 0) {
|
||||
free(olh);
|
||||
return (errno);
|
||||
}
|
||||
|
||||
if (sort != 0)
|
||||
qsort(olh + 1, olh->count, olh->objsize, tablename_cmp);
|
||||
|
||||
info = (ipfw_xtable_info *)(olh + 1);
|
||||
for (i = 0; i < olh->count; i++) {
|
||||
error = f(info, arg); /* Ignore errors for now */
|
||||
info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize);
|
||||
}
|
||||
|
||||
free(olh);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves all entries for given table @i in
|
||||
* eXtended format, returning pointer vi @ooh.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh)
|
||||
{
|
||||
size_t sz;
|
||||
int error;
|
||||
|
||||
table_fill_objheader(oh, i);
|
||||
sz = i->size;
|
||||
|
||||
if ((error = do_get3(IP_FW_OBJ_DUMP, &oh->opheader, &sz)) != 0)
|
||||
return (errno);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shows all entries from @oh in human-readable format
|
||||
*/
|
||||
static void
|
||||
table_show_list(ipfw_obj_header *oh, int need_header)
|
||||
{
|
||||
ipfw_table_xentry *xent;
|
||||
uint32_t count, tval;
|
||||
char tbuf[128];
|
||||
struct in6_addr *addr6;
|
||||
ipfw_xtable_info *i;
|
||||
|
||||
i = (ipfw_xtable_info *)(oh + 1);
|
||||
xent = (ipfw_table_xentry *)(i + 1);
|
||||
|
||||
if (need_header)
|
||||
printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
|
||||
|
||||
count = i->count;
|
||||
while (count > 0) {
|
||||
switch (i->type) {
|
||||
case IPFW_TABLE_CIDR:
|
||||
/* IPv4 or IPv6 prefixes */
|
||||
tval = xent->value;
|
||||
addr6 = &xent->k.addr6;
|
||||
|
||||
|
||||
if ((xent->flags & IPFW_TCF_INET) != 0) {
|
||||
/* IPv4 address */
|
||||
inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf,
|
||||
sizeof(tbuf));
|
||||
} else {
|
||||
/* IPv6 address */
|
||||
inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf));
|
||||
}
|
||||
|
||||
if (co.do_value_as_ip) {
|
||||
tval = htonl(tval);
|
||||
printf("%s/%u %s\n", tbuf, xent->masklen,
|
||||
inet_ntoa(*(struct in_addr *)&tval));
|
||||
} else
|
||||
printf("%s/%u %u\n", tbuf, xent->masklen, tval);
|
||||
break;
|
||||
case IPFW_TABLE_INTERFACE:
|
||||
/* Interface names */
|
||||
tval = xent->value;
|
||||
if (co.do_value_as_ip) {
|
||||
tval = htonl(tval);
|
||||
printf("%s %s\n", xent->k.iface,
|
||||
inet_ntoa(*(struct in_addr *)&tval));
|
||||
} else
|
||||
printf("%s %u\n", xent->k.iface, tval);
|
||||
}
|
||||
|
||||
xent = (ipfw_table_xentry *)((caddr_t)xent + xent->len);
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ typedef struct _ip_fw3_opheader {
|
||||
#define IP_FW_OBJ_LISTSIZE 91 /* get size for table/etc list */
|
||||
#define IP_FW_OBJ_LIST 92 /* list all objects of given type */
|
||||
#define IP_FW_OBJ_INFO 93 /* request info for one object */
|
||||
#define IP_FW_OBJ_FLUSH 94 /* flush data for given object */
|
||||
#define IP_FW_OBJ_DUMP 95 /* dump all data for given object */
|
||||
|
||||
/*
|
||||
* The kernel representation of ipfw rules is made of a list of
|
||||
@ -677,7 +679,10 @@ typedef struct _ipfw_xtable_info {
|
||||
} ipfw_xtable_info;
|
||||
|
||||
#define IPFW_OBJTYPE_TABLE 1
|
||||
/* IP_FW_OBJ_DEL, IP_FW_OBJ_INFO (followed by ipfw_xtable_info) */
|
||||
/*
|
||||
* IP_FW_OBJ_DEL, IP_FW_OBJ_INFO (followed by ipfw_xtable_info),
|
||||
* IP_FW_OBJ_DUMP (followed by ipfw_xtable_info and ipfw_table_xentry'xN )
|
||||
*/
|
||||
typedef struct _ipfw_obj_header {
|
||||
ip_fw3_opheader opheader; /* IP_FW3 opcode */
|
||||
uint32_t set; /* Set we're operating */
|
||||
|
@ -1174,6 +1174,7 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
/*--- TABLE manipulations are protected by the IPFW_LOCK ---*/
|
||||
case IP_FW_OBJ_DEL: /* IP_FW3 */
|
||||
case IP_FW_OBJ_INFO: /* IP_FW3 */
|
||||
case IP_FW_OBJ_FLUSH: /* IP_FW3 */
|
||||
{
|
||||
struct _ipfw_obj_header *oh;
|
||||
struct tid_info ti;
|
||||
@ -1194,6 +1195,8 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
ti.tlen = oh->ntlv.head.length;
|
||||
if (opt == IP_FW_OBJ_DEL)
|
||||
error = ipfw_destroy_table(chain, &ti);
|
||||
else if (opt == IP_FW_OBJ_FLUSH)
|
||||
error = ipfw_flush_table(chain, &ti);
|
||||
else {
|
||||
/* IP_FW_OBJ_INFO */
|
||||
if (sopt->sopt_valsize < sizeof(*oh) +
|
||||
@ -1338,7 +1341,7 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
ti.set = 0; /* XXX: No way to specify set */
|
||||
ti.uidx = tbl->tbl;
|
||||
IPFW_RLOCK(chain);
|
||||
error = ipfw_dump_table(chain, &ti, tbl);
|
||||
error = ipfw_dump_table_legacy(chain, &ti, tbl);
|
||||
IPFW_RUNLOCK(chain);
|
||||
if (error) {
|
||||
free(tbl, M_TEMP);
|
||||
@ -1364,9 +1367,9 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
memset(&ti, 0, sizeof(ti));
|
||||
ti.set = 0; /* XXX: No way to specify set */
|
||||
ti.uidx = *tbl;
|
||||
IPFW_RLOCK(chain);
|
||||
IPFW_UH_RLOCK(chain);
|
||||
error = ipfw_count_xtable(chain, &ti, tbl);
|
||||
IPFW_RUNLOCK(chain);
|
||||
IPFW_UH_RUNLOCK(chain);
|
||||
if (error)
|
||||
break;
|
||||
error = sooptcopyout(sopt, op3, sopt->sopt_valsize);
|
||||
@ -1392,9 +1395,9 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
memset(&ti, 0, sizeof(ti));
|
||||
ti.set = 0; /* XXX: No way to specify set */
|
||||
ti.uidx = tbl->tbl;
|
||||
IPFW_RLOCK(chain);
|
||||
IPFW_UH_RLOCK(chain);
|
||||
error = ipfw_dump_xtable(chain, &ti, tbl);
|
||||
IPFW_RUNLOCK(chain);
|
||||
IPFW_UH_RUNLOCK(chain);
|
||||
if (error) {
|
||||
free(tbl, M_TEMP);
|
||||
break;
|
||||
@ -1412,7 +1415,70 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
free(tbl, M_TEMP);
|
||||
}
|
||||
break;
|
||||
case IP_FW_OBJ_LISTSIZE: /* IP_FW3 */
|
||||
{
|
||||
struct _ipfw_obj_lheader *olh;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*olh)) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
olh = (struct _ipfw_obj_lheader *)op3;
|
||||
|
||||
switch (olh->objtype) {
|
||||
case IPFW_OBJTYPE_TABLE:
|
||||
error = ipfw_listsize_tables(chain, sopt, op3,
|
||||
valsize);
|
||||
break;
|
||||
default:
|
||||
error = ENOTSUP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IP_FW_OBJ_LIST: /* IP_FW3 */
|
||||
{
|
||||
struct _ipfw_obj_lheader *olh;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*olh)) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
olh = (struct _ipfw_obj_lheader *)op3;
|
||||
switch (olh->objtype) {
|
||||
case IPFW_OBJTYPE_TABLE:
|
||||
error = ipfw_list_tables(chain, sopt, op3,
|
||||
valsize);
|
||||
break;
|
||||
default:
|
||||
error = ENOTSUP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IP_FW_OBJ_DUMP: /* IP_FW3 */
|
||||
{
|
||||
struct _ipfw_obj_header *oh;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*oh)) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
oh = (struct _ipfw_obj_header *)op3;
|
||||
switch (oh->objtype) {
|
||||
case IPFW_OBJTYPE_TABLE:
|
||||
error = ipfw_dump_table(chain, sopt, op3,
|
||||
valsize);
|
||||
break;
|
||||
default:
|
||||
error = ENOTSUP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
/*--- NAT operations are protected by the IPFW_LOCK ---*/
|
||||
case IP_FW_NAT_CFG:
|
||||
if (IPFW_NAT_LOADED)
|
||||
|
@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/lock.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/socketvar.h>
|
||||
#include <sys/queue.h>
|
||||
#include <net/if.h> /* ip_fw.h requires IFNAMSIZ */
|
||||
#include <net/route.h>
|
||||
@ -100,6 +101,9 @@ static void free_table_config(struct namedobj_instance *ni,
|
||||
static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
|
||||
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);
|
||||
static void export_table_info(struct table_config *tc, ipfw_xtable_info *i);
|
||||
static int dump_table_xentry(void *e, void *arg);
|
||||
|
||||
static struct table_algo *find_table_algo(struct tables_config *tableconf,
|
||||
struct tid_info *ti);
|
||||
@ -533,6 +537,181 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, uint16_t plen,
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* High-level handlers for setsockopt
|
||||
*/
|
||||
int
|
||||
ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
{
|
||||
struct _ipfw_obj_lheader *olh;
|
||||
|
||||
olh = (struct _ipfw_obj_lheader *)op3;
|
||||
|
||||
olh->size = sizeof(*olh); /* Make export_table store needed size */
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
export_tables(ch, olh);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
sopt->sopt_valsize = sizeof(*olh);
|
||||
return (sooptcopyout(sopt, olh, sopt->sopt_valsize));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
{
|
||||
struct _ipfw_obj_lheader *olh;
|
||||
uint32_t sz, sz_min, sz_max;
|
||||
int error;
|
||||
|
||||
olh = (struct _ipfw_obj_lheader *)op3;
|
||||
|
||||
if (valsize != olh->size)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
* Check if array size is "reasonable":
|
||||
* Permit valsize between current size and
|
||||
* 2x current size + 1
|
||||
*/
|
||||
IPFW_UH_RLOCK(ch);
|
||||
sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
sz_min = sz * sizeof(ipfw_xtable_info) + sizeof(*olh);
|
||||
sz_max = sz_min + (sz + 1) * sizeof(ipfw_xtable_info);
|
||||
|
||||
if (valsize < sz_min || valsize > sz_max)
|
||||
return (EINVAL);
|
||||
|
||||
olh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
|
||||
/* Copy header to new storage */
|
||||
memcpy(olh, op3, sizeof(*olh));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
error = export_tables(ch, olh);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
if (error != 0) {
|
||||
free(olh, M_TEMP);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Since we call sooptcopyin() with small buffer,
|
||||
* sopt_valsize is decreased to reflect supplied
|
||||
* buffer size. Set it back to original value.
|
||||
*/
|
||||
sopt->sopt_valsize = valsize;
|
||||
error = sooptcopyout(sopt, olh, olh->size);
|
||||
free(olh, M_TEMP);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct dump_args {
|
||||
struct table_info *ti;
|
||||
struct table_config *tc;
|
||||
ipfw_table_entry *ent;
|
||||
ipfw_table_xentry *xent;
|
||||
uint32_t cnt;
|
||||
uint32_t size;
|
||||
uint16_t uidx;
|
||||
};
|
||||
|
||||
int
|
||||
ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize)
|
||||
{
|
||||
struct _ipfw_obj_header *oh;
|
||||
ipfw_xtable_info *i;
|
||||
struct tid_info ti;
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
struct dump_args da;
|
||||
uint32_t sz, sz_min, sz_max;
|
||||
int error;
|
||||
|
||||
oh = (struct _ipfw_obj_header *)op3;
|
||||
|
||||
memset(&ti, 0, sizeof(ti));
|
||||
ti.set = oh->set;
|
||||
ti.uidx = oh->idx;
|
||||
ti.tlvs = &oh->ntlv;
|
||||
ti.tlen = oh->ntlv.head.length;
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (ESRCH);
|
||||
}
|
||||
sz = tc->count;
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
/*
|
||||
* Check if array size is "reasonable":
|
||||
* Permit valsize between current size and
|
||||
* 2x current size + 1
|
||||
*/
|
||||
|
||||
sz_min = sz * sizeof(ipfw_table_xentry) + sizeof(ipfw_xtable_info) +
|
||||
sizeof(*oh);
|
||||
|
||||
sz_max = sz_min + (sz + 1) * sizeof(ipfw_table_xentry);
|
||||
|
||||
if (valsize < sz_min || valsize > sz_max)
|
||||
return (EINVAL);
|
||||
|
||||
oh = malloc(valsize, M_TEMP, M_ZERO | M_WAITOK);
|
||||
i = (ipfw_xtable_info *)(oh + 1);
|
||||
/* Copy header to new storage */
|
||||
memcpy(oh, op3, sizeof(*oh));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
/* Find table and export info */
|
||||
if ((tc = find_table(CHAIN_TO_NI(ch), &ti)) == NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
free(oh, M_TEMP);
|
||||
return (ESRCH);
|
||||
}
|
||||
|
||||
export_table_info(tc, i);
|
||||
if (i->size > valsize) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
/* XXX: Should we pass size structure back ? */
|
||||
free(oh, M_TEMP);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the actual dump in eXtended format
|
||||
*/
|
||||
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
|
||||
da.tc = tc;
|
||||
da.xent = (ipfw_table_xentry *)(i + 1);
|
||||
da.size = (valsize - sizeof(*oh) - sizeof(ipfw_xtable_info)) /
|
||||
sizeof(ipfw_table_xentry);
|
||||
|
||||
ta = tc->ta;
|
||||
|
||||
ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
/*
|
||||
* Since we call sooptcopyin() with small buffer,
|
||||
* sopt_valsize is decreased to reflect supplied
|
||||
* buffer size. Set it back to original value.
|
||||
*/
|
||||
sopt->sopt_valsize = valsize;
|
||||
error = sooptcopyout(sopt, oh, i->size);
|
||||
free(oh, M_TEMP);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
export_table_info(struct table_config *tc, ipfw_xtable_info *i)
|
||||
{
|
||||
@ -545,8 +724,7 @@ export_table_info(struct table_config *tc, ipfw_xtable_info *i)
|
||||
i->refcnt = tc->no.refcnt;
|
||||
i->count = tc->count;
|
||||
i->size = tc->count * sizeof(ipfw_table_xentry);
|
||||
if (tc->count > 0)
|
||||
i->size += sizeof(ipfw_xtable);
|
||||
i->size += sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info);
|
||||
strlcpy(i->tablename, tc->tablename, sizeof(i->tablename));
|
||||
}
|
||||
|
||||
@ -592,17 +770,26 @@ export_table_internal(struct namedobj_instance *ni, struct named_object *no,
|
||||
export_table_info((struct table_config *)no, i);
|
||||
}
|
||||
|
||||
int
|
||||
ipfw_list_tables(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
ipfw_obj_lheader *olh)
|
||||
/*
|
||||
* Export all tables as ipfw_xtable_info structures to
|
||||
* storage provided by @olh.
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
static int
|
||||
export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh)
|
||||
{
|
||||
uint32_t size;
|
||||
uint32_t count;
|
||||
|
||||
count = ipfw_objhash_count(CHAIN_TO_NI(ch));
|
||||
size = count * sizeof(ipfw_xtable_info) + sizeof(ipfw_obj_lheader);
|
||||
if (size > olh->size)
|
||||
if (size > olh->size) {
|
||||
/* Store new values anyway */
|
||||
olh->count = count;
|
||||
olh->size = size;
|
||||
olh->objsize = sizeof(ipfw_xtable_info);
|
||||
return (ENOMEM);
|
||||
}
|
||||
|
||||
olh->count = 0;
|
||||
ipfw_objhash_foreach(CHAIN_TO_NI(ch), export_table_internal, olh);
|
||||
@ -639,17 +826,9 @@ ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti, uint32_t *cnt)
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct dump_args {
|
||||
struct table_info *ti;
|
||||
struct table_config *tc;
|
||||
ipfw_table *tbl;
|
||||
ipfw_xtable *xtbl;
|
||||
};
|
||||
|
||||
static int
|
||||
dump_table_entry(void *e, void *arg)
|
||||
{
|
||||
ipfw_table *tbl;
|
||||
struct dump_args *da;
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
@ -657,22 +836,25 @@ dump_table_entry(void *e, void *arg)
|
||||
|
||||
da = (struct dump_args *)arg;
|
||||
|
||||
tbl = da->tbl;
|
||||
tc = da->tc;
|
||||
ta = tc->ta;
|
||||
|
||||
/* Out of memory, returning */
|
||||
if (tbl->cnt == tbl->size)
|
||||
if (da->cnt == da->size)
|
||||
return (1);
|
||||
ent = &tbl->ent[tbl->cnt];
|
||||
ent->tbl = tbl->tbl;
|
||||
tbl->cnt++;
|
||||
ent = da->ent++;
|
||||
ent->tbl = da->uidx;
|
||||
da->cnt++;
|
||||
|
||||
return (ta->dump_entry(tc->astate, da->ti, e, ent));
|
||||
}
|
||||
|
||||
/*
|
||||
* Dumps table in pre-8.1 legacy format.
|
||||
*/
|
||||
int
|
||||
ipfw_dump_table(struct ip_fw_chain *ch, struct tid_info *ti, ipfw_table *tbl)
|
||||
ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
ipfw_table *tbl)
|
||||
{
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
@ -690,10 +872,13 @@ ipfw_dump_table(struct ip_fw_chain *ch, struct tid_info *ti, ipfw_table *tbl)
|
||||
|
||||
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
|
||||
da.tc = tc;
|
||||
da.tbl = tbl;
|
||||
da.ent = &tbl->ent[0];
|
||||
da.cnt = 0;
|
||||
da.size = tbl->size;
|
||||
|
||||
tbl->cnt = 0;
|
||||
ta->foreach(tc->astate, da.ti, dump_table_entry, &da);
|
||||
tbl->cnt = da.cnt;
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -702,7 +887,6 @@ ipfw_dump_table(struct ip_fw_chain *ch, struct tid_info *ti, ipfw_table *tbl)
|
||||
static int
|
||||
dump_table_xentry(void *e, void *arg)
|
||||
{
|
||||
ipfw_xtable *xtbl;
|
||||
struct dump_args *da;
|
||||
struct table_config *tc;
|
||||
struct table_algo *ta;
|
||||
@ -710,17 +894,16 @@ dump_table_xentry(void *e, void *arg)
|
||||
|
||||
da = (struct dump_args *)arg;
|
||||
|
||||
xtbl = da->xtbl;
|
||||
tc = da->tc;
|
||||
ta = tc->ta;
|
||||
|
||||
/* Out of memory, returning */
|
||||
if (xtbl->cnt == xtbl->size)
|
||||
if (da->cnt == da->size)
|
||||
return (1);
|
||||
xent = &xtbl->xent[xtbl->cnt];
|
||||
xent = da->xent++;
|
||||
xent->len = sizeof(ipfw_table_xentry);
|
||||
xent->tbl = xtbl->tbl;
|
||||
xtbl->cnt++;
|
||||
xent->tbl = da->uidx;
|
||||
da->cnt++;
|
||||
|
||||
return (ta->dump_xentry(tc->astate, da->ti, e, xent));
|
||||
}
|
||||
@ -740,11 +923,15 @@ ipfw_dump_xtable(struct ip_fw_chain *ch, struct tid_info *ti, ipfw_xtable *xtbl)
|
||||
|
||||
da.ti = KIDX_TO_TI(ch, tc->no.kidx);
|
||||
da.tc = tc;
|
||||
da.xtbl = xtbl;
|
||||
da.xent = &xtbl->xent[0];
|
||||
da.size = xtbl->size;
|
||||
da.cnt = 0;
|
||||
xtbl->type = tc->no.type;
|
||||
xtbl->tbl = ti->uidx;
|
||||
ta = tc->ta;
|
||||
|
||||
ta->foreach(tc->astate, da.ti, dump_table_xentry, &da);
|
||||
xtbl->cnt = da.cnt;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
@ -77,6 +77,14 @@ struct table_algo {
|
||||
void ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta);
|
||||
extern struct table_algo radix_cidr, radix_iface;
|
||||
|
||||
/* direct ipfw_ctl handlers */
|
||||
int ipfw_listsize_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
int ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
int ipfw_dump_table(struct ip_fw_chain *ch, struct sockopt *sopt,
|
||||
ip_fw3_opheader *op3, size_t valsize);
|
||||
|
||||
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_add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
@ -87,15 +95,13 @@ int ipfw_count_table(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
uint32_t *cnt);
|
||||
int ipfw_count_xtable(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
uint32_t *cnt);
|
||||
int ipfw_dump_table(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
int ipfw_dump_table_legacy(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
ipfw_table *tbl);
|
||||
int ipfw_dump_xtable(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
ipfw_xtable *tbl);
|
||||
int ipfw_describe_table(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
ipfw_xtable_info *i);
|
||||
int ipfw_count_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh);
|
||||
int ipfw_list_tables(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
ipfw_obj_lheader *olh);
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user