summaryrefslogtreecommitdiffstats
path: root/sbin/ipfw
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2012-03-12 14:07:57 +0000
committermelifaro <melifaro@FreeBSD.org>2012-03-12 14:07:57 +0000
commitc614ff641f951a75a93d083b1980b4bd3480b949 (patch)
treebc4e5f2db723245d6f66a54bec8c2de91cede71d /sbin/ipfw
parent4861f7911395459349e837d48f6d239d6c1e80d7 (diff)
downloadFreeBSD-src-c614ff641f951a75a93d083b1980b4bd3480b949.zip
FreeBSD-src-c614ff641f951a75a93d083b1980b4bd3480b949.tar.gz
- Add ipfw eXtended tables permitting radix to be used for any kind of keys.
- Add support for IPv6 and interface extended tables - Make number of tables to be loader tunable in range 0..65534. - Use IP_FW3 opcode for all new extended table cmds No ABI changes are introduced. Old userland will see valid tables for IPv4 tables and no entries otherwise. Flush works for any table. IP_FW3 socket option is used to encapsulate all new opcodes: /* IP_FW3 header/opcodes */ typedef struct _ip_fw3_opheader { uint16_t opcode; /* Operation opcode */ uint16_t reserved[3]; /* Align to 64-bit boundary */ } ip_fw3_opheader; New opcodes added: IP_FW_TABLE_XADD, IP_FW_TABLE_XDEL, IP_FW_TABLE_XGETSIZE, IP_FW_TABLE_XLIST ipfw(8) table argument parsing behavior is changed: 'ipfw table 999 add host' now assumes 'host' to be interface name instead of hostname. New tunable: net.inet.ip.fw.tables_max controls number of table supported by ipfw in given VNET instance. 128 is still the default value. New syntax: ipfw add skipto tablearg ip from any to any via table(42) in ipfw add skipto tablearg ip from any to any via table(4242) out This is a bit hackish, special interface name '\1' is used to signal interface table number is passed in p.glob field. Sponsored by Yandex LLC Reviewed by: ae Approved by: ae (mentor) MFC after: 4 weeks
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/ipfw.844
-rw-r--r--sbin/ipfw/ipfw2.c268
2 files changed, 232 insertions, 80 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 6293d73..dba23f4 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd February 6, 2012
+.Dd March 9, 2012
.Dt IPFW 8
.Os
.Sh NAME
@@ -1539,7 +1539,7 @@ and they are always printed as hexadecimal (unless the
option is used, in which case symbolic resolution will be attempted).
.It Cm proto Ar protocol
Matches packets with the corresponding IP protocol.
-.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar ipno | Ar any
+.It Cm recv | xmit | via Brq Ar ifX | Ar if Ns Cm * | Ar table Ns Pq Ar number Ns Op , Ns Ar value | Ar ipno | Ar any
Matches packets received, transmitted or going through,
respectively, the interface specified by exact name
.Ns No ( Ar ifX Ns No ),
@@ -1738,22 +1738,21 @@ connected networks instead of all source addresses.
.El
.Sh LOOKUP TABLES
Lookup tables are useful to handle large sparse sets of
-addresses or other search keys (e.g. ports, jail IDs).
-In the rest of this section we will use the term ``address''
-to mean any unsigned value of up to 32-bit.
-There may be up to 128 different lookup tables, numbered 0 to 127.
+addresses or other search keys (e.g. ports, jail IDs, interface names).
+In the rest of this section we will use the term ``address''.
+There may be up to 4096 different lookup tables, numbered 0 to 4095.
.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 an IP address, a hostname or an unsigned integer)
+(specified as an IPv4/IPv6 address, a hostname or an unsigned integer)
and mask width of
.Ar masklen
bits.
If
.Ar masklen
-is not specified, it defaults to 32.
+is not specified, it defaults to 32 for IPv4 and 128 for IPv6.
When looking up an IP address in a table, the most specific
entry will match.
Associated with each entry is a 32-bit unsigned
@@ -1776,7 +1775,8 @@ Internally, each table is stored in a Radix tree, the same way as
the routing table (see
.Xr route 4 ) .
.Pp
-Lookup tables currently support only ports, jail IDs and IPv4 addresses.
+Lookup tables currently support only ports, jail IDs, IPv4/IPv6 addresses
+and interface names. Wildcards is not supported for interface names.
.Pp
The
.Cm tablearg
@@ -2579,6 +2579,22 @@ instances.
See
.Sx SYSCTL VARIABLES
for more info.
+.Sh LOADER TUNABLES
+Tunables can be set in
+.Xr loader 8
+prompt,
+.Xr loader.conf 5
+or
+.Xr kenv 1
+before ipfw module gets loaded.
+.Bl -tag -width indent
+.It Va net.inet.ip.fw.default_to_accept: No 0
+Defines ipfw last rule behavior. This value overrides
+.Cd "options IPFW_DEFAULT_TO_(ACCEPT|DENY)"
+from kernel configuration file.
+.It Va net.inet.ip.fw.tables_max: No 128
+Defines number of tables available in ipfw. Number cannot exceed 65534.
+.El
.Sh SYSCTL VARIABLES
A set of
.Xr sysctl 8
@@ -3112,6 +3128,16 @@ action, the table entries may include hostnames and IP addresses.
.Dl "ipfw table 1 add 192.168.0.0/27 router1.dmz"
.Dl "..."
.Dl "ipfw add 100 fwd tablearg ip from any to table(1)"
+.Pp
+In the following example per-interface firewall is created:
+.Pp
+.Dl "ipfw table 10 add vlan20 12000"
+.Dl "ipfw table 10 add vlan30 13000"
+.Dl "ipfw table 20 add vlan20 22000"
+.Dl "ipfw table 20 add vlan30 23000"
+.Dl ".."
+.Dl "ipfw add 100 ipfw skipto tablearg ip from any to any recv 'table(10)' in"
+.Dl "ipfw add 200 ipfw skipto tablearg ip from any to any xmit 'table(10)' out"
.Ss SETS OF RULES
To add a set of rules atomically, e.g.\& set 18:
.Pp
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index ac0632e..41f7be3 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -42,6 +42,7 @@
#include <timeconv.h> /* _long_to_time */
#include <unistd.h>
#include <fcntl.h>
+#include <stddef.h> /* offsetof */
#include <net/ethernet.h>
#include <net/if.h> /* only IFNAMSIZ */
@@ -57,6 +58,12 @@ struct cmdline_opts co; /* global options */
int resvd_set_number = RESVD_SET;
+int ipfw_socket = -1;
+
+#ifndef s6_addr32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
#define GET_UINT_ARG(arg, min, max, tok, s_x) do { \
if (!av[0]) \
errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \
@@ -370,33 +377,65 @@ safe_realloc(void *ptr, size_t size)
int
do_cmd(int optname, void *optval, uintptr_t optlen)
{
- static int s = -1; /* the socket */
int i;
if (co.test_only)
return 0;
- if (s == -1)
- s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
- if (s < 0)
+ if (ipfw_socket == -1)
+ ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (ipfw_socket < 0)
err(EX_UNAVAILABLE, "socket");
if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
- optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST ||
- optname == IP_FW_TABLE_GETSIZE ||
+ optname == IP_FW_ADD || optname == IP_FW3 ||
optname == IP_FW_NAT_GET_CONFIG ||
optname < 0 ||
optname == IP_FW_NAT_GET_LOG) {
if (optname < 0)
optname = -optname;
- i = getsockopt(s, IPPROTO_IP, optname, optval,
+ i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval,
(socklen_t *)optlen);
} else {
- i = setsockopt(s, IPPROTO_IP, optname, optval, optlen);
+ i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen);
}
return i;
}
+/*
+ * do_setcmd3 - pass ipfw control cmd to kernel
+ * @optname: option name
+ * @optval: pointer to option data
+ * @optlen: option length
+ *
+ * Function encapsulates option value in IP_FW3 socket option
+ * and calls setsockopt().
+ * Function returns 0 on success or -1 otherwise.
+ */
+int
+do_setcmd3(int optname, void *optval, socklen_t optlen)
+{
+ socklen_t len;
+ ip_fw3_opheader *op3;
+
+ 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");
+
+ len = sizeof(ip_fw3_opheader) + optlen;
+ op3 = alloca(len);
+ /* Zero reserved fields */
+ memset(op3, 0, sizeof(ip_fw3_opheader));
+ memcpy(op3 + 1, optval, optlen);
+ op3->opcode = optname;
+
+ return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len);
+}
+
/**
* match_token takes a table and a string, returns the value associated
* with the string (-1 in case of failure).
@@ -1411,6 +1450,8 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
if (cmdif->name[0] == '\0')
printf(" %s %s", s,
inet_ntoa(cmdif->p.ip));
+ else if (cmdif->name[0] == '\1') /* interface table */
+ printf(" %s table(%d)", s, cmdif->p.glob);
else
printf(" %s %s", s, cmdif->name);
@@ -2332,7 +2373,13 @@ fill_iface(ipfw_insn_if *cmd, char *arg)
/* Parse the interface or address */
if (strcmp(arg, "any") == 0)
cmd->o.len = 0; /* effectively ignore this command */
- else if (!isdigit(*arg)) {
+ else if (strncmp(arg, "table(", 6) == 0) {
+ char *p = strchr(arg + 6, ',');
+ if (p)
+ *p++ = '\0';
+ cmd->name[0] = '\1'; /* Special value indicating table */
+ cmd->p.glob = strtoul(arg + 6, NULL, 0);
+ } else if (!isdigit(*arg)) {
strlcpy(cmd->name, arg, sizeof(cmd->name));
cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0;
} else if (!inet_aton(arg, &cmd->p.ip))
@@ -3863,7 +3910,7 @@ ipfw_flush(int force)
}
-static void table_list(ipfw_table_entry ent, int need_header);
+static void table_list(uint16_t num, int need_header);
/*
* This one handles all table-related commands
@@ -3875,12 +3922,12 @@ static void table_list(ipfw_table_entry ent, int need_header);
void
ipfw_table_handler(int ac, char *av[])
{
- ipfw_table_entry ent;
+ ipfw_table_xentry xent;
int do_add;
int is_all;
size_t len;
char *p;
- uint32_t a;
+ uint32_t a, type, mask, addrlen;
uint32_t tables_max;
len = sizeof(tables_max);
@@ -3895,18 +3942,20 @@ ipfw_table_handler(int ac, char *av[])
#endif
}
+ memset(&xent, 0, sizeof(xent));
+
ac--; av++;
if (ac && isdigit(**av)) {
- ent.tbl = atoi(*av);
+ xent.tbl = atoi(*av);
is_all = 0;
ac--; av++;
} else if (ac && _substrcmp(*av, "all") == 0) {
- ent.tbl = 0;
+ xent.tbl = 0;
is_all = 1;
ac--; av++;
} else
errx(EX_USAGE, "table number or 'all' keyword required");
- if (ent.tbl >= tables_max)
+ if (xent.tbl >= tables_max)
errx(EX_USAGE, "The table number exceeds the maximum allowed "
"value (%d)", tables_max - 1);
NEED1("table needs command");
@@ -3919,104 +3968,181 @@ ipfw_table_handler(int ac, char *av[])
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);
+ errx(EX_USAGE, "address required");
+ /*
+ * Let's try to guess type by agrument.
+ * Possible types:
+ * 1) IPv4[/mask]
+ * 2) IPv6[/mask]
+ * 3) interface name
+ * 4) port ?
+ */
+ type = 0;
+ if (ishexnumber(*av[0])) {
+ /* Remove / if exists */
+ if ((p = strchr(*av, '/')) != NULL) {
+ *p = '\0';
+ mask = atoi(p + 1);
+ }
+
+ if (inet_pton(AF_INET, *av, &xent.k.addr6) == 1) {
+ type = IPFW_TABLE_CIDR;
+ if ((p != NULL) && (mask > 32))
+ errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1);
+ xent.masklen = p ? mask : 32;
+ addrlen = sizeof(struct in_addr);
+ } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) == 1) {
+ type = IPFW_TABLE_CIDR;
+ if ((p != NULL) && (mask > 128))
+ errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1);
+ xent.masklen = p ? mask : 128;
+ addrlen = sizeof(struct in6_addr);
+ }
+ }
+
+ if ((type == 0) && (strchr(*av, '.') == NULL)) {
+ /* Assume interface name. Copy significant data only */
+ mask = MIN(strlen(*av), IF_NAMESIZE - 1);
+ memcpy(xent.k.iface, *av, mask);
+ /* Set mask to exact match */
+ xent.masklen = 8 * IF_NAMESIZE;
+ type = IPFW_TABLE_INTERFACE;
+ addrlen = IF_NAMESIZE;
+ }
+
+ if (type == 0) {
+ if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+ xent.masklen = 32;
+ type = IPFW_TABLE_CIDR;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ xent.type = type;
+ xent.len = offsetof(ipfw_table_xentry, k) + addrlen;
+
ac--; av++;
if (do_add && ac) {
unsigned int tval;
/* isdigit is a bit of a hack here.. */
if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
- ent.value = strtoul(*av, NULL, 0);
+ 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 */
- ent.value = ntohl(tval);
+ xent.value = ntohl(tval);
} else {
errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
}
}
} else
- ent.value = 0;
- if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL,
- &ent, sizeof(ent)) < 0) {
+ 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 ? "ADD" : "DEL");
+ do_add ? "XADD" : "XDEL");
/* In silent mode, react to a failed add by deleting */
if (do_add) {
- do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent));
- if (do_cmd(IP_FW_TABLE_ADD,
- &ent, sizeof(ent)) < 0)
+ 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_ADD)");
+ "setsockopt(IP_FW_TABLE_XADD)");
}
}
} else if (_substrcmp(*av, "flush") == 0) {
- a = is_all ? tables_max : (uint32_t)(ent.tbl + 1);
+ a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
do {
- if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl,
- sizeof(ent.tbl)) < 0)
+ if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl,
+ sizeof(xent.tbl)) < 0)
err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
- } while (++ent.tbl < a);
+ } while (++xent.tbl < a);
} else if (_substrcmp(*av, "list") == 0) {
- a = is_all ? tables_max : (uint32_t)(ent.tbl + 1);
+ a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
do {
- table_list(ent, is_all);
- } while (++ent.tbl < a);
+ table_list(xent.tbl, is_all);
+ } while (++xent.tbl < a);
} else
errx(EX_USAGE, "invalid table command %s", *av);
}
static void
-table_list(ipfw_table_entry ent, int need_header)
+table_list(uint16_t num, int need_header)
{
- ipfw_table *tbl;
+ ipfw_xtable *tbl;
+ ipfw_table_xentry *xent;
socklen_t l;
- uint32_t a;
-
- 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)");
+ 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)
+ if (*a == 0)
return;
- l = sizeof(*tbl) + a * sizeof(ipfw_table_entry);
+ l = *a;
tbl = safe_calloc(1, l);
- tbl->tbl = ent.tbl;
- if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0)
- err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)");
+ 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);
- for (a = 0; a < tbl->cnt; a++) {
- unsigned int tval;
- tval = tbl->ent[a].value;
- if (co.do_value_as_ip) {
- char tbuf[128];
- strncpy(tbuf, inet_ntoa(*(struct in_addr *)
- &tbl->ent[a].addr), 127);
- /* inet_ntoa expects network order */
- tval = htonl(tval);
- printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen,
- inet_ntoa(*(struct in_addr *)&tval));
- } else {
- printf("%s/%u %u\n",
- inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr),
- tbl->ent[a].masklen, tval);
+ 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 ((addr6->s6_addr32[0] == 0) && (addr6->s6_addr32[1] == 0) &&
+ (addr6->s6_addr32[2] == 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 = (void *)xent + xent->len;
}
+
free(tbl);
}
OpenPOWER on IntegriCloud