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

Alexander V. Chernikov melifaro at FreeBSD.org
Thu Jul 31 20:08:21 UTC 2014


Author: melifaro
Date: Thu Jul 31 20:08:19 2014
New Revision: 269348
URL: http://svnweb.freebsd.org/changeset/base/269348

Log:
  * Add new "flow" table type to support N=1..5-tuple lookups
  * Add "flow:hash" algorithm
  
  Kernel changes:
  * Add O_IP_FLOW_LOOKUP opcode to support "flow" lookups
  * Add IPFW_TABLE_FLOW table type
  * Add "struct tflow_entry" as strage for 6-tuple flows
  * Add "flow:hash" algorithm. Basically it is auto-growing chained hash table.
    Additionally, we store mask of fields we need to compare in each instance/
  
  * Increase ipfw_obj_tentry size by adding struct tflow_entry
  * Add per-algorithm stat (ifpw_ta_tinfo) to ipfw_xtable_info
  * Increase algoname length: 32 -> 64 (algo options passed there as string)
  * Assume every table type can be customized by flags, use u8 to store "tflags" field.
  * Simplify ipfw_find_table_entry() by providing @tentry directly to algo callback.
  * Fix bug in cidr:chash resize procedure.
  
  Userland changes:
  * add "flow table(NAME)" syntax to support n-tuple checking tables.
  * make fill_flags() separate function to ease working with _s_x arrays
  * change "table info" output to reflect longer "type" fields
  
  Syntax:
  ipfw table fl2 create type flow:[src-ip][,proto][,src-port][,dst-ip][dst-port] [algo flow:hash]
  
  Examples:
  
  0:02 [2] zfscurr0# ipfw table fl2 create type flow:src-ip,proto,dst-port algo flow:hash
  0:02 [2] zfscurr0# ipfw table fl2 info
  +++ table(fl2), set(0) +++
   kindex: 0, type: flow:src-ip,proto,dst-port
   valtype: number, references: 0
   algorithm: flow:hash
   items: 0, size: 280
  0:02 [2] zfscurr0# ipfw table fl2 add 2a02:6b8::333,tcp,443 45000
  0:02 [2] zfscurr0# ipfw table fl2 add 10.0.0.92,tcp,80 22000
  0:02 [2] zfscurr0# ipfw table fl2 list
  +++ table(fl2), set(0) +++
  2a02:6b8::333,6,443 45000
  10.0.0.92,6,80 22000
  0:02 [2] zfscurr0# ipfw add 200 count tcp from me to 78.46.89.105 80 flow 'table(fl2)'
  00200 count tcp from me to 78.46.89.105 dst-port 80 flow table(fl2)
  0:03 [2] zfscurr0# ipfw show
  00200   0     0 count tcp from me to 78.46.89.105 dst-port 80 flow table(fl2)
  65535 617 59416 allow ip from any to any
  0:03 [2] zfscurr0# telnet -s 10.0.0.92 78.46.89.105 80
  Trying 78.46.89.105...
  ..
  0:04 [2] zfscurr0# ipfw show
  00200   5   272 count tcp from me to 78.46.89.105 dst-port 80 flow table(fl2)
  65535 682 66733 allow ip from any to any

Modified:
  projects/ipfw/sbin/ipfw/ipfw2.c
  projects/ipfw/sbin/ipfw/ipfw2.h
  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_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 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Thu Jul 31 20:08:19 2014	(r269348)
@@ -364,6 +364,7 @@ static struct _s_x rule_options[] = {
 	{ "src-ipv6",		TOK_SRCIP6},
 	{ "src-ip6",		TOK_SRCIP6},
 	{ "lookup",		TOK_LOOKUP},
+	{ "flow",		TOK_FLOW},
 	{ "//",			TOK_COMMENT },
 
 	{ "not",		TOK_NOT },		/* pseudo option */
@@ -707,6 +708,54 @@ concat_tokens(char *buf, size_t bufsize,
 }
 
 /*
+ * helper function to process a set of flags and set bits in the
+ * appropriate masks.
+ */
+void
+fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear)
+{
+	char *q;	/* points to the separator */
+	int val;
+	uint8_t *which;	/* mask we are working on */
+
+	while (p && *p) {
+		if (*p == '!') {
+			p++;
+			which = clear;
+		} else
+			which = set;
+		q = strchr(p, ',');
+		if (q)
+			*q++ = '\0';
+		val = match_token(flags, p);
+		if (val <= 0)
+			errx(EX_DATAERR, "invalid flag %s", p);
+		*which |= (uint8_t)val;
+		p = q;
+	}
+}
+
+void
+print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set)
+{
+	char const *comma = "";
+	int i, l;
+
+	for (i = 0; list[i].x != 0; i++) {
+		if ((set & list[i].x) == 0)
+			continue;
+		
+		set &= ~list[i].x;
+		l = snprintf(buf, sz, "%s%s", comma, list[i].s);
+		if (l >= sz)
+			return;
+		comma = ",";
+		buf += l;
+		sz -=l;
+	}
+}
+
+/*
  * _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
  * of the second.  A warning is printed to stderr in the case that the
@@ -1087,6 +1136,7 @@ print_flags(char const *name, ipfw_insn 
 	}
 }
 
+
 /*
  * Print the ip address contained in a command.
  */
@@ -1795,6 +1845,18 @@ show_static_rule(struct cmdline_opts *co
 
 				break;
 			    }
+			case O_IP_FLOW_LOOKUP:
+			    {
+				char *t;
+
+				t = table_search_ctlv(fo->tstate, cmd->arg1);
+				printf(" flow table(%s", t);
+				if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32))
+					printf(",%u",
+					    ((ipfw_insn_u32 *)cmd)->d[0]);
+				printf(")");
+				break;
+			    }
 			case O_IPID:
 				if (F_LEN(cmd) == 1)
 				    printf(" ipid %u", cmd->arg1 );
@@ -2660,6 +2722,33 @@ pack_table(struct tidx *tstate, char *na
 	return (ntlv->idx);
 }
 
+static void
+fill_table(ipfw_insn *cmd, char *av, uint8_t opcode, struct tidx *tstate)
+{
+	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
+	uint16_t uidx;
+	char *p;
+
+	if ((p = strchr(av + 6, ')')) == NULL)
+		errx(EX_DATAERR, "forgotten parenthesis: '%s'", av);
+	*p = '\0';
+	p = strchr(av + 6, ',');
+	if (p)
+		*p++ = '\0';
+
+	if ((uidx = pack_table(tstate, av + 6, 0)) == 0)
+		errx(EX_DATAERR, "Invalid table name: %s", av + 6);
+
+	cmd->opcode = opcode;
+	cmd->arg1 = uidx;
+	if (p) {
+		cmd->len |= F_INSN_SIZE(ipfw_insn_u32);
+		d[0] = strtoul(p, NULL, 0);
+	} else
+		cmd->len |= F_INSN_SIZE(ipfw_insn);
+}
+
+
 /*
  * fills the addr and mask fields in the instruction as appropriate from av.
  * Update length as appropriate.
@@ -2676,8 +2765,6 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int
 {
 	int len = 0;
 	uint32_t *d = ((ipfw_insn_u32 *)cmd)->d;
-	uint16_t uidx;
-	char *p;
 
 	cmd->o.len &= ~F_LEN_MASK;	/* zero len */
 
@@ -2690,23 +2777,7 @@ fill_ip(ipfw_insn_ip *cmd, char *av, int
 	}
 
 	if (strncmp(av, "table(", 6) == 0) {
-		if ((p = strchr(av + 6, ')')) == NULL)
-			errx(EX_DATAERR, "forgotten parenthesis: '%s'", av);
-		*p = '\0';
-		p = strchr(av + 6, ',');
-		if (p)
-			*p++ = '\0';
-
-		if ((uidx = pack_table(tstate, av + 6, 0)) == 0)
-			errx(EX_DATAERR, "Invalid table name: %s", av + 6);
-
-		cmd->o.opcode = O_IP_DST_LOOKUP;
-		cmd->o.arg1 = uidx;
-		if (p) {
-			cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32);
-			d[0] = strtoul(p, NULL, 0);
-		} else
-			cmd->o.len |= F_INSN_SIZE(ipfw_insn);
+		fill_table(&cmd->o, av, O_IP_DST_LOOKUP, tstate);
 		return;
 	}
 
@@ -2887,35 +2958,14 @@ n2mask(struct in6_addr *mask, int n)
 	return;
 }
 
-/*
- * helper function to process a set of flags and set bits in the
- * appropriate masks.
- */
 static void
-fill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode,
+fill_flags_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode,
 	struct _s_x *flags, char *p)
 {
-	uint8_t set=0, clear=0;
+	uint8_t set = 0, clear = 0;
 
-	while (p && *p) {
-		char *q;	/* points to the separator */
-		int val;
-		uint8_t *which;	/* mask we are working on */
+	fill_flags(flags, p, &set, &clear);
 
-		if (*p == '!') {
-			p++;
-			which = &clear;
-		} else
-			which = &set;
-		q = strchr(p, ',');
-		if (q)
-			*q++ = '\0';
-		val = match_token(flags, p);
-		if (val <= 0)
-			errx(EX_DATAERR, "invalid flag %s", p);
-		*which |= (uint8_t)val;
-		p = q;
-	}
 	cmd->opcode = opcode;
 	cmd->len =  (cmd->len & (F_NOT | F_OR)) | 1;
 	cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8);
@@ -4087,13 +4137,13 @@ read_options:
 
 		case TOK_IPOPTS:
 			NEED1("missing argument for ipoptions");
-			fill_flags(cmd, O_IPOPT, f_ipopts, *av);
+			fill_flags_cmd(cmd, O_IPOPT, f_ipopts, *av);
 			av++;
 			break;
 
 		case TOK_IPTOS:
 			NEED1("missing argument for iptos");
-			fill_flags(cmd, O_IPTOS, f_iptos, *av);
+			fill_flags_cmd(cmd, O_IPTOS, f_iptos, *av);
 			av++;
 			break;
 
@@ -4171,7 +4221,7 @@ read_options:
 
 		case TOK_TCPOPTS:
 			NEED1("missing argument for tcpoptions");
-			fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av);
+			fill_flags_cmd(cmd, O_TCPOPTS, f_tcpopts, *av);
 			av++;
 			break;
 
@@ -4198,7 +4248,7 @@ read_options:
 		case TOK_TCPFLAGS:
 			NEED1("missing argument for tcpflags");
 			cmd->opcode = O_TCPFLAGS;
-			fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av);
+			fill_flags_cmd(cmd, O_TCPFLAGS, f_tcpflags, *av);
 			av++;
 			break;
 
@@ -4407,6 +4457,14 @@ read_options:
 			av++;
 		    }
 			break;
+		case TOK_FLOW:
+			NEED1("missing table name");
+			if (strncmp(*av, "table(", 6) != 0)
+				errx(EX_DATAERR,
+				    "enclose table name into \"table()\"");
+			fill_table(cmd, *av, O_IP_FLOW_LOOKUP, tstate);
+			av++;
+			break;
 
 		default:
 			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);

Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h	Thu Jul 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sbin/ipfw/ipfw2.h	Thu Jul 31 20:08:19 2014	(r269348)
@@ -217,6 +217,7 @@ enum tokens {
 	TOK_DEL,
 	TOK_VALTYPE,
 	TOK_ALGO,
+	TOK_FLOW,
 };
 /*
  * the following macro returns an error message if we run out of
@@ -253,6 +254,10 @@ int match_token(struct _s_x *table, char
 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);
+void fill_flags(struct _s_x *flags, char *p, uint8_t *set, uint8_t *clear);
+void print_flags(char const *name, struct _s_x *list, uint8_t set,
+    uint8_t clear);
+void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint8_t set);
 
 struct _ip_fw3_opheader;
 int do_cmd(int optname, void *optval, uintptr_t optlen);

Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c	Thu Jul 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sbin/ipfw/tables.c	Thu Jul 31 20:08:19 2014	(r269348)
@@ -83,6 +83,7 @@ static struct _s_x tabletypes[] = {
       { "cidr",		IPFW_TABLE_CIDR },
       { "iface",	IPFW_TABLE_INTERFACE },
       { "number",	IPFW_TABLE_NUMBER },
+      { "flow",		IPFW_TABLE_FLOW },
       { NULL, 0 }
 };
 
@@ -256,6 +257,59 @@ static struct _s_x tablenewcmds[] = {
       { NULL, 0 }
 };
 
+static struct _s_x flowtypecmds[] = {
+      { "src-ip",	IPFW_TFFLAG_SRCIP },
+      { "proto",	IPFW_TFFLAG_PROTO },
+      { "src-port",	IPFW_TFFLAG_SRCPORT },
+      { "dst-ip",	IPFW_TFFLAG_DSTIP },
+      { "dst-port",	IPFW_TFFLAG_DSTPORT },
+      { NULL, 0 }
+};
+
+int
+table_parse_type(uint8_t ttype, char *p, uint8_t *tflags)
+{
+	uint8_t fset, fclear;
+
+	/* Parse type options */
+	switch(ttype) {
+	case IPFW_TABLE_FLOW:
+		fset = fclear = 0;
+		fill_flags(flowtypecmds, p, &fset,
+		    &fclear);
+		*tflags = fset;
+		break;
+	default:
+		return (EX_USAGE);
+	}
+
+	return (0);
+}
+
+void
+table_print_type(char *tbuf, size_t size, uint8_t type, uint8_t tflags)
+{
+	const char *tname;
+	int l;
+
+	if ((tname = match_value(tabletypes, type)) == NULL)
+		tname = "unknown";
+
+	l = snprintf(tbuf, size, "%s", tname);
+	tbuf += l;
+	size -= l;
+
+	switch(type) {
+	case IPFW_TABLE_FLOW:
+		if (tflags != 0) {
+			*tbuf++ = ':';
+			l--;
+			print_flags_buffer(tbuf, size, flowtypecmds, tflags);
+		}
+		break;
+	}
+}
+
 /*
  * Creates new table
  *
@@ -271,6 +325,7 @@ table_create(ipfw_obj_header *oh, int ac
 	ipfw_xtable_info xi;
 	int error, tcmd, val;
 	size_t sz;
+	char *p;
 	char tbuf[128];
 
 	sz = sizeof(tbuf);
@@ -288,15 +343,25 @@ table_create(ipfw_obj_header *oh, int ac
 		switch (tcmd) {
 		case TOK_TYPE:
 			NEED1("table type required");
+			/* Type may have suboptions after ':' */
+			if ((p = strchr(*av, ':')) != NULL)
+				*p++ = '\0';
 			val = match_token(tabletypes, *av);
-			if (val != -1) {
-				xi.type = val;
-				ac--; av++;
-				break;
+			if (val == -1) {
+				concat_tokens(tbuf, sizeof(tbuf), tabletypes,
+				    ", ");
+				errx(EX_USAGE,
+				    "Unknown tabletype: %s. Supported: %s",
+				    *av, tbuf);
 			}
-			concat_tokens(tbuf, sizeof(tbuf), tabletypes, ", ");
-			errx(EX_USAGE, "Unknown tabletype: %s. Supported: %s",
-			    *av, tbuf);
+			xi.type = val;
+			if (p != NULL) {
+				error = table_parse_type(val, p, &xi.tflags);
+				if (error != 0)
+					errx(EX_USAGE,
+					    "Unsupported suboptions: %s", p);
+			}
+			ac--; av++;
 			break;
 		case TOK_VALTYPE:
 			NEED1("table value type required");
@@ -408,15 +473,15 @@ table_get_info(ipfw_obj_header *oh, ipfw
 static int
 table_show_info(ipfw_xtable_info *i, void *arg)
 {
-	const char *ttype, *vtype;
+	const char *vtype;
+	char ttype[64];
 
-	printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
-	if ((ttype = match_value(tabletypes, i->type)) == NULL)
-		ttype = "unknown";
+	table_print_type(ttype, sizeof(ttype), i->type, i->tflags);
 	if ((vtype = match_value(tablevaltypes, i->vtype)) == NULL)
 		vtype = "unknown";
 
-	printf(" type: %s, kindex: %d\n", ttype, i->kidx);
+	printf("--- table(%s), set(%u) ---\n", i->tablename, i->set);
+	printf(" kindex: %d, type: %s\n", i->kidx, ttype);
 	printf(" valtype: %s, references: %u\n", vtype, i->refcnt);
 	printf(" algorithm: %s\n", i->algoname);
 	printf(" items: %u, size: %u\n", i->count, i->size);
@@ -575,12 +640,15 @@ table_lookup(ipfw_obj_header *oh, int ac
 {
 	ipfw_obj_tentry xtent;
 	ipfw_xtable_info xi;
+	char key[64];
 	int error;
 
 	if (ac == 0)
 		errx(EX_USAGE, "address required");
 
-	error = table_do_lookup(oh, *av, &xi, &xtent);
+	strlcpy(key, *av, sizeof(key));
+
+	error = table_do_lookup(oh, key, &xi, &xtent);
 
 	switch (error) {
 	case 0:
@@ -600,12 +668,17 @@ table_lookup(ipfw_obj_header *oh, int ac
 }
 
 static void
-tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type)
+tentry_fill_key_type(char *arg, ipfw_obj_tentry *tentry, uint8_t type,
+    uint8_t tflags)
 {
-	char *p;
+	char *p, *pp;
 	int mask, af;
-	struct in6_addr *paddr;
+	struct in6_addr *paddr, tmp;
+	struct tflow_entry *tfe;
 	uint32_t key, *pkey;
+	uint16_t port;
+	struct protoent *pent;
+	struct servent *sent;
 	int masklen;
 
 	masklen = 0;
@@ -664,6 +737,117 @@ tentry_fill_key_type(char *arg, ipfw_obj
 		*pkey = key;
 		masklen = 32;
 		break;
+	case IPFW_TABLE_FLOW:
+		/* Assume [src-ip][,proto][,src-port][,dst-ip][,dst-port] */
+		tfe = &tentry->k.flow;
+		af = 0;
+
+		/* Handle <ipv4|ipv6>*/
+		if ((tflags & IPFW_TFFLAG_SRCIP) != 0) {
+			if ((p = strchr(arg, ',')) != NULL)
+				*p++ = '\0';
+			/* Determine family using temporary storage */
+			if (inet_pton(AF_INET, arg, &tmp) == 1) {
+				if (af != 0 && af != AF_INET)
+					errx(EX_DATAERR,
+					    "Inconsistent address family\n");
+				af = AF_INET;
+				memcpy(&tfe->a.a4.sip, &tmp, 4);
+			} else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
+				if (af != 0 && af != AF_INET6)
+					errx(EX_DATAERR,
+					    "Inconsistent address family\n");
+				af = AF_INET6;
+				memcpy(&tfe->a.a6.sip6, &tmp, 16);
+			}
+
+			arg = p;
+		}
+
+		/* Handle <proto-num|proto-name> */
+		if ((tflags & IPFW_TFFLAG_PROTO) != 0) {
+			if ((p = strchr(arg, ',')) != NULL)
+				*p++ = '\0';
+
+			key = strtol(arg, &pp, 10);
+			if (*pp != '\0') {
+				if ((pent = getprotobyname(arg)) == NULL)
+					errx(EX_DATAERR, "Unknown proto: %s",
+					    arg);
+				else
+					key = pent->p_proto;
+			}
+			
+			if (key > 255)
+				errx(EX_DATAERR, "Bad protocol number: %u",key);
+
+			tfe->proto = key;
+
+			arg = p;
+		}
+
+		/* Handle <port-num|service-name> */
+		if ((tflags & IPFW_TFFLAG_SRCPORT) != 0) {
+			if ((p = strchr(arg, ',')) != NULL)
+				*p++ = '\0';
+
+			if ((port = htons(strtol(arg, NULL, 10))) == 0) {
+				if ((sent = getservbyname(arg, NULL)) == NULL)
+					errx(EX_DATAERR, "Unknown service: %s",
+					    arg);
+				else
+					key = sent->s_port;
+			}
+			
+			tfe->sport = port;
+
+			arg = p;
+		}
+
+		/* Handle <ipv4|ipv6>*/
+		if ((tflags & IPFW_TFFLAG_DSTIP) != 0) {
+			if ((p = strchr(arg, ',')) != NULL)
+				*p++ = '\0';
+			/* Determine family using temporary storage */
+			if (inet_pton(AF_INET, arg, &tmp) == 1) {
+				if (af != 0 && af != AF_INET)
+					errx(EX_DATAERR,
+					    "Inconsistent address family");
+				af = AF_INET;
+				memcpy(&tfe->a.a4.dip, &tmp, 4);
+			} else if (inet_pton(AF_INET6, arg, &tmp) == 1) {
+				if (af != 0 && af != AF_INET6)
+					errx(EX_DATAERR,
+					    "Inconsistent address family");
+				af = AF_INET6;
+				memcpy(&tfe->a.a6.dip6, &tmp, 16);
+			}
+
+			arg = p;
+		}
+
+		/* Handle <port-num|service-name> */
+		if ((tflags & IPFW_TFFLAG_DSTPORT) != 0) {
+			if ((p = strchr(arg, ',')) != NULL)
+				*p++ = '\0';
+
+			if ((port = htons(strtol(arg, NULL, 10))) == 0) {
+				if ((sent = getservbyname(arg, NULL)) == NULL)
+					errx(EX_DATAERR, "Unknown service: %s",
+					    arg);
+				else
+					key = sent->s_port;
+			}
+			
+			tfe->dport = port;
+
+			arg = p;
+		}
+
+		tfe->af = af;
+
+		break;
+	
 	default:
 		errx(EX_DATAERR, "Unsupported table type: %d", type);
 	}
@@ -676,11 +860,12 @@ 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;
+	uint8_t type, tflags, vtype;
 	int error;
 	char *del;
 
 	type = 0;
+	tflags = 0;
 	vtype = 0;
 
 	error = table_get_info(oh, xi);
@@ -688,6 +873,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipf
 	if (error == 0) {
 		/* Table found. */
 		type = xi->type;
+		tflags = xi->tflags;
 		vtype = xi->vtype;
 	} else {
 		if (error != ESRCH)
@@ -718,7 +904,7 @@ tentry_fill_key(ipfw_obj_header *oh, ipf
 			*del = '/';
 	}
 
-	tentry_fill_key_type(key, tent, type);
+	tentry_fill_key_type(key, tent, type, tflags);
 
 	*ptype = type;
 	*pvtype = vtype;
@@ -874,41 +1060,75 @@ table_show_list(ipfw_obj_header *oh, int
 static void
 table_show_entry(ipfw_xtable_info *i, ipfw_obj_tentry *tent)
 {
-	char tbuf[128];
+	char *comma, tbuf[128], pval[32];
+	void *paddr;
 	uint32_t tval;
+	struct tflow_entry *tfe;
 
 	tval = tent->value;
 
+	if (co.do_value_as_ip) {
+		tval = htonl(tval);
+		inet_ntop(AF_INET, &tval, pval, sizeof(pval));
+	} else
+		snprintf(pval, sizeof(pval), "%u", tval);
+
 	switch (i->type) {
 	case IPFW_TABLE_CIDR:
 		/* IPv4 or IPv6 prefixes */
 		inet_ntop(tent->subtype, &tent->k, tbuf, sizeof(tbuf));
-
-		if (co.do_value_as_ip) {
-			tval = htonl(tval);
-			printf("%s/%u %s\n", tbuf, tent->masklen,
-			    inet_ntoa(*(struct in_addr *)&tval));
-		} else
-			printf("%s/%u %u\n", tbuf, tent->masklen, tval);
+		printf("%s/%u %s\n", tbuf, tent->masklen, pval);
 		break;
 	case IPFW_TABLE_INTERFACE:
 		/* Interface names */
-		if (co.do_value_as_ip) {
-			tval = htonl(tval);
-			printf("%s %s\n", tent->k.iface,
-			    inet_ntoa(*(struct in_addr *)&tval));
-		} else
-			printf("%s %u\n", tent->k.iface, tval);
+		printf("%s %s\n", tent->k.iface, pval);
 		break;
 	case IPFW_TABLE_NUMBER:
 		/* numbers */
-		if (co.do_value_as_ip) {
-			tval = htonl(tval);
-			printf("%u %s\n", tent->k.key,
-			    inet_ntoa(*(struct in_addr *)&tval));
-		} else
-			printf("%u %u\n", tent->k.key, tval);
+		printf("%u %s\n", tent->k.key, pval);
 		break;
+	case IPFW_TABLE_FLOW:
+		/* flows */
+		tfe = &tent->k.flow;
+		comma = "";
+
+		if ((i->tflags & IPFW_TFFLAG_SRCIP) != 0) {
+			if (tfe->af == AF_INET)
+				paddr = &tfe->a.a4.sip;
+			else
+				paddr = &tfe->a.a6.sip6;
+
+			inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
+			printf("%s%s", comma, tbuf);
+			comma = ",";
+		}
+
+		if ((i->tflags & IPFW_TFFLAG_PROTO) != 0) {
+			printf("%s%d", comma, tfe->proto);
+			comma = ",";
+		}
+
+		if ((i->tflags & IPFW_TFFLAG_SRCPORT) != 0) {
+			printf("%s%d", comma, ntohs(tfe->sport));
+			comma = ",";
+		}
+		if ((i->tflags & IPFW_TFFLAG_DSTIP) != 0) {
+			if (tfe->af == AF_INET)
+				paddr = &tfe->a.a4.dip;
+			else
+				paddr = &tfe->a.a6.dip6;
+
+			inet_ntop(tfe->af, paddr, tbuf, sizeof(tbuf));
+			printf("%s%s", comma, tbuf);
+			comma = ",";
+		}
+
+		if ((i->tflags & IPFW_TFFLAG_DSTPORT) != 0) {
+			printf("%s%d", comma, ntohs(tfe->dport));
+			comma = ",";
+		}
+
+		printf(" %s\n", pval);
 	}
 }
 

Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h	Thu Jul 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sys/netinet/ip_fw.h	Thu Jul 31 20:08:19 2014	(r269348)
@@ -262,6 +262,7 @@ enum ipfw_opcodes {		/* arguments (4 byt
 
 	O_DSCP,			/* 2 u32 = DSCP mask */
 	O_SETDSCP,		/* arg1=DSCP value */
+	O_IP_FLOW_LOOKUP,	/* arg1=table number, u32=value	*/
 
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
@@ -675,7 +676,8 @@ struct _ipfw_dyn_rule {
 #define	IPFW_TABLE_CIDR		1	/* Table for holding IPv4/IPv6 prefixes */
 #define	IPFW_TABLE_INTERFACE	2	/* Table for holding interface names */
 #define	IPFW_TABLE_NUMBER	3	/* Table for holding ports/uid/gid/etc */
-#define	IPFW_TABLE_MAXTYPE	3	/* Maximum valid number */
+#define	IPFW_TABLE_FLOW		4	/* Table for holding flow data */
+#define	IPFW_TABLE_MAXTYPE	4	/* Maximum valid number */
 
 #define	IPFW_VTYPE_U32		1	/* Skipto/tablearg integer */
 #define	IPFW_VTYPE_IP		2	/* Nexthop IP address */
@@ -743,6 +745,25 @@ typedef struct _ipfw_obj_ntlv {
 	char		name[64];	/* Null-terminated name		*/
 } ipfw_obj_ntlv;
 
+/* IPv4/IPv6 L4 flow description */
+struct tflow_entry {
+	uint8_t		af;
+	uint8_t		proto;
+	uint16_t	spare;
+	uint16_t	sport;
+	uint16_t	dport;
+	union {
+		struct {
+			struct in_addr	sip;
+			struct in_addr	dip;
+		} a4;
+		struct {
+			struct in6_addr	sip6;
+			struct in6_addr	dip6;
+		} a6;
+	} a;
+};
+
 /* Table entry TLV */
 typedef struct	_ipfw_obj_tentry {
 	ipfw_obj_tlv	head;		/* TLV header			*/
@@ -753,10 +774,11 @@ typedef struct	_ipfw_obj_tentry {
 	uint64_t	spare;
 	union {
 		/* Longest field needs to be aligned by 8-byte boundary	*/
-		struct in_addr addr;	/* IPv4 address			*/
-		uint32_t key;		/* uid/gid/port			*/
-		struct in6_addr	addr6;	/* IPv6 address 		*/
+		struct in_addr		addr;	/* IPv4 address		*/
+		uint32_t		key;		/* uid/gid/port	*/
+		struct in6_addr		addr6;	/* IPv6 address 	*/
 		char	iface[IF_NAMESIZE];	/* interface name	*/
+		struct tflow_entry	flow;	
 	} k;
 } ipfw_obj_tentry;
 #define	IPFW_TF_UPDATE	0x01		/* Update record if exists	*/
@@ -776,19 +798,44 @@ typedef struct _ipfw_obj_ctlv {
 	uint8_t		spare;
 } ipfw_obj_ctlv;
 
+typedef struct _ifpw_ta_tinfo {
+	uint32_t	flags;		/* Format flags			*/
+	uint8_t		taclass;	/* algorithm class		*/
+	uint8_t		spare0;
+	uint16_t	spare1;
+	uint32_t	rssize4;	/* runtime structure size	*/
+	uint32_t	rcount4;	/* number of items in runtime	*/
+	uint32_t	rsize4;		/* item size in runtime		*/
+	uint32_t	rssize6;	/* runtime structure size	*/
+	uint32_t	rcount6;	/* number of items in runtime	*/
+	uint32_t	rsize6;		/* item size in runtime		*/
+} ifpw_ta_tinfo;
+#define	IPFW_TACLASS_HASH	1	/* algo is based on hash	*/
+#define	IPFW_TACLASS_ARRAY	2	/* algo is based on array	*/
+#define	IPFW_TACLASS_RADIX	3	/* algo is based on radix tree	*/
+
+#define	IPFW_TATFLAGS_DATA	0x0001		/* Has data filled in	*/
+#define	IPFW_TATFLAGS_AF	0x0002		/* Separate data per AF	*/
+
 typedef struct _ipfw_xtable_info {
 	uint8_t		type;		/* table type (cidr,iface,..)	*/
+	uint8_t		tflags;		/* type flags			*/
 	uint8_t		ftype;		/* table value format type	*/
 	uint8_t		vtype;		/* value type			*/
-	uint16_t	spare0;
 	uint32_t	set;		/* set table is in		*/
 	uint32_t	kidx;		/* kernel index			*/
 	uint32_t	refcnt;		/* number of references		*/
 	uint32_t	count;		/* Number of records		*/
-	uint32_t	size;		/* Total size of records	*/
+	uint32_t	size;		/* Total size of records(export)*/
 	char		tablename[64];	/* table name */
-	char		algoname[32];	/* algorithm name		*/
+	char		algoname[64];	/* algorithm name		*/
+	ifpw_ta_tinfo	ta_info;	/* additional algo stats	*/
 } ipfw_xtable_info;
+#define	IPFW_TFFLAG_SRCIP	0x01
+#define	IPFW_TFFLAG_DSTIP	0x02
+#define	IPFW_TFFLAG_SRCPORT	0x04
+#define	IPFW_TFFLAG_DSTPORT	0x08
+#define	IPFW_TFFLAG_PROTO	0x10
 
 typedef struct _ipfw_iface_info {
 	char		ifname[64];	/* interface name		*/
@@ -801,7 +848,7 @@ typedef struct _ipfw_iface_info {
 #define	IPFW_IFFLAG_RESOLVED	0x01	/* Interface exists		*/
 
 typedef struct _ipfw_ta_info {
-	char		algoname[32];	/* algorithm name		*/
+	char		algoname[64];	/* algorithm name		*/
 	uint32_t	type;		/* lookup type			*/
 	uint32_t	flags;
 	uint32_t	refcnt;

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Thu Jul 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Thu Jul 31 20:08:19 2014	(r269348)
@@ -1522,6 +1522,17 @@ do {								\
 				}
 				break;
 
+			case O_IP_FLOW_LOOKUP:
+				{
+					uint32_t v = 0;
+					match = ipfw_lookup_table_extended(chain,
+					    cmd->arg1, 0, &args->f_id, &v);
+					if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
+						match = ((ipfw_insn_u32 *)cmd)->d[0] == v;
+					if (match)
+						tablearg = v;
+				}
+				break;
 			case O_IP_SRC_MASK:
 			case O_IP_DST_MASK:
 				if (is_ipv4) {

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Thu Jul 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Thu Jul 31 20:08:19 2014	(r269348)
@@ -1011,6 +1011,17 @@ check_ipfw_rule_body(ipfw_insn *cmd, int
 				goto bad_size;
 			ci->table_opcodes++;
 			break;
+		case O_IP_FLOW_LOOKUP:
+			if (cmd->arg1 >= V_fw_tables_max) {
+				printf("ipfw: invalid table number %d\n",
+				    cmd->arg1);
+				return (EINVAL);
+			}
+			if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
+			    cmdlen != F_INSN_SIZE(ipfw_insn_u32))
+				goto bad_size;
+			ci->table_opcodes++;
+			break;
 		case O_MACADDR2:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn_mac))
 				goto bad_size;
@@ -1726,7 +1737,7 @@ ipfw_ctl3(struct sockopt *sopt)
 	size_t bsize_max, size, valsize;
 	struct ip_fw_chain *chain;
 	uint32_t opt;
-	char xbuf[128];
+	char xbuf[256];
 	struct sockopt_data sdata;
 	ip_fw3_opheader *op3 = NULL;
 

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Thu Jul 31 19:24:44 2014	(r269347)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Thu Jul 31 20:08:19 2014	(r269348)
@@ -77,7 +77,8 @@ struct table_config {
 	struct named_object	no;
 	uint8_t		vtype;		/* format table type */
 	uint8_t		linked;		/* 1 if already linked */
-	uint16_t	spare;
+	uint8_t		tflags;		/* type flags */
+	uint8_t	spare;
 	uint32_t	count;		/* Number of records */
 	uint64_t	flags;		/* state flags */
 	char		tablename[64];	/* table name */
@@ -95,11 +96,12 @@ struct tables_config {
 static struct table_config *find_table(struct namedobj_instance *ni,
     struct tid_info *ti);
 static struct table_config *alloc_table_config(struct ip_fw_chain *ch,
-    struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t vtype);
+    struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t tflags,
+    uint8_t vtype);
 static void free_table_config(struct namedobj_instance *ni,
     struct table_config *tc);
 static int create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
-    char *aname, uint8_t vtype);
+    char *aname, uint8_t tflags, uint8_t vtype);
 static void link_table(struct ip_fw_chain *chain, struct table_config *tc);
 static void unlink_table(struct ip_fw_chain *chain, struct table_config *tc);
 static void free_table_state(void **state, void **xstate, uint8_t type);
@@ -169,7 +171,7 @@ add_table_entry(struct ip_fw_chain *ch, 
 		if ((tei->flags & TEI_FLAGS_COMPAT) == 0)
 			return (ESRCH);
 
-		error = create_table_internal(ch, ti, NULL, IPFW_VTYPE_U32);
+		error = create_table_internal(ch, ti, NULL, 0, IPFW_VTYPE_U32);
 
 		if (error != 0)
 			return (error);
@@ -533,8 +535,7 @@ ipfw_find_table_entry(struct ip_fw_chain
 	struct table_algo *ta;
 	struct table_info *kti;
 	struct namedobj_instance *ni;
-	int error, plen;
-	void *paddr;
+	int error;
 	size_t sz;
 
 	/* Check minimum header size */
@@ -571,41 +572,13 @@ ipfw_find_table_entry(struct ip_fw_chain
 		return (EINVAL);
 	}
 
-	/* Check lookup key for validness */
-	plen = 0;
-	paddr = &tent->k;
-	switch (ti.type)
-	{
-	case IPFW_TABLE_CIDR:
-		if (tent->subtype == AF_INET)
-			plen = sizeof(struct in_addr);
-		else if (tent->subtype == AF_INET6)
-			plen = sizeof(struct in6_addr);
-		else {
-			IPFW_UH_RUNLOCK(ch);
-			return (EINVAL);
-		}
-		break;
-	case IPFW_TABLE_INTERFACE:
-		/* Check key first */
-		plen = sizeof(tent->k.iface);
-		if (strnlen(tent->k.iface, plen) == plen) {
-			IPFW_UH_RUNLOCK(ch);
-			return (EINVAL);
-		}
-	case IPFW_TABLE_NUMBER:
-		plen = sizeof(uint32_t);
-		break;
-
-		break;
-	default:
-		IPFW_UH_RUNLOCK(ch);
-		return (ENOTSUP);
-	}
 	kti = KIDX_TO_TI(ch, tc->no.kidx);
 	ta = tc->ta;
 
-	error = ta->find_tentry(tc->astate, kti, paddr, plen, tent);
+	if (ta->find_tentry == NULL)
+		return (ENOTSUP);
+
+	error = ta->find_tentry(tc->astate, kti, tent);
 
 	IPFW_UH_RUNLOCK(ch);
 
@@ -651,9 +624,10 @@ flush_table(struct ip_fw_chain *ch, stru
 	struct table_algo *ta;
 	struct table_info ti_old, ti_new, *tablestate;
 	void *astate_old, *astate_new;
-	char algostate[32], *pstate;
+	char algostate[64], *pstate;
 	int error;
 	uint16_t kidx;
+	uint8_t tflags;
 
 	/*
 	 * Stage 1: save table algoritm.
@@ -674,13 +648,14 @@ flush_table(struct ip_fw_chain *ch, stru
 		pstate = algostate;
 	} else
 		pstate = NULL;
+	tflags = tc->tflags;
 	IPFW_UH_WUNLOCK(ch);
 
 	/*
 	 * Stage 2: allocate new table instance using same algo.
 	 */
 	memset(&ti_new, 0, sizeof(struct table_info));
-	if ((error = ta->init(ch, &astate_new, &ti_new, pstate)) != 0) {
+	if ((error = ta->init(ch, &astate_new, &ti_new, pstate, tflags)) != 0) {
 		IPFW_UH_WLOCK(ch);
 		tc->no.refcnt--;
 		IPFW_UH_WUNLOCK(ch);
@@ -1211,7 +1186,7 @@ ipfw_create_table(struct ip_fw_chain *ch
 	}
 	IPFW_UH_RUNLOCK(ch);
 
-	return (create_table_internal(ch, &ti, aname, i->vtype));
+	return (create_table_internal(ch, &ti, aname, i->tflags, i->vtype));
 }
 
 /*
@@ -1224,7 +1199,7 @@ ipfw_create_table(struct ip_fw_chain *ch
  */
 static int
 create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
-    char *aname, uint8_t vtype)
+    char *aname, uint8_t tflags, uint8_t vtype)
 {
 	struct namedobj_instance *ni;
 	struct table_config *tc;
@@ -1237,7 +1212,7 @@ create_table_internal(struct ip_fw_chain
 	if (ta == NULL)
 		return (ENOTSUP);
 	
-	if ((tc = alloc_table_config(ch, ti, ta, aname, vtype)) == NULL)
+	if ((tc = alloc_table_config(ch, ti, ta, aname, tflags, vtype)) == NULL)
 		return (ENOMEM);
 
 	IPFW_UH_WLOCK(ch);
@@ -1311,6 +1286,7 @@ export_table_info(struct ip_fw_chain *ch
 	struct table_info *ti;

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


More information about the svn-src-projects mailing list