Introduce a new feature to IPFW2: lookup tables. These are useful
for handling large sparse address sets. Initial implementation by Vsevolod Lobko <seva@ip.net.ua>, refined by me. MFC after: 1 week
This commit is contained in:
parent
e6a8fb50df
commit
27bed143c8
@ -1,7 +1,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd December 1, 2003
|
||||
.Dd June 9, 2004
|
||||
.Dt IPFW 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -43,6 +43,15 @@
|
||||
.Cm set show
|
||||
.Pp
|
||||
.Nm
|
||||
.Cm table Ar number Cm add Ar addr Ns Oo / Ns Ar masklen Oc Op Ar value
|
||||
.Nm
|
||||
.Cm table Ar number Cm delete Ar addr Ns Op / Ns Ar masklen
|
||||
.Nm
|
||||
.Cm table Ar number Cm flush
|
||||
.Nm
|
||||
.Cm table Ar number Cm list
|
||||
.Pp
|
||||
.Nm
|
||||
.Brq Cm pipe | queue
|
||||
.Ar number
|
||||
.Cm config
|
||||
@ -758,13 +767,26 @@ The second format (
|
||||
.Em or-block
|
||||
with multiple addresses) is provided for convenience only and
|
||||
its use is discouraged.
|
||||
.It Ar addr : Oo Cm not Oc Brq Cm any | me | Ar addr-list | Ar addr-set
|
||||
.It Ar addr : Oo Cm not Oc Bro
|
||||
.Cm any | me |
|
||||
.Cm table Ns Pq Ar number Ns Op , Ns Ar value
|
||||
.Ar | addr-list | addr-set
|
||||
.Brc
|
||||
.It Cm any
|
||||
matches any IP address.
|
||||
.It Cm me
|
||||
matches any IP address configured on an interface in the system.
|
||||
The address list is evaluated at the time the packet is
|
||||
analysed.
|
||||
.It Cm table Ns Pq Ar number Ns Op , Ns Ar value
|
||||
Matches any IP address for which an entry exists in the lookup table
|
||||
.Ar number .
|
||||
If an optional 32-bit unsigned
|
||||
.Ar value
|
||||
is also specified, an entry will match only if it has this value.
|
||||
See the
|
||||
.Sx LOOKUP TABLES
|
||||
section below for more information on lookup tables.
|
||||
.It Ar addr-list : ip-addr Ns Op Ns , Ns Ar addr-list
|
||||
.It Ar ip-addr :
|
||||
A host or subnet address specified in one of the following ways:
|
||||
@ -1248,6 +1270,43 @@ the Cisco IOS command:
|
||||
This option can be used to make anti-spoofing rules to reject all
|
||||
packets whose source address is unreachable.
|
||||
.El
|
||||
.Sh LOOKUP TABLES
|
||||
Lookup tables are useful to handle large sparse address sets,
|
||||
typically from a hundred to several thousands of entries.
|
||||
There could be 128 different lookup tables, numbered 0 to 127.
|
||||
.Pp
|
||||
Each entry is represented by an
|
||||
.Ar addr Ns Op / Ns Ar masklen
|
||||
and will match all addresses with base
|
||||
.Ar addr
|
||||
(specified as a dotted quad or a hostname)
|
||||
and mask width of
|
||||
.Ar masklen
|
||||
bits.
|
||||
If
|
||||
.Ar masklen
|
||||
is not specified, it defaults to 32.
|
||||
When looking up an IP address in a table, the most specific
|
||||
entry will match.
|
||||
Associated with each entry is a 32-bit unsigned
|
||||
.Ar value ,
|
||||
which can optionally be checked by a rule matching code.
|
||||
When adding an entry, if
|
||||
.Ar value
|
||||
is not specified, it defaults to 0.
|
||||
.Pp
|
||||
An entry can be added to a table
|
||||
.Pq Cm add ,
|
||||
removed from a table
|
||||
.Pq Cm delete ,
|
||||
a table can be examined
|
||||
.Pq Cm list
|
||||
or flushed
|
||||
.Pq Cm flush .
|
||||
.Pp
|
||||
Internally, each table is stored in a Radix tree, the same way as
|
||||
the routing table (see
|
||||
.Xr route 4 ) .
|
||||
.Sh SETS OF RULES
|
||||
Each rule belongs to one of 32 different
|
||||
.Em sets
|
||||
|
@ -385,7 +385,8 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
|
||||
err(EX_UNAVAILABLE, "socket");
|
||||
|
||||
if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
|
||||
optname == IP_FW_ADD)
|
||||
optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
|
||||
optname == IP_FW_TABLE_GETSIZE)
|
||||
i = getsockopt(s, IPPROTO_IP, optname, optval,
|
||||
(socklen_t *)optlen);
|
||||
else
|
||||
@ -714,6 +715,14 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
|
||||
printf("me");
|
||||
return;
|
||||
}
|
||||
if (cmd->o.opcode == O_IP_SRC_LOOKUP ||
|
||||
cmd->o.opcode == O_IP_DST_LOOKUP) {
|
||||
printf("table(%u", ((ipfw_insn *)cmd)->arg1);
|
||||
if (len == F_INSN_SIZE(ipfw_insn_u32))
|
||||
printf(",%u", *a);
|
||||
printf(")");
|
||||
return;
|
||||
}
|
||||
if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
|
||||
uint32_t x, *map = (uint32_t *)&(cmd->mask);
|
||||
int i, j;
|
||||
@ -1088,6 +1097,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
|
||||
break;
|
||||
|
||||
case O_IP_SRC:
|
||||
case O_IP_SRC_LOOKUP:
|
||||
case O_IP_SRC_MASK:
|
||||
case O_IP_SRC_ME:
|
||||
case O_IP_SRC_SET:
|
||||
@ -1102,6 +1112,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
|
||||
break;
|
||||
|
||||
case O_IP_DST:
|
||||
case O_IP_DST_LOOKUP:
|
||||
case O_IP_DST_MASK:
|
||||
case O_IP_DST_ME:
|
||||
case O_IP_DST_SET:
|
||||
@ -1870,13 +1881,14 @@ help(void)
|
||||
"{pipe|queue} N config PIPE-BODY\n"
|
||||
"[pipe|queue] {zero|delete|show} [N{,N}]\n"
|
||||
"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n"
|
||||
"table N {add ip[/bits] [value] | delete ip[/bits] | flush | list}\n"
|
||||
"\n"
|
||||
"RULE-BODY: check-state [LOG] | ACTION [LOG] ADDR [OPTION_LIST]\n"
|
||||
"ACTION: check-state | allow | count | deny | reject | skipto N |\n"
|
||||
" {divert|tee} PORT | forward ADDR | pipe N | queue N\n"
|
||||
"ADDR: [ MAC dst src ether_type ] \n"
|
||||
" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n"
|
||||
"IPADDR: [not] { any | me | ip/bits{x,y,z} | IPLIST }\n"
|
||||
"IPADDR: [not] { any | me | ip/bits{x,y,z} | table(t[,v]) | IPLIST }\n"
|
||||
"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n"
|
||||
"OPTION_LIST: OPTION [OPTION_LIST]\n"
|
||||
"OPTION: bridged | {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n"
|
||||
@ -1932,6 +1944,21 @@ fill_ip(ipfw_insn_ip *cmd, char *av)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(av, "table(", 6)) {
|
||||
char *p = strchr(av + 6, ',');
|
||||
|
||||
if (p)
|
||||
*p++ = '\0';
|
||||
cmd->o.opcode = O_IP_DST_LOOKUP;
|
||||
cmd->o.arg1 = strtoul(av + 6, NULL, 0);
|
||||
if (p) {
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
|
||||
d[0] = strtoul(p, NULL, 0);
|
||||
} else
|
||||
cmd->o.len |= F_INSN_SIZE(ipfw_insn);
|
||||
return;
|
||||
}
|
||||
|
||||
while (av) {
|
||||
/*
|
||||
* After the address we can have '/' or ':' indicating a mask,
|
||||
@ -2663,6 +2690,8 @@ add_srcip(ipfw_insn *cmd, char *av)
|
||||
fill_ip((ipfw_insn_ip *)cmd, av);
|
||||
if (cmd->opcode == O_IP_DST_SET) /* set */
|
||||
cmd->opcode = O_IP_SRC_SET;
|
||||
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
|
||||
cmd->opcode = O_IP_SRC_LOOKUP;
|
||||
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
|
||||
cmd->opcode = O_IP_SRC_ME;
|
||||
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
|
||||
@ -2678,6 +2707,8 @@ add_dstip(ipfw_insn *cmd, char *av)
|
||||
fill_ip((ipfw_insn_ip *)cmd, av);
|
||||
if (cmd->opcode == O_IP_DST_SET) /* set */
|
||||
;
|
||||
else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */
|
||||
;
|
||||
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
|
||||
cmd->opcode = O_IP_DST_ME;
|
||||
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
|
||||
@ -3592,6 +3623,79 @@ free_args(int ac, char **av)
|
||||
free(av);
|
||||
}
|
||||
|
||||
/*
|
||||
* This one handles all table-related commands
|
||||
* ipfw table N add addr[/masklen] [value]
|
||||
* ipfw table N delete addr[/masklen]
|
||||
* ipfw table N flush
|
||||
* ipfw table N list
|
||||
*/
|
||||
static void
|
||||
table_handler(int ac, char *av[])
|
||||
{
|
||||
ipfw_table_entry ent;
|
||||
ipfw_table *tbl;
|
||||
int do_add;
|
||||
char *p;
|
||||
socklen_t l;
|
||||
uint32_t a;
|
||||
|
||||
ac--; av++;
|
||||
if (ac && isdigit(**av)) {
|
||||
ent.tbl = atoi(*av);
|
||||
ac--; av++;
|
||||
} else
|
||||
errx(EX_USAGE, "table number required");
|
||||
NEED1("table needs command");
|
||||
if (strncmp(*av, "add", strlen(*av)) == 0 ||
|
||||
strncmp(*av, "delete", strlen(*av)) == 0) {
|
||||
do_add = **av == 'a';
|
||||
ac--; av++;
|
||||
if (!ac)
|
||||
errx(EX_USAGE, "IP address required");
|
||||
p = strchr(*av, '/');
|
||||
if (p) {
|
||||
*p++ = '\0';
|
||||
ent.masklen = atoi(p);
|
||||
if (ent.masklen > 32)
|
||||
errx(EX_DATAERR, "bad width ``%s''", p);
|
||||
} else
|
||||
ent.masklen = 32;
|
||||
if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0)
|
||||
errx(EX_NOHOST, "hostname ``%s'' unknown", av);
|
||||
ac--; av++;
|
||||
if (do_add && ac)
|
||||
ent.value = strtoul(*av, NULL, 0);
|
||||
else
|
||||
ent.value = 0;
|
||||
if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
|
||||
&ent, sizeof(ent)) < 0)
|
||||
err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
|
||||
do_add ? "ADD" : "DEL");
|
||||
} else if (strncmp(*av, "flush", strlen(*av)) == 0) {
|
||||
if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, sizeof(ent.tbl)) < 0)
|
||||
err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
|
||||
} else if (strncmp(*av, "list", strlen(*av)) == 0) {
|
||||
a = ent.tbl;
|
||||
l = sizeof(a);
|
||||
if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0)
|
||||
err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)");
|
||||
l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
|
||||
tbl = malloc(l);
|
||||
if (tbl == NULL)
|
||||
err(EX_OSERR, "malloc");
|
||||
tbl->tbl = ent.tbl;
|
||||
if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
|
||||
err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
|
||||
for (a = 0; a < tbl->cnt; a++) {
|
||||
printf("%s/%u %u\n",
|
||||
inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
|
||||
tbl->ent[a].masklen, tbl->ent[a].value);
|
||||
}
|
||||
} else
|
||||
errx(EX_USAGE, "invalid table command %s", *av);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with the arguments (excluding program name).
|
||||
* Returns 0 if successful, 1 if empty command, errx() in case of errors.
|
||||
@ -3822,6 +3926,8 @@ ipfw_main(int oldac, char **oldav)
|
||||
list(ac, av, do_acct);
|
||||
else if (!strncmp(*av, "set", strlen(*av)))
|
||||
sets_handler(ac, av);
|
||||
else if (!strncmp(*av, "table", strlen(*av)))
|
||||
table_handler(ac, av);
|
||||
else if (!strncmp(*av, "enable", strlen(*av)))
|
||||
sysctl_handler(ac, av, 1);
|
||||
else if (!strncmp(*av, "disable", strlen(*av)))
|
||||
|
@ -386,6 +386,12 @@ __END_DECLS
|
||||
|
||||
#define IP_ONESBCAST 23 /* bool: send all-ones broadcast */
|
||||
|
||||
#define IP_FW_TABLE_ADD 40 /* add entry */
|
||||
#define IP_FW_TABLE_DEL 41 /* delete entry */
|
||||
#define IP_FW_TABLE_FLUSH 42 /* flush table */
|
||||
#define IP_FW_TABLE_GETSIZE 43 /* get table size */
|
||||
#define IP_FW_TABLE_LIST 44 /* list table contents */
|
||||
|
||||
#define IP_FW_ADD 50 /* add a firewall rule to chain */
|
||||
#define IP_FW_DEL 51 /* delete a firewall rule from chain */
|
||||
#define IP_FW_FLUSH 52 /* flush firewall rule chain */
|
||||
|
@ -126,6 +126,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
|
||||
* More opcodes.
|
||||
*/
|
||||
O_IPSEC, /* has ipsec history */
|
||||
O_IP_SRC_LOOKUP, /* arg1=table number, u32=value */
|
||||
O_IP_DST_LOOKUP, /* arg1=table number, u32=value */
|
||||
|
||||
O_LAST_OPCODE /* not an opcode! */
|
||||
};
|
||||
@ -375,6 +377,23 @@ struct _ipfw_dyn_rule {
|
||||
|
||||
#define ICMP_REJECT_RST 0x100 /* fake ICMP code (send a TCP RST) */
|
||||
|
||||
/*
|
||||
* These are used for lookup tables.
|
||||
*/
|
||||
typedef struct _ipfw_table_entry {
|
||||
in_addr_t addr; /* network address */
|
||||
u_int32_t value; /* value */
|
||||
u_int16_t tbl; /* table number */
|
||||
u_int8_t masklen; /* mask length */
|
||||
} ipfw_table_entry;
|
||||
|
||||
typedef struct _ipfw_table {
|
||||
u_int32_t size; /* size of entries in bytes */
|
||||
u_int32_t cnt; /* # of entries */
|
||||
u_int16_t tbl; /* table number */
|
||||
ipfw_table_entry ent[0]; /* entries */
|
||||
} ipfw_table;
|
||||
|
||||
/*
|
||||
* Main firewall chains definitions and global var's definitions.
|
||||
*/
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <net/if.h>
|
||||
#include <net/radix.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
@ -131,6 +132,19 @@ struct ip_fw_chain {
|
||||
static struct ip_fw_chain layer3_chain;
|
||||
|
||||
MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
|
||||
MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
|
||||
|
||||
struct table_entry {
|
||||
struct radix_node rn[2];
|
||||
struct sockaddr_in addr, mask;
|
||||
u_int32_t value;
|
||||
};
|
||||
|
||||
#define IPFW_TABLES_MAX 128
|
||||
static struct {
|
||||
struct radix_node_head *rnh;
|
||||
int modified;
|
||||
} ipfw_tables[IPFW_TABLES_MAX];
|
||||
|
||||
static int fw_debug = 1;
|
||||
static int autoinc_step = 100; /* bounded to 1..1000 in add_rule() */
|
||||
@ -1313,6 +1327,204 @@ lookup_next_rule(struct ip_fw *me)
|
||||
return rule;
|
||||
}
|
||||
|
||||
static void
|
||||
init_tables(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < IPFW_TABLES_MAX; i++) {
|
||||
rn_inithead((void **)&ipfw_tables[i].rnh, 32);
|
||||
ipfw_tables[i].modified = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
add_table_entry(u_int16_t tbl, in_addr_t addr, u_int8_t mlen, u_int32_t value)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
struct table_entry *ent;
|
||||
|
||||
if (tbl >= IPFW_TABLES_MAX)
|
||||
return (EINVAL);
|
||||
rnh = ipfw_tables[tbl].rnh;
|
||||
ent = malloc(sizeof(*ent), M_IPFW_TBL, M_NOWAIT | M_ZERO);
|
||||
if (ent == NULL)
|
||||
return (ENOMEM);
|
||||
ent->value = value;
|
||||
ent->addr.sin_len = ent->mask.sin_len = 8;
|
||||
ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
|
||||
ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
|
||||
RADIX_NODE_HEAD_LOCK(rnh);
|
||||
if (rnh->rnh_addaddr(&ent->addr, &ent->mask, rnh, (void *)ent) ==
|
||||
NULL) {
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
free(ent, M_IPFW_TBL);
|
||||
return (EEXIST);
|
||||
}
|
||||
ipfw_tables[tbl].modified = 1;
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
del_table_entry(u_int16_t tbl, in_addr_t addr, u_int8_t mlen)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
struct table_entry *ent;
|
||||
struct sockaddr_in sa, mask;
|
||||
|
||||
if (tbl >= IPFW_TABLES_MAX)
|
||||
return (EINVAL);
|
||||
rnh = ipfw_tables[tbl].rnh;
|
||||
sa.sin_len = mask.sin_len = 8;
|
||||
mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
|
||||
sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
|
||||
RADIX_NODE_HEAD_LOCK(rnh);
|
||||
ent = (struct table_entry *)rnh->rnh_deladdr(&sa, &mask, rnh);
|
||||
if (ent == NULL) {
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
return (ESRCH);
|
||||
}
|
||||
ipfw_tables[tbl].modified = 1;
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
free(ent, M_IPFW_TBL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
flush_table_entry(struct radix_node *rn, void *arg)
|
||||
{
|
||||
struct radix_node_head * const rnh = arg;
|
||||
struct table_entry *ent;
|
||||
|
||||
ent = (struct table_entry *)
|
||||
rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
|
||||
if (ent != NULL)
|
||||
free(ent, M_IPFW_TBL);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
flush_table(u_int16_t tbl)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
|
||||
if (tbl >= IPFW_TABLES_MAX)
|
||||
return (EINVAL);
|
||||
rnh = ipfw_tables[tbl].rnh;
|
||||
RADIX_NODE_HEAD_LOCK(rnh);
|
||||
rnh->rnh_walktree(rnh, flush_table_entry, rnh);
|
||||
ipfw_tables[tbl].modified = 1;
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
flush_tables(void)
|
||||
{
|
||||
u_int16_t tbl;
|
||||
|
||||
for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++)
|
||||
flush_table(tbl);
|
||||
}
|
||||
|
||||
static int
|
||||
lookup_table(u_int16_t tbl, in_addr_t addr, u_int32_t *val)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
struct table_entry *ent;
|
||||
struct sockaddr_in sa;
|
||||
static in_addr_t last_addr;
|
||||
static int last_tbl;
|
||||
static int last_match;
|
||||
static u_int32_t last_value;
|
||||
|
||||
if (tbl >= IPFW_TABLES_MAX)
|
||||
return (0);
|
||||
if (tbl == last_tbl && addr == last_addr &&
|
||||
!ipfw_tables[tbl].modified) {
|
||||
if (last_match)
|
||||
*val = last_value;
|
||||
return (last_match);
|
||||
}
|
||||
rnh = ipfw_tables[tbl].rnh;
|
||||
sa.sin_len = 8;
|
||||
sa.sin_addr.s_addr = addr;
|
||||
RADIX_NODE_HEAD_LOCK(rnh);
|
||||
ipfw_tables[tbl].modified = 0;
|
||||
ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
last_addr = addr;
|
||||
last_tbl = tbl;
|
||||
if (ent != NULL) {
|
||||
last_value = *val = ent->value;
|
||||
last_match = 1;
|
||||
return (1);
|
||||
}
|
||||
last_match = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
count_table_entry(struct radix_node *rn, void *arg)
|
||||
{
|
||||
u_int32_t * const cnt = arg;
|
||||
|
||||
(*cnt)++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
count_table(u_int32_t tbl, u_int32_t *cnt)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
|
||||
if (tbl >= IPFW_TABLES_MAX)
|
||||
return (EINVAL);
|
||||
rnh = ipfw_tables[tbl].rnh;
|
||||
*cnt = 0;
|
||||
RADIX_NODE_HEAD_LOCK(rnh);
|
||||
rnh->rnh_walktree(rnh, count_table_entry, cnt);
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
dump_table_entry(struct radix_node *rn, void *arg)
|
||||
{
|
||||
struct table_entry * const n = (struct table_entry *)rn;
|
||||
ipfw_table * const tbl = arg;
|
||||
ipfw_table_entry *ent;
|
||||
|
||||
if (tbl->cnt == tbl->size)
|
||||
return (1);
|
||||
ent = &tbl->ent[tbl->cnt];
|
||||
ent->tbl = tbl->tbl;
|
||||
if (in_nullhost(n->mask.sin_addr))
|
||||
ent->masklen = 0;
|
||||
else
|
||||
ent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
|
||||
ent->addr = n->addr.sin_addr.s_addr;
|
||||
ent->value = n->value;
|
||||
tbl->cnt++;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
dump_table(ipfw_table *tbl)
|
||||
{
|
||||
struct radix_node_head *rnh;
|
||||
|
||||
if (tbl->tbl >= IPFW_TABLES_MAX)
|
||||
return (EINVAL);
|
||||
rnh = ipfw_tables[tbl->tbl].rnh;
|
||||
tbl->cnt = 0;
|
||||
RADIX_NODE_HEAD_LOCK(rnh);
|
||||
rnh->rnh_walktree(rnh, dump_table_entry, tbl);
|
||||
RADIX_NODE_HEAD_UNLOCK(rnh);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
check_uidgid(ipfw_insn_u32 *insn,
|
||||
int proto, struct ifnet *oif,
|
||||
@ -1751,6 +1963,23 @@ ipfw_chk(struct ip_fw_args *args)
|
||||
src_ip.s_addr);
|
||||
break;
|
||||
|
||||
case O_IP_SRC_LOOKUP:
|
||||
case O_IP_DST_LOOKUP:
|
||||
if (hlen > 0) {
|
||||
uint32_t a =
|
||||
(cmd->opcode == O_IP_DST_LOOKUP) ?
|
||||
dst_ip.s_addr : src_ip.s_addr;
|
||||
uint32_t v;
|
||||
|
||||
match = lookup_table(cmd->arg1, a, &v);
|
||||
if (!match)
|
||||
break;
|
||||
if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
|
||||
match =
|
||||
((ipfw_insn_u32 *)cmd)->d[0] == v;
|
||||
}
|
||||
break;
|
||||
|
||||
case O_IP_SRC_MASK:
|
||||
case O_IP_DST_MASK:
|
||||
if (hlen > 0) {
|
||||
@ -2621,6 +2850,18 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
||||
goto bad_size;
|
||||
break;
|
||||
|
||||
case O_IP_SRC_LOOKUP:
|
||||
case O_IP_DST_LOOKUP:
|
||||
if (cmd->arg1 >= IPFW_TABLES_MAX) {
|
||||
printf("ipfw: invalid table number %d\n",
|
||||
cmd->arg1);
|
||||
return (EINVAL);
|
||||
}
|
||||
if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
|
||||
cmdlen != F_INSN_SIZE(ipfw_insn_u32))
|
||||
goto bad_size;
|
||||
break;
|
||||
|
||||
case O_MACADDR2:
|
||||
if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
|
||||
goto bad_size;
|
||||
@ -2908,6 +3149,87 @@ ipfw_ctl(struct sockopt *sopt)
|
||||
sopt->sopt_name == IP_FW_RESETLOG);
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_ADD:
|
||||
{
|
||||
ipfw_table_entry ent;
|
||||
|
||||
error = sooptcopyin(sopt, &ent,
|
||||
sizeof(ent), sizeof(ent));
|
||||
if (error)
|
||||
break;
|
||||
error = add_table_entry(ent.tbl, ent.addr,
|
||||
ent.masklen, ent.value);
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_DEL:
|
||||
{
|
||||
ipfw_table_entry ent;
|
||||
|
||||
error = sooptcopyin(sopt, &ent,
|
||||
sizeof(ent), sizeof(ent));
|
||||
if (error)
|
||||
break;
|
||||
error = del_table_entry(ent.tbl, ent.addr, ent.masklen);
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_FLUSH:
|
||||
{
|
||||
u_int16_t tbl;
|
||||
|
||||
error = sooptcopyin(sopt, &tbl,
|
||||
sizeof(tbl), sizeof(tbl));
|
||||
if (error)
|
||||
break;
|
||||
error = flush_table(tbl);
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_GETSIZE:
|
||||
{
|
||||
u_int32_t tbl, cnt;
|
||||
|
||||
if ((error = sooptcopyin(sopt, &tbl, sizeof(tbl),
|
||||
sizeof(tbl))))
|
||||
break;
|
||||
if ((error = count_table(tbl, &cnt)))
|
||||
break;
|
||||
error = sooptcopyout(sopt, &cnt, sizeof(cnt));
|
||||
}
|
||||
break;
|
||||
|
||||
case IP_FW_TABLE_LIST:
|
||||
{
|
||||
ipfw_table *tbl;
|
||||
|
||||
if (sopt->sopt_valsize < sizeof(*tbl)) {
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
size = sopt->sopt_valsize;
|
||||
tbl = malloc(size, M_TEMP, M_WAITOK);
|
||||
if (tbl == NULL) {
|
||||
error = ENOMEM;
|
||||
break;
|
||||
}
|
||||
error = sooptcopyin(sopt, tbl, size, sizeof(*tbl));
|
||||
if (error) {
|
||||
free(tbl, M_TEMP);
|
||||
break;
|
||||
}
|
||||
tbl->size = (size - sizeof(*tbl)) /
|
||||
sizeof(ipfw_table_entry);
|
||||
error = dump_table(tbl);
|
||||
if (error) {
|
||||
free(tbl, M_TEMP);
|
||||
break;
|
||||
}
|
||||
error = sooptcopyout(sopt, tbl, size);
|
||||
free(tbl, M_TEMP);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
|
||||
error = EINVAL;
|
||||
@ -2972,6 +3294,7 @@ ipfw_init(void)
|
||||
layer3_chain.rules = NULL;
|
||||
IPFW_LOCK_INIT(&layer3_chain);
|
||||
IPFW_DYN_LOCK_INIT();
|
||||
init_tables();
|
||||
callout_init(&ipfw_timeout, debug_mpsafenet ? CALLOUT_MPSAFE : 0);
|
||||
|
||||
bzero(&default_rule, sizeof default_rule);
|
||||
@ -3043,7 +3366,7 @@ ipfw_destroy(void)
|
||||
IPFW_UNLOCK(&layer3_chain);
|
||||
if (reap != NULL)
|
||||
reap_rules(reap);
|
||||
|
||||
flush_tables();
|
||||
IPFW_DYN_LOCK_DESTROY();
|
||||
IPFW_LOCK_DESTROY(&layer3_chain);
|
||||
printf("IP firewall unloaded\n");
|
||||
|
@ -357,6 +357,8 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
|
||||
|
||||
case IP_FW_ADD: /* ADD actually returns the body... */
|
||||
case IP_FW_GET:
|
||||
case IP_FW_TABLE_GETSIZE:
|
||||
case IP_FW_TABLE_LIST:
|
||||
if (IPFW_LOADED)
|
||||
error = ip_fw_ctl_ptr(sopt);
|
||||
else
|
||||
@ -410,6 +412,9 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
|
||||
case IP_FW_FLUSH:
|
||||
case IP_FW_ZERO:
|
||||
case IP_FW_RESETLOG:
|
||||
case IP_FW_TABLE_ADD:
|
||||
case IP_FW_TABLE_DEL:
|
||||
case IP_FW_TABLE_FLUSH:
|
||||
if (IPFW_LOADED)
|
||||
error = ip_fw_ctl_ptr(sopt);
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user