Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 26 Jan 2012 10:33:19 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org
Subject:   svn commit: r230575 - in stable/8: sbin/ipfw sys/netinet sys/netinet/ipfw
Message-ID:  <201201261033.q0QAXJSA076754@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Thu Jan 26 10:33:19 2012
New Revision: 230575
URL: http://svn.freebsd.org/changeset/base/230575

Log:
  MFC r223666:
    Add new rule actions "call" and "return" to ipfw. They make
    possible to organize subroutines with rules.
  
    The "call" action saves the current rule number in the internal
    stack and rules processing continues from the first rule with
    specified number (similar to skipto action). If later a rule with
    "return" action is encountered, the processing returns to the first
    rule with number of "call" rule saved in the stack plus one or higher.
  
    Submitted by:	Vadim Goncharov

Modified:
  stable/8/sbin/ipfw/ipfw.8
  stable/8/sbin/ipfw/ipfw2.c
  stable/8/sbin/ipfw/ipfw2.h
  stable/8/sys/netinet/ip_fw.h
  stable/8/sys/netinet/ip_var.h
  stable/8/sys/netinet/ipfw/ip_fw2.c
  stable/8/sys/netinet/ipfw/ip_fw_log.c
  stable/8/sys/netinet/ipfw/ip_fw_sockopt.c
Directory Properties:
  stable/8/sbin/ipfw/   (props changed)
  stable/8/sys/   (props changed)

Modified: stable/8/sbin/ipfw/ipfw.8
==============================================================================
--- stable/8/sbin/ipfw/ipfw.8	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sbin/ipfw/ipfw.8	Thu Jan 26 10:33:19 2012	(r230575)
@@ -1,7 +1,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 14, 2011
+.Dd June 29, 2011
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -818,6 +818,78 @@ skipto, but care should be used, as no d
 is possible in this case so the rules are always walked to find it,
 starting from the 
 .Cm skipto .
+.It Cm call Ar number | tablearg
+The current rule number is saved in the internal stack and
+ruleset processing continues with the first rule numbered
+.Ar number
+or higher.
+If later a rule with the
+.Cm return
+action is encountered, the processing returns to the first rule
+with number of this
+.Cm call
+rule plus one or higher
+(the same behaviour as with packets returning from
+.Xr divert 4
+socket after a
+.Cm divert
+action).
+This could be used to make somewhat like an assembly language
+.Dq subroutine
+calls to rules with common checks for different interfaces, etc.
+.Pp
+Rule with any number could be called, not just forward jumps as with
+.Cm skipto .
+So, to prevent endless loops in case of mistakes, both
+.Cm call
+and
+.Cm return
+actions don't do any jumps and simply go to the next rule if memory
+can't be allocated or stack overflowed/undeflowed.
+.Pp
+Internally stack for rule numbers is implemented using
+.Xr mbuf_tags 9
+facility and currently has size of 16 entries.
+As mbuf tags are lost when packet leaves the kernel,
+.Cm divert
+should not be used in subroutines to avoid endless loops
+and other undesired effects.
+.It Cm return
+Takes rule number saved to internal stack by the last
+.Cm call
+action and returns ruleset processing to the first rule
+with number greater than number of corresponding
+.Cm call
+rule. See description of the
+.Cm call
+action for more details.
+.Pp
+Note that
+.Cm return
+rules usually end a
+.Dq subroutine
+and thus are unconditional, but
+.Nm
+command-line utility currently requires every action except
+.Cm check-state
+to have body.
+While it is sometimes useful to return only on some packets,
+usually you want to print just
+.Dq return
+for readability.
+A workaround for this is to use new syntax and
+.Fl c
+switch:
+.Pp
+.Bd -literal -offset indent
+# Add a rule without actual body
+ipfw add 2999 return via any
+
+# List rules without "from any to any" part
+ipfw -c list
+.Ed
+.Pp
+This cosmetic annoyance may be fixed in future releases.
 .It Cm tee Ar port
 Send a copy of packets matching this rule to the
 .Xr divert 4
@@ -3240,3 +3312,18 @@ for the respective conversations.
 To avoid failures of network error detection and path MTU discovery,
 ICMP error messages may need to be allowed explicitly through static
 rules.
+.Pp
+Rules using
+.Cm call
+and
+.Cm return
+actions may lead to confusing behaviour if ruleset has mistakes,
+and/or interaction with other subsystems (netgraph, dummynet, etc.) is used.
+One possible case for this is packet leaving
+.Nm
+in subroutine on the input pass, while later on output encountering unpaired
+.Cm return
+first.
+As the call stack is kept intact after input pass, packet will suddenly
+return to the rule number used on input pass, not on output one.
+Order of processing should be checked carefully to avoid such mistakes.

Modified: stable/8/sbin/ipfw/ipfw2.c
==============================================================================
--- stable/8/sbin/ipfw/ipfw2.c	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sbin/ipfw/ipfw2.c	Thu Jan 26 10:33:19 2012	(r230575)
@@ -213,6 +213,8 @@ static struct _s_x rule_actions[] = {
 	{ "nat",		TOK_NAT },
 	{ "reass",		TOK_REASS },
 	{ "setfib",		TOK_SETFIB },
+	{ "call",		TOK_CALL },
+	{ "return",		TOK_RETURN },
 	{ NULL, 0 }	/* terminator */
 };
 
@@ -1126,6 +1128,13 @@ show_ipfw(struct ip_fw *rule, int pcwidt
 			printf("reass");
 			break;
 
+		case O_CALLRETURN:
+			if (cmd->len & F_NOT)
+				printf("return");
+			else
+				PRINT_UINT_ARG("call ", cmd->arg1);
+			break;
+
 		default:
 			printf("** unrecognized action %d len %d ",
 				cmd->opcode, cmd->len);
@@ -2762,6 +2771,9 @@ ipfw_add(char *av[])
 		goto chkarg;
 	case TOK_TEE:
 		action->opcode = O_TEE;
+		goto chkarg;
+	case TOK_CALL:
+		action->opcode = O_CALLRETURN;
 chkarg:
 		if (!av[0])
 			errx(EX_USAGE, "missing argument for %s", *(av - 1));
@@ -2854,6 +2866,10 @@ chkarg:
 		action->opcode = O_REASS;
 		break;
 
+	case TOK_RETURN:
+		fill_cmd(action, O_CALLRETURN, F_NOT, 0);
+		break;
+
 	default:
 		errx(EX_DATAERR, "invalid action %s\n", av[-1]);
 	}

Modified: stable/8/sbin/ipfw/ipfw2.h
==============================================================================
--- stable/8/sbin/ipfw/ipfw2.h	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sbin/ipfw/ipfw2.h	Thu Jan 26 10:33:19 2012	(r230575)
@@ -99,6 +99,8 @@ enum tokens {
 	TOK_CHECKSTATE,
 	TOK_NAT,
 	TOK_REASS,
+	TOK_CALL,
+	TOK_RETURN,
 
 	TOK_ALTQ,
 	TOK_LOG,

Modified: stable/8/sys/netinet/ip_fw.h
==============================================================================
--- stable/8/sys/netinet/ip_fw.h	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sys/netinet/ip_fw.h	Thu Jan 26 10:33:19 2012	(r230575)
@@ -57,6 +57,12 @@
 #define IP_FW_TABLEARG		65535	/* XXX should use 0 */
 
 /*
+ * Number of entries in the call stack of the call/return commands.
+ * Call stack currently is an uint16_t array with rule numbers.
+ */
+#define	IPFW_CALLSTACK_SIZE	16
+
+/*
  * The kernel representation of ipfw rules is made of a list of
  * 'instructions' (for all practical purposes equivalent to BPF
  * instructions), which specify which fields of the packet
@@ -193,6 +199,8 @@ enum ipfw_opcodes {		/* arguments (4 byt
 	O_SETFIB,		/* arg1=FIB number */
 	O_FIB,			/* arg1=FIB desired fib number */
 
+	O_CALLRETURN,		/* arg1=called rule number */
+
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
 

Modified: stable/8/sys/netinet/ip_var.h
==============================================================================
--- stable/8/sys/netinet/ip_var.h	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sys/netinet/ip_var.h	Thu Jan 26 10:33:19 2012	(r230575)
@@ -287,6 +287,7 @@ enum {
 };
 #define MTAG_IPFW	1148380143	/* IPFW-tagged cookie */
 #define MTAG_IPFW_RULE	1262273568	/* rule reference */
+#define	MTAG_IPFW_CALL	1308397630	/* call stack */
 
 struct ip_fw_args;
 typedef int	(*ip_fw_chk_ptr_t)(struct ip_fw_args *args);

Modified: stable/8/sys/netinet/ipfw/ip_fw2.c
==============================================================================
--- stable/8/sys/netinet/ipfw/ip_fw2.c	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sys/netinet/ipfw/ip_fw2.c	Thu Jan 26 10:33:19 2012	(r230575)
@@ -2030,6 +2030,123 @@ do {								\
 			    continue;
 			    break;	/* not reached */
 
+			case O_CALLRETURN: {
+				/*
+				 * Implementation of `subroutine' call/return,
+				 * in the stack carried in an mbuf tag. This
+				 * is different from `skipto' in that any call
+				 * address is possible (`skipto' must prevent
+				 * backward jumps to avoid endless loops).
+				 * We have `return' action when F_NOT flag is
+				 * present. The `m_tag_id' field is used as
+				 * stack pointer.
+				 */
+				struct m_tag *mtag;
+				uint16_t jmpto, *stack;
+
+#define	IS_CALL		((cmd->len & F_NOT) == 0)
+#define	IS_RETURN	((cmd->len & F_NOT) != 0)
+				/*
+				 * Hand-rolled version of m_tag_locate() with
+				 * wildcard `type'.
+				 * If not already tagged, allocate new tag.
+				 */
+				mtag = m_tag_first(m);
+				while (mtag != NULL) {
+					if (mtag->m_tag_cookie ==
+					    MTAG_IPFW_CALL)
+						break;
+					mtag = m_tag_next(m, mtag);
+				}
+				if (mtag == NULL && IS_CALL) {
+					mtag = m_tag_alloc(MTAG_IPFW_CALL, 0,
+					    IPFW_CALLSTACK_SIZE *
+					    sizeof(uint16_t), M_NOWAIT);
+					if (mtag != NULL)
+						m_tag_prepend(m, mtag);
+				}
+
+				/*
+				 * On error both `call' and `return' just
+				 * continue with next rule.
+				 */
+				if (IS_RETURN && (mtag == NULL ||
+				    mtag->m_tag_id == 0)) {
+					l = 0;		/* exit inner loop */
+					break;
+				}
+				if (IS_CALL && (mtag == NULL ||
+				    mtag->m_tag_id >= IPFW_CALLSTACK_SIZE)) {
+					printf("ipfw: call stack error, "
+					    "go to next rule\n");
+					l = 0;		/* exit inner loop */
+					break;
+				}
+
+				f->pcnt++;	/* update stats */
+				f->bcnt += pktlen;
+				f->timestamp = time_uptime;
+				stack = (uint16_t *)(mtag + 1);
+
+				/*
+				 * The `call' action may use cached f_pos
+				 * (in f->next_rule), whose version is written
+				 * in f->next_rule.
+				 * The `return' action, however, doesn't have
+				 * fixed jump address in cmd->arg1 and can't use
+				 * cache.
+				 */
+				if (IS_CALL) {
+					stack[mtag->m_tag_id] = f->rulenum;
+					mtag->m_tag_id++;
+					if (cmd->arg1 != IP_FW_TABLEARG &&
+					    (uintptr_t)f->x_next == chain->id) {
+						f_pos = (uintptr_t)f->next_rule;
+					} else {
+						jmpto = (cmd->arg1 ==
+						    IP_FW_TABLEARG) ? tablearg:
+						    cmd->arg1;
+						f_pos = ipfw_find_rule(chain,
+						    jmpto, 0);
+						/* update the cache */
+						if (cmd->arg1 !=
+						    IP_FW_TABLEARG) {
+							f->next_rule =
+							    (void *)(uintptr_t)
+							    f_pos;
+							f->x_next =
+							    (void *)(uintptr_t)
+							    chain->id;
+						}
+					}
+				} else {	/* `return' action */
+					mtag->m_tag_id--;
+					jmpto = stack[mtag->m_tag_id] + 1;
+					f_pos = ipfw_find_rule(chain, jmpto, 0);
+				}
+
+				/*
+				 * Skip disabled rules, and re-enter
+				 * the inner loop with the correct
+				 * f_pos, f, l and cmd.
+				 * Also clear cmdlen and skip_or
+				 */
+				for (; f_pos < chain->n_rules - 1 &&
+				    (V_set_disable &
+				    (1 << chain->map[f_pos]->set)); f_pos++)
+					;
+				/* Re-enter the inner loop at the dest rule. */
+				f = chain->map[f_pos];
+				l = f->cmd_len;
+				cmd = f->cmd;
+				cmdlen = 0;
+				skip_or = 0;
+				continue;
+				break;	/* NOTREACHED */
+			}
+#undef IS_CALL
+#undef IS_RETURN
+
 			case O_REJECT:
 				/*
 				 * Drop the packet and send a reject notice

Modified: stable/8/sys/netinet/ipfw/ip_fw_log.c
==============================================================================
--- stable/8/sys/netinet/ipfw/ip_fw_log.c	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sys/netinet/ipfw/ip_fw_log.c	Thu Jan 26 10:33:19 2012	(r230575)
@@ -299,6 +299,13 @@ ipfw_log(struct ip_fw *f, u_int hlen, st
 		case O_REASS:
 			action = "Reass";
 			break;
+		case O_CALLRETURN:
+			if (cmd->len & F_NOT)
+				action = "Return";
+			else
+				snprintf(SNPARGS(action2, 0), "Call %d",
+				    cmd->arg1);
+			break;
 		default:
 			action = "UNKNOWN";
 			break;

Modified: stable/8/sys/netinet/ipfw/ip_fw_sockopt.c
==============================================================================
--- stable/8/sys/netinet/ipfw/ip_fw_sockopt.c	Thu Jan 26 10:30:50 2012	(r230574)
+++ stable/8/sys/netinet/ipfw/ip_fw_sockopt.c	Thu Jan 26 10:33:19 2012	(r230575)
@@ -746,6 +746,7 @@ check_ipfw_struct(struct ip_fw *rule, in
 #endif
 		case O_SKIPTO:
 		case O_REASS:
+		case O_CALLRETURN:
 check_size:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
 				goto bad_size;



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