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

Alexander V. Chernikov melifaro at FreeBSD.org
Thu Jul 3 22:26:00 UTC 2014


Author: melifaro
Date: Thu Jul  3 22:25:59 2014
New Revision: 268239
URL: http://svnweb.freebsd.org/changeset/base/268239

Log:
  Fully switch to named tables:
  
  Kernel changes:
  * Introduce ipfw_obj_tentry table entry structure to force u64 alignment.
  * Support "update-on-existing-key" "add" bahavior (TEI_FLAGS_UPDATED).
  * Use "subtype" field to distingush between IPv4 and IPv6 table records
    instead of previous hack.
  * Add value type (vtype) field for kernel tables. Current types are
    number,ip and dscp
  * Fix sets mask retrieval for old binaries
  * Fix crash while using interface tables
  
  Userland changes:
  * Switch ipfw_table_handler() to use named-only tables.
  * Add "table NAME create [type {cidr|iface|u32} [valtype {number|ip|dscp}] ..."
  * Switch ipfw_table_handler to match_token()-based parser.
  * Switch ipfw_sets_handler to use new ipfw_get_config() for mask  retrieval.
  * Allow ipfw set X table ... syntax to permit using per-set table namespaces.

Modified:
  projects/ipfw/sbin/ipfw/ipfw2.c
  projects/ipfw/sbin/ipfw/ipfw2.h
  projects/ipfw/sbin/ipfw/main.c
  projects/ipfw/sbin/ipfw/tables.c
  projects/ipfw/sys/netinet/ip_fw.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_private.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
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c

Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c	Thu Jul  3 21:48:19 2014	(r268238)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Thu Jul  3 22:25:59 2014	(r268239)
@@ -174,7 +174,7 @@ static struct _s_x f_iptos[] = {
 	{ NULL,	0 }
 };
 
-static struct _s_x f_ipdscp[] = {
+struct _s_x f_ipdscp[] = {
 	{ "af11", IPTOS_DSCP_AF11 >> 2 },	/* 001010 */
 	{ "af12", IPTOS_DSCP_AF12 >> 2 },	/* 001100 */
 	{ "af13", IPTOS_DSCP_AF13 >> 2 },	/* 001110 */
@@ -649,7 +649,7 @@ match_token(struct _s_x *table, char *st
 	for (pt = table ; i && pt->s != NULL ; pt++)
 		if (strlen(pt->s) == i && !bcmp(string, pt->s, i))
 			return pt->x;
-	return -1;
+	return (-1);
 }
 
 /**
@@ -665,6 +665,25 @@ match_value(struct _s_x *p, int value)
 	return NULL;
 }
 
+size_t
+concat_tokens(char *buf, size_t bufsize, struct _s_x *table, char *delimiter)
+{
+	struct _s_x *pt;
+	int l;
+	size_t sz;
+
+	for (sz = 0, pt = table ; pt->s != NULL; pt++) {
+		l = snprintf(buf + sz, bufsize - sz, "%s%s",
+		    (sz == 0) ? "" : delimiter, pt->s);
+		sz += l;
+		bufsize += l;
+		if (sz > bufsize)
+			return (bufsize);
+	}
+
+	return (sz);
+}
+
 /*
  * _substrcmp takes two strings and returns 1 if they do not match,
  * and 0 if they match exactly or the first string is a sub-string
@@ -2012,46 +2031,38 @@ show_dyn_state(struct cmdline_opts *co, 
 void
 ipfw_sets_handler(char *av[])
 {
-	uint32_t set_disable, masks[2];
-	int i, nbytes;
+	uint32_t masks[2];
+	int i;
 	uint16_t rulenum;
 	uint8_t cmd, new_set;
+	char *msg;
+	size_t size;
 
 	av++;
 
 	if (av[0] == NULL)
 		errx(EX_USAGE, "set needs command");
 	if (_substrcmp(*av, "show") == 0) {
-		void *data = NULL;
-		char const *msg;
-		int nalloc;
-
-		nalloc = nbytes = sizeof(struct ip_fw);
-		while (nbytes >= nalloc) {
-			if (data)
-				free(data);
-			nalloc = nalloc * 2 + 200;
-			nbytes = nalloc;
-			data = safe_calloc(1, nbytes);
-			if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0)
-				err(EX_OSERR, "getsockopt(IP_FW_GET)");
-		}
+		struct format_opts fo;
+		ipfw_cfg_lheader *cfg;
 
-		bcopy(&((struct ip_fw *)data)->next_rule,
-			&set_disable, sizeof(set_disable));
+		memset(&fo, 0, sizeof(fo));
+		if (ipfw_get_config(&co, &fo, &cfg, &size) != 0)
+			err(EX_OSERR, "requesting config failed");
 
-		for (i = 0, msg = "disable" ; i < RESVD_SET; i++)
-			if ((set_disable & (1<<i))) {
+		for (i = 0, msg = "disable"; i < RESVD_SET; i++)
+			if ((cfg->set_mask & (1<<i)) == 0) {
 				printf("%s %d", msg, i);
 				msg = "";
 			}
-		msg = (set_disable) ? " enable" : "enable";
+		msg = (cfg->set_mask != (uint32_t)-1) ? " enable" : "enable";
 		for (i = 0; i < RESVD_SET; i++)
-			if (!(set_disable & (1<<i))) {
+			if ((cfg->set_mask & (1<<i)) != 0) {
 				printf("%s %d", msg, i);
 				msg = "";
 			}
 		printf("\n");
+		free(cfg);
 	} else if (_substrcmp(*av, "swap") == 0) {
 		av++;
 		if ( av[0] == NULL || av[1] == NULL )

Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h	Thu Jul  3 21:48:19 2014	(r268238)
+++ projects/ipfw/sbin/ipfw/ipfw2.h	Thu Jul  3 22:25:59 2014	(r268239)
@@ -71,6 +71,8 @@ struct _s_x {
 	int x;
 };
 
+extern struct _s_x f_ipdscp[];
+
 enum tokens {
 	TOK_NULL=0,
 
@@ -205,6 +207,16 @@ enum tokens {
 	TOK_LOOKUP,
 	TOK_SOCKARG,
 	TOK_SETDSCP,
+	/* Table tokens */
+	TOK_CREATE,
+	TOK_DESTROY,
+	TOK_LIST,
+	TOK_INFO,
+	TOK_FLUSH,
+	TOK_ADD,
+	TOK_DEL,
+	TOK_VALTYPE,
+	TOK_ALGO,
 };
 /*
  * the following macro returns an error message if we run out of
@@ -238,6 +250,8 @@ int _substrcmp2(const char *str1, const 
 /* utility functions */
 int match_token(struct _s_x *table, char *string);
 char const *match_value(struct _s_x *p, int value);
+size_t concat_tokens(char *buf, size_t bufsize, struct _s_x *table,
+    char *delimiter);
 
 struct _ip_fw3_opheader;
 int do_cmd(int optname, void *optval, uintptr_t optlen);

Modified: projects/ipfw/sbin/ipfw/main.c
==============================================================================
--- projects/ipfw/sbin/ipfw/main.c	Thu Jul  3 21:48:19 2014	(r268238)
+++ projects/ipfw/sbin/ipfw/main.c	Thu Jul  3 22:25:59 2014	(r268239)
@@ -436,6 +436,8 @@ ipfw_main(int oldac, char **oldav)
 			ipfw_list(ac, av, do_acct);
 		else if (_substrcmp(*av, "show") == 0)
 			ipfw_list(ac, av, 1 /* show counters */);
+		else if (_substrcmp(*av, "table") == 0)
+			ipfw_table_handler(ac, av);
 		else
 			errx(EX_USAGE, "bad command `%s'", *av);
 	}

Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c	Thu Jul  3 21:48:19 2014	(r268238)
+++ projects/ipfw/sbin/ipfw/tables.c	Thu Jul  3 22:25:59 2014	(r268239)
@@ -49,18 +49,27 @@
 #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 void table_modify_record(ipfw_obj_header *oh, int ac, char *av[],
+    int add, int update);
+static int table_flush(ipfw_obj_header *oh);
+static int table_destroy(ipfw_obj_header *oh);
+static int table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i);
+static void table_create(ipfw_obj_header *oh, int ac, char *av[]);
+static int table_get_info(ipfw_obj_header *oh, 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 void table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set,
+    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);
 
+static void tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
+    char *key, uint8_t *ptype, uint8_t *pvtype);
+static void tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent,
+    char *arg, uint8_t type, uint8_t vtype);
+
 typedef int (table_cb_t)(ipfw_xtable_info *i, void *arg);
 static int tables_foreach(table_cb_t *f, void *arg, int sort);
 
@@ -68,6 +77,31 @@ static int tables_foreach(table_cb_t *f,
 #define s6_addr32 __u6_addr.__u6_addr32
 #endif
 
+static struct _s_x tabletypes[] = {
+      { "cidr",		IPFW_TABLE_CIDR },
+      { "iface",	IPFW_TABLE_INTERFACE },
+      { "u32",		IPFW_TABLE_U32 },
+      { NULL, 0 }
+};
+
+static struct _s_x tablevaltypes[] = {
+      { "dscp",		IPFW_VTYPE_DSCP },
+      { "ip",		IPFW_VTYPE_IP },
+      { "number",	IPFW_VTYPE_U32 },
+      { NULL, 0 }
+};
+
+static struct _s_x tablecmds[] = {
+      { "add",		TOK_ADD },
+      { "create",	TOK_CREATE },
+      { "delete",	TOK_DEL },
+      { "destroy",	TOK_DESTROY },
+      { "flush",	TOK_FLUSH },
+      { "info",		TOK_INFO },
+      { "list",		TOK_LIST },
+      { NULL, 0 }
+};
+
 static int
 lookup_host (char *host, struct in_addr *ipaddr)
 {
@@ -83,305 +117,280 @@ lookup_host (char *host, struct in_addr 
 
 /*
  * 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
+ * 	ipfw table NAME create ...
+ * 	ipfw table NAME destroy
+ * 	ipfw table NAME add addr[/masklen] [value]
+ * 	ipfw table NAME delete addr[/masklen]
+ * 	ipfw table {NAME | all} flush
+ * 	ipfw table {NAME | all} list
+ * 	ipfw table {NAME | 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;
+	int do_add, is_all;
+	int error, tcmd;
+	ipfw_xtable_info i;
+	ipfw_obj_header oh;
 	char *tablename;
+	uint32_t set;
 
-	memset(xbuf, 0, sizeof(xbuf));
-	op3 = (ip_fw3_opheader *)xbuf;
-	xent = (ipfw_table_xentry *)(op3 + 1);
+	memset(&oh, 0, sizeof(oh));
+	is_all = 0;
+	if (co.use_set != 0)
+		set = co.use_set - 1;
+	else
+		set = 0;
 
 	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 (table_check_name(tablename) == 0) {
+		table_fill_ntlv(&oh.ntlv, *av, set, 1);
+		//oh->set = set;
+		oh.idx = 1;
+	} else {
+		if (strcmp(tablename, "all") == 0)
+			is_all = 1;
+		else
+			errx(EX_USAGE, "table name %s is invalid", tablename);
+	}
+	ac--; av++;
+
+	if ((tcmd = match_token(tablecmds, *av)) == -1)
+		errx(EX_USAGE, "invalid table command %s", *av);
+
 	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");
+	switch (tcmd) {
+	case TOK_LIST:
+	case TOK_INFO:
+	case TOK_FLUSH:
+		break;
+	default:
+		if (is_all != 0)
+			errx(EX_USAGE, "table name required");
+	}
 
-	if (_substrcmp(*av, "add") == 0 ||
-	    _substrcmp(*av, "delete") == 0) {
+	switch (tcmd) {
+	case TOK_ADD:
+	case TOK_DEL:
 		do_add = **av == 'a';
 		ac--; av++;
-		if (!ac)
-			errx(EX_USAGE, "address required");
-
-		table_fill_xentry(*av, xent);
-
+		table_modify_record(&oh, ac, av, do_add, co.do_quiet);
+		break;
+	case TOK_CREATE:
 		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) {
+		table_create(&oh, ac, av);
+		break;
+	case TOK_DESTROY:
+		if (table_destroy(&oh) != 0)
+			err(EX_OSERR, "failed to destroy table %s", tablename);
+		break;
+	case TOK_FLUSH:
 		if (is_all == 0) {
-			if ((error = table_flush(tablename, set)) != 0)
+			if ((error = table_flush(&oh)) != 0)
 				err(EX_OSERR, "failed to flush table %s info",
 				    tablename);
 		} else {
-			error = tables_foreach(table_flush_one, NULL, 1);
+			error = tables_foreach(table_flush_one, &oh, 1);
 			if (error != 0)
 				err(EX_OSERR, "failed to flush tables list");
 		}
-	} else if (_substrcmp(*av, "list") == 0) {
+		break;
+	case TOK_INFO:
 		if (is_all == 0) {
-			ipfw_xtable_info i;
-			if ((error = table_get_info(tablename, set, &i)) != 0)
+			if ((error = table_get_info(&oh, &i)) != 0)
 				err(EX_OSERR, "failed to request table info");
-			table_show_one(&i, NULL);
+			table_show_info(&i, NULL);
 		} else {
-			error = tables_foreach(table_show_one, NULL, 1);
+			error = tables_foreach(table_show_info, 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) {
+		break;
+	case TOK_LIST:
 		if (is_all == 0) {
 			ipfw_xtable_info i;
-			if ((error = table_get_info(tablename, set, &i)) != 0)
+			if ((error = table_get_info(&oh, &i)) != 0)
 				err(EX_OSERR, "failed to request table info");
-			table_show_info(&i, NULL);
+			table_show_one(&i, NULL);
 		} else {
-			error = tables_foreach(table_show_info, NULL, 1);
+			error = tables_foreach(table_show_one, NULL, 1);
 			if (error != 0)
 				err(EX_OSERR, "failed to request tables list");
 		}
-	} else
-		errx(EX_USAGE, "invalid table command %s", *av);
+		break;
+	}
 }
 
 static void
-table_fill_xentry(char *arg, ipfw_table_xentry *xent)
+table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint32_t set, uint16_t uidx)
 {
-	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;
+	ntlv->head.type = IPFW_TLV_TBL_NAME;
+	ntlv->head.length = sizeof(ipfw_obj_ntlv);
+	ntlv->idx = uidx;
+	ntlv->set = set;
+	strlcpy(ntlv->name, name, sizeof(ntlv->name));
+}
 
-	/* 
-	 * 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);
-		}
+static void
+table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
+{
 
-		if (inet_pton(AF_INET, arg, paddr) == 1) {
-			if (p != NULL && mask > 32)
-				errx(EX_DATAERR, "bad IPv4 mask width: %s",
-				    p + 1);
+	oh->set = i->set;
+	oh->idx = 1;
+	table_fill_ntlv(&oh->ntlv, i->tablename, oh->set, 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);
+static struct _s_x tablenewcmds[] = {
+      { "type",		TOK_TYPE},
+      { "valtype",	TOK_VALTYPE },
+      { "algo",		TOK_ALGO },
+      { NULL, 0 }
+};
 
-			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);
-			}
-		}
-	}
+/*
+ * Creates new table
+ *
+ * ipfw table NAME create [ type { cidr | iface | u32 } ]
+ *     [ valtype { number | ip | dscp } ]
+ *     [ algo algoname ]
+ *
+ * Request: [ ipfw_obj_header ipfw_xtable_info ]
+ */
+static void
+table_create(ipfw_obj_header *oh, int ac, char *av[])
+{
+	ipfw_xtable_info xi;
+	int error, tcmd, val;
+	size_t sz;
+	char tbuf[128];
 
-	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;
-	}
+	sz = sizeof(tbuf);
+	memset(&xi, 0, sizeof(xi));
 
-	if (type == 0) {
-		if (lookup_host(arg, (struct in_addr *)paddr) != 0)
-			errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+	/* Set some defaults to preserve compability */
+	xi.type = IPFW_TABLE_CIDR;
+	xi.vtype = IPFW_VTYPE_U32;
+
+	while (ac > 0) {
+		if ((tcmd = match_token(tablenewcmds, *av)) == -1)
+			errx(EX_USAGE, "unknown option: %s", *av);
+		ac--; av++;
 
-		masklen = 32;
-		type = IPFW_TABLE_CIDR;
-		addrlen = sizeof(struct in_addr);
+		switch (tcmd) {
+		case TOK_TYPE:
+			NEED1("table type required");
+			val = match_token(tabletypes, *av);
+			if (val != -1) {
+				printf("av %s type %d\n", *av, xi.type);
+				xi.type = val;
+				ac--; av++;
+				break;
+			}
+			concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", ");
+			errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s",
+			    *av, tbuf);
+			break;
+		case TOK_VALTYPE:
+			NEED1("table value type required");
+			val = match_token(tablevaltypes, *av);
+			if (val != -1) {
+				xi.vtype = val;
+				ac--; av++;
+				break;
+			}
+			concat_tokens(tbuf, sizeof(tbuf), tablevaltypes, ", ");
+			errx(EX_USAGE, "Unknown value type: %s. Supported: %s",
+			    *av, tbuf);
+			break;
+		case TOK_ALGO:
+			NEED1("table algorithm name required");
+			if (strlen(*av) > sizeof(xi.algoname))
+				errx(EX_USAGE, "algorithm name too long");
+			strlcpy(xi.algoname, *av, sizeof(xi.algoname));
+			ac--; av++;
+			break;
+		}
 	}
 
-	xent->type = type;
-	xent->masklen = masklen;
-	xent->len = offsetof(ipfw_table_xentry, k) + addrlen;
+	if ((error = table_do_create(oh, &xi)) != 0)
+		err(EX_OSERR, "Table creation failed");
 }
 
-static void
-table_fill_ntlv(ipfw_obj_ntlv *ntlv, char *name, uint16_t uidx)
+/*
+ * Creates new table
+ *
+ * Request: [ ipfw_obj_header ipfw_xtable_info ]
+ *
+ * Returns 0 on success.
+ */
+static int
+table_do_create(ipfw_obj_header *oh, ipfw_xtable_info *i)
 {
+	char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
+	int error;
 
-	ntlv->head.type = IPFW_TLV_TBL_NAME;
-	ntlv->head.length = sizeof(ipfw_obj_ntlv);
-	ntlv->idx = uidx;
-	strlcpy(ntlv->name, name, sizeof(ntlv->name));
-}
+	memcpy(tbuf, oh, sizeof(*oh));
+	memcpy(tbuf + sizeof(*oh), i, sizeof(*i));
+	oh = (ipfw_obj_header *)tbuf;
 
-static void
-table_fill_objheader(ipfw_obj_header *oh, ipfw_xtable_info *i)
-{
+	error = do_set3(IP_FW_TABLE_XCREATE, &oh->opheader, sizeof(tbuf));
 
-	oh->set = i->set;
-	oh->idx = 1;
-	table_fill_ntlv(&oh->ntlv, i->tablename, 1);
+	return (error);
 }
 
 /*
- * Destroys given table @name in given @set.
+ * Destroys given table specified by @oh->ntlv.
  * Returns 0 on success.
  */
 static int
-table_destroy(char *name, uint32_t set)
+table_destroy(ipfw_obj_header *oh)
 {
-	ipfw_obj_header oh;
 
-	memset(&oh, 0, sizeof(oh));
-	oh.idx = 1;
-	table_fill_ntlv(&oh.ntlv, name, 1);
-	if (do_set3(IP_FW_TABLE_XDESTROY, &oh.opheader, sizeof(oh)) != 0)
+	if (do_set3(IP_FW_TABLE_XDESTROY, &oh->opheader, sizeof(*oh)) != 0)
 		return (-1);
 
 	return (0);
 }
 
 /*
- * Flushes given table @name in given @set.
+ * Flushes given table specified by @oh->ntlv.
  * Returns 0 on success.
  */
 static int
-table_flush(char *name, uint32_t set)
+table_flush(ipfw_obj_header *oh)
 {
-	ipfw_obj_header oh;
 
-	memset(&oh, 0, sizeof(oh));
-	oh.idx = 1;
-	table_fill_ntlv(&oh.ntlv, name, 1);
-	if (do_set3(IP_FW_TABLE_XFLUSH, &oh.opheader, sizeof(oh)) != 0)
+	if (do_set3(IP_FW_TABLE_XFLUSH, &oh->opheader, sizeof(*oh)) != 0)
 		return (-1);
 
 	return (0);
 }
 
 /*
- * Retrieves info for given table @name in given @set and stores
+ * Retrieves table in given table specified by @oh->ntlv.
  * it inside @i.
  * Returns 0 on success.
  */
 static int
-table_get_info(char *name, uint32_t set, ipfw_xtable_info *i)
+table_get_info(ipfw_obj_header *oh, ipfw_xtable_info *i)
 {
-	char tbuf[sizeof(ipfw_obj_header)+sizeof(ipfw_xtable_info)];
-	ipfw_obj_header *oh;
+	char tbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_xtable_info)];
+	int error;
 	size_t sz;
 
 	sz = sizeof(tbuf);
 	memset(tbuf, 0, sizeof(tbuf));
+	memcpy(tbuf, oh, sizeof(*oh));
 	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_TABLE_XINFO, &oh->opheader, &sz) < 0)
-		return (-1);
+	if ((error = do_get3(IP_FW_TABLE_XINFO, &oh->opheader, &sz)) != 0)
+		return (error);
 
 	if (sz < sizeof(tbuf))
-		return (-1);
+		return (EINVAL);
 
 	*i = *(ipfw_xtable_info *)(oh + 1);
 
@@ -394,21 +403,16 @@ table_get_info(char *name, uint32_t set,
 static int
 table_show_info(ipfw_xtable_info *i, void *arg)
 {
-	char *type;
+	const char *ttype, *vtype;
 
 	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);
+	if ((ttype = match_value(tabletypes, i->type)) == NULL)
+		ttype = "unknown";
+	if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL)
+		vtype = "unknown";
+
+	printf(" type: %s, kindex: %d\n", ttype, i->kidx);
+	printf(" valtype: %s, algorithm: %s\n", vtype, i->algoname);
 	printf(" references: %u\n", i->refcnt);
 	printf(" items: %u, size: %u\n", i->count, i->size);
 
@@ -426,7 +430,7 @@ table_show_one(ipfw_xtable_info *i, void
 {
 	ipfw_obj_header *oh;
 
-	if ((oh = malloc(i->size)) == NULL)
+	if ((oh = calloc(1, i->size)) == NULL)
 		return (ENOMEM);
 
 	if (table_get_list(i, oh) == 0)
@@ -439,11 +443,244 @@ table_show_one(ipfw_xtable_info *i, void
 static int
 table_flush_one(ipfw_xtable_info *i, void *arg)
 {
+	ipfw_obj_header *oh;
+
+	oh = (ipfw_obj_header *)arg;
+
+	table_fill_ntlv(&oh->ntlv, i->tablename, i->set, 1);
+
+	return (table_flush(oh));
+}
+
+static int
+table_do_modify_record(int cmd, ipfw_obj_header *oh,
+    ipfw_obj_tentry *tent, int update)
+{
+	char xbuf[sizeof(ipfw_obj_header) + sizeof(ipfw_obj_tentry)];
+	int error;
+
+	memset(xbuf, 0, sizeof(xbuf));
+	memcpy(xbuf, oh, sizeof(*oh));
+	oh = (ipfw_obj_header *)xbuf;
+	oh->opheader.version = 1;
+
+	memcpy(oh + 1, tent, sizeof(*tent));
+	tent = (ipfw_obj_tentry *)(oh + 1);
+	if (update != 0)
+		tent->flags |= IPFW_TF_UPDATE;
+	tent->head.length = sizeof(ipfw_obj_tentry);
+
+	error = do_set3(cmd, &oh->opheader, sizeof(xbuf));
+
+	return (error);
+}
+
+static void
+table_modify_record(ipfw_obj_header *oh, int ac, char *av[], int add, int update)
+{
+	ipfw_obj_tentry tent;
+	uint8_t type, vtype;
+	int cmd;
+	char *texterr;
+
+	if (ac == 0)
+		errx(EX_USAGE, "address required");
+	
+	memset(&tent, 0, sizeof(tent));
+	tent.head.length = sizeof(tent);
+	tent.idx = 1;
+
+	tentry_fill_key(oh, &tent, *av, &type, &vtype);
+	oh->ntlv.type = type;
+	ac--; av++;
+
+	if (add != 0) {
+		if (ac > 0)
+			tentry_fill_value(oh, &tent, *av, type, vtype);
+		cmd = IP_FW_TABLE_XADD;
+		texterr = "setsockopt(IP_FW_TABLE_XADD)";
+	} else {
+		cmd = IP_FW_TABLE_XDEL;
+		texterr = "setsockopt(IP_FW_TABLE_XDEL)";
+	}
 
-	return (table_flush(i->tablename, i->set));
+	if (table_do_modify_record(cmd, oh, &tent, update) != 0)
+		err(EX_OSERR, "%s", texterr);
 }
 
 
+static void
+tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type)
+{
+	char *p;
+	int mask, af;
+	struct in6_addr *paddr;
+	uint32_t key, *pkey;
+	int masklen;
+
+	masklen = 0;
+	af = 0;
+	paddr = (struct in6_addr *)&tentry->k;
+
+	switch (type) {
+	case IPFW_TABLE_CIDR:
+		/* 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);
+
+			masklen = p ? mask : 32;
+			af = AF_INET;
+		} 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);
+
+			masklen = p ? mask : 128;
+			af = AF_INET6;
+		} else {
+			/* Assume FQDN */
+			if (lookup_host(arg, (struct in_addr *)paddr) != 0)
+				errx(EX_NOHOST, "hostname ``%s'' unknown", arg);
+
+			masklen = 32;
+			type = IPFW_TABLE_CIDR;
+			af = AF_INET;
+		}
+		break;
+	case IPFW_TABLE_INTERFACE:
+		/* Assume interface name. Copy significant data only */
+		mask = MIN(strlen(arg), IF_NAMESIZE - 1);
+		memcpy(paddr, arg, mask);
+		/* Set mask to exact match */
+		masklen = 8 * IF_NAMESIZE;
+		break;
+	case IPFW_TABLE_U32:
+		/* Port or any other key */
+		key = strtol(arg, &p, 10);
+		if (*p != '\0')
+			errx(EX_DATAERR, "Invalid number: %s", arg);
+
+		pkey = (uint32_t *)paddr;
+		*pkey = key;
+		masklen = 32;
+		break;
+	default:
+		errx(EX_DATAERR, "Unsupported table type: %d", type);
+	}
+
+	tentry->subtype = af;
+	tentry->masklen = masklen;
+}
+
+static void
+tentry_fill_key(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *key,
+    uint8_t *ptype, uint8_t *pvtype)
+{
+	ipfw_xtable_info xi;
+	uint8_t type, vtype;
+	int error;
+
+	type = 0;
+	vtype = 0;
+
+	/*
+	 * Compability layer. Try to interpret data as CIDR first.
+	 */
+	if (inet_pton(AF_INET, key, &tent->k.addr6) == 1 ||
+	    inet_pton(AF_INET6, key, &tent->k.addr6) == 1) {
+		/* OK Prepare and send */
+		type = IPFW_TABLE_CIDR;
+	} else {
+
+		/*
+		 * Non-CIDR of FQDN hostname. Ask kernel
+		 * about given table.
+		 */
+		error = table_get_info(oh, &xi);
+		if (error == ESRCH)
+			errx(EX_USAGE, "Table %s does not exist, cannot intuit "
+			    "key type", oh->ntlv.name);
+		else if (error != 0)
+			errx(EX_OSERR, "Error requesting table %s info",
+			    oh->ntlv.name);
+
+		/* Table found. */
+		type = xi.type;
+		vtype = xi.vtype;
+	}
+
+	tentry_fill_key_type(key, tent, type);
+
+	*ptype = type;
+	*pvtype = vtype;
+}
+
+static void
+tentry_fill_value(ipfw_obj_header *oh, ipfw_obj_tentry *tent, char *arg,
+    uint8_t type, uint8_t vtype)
+{
+	ipfw_xtable_info xi;
+	int error;
+	int code;
+	char *p;
+
+	if (vtype == 0) {
+		/* Format type is unknown, ask kernel */
+		error = table_get_info(oh, &xi);
+		if (error == ESRCH) {
+
+			/*
+			 * XXX: This one may break some scripts.
+			 * Change this behavior for MFC.
+			 */
+			errx(EX_USAGE, "Table %s does not exist. Unable to "
+			    "guess value format.",  oh->ntlv.name);
+		} else if (error != 0)
+			errx(EX_OSERR, "Error requesting table %s info",
+			    oh->ntlv.name);
+
+		vtype = xi.vtype;
+	}
+
+	switch (vtype) {
+	case IPFW_VTYPE_U32:
+		tent->value = strtoul(arg, &p, 0);
+		if (*p != '\0')
+			errx(EX_USAGE, "Invalid number: %s", arg);
+		break;
+	case IPFW_VTYPE_IP:
+		if (inet_pton(AF_INET, arg, &tent->value) == 1)
+			break;
+		/* Try hostname */
+		if (lookup_host(arg, (struct in_addr *)&tent->value) != 0)
+			errx(EX_USAGE, "Invalid IPv4 address: %s", arg);
+		break;
+	case IPFW_VTYPE_DSCP:
+		if (isalpha(*arg)) {
+			if ((code = match_token(f_ipdscp, arg)) == -1)
+				errx(EX_DATAERR, "Unknown DSCP code");
+		} else {
+			code = strtoul(arg, NULL, 10);
+			if (code < 0 || code > 63)
+				errx(EX_DATAERR, "Invalid DSCP value");
+		}
+		tent->value = code;
+		break;
+	default:
+		errx(EX_OSERR, "Unsupported format type %d", vtype);
+	}
+}
+
 /*
  * Compare table names.
  * Honor number comparison.
@@ -668,14 +905,11 @@ table_check_name(char *tablename)
 	/*
 	 * Check if tablename is null-terminated and contains
 	 * valid symbols only. Valid mask is:
-	 * [a-zA-Z\-\.][a-zA-Z0-9\-_\.]{0,62}

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


More information about the svn-src-projects mailing list