From owner-freebsd-hackers@FreeBSD.ORG Fri Jun 17 10:49:31 2005 Return-Path: X-Original-To: freebsd-hackers@freebsd.org Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 7576B16A41C for ; Fri, 17 Jun 2005 10:49:31 +0000 (GMT) (envelope-from bu7cher@yandex.ru) Received: from mail.rdu.kirov.ru (ns.rdu.kirov.ru [217.9.151.217]) by mx1.FreeBSD.org (Postfix) with ESMTP id 8585543D58 for ; Fri, 17 Jun 2005 10:49:29 +0000 (GMT) (envelope-from bu7cher@yandex.ru) Received: from kirov.so-cdu.ru (kirov [172.21.81.1]) by mail.rdu.kirov.ru (Postfix) with ESMTP id 84EFDFE1C for ; Fri, 17 Jun 2005 14:49:28 +0400 (MSD) Received: from kirov.so-cdu.ru (localhost [127.0.0.1]) by rdu.kirov.ru (Postfix) with SMTP id 6955C1544D for ; Fri, 17 Jun 2005 14:49:28 +0400 (MSD) Received: by rdu.kirov.ru (Postfix, from userid 1014) id 2CBD11544B; Fri, 17 Jun 2005 14:49:28 +0400 (MSD) Received: from [172.21.81.52] (elsukov.kirov.so-cdu.ru [172.21.81.52]) by rdu.kirov.ru (Postfix) with ESMTP id 154451542C for ; Fri, 17 Jun 2005 14:49:28 +0400 (MSD) Message-ID: <42B2AA96.7020905@yandex.ru> Date: Fri, 17 Jun 2005 14:48:54 +0400 From: "Andrey V. Elsukov" User-Agent: Mozilla Thunderbird 1.0.2 (Windows/20050317) X-Accept-Language: en-us, en MIME-Version: 1.0 To: freebsd-hackers@freebsd.org Content-Type: multipart/mixed; boundary="------------070104010906080508070707" Subject: Re: kern/80642: [patch] IPFW small patch - new RULE OPTION X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 17 Jun 2005 10:49:31 -0000 This is a multi-part message in MIME format. --------------070104010906080508070707 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 --------------070104010906080508070707 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; --------------070104010906080508070707 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: --------------070104010906080508070707--