summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw.863
-rw-r--r--sbin/ipfw/ipfw2.c110
-rw-r--r--sys/netinet/in.h6
-rw-r--r--sys/netinet/ip_fw.h19
-rw-r--r--sys/netinet/ip_fw2.c325
-rw-r--r--sys/netinet/raw_ip.c5
6 files changed, 523 insertions, 5 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 4265686..1845c68 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -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
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index b2e4d10..69561af 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -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 */
@@ -3593,6 +3624,79 @@ free_args(int ac, char **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)))
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index eb71af6..95c2fa3 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -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 */
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 7258b6c..d66335a 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -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! */
};
@@ -376,6 +378,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.
*/
#ifdef _KERNEL
diff --git a/sys/netinet/ip_fw2.c b/sys/netinet/ip_fw2.c
index ddccd48..a62bab4 100644
--- a/sys/netinet/ip_fw2.c
+++ b/sys/netinet/ip_fw2.c
@@ -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 @@ check_body:
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");
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index 7cf1fea..85eaaf2 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -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
OpenPOWER on IntegriCloud