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