[RFC][patch] Two new actions: state-allow and state-deny

Lev Serebryakov lev at FreeBSD.org
Mon Feb 2 19:17:38 UTC 2015


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


 Now to make stateful firewall with NAT you need to make some not very
"readable" tricks to record state ("allow") of outbound connection
before NAT, but pass packet to NAT after that. I know two:

 (a) skipto-nat-allow pattern from many HOWOTOs

add 1000 skipto 2000 all from any to any out xmit outIface
add 1010 skipto 3000 all from any to any in  recv outIface

add 2000 skipto 2010 from any to any keep-state
add 2010 nat NR from any to any out // Note this "out" in out section!
add 2020 allow all from any to any

add 3000 nat NR from any to any
add 3010 check-state // Use dynamic rule based on 2000

 (b) Adding "allow keep-state" to _IN_ rules on _internal_ interfaces
to check this states AFTER _IN_ nat on _external_ interfaces.

 I don't like both of them. First one is not very clear and needs
additional "out" option on outbound NAT rule. Second one requires to
have "allow keep-state" and "check-state" rules in different parts of
firewall, on different interfaces, which is not very clear too and
needs additional conditions for "allow keep-state" if you don't want
stateful firewall for internal networks and only want states for
external traffic.

 I propose two new actions: state-allow and state-deny.

 They imply "keep-state" and create new dynamic rules, when called
directly, but pass packet to NEXT rule after that (don't stop search).

 When they are called as dynamic rule, they acts as "allow" and "deny".

 So, stateful firewall with NAT could be rewritten like this:

add 1000 skipto 2000 all from any to any out xmit outIface
add 1010 skipto 3000 all from any to any in  recv outIface

add 2000 state-allow from any to any // keep-state is implied
add 2010 nat NR from any to any // No "out" here!
add 2020 allow all from any to any

add 3000 nat NR from any to any
add 3010 check-state // Use dynamic rule based on 2000 as "allow" here

 What do you think?

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

iQJ8BAEBCgBmBQJUz81EXxSAAAAAAC4AKGlzc3Vlci1mcHJAbm90YXRpb25zLm9w
ZW5wZ3AuZmlmdGhob3JzZW1hbi5uZXRGOTZEMUNBMEI1RjQzMThCNjc0QjMzMEFF
QUIwM0M1OEJGREM0NzhGAAoJEOqwPFi/3EePkhwQAJg4s1Ipomi0lZTqa8pklExD
GHvkeuVdm1RSakolwHf8M26a+Xg1zlIm0tD2PQ18FkaA1QTjwai7kyKu2SwhsvkF
P7B3GE33Pj4dhMzhpnmSnxcjLZEAENENzAOnGBN47NM617KOkJmyRmH54RO8xFI8
UbctlfiWC0ECujlWC4HcLthlrI3ZemqeFK1llzQ+k0LgUDQ8eegFmrLCbMVbVKxJ
4HACPQzzPwzabZE+kifE1KDnOEthEuTXuMpL6pS98s8w+b92TFJsS40DqngWNuqv
M2QCCJbLZRwoDRTkf3H8AlbdIk94CPFjJkmZd86ZUpKF3rVJ7VICH7SkV90P1hlm
yRc/26jX2LvqyyKgxMDQ4UpJuSikxASHx/3mDOV83snxlXtwW0or6f+XSW1QVFFt
2OCo6DmwclQ2HzBaomy0QKqlKq09VzHJdEBtfsyBqKyQP2UG3/CDj6rwqc564rOb
MDJFDtsMsquOgJTBSYLcAQhc8v9I3HUuELT/eyo3YCCrPKAAPtV89jWZ2dI+np3h
utaVJxJ4qSyVp5R3H2MTvWdk1PPptygxx0UHMVyNTgSnsbczNsywWzELoOzTzEZn
XS352D2dWXsvFV07cwtHovnY+vCKOXVI2ljJ6uHwZZlisJg4M+o80LChHT5jQ6nw
9DVWmu2YK6nC7aJI6Fy7
=TMJo
-----END PGP SIGNATURE-----
-------------- next part --------------
Index: sbin/ipfw/ipfw.8
===================================================================
--- sbin/ipfw/ipfw.8	(revision 278021)
+++ sbin/ipfw/ipfw.8	(working copy)
@@ -932,6 +932,26 @@
 .Ed
 .Pp
 This cosmetic annoyance may be fixed in future releases.
+.It Cm state-allow
+Create dynamic rule which acts as
+.Cm allow
+rule when checked with
+.Cm check-state
+action.
+When this action is taken directly, search continues with the next rule.
+This action implies
+.Cm keep-state
+instruction.
+.It Cm state-deny
+Create dynamic rule which acts as
+.Cm deny
+rule when checked with
+.Cm check-state
+action.
+When this action is taken directly, search continues with the next rule.
+This action implies
+.Cm keep-state
+instruction.
 .It Cm tee Ar port
 Send a copy of packets matching this rule to the
 .Xr divert 4
Index: sbin/ipfw/ipfw2.c
===================================================================
--- sbin/ipfw/ipfw2.c	(revision 278021)
+++ sbin/ipfw/ipfw2.c	(working copy)
@@ -264,6 +264,12 @@
 	{ "setdscp",		TOK_SETDSCP },
 	{ "call",		TOK_CALL },
 	{ "return",		TOK_RETURN },
+	{ "state-accept",	TOK_STATE_ACCEPT },
+	{ "state-pass",		TOK_STATE_ACCEPT },
+	{ "state-allow",	TOK_STATE_ACCEPT },
+	{ "state-permit",	TOK_STATE_ACCEPT },
+	{ "state-deny",		TOK_STATE_DENY },
+	{ "state-drop",		TOK_STATE_DENY },
 	{ NULL, 0 }	/* terminator */
 };
 
@@ -1584,6 +1590,14 @@
 				bprint_uint_arg(bp, "call ", cmd->arg1);
 			break;
 
+		case O_STATE_ACCEPT:
+			bprintf(bp, "state-allow");
+			break;
+
+		case O_STATE_DENY:
+			bprintf(bp, "state-deny");
+			break;
+
 		default:
 			bprintf(bp, "** unrecognized action %d len %d ",
 				cmd->opcode, cmd->len);
@@ -3807,6 +3821,16 @@
 		fill_cmd(action, O_CALLRETURN, F_NOT, 0);
 		break;
 
+	case TOK_STATE_ACCEPT:
+		have_state = action;
+		action->opcode = O_STATE_ACCEPT;
+		break;
+
+	case TOK_STATE_DENY:
+		have_state = action;
+		action->opcode = O_STATE_DENY;
+		break;
+
 	default:
 		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
 	}
@@ -3898,7 +3922,7 @@
 		cmd = next_cmd(cmd, &cblen);
 	}
 
-	if (have_state)	/* must be a check-state, we are done */
+	if (have_state && have_state->opcode == TOK_CHECKSTATE)	/* check-state, we are done */
 		goto done;
 
 #define OR_START(target)					\
@@ -4580,7 +4604,9 @@
 	/*
 	 * 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_ACCEPT &&
+	    have_state->opcode != O_STATE_DENY) {
 		fill_cmd(dst, O_PROBE_STATE, 0, 0);
 		dst = next_cmd(dst, &rblen);
 	}
@@ -4606,7 +4632,9 @@
 	/*
 	 * put back the have_state command as last opcode
 	 */
-	if (have_state && have_state->opcode != O_CHECK_STATE) {
+	if (have_state && have_state->opcode != O_CHECK_STATE &&
+	    have_state->opcode != O_STATE_ACCEPT &&
+	    have_state->opcode != O_STATE_DENY) {
 		i = F_LEN(have_state);
 		CHECK_RBUFLEN(i);
 		bcopy(have_state, dst, i * sizeof(uint32_t));
Index: sbin/ipfw/ipfw2.h
===================================================================
--- sbin/ipfw/ipfw2.h	(revision 278021)
+++ sbin/ipfw/ipfw2.h	(working copy)
@@ -103,6 +103,8 @@
 	TOK_REASS,
 	TOK_CALL,
 	TOK_RETURN,
+	TOK_STATE_ACCEPT,
+	TOK_STATE_DENY,
 
 	TOK_ALTQ,
 	TOK_LOG,
Index: sys/netinet/ip_fw.h
===================================================================
--- sys/netinet/ip_fw.h	(revision 278021)
+++ sys/netinet/ip_fw.h	(working copy)
@@ -253,6 +253,9 @@
 	O_SETDSCP,		/* arg1=DSCP value */
 	O_IP_FLOW_LOOKUP,	/* arg1=table number, u32=value	*/
 
+	O_STATE_ACCEPT,		/* none				*/
+	O_STATE_DENY,		/* none 			*/
+
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 
Index: sys/netpfil/ipfw/ip_fw2.c
===================================================================
--- sys/netpfil/ipfw/ip_fw2.c	(revision 278021)
+++ sys/netpfil/ipfw/ip_fw2.c	(working copy)
@@ -1856,7 +1856,7 @@
 
 			case O_LOG:
 				ipfw_log(chain, f, hlen, args, m,
-				    oif, offset | ip6f_mf, tablearg, ip);
+				    oif, offset | ip6f_mf, tablearg, ip, q);
 				match = 1;
 				break;
 
@@ -2188,7 +2188,7 @@
 				break;
 
 			case O_ACCEPT:
-				retval = 0;	/* accept */
+				retval = IP_FW_PASS;	/* accept */
 				l = 0;		/* exit inner loop */
 				done = 1;	/* exit outer loop */
 				break;
@@ -2538,6 +2538,24 @@
 				break;
 			}
 
+			case O_STATE_ACCEPT:
+			case O_STATE_DENY: {
+				l = 0;	/* in any case exit inner loop */
+				if (q == NULL || q->rule != f) {
+					if (ipfw_install_state(chain, f,
+					    (ipfw_insn_limit *)cmd, args, tablearg)) {
+						/* error or limit violation */
+						retval = IP_FW_DENY;
+						done = 1; /* exit outer loop */
+					}
+				} else {
+					retval = cmd->opcode == O_STATE_ACCEPT ?
+					    IP_FW_PASS : IP_FW_DENY;
+					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 278021)
+++ sys/netpfil/ipfw/ip_fw_dynamic.c	(working copy)
@@ -708,6 +708,8 @@
 
 	switch (cmd->o.opcode) {
 	case O_KEEP_STATE:	/* bidir rule */
+	case O_STATE_ALLOW:
+	case O_STATE_DENY:
 		q = add_dyn_rule(&args->f_id, i, O_KEEP_STATE, rule);
 		break;
 
Index: sys/netpfil/ipfw/ip_fw_log.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_log.c	(revision 278021)
+++ sys/netpfil/ipfw/ip_fw_log.c	(working copy)
@@ -248,7 +248,7 @@
 void
 ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
     struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif,
-    u_short offset, uint32_t tablearg, struct ip *ip)
+    u_short offset, uint32_t tablearg, struct ip *ip, ipfw_dyn_rule *q)
 {
 	char *action;
 	int limit_reached = 0;
@@ -419,6 +419,18 @@
 				snprintf(SNPARGS(action2, 0), "Call %d",
 				    cmd->arg1);
 			break;
+		case O_STATE_ACCEPT:
+			if (q != NULL && q->rule == f)
+				action = "Accept";
+			else
+				action = "Create accept state";
+			break;
+		case O_STATE_DENY:
+			if (q != NULL && q->rule == f)
+				action = "Deny";
+			else
+				action = "Create deny state";
+			break;
 		default:
 			action = "UNKNOWN";
 			break;
Index: sys/netpfil/ipfw/ip_fw_private.h
===================================================================
--- sys/netpfil/ipfw/ip_fw_private.h	(revision 278021)
+++ sys/netpfil/ipfw/ip_fw_private.h	(working copy)
@@ -154,7 +154,7 @@
 void ipfw_log_bpf(int);
 void ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
     struct ip_fw_args *args, struct mbuf *m, struct ifnet *oif,
-    u_short offset, uint32_t tablearg, struct ip *ip);
+    u_short offset, uint32_t tablearg, struct ip *ip, ipfw_dyn_rule *q);
 VNET_DECLARE(u_int64_t, norule_counter);
 #define	V_norule_counter	VNET(norule_counter)
 VNET_DECLARE(int, verbose_limit);
Index: sys/netpfil/ipfw/ip_fw_sockopt.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_sockopt.c	(revision 278021)
+++ sys/netpfil/ipfw/ip_fw_sockopt.c	(working copy)
@@ -1645,6 +1645,8 @@
 		case O_SKIPTO:
 		case O_REASS:
 		case O_CALLRETURN:
+		case O_STATE_ACCEPT:
+		case O_STATE_DENY:
 check_size:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
 				goto bad_size;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ipfw-state-actions.diff.sig
Type: application/octet-stream
Size: 639 bytes
Desc: not available
URL: <http://lists.freebsd.org/pipermail/freebsd-net/attachments/20150202/02a81452/attachment.obj>


More information about the freebsd-net mailing list