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

Andrey V. Elsukov elsukov at rdu.kirov.ru
Fri Jun 17 10:40:21 GMT 2005


The following reply was made to PR kern/80642; it has been noted by GNATS.

From: "Andrey V. Elsukov" <elsukov at rdu.kirov.ru>
To: bug-followup at FreeBSD.org, bu7cher at yandex.ru
Cc:  
Subject: Re: kern/80642: [patch] IPFW small patch - new RULE OPTION
Date: Fri, 17 Jun 2005 14:31:20 +0400

 This is a multi-part message in MIME format.
 --------------020602060206080505060104
 Content-Type: text/plain; charset=KOI8-R; format=flowed
 Content-Transfer-Encoding: 7bit
 
 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
 
 --------------020602060206080505060104
 Content-Type: text/plain;
  name="ipfw_bound.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline;
  filename="ipfw_bound.diff"
 
 --- sbin/ipfw/ipfw2.c	Tue Jun  7 18:11:17 2005
 +++ sbin/ipfw/ipfw2.c	Fri Jun 17 13:09:43 2005
 @@ -277,6 +277,7 @@
  	TOK_SRCIP6,
  
  	TOK_IPV4,
 +	TOK_BOUND,
  };
  
  struct _s_x dummynet_params[] = {
 @@ -403,6 +404,7 @@
  	{ "dst-ip6",		TOK_DSTIP6},
  	{ "src-ipv6",		TOK_SRCIP6},
  	{ "src-ip6",		TOK_SRCIP6},
 +	{ "bound",		TOK_BOUND},
  	{ "//",			TOK_COMMENT },
  
  	{ "not",		TOK_NOT },		/* pseudo option */
 @@ -1858,6 +1860,10 @@
  				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);
 @@ -2515,7 +2521,7 @@
  "	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);
  }
 @@ -3683,6 +3689,7 @@
  	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 */
 @@ -4492,6 +4499,33 @@
  			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
 @@ -154,6 +154,7 @@
  	O_NGTEE,		/* copy to ng_ipfw		*/
  
  	O_IP4,
 +	O_BOUND,		/* u64 = bound in bytes */
  
  	O_LAST_OPCODE		/* not an opcode!		*/
  };
 @@ -228,6 +229,14 @@
  	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
 @@ -2251,6 +2251,10 @@
  			 * 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;
 @@ -3387,6 +3391,11 @@
  		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;
  
 
 --------------020602060206080505060104
 Content-Type: text/plain;
  name="ipfw_bound2.diff"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: inline;
  filename="ipfw_bound2.diff"
 
 --- sbin/ipfw/ipfw2.c	Tue Jun  7 18:11:17 2005
 +++ sbin/ipfw/ipfw2.c	Fri Jun 17 13:40:54 2005
 @@ -277,6 +277,8 @@
  	TOK_SRCIP6,
  
  	TOK_IPV4,
 +	TOK_BOUND,
 +	TOK_CHECK_BOUND,
  };
  
  struct _s_x dummynet_params[] = {
 @@ -403,6 +405,8 @@
  	{ "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 */
 @@ -1636,6 +1640,9 @@
  			flags |= HAVE_PROTO;
  			break;
  
 +		case O_BOUND:
 +			break;
 +
  		default: /*options ... */
  			if (!(cmd->len & (F_OR|F_NOT)))
  				if (((cmd->opcode == O_IP6) &&
 @@ -1858,6 +1865,10 @@
  				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);
 @@ -1872,6 +1883,8 @@
  		}
  	}
  	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");
 @@ -2515,7 +2528,8 @@
  "	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);
  }
 @@ -3677,7 +3691,8 @@
  	 * 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;
 @@ -4494,6 +4509,39 @@
  			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);
  		}
 @@ -4506,6 +4554,8 @@
  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,
 @@ -4514,7 +4564,15 @@
  	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;
 @@ -4531,7 +4589,8 @@
  		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);
 @@ -4541,6 +4600,7 @@
  		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
 @@ -154,6 +154,8 @@
  	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!		*/
  };
 @@ -230,6 +232,14 @@
  } 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 {
 @@ -351,11 +361,16 @@
   *
   * 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
 @@ -2251,6 +2251,26 @@
  			 * 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;
 @@ -3373,6 +3393,7 @@
  		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;
 @@ -3388,6 +3409,16 @@
  		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:
 
 --------------020602060206080505060104--
 


More information about the freebsd-ipfw mailing list