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
cf4572847a
commit
cd8b5ae0ae
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=130281
@ -1,7 +1,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd December 1, 2003
|
.Dd June 9, 2004
|
||||||
.Dt IPFW 8
|
.Dt IPFW 8
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -43,6 +43,15 @@
|
|||||||
.Cm set show
|
.Cm set show
|
||||||
.Pp
|
.Pp
|
||||||
.Nm
|
.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
|
.Brq Cm pipe | queue
|
||||||
.Ar number
|
.Ar number
|
||||||
.Cm config
|
.Cm config
|
||||||
@ -758,13 +767,26 @@ The second format (
|
|||||||
.Em or-block
|
.Em or-block
|
||||||
with multiple addresses) is provided for convenience only and
|
with multiple addresses) is provided for convenience only and
|
||||||
its use is discouraged.
|
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
|
.It Cm any
|
||||||
matches any IP address.
|
matches any IP address.
|
||||||
.It Cm me
|
.It Cm me
|
||||||
matches any IP address configured on an interface in the system.
|
matches any IP address configured on an interface in the system.
|
||||||
The address list is evaluated at the time the packet is
|
The address list is evaluated at the time the packet is
|
||||||
analysed.
|
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 addr-list : ip-addr Ns Op Ns , Ns Ar addr-list
|
||||||
.It Ar ip-addr :
|
.It Ar ip-addr :
|
||||||
A host or subnet address specified in one of the following ways:
|
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
|
This option can be used to make anti-spoofing rules to reject all
|
||||||
packets whose source address is unreachable.
|
packets whose source address is unreachable.
|
||||||
.El
|
.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
|
.Sh SETS OF RULES
|
||||||
Each rule belongs to one of 32 different
|
Each rule belongs to one of 32 different
|
||||||
.Em sets
|
.Em sets
|
||||||
|
@ -385,7 +385,8 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
|
|||||||
err(EX_UNAVAILABLE, "socket");
|
err(EX_UNAVAILABLE, "socket");
|
||||||
|
|
||||||
if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
|
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,
|
i = getsockopt(s, IPPROTO_IP, optname, optval,
|
||||||
(socklen_t *)optlen);
|
(socklen_t *)optlen);
|
||||||
else
|
else
|
||||||
@ -714,6 +715,14 @@ print_ip(ipfw_insn_ip *cmd, char const *s)
|
|||||||
printf("me");
|
printf("me");
|
||||||
return;
|
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) {
|
if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) {
|
||||||
uint32_t x, *map = (uint32_t *)&(cmd->mask);
|
uint32_t x, *map = (uint32_t *)&(cmd->mask);
|
||||||
int i, j;
|
int i, j;
|
||||||
@ -1088,6 +1097,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case O_IP_SRC:
|
case O_IP_SRC:
|
||||||
|
case O_IP_SRC_LOOKUP:
|
||||||
case O_IP_SRC_MASK:
|
case O_IP_SRC_MASK:
|
||||||
case O_IP_SRC_ME:
|
case O_IP_SRC_ME:
|
||||||
case O_IP_SRC_SET:
|
case O_IP_SRC_SET:
|
||||||
@ -1102,6 +1112,7 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case O_IP_DST:
|
case O_IP_DST:
|
||||||
|
case O_IP_DST_LOOKUP:
|
||||||
case O_IP_DST_MASK:
|
case O_IP_DST_MASK:
|
||||||
case O_IP_DST_ME:
|
case O_IP_DST_ME:
|
||||||
case O_IP_DST_SET:
|
case O_IP_DST_SET:
|
||||||
@ -1870,13 +1881,14 @@ help(void)
|
|||||||
"{pipe|queue} N config PIPE-BODY\n"
|
"{pipe|queue} N config PIPE-BODY\n"
|
||||||
"[pipe|queue] {zero|delete|show} [N{,N}]\n"
|
"[pipe|queue] {zero|delete|show} [N{,N}]\n"
|
||||||
"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\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"
|
"\n"
|
||||||
"RULE-BODY: check-state [LOG] | ACTION [LOG] ADDR [OPTION_LIST]\n"
|
"RULE-BODY: check-state [LOG] | ACTION [LOG] ADDR [OPTION_LIST]\n"
|
||||||
"ACTION: check-state | allow | count | deny | reject | skipto N |\n"
|
"ACTION: check-state | allow | count | deny | reject | skipto N |\n"
|
||||||
" {divert|tee} PORT | forward ADDR | pipe N | queue N\n"
|
" {divert|tee} PORT | forward ADDR | pipe N | queue N\n"
|
||||||
"ADDR: [ MAC dst src ether_type ] \n"
|
"ADDR: [ MAC dst src ether_type ] \n"
|
||||||
" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\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"
|
"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n"
|
||||||
"OPTION_LIST: OPTION [OPTION_LIST]\n"
|
"OPTION_LIST: OPTION [OPTION_LIST]\n"
|
||||||
"OPTION: bridged | {dst-ip|src-ip} ADDR | {dst-port|src-port} 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;
|
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) {
|
while (av) {
|
||||||
/*
|
/*
|
||||||
* After the address we can have '/' or ':' indicating a mask,
|
* 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);
|
fill_ip((ipfw_insn_ip *)cmd, av);
|
||||||
if (cmd->opcode == O_IP_DST_SET) /* set */
|
if (cmd->opcode == O_IP_DST_SET) /* set */
|
||||||
cmd->opcode = O_IP_SRC_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 */
|
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
|
||||||
cmd->opcode = O_IP_SRC_ME;
|
cmd->opcode = O_IP_SRC_ME;
|
||||||
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
|
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);
|
fill_ip((ipfw_insn_ip *)cmd, av);
|
||||||
if (cmd->opcode == O_IP_DST_SET) /* set */
|
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 */
|
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */
|
||||||
cmd->opcode = O_IP_DST_ME;
|
cmd->opcode = O_IP_DST_ME;
|
||||||
else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */
|
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);
|
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).
|
* Called with the arguments (excluding program name).
|
||||||
* Returns 0 if successful, 1 if empty command, errx() in case of errors.
|
* 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);
|
list(ac, av, do_acct);
|
||||||
else if (!strncmp(*av, "set", strlen(*av)))
|
else if (!strncmp(*av, "set", strlen(*av)))
|
||||||
sets_handler(ac, av);
|
sets_handler(ac, av);
|
||||||
|
else if (!strncmp(*av, "table", strlen(*av)))
|
||||||
|
table_handler(ac, av);
|
||||||
else if (!strncmp(*av, "enable", strlen(*av)))
|
else if (!strncmp(*av, "enable", strlen(*av)))
|
||||||
sysctl_handler(ac, av, 1);
|
sysctl_handler(ac, av, 1);
|
||||||
else if (!strncmp(*av, "disable", strlen(*av)))
|
else if (!strncmp(*av, "disable", strlen(*av)))
|
||||||
|
@ -386,6 +386,12 @@ __END_DECLS
|
|||||||
|
|
||||||
#define IP_ONESBCAST 23 /* bool: send all-ones broadcast */
|
#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_ADD 50 /* add a firewall rule to chain */
|
||||||
#define IP_FW_DEL 51 /* delete a firewall rule from chain */
|
#define IP_FW_DEL 51 /* delete a firewall rule from chain */
|
||||||
#define IP_FW_FLUSH 52 /* flush firewall rule chain */
|
#define IP_FW_FLUSH 52 /* flush firewall rule chain */
|
||||||
|
@ -126,6 +126,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
|
|||||||
* More opcodes.
|
* More opcodes.
|
||||||
*/
|
*/
|
||||||
O_IPSEC, /* has ipsec history */
|
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! */
|
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) */
|
#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.
|
* Main firewall chains definitions and global var's definitions.
|
||||||
*/
|
*/
|
||||||
|
@ -58,6 +58,7 @@
|
|||||||
#include <sys/syslog.h>
|
#include <sys/syslog.h>
|
||||||
#include <sys/ucred.h>
|
#include <sys/ucred.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
#include <net/radix.h>
|
||||||
#include <net/route.h>
|
#include <net/route.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/in_systm.h>
|
#include <netinet/in_systm.h>
|
||||||
@ -131,6 +132,19 @@ struct ip_fw_chain {
|
|||||||
static struct ip_fw_chain layer3_chain;
|
static struct ip_fw_chain layer3_chain;
|
||||||
|
|
||||||
MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
|
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 fw_debug = 1;
|
||||||
static int autoinc_step = 100; /* bounded to 1..1000 in add_rule() */
|
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;
|
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
|
static int
|
||||||
check_uidgid(ipfw_insn_u32 *insn,
|
check_uidgid(ipfw_insn_u32 *insn,
|
||||||
int proto, struct ifnet *oif,
|
int proto, struct ifnet *oif,
|
||||||
@ -1751,6 +1963,23 @@ ipfw_chk(struct ip_fw_args *args)
|
|||||||
src_ip.s_addr);
|
src_ip.s_addr);
|
||||||
break;
|
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_SRC_MASK:
|
||||||
case O_IP_DST_MASK:
|
case O_IP_DST_MASK:
|
||||||
if (hlen > 0) {
|
if (hlen > 0) {
|
||||||
@ -2621,6 +2850,18 @@ check_ipfw_struct(struct ip_fw *rule, int size)
|
|||||||
goto bad_size;
|
goto bad_size;
|
||||||
break;
|
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:
|
case O_MACADDR2:
|
||||||
if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
|
if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
|
||||||
goto bad_size;
|
goto bad_size;
|
||||||
@ -2908,6 +3149,87 @@ ipfw_ctl(struct sockopt *sopt)
|
|||||||
sopt->sopt_name == IP_FW_RESETLOG);
|
sopt->sopt_name == IP_FW_RESETLOG);
|
||||||
break;
|
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:
|
default:
|
||||||
printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
|
printf("ipfw: ipfw_ctl invalid option %d\n", sopt->sopt_name);
|
||||||
error = EINVAL;
|
error = EINVAL;
|
||||||
@ -2972,6 +3294,7 @@ ipfw_init(void)
|
|||||||
layer3_chain.rules = NULL;
|
layer3_chain.rules = NULL;
|
||||||
IPFW_LOCK_INIT(&layer3_chain);
|
IPFW_LOCK_INIT(&layer3_chain);
|
||||||
IPFW_DYN_LOCK_INIT();
|
IPFW_DYN_LOCK_INIT();
|
||||||
|
init_tables();
|
||||||
callout_init(&ipfw_timeout, debug_mpsafenet ? CALLOUT_MPSAFE : 0);
|
callout_init(&ipfw_timeout, debug_mpsafenet ? CALLOUT_MPSAFE : 0);
|
||||||
|
|
||||||
bzero(&default_rule, sizeof default_rule);
|
bzero(&default_rule, sizeof default_rule);
|
||||||
@ -3043,7 +3366,7 @@ ipfw_destroy(void)
|
|||||||
IPFW_UNLOCK(&layer3_chain);
|
IPFW_UNLOCK(&layer3_chain);
|
||||||
if (reap != NULL)
|
if (reap != NULL)
|
||||||
reap_rules(reap);
|
reap_rules(reap);
|
||||||
|
flush_tables();
|
||||||
IPFW_DYN_LOCK_DESTROY();
|
IPFW_DYN_LOCK_DESTROY();
|
||||||
IPFW_LOCK_DESTROY(&layer3_chain);
|
IPFW_LOCK_DESTROY(&layer3_chain);
|
||||||
printf("IP firewall unloaded\n");
|
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_ADD: /* ADD actually returns the body... */
|
||||||
case IP_FW_GET:
|
case IP_FW_GET:
|
||||||
|
case IP_FW_TABLE_GETSIZE:
|
||||||
|
case IP_FW_TABLE_LIST:
|
||||||
if (IPFW_LOADED)
|
if (IPFW_LOADED)
|
||||||
error = ip_fw_ctl_ptr(sopt);
|
error = ip_fw_ctl_ptr(sopt);
|
||||||
else
|
else
|
||||||
@ -410,6 +412,9 @@ rip_ctloutput(struct socket *so, struct sockopt *sopt)
|
|||||||
case IP_FW_FLUSH:
|
case IP_FW_FLUSH:
|
||||||
case IP_FW_ZERO:
|
case IP_FW_ZERO:
|
||||||
case IP_FW_RESETLOG:
|
case IP_FW_RESETLOG:
|
||||||
|
case IP_FW_TABLE_ADD:
|
||||||
|
case IP_FW_TABLE_DEL:
|
||||||
|
case IP_FW_TABLE_FLUSH:
|
||||||
if (IPFW_LOADED)
|
if (IPFW_LOADED)
|
||||||
error = ip_fw_ctl_ptr(sopt);
|
error = ip_fw_ctl_ptr(sopt);
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user