[RFC][patch] New "keep-state-only" option (version 3)

Lev Serebryakov lev at FreeBSD.org
Wed Feb 4 09:24:47 UTC 2015


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

On 03.02.2015 19:55, Lev Serebryakov wrote:

>> Ok, "allow-state"/"deny-state" was very limited idea. Here is
>> more universal mechanism: new "keep-state-only" (aliased as 
>> "record-only") option, which works exactly as "keep-state" BUT 
>> cancel match of rule after state creation. It allows to write 
>> stateful + nat firewall as easy as:
> To work as expected, "keep-state-only" should not imply
> "check-state" in opposite to "keep-state".
  Re-installation of state (with second, third, etc... packet of
connection) should update TCP state of state (sorry!), or it will die
in 10 seconds.
  This version seems to be final (apart from name of new option!).
  It works perfectly on my router with 2 uplink ISPs.

- -- 
// Lev Serebryakov AKA Black Lion
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (MingW32)

iQJ8BAEBCgBmBQJU0eVYXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w
ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRGOTZEMUNBMEI1RjQzMThCNjc0QjMzMEFF
QUIwM0M1OEJGREM0NzhGAAoJEOqwPFi/3EePOD0P/RwpwF9yMUjyAj/KZnphr/0Y
aXHM040qIocIUqnxH7T/vwdhm2w3Zciry8hwXp9f+r2bTIe8+tTn8OwaJ0M/Wp1j
QBPxW+rjw49hy3rf2eIQbgX7nTwdIZo7YDnT82Kqtje1mImTBR4qdFcSStJac4hE
dJsbpzC6raHUuE8h5V5pWPV/m/OQebK3P5CZzBKKpVTMCX3nVsTnff9qf9L1A0Jd
q4KYfOv+NJBaB8G6vJhDHjcqtzGfEJBmYL8kOAslYhlUuyYe+iAhyGFbcUBsXwk8
/dqBalUL2iewFaZppszYZ0rTpVOfA4fOV0ECbVmpcw36uocrC2iOEpBl0WRIy+TM
HYIMkIeubF9IT24CwMwiriONpppl8MGynCmL9hyMgu+HiuvHZ/C/vYcVV9/DHFGB
iKkNe9QjX34anP6qVvEvHHmuv26PO7eq7hkdK2PZNlA9dwwNHehN8xG3DxB9N8gG
MPRGtM8yH/C/FXpqKmHoqj6shMGQCSfmZKPfJ0D49Rze8tSjo7kZaSmaELJAjmsc
xLv5umEAg7gym54bMhv8As2lXHnyeDp3uJz6glM72cmtBM5/n8N7NLk6Xga+8eM3
cZ122dgOqzGpts9TqCGWmTRW+f2Y8hLukzIjOLdzlqLPfQmXVn9pOWmqo9OKHdvD
we0uYcnte/iSltopkVuG
=muco
-----END PGP SIGNATURE-----
-------------- next part --------------
Index: sbin/ipfw/ipfw.8
===================================================================
--- sbin/ipfw/ipfw.8	(revision 278151)
+++ sbin/ipfw/ipfw.8	(working copy)
@@ -166,7 +166,8 @@
 depending on how the kernel is configured.
 .Pp
 If the ruleset includes one or more rules with the
-.Cm keep-state
+.Cm keep-state ,
+.Cm keep-state-only
 or
 .Cm limit
 option,
@@ -582,7 +583,8 @@
 packet delivery.
 .Pp
 Note: this condition is checked before any other condition, including
-ones such as keep-state or check-state which might have side effects.
+ones such as keep-state, keep-stat-only or check-state which might have
+side effects.
 .It Cm log Op Cm logamount Ar number
 Packets matching a rule with the
 .Cm log
@@ -1583,6 +1585,18 @@
 .Xr sysctl 8
 variables), and the lifetime is refreshed every time a matching
 packet is found.
+.It Cm keep-state-only | record-only
+Upon a match, the firewall will create a dynamic rule as if
+.Cm keep-state
+was specified, but after that match is cancelled and the search
+continues with the next rule.
+On dynamic rule match action, specified in this rule,
+performed as if rule contains
+.Cm keep-state .
+This option doesn't act as
+.Cm check-state
+in contrast to
+.Cm keep-state .
 .It Cm layer2
 Matches only layer2 packets, i.e., those passed to
 .Nm
Index: sbin/ipfw/ipfw2.c
===================================================================
--- sbin/ipfw/ipfw2.c	(revision 278151)
+++ sbin/ipfw/ipfw2.c	(working copy)
@@ -292,6 +292,8 @@
 	{ "in",			TOK_IN },
 	{ "limit",		TOK_LIMIT },
 	{ "keep-state",		TOK_KEEPSTATE },
+	{ "record-state",	TOK_STATE_ONLY },
+	{ "keep-state-only",	TOK_STATE_ONLY },
 	{ "bridged",		TOK_LAYER2 },
 	{ "layer2",		TOK_LAYER2 },
 	{ "out",		TOK_OUT },
@@ -1993,6 +1995,10 @@
 				bprintf(bp, " keep-state");
 				break;
 
+			case O_STATE_ONLY:
+				bprintf(bp, " keep-state-only");
+				break;
+
 			case O_LIMIT: {
 				struct _s_x *p = limit_masks;
 				ipfw_insn_limit *c = (ipfw_insn_limit *)cmd;
@@ -4335,14 +4341,16 @@
 			break;
 
 		case TOK_KEEPSTATE:
+		case TOK_STATE_ONLY:
 			if (open_par)
-				errx(EX_USAGE, "keep-state cannot be part "
+				errx(EX_USAGE, "keep-state or keep-state-only cannot be part "
 				    "of an or block");
 			if (have_state)
 				errx(EX_USAGE, "only one of keep-state "
 					"and limit is allowed");
 			have_state = cmd;
-			fill_cmd(cmd, O_KEEP_STATE, 0, 0);
+			fill_cmd(cmd, i == TOK_KEEPSTATE ?
+				O_KEEP_STATE : O_STATE_ONLY, 0, 0);
 			break;
 
 		case TOK_LIMIT: {
@@ -4580,12 +4588,13 @@
 	/*
 	 * generate O_PROBE_STATE if necessary
 	 */
-	if (have_state && have_state->opcode != O_CHECK_STATE) {
+	if (have_state && have_state->opcode != O_CHECK_STATE &&
+	    have_state->opcode != O_STATE_ONLY) {
 		fill_cmd(dst, O_PROBE_STATE, 0, 0);
 		dst = next_cmd(dst, &rblen);
 	}
 
-	/* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */
+	/* copy all commands but O_LOG, O_KEEP_STATE, O_STATE_ONLY, O_LIMIT, O_ALTQ, O_TAG */
 	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
 		i = F_LEN(src);
 		CHECK_RBUFLEN(i);
@@ -4593,6 +4602,7 @@
 		switch (src->opcode) {
 		case O_LOG:
 		case O_KEEP_STATE:
+		case O_STATE_ONLY:
 		case O_LIMIT:
 		case O_ALTQ:
 		case O_TAG:
Index: sbin/ipfw/ipfw2.h
===================================================================
--- sbin/ipfw/ipfw2.h	(revision 278151)
+++ sbin/ipfw/ipfw2.h	(working copy)
@@ -227,6 +227,7 @@
 	TOK_LOCK,
 	TOK_UNLOCK,
 	TOK_VLIST,
+	TOK_STATE_ONLY,
 };
 
 /*
Index: sys/netinet/ip_fw.h
===================================================================
--- sys/netinet/ip_fw.h	(revision 278151)
+++ sys/netinet/ip_fw.h	(working copy)
@@ -252,6 +252,8 @@
 	O_DSCP,			/* 2 u32 = DSCP mask */
 	O_SETDSCP,		/* arg1=DSCP value */
 	O_IP_FLOW_LOOKUP,	/* arg1=table number, u32=value	*/
+	
+	O_STATE_ONLY,		/* none				*/
 
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
Index: sys/netpfil/ipfw/ip_fw2.c
===================================================================
--- sys/netpfil/ipfw/ip_fw2.c	(revision 278151)
+++ sys/netpfil/ipfw/ip_fw2.c	(working copy)
@@ -2107,9 +2107,9 @@
 			 * O_TAG, O_LOG and O_ALTQ action parameters:
 			 *   perform some action and set match = 1;
 			 *
-			 * O_LIMIT and O_KEEP_STATE: these opcodes are
-			 *   not real 'actions', and are stored right
-			 *   before the 'action' part of the rule.
+			 * O_LIMIT, O_KEEP_STATE and O_STATE_ONLY: these
+			 *   opcodes are not real 'actions', and are stored
+			 *   right before the 'action' part of the rule.
 			 *   These opcodes try to install an entry in the
 			 *   state tables; if successful, we continue with
 			 *   the next opcode (match=1; break;), otherwise
@@ -2126,17 +2126,33 @@
 			 *   further instances of these opcodes become NOPs.
 			 *   The jump to the next rule is done by setting
 			 *   l=0, cmdlen=0.
+			 *
+			 * O_STATE_ONLY: this opcode is not real 'action'
+			 *  too, and is stored right before the 'action'
+			 *  part of the rule, right after O_KEEP_STATE
+			 *  opcode. It causes match failure so real
+			 *  'action' could be executed only if rule
+			 *  is checked via dynamic rule from state
+			 *  table, as in such case execution starts
+			 *  from true 'action' opcode directly.
+			 *   
 			 */
 			case O_LIMIT:
 			case O_KEEP_STATE:
-				if (ipfw_install_state(chain, f,
-				    (ipfw_insn_limit *)cmd, args, tablearg)) {
+			case O_STATE_ONLY:
+				if (ipfw_install_or_update_state(chain, f,
+				    (ipfw_insn_limit *)cmd, args, tablearg,
+				    proto == IPPROTO_TCP ? TCP(ulp) : NULL)) {
 					/* error or limit violation */
 					retval = IP_FW_DENY;
 					l = 0;	/* exit inner loop */
 					done = 1; /* exit outer loop */
 				}
-				match = 1;
+				if (cmd->opcode == O_STATE_ONLY) {
+					l = 0;	/* exit inner loop */
+					match = 0;
+				} else
+					match = 1;
 				break;
 
 			case O_PROBE_STATE:
@@ -2188,6 +2204,7 @@
 				break;
 
 			case O_ACCEPT:
+
 				retval = 0;	/* accept */
 				l = 0;		/* exit inner loop */
 				done = 1;	/* exit outer loop */
@@ -2537,7 +2554,7 @@
 				done = 1;	/* exit outer loop */
 				break;
 			}
-
+			
 			default:
 				panic("-- unknown opcode %d\n", cmd->opcode);
 			} /* end of switch() on opcodes */
Index: sys/netpfil/ipfw/ip_fw_dynamic.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_dynamic.c	(revision 278151)
+++ sys/netpfil/ipfw/ip_fw_dynamic.c	(working copy)
@@ -669,13 +669,15 @@
 
 /**
  * Install dynamic state for rule type cmd->o.opcode
+ * If rule exists, update it state.
  *
  * Returns 1 (failure) if state is not installed because of errors or because
  * session limitations are enforced.
  */
 int
-ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule,
-    ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg)
+ipfw_install_or_update_state(struct ip_fw_chain *chain, struct ip_fw *rule,
+    ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg,
+    struct tcphdr *tcp)
 {
 	ipfw_dyn_rule *q;
 	int i;
@@ -686,7 +688,7 @@
 
 	IPFW_BUCK_LOCK(i);
 
-	q = lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL);
+	q = lookup_dyn_rule_locked(&args->f_id, i, NULL, tcp);
 
 	if (q != NULL) {	/* should never occur */
 		DEB(
@@ -708,6 +710,7 @@
 
 	switch (cmd->o.opcode) {
 	case O_KEEP_STATE:	/* bidir rule */
+	case O_STATE_ONLY:
 		q = add_dyn_rule(&args->f_id, i, O_KEEP_STATE, rule);
 		break;
 
@@ -1357,6 +1360,7 @@
 		switch (cmd->opcode) {
 		case O_LIMIT:
 		case O_KEEP_STATE:
+		case O_STATE_ONLY:
 		case O_PROBE_STATE:
 		case O_CHECK_STATE:
 			return (1);
Index: sys/netpfil/ipfw/ip_fw_private.h
===================================================================
--- sys/netpfil/ipfw/ip_fw_private.h	(revision 278151)
+++ sys/netpfil/ipfw/ip_fw_private.h	(working copy)
@@ -183,8 +183,9 @@
 struct tcphdr;
 struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *,
     u_int32_t, u_int32_t, int);
-int ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule,
-    ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg);
+int ipfw_install_or_update_state(struct ip_fw_chain *chain, struct ip_fw *rule,
+    ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg,
+    struct tcphdr *tcp);
 ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt,
 	int *match_direction, struct tcphdr *tcp);
 void ipfw_remove_dyn_children(struct ip_fw *rule);
Index: sys/netpfil/ipfw/ip_fw_sockopt.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_sockopt.c	(revision 278151)
+++ sys/netpfil/ipfw/ip_fw_sockopt.c	(working copy)
@@ -1433,6 +1433,7 @@
 		switch (cmd->opcode) {
 		case O_PROBE_STATE:
 		case O_KEEP_STATE:
+		case O_STATE_ONLY:
 		case O_PROTO:
 		case O_IP_SRC_ME:
 		case O_IP_DST_ME:
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ipfw-state-only-v3.diff.sig
Type: application/octet-stream
Size: 639 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-net/attachments/20150204/6a3cc24b/attachment.obj>


More information about the freebsd-net mailing list