Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 8 Jul 2014 23:11:16 +0000 (UTC)
From:      "Alexander V. Chernikov" <melifaro@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r268440 - in projects/ipfw: sbin/ipfw sys/netinet sys/netpfil/ipfw
Message-ID:  <201407082311.s68NBGwb078468@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: melifaro
Date: Tue Jul  8 23:11:15 2014
New Revision: 268440
URL: http://svnweb.freebsd.org/changeset/base/268440

Log:
  * Use different rule structures in kernel/userland.
  * Switch kernel to use per-cpu counters for rules.
  * Keep ABI/API.
  
  Kernel changes:
  * Each rules is now exported as TLV with optional extenable
    counter block (ip_fW_bcounter for base one) and
    ip_fw_rule for rule&cmd data.
  * Counters needs to be explicitly requested by IPFW_CFG_GET_COUNTERS flag.
  * Separate counters from rules in kernel and clean up ip_fw a bit.
  * Pack each rule in IPFW_TLV_RULE_ENT tlv to ease parsing.
  * Introduce versioning in container TLV (may be needed in future).
  * Fix ipfw_cfg_lheader broken u64 alignment.
  
  Userland changes:
  * Use set_mask from cfg header when requesting config
  * Fix incorrect read accouting in ipfw_show_config()
  * Use IPFW_RULE_NOOPT flag instead of playing with _pad
  * Fix "ipfw -d list": do not print counters for dynamic states
  * Some small fixes

Modified:
  projects/ipfw/sbin/ipfw/ipfw2.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

Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c	Tue Jul  8 23:07:09 2014	(r268439)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Tue Jul  8 23:11:15 2014	(r268440)
@@ -61,6 +61,7 @@ struct format_opts {
 	int bcwidth;
 	int pcwidth;
 	int show_counters;
+	uint32_t set_mask;	/* enabled sets mask */
 	uint32_t flags;		/* request flags */
 	uint32_t first;		/* first rule to request */
 	uint32_t last;		/* last rule to request */
@@ -1298,7 +1299,7 @@ show_prerequisites(int *flags, int want,
 
 static void
 show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
-    struct buf_pr *bp, struct ip_fw *rule)
+    struct buf_pr *bp, struct ip_fw_rule *rule, struct ip_fw_bcounter *cntr)
 {
 	static int twidth = 0;
 	int l;
@@ -1309,11 +1310,9 @@ show_static_rule(struct cmdline_opts *co
 	ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */
 	ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */
 	int or_block = 0;	/* we are in an or block */
-	uint32_t set_disable;
 
-	bcopy(&rule->next_rule, &set_disable, sizeof(set_disable));
-
-	if (set_disable & (1 << rule->set)) { /* disabled */
+	if ((fo->set_mask & (1 << rule->set)) == 0) {
+		/* disabled mask */
 		if (!co->show_sets)
 			return;
 		else
@@ -1321,13 +1320,14 @@ show_static_rule(struct cmdline_opts *co
 	}
 	bprintf(bp, "%05u ", rule->rulenum);
 
+	/* Print counters if enabled */
 	if (fo->pcwidth > 0 || fo->bcwidth > 0) {
-		pr_u64(bp, &rule->pcnt, fo->pcwidth);
-		pr_u64(bp, &rule->bcnt, fo->bcwidth);
+		pr_u64(bp, &cntr->pcnt, fo->pcwidth);
+		pr_u64(bp, &cntr->bcnt, fo->bcwidth);
 	}
 
 	if (co->do_time == 2)
-		bprintf(bp, "%10u ", rule->timestamp);
+		bprintf(bp, "%10u ", cntr->timestamp);
 	else if (co->do_time == 1) {
 		char timestr[30];
 		time_t t = (time_t)0;
@@ -1337,8 +1337,8 @@ show_static_rule(struct cmdline_opts *co
 			*strchr(timestr, '\n') = '\0';
 			twidth = strlen(timestr);
 		}
-		if (rule->timestamp) {
-			t = _long_to_time(rule->timestamp);
+		if (cntr->timestamp > 0) {
+			t = _long_to_time(cntr->timestamp);
 
 			strcpy(timestr, ctime(&t));
 			*strchr(timestr, '\n') = '\0';
@@ -1537,7 +1537,7 @@ show_static_rule(struct cmdline_opts *co
 	/*
 	 * then print the body.
 	 */
-	for (l = rule->act_ofs, cmd = rule->cmd ;
+	for (l = rule->act_ofs, cmd = rule->cmd;
 			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
 		if ((cmd->len & F_OR) || (cmd->len & F_NOT))
 			continue;
@@ -1549,7 +1549,7 @@ show_static_rule(struct cmdline_opts *co
 			break;
 		}
 	}
-	if (rule->_pad & 1) {	/* empty rules before options */
+	if (rule->flags & IPFW_RULE_NOOPT) {	/* empty rules before options */
 		if (!co->do_compact) {
 			show_prerequisites(&flags, HAVE_PROTO, 0);
 			printf(" from any to any");
@@ -1561,7 +1561,7 @@ show_static_rule(struct cmdline_opts *co
 	if (co->comment_only)
 		comment = "...";
 
-	for (l = rule->act_ofs, cmd = rule->cmd ;
+	for (l = rule->act_ofs, cmd = rule->cmd;
 			l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) {
 		/* useful alias */
 		ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd;
@@ -2177,6 +2177,9 @@ prepare_format_dyn(struct cmdline_opts *
 	/* Count _ALL_ states */
 	fo->dcnt++;
 
+	if (fo->show_counters == 0)
+		return;
+
 	if (co->use_set) {
 		/* skip states from another set */
 		bcopy((char *)&d->rule + sizeof(uint16_t), &set,
@@ -2237,27 +2240,31 @@ foreach_state(struct cmdline_opts *co, s
 
 static void
 prepare_format_opts(struct cmdline_opts *co, struct format_opts *fo,
-    struct ip_fw *r, int rcnt, caddr_t base, size_t sz)
+    ipfw_obj_tlv *rtlv, int rcnt, caddr_t dynbase, size_t dynsz)
 {
 	int bcwidth, pcwidth, width;
 	int n;
-#define NEXT(r)	((struct ip_fw *)((char *)r + RULESIZE(r)))
+	struct ip_fw_bcounter *cntr;
+	struct ip_fw_rule *r;
 
 	bcwidth = 0;
 	pcwidth = 0;
 	if (fo->show_counters != 0) {
-		for (n = 0; n < rcnt; n++, r = NEXT(r)) {
+		for (n = 0; n < rcnt; n++,
+		    rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
+			cntr = (struct ip_fw_bcounter *)(rtlv + 1);
+			r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
 			/* skip rules from another set */
 			if (co->use_set && r->set != co->use_set - 1)
 				continue;
 
 			/* packet counter */
-			width = pr_u64(NULL, &r->pcnt, 0);
+			width = pr_u64(NULL, &cntr->pcnt, 0);
 			if (width > pcwidth)
 				pcwidth = width;
 
 			/* byte counter */
-			width = pr_u64(NULL, &r->bcnt, 0);
+			width = pr_u64(NULL, &cntr->bcnt, 0);
 			if (width > bcwidth)
 				bcwidth = width;
 		}
@@ -2266,23 +2273,36 @@ prepare_format_opts(struct cmdline_opts 
 	fo->pcwidth = pcwidth;
 
 	fo->dcnt = 0;
-	if (co->do_dynamic && sz > 0)
-		sz = foreach_state(co, fo, base, sz, prepare_format_dyn, NULL);
+	if (co->do_dynamic && dynsz > 0)
+		foreach_state(co, fo, dynbase, dynsz, prepare_format_dyn, NULL);
 }
 
 static int
 list_static_range(struct cmdline_opts *co, struct format_opts *fo,
-    struct buf_pr *bp, struct ip_fw *r, int rcnt)
+    struct buf_pr *bp, ipfw_obj_tlv *rtlv, int rcnt)
 {
 	int n, seen;
-
-	for (n = seen = 0; n < rcnt; n++, r = NEXT(r) ) {
+	struct ip_fw_rule *r;
+	struct ip_fw_bcounter *cntr;
+	int c = 0;
+
+	for (n = seen = 0; n < rcnt; n++,
+	    rtlv = (ipfw_obj_tlv *)((caddr_t)rtlv + rtlv->length)) {
+
+		if (fo->show_counters != 0) {
+			cntr = (struct ip_fw_bcounter *)(rtlv + 1);
+			r = (struct ip_fw_rule *)((caddr_t)cntr + cntr->size);
+		} else {
+			cntr = NULL;
+			r = (struct ip_fw_rule *)(rtlv + 1);
+		}
 		if (r->rulenum > fo->last)
 			break;
 		if (co->use_set && r->set != co->use_set - 1)
 			continue;
 		if (r->rulenum >= fo->first && r->rulenum <= fo->last) {
-			show_static_rule(co, fo, bp, r);
+			show_static_rule(co, fo, bp, r, cntr);
+			c += rtlv->length;
 			bp_flush(bp);
 			seen++;
 		}
@@ -2369,27 +2389,27 @@ ipfw_list(int ac, char *av[], int show_c
 
 	/* get configuraion from kernel */
 	cfg = NULL;
+	sfo.show_counters = show_counters;
 	sfo.flags = IPFW_CFG_GET_STATIC;
 	if (co.do_dynamic != 0)
 		sfo.flags |= IPFW_CFG_GET_STATES;
+	if (sfo.show_counters != 0)
+		sfo.flags |= IPFW_CFG_GET_COUNTERS;
 	if ((error = ipfw_get_config(&co, &sfo, &cfg, &sz)) != 0)
 		err(EX_OSERR, "retrieving config failed");
 
-	sfo.show_counters = show_counters;
 	error = ipfw_show_config(&co, &sfo, cfg, sz, ac, av);
 
 	free(cfg);
 
 	if (error != EX_OK)
 		exit(error);
-#undef NEXT
 }
 
 static int
 ipfw_show_config(struct cmdline_opts *co, struct format_opts *fo,
     ipfw_cfg_lheader *cfg, size_t sz, int ac, char *av[])
 {
-	struct ip_fw *rbase;
 	caddr_t dynbase;
 	size_t dynsz;
 	int rcnt;
@@ -2400,6 +2420,7 @@ ipfw_show_config(struct cmdline_opts *co
 	size_t read;
 	struct buf_pr bp;
 	ipfw_obj_ctlv *ctlv, *tstate;
+	ipfw_obj_tlv *rbase;
 
 	/*
 	 * Handle tablenames TLV first, if any
@@ -2408,7 +2429,9 @@ ipfw_show_config(struct cmdline_opts *co
 	rbase = NULL;
 	dynbase = NULL;
 	dynsz = 0;
-	read = 0;
+	read = sizeof(*cfg);
+
+	fo->set_mask = cfg->set_mask;
 
 	ctlv = (ipfw_obj_ctlv *)(cfg + 1);
 
@@ -2422,7 +2445,7 @@ ipfw_show_config(struct cmdline_opts *co
 		}
 
 		if (ctlv->head.type == IPFW_TLV_RULE_LIST) {
-			rbase = (struct ip_fw *)(ctlv + 1);
+			rbase = (ipfw_obj_tlv *)(ctlv + 1);
 			rcnt = ctlv->count;
 			read += ctlv->head.length;
 			ctlv = (ipfw_obj_ctlv *)((caddr_t)ctlv +
@@ -2432,8 +2455,12 @@ ipfw_show_config(struct cmdline_opts *co
 
 	if ((cfg->flags & IPFW_CFG_GET_STATES) && (read != sz))  {
 		/* We may have some dynamic states */
-		dynbase = (caddr_t)ctlv;
 		dynsz = sz - read;
+		/* Skip empty header */
+		if (dynsz != sizeof(ipfw_obj_ctlv))
+			dynbase = (caddr_t)ctlv;
+		else
+			dynsz = 0;
 	}
 
 	prepare_format_opts(co, fo, rbase, rcnt, dynbase, dynsz);
@@ -2446,7 +2473,7 @@ ipfw_show_config(struct cmdline_opts *co
 		list_static_range(co, fo, &bp, rbase, rcnt);
 
 		if (co->do_dynamic && dynsz > 0) {
-			printf("## Dynamic rules (%d):\n", fo->dcnt);
+			printf("## Dynamic rules (%d %lu):\n", fo->dcnt, dynsz);
 			list_dyn_range(co, fo, &bp, dynbase, dynsz);
 		}
 
@@ -3304,7 +3331,7 @@ compile_rule(char *av[], uint32_t *rbuf,
 	ipfw_insn *src, *dst, *cmd, *action, *prev=NULL;
 	ipfw_insn *first_cmd;	/* first match pattern */
 
-	struct ip_fw *rule;
+	struct ip_fw_rule *rule;
 
 	/*
 	 * various flags used to record that we entered some fields.
@@ -3326,12 +3353,12 @@ compile_rule(char *av[], uint32_t *rbuf,
 	bzero(cmdbuf, sizeof(cmdbuf));
 	bzero(rbuf, *rbufsize);
 
-	rule = (struct ip_fw *)rbuf;
+	rule = (struct ip_fw_rule *)rbuf;
 	cmd = (ipfw_insn *)cmdbuf;
 	action = (ipfw_insn *)actbuf;
 
 	rblen = *rbufsize / sizeof(uint32_t);
-	rblen -= offsetof(struct ip_fw, cmd) / sizeof(uint32_t);
+	rblen -= sizeof(struct ip_fw_rule) / sizeof(uint32_t);
 	ablen = sizeof(actbuf) / sizeof(actbuf[0]);
 	cblen = sizeof(cmdbuf) / sizeof(cmdbuf[0]);
 	cblen -= F_INSN_SIZE(ipfw_insn_u32) + 1;
@@ -3884,7 +3911,7 @@ read_options:
 		 * nothing specified so far, store in the rule to ease
 		 * printout later.
 		 */
-		 rule->_pad = 1;
+		 rule->flags |= IPFW_RULE_NOOPT;
 	}
 	prev = NULL;
 	while ( av[0] != NULL ) {
@@ -4467,13 +4494,13 @@ done:
  * [
  *   ip_fw3_opheader
  *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional *1)
- *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ] (*2) (*3)
+ *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ] (*2) (*3)
  * ]
  * Reply:
  * [
  *   ip_fw3_opheader
  *   [ ipfw_obj_ctlv(IPFW_TLV_TBL_LIST) ipfw_obj_ntlv x N ] (optional)
- *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) ip_fw x N ]
+ *   [ ipfw_obj_ctlv(IPFW_TLV_RULE_LIST) [ ip_fw_rule ip_fw_insn ] x N ]
  * ]
  *
  * Rules in reply are modified to store their actual ruleset number.
@@ -4490,7 +4517,7 @@ ipfw_add(char *av[])
 	int rbufsize, default_off, tlen, rlen;
 	size_t sz;
 	struct tidx ts;
-	struct ip_fw *rule;
+	struct ip_fw_rule *rule;
 	caddr_t tbuf;
 	ip_fw3_opheader *op3;
 	ipfw_obj_ctlv *ctlv, *tstate;
@@ -4502,7 +4529,7 @@ ipfw_add(char *av[])
 	default_off = sizeof(ipfw_obj_ctlv) + sizeof(ip_fw3_opheader);
 	op3 = (ip_fw3_opheader *)rulebuf;
 	ctlv = (ipfw_obj_ctlv *)(op3 + 1);
-	rule = (struct ip_fw *)(ctlv + 1);
+	rule = (struct ip_fw_rule *)(ctlv + 1);
 	rbufsize -= default_off;
 
 	compile_rule(av, (uint32_t *)rule, &rbufsize, &ts);
@@ -4552,8 +4579,9 @@ ipfw_add(char *av[])
 		struct buf_pr bp;
 		memset(&sfo, 0, sizeof(sfo));
 		sfo.tstate = tstate;
+		sfo.set_mask = (uint32_t)(-1);
 		bp_alloc(&bp, 4096);
-		show_static_rule(&co, &sfo, &bp, rule);
+		show_static_rule(&co, &sfo, &bp, rule, NULL);
 		bp_free(&bp);
 	}
 

Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h	Tue Jul  8 23:07:09 2014	(r268439)
+++ projects/ipfw/sys/netinet/ip_fw.h	Tue Jul  8 23:11:15 2014	(r268440)
@@ -36,10 +36,8 @@
  */
 #define	IPFW_DEFAULT_RULE	65535
 
-/*
- * Number of sets supported by ipfw
- */
-#define	IPFW_MAX_SETS		32
+#define	RESVD_SET		31	/*set for default and persistent rules*/
+#define	IPFW_MAX_SETS		32	/* Number of sets supported by ipfw*/
 
 /*
  * Default number of ipfw tables.
@@ -513,15 +511,17 @@ typedef struct _ipfw_insn_icmp6 {
 /*
  * Here we have the structure representing an ipfw rule.
  *
- * It starts with a general area (with link fields and counters)
- * followed by an array of one or more instructions, which the code
- * accesses as an array of 32-bit values.
- *
- * Given a rule pointer  r:
+ * Layout:
+ * struct ip_fw_rule
+ * [ counter block, size = rule->cntr_len ]
+ * [ one or more instructions, size = rule->cmd_len * 4 ]
  *
- *  r->cmd		is the start of the first instruction.
- *  ACTION_PTR(r)	is the start of the first action (things to do
- *			once a rule matched).
+ * It starts with a general area (with link fields).
+ * Counter block may be next (if rule->cntr_len > 0),
+ * followed by an array of one or more instructions, which the code
+ * accesses as an array of 32-bit values. rule->cmd_len represents
+ * the total instructions legth in u32 worrd, while act_ofs represents
+ * rule action offset in u32 words.
  *
  * When assembling instruction, remember the following:
  *
@@ -532,11 +532,41 @@ typedef struct _ipfw_insn_icmp6 {
  *  + if a rule has an "altq" option, it comes after "log"
  *  + if a rule has an O_TAG option, it comes after "log" and "altq"
  *
- * NOTE: we use a simple linked list of rules because we never need
- * 	to delete a rule without scanning the list. We do not use
- *	queue(3) macros for portability and readability.
+ *
+ * All structures (excluding instructions) are u64-aligned.
+ * Please keep this.
  */
 
+struct ip_fw_rule {
+	uint16_t	act_ofs;	/* offset of action in 32-bit units */
+	uint16_t	cmd_len;	/* # of 32-bit words in cmd	*/
+	uint16_t	spare;
+	uint8_t		set;		/* rule set (0..31)		*/
+	uint8_t		flags;		/* rule flags			*/
+	uint32_t	rulenum;	/* rule number			*/
+	uint32_t	id;		/* rule id			*/
+
+	ipfw_insn	cmd[1];		/* storage for commands		*/
+};
+#define	IPFW_RULE_NOOPT		0x01	/* Has no options in body	*/
+
+/* Unaligned version */
+
+/* Base ipfw rule counter block. */
+struct ip_fw_bcounter {
+	uint16_t	size;		/* Size of counter block, bytes	*/
+	uint8_t		flags;		/* flags for given block	*/
+	uint8_t		spare;
+	uint32_t	timestamp;	/* tv_sec of last match		*/
+	uint64_t	pcnt;		/* Packet counter		*/
+	uint64_t	bcnt;		/* Byte counter			*/
+};
+
+
+#ifndef	_KERNEL
+/*
+ * Legacy rule format
+ */
 struct ip_fw {
 	struct ip_fw	*x_next;	/* linked list of rules		*/
 	struct ip_fw	*next_rule;	/* ptr to next [skipto] rule	*/
@@ -546,7 +576,6 @@ struct ip_fw {
 	uint16_t	cmd_len;	/* # of 32-bit words in cmd	*/
 	uint16_t	rulenum;	/* rule number			*/
 	uint8_t	set;		/* rule set (0..31)		*/
-#define	RESVD_SET	31	/* set for default and persistent rules */
 	uint8_t		_pad;		/* padding			*/
 	uint32_t	id;		/* rule id */
 
@@ -557,12 +586,13 @@ struct ip_fw {
 
 	ipfw_insn	cmd[1];		/* storage for commands		*/
 };
+#endif
 
 #define ACTION_PTR(rule)				\
 	(ipfw_insn *)( (u_int32_t *)((rule)->cmd) + ((rule)->act_ofs) )
 
-#define RULESIZE(rule)  (sizeof(struct ip_fw) + \
-	((struct ip_fw *)(rule))->cmd_len * 4 - 4)
+#define RULESIZE(rule)  (sizeof(*(rule)) + (rule)->cmd_len * 4 - 4)
+
 
 #if 1 // should be moved to in.h
 /*
@@ -698,6 +728,7 @@ typedef struct  _ipfw_obj_tlv {
 #define	IPFW_TLV_DYNSTATE_LIST	4
 #define	IPFW_TLV_TBL_ENT	5
 #define	IPFW_TLV_DYN_ENT	6
+#define	IPFW_TLV_RULE_ENT	7
 
 /* Object name TLV */
 typedef struct _ipfw_obj_ntlv {
@@ -737,7 +768,9 @@ typedef struct _ipfw_obj_dyntlv {
 typedef struct _ipfw_obj_ctlv {
 	ipfw_obj_tlv	head;		/* TLV header			*/
 	uint32_t	count;		/* Number of sub-TLVs		*/
-	uint32_t	objsize;	/* Single object size		*/
+	uint16_t	objsize;	/* Single object size		*/
+	uint8_t		version;	/* TLV version			*/
+	uint8_t		spare;
 } ipfw_obj_ctlv;
 
 typedef struct _ipfw_xtable_info {
@@ -772,11 +805,13 @@ typedef struct _ipfw_obj_lheader {
 	uint32_t	objsize;	/* Size of one object		*/
 } ipfw_obj_lheader;
 
-#define	IPFW_CFG_GET_STATIC	1
-#define	IPFW_CFG_GET_STATES	2
+#define	IPFW_CFG_GET_STATIC	0x01
+#define	IPFW_CFG_GET_STATES	0x02
+#define	IPFW_CFG_GET_COUNTERS	0x04
 typedef struct _ipfw_cfg_lheader {
 	ip_fw3_opheader	opheader;	/* IP_FW3 opcode		*/
 	uint32_t	set_mask;	/* enabled set mask		*/
+	uint32_t	spare;
 	uint32_t	flags;		/* Request flags		*/
 	uint32_t	size;		/* neded buffer size		*/
 	uint32_t	start_rule;

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Tue Jul  8 23:07:09 2014	(r268439)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Tue Jul  8 23:11:15 2014	(r268440)
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/condvar.h>
+#include <sys/counter.h>
 #include <sys/eventhandler.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
@@ -2647,12 +2648,11 @@ vnet_ipfw_init(const void *unused)
 	LIST_INIT(&chain->nat);
 #endif
 
+	ipfw_init_counters();
 	/* insert the default rule and create the initial map */
 	chain->n_rules = 1;
-	chain->static_len = sizeof(struct ip_fw);
 	chain->map = malloc(sizeof(struct ip_fw *), M_IPFW, M_WAITOK | M_ZERO);
-	if (chain->map)
-		rule = malloc(chain->static_len, M_IPFW, M_WAITOK | M_ZERO);
+	rule = ipfw_alloc_rule(chain, sizeof(struct ip_fw));
 
 	/* Set initial number of tables */
 	V_fw_tables_max = default_fw_tables;
@@ -2673,6 +2673,8 @@ vnet_ipfw_init(const void *unused)
 	rule->cmd[0].opcode = default_to_accept ? O_ACCEPT : O_DENY;
 	chain->default_rule = chain->map[0] = rule;
 	chain->id = rule->id = 1;
+	/* Pre-calculate rules length for legacy dump format */
+	chain->static_len = sizeof(struct ip_fw_rule0);
 
 	IPFW_LOCK_INIT(chain);
 	ipfw_dyn_init(chain);
@@ -2740,7 +2742,8 @@ vnet_ipfw_uninit(const void *unused)
 		ipfw_reap_rules(reap);
 	IPFW_LOCK_DESTROY(chain);
 	ipfw_dyn_uninit(1);	/* free the remaining parts */
-	return 0;
+	ipfw_destroy_counters();
+	return (0);
 }
 
 /*

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Tue Jul  8 23:07:09 2014	(r268439)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Tue Jul  8 23:11:15 2014	(r268440)
@@ -220,6 +220,44 @@ VNET_DECLARE(unsigned int, fw_tables_set
 
 struct tables_config;
 
+#ifdef _KERNEL
+typedef struct ip_fw_cntr {
+	uint64_t	pcnt;	   /* Packet counter		*/
+	uint64_t	bcnt;	   /* Byte counter		 */
+	uint64_t	timestamp;      /* tv_sec of last match	 */
+} ip_fw_cntr;
+
+/*
+ * Here we have the structure representing an ipfw rule.
+ *
+ * It starts with a general area 
+ * followed by an array of one or more instructions, which the code
+ * accesses as an array of 32-bit values.
+ *
+ * Given a rule pointer  r:
+ *
+ *  r->cmd		is the start of the first instruction.
+ *  ACTION_PTR(r)	is the start of the first action (things to do
+ *			once a rule matched).
+ */
+
+struct ip_fw {
+	uint16_t	act_ofs;	/* offset of action in 32-bit units */
+	uint16_t	cmd_len;	/* # of 32-bit words in cmd	*/
+	uint16_t	rulenum;	/* rule number			*/
+	uint8_t		set;		/* rule set (0..31)		*/
+	uint8_t		flags;		/* currently unused		*/
+	counter_u64_t	cntr;		/* Pointer to rule counters	*/
+	uint32_t	timestamp;	/* tv_sec of last match		*/
+	uint32_t	id;		/* rule id			*/
+	struct ip_fw    *x_next;	/* linked list of rules		*/
+	struct ip_fw    *next_rule;	/* ptr to next [skipto] rule	*/
+
+	ipfw_insn	cmd[1];		/* storage for commands		*/
+};
+
+#endif
+
 struct ip_fw_chain {
 	struct ip_fw	**map;		/* array of rule ptrs to ease lookup */
 	uint32_t	id;		/* ruleset id */
@@ -231,7 +269,7 @@ struct ip_fw_chain {
 #else
 	struct rwlock	rwmtx;
 #endif
-	int		static_len;	/* total len of static rules */
+	int		static_len;	/* total len of static rules (v0) */
 	uint32_t	gencnt;		/* NAT generation count */
 	struct ip_fw	*reap;		/* list of rules to reap */
 	struct ip_fw	*default_rule;
@@ -255,6 +293,7 @@ struct sockopt_data {
 };
 
 /* Macro for working with various counters */
+#ifdef USERSPACE
 #define	IPFW_INC_RULE_COUNTER(_cntr, _bytes)	do {	\
 	(_cntr)->pcnt++;				\
 	(_cntr)->bcnt += _bytes;			\
@@ -276,6 +315,31 @@ struct sockopt_data {
 	(_cntr)->pcnt = 0;				\
 	(_cntr)->bcnt = 0;				\
 	} while (0)
+#else
+#define	IPFW_INC_RULE_COUNTER(_cntr, _bytes)	do {	\
+	counter_u64_add((_cntr)->cntr, 1);		\
+	counter_u64_add((_cntr)->cntr + 1, _bytes);	\
+	if ((_cntr)->timestamp != time_uptime)		\
+		(_cntr)->timestamp = time_uptime;	\
+	} while (0)
+
+#define	IPFW_INC_DYN_COUNTER(_cntr, _bytes)	do {		\
+	(_cntr)->pcnt++;				\
+	(_cntr)->bcnt += _bytes;			\
+	} while (0)
+
+#define	IPFW_ZERO_RULE_COUNTER(_cntr) do {		\
+	counter_u64_zero((_cntr)->cntr);		\
+	counter_u64_zero((_cntr)->cntr + 1);		\
+	(_cntr)->timestamp = 0;				\
+	} while (0)
+
+#define	IPFW_ZERO_DYN_COUNTER(_cntr) do {		\
+	(_cntr)->pcnt = 0;				\
+	(_cntr)->bcnt = 0;				\
+	} while (0)
+#endif
+
 
 #define	IP_FW_ARG_TABLEARG(a)	(((a) == IP_FW_TABLEARG) ? tablearg : (a))
 /*
@@ -322,17 +386,70 @@ struct obj_idx {
 struct rule_check_info {
 	uint16_t	table_opcodes;	/* count of opcodes referencing table */
 	uint16_t	new_tables;	/* count of opcodes referencing table */
+	uint16_t	urule_numoff;	/* offset of rulenum in bytes */
+	uint8_t		version;	/* rule version */
 	ipfw_obj_ctlv	*ctlv;		/* name TLV containter */
 	struct ip_fw	*krule;		/* resulting rule pointer */
-	struct ip_fw	*urule;		/* original rule pointer */
+	caddr_t		urule;		/* original rule pointer */
 	struct obj_idx	obuf[8];	/* table references storage */
 };
 
+/* Legacy interface support */
+/*
+ * FreeBSD 8 export rule format
+ */
+struct ip_fw_rule0 {
+	struct ip_fw	*x_next;	/* linked list of rules		*/
+	struct ip_fw	*next_rule;	/* ptr to next [skipto] rule	*/
+	/* 'next_rule' is used to pass up 'set_disable' status		*/
+
+	uint16_t	act_ofs;	/* offset of action in 32-bit units */
+	uint16_t	cmd_len;	/* # of 32-bit words in cmd	*/
+	uint16_t	rulenum;	/* rule number			*/
+	uint8_t		set;		/* rule set (0..31)		*/
+	uint8_t		_pad;		/* padding			*/
+	uint32_t	id;		/* rule id */
+
+	/* These fields are present in all rules.			*/
+	uint64_t	pcnt;		/* Packet counter		*/
+	uint64_t	bcnt;		/* Byte counter			*/
+	uint32_t	timestamp;	/* tv_sec of last match		*/
+
+	ipfw_insn	cmd[1];		/* storage for commands		*/
+};
+
+struct ip_fw_bcounter0 {
+	uint64_t	pcnt;		/* Packet counter		*/
+	uint64_t	bcnt;		/* Byte counter			*/
+	uint32_t	timestamp;	/* tv_sec of last match		*/
+};
+
+/* Kernel rule length */
+/*
+ * RULE _K_ SIZE _V_ ->
+ * get kernel size from userland rool version _V_.
+ * RULE _U_ SIZE _V_ ->
+ * get user size version _V_ from kernel rule
+ * RULESIZE _V_ ->
+ * get user size rule length 
+ */
+/* FreeBSD8 <> current kernel format */
+#define	RULEUSIZE0(r)	(sizeof(struct ip_fw_rule0) + (r)->cmd_len * 4 - 4)
+#define	RULEKSIZE0(r)	roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8)
+/* FreeBSD11 <> current kernel format */
+#define	RULEUSIZE1(r)	(roundup2(sizeof(struct ip_fw_rule) + \
+    (r)->cmd_len * 4 - 4, 8))
+#define	RULEKSIZE1(r)	roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8)
+
+
 /* In ip_fw_sockopt.c */
 int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id);
 int ipfw_ctl(struct sockopt *sopt);
 int ipfw_chk(struct ip_fw_args *args);
 void ipfw_reap_rules(struct ip_fw *head);
+void ipfw_init_counters(void);
+void ipfw_destroy_counters(void);
+struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize);
 
 caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed);
 caddr_t ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed);

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Tue Jul  8 23:07:09 2014	(r268439)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Tue Jul  8 23:11:15 2014	(r268440)
@@ -69,6 +69,13 @@ __FBSDID("$FreeBSD$");
 #include <security/mac/mac_framework.h>
 #endif
 
+static int check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len,
+    struct rule_check_info *ci);
+static int check_ipfw_rule1(struct ip_fw_rule *rule, int size,
+    struct rule_check_info *ci);
+static int check_ipfw_rule0(struct ip_fw_rule0 *rule, int size,
+    struct rule_check_info *ci);
+
 #define	NAMEDOBJ_HASH_SIZE	32
 
 struct namedobj_instance {
@@ -92,9 +99,79 @@ static int ipfw_flush_sopt_data(struct s
 MALLOC_DEFINE(M_IPFW, "IpFw/IpAcct", "IpFw/IpAcct chain's");
 
 /*
- * static variables followed by global ones (none in this file)
+ * static variables followed by global ones
  */
 
+#ifndef USERSPACE
+
+static VNET_DEFINE(uma_zone_t, ipfw_cntr_zone);
+#define	V_ipfw_cntr_zone		VNET(ipfw_cntr_zone)
+
+void
+ipfw_init_counters()
+{
+
+	V_ipfw_cntr_zone = uma_zcreate("IPFW counters",
+	    sizeof(ip_fw_cntr), NULL, NULL, NULL, NULL,
+	    UMA_ALIGN_PTR, UMA_ZONE_PCPU);
+}
+
+void
+ipfw_destroy_counters()
+{
+	
+	uma_zdestroy(V_ipfw_cntr_zone);
+}
+
+struct ip_fw *
+ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
+{
+	struct ip_fw *rule;
+
+	rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
+	rule->cntr = uma_zalloc(V_ipfw_cntr_zone, M_WAITOK | M_ZERO);
+
+	return (rule);
+}
+
+static void
+free_rule(struct ip_fw *rule)
+{
+
+	uma_zfree(V_ipfw_cntr_zone, rule->cntr);
+	free(rule, M_IPFW);
+}
+#else
+void
+ipfw_init_counters()
+{
+}
+
+void
+ipfw_destroy_counters()
+{
+}
+
+struct ip_fw *
+ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize)
+{
+	struct ip_fw *rule;
+
+	rule = malloc(rulesize, M_IPFW, M_WAITOK | M_ZERO);
+
+	return (rule);
+}
+
+static void
+free_rule(struct ip_fw *rule)
+{
+
+	free(rule, M_IPFW);
+}
+
+#endif
+
+
 /*
  * Find the smallest rule >= key, id.
  * We could use bsearch but it is so simple that we code it directly
@@ -167,20 +244,151 @@ swap_map(struct ip_fw_chain *chain, stru
 	return old_map;
 }
 
+
+static void
+export_cntr1_base(struct ip_fw *krule, struct ip_fw_bcounter *cntr)
+{
+
+	cntr->size = sizeof(*cntr);
+
+	if (krule->cntr != NULL) {
+		cntr->pcnt = counter_u64_fetch(krule->cntr);
+		cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
+		cntr->timestamp = krule->timestamp;
+	}
+	if (cntr->timestamp > 0)
+		cntr->timestamp += boottime.tv_sec;
+}
+
+static void
+export_cntr0_base(struct ip_fw *krule, struct ip_fw_bcounter0 *cntr)
+{
+
+	if (krule->cntr != NULL) {
+		cntr->pcnt = counter_u64_fetch(krule->cntr);
+		cntr->bcnt = counter_u64_fetch(krule->cntr + 1);
+		cntr->timestamp = krule->timestamp;
+	}
+	if (cntr->timestamp > 0)
+		cntr->timestamp += boottime.tv_sec;
+}
+
 /*
- * Copies rule @urule from userland format to kernel @krule.
+ * Copies rule @urule from v1 userland format
+ * to kernel @krule.
+ * Assume @krule is zeroed.
  */
 static void
-copy_rule(struct ip_fw *urule, struct ip_fw *krule)
+import_rule1(struct rule_check_info *ci)
 {
-	int l;
+	struct ip_fw_rule *urule;
+	struct ip_fw *krule;
+
+	urule = (struct ip_fw_rule *)ci->urule;
+	krule = (struct ip_fw *)ci->krule;
+
+	/* copy header */
+	krule->act_ofs = urule->act_ofs;
+	krule->cmd_len = urule->cmd_len;
+	krule->rulenum = urule->rulenum;
+	krule->set = urule->set;
+	krule->flags = urule->flags;
+
+	/* Save rulenum offset */
+	ci->urule_numoff = offsetof(struct ip_fw_rule, rulenum);
+
+	/* Copy opcodes */
+	memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
+}
+
+/*
+ * Export rule into v1 format (Current).
+ * Layout:
+ * [ ipfw_obj_tlv(IPFW_TLV_RULE_ENT)
+ *     [ ip_fw_rule ] OR
+ *     [ ip_fw_bcounter ip_fw_rule] (depends on rcntrs).
+ * ]
+ * Assume @data is zeroed.
+ */
+static void
+export_rule1(struct ip_fw *krule, caddr_t data, int len, int rcntrs)
+{
+	struct ip_fw_bcounter *cntr;
+	struct ip_fw_rule *urule;
+	ipfw_obj_tlv *tlv;
+
+	/* Fill in TLV header */
+	tlv = (ipfw_obj_tlv *)data;
+	tlv->type = IPFW_TLV_RULE_ENT;
+	tlv->length = len;
+
+	if (rcntrs != 0) {
+		/* Copy counters */
+		cntr = (struct ip_fw_bcounter *)(tlv + 1);
+		urule = (struct ip_fw_rule *)(cntr + 1);
+		export_cntr1_base(krule, cntr);
+	} else
+		urule = (struct ip_fw_rule *)(tlv + 1);
+
+	/* copy header */
+	urule->act_ofs = krule->act_ofs;
+	urule->cmd_len = krule->cmd_len;
+	urule->rulenum = krule->rulenum;
+	urule->set = krule->set;
+	urule->flags = krule->flags;
+	urule->id = krule->id;
+
+	/* Copy opcodes */
+	memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
+}
+
+
+/*
+ * Copies rule @urule from FreeBSD8 userland format (v0)
+ * to kernel @krule.
+ * Assume @krule is zeroed.
+ */
+static void
+import_rule0(struct rule_check_info *ci)
+{
+	struct ip_fw_rule0 *urule;
+	struct ip_fw *krule;
+
+	urule = (struct ip_fw_rule0 *)ci->urule;
+	krule = (struct ip_fw *)ci->krule;
+
+	/* copy header */
+	krule->act_ofs = urule->act_ofs;
+	krule->cmd_len = urule->cmd_len;
+	krule->rulenum = urule->rulenum;
+	krule->set = urule->set;
+	if ((urule->_pad & 1) != 0)
+		krule->flags |= IPFW_RULE_NOOPT;
 
-	l = RULESIZE(urule);
-	bcopy(urule, krule, l);
-	/* clear fields not settable from userland */
-	krule->x_next = NULL;
-	krule->next_rule = NULL;
-	IPFW_ZERO_RULE_COUNTER(krule);
+	/* Save rulenum offset */
+	ci->urule_numoff = offsetof(struct ip_fw_rule0, rulenum);
+
+	/* Copy opcodes */
+	memcpy(krule->cmd, urule->cmd, krule->cmd_len * sizeof(uint32_t));
+}
+
+static void
+export_rule0(struct ip_fw *krule, struct ip_fw_rule0 *urule, int len)
+{
+	/* copy header */
+	memset(urule, 0, len);
+	urule->act_ofs = krule->act_ofs;
+	urule->cmd_len = krule->cmd_len;
+	urule->rulenum = krule->rulenum;
+	urule->set = krule->set;
+	if ((krule->flags & IPFW_RULE_NOOPT) != 0)
+		urule->_pad |= 1;
+
+	/* Copy opcodes */
+	memcpy(urule->cmd, krule->cmd, krule->cmd_len * sizeof(uint32_t));
+
+	/* Export counters */
+	export_cntr0_base(krule, (struct ip_fw_bcounter0 *)&urule->pcnt);
 }
 
 /*
@@ -191,9 +399,10 @@ copy_rule(struct ip_fw *urule, struct ip
 static int
 commit_rules(struct ip_fw_chain *chain, struct rule_check_info *rci, int count)
 {
-	int error, i, l, insert_before, tcount;
+	int error, i, insert_before, tcount;
+	uint16_t rulenum, *pnum;
 	struct rule_check_info *ci;
-	struct ip_fw *rule, *urule;
+	struct ip_fw *krule;
 	struct ip_fw **map;	/* the new array of pointers */
 
 	/* Check if we need to do table remap */
@@ -264,31 +473,33 @@ commit_rules(struct ip_fw_chain *chain, 
 
 	/* FIXME: Handle count > 1 */
 	ci = rci;
-	rule = ci->krule;
-	urule = ci->urule;
-	l = RULESIZE(rule);
+	krule = ci->krule;
+	rulenum = krule->rulenum;
 
 	/* find the insertion point, we will insert before */
-	insert_before = rule->rulenum ? rule->rulenum + 1 : IPFW_DEFAULT_RULE;
+	insert_before = rulenum ? rulenum + 1 : IPFW_DEFAULT_RULE;
 	i = ipfw_find_rule(chain, insert_before, 0);
 	/* duplicate first part */
 	if (i > 0)
 		bcopy(chain->map, map, i * sizeof(struct ip_fw *));
-	map[i] = rule;
+	map[i] = krule;
 	/* duplicate remaining part, we always have the default rule */
 	bcopy(chain->map + i, map + i + 1,
 		sizeof(struct ip_fw *) *(chain->n_rules - i));
-	if (rule->rulenum == 0) {
-		/* write back the number */
-		rule->rulenum = i > 0 ? map[i-1]->rulenum : 0;
-		if (rule->rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
-			rule->rulenum += V_autoinc_step;
-		urule->rulenum = rule->rulenum;
+	if (rulenum == 0) {
+		/* Compute rule number and write it back */
+		rulenum = i > 0 ? map[i-1]->rulenum : 0;
+		if (rulenum < IPFW_DEFAULT_RULE - V_autoinc_step)
+			rulenum += V_autoinc_step;

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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201407082311.s68NBGwb078468>