Re: kern/80642: [patch] IPFW small patch - new RULE OPTION

From: Andrey V. Elsukov <bu7cher_at_yandex.ru>
Date: Fri, 17 Jun 2005 14:45:35 +0400
Robert Watson wrote:
  > This patch breaks the ABI by inserting a new type into an implicitly
  > numbered enumeration, renumbering all entries later in the enum.
  > O_BOUND, if added, should be appended to the end, and/or we should
  > number  the operations explicitly.

Ok. I have corrected this.
* ipfw_bound.diff - the patch with smallest changes, with only bound option.
* ipfw_bound2.diff - bound and check-bound option.

Examples:

We can limit incoming traffic (internet is external interface):
# ipfw add allow ip from any to 10.0.0.20 in recv internet bound 10MB
# ipfw add deny ip from any to 10.0.0.0/24 in recv internet

We can use traffic shaper after excess of a limit:
# ipfw add allow ip from any to 10.0.0.20 in recv internet bound 10MB
# ipfw add pipe 1 ip from any to 10.0.0.20 in recv internet
# ipfw pipe 1 config bw 5Kbit/s queue 10Kbytes

We can block any access after limit excess:
# ipfw add 100 allow ip from 10.0.0.20 to any out xmit internet \
check-bound 200
# ipfw add 200 allow ip from any to 10.0.0.20 in recv internet bound \ 10MB
# ipfw add 300 deny ip from any to any

More details you can read on http://butcher.heavennet.ru/
-- 
WBR, Andrey V. Elsukov

--- sbin/ipfw/ipfw2.c	Tue Jun  7 18:11:17 2005
+++ sbin/ipfw/ipfw2.c	Fri Jun 17 13:09:43 2005
_at__at_ -277,6 +277,7 _at__at_
 	TOK_SRCIP6,
 
 	TOK_IPV4,
+	TOK_BOUND,
 };
 
 struct _s_x dummynet_params[] = {
_at__at_ -403,6 +404,7 _at__at_
 	{ "dst-ip6",		TOK_DSTIP6},
 	{ "src-ipv6",		TOK_SRCIP6},
 	{ "src-ip6",		TOK_SRCIP6},
+	{ "bound",		TOK_BOUND},
 	{ "//",			TOK_COMMENT },
 
 	{ "not",		TOK_NOT },		/* pseudo option */
_at__at_ -1858,6 +1860,10 _at__at_
 				print_ext6hdr( (ipfw_insn *) cmd );
 				break;
 
+			case O_BOUND:
+				printf(" bound %u", ((ipfw_insn_u64 *)cmd)->bound);
+				break;
+
 			default:
 				printf(" [opcode %d len %d]",
 				    cmd->opcode, cmd->len);
_at__at_ -2515,7 +2521,7 _at__at_
 "	icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
 "	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
 "	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
-"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
+"	tcpdatalen LIST | verrevpath | versrcreach | antispoof | bound VALUE\n"
 );
 exit(0);
 }
_at__at_ -3683,6 +3689,7 _at__at_
 	int i;
 
 	int open_par = 0;	/* open parenthesis ( */
+	int have_bound = 0;
 
 	/* proto is here because it is used to fetch ports */
 	u_char proto = IPPROTO_IP;	/* default protocol */
_at__at_ -4492,6 +4499,33 _at__at_
 			fill_comment(cmd, ac, av);
 			av += ac;
 			ac = 0;
+			break;
+
+		case TOK_BOUND:
+			NEED1("bound requires numeric value");
+			if (have_bound)
+				errx(EX_USAGE, "only one of bound is allowed");
+			if (open_par)
+				errx(EX_USAGE, "bound cannot be part "
+						"of an or block"); 
+			if (cmd->len & F_NOT)
+				errx(EX_USAGE, 
+				"\"not\" not allowed with bound option"); 
+			{
+				char *end = NULL;
+				uint64_t bound = strtoull(*av, &end, 0);
+				if (bound)
+				switch (*end){
+					case 'G': bound *= 1024;
+					case 'M': bound *= 1024;
+					case 'K': bound *= 1024;
+				};
+				cmd->opcode = O_BOUND;
+				((ipfw_insn_u64 *)cmd)->bound = bound;
+				cmd->len = F_INSN_SIZE(ipfw_insn_u64) & F_LEN_MASK;
+				have_bound = 1;
+				ac--; av++;
+			} 
 			break;
 
 		default:
--- sys/netinet/ip_fw.h	Fri Jun  3 05:10:28 2005
+++ sys/netinet/ip_fw.h	Fri Jun 17 11:30:30 2005
_at__at_ -154,6 +154,7 _at__at_
 	O_NGTEE,		/* copy to ng_ipfw		*/
 
 	O_IP4,
+	O_BOUND,		/* u64 = bound in bytes */
 
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
_at__at_ -228,6 +229,14 _at__at_
 	ipfw_insn o;
 	u_int32_t d[1];	/* one or more */
 } ipfw_insn_u32;
+
+/*
+ * This is used to store 64-bit bound value.
+ */
+typedef struct	_ipfw_insn_u64 {
+	ipfw_insn o;
+	u_int64_t bound;
+} ipfw_insn_u64; 
 
 /*
  * This is used to store IP addr-mask pairs.
--- sys/netinet/ip_fw2.c	Thu Jun 16 18:55:58 2005
+++ sys/netinet/ip_fw2.c	Fri Jun 17 11:46:36 2005
_at__at_ -2251,6 +2251,10 _at__at_
 			 * logic to deal with F_NOT and F_OR flags associated
 			 * with the opcode.
 			 */
+			case O_BOUND:
+				match = (f->bcnt < ((ipfw_insn_u64 *)cmd)->bound);
+				break;
+
 			case O_NOP:
 				match = 1;
 				break;
_at__at_ -3387,6 +3391,11 _at__at_
 		case O_PROB:
 		case O_ICMPTYPE:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
+				goto bad_size;
+			break;
+		
+		case O_BOUND:
+			if (cmdlen != F_INSN_SIZE(ipfw_insn_u64))
 				goto bad_size;
 			break;
 


--- sbin/ipfw/ipfw2.c	Tue Jun  7 18:11:17 2005
+++ sbin/ipfw/ipfw2.c	Fri Jun 17 13:40:54 2005
_at__at_ -277,6 +277,8 _at__at_
 	TOK_SRCIP6,
 
 	TOK_IPV4,
+	TOK_BOUND,
+	TOK_CHECK_BOUND,
 };
 
 struct _s_x dummynet_params[] = {
_at__at_ -403,6 +405,8 _at__at_
 	{ "dst-ip6",		TOK_DSTIP6},
 	{ "src-ipv6",		TOK_SRCIP6},
 	{ "src-ip6",		TOK_SRCIP6},
+	{ "bound",		TOK_BOUND},
+	{ "check-bound",	TOK_CHECK_BOUND},
 	{ "//",			TOK_COMMENT },
 
 	{ "not",		TOK_NOT },		/* pseudo option */
_at__at_ -1636,6 +1640,9 _at__at_
 			flags |= HAVE_PROTO;
 			break;
 
+		case O_BOUND:
+			break;
+
 		default: /*options ... */
 			if (!(cmd->len & (F_OR|F_NOT)))
 				if (((cmd->opcode == O_IP6) &&
_at__at_ -1858,6 +1865,10 _at__at_
 				print_ext6hdr( (ipfw_insn *) cmd );
 				break;
 
+			case O_CHECK_BOUND:
+				printf(" check-bound %d", cmd->arg1);
+				break;
+
 			default:
 				printf(" [opcode %d len %d]",
 				    cmd->opcode, cmd->len);
_at__at_ -1872,6 +1883,8 _at__at_
 		}
 	}
 	show_prerequisites(&flags, HAVE_IP, 0);
+	if (rule->cmd->opcode == O_BOUND) 
+		printf(" bound %u", ((ipfw_insn_u64 *)(rule->cmd))->bound);
 	if (comment)
 		printf(" // %s", comment);
 	printf("\n");
_at__at_ -2515,7 +2528,8 _at__at_
 "	icmp6types LIST | ext6hdr LIST | flow-id N[,N] |\n"
 "	mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n"
 "	setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n"
-"	tcpdatalen LIST | verrevpath | versrcreach | antispoof\n"
+"	tcpdatalen LIST | verrevpath | versrcreach | antispoof | bound VALUE |\n"
+"	check-bound NUM\n"
 );
 exit(0);
 }
_at__at_ -3677,7 +3691,8 _at__at_
 	 * various flags used to record that we entered some fields.
 	 */
 	ipfw_insn *have_state = NULL;	/* check-state or keep-state */
-	ipfw_insn *have_log = NULL, *have_altq = NULL;
+	ipfw_insn *have_log = NULL, *have_altq = NULL,
+		  *have_bound = NULL;
 	size_t len;
 
 	int i;
_at__at_ -4494,6 +4509,39 _at__at_
 			ac = 0;
 			break;
 
+		case TOK_BOUND:
+			NEED1("bound requires numeric value");
+			if (have_bound)
+				errx(EX_USAGE, "only one of bound is allowed");
+			if (open_par)
+				errx(EX_USAGE, "bound cannot be part "
+						"of an or block"); 
+			if (cmd->len & F_NOT)
+				errx(EX_USAGE, 
+				"\"not\" not allowed with bound option"); 
+			{
+				char *end = NULL;
+				uint64_t bound = strtoull(*av, &end, 0);
+				if (bound)
+				switch (*end){
+					case 'G': bound *= 1024;
+					case 'M': bound *= 1024;
+					case 'K': bound *= 1024;
+				};
+				cmd->opcode = O_BOUND;
+				((ipfw_insn_u64 *)cmd)->bound = bound;
+				cmd->len = F_INSN_SIZE(ipfw_insn_u64) & F_LEN_MASK;
+				have_bound = cmd;
+				ac--; av++;
+			} 
+			break;
+
+		case TOK_CHECK_BOUND:
+			NEED1("check-bound requires rule number"); 
+			fill_cmd(cmd, O_CHECK_BOUND, 0, strtoul(*av, NULL, 0));
+			ac--; av++; 
+			break;
+
 		default:
 			errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s);
 		}
_at__at_ -4506,6 +4554,8 _at__at_
 done:
 	/*
 	 * Now copy stuff into the rule.
+	 * If we have a bound option, the first instruction MUST BE 
+	 * a O_BOUND.
 	 * If we have a keep-state option, the first instruction
 	 * must be a PROBE_STATE (which is generated here).
 	 * If we have a LOG option, it was stored as the first command,
_at__at_ -4514,7 +4564,15 _at__at_
 	dst = (ipfw_insn *)rule->cmd;
 
 	/*
-	 * First thing to write into the command stream is the match probability.
+	 * First write into the command stream bound instruction
+	 */
+	if (have_bound) {
+		bcopy(have_bound, dst, F_LEN(have_bound) * sizeof(uint32_t));
+		dst = next_cmd(dst);
+	}
+
+	/*
+	 * write the match probability 	
 	 */
 	if (match_prob != 1) { /* 1 means always match */
 		dst->opcode = O_PROB;
_at__at_ -4531,7 +4589,8 _at__at_
 		dst = next_cmd(dst);
 	}
 	/*
-	 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ
+	 * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ,
+	 * O_BOUND
 	 */
 	for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) {
 		i = F_LEN(src);
_at__at_ -4541,6 +4600,7 _at__at_
 		case O_KEEP_STATE:
 		case O_LIMIT:
 		case O_ALTQ:
+		case O_BOUND:
 			break;
 		default:
 			bcopy(src, dst, i * sizeof(uint32_t));
--- sys/netinet/ip_fw.h	Fri Jun  3 05:10:28 2005
+++ sys/netinet/ip_fw.h	Fri Jun 17 13:18:47 2005
_at__at_ -154,6 +154,8 _at__at_
 	O_NGTEE,		/* copy to ng_ipfw		*/
 
 	O_IP4,
+	O_BOUND,		/* u64 = bound in bytes */
+	O_CHECK_BOUND,		/* u16 = rule number */
 
 	O_LAST_OPCODE		/* not an opcode!		*/
 };
_at__at_ -230,6 +232,14 _at__at_
 } ipfw_insn_u32;
 
 /*
+ * This is used to store 64-bit bound value.
+ */
+typedef struct	_ipfw_insn_u64 {
+	ipfw_insn o;
+	u_int64_t bound;
+} ipfw_insn_u64; 
+
+/*
  * This is used to store IP addr-mask pairs.
  */
 typedef struct	_ipfw_insn_ip {
_at__at_ -351,11 +361,16 _at__at_
  *
  * When assembling instruction, remember the following:
  *
+ *  + if a rule has a "bound" option, then the first instruction
+ *	(at r->cmd) MUST BE an O_BOUND
  *  + if a rule has a "keep-state" (or "limit") option, then the
  *	first instruction (at r->cmd) MUST BE an O_PROBE_STATE
  *  + if a rule has a "log" option, then the first action
  *	(at ACTION_PTR(r)) MUST be O_LOG
  *  + if a rule has an "altq" option, it comes after "log"
+ *
+ * NOTE: actually, O_PROB instruction may be first too. But O_BOUND 
+ *	MUST BE always first (at r->cmd). 
  *
  * 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
--- sys/netinet/ip_fw2.c	Thu Jun 16 18:55:58 2005
+++ sys/netinet/ip_fw2.c	Fri Jun 17 13:26:19 2005
_at__at_ -2251,6 +2251,26 _at__at_
 			 * logic to deal with F_NOT and F_OR flags associated
 			 * with the opcode.
 			 */
+			case O_BOUND:
+				match = (f->bcnt < ((ipfw_insn_u64 *)cmd)->bound);
+				break;
+
+			case O_CHECK_BOUND:
+				{
+				struct ip_fw* rule;
+				for (rule = f->next; 
+					 rule && cmd->arg1 >= rule->rulenum; 
+					 rule = rule->next) 
+					if (rule->rulenum == cmd->arg1 && 
+						rule->cmd->opcode == O_BOUND )
+				   	{
+						match = (rule->bcnt < 
+							((ipfw_insn_u64 *)(rule->cmd))->bound);
+						break;
+					}
+				}
+				break;
+ 
 			case O_NOP:
 				match = 1;
 				break;
_at__at_ -3373,6 +3393,7 _at__at_
 		case O_EXT_HDR:
 		case O_IP6:
 		case O_IP4:
+		case O_CHECK_BOUND:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn))
 				goto bad_size;
 			break;
_at__at_ -3388,6 +3409,16 _at__at_
 		case O_ICMPTYPE:
 			if (cmdlen != F_INSN_SIZE(ipfw_insn_u32))
 				goto bad_size;
+			break;
+		
+		case O_BOUND:
+			if (cmdlen != F_INSN_SIZE(ipfw_insn_u64))
+				goto bad_size;
+			if (cmd != rule->cmd) {
+				printf("ipfw: bogus rule, opcode %d must be first\n",
+						cmd->opcode);
+				return EINVAL;
+			} 
 			break;
 
 		case O_LIMIT:
Received on Fri Jun 17 2005 - 08:46:14 UTC

This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:38:36 UTC