From owner-freebsd-net@FreeBSD.ORG Fri Apr 4 16:03:32 2008 Return-Path: Delivered-To: freebsd-net@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id C01A6106564A; Fri, 4 Apr 2008 16:03:32 +0000 (UTC) (envelope-from smithi@nimnet.asn.au) Received: from gaia.nimnet.asn.au (nimbin.lnk.telstra.net [139.130.45.143]) by mx1.freebsd.org (Postfix) with ESMTP id 663138FC1F; Fri, 4 Apr 2008 16:03:30 +0000 (UTC) (envelope-from smithi@nimnet.asn.au) Received: from localhost (smithi@localhost) by gaia.nimnet.asn.au (8.8.8/8.8.8R1.5) with SMTP id CAA12901; Sat, 5 Apr 2008 02:03:21 +1000 (EST) (envelope-from smithi@nimnet.asn.au) Date: Sat, 5 Apr 2008 02:03:17 +1000 (EST) From: Ian Smith To: Julian Elischer In-Reply-To: <47F5B17E.5000304@elischer.org> Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Cc: freebsd-net@freebsd.org, Ivan Voras Subject: Re: Trouble with IPFW or TCP? X-BeenThere: freebsd-net@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Networking and TCP/IP with FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 04 Apr 2008 16:03:32 -0000 On Thu, 3 Apr 2008, Julian Elischer wrote: > Ian Smith wrote: > > On Thu, 3 Apr 2008, Julian Elischer wrote: > > > Ivan Voras wrote: > > > > Erik Trulsson wrote: > > > >> On Fri, Apr 04, 2008 at 01:34:07AM +0200, Ivan Voras wrote: > > > >>> In which case would an ipfw ruleset like this: > > > >>> > > > >>> 00100 114872026 40487887607 allow ip from any to any via lo0 > > > >>> 00200 0 0 deny ip from any to 127.0.0.0/8 > > > >>> 00300 0 0 deny ip from 127.0.0.0/8 to any > > > >>> 00600 1585 112576 deny ip from table(0) to me > > > >>> 01000 90279 7325972 allow icmp from any to any > > > >>> 05000 475961039 334422494257 allow tcp from me to any setup keep-state > > > >>> 05100 634155 65779377 allow udp from me to any keep-state > > > >>> 06022 409604 69177326 allow tcp from any to me dst-port 22 > > > >>> setup keep-state > > > >>> 06080 52159025 43182548092 allow tcp from any to me dst-port 80 > > > >>> setup keep-state > > > >>> 06443 6392366 2043532158 allow tcp from any to me dst-port 443 > > > >>> setup keep-state > > > >>> 07020 517065 292377553 allow tcp from any to me dst-port 8080 > > > >>> setup keep-state > > > >>> 65400 12273387 629703212 deny log ip from any to any > > > >>> 65535 0 0 deny ip from any to any > > > >> > > > >> If you are using 'keep-state' should there not also be some rule > > > >> containing > > > >> 'check-state' ? > > > > > > > > Not according to the ipfw(8) manual: > > > > > > > > """ > > > > These dynamic rules, which have a limited lifetime, are checked at the > > > > first occurrence of a check-state, keep-state or limit rule, and > > > > are typ- > > > > ically used to open the firewall on-demand to legitimate traffic only. > > > > See the STATEFUL FIREWALL and EXAMPLES Sections below for more > > > > informa- > > > > tion on the stateful behaviour of ipfw. > > > > """ > > > > > > > > I read this to mean the dynamic rules are checked at rule #5000 from the > > > > above list. Is there an advantage to having an explicit check-state rule > > > > in simple rulesets like this one? > > > > > > the docs are wrong then I think. > > > > If so, they've been wrong since 4.something .. certainly before 4.8. > > It's hard to imagine nobody else has ever relied on that doc behaviour, > > so perhaps the docs, if wrong, have become so at some more recent time? > > Not that I have known... keep-state does not (and never has) include > an implicit check-state. Sorry (and surprised!) to have to differ, but you MADE me read the code! Bearing in mind I'm reading 5.5 sources - stop me if it's changed - but starting with /usr/sbin/ipfw/ipfw2.c we see that adding check-state just generates an O_CHECK_STATE, while adding keep-state or limit rules first generate an initial O_PROBE_STATE opcode (ignored when listing rules): /* * Now copy stuff into the rule. * 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, * and now must be moved to the top of the action part. */ dst = (ipfw_insn *)rule->cmd; [..] /* * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE) { fill_cmd(dst, O_PROBE_STATE, 0, 0); dst = next_cmd(dst); then go on to generate the O_KEEP_STATE or O_LIMIT rule as appropriate. Now in /sys/netinet/ip_fw2.c in ipfw_chk circa line 2400 (@5.5) we have: * O_LIMIT and O_KEEP_STATE: these opcodes are * not real 'actions', and are stored right * before the 'action' part of the rule. * These opcodes try to install an entry in the * state tables; if successful, we continue with * the next opcode (match=1; break;), otherwise * the packet * must be dropped * ('goto done' after setting retval); * * O_PROBE_STATE and O_CHECK_STATE: these opcodes * cause a lookup of the state table, and a jump * to the 'action' part of the parent rule * ('goto check_body') if an entry is found, or * (CHECK_STATE only) a jump to the next rule if * the entry is not found ('goto next_rule'). * The result of the lookup is cached to make * further instances of these opcodes are * effectively NOPs. */ case O_LIMIT: case O_KEEP_STATE: if (install_state(f, (ipfw_insn_limit *)cmd, args)) { retval = IP_FW_PORT_DENY_FLAG; goto done; /* error/limit violation */ } match = 1; break; case O_PROBE_STATE: case O_CHECK_STATE: /* * dynamic rules are checked at the first * keep-state or check-state occurrence, * with the result being stored in dyn_dir. * The compiler introduces a PROBE_STATE * instruction for us when we have a * KEEP_STATE (because PROBE_STATE needs * to be run first). */ if (dyn_dir == MATCH_UNKNOWN && (q = lookup_dyn_rule(&args->f_id, &dyn_dir, proto == IPPROTO_TCP ? L3HDR(struct tcphdr, ip) : NULL)) != NULL) { /* * Found dynamic entry, update stats * and jump to the 'action' part of * the parent rule. */ q->pcnt++; q->bcnt += pktlen; f = q->rule; cmd = ACTION_PTR(f); l = f->cmd_len - f->act_ofs; IPFW_DYN_UNLOCK(); goto check_body; } /* * Dynamic entry not found. If CHECK_STATE, * skip to next rule, if PROBE_STATE just * ignore and continue with next opcode. */ if (cmd->opcode == O_CHECK_STATE) goto next_rule; match = 1; break; So indeed each rule with keep-state or limit options does the same probe as a check-state in the first opcode, before then installing or checking state in the subsequent opcode. Or so it reads to an ancient neophyte .. > I think the document is talking about the lifetime. > Each time a keep-state or check-state or limit is hit, > the TTL is kicked. That's pretty well described under keep-state and elsewhere. Good ol' ipfw(8) has yet to let me down, and like Ivan I recall keep-state rules (albeit only for UDP) without any check-state working just fine. Not that any of that helps solve Ivan's problem .. cheers, Ian