[RFC][patch] New "keep-state-only" option

Lev Serebryakov lev at FreeBSD.org
Tue Feb 3 16:13:33 UTC 2015


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


 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:

nat 1 config if outIface

1000 skipto 2000 in
     skipto 3000 out
     deny all from any to any // Safeguard
2000 skipto 4000 recv inIface
     skipto 6000 recv outIface
     deny all from any to any // Safeguard
3000 skipto 5000 xmit inIface
     skipto 7000 xmit outIface
     deny all from any to any // Safeguard
4000 // For sake of simplicity!
     // Real firewall will have some checks about local network here
     allow all from any to any
     deny all from any to any // Safeguard
5000 // For sake of simplicity!
     // Real firewall will have some checks about local network here
     allow all from any to any
     deny all from any to any // Safeguard
6000 deny all not dst-ip $EXT_IP
     nat 1 all from any to any
     // All enabled with "keep-state-only" at block 7000 before NAT
     check-state all from any to any
     // Here could be accept rules for our servers or servers in DMZ
     // Disable everything else
     deny all from any to any
7000 // Here goes rules which could DISABLE outbound external traffic
     // Create state for "check-state" at block 6000 and fallthrough
     allow keep-state-only
     allow src-ip $EXT_IP      // Save NAT some work
     nat 1 all from any to any
     allow all from any to any
     deny all from any to any // Safeguard

 And variants with multiple NATs and "nat global" becomes as easy as
this, too! No stupid "skipto", no "keep-state" at "incoming from local
network" parts of firewall, nothing!

P.S. I HATE this "all any to any" part!

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

iQJ8BAEBCgBmBQJU0POaXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w
ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRGOTZEMUNBMEI1RjQzMThCNjc0QjMzMEFF
QUIwM0M1OEJGREM0NzhGAAoJEOqwPFi/3EePR+gP/1Oxi+h7pi0UlnqfrKyfHJRS
FUbrMNeR9NATnTwxIK1UxNT1kF3m7wiwnFlgwW7rwLtTviFB1wK/pfd38l2h4t/w
qUbtyK4PFMCq8I6wAJIB0qUl3C/mN1rwc+LSJJyFM07R52snoQs6FvkIYkCz0fOy
Cak1f/P+scc21IRhFvYJXMMDO/1Y1nkxZk/HdHbn1GELpTXuHugvL1T9hHl98sqO
HKlHnvtqAVlyZn9Sv3uC9nsyjFA2sdOCtb67UGnPDV3CIs4Jwj5CSst5jbz13qFG
aXF8ZSm0coPJMUjH1PSogZM9Xiq23yZ47V0mesBxQsHL24548jM/wKcsR3buDjP7
NJ2rqo2OBCzTu6VCK2oIY5j9A6vq1mu8+/eBs5jF4C2k0xHiw53Okou7zOCA0gJ+
z+VGZvD3la/+tFjacty7Ra7LLNA8kNCnRa0QML7LOJ1/99a4l3Z/uGFxy5zYnk7d
p27Y85CAhTJQjkYZSGAiFD5SE4XxRqtSJ9OL89w7vLxoHqW0rqwi+DVrr9uvXQZS
8Z5G5iQARG4ygXuKsl6MlwChCXa3ucbOs41lorrug94cuVCwGg859zBZY3dpQsKz
XIhtVQS21wPLxXywzIc678ar4uKVWNiaRWg+k57O7375gAszvqujRuTEcfHRf/T+
gHJJZt8Tc+en4bw8XItY
=wOAJ
-----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,
@@ -180,7 +181,8 @@
 Dynamic rules, which have a limited lifetime, are checked
 at the first occurrence of a
 .Cm check-state ,
-.Cm keep-state
+.Cm keep-state ,
+.Cm keep-state-only
 or
 .Cm limit
 rule, and are typically used to open the firewall on-demand to
@@ -582,7 +584,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
@@ -748,7 +751,8 @@
 If no
 .Cm check-state
 rule is found, the dynamic ruleset is checked at the first
-.Cm keep-state
+.Cm keep-state ,
+.Cm keep-state-only ,
 or
 .Cm limit
 rule.
@@ -1583,6 +1587,14 @@
 .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 .
 .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: {
@@ -4585,7 +4593,7 @@
 		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 +4601,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,9 +2126,20 @@
 			 *   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:
+			case O_STATE_ONLY:
 				if (ipfw_install_state(chain, f,
 				    (ipfw_insn_limit *)cmd, args, tablearg)) {
 					/* error or limit violation */
@@ -2136,7 +2147,11 @@
 					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 +2203,7 @@
 				break;
 
 			case O_ACCEPT:
+
 				retval = 0;	/* accept */
 				l = 0;		/* exit inner loop */
 				done = 1;	/* exit outer loop */
@@ -2537,7 +2553,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)
@@ -708,6 +708,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 +1358,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_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.diff.sig
Type: application/octet-stream
Size: 639 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-net/attachments/20150203/a92b8ae6/attachment.obj>


More information about the freebsd-net mailing list