summaryrefslogtreecommitdiffstats
path: root/sbin/ipfw
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/Makefile2
-rw-r--r--sbin/ipfw/context.c148
-rw-r--r--sbin/ipfw/dummynet.c6
-rw-r--r--sbin/ipfw/ipfw2.c185
-rw-r--r--sbin/ipfw/ipfw2.h4
-rw-r--r--sbin/ipfw/main.c23
6 files changed, 327 insertions, 41 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index 6aea26b..e137e08 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -3,7 +3,7 @@
.include <bsd.own.mk>
PROG= ipfw
-SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c
+SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c context.c
WARNS?= 2
.if ${MK_PF} != "no"
diff --git a/sbin/ipfw/context.c b/sbin/ipfw/context.c
new file mode 100644
index 0000000..bce576e
--- /dev/null
+++ b/sbin/ipfw/context.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013 Ermal Lu‡i
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_fw.h>
+
+#include "ipfw2.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <string.h>
+#include <errno.h>
+#include <err.h>
+
+extern int ipfw_socket;
+
+int
+ipfw_context_handler(int ac, char **av)
+{
+ ip_fw3_opheader *op3;
+ int error = 0;
+ uint32_t action = 0;
+ socklen_t len, nlen;
+ char *ifname;
+
+ av++;
+ ac--;
+ NEED1("bad arguments, for usage summary ``ipfw''");
+
+ if (!strncmp(*av, "list", strlen(*av))) {
+ action = IP_FW_CTX_GET;
+ av++;
+ ac--;
+ if (ac > 0)
+ errx(EX_DATAERR, "list: does not take any extra arguments.");
+
+ } else {
+ co.ctx = atoi(*av);
+
+ av++;
+ ac--;
+ NEED1("bad arguments, for usage summary ``ipfw''");
+
+ if (!strncmp(*av, "create", strlen(*av)))
+ action = IP_FW_CTX_ADD;
+ else if (!strncmp(*av, "destroy", strlen(*av)))
+ action = IP_FW_CTX_DEL;
+ else {
+ if (!strncmp(*av, "madd", strlen(*av)))
+ action = IP_FW_CTX_ADDMEMBER;
+ else if (!strncmp(*av, "mdel", strlen(*av)))
+ action = IP_FW_CTX_DELMEMBER;
+ else
+ errx(EX_DATAERR, "Wrong parameters passed");
+
+ av++;
+ ac--;
+ NEED1("bad arguments, for usage summary ``ipfw''");
+
+ ifname = *av;
+ }
+
+ ac--;
+ if (ac > 0)
+ errx(EX_DATAERR, "context handling: Too many arguments passed");
+
+ }
+
+ if (co.test_only)
+ return (0);
+
+ if (ipfw_socket < 0)
+ ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ if (ipfw_socket < 0)
+ err(EX_UNAVAILABLE, "socket");
+
+ switch (action) {
+ case IP_FW_CTX_ADD:
+ case IP_FW_CTX_DEL:
+ case IP_FW_CTX_SET:
+ len = sizeof(ip_fw3_opheader);
+ op3 = alloca(len);
+ /* Zero reserved fields */
+ memset(op3, 0, sizeof(ip_fw3_opheader));
+ op3->opcode = action;
+ op3->ctxid = co.ctx;
+ error = setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len);
+ break;
+ case IP_FW_CTX_ADDMEMBER:
+ case IP_FW_CTX_DELMEMBER:
+ len = sizeof(ip_fw3_opheader) + strlen(ifname) + 1;
+ op3 = alloca(len);
+ /* Zero reserved fields */
+ memset(op3, 0, sizeof(ip_fw3_opheader) + strlen(ifname) + 1);
+ memcpy((op3 + 1), ifname, strlen(ifname));
+ op3->opcode = action;
+ op3->ctxid = co.ctx;
+ error = setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len);
+ break;
+ case IP_FW_CTX_GET:
+ len = sizeof(ip_fw3_opheader) + 1000;
+ nlen = len;
+ do {
+ if (nlen > len) {
+ len = nlen;
+ }
+ op3 = alloca(len);
+ /* Zero reserved fields */
+ memset(op3, 0, len);
+ op3->opcode = action;
+ op3->ctxid = co.ctx;
+ nlen = len;
+ error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &nlen);
+ } while (nlen > len && !error);
+
+ if (!error) {
+ if (nlen == 0)
+ printf("There are no contexts defined\n");
+ else
+ printf("Currently defined contexts and their members:\n%s\n", (char *)op3);
+ } else
+ err(EX_UNAVAILABLE, "Error returned: %s\n", strerror(error));
+
+ break;
+ }
+
+ return (error);
+}
diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c
index 28dc2c7..c5b5253 100644
--- a/sbin/ipfw/dummynet.c
+++ b/sbin/ipfw/dummynet.c
@@ -538,10 +538,10 @@ read_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen)
if_name[namelen] = '\0';
*bandwidth = 0;
} else { /* read bandwidth value */
- int bw;
+ double bw;
char *end = NULL;
- bw = strtoul(arg, &end, 0);
+ bw = strtod(arg, &end);
if (*end == 'K' || *end == 'k') {
end++;
bw *= 1000;
@@ -557,7 +557,7 @@ read_bandwidth(char *arg, int *bandwidth, char *if_name, int namelen)
if (bw < 0)
errx(EX_DATAERR, "bandwidth too large");
- *bandwidth = bw;
+ *bandwidth = (int)bw;
if (if_name)
if_name[0] = '\0';
}
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index f585ded..6d7d3d7 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -54,7 +54,7 @@
#include <netinet/tcp.h>
#include <arpa/inet.h>
-struct cmdline_opts co; /* global options */
+struct cmdline_opts co = { 0 }; /* global options */
int resvd_set_number = RESVD_SET;
@@ -422,6 +422,7 @@ safe_realloc(void *ptr, size_t size)
int
do_cmd(int optname, void *optval, uintptr_t optlen)
{
+ ip_fw3_opheader op3;
int i;
if (co.test_only)
@@ -432,6 +433,15 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
if (ipfw_socket < 0)
err(EX_UNAVAILABLE, "socket");
+ if (optname != IP_FW3 && optname != IP_DUMMYNET3 && optname != -IP_DUMMYNET3) {
+ memset(&op3, 0, sizeof op3);
+ op3.ctxid = co.ctx;
+ op3.opcode = IP_FW_CTX_SET;
+ i = setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, &op3, sizeof(op3));
+ if (i)
+ errx(EX_OSERR, "setsockopt: choosing context");
+ }
+
if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET ||
optname == IP_FW_ADD || optname == IP_FW3 ||
optname == IP_FW_NAT_GET_CONFIG ||
@@ -477,6 +487,7 @@ do_setcmd3(int optname, void *optval, socklen_t optlen)
memset(op3, 0, sizeof(ip_fw3_opheader));
memcpy(op3 + 1, optval, optlen);
op3->opcode = optname;
+ op3->ctxid = co.ctx;
return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len);
}
@@ -4122,8 +4133,9 @@ ipfw_flush(int force)
}
+static void table_list_entry(ipfw_table_xentry *);
static void table_list(uint16_t num, int need_header);
-static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
+static void table_fill_xentry(int ac, char *av[], ipfw_table_xentry *xent);
/*
* Retrieve maximum number of tables supported by ipfw(4) module.
@@ -4194,29 +4206,9 @@ ipfw_table_handler(int ac, char *av[])
if (_substrcmp(*av, "add") == 0 ||
_substrcmp(*av, "delete") == 0) {
do_add = **av == 'a';
- ac--; av++;
- if (!ac)
- errx(EX_USAGE, "address required");
- table_fill_xentry(*av, &xent);
+ table_fill_xentry(ac, av, &xent);
- ac--; av++;
- if (do_add && ac) {
- unsigned int tval;
- /* isdigit is a bit of a hack here.. */
- if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
- 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 */
- xent.value = ntohl(tval);
- } else {
- errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
- }
- }
- } else
- 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. */
@@ -4243,19 +4235,41 @@ ipfw_table_handler(int ac, char *av[])
do {
table_list(xent.tbl, is_all);
} while (++xent.tbl < a);
+ } else if (_substrcmp(*av, "entrystats") == 0) {
+ table_fill_xentry(ac, av, &xent);
+
+ if (do_setcmd3(IP_FW_TABLE_XLISTENTRY, &xent, xent.len) < 0) {
+ /* If running silent, don't bomb out on these errors. */
+ if (!(co.do_quiet))
+ err(EX_OSERR, "setsockopt(IP_FW_TABLE_XLISTENTRY)");
+ } else
+ table_list_entry(&xent);
+ } else if (_substrcmp(*av, "entryzerostats") == 0) {
+ table_fill_xentry(ac, av, &xent);
+
+ if (do_setcmd3(IP_FW_TABLE_XZEROENTRY, &xent, xent.len) < 0) {
+ /* If running silent, don't bomb out on these errors. */
+ if (!(co.do_quiet))
+ err(EX_OSERR, "setsockopt(IP_FW_TABLE_XZEROENTRY)");
+ }
} else
errx(EX_USAGE, "invalid table command %s", *av);
}
static void
-table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+table_fill_xentry(int ac, char *av[], ipfw_table_xentry *xent)
{
- int addrlen, mask, masklen, type;
+ int addrlen, mask, masklen, type, do_add;
struct in6_addr *paddr;
uint32_t *pkey;
- char *p;
+ char *p, *arg;
uint32_t key;
+ do_add = **av == 'a';
+ ac--; av++;
+ if (!ac)
+ errx(EX_USAGE, "address required");
+
mask = 0;
type = 0;
addrlen = 0;
@@ -4270,7 +4284,20 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent)
* 4) port, uid/gid or other u32 key (base 10 format)
* 5) hostname
*/
- paddr = &xent->k.addr6;
+ if (ac > 1 && av && _substrcmp(*av, "mac") == 0) {
+ uint8_t _mask[8];
+
+ type = IPFW_TABLE_MIX;
+ get_mac_addr_mask(av[1], (uint8_t *)xent->mac_addr, _mask);
+ ac-=2; av+=2;
+ if (ac <= 0)
+ errx(EX_DATAERR, "wrong argument passed.");
+
+ paddr = (struct in6_addr *)&xent->k.addr6;
+ } else
+ paddr = &xent->k.addr6;
+
+ arg = *av;
if (ishexnumber(*arg) != 0 || *arg == ':') {
/* Remove / if exists */
if ((p = strchr(arg, '/')) != NULL) {
@@ -4283,9 +4310,11 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent)
errx(EX_DATAERR, "bad IPv4 mask width: %s",
p + 1);
- type = IPFW_TABLE_CIDR;
+ if (type == 0)
+ type = IPFW_TABLE_CIDR;
masklen = p ? mask : 32;
addrlen = sizeof(struct in_addr);
+ xent->flags = IPFW_TCF_INET;
} else if (inet_pton(AF_INET6, arg, paddr) == 1) {
if (IN6_IS_ADDR_V4COMPAT(paddr))
errx(EX_DATAERR,
@@ -4294,10 +4323,14 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent)
errx(EX_DATAERR, "bad IPv6 mask width: %s",
p + 1);
- type = IPFW_TABLE_CIDR;
+ if (type == 0)
+ type = IPFW_TABLE_CIDR;
masklen = p ? mask : 128;
addrlen = sizeof(struct in6_addr);
} else {
+ if (type != 0 && type != IPFW_TABLE_MIX)
+ errx(EX_DATAERR, "Wrong value passed as address");
+
/* Port or any other key */
/* Skip non-base 10 entries like 'fa1' */
key = strtol(arg, &p, 10);
@@ -4338,11 +4371,88 @@ table_fill_xentry(char *arg, ipfw_table_xentry *xent)
masklen = 32;
type = IPFW_TABLE_CIDR;
addrlen = sizeof(struct in_addr);
+ xent->flags = IPFW_TCF_INET;
+ }
+
+ ac--; av++;
+ if (ac > 1 && av) {
+ if (_substrcmp(*av, "mac") == 0) {
+ uint8_t _mask[8];
+
+ if (type == 0)
+ type = IPFW_TABLE_CIDR;
+ get_mac_addr_mask(av[1], (uint8_t *)&xent->mac_addr, _mask);
+ ac-=2; av+=2;
+ }
}
+ if (do_add && ac > 0) {
+ unsigned int tval;
+ /* isdigit is a bit of a hack here.. */
+ if (strchr(*av, (int)'.') == NULL && isdigit(**av)) {
+ 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 */
+ xent->value = ntohl(tval);
+ } else {
+ errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
+ }
+ }
+ } else
+ xent->value = 0;
+
xent->type = type;
xent->masklen = masklen;
- xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+ if (type == IPFW_TABLE_MIX)
+ xent->len = offsetof(ipfw_table_xentry, k) + addrlen + ETHER_ADDR_LEN;
+ else
+ xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+}
+
+static void
+table_list_entry(ipfw_table_xentry *xent)
+{
+ struct in6_addr *addr6;
+ uint32_t tval;
+ char tbuf[128];
+
+ switch (xent->type) {
+ case IPFW_TABLE_CIDR:
+ /* IPv4 or IPv6 prefixes */
+ tval = xent->value;
+ addr6 = &xent->k.addr6;
+
+ if ((xent->flags & IPFW_TCF_INET) != 0) {
+ /* IPv4 address */
+ inet_ntop(AF_INET, (in_addr_t *)&addr6->s6_addr32[0], 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 %d %d %u\n", tbuf, xent->masklen,
+ inet_ntoa(*(struct in_addr *)&tval), pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp);
+ } else
+ printf("%s/%u %u %d %d %u\n", tbuf, xent->masklen, tval,
+ pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp);
+ break;
+ case IPFW_TABLE_INTERFACE:
+ /* Interface names */
+ tval = xent->value;
+ if (co.do_value_as_ip) {
+ tval = htonl(tval);
+ printf("%s %u %s %d %d %u\n", xent->k.iface, xent->masklen,
+ inet_ntoa(*(struct in_addr *)&tval), pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp);
+ } else
+ printf("%s %u %u %d %d %u\n", xent->k.iface, xent->masklen, tval,
+ pr_u64(&xent->packets, 0), pr_u64(&xent->bytes, 0), xent->timestamp);
+
+ break;
+ }
}
static void
@@ -4364,6 +4474,7 @@ table_list(uint16_t num, int need_header)
a = (uint32_t *)(op3 + 1);
*a = num;
op3->opcode = IP_FW_TABLE_XGETSIZE;
+ op3->ctxid = co.ctx;
if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0)
err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)");
@@ -4374,6 +4485,7 @@ table_list(uint16_t num, int need_header)
l = *a;
tbl = safe_calloc(1, l);
tbl->opheader.opcode = IP_FW_TABLE_XLIST;
+ tbl->opheader.ctxid = co.ctx;
tbl->tbl = num;
if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0)
err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)");
@@ -4388,21 +4500,24 @@ table_list(uint16_t num, int need_header)
tval = xent->value;
addr6 = &xent->k.addr6;
-
if ((xent->flags & IPFW_TCF_INET) != 0) {
/* IPv4 address */
- inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf));
+ inet_ntop(AF_INET, &addr6->s6_addr32[0], tbuf, sizeof(tbuf));
} else {
/* IPv6 address */
inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf));
}
+ printf("%s/%u", tbuf, xent->masklen);
+ if (xent->mac_addr)
+ printf(" mac %s", ether_ntoa((struct ether_addr *)&xent->mac_addr));
+
if (co.do_value_as_ip) {
tval = htonl(tval);
- printf("%s/%u %s\n", tbuf, xent->masklen,
+ printf(" %s\n",
inet_ntoa(*(struct in_addr *)&tval));
} else
- printf("%s/%u %u\n", tbuf, xent->masklen, tval);
+ printf(" %u\n", tval);
break;
case IPFW_TABLE_INTERFACE:
/* Interface names */
@@ -4413,6 +4528,8 @@ table_list(uint16_t num, int need_header)
inet_ntoa(*(struct in_addr *)&tval));
} else
printf("%s %u\n", xent->k.iface, tval);
+
+ break;
}
if (sz < xent->len)
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 6e895b8..3ef9b19 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -54,6 +54,7 @@ struct cmdline_opts {
int use_set; /* work with specified set number */
/* 0 means all sets, otherwise apply to set use_set - 1 */
+ u_int ctx;
};
extern struct cmdline_opts co;
@@ -282,6 +283,9 @@ void dummynet_list(int ac, char *av[], int show_counters);
void dummynet_flush(void);
int ipfw_delete_pipe(int pipe_or_queue, int n);
+/* Contextes */
+int ipfw_context_handler(int, char **);
+
/* ipv6.c */
void print_unreach6_code(uint16_t code);
void print_ip6(struct _ipfw_insn_ip6 *cmd, char const *s);
diff --git a/sbin/ipfw/main.c b/sbin/ipfw/main.c
index 82a299b..ab75a1b 100644
--- a/sbin/ipfw/main.c
+++ b/sbin/ipfw/main.c
@@ -262,7 +262,7 @@ ipfw_main(int oldac, char **oldav)
save_av = av;
optind = optreset = 1; /* restart getopt() */
- while ((ch = getopt(ac, av, "abcdefhinNp:qs:STtv")) != -1)
+ while ((ch = getopt(ac, av, "abcdefhinNp:qs:STtvx:")) != -1)
switch (ch) {
case 'a':
do_acct = 1;
@@ -335,6 +335,12 @@ ipfw_main(int oldac, char **oldav)
co.verbose = 1;
break;
+ case 'x':
+ co.ctx = atoi(optarg);
+ if (co.ctx == 0)
+ errx(EX_USAGE, "Context 0 is invalid");
+ break;
+
default:
free(save_av);
return 1;
@@ -362,7 +368,9 @@ ipfw_main(int oldac, char **oldav)
co.do_nat = 0;
co.do_pipe = 0;
co.use_set = 0;
- if (!strncmp(*av, "nat", strlen(*av)))
+ if (!strncmp(*av, "zone", strlen(*av)))
+ return (ipfw_context_handler(ac, av));
+ else if (!strncmp(*av, "nat", strlen(*av)))
co.do_nat = 1;
else if (!strncmp(*av, "pipe", strlen(*av)))
co.do_pipe = 1;
@@ -389,6 +397,9 @@ ipfw_main(int oldac, char **oldav)
}
NEED1("missing command");
+ if (!co.ctx && !co.do_pipe)
+ err(11, "Context is mandatory");
+
/*
* For pipes, queues and nats we normally say 'nat|pipe NN config'
* but the code is easier to parse as 'nat|pipe config NN'
@@ -458,7 +469,7 @@ ipfw_readfile(int ac, char *av[])
FILE *f = NULL;
pid_t preproc = 0;
- while ((c = getopt(ac, av, "cfNnp:qS")) != -1) {
+ while ((c = getopt(ac, av, "cfNnp:qSx:")) != -1) {
switch(c) {
case 'c':
co.do_compact = 1;
@@ -509,6 +520,12 @@ ipfw_readfile(int ac, char *av[])
co.show_sets = 1;
break;
+ case 'x':
+ co.ctx = atoi(optarg);
+ if (co.ctx == 0)
+ errx(EX_USAGE, "Context 0 is invalid");
+ break;
+
default:
errx(EX_USAGE, "bad arguments, for usage"
" summary ``ipfw''");
OpenPOWER on IntegriCloud