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:
ru 2004-06-09 20:10:38 +00:00
parent e6a8fb50df
commit 27bed143c8
6 changed files with 523 additions and 5 deletions

View File

@ -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

View File

@ -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)))

View File

@ -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 */

View File

@ -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.
*/

View File

@ -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");

View File

@ -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