summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authormelifaro <melifaro@FreeBSD.org>2014-06-14 22:47:25 +0000
committermelifaro <melifaro@FreeBSD.org>2014-06-14 22:47:25 +0000
commitfe9646e6ff1b3a57a7e6178adbd24217fe42d8a1 (patch)
treed694e53bf952a5685dabed4473ffb07855a9642d /sbin
parent0001953a3587f852c2a6b4d935071dc2523dff84 (diff)
downloadFreeBSD-src-fe9646e6ff1b3a57a7e6178adbd24217fe42d8a1.zip
FreeBSD-src-fe9646e6ff1b3a57a7e6178adbd24217fe42d8a1.tar.gz
Move further to eliminate next pieces of number-assuming code inside tables.
Kernel changes: * Add IP_FW_OBJ_FLUSH opcode (flush table based on its name/set) * Add IP_FW_OBJ_DUMP opcode (dumps table data based on its names/set) * Add IP_FW_OBJ_LISTSIZE / IP_FW_OBJ_LIST opcodes (get list of kernel tables) Userland changes: * move tables code to separate tables.c file * get rid of tables_max * switch "all"/list handling to new opcodes
Diffstat (limited to 'sbin')
-rw-r--r--sbin/ipfw/Makefile2
-rw-r--r--sbin/ipfw/ipfw2.c442
-rw-r--r--sbin/ipfw/ipfw2.h5
-rw-r--r--sbin/ipfw/tables.c597
4 files changed, 624 insertions, 422 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile
index de27e3e..9eb4511 100644
--- a/sbin/ipfw/Makefile
+++ b/sbin/ipfw/Makefile
@@ -3,7 +3,7 @@
.include <src.opts.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 tables.c
WARNS?= 2
.if ${MK_PF} != "no"
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 1282822..0faa6f9 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -60,12 +60,6 @@ int resvd_set_number = RESVD_SET;
int ipfw_socket = -1;
-uint32_t ipfw_tables_max = 0; /* Number of tables supported by kernel */
-
-#ifndef s6_addr32
-#define s6_addr32 __u6_addr.__u6_addr32
-#endif
-
#define CHECK_LENGTH(v, len) do { \
if ((v) < (len)) \
errx(EX_DATAERR, "Rule too long"); \
@@ -457,8 +451,8 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
* Calls setsockopt() with IP_FW3 as kernel-visible opcode.
* Returns 0 on success or -1 otherwise.
*/
-static int
-do_set3(int optname, ip_fw3_opheader *op3, socklen_t optlen)
+int
+do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen)
{
if (co.test_only)
@@ -474,6 +468,27 @@ do_set3(int optname, ip_fw3_opheader *op3, socklen_t optlen)
return (setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, optlen));
}
+int
+do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
+{
+ int error;
+
+ 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");
+
+ op3->opcode = optname;
+
+ error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3,
+ (socklen_t *)optlen);
+
+ return (error);
+}
+
/*
* do_setcmd3 - pass ipfw control cmd to kernel
* @optname: option name
@@ -2232,7 +2247,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
{
int len = 0;
uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
- uint32_t tables_max;
cmd->o.len &= ~F_LEN_MASK; /* zero len */
@@ -2251,10 +2265,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int cblen)
*p++ = '\0';
cmd->o.opcode = O_IP_DST_LOOKUP;
cmd->o.arg1 = strtoul(av + 6, NULL, 0);
- tables_max = ipfw_get_tables_max();
- if (cmd->o.arg1 > tables_max)
- errx(EX_USAGE, "The table number exceeds the maximum "
- "allowed value (%u)", tables_max - 1);
if (p) {
cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
d[0] = strtoul(p, NULL, 0);
@@ -4148,409 +4158,3 @@ ipfw_flush(int force)
printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
}
-
-static void table_list(uint16_t num, int need_header);
-static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
-static int table_destroy(char *name, uint32_t set);
-static int table_get_info(char *name, uint32_t set, ipfw_xtable_info *i);
-static void table_show_info(ipfw_xtable_info *i);
-static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx);
-
-/*
- * Retrieve maximum number of tables supported by ipfw(4) module.
- */
-uint32_t
-ipfw_get_tables_max()
-{
- size_t len;
- uint32_t tables_max;
-
- if (ipfw_tables_max != 0)
- return (ipfw_tables_max);
-
- len = sizeof(tables_max);
- if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len,
- NULL, 0) == -1) {
- if (co.test_only)
- tables_max = 128; /* Old conservative default */
- else
- errx(1, "Can't determine maximum number of ipfw tables."
- " Perhaps you forgot to load ipfw module?");
- }
-
- ipfw_tables_max = tables_max;
-
- return (ipfw_tables_max);
-}
-
-/*
- * This one handles all table-related commands
- * ipfw table N add addr[/masklen] [value]
- * ipfw table N delete addr[/masklen]
- * ipfw table {N | all} flush
- * ipfw table {N | all} list
- */
-void
-ipfw_table_handler(int ac, char *av[])
-{
- ipfw_table_xentry xent;
- int do_add;
- int is_all;
- uint32_t a;
- uint32_t tables_max;
- uint32_t set;
- int error;
- char *tablename;
-
- tables_max = ipfw_get_tables_max();
-
- memset(&xent, 0, sizeof(xent));
-
- ac--; av++;
- tablename = *av;
- set = 0;
- if (ac && isdigit(**av)) {
- xent.tbl = atoi(*av);
- is_all = 0;
- ac--; av++;
- } else if (ac && _substrcmp(*av, "all") == 0) {
- xent.tbl = 0;
- is_all = 1;
- ac--; av++;
- } else
- errx(EX_USAGE, "table number or 'all' keyword required");
- if (xent.tbl >= tables_max)
- errx(EX_USAGE, "The table number exceeds the maximum allowed "
- "value (%d)", tables_max - 1);
- NEED1("table needs command");
- if (is_all && _substrcmp(*av, "list") != 0
- && _substrcmp(*av, "flush") != 0)
- errx(EX_USAGE, "table number required");
-
- 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);
-
- 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. */
- if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH))))
- err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)",
- do_add ? "XADD" : "XDEL");
- /* In silent mode, react to a failed add by deleting */
- if (do_add) {
- 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_XADD)");
- }
- }
- } else if (_substrcmp(*av, "flush") == 0) {
- a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
- do {
- if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl,
- sizeof(xent.tbl)) < 0)
- err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)");
- } while (++xent.tbl < a);
- } else if (_substrcmp(*av, "list") == 0) {
- a = is_all ? tables_max : (uint32_t)(xent.tbl + 1);
- do {
- table_list(xent.tbl, is_all);
- } while (++xent.tbl < a);
- } else if (_substrcmp(*av, "destroy") == 0) {
- if (table_destroy(tablename, set) != 0)
- err(EX_OSERR, "failed to destroy table %s", tablename);
- } else if (_substrcmp(*av, "info") == 0) {
- ipfw_xtable_info i;
- if ((error = table_get_info(tablename, set, &i)) != 0)
- err(EX_OSERR, "failed to request table info");
- table_show_info(&i);
- } else
- errx(EX_USAGE, "invalid table command %s", *av);
-}
-
-static void
-table_fill_xentry(char *arg, ipfw_table_xentry *xent)
-{
- int addrlen, mask, masklen, type;
- struct in6_addr *paddr;
- uint32_t *pkey;
- char *p;
- uint32_t key;
-
- mask = 0;
- type = 0;
- addrlen = 0;
- masklen = 0;
-
- /*
- * Let's try to guess type by agrument.
- * Possible types:
- * 1) IPv4[/mask]
- * 2) IPv6[/mask]
- * 3) interface name
- * 4) port, uid/gid or other u32 key (base 10 format)
- * 5) hostname
- */
- paddr = &xent->k.addr6;
- if (ishexnumber(*arg) != 0 || *arg == ':') {
- /* Remove / if exists */
- if ((p = strchr(arg, '/')) != NULL) {
- *p = '\0';
- mask = atoi(p + 1);
- }
-
- if (inet_pton(AF_INET, arg, paddr) == 1) {
- if (p != NULL && mask > 32)
- errx(EX_DATAERR, "bad IPv4 mask width: %s",
- p + 1);
-
- type = IPFW_TABLE_CIDR;
- masklen = p ? mask : 32;
- addrlen = sizeof(struct in_addr);
- } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
- if (IN6_IS_ADDR_V4COMPAT(paddr))
- errx(EX_DATAERR,
- "Use IPv4 instead of v4-compatible");
- if (p != NULL && mask > 128)
- errx(EX_DATAERR, "bad IPv6 mask width: %s",
- p + 1);
-
- type = IPFW_TABLE_CIDR;
- masklen = p ? mask : 128;
- addrlen = sizeof(struct in6_addr);
- } else {
- /* Port or any other key */
- /* Skip non-base 10 entries like 'fa1' */
- key = strtol(arg, &p, 10);
- if (*p == '\0') {
- pkey = (uint32_t *)paddr;
- *pkey = htonl(key);
- type = IPFW_TABLE_CIDR;
- masklen = 32;
- addrlen = sizeof(uint32_t);
- } else if ((p != arg) && (*p == '.')) {
- /*
- * Warn on IPv4 address strings
- * which are "valid" for inet_aton() but not
- * in inet_pton().
- *
- * Typical examples: '10.5' or '10.0.0.05'
- */
- errx(EX_DATAERR,
- "Invalid IPv4 address: %s", arg);
- }
- }
- }
-
- if (type == 0 && strchr(arg, '.') == NULL) {
- /* Assume interface name. Copy significant data only */
- mask = MIN(strlen(arg), IF_NAMESIZE - 1);
- memcpy(xent->k.iface, arg, mask);
- /* Set mask to exact match */
- masklen = 8 * IF_NAMESIZE;
- type = IPFW_TABLE_INTERFACE;
- addrlen = IF_NAMESIZE;
- }
-
- if (type == 0) {
- if (lookup_host(arg, (struct in_addr *)paddr) != 0)
- errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
-
- masklen = 32;
- type = IPFW_TABLE_CIDR;
- addrlen = sizeof(struct in_addr);
- }
-
- xent->type = type;
- xent->masklen = masklen;
- xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
-}
-
-static void
-table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx)
-{
-
- ntlv->head.type = IPFW_TLV_NAME;
- ntlv->head.length = sizeof(ipfw_obj_ntlv);
- ntlv->idx = uidx;
- strlcpy(ntlv->name, name, sizeof(ntlv->name));
-}
-
-/*
- * Destroys given table @name in given @set.
- * Returns 0 on success.
- */
-static int
-table_destroy(char *name, uint32_t set)
-{
- ipfw_obj_header oh;
-
- memset(&oh, 0, sizeof(oh));
- oh.idx = 1;
- oh.objtype = IPFW_OBJTYPE_TABLE;
- table_fill_ntlv(&oh.ntlv, name, 1);
- if (do_set3(IP_FW_OBJ_DEL, &oh.opheader, sizeof(oh)) != 0)
- return (-1);
-
- return (0);
-}
-
-/*
- * Retrieves info for given table @name in given @set and stores
- * it inside @i.
- * Returns 0 on success.
- */
-static int
-table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
-{
- char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
- ipfw_obj_header *oh;
- size_t sz;
-
- sz = sizeof(tbuf);
- memset(tbuf, 0, sizeof(tbuf));
- oh = (ipfw_obj_header *)tbuf;
-
- oh->opheader.opcode = IP_FW_OBJ_INFO;
- oh->set = set;
- oh->idx = 1;
- oh->objtype = IPFW_OBJTYPE_TABLE;
- table_fill_ntlv(&oh->ntlv, name, 1);
-
- if (do_cmd(IP_FW3, oh, (uintptr_t)&sz) < 0)
- return (-1);
-
- if (sz < sizeof(tbuf))
- return (-1);
-
- *i = *(ipfw_xtable_info *)(oh + 1);
-
- return (0);
-}
-
-/*
- * Prints table info struct @i in human-readable form.
- */
-static void
-table_show_info(ipfw_xtable_info *i)
-{
- char *type;
-
- printf("--- table %s, set %u ---\n", i->tablename, i->set);
- switch (i->type) {
- case IPFW_TABLE_CIDR:
- type = "cidr";
- break;
- case IPFW_TABLE_INTERFACE:
- type = "iface";
- break;
- default:
- type = "unknown";
- }
- printf("type: %s, kindex: %d\n", type, i->kidx);
- printf("ftype: %d, algorithm: %d\n", i->ftype, i->atype);
- printf("references: %u\n", i->refcnt);
- printf("items: %u, size: %u\n", i->count, i->size);
-}
-
-static void
-table_list(uint16_t num, int need_header)
-{
- ipfw_xtable *tbl;
- ipfw_table_xentry *xent;
- socklen_t l;
- 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)
- return;
-
- l = *a;
- tbl = safe_calloc(1, l);
- 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);
- 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 ((xent->flags & IPFW_TCF_INET) != 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 = (ipfw_table_xentry *)((char *)xent + xent->len);
- }
-
- free(tbl);
-}
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index 2301c40..2729aaf 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -227,9 +227,10 @@ int _substrcmp2(const char *str1, const char* str2, const char* str3);
int match_token(struct _s_x *table, char *string);
char const *match_value(struct _s_x *p, int value);
+struct _ip_fw3_opheader;
int do_cmd(int optname, void *optval, uintptr_t optlen);
-
-uint32_t ipfw_get_tables_max(void);
+int do_set3(int optname, struct _ip_fw3_opheader *op3, uintptr_t optlen);
+int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen);
struct in6_addr;
void n2mask(struct in6_addr *mask, int n);
diff --git a/sbin/ipfw/tables.c b/sbin/ipfw/tables.c
new file mode 100644
index 0000000..0d16bec7
--- /dev/null
+++ b/sbin/ipfw/tables.c
@@ -0,0 +1,597 @@
+/*
+ * Copyright (c) 2002-2003 Luigi Rizzo
+ * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
+ * Copyright (c) 1994 Ugen J.S.Antsilevich
+ *
+ * Idea and grammar partially left from:
+ * Copyright (c) 1993 Daniel Boulet
+ *
+ * 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.
+ *
+ * in-kernel tables support
+ *
+ * $FreeBSD: projects/ipfw/sbin/ipfw/ipfw2.c 267467 2014-06-14 10:58:39Z melifaro $
+ */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stddef.h> /* offsetof */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h> /* def. of struct route */
+#include <netinet/in.h>
+#include <netinet/ip_fw.h>
+#include <arpa/inet.h>
+#include <alias.h>
+
+#include "ipfw2.h"
+
+static void table_list(ipfw_xtable_info *i, int need_header);
+static void table_fill_xentry(char *arg, ipfw_table_xentry *xent);
+static int table_flush(char *name, uint32_t set);
+static int table_destroy(char *name, uint32_t set);
+static int table_get_info(char *name, uint32_t set, ipfw_xtable_info *i);
+static int table_show_info(ipfw_xtable_info *i, void *arg);
+static void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx);
+
+static int table_flush_one(ipfw_xtable_info *i, void *arg);
+static int table_show_one(ipfw_xtable_info *i, void *arg);
+static int table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh);
+static void table_show_list(ipfw_obj_header *oh, int need_header);
+
+typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
+static int tables_foreach(table_cb_t *f, void *arg, int sort);
+
+#ifndef s6_addr32
+#define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+static int
+lookup_host (char *host, struct in_addr *ipaddr)
+{
+ struct hostent *he;
+
+ if (!inet_aton(host, ipaddr)) {
+ if ((he = gethostbyname(host)) == NULL)
+ return(-1);
+ *ipaddr = *(struct in_addr *)he->h_addr_list[0];
+ }
+ return(0);
+}
+
+/*
+ * This one handles all table-related commands
+ * ipfw table N add addr[/masklen] [value]
+ * ipfw table N delete addr[/masklen]
+ * ipfw table {N | all} flush
+ * ipfw table {N | all} list
+ * ipfw table {N | all} info
+ */
+void
+ipfw_table_handler(int ac, char *av[])
+{
+ ipfw_table_xentry *xent;
+ int do_add;
+ int is_all;
+ uint32_t set;
+ int error;
+ char xbuf[sizeof(ip_fw3_opheader) + sizeof(ipfw_table_xentry)];
+ ip_fw3_opheader *op3;
+ char *tablename;
+
+ memset(xbuf, 0, sizeof(xbuf));
+ op3 = (ip_fw3_opheader *)xbuf;
+ xent = (ipfw_table_xentry *)(op3 + 1);
+
+ ac--; av++;
+ tablename = *av;
+ set = 0;
+ if (ac && isdigit(**av)) {
+ xent->tbl = atoi(*av);
+ is_all = 0;
+ ac--; av++;
+ } else if (ac && _substrcmp(*av, "all") == 0) {
+ xent->tbl = 0;
+ is_all = 1;
+ ac--; av++;
+ } else
+ errx(EX_USAGE, "table number or 'all' keyword required");
+ NEED1("table needs command");
+ if (is_all && _substrcmp(*av, "list") != 0
+ && _substrcmp(*av, "info") != 0
+ && _substrcmp(*av, "flush") != 0)
+ errx(EX_USAGE, "table number required");
+
+ 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);
+
+ 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_set3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL,
+ op3, sizeof(xbuf)) < 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 ? "XADD" : "XDEL");
+ /* In silent mode, react to a failed add by deleting */
+ if (do_add) {
+ do_set3(IP_FW_TABLE_XDEL, op3, sizeof(xbuf));
+ if (do_set3(IP_FW_TABLE_XADD, op3, sizeof(xbuf)) < 0)
+ err(EX_OSERR,
+ "setsockopt(IP_FW_TABLE_XADD)");
+ }
+ }
+ } else if (_substrcmp(*av, "flush") == 0) {
+ if (is_all == 0) {
+ if ((error = table_flush(tablename, set)) != 0)
+ err(EX_OSERR, "failed to flush table %s info",
+ tablename);
+ } else {
+ error = tables_foreach(table_flush_one, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to flush tables list");
+ }
+ } else if (_substrcmp(*av, "list") == 0) {
+ if (is_all == 0) {
+ ipfw_xtable_info i;
+ if ((error = table_get_info(tablename, set, &i)) != 0)
+ err(EX_OSERR, "failed to request table info");
+ table_show_one(&i, NULL);
+ } else {
+ error = tables_foreach(table_show_one, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to request tables list");
+ }
+ } else if (_substrcmp(*av, "destroy") == 0) {
+ if (table_destroy(tablename, set) != 0)
+ err(EX_OSERR, "failed to destroy table %s", tablename);
+ } else if (_substrcmp(*av, "info") == 0) {
+ if (is_all == 0) {
+ ipfw_xtable_info i;
+ if ((error = table_get_info(tablename, set, &i)) != 0)
+ err(EX_OSERR, "failed to request table info");
+ table_show_info(&i, NULL);
+ } else {
+ error = tables_foreach(table_show_info, NULL, 1);
+ if (error != 0)
+ err(EX_OSERR, "failed to request tables list");
+ }
+ } else
+ errx(EX_USAGE, "invalid table command %s", *av);
+}
+
+static void
+table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+{
+ int addrlen, mask, masklen, type;
+ struct in6_addr *paddr;
+ uint32_t *pkey;
+ char *p;
+ uint32_t key;
+
+ mask = 0;
+ type = 0;
+ addrlen = 0;
+ masklen = 0;
+
+ /*
+ * Let's try to guess type by agrument.
+ * Possible types:
+ * 1) IPv4[/mask]
+ * 2) IPv6[/mask]
+ * 3) interface name
+ * 4) port, uid/gid or other u32 key (base 10 format)
+ * 5) hostname
+ */
+ paddr = &xent->k.addr6;
+ if (ishexnumber(*arg) != 0 || *arg == ':') {
+ /* Remove / if exists */
+ if ((p = strchr(arg, '/')) != NULL) {
+ *p = '\0';
+ mask = atoi(p + 1);
+ }
+
+ if (inet_pton(AF_INET, arg, paddr) == 1) {
+ if (p != NULL && mask > 32)
+ errx(EX_DATAERR, "bad IPv4 mask width: %s",
+ p + 1);
+
+ type = IPFW_TABLE_CIDR;
+ masklen = p ? mask : 32;
+ addrlen = sizeof(struct in_addr);
+ } else if (inet_pton(AF_INET6, arg, paddr) == 1) {
+ if (IN6_IS_ADDR_V4COMPAT(paddr))
+ errx(EX_DATAERR,
+ "Use IPv4 instead of v4-compatible");
+ if (p != NULL && mask > 128)
+ errx(EX_DATAERR, "bad IPv6 mask width: %s",
+ p + 1);
+
+ type = IPFW_TABLE_CIDR;
+ masklen = p ? mask : 128;
+ addrlen = sizeof(struct in6_addr);
+ } else {
+ /* Port or any other key */
+ /* Skip non-base 10 entries like 'fa1' */
+ key = strtol(arg, &p, 10);
+ if (*p == '\0') {
+ pkey = (uint32_t *)paddr;
+ *pkey = htonl(key);
+ type = IPFW_TABLE_CIDR;
+ masklen = 32;
+ addrlen = sizeof(uint32_t);
+ } else if ((p != arg) && (*p == '.')) {
+ /*
+ * Warn on IPv4 address strings
+ * which are "valid" for inet_aton() but not
+ * in inet_pton().
+ *
+ * Typical examples: '10.5' or '10.0.0.05'
+ */
+ errx(EX_DATAERR,
+ "Invalid IPv4 address: %s", arg);
+ }
+ }
+ }
+
+ if (type == 0 && strchr(arg, '.') == NULL) {
+ /* Assume interface name. Copy significant data only */
+ mask = MIN(strlen(arg), IF_NAMESIZE - 1);
+ memcpy(xent->k.iface, arg, mask);
+ /* Set mask to exact match */
+ masklen = 8 * IF_NAMESIZE;
+ type = IPFW_TABLE_INTERFACE;
+ addrlen = IF_NAMESIZE;
+ }
+
+ if (type == 0) {
+ if (lookup_host(arg, (struct in_addr *)paddr) != 0)
+ errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+
+ masklen = 32;
+ type = IPFW_TABLE_CIDR;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ xent->type = type;
+ xent->masklen = masklen;
+ xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+}
+
+static void
+table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx)
+{
+
+ ntlv->head.type = IPFW_TLV_NAME;
+ ntlv->head.length = sizeof(ipfw_obj_ntlv);
+ ntlv->idx = uidx;
+ strlcpy(ntlv->name, name, sizeof(ntlv->name));
+}
+
+static void
+table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
+
+ oh->set = i->set;
+ oh->idx = 1;
+ oh->objtype = IPFW_OBJTYPE_TABLE;
+ table_fill_ntlv(&oh->ntlv, i->tablename, 1);
+}
+
+/*
+ * Destroys given table @name in given @set.
+ * Returns 0 on success.
+ */
+static int
+table_destroy(char *name, uint32_t set)
+{
+ ipfw_obj_header oh;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.idx = 1;
+ oh.objtype = IPFW_OBJTYPE_TABLE;
+ table_fill_ntlv(&oh.ntlv, name, 1);
+ if (do_set3(IP_FW_OBJ_DEL, &oh.opheader, sizeof(oh)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Flushes given table @name in given @set.
+ * Returns 0 on success.
+ */
+static int
+table_flush(char *name, uint32_t set)
+{
+ ipfw_obj_header oh;
+
+ memset(&oh, 0, sizeof(oh));
+ oh.idx = 1;
+ oh.objtype = IPFW_OBJTYPE_TABLE;
+ table_fill_ntlv(&oh.ntlv, name, 1);
+ if (do_set3(IP_FW_OBJ_FLUSH, &oh.opheader, sizeof(oh)) != 0)
+ return (-1);
+
+ return (0);
+}
+
+/*
+ * Retrieves info for given table @name in given @set and stores
+ * it inside @i.
+ * Returns 0 on success.
+ */
+static int
+table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
+{
+ char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
+ ipfw_obj_header *oh;
+ size_t sz;
+
+ sz = sizeof(tbuf);
+ memset(tbuf, 0, sizeof(tbuf));
+ oh = (ipfw_obj_header *)tbuf;
+
+ i->set = set;
+ strlcpy(i->tablename, name, sizeof(i->tablename));
+
+ table_fill_objheader(oh, i);
+
+ if (do_get3(IP_FW_OBJ_INFO, &oh->opheader, &sz) < 0)
+ return (-1);
+
+ if (sz < sizeof(tbuf))
+ return (-1);
+
+ *i = *(ipfw_xtable_info *)(oh + 1);
+
+ return (0);
+}
+
+/*
+ * Prints table info struct @i in human-readable form.
+ */
+static int
+table_show_info(ipfw_xtable_info *i, void *arg)
+{
+ char *type;
+
+ printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+ switch (i->type) {
+ case IPFW_TABLE_CIDR:
+ type = "cidr";
+ break;
+ case IPFW_TABLE_INTERFACE:
+ type = "iface";
+ break;
+ default:
+ type = "unknown";
+ }
+ printf(" type: %s, kindex: %d\n", type, i->kidx);
+ printf(" ftype: %d, algorithm: %d\n", i->ftype, i->atype);
+ printf(" references: %u\n", i->refcnt);
+ printf(" items: %u, size: %u\n", i->count, i->size);
+
+ return (0);
+}
+
+
+/*
+ * Function wrappers which can be used either
+ * as is or as foreach function parameter.
+ */
+
+static int
+table_show_one(ipfw_xtable_info *i, void *arg)
+{
+ ipfw_obj_header *oh;
+
+ if ((oh = malloc(i->size)) == NULL)
+ return (ENOMEM);
+
+ if (table_get_list(i, oh) == 0)
+ table_show_list(oh, 1);
+
+ free(oh);
+ return (0);
+}
+
+static int
+table_flush_one(ipfw_xtable_info *i, void *arg)
+{
+
+ return (table_flush(i->tablename, i->set));
+}
+
+
+/*
+ * Compare table names.
+ * Honor number comparison.
+ */
+static int
+tablename_cmp(const void *a, const void *b)
+{
+ ipfw_xtable_info *ia, *ib;
+ int la, lb;
+
+ ia = (ipfw_xtable_info *)a;
+ ib = (ipfw_xtable_info *)b;
+ la = strlen(ia->tablename);
+ lb = strlen(ib->tablename);
+
+ if (la > lb)
+ return (1);
+ else if (la < lb)
+ return (-01);
+
+ return (strcmp(ia->tablename, ib->tablename));
+}
+
+/*
+ * Retrieves table list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+static int
+tables_foreach(table_cb_t *f, void *arg, int sort)
+{
+ ipfw_obj_lheader req, *olh;
+ ipfw_xtable_info *info;
+ size_t sz;
+ int i, error;
+
+ memset(&req, 0, sizeof(req));
+ sz = sizeof(req);
+
+ req.objtype = IPFW_OBJTYPE_TABLE;
+ if ((error = do_get3(IP_FW_OBJ_LISTSIZE, &req.opheader, &sz)) != 0)
+ return (errno);
+
+ sz = req.size;
+ if ((olh = calloc(1, sz)) == NULL)
+ return (ENOMEM);
+
+ olh->objtype = IPFW_OBJTYPE_TABLE;
+ olh->size = sz;
+ if ((error = do_get3(IP_FW_OBJ_LIST, &olh->opheader, &sz)) != 0) {
+ free(olh);
+ return (errno);
+ }
+
+ if (sort != 0)
+ qsort(olh + 1, olh->count, olh->objsize, tablename_cmp);
+
+ info = (ipfw_xtable_info *)(olh + 1);
+ for (i = 0; i < olh->count; i++) {
+ error = f(info, arg); /* Ignore errors for now */
+ info = (ipfw_xtable_info *)((caddr_t)info + olh->objsize);
+ }
+
+ free(olh);
+
+ return (0);
+}
+
+/*
+ * Retrieves all entries for given table @i in
+ * eXtended format, returning pointer vi @ooh.
+ *
+ * Returns 0 on success.
+ */
+static int
+table_get_list(ipfw_xtable_info *i, ipfw_obj_header *oh)
+{
+ size_t sz;
+ int error;
+
+ table_fill_objheader(oh, i);
+ sz = i->size;
+
+ if ((error = do_get3(IP_FW_OBJ_DUMP, &oh->opheader, &sz)) != 0)
+ return (errno);
+
+ return (0);
+}
+
+/*
+ * Shows all entries from @oh in human-readable format
+ */
+static void
+table_show_list(ipfw_obj_header *oh, int need_header)
+{
+ ipfw_table_xentry *xent;
+ uint32_t count, tval;
+ char tbuf[128];
+ struct in6_addr *addr6;
+ ipfw_xtable_info *i;
+
+ i = (ipfw_xtable_info *)(oh + 1);
+ xent = (ipfw_table_xentry *)(i + 1);
+
+ if (need_header)
+ printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+
+ count = i->count;
+ while (count > 0) {
+ switch (i->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, &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);
+ }
+
+ xent = (ipfw_table_xentry *)((caddr_t)xent + xent->len);
+ count--;
+ }
+}
+
OpenPOWER on IntegriCloud