svn commit: r267487 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw

Alexander V. Chernikov melifaro at FreeBSD.org
Sat Jun 14 22:47:27 UTC 2014


Author: melifaro
Date: Sat Jun 14 22:47:25 2014
New Revision: 267487
URL: http://svnweb.freebsd.org/changeset/base/267487

Log:
  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

Added:
  projects/ipfw/sbin/ipfw/tables.c
Modified:
  projects/ipfw/sbin/ipfw/Makefile
  projects/ipfw/sbin/ipfw/ipfw2.c
  projects/ipfw/sbin/ipfw/ipfw2.h
  projects/ipfw/sys/netinet/ip_fw.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h

Modified: projects/ipfw/sbin/ipfw/Makefile
==============================================================================
--- projects/ipfw/sbin/ipfw/Makefile	Sat Jun 14 20:36:32 2014	(r267486)
+++ projects/ipfw/sbin/ipfw/Makefile	Sat Jun 14 22:47:25 2014	(r267487)
@@ -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"

Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c	Sat Jun 14 20:36:32 2014	(r267486)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Sat Jun 14 22:47:25 2014	(r267487)
@@ -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, uintpt
  * 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 *op
 	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
 {
 	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
 			*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);
-}

Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h	Sat Jun 14 20:36:32 2014	(r267486)
+++ projects/ipfw/sbin/ipfw/ipfw2.h	Sat Jun 14 22:47:25 2014	(r267487)
@@ -227,9 +227,10 @@ int _substrcmp2(const char *str1, const 
 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);

Added: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/ipfw/sbin/ipfw/tables.c	Sat Jun 14 22:47:25 2014	(r267487)
@@ -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);
+

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-projects mailing list