From owner-freebsd-ipfw@FreeBSD.ORG Mon May 15 10:19:26 2006 Return-Path: X-Original-To: freebsd-ipfw@FreeBSD.org Delivered-To: freebsd-ipfw@FreeBSD.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 7EA3516A40D for ; Mon, 15 May 2006 10:19:26 +0000 (UTC) (envelope-from ianf@hetzner.co.za) Received: from mail1a.your-server.co.za (mail1a.your-server.co.za [196.7.18.227]) by mx1.FreeBSD.org (Postfix) with ESMTP id A024543D45 for ; Mon, 15 May 2006 10:19:25 +0000 (GMT) (envelope-from ianf@hetzner.co.za) Received: from lfw.hetzner.co.za ([196.7.18.226] helo=hetzner.co.za) by mail1a.your-server.co.za with esmtps (TLSv1:AES256-SHA:256) (Exim 4.54) id 1FfaAb-0000iU-UF for freebsd-ipfw@FreeBSD.org; Mon, 15 May 2006 12:19:23 +0200 Received: from localhost ([127.0.0.1]) by hetzner.co.za with esmtp (Exim 4.51 (FreeBSD)) id 1FfaAb-000B6F-UK for freebsd-ipfw@FreeBSD.org; Mon, 15 May 2006 12:19:21 +0200 X-Mailer: exmh version 2.7.2 01/07/2005 with nmh-1.2 X-Exmh-Isig-CompType: comp X-Exmh-Isig-Folder: lists/FreeBSD-ipfw To: freebsd-ipfw@FreeBSD.org From: Ian FREISLICH X-Attribution: BOFH Mime-Version: 1.0 Content-Type: multipart/mixed ; boundary="==_Exmh_1147688197_443700" Date: Mon, 15 May 2006 12:19:21 +0200 Sender: ianf@hetzner.co.za Message-Id: X-Virus-Scanned: Clear (ClamAV 0.88.1/1462/Sun May 14 20:24:29 2006) Cc: Subject: ipfw state interface context. X-BeenThere: freebsd-ipfw@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: IPFW Technical Discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 15 May 2006 10:19:26 -0000 This is a multipart MIME message. --==_Exmh_1147688197_443700 Content-Type: text/plain Hi We're using ipfw as a shared customer firewall with lots of vlan interfaces for customers, and where customers are able to modify their rules, we're unable to do any stateful firewalling. Maybe it's just a design issue with the way it's been setup: 00020 deny log ip from any to any not antispoof 00040 skipto 60000 ip from any to me 00050 skipto 60000 ip from me to any 00301 skipto 1100 ip from any to any in recv vlan1 00301 skipto 1100 ip from any to any out xmit vlan1 ... 00564 skipto 27400 ip from any to any in recv vlan264 00564 skipto 27400 ip from any to any out xmit vlan264 01000 allow ip from any to not me #vlan1 01100 allow tcp from any to any dst-port 22 setup out xmit vlan1 01199 allow udp from any to any dst-port 53 in recv vlan1 keep-state 01199 allow icmp from any to any in recv vlan1 icmptypes 8 01199 allow icmp from any to any out xmit vlan1 icmptypes 0 01199 check-state 01199 allow tcp from any to any established out xmit vlan1 01199 allow tcp from any to any in recv vlan1 01199 deny log ip from any to any ... #vlan264 27400 allow tcp from any to any dst-port 22 setup out xmit vlan264 27499 allow udp from any to any dst-port 53 in recv vlan264 keep-state 27499 allow icmp from any to any in recv vlan264 icmptypes 8 27499 allow icmp from any to any out xmit vlan264 icmptypes 0 27499 check-state 27499 allow tcp from any to any established out xmit vlan264 27499 allow tcp from any to any in recv vlan264 27499 deny log ip from any to any ... 60000 allow icmp from 196.40.106.243 to me 60010 allow ip from any to me icmptypes 8 not via re1 60020 allow icmp from 196.7.147.237,196.22.132.223 to me icmptypes 8 via re1 60030 deny log ip from any to me out via re1 60040 allow tcp from me to any 60050 allow udp from me to any keep-state 60060 allow udp from any to me frag 60070 allow icmp from me to any keep-state 60080 allow ospf from any to any via re1 60090 allow tcp from any to me dst-port 22,3000 setup 60100 allow ip from any to me established 60110 check-state 65533 deny tcp from any to me dst-port 137,138,139,445 65534 deny log ip from any to any 65535 deny ip from any to any The problem is this: say a udp packet enters enters vlan264 destined for a host on vlan1 port 53. This packet relults in a dynamic rule(contrived in this example): 27499 1 141 (5s) STATE udp 192.168.0.2 2191 <-> 192.168.1.9 53 Now, even though vlan1 doesn't have a rule allowing these packets in its rule section, the keep-state or check-state rule allows allows the packets through because of the state rule added by vlan264. I've patched the ipfw to store the interface details with the dynamic rule so the above rule would be printed out: 27499 1 141 (5s) STATE (vlan264) udp 192.168.0.2 2191 <-> 192.168.1.9 53 The check-state rule (01199) will ignore this dynamic rule because at that point the packet is leaving vlan1 and this dynamic rule was created by a packet entering vlan264. Without this patch it is impossible to do stateful filtering for multiple interfaces without introducing some security issues. I've checked for regressions with stateful filtering and connection limiting and I've not uncovered any yet. Thoughts? Ian -- Ian Freislich --==_Exmh_1147688197_443700 Content-Type: text/plain ; name="ipfw_sbin.patch" Content-Description: ipfw_sbin.patch Content-Disposition: attachment; filename="ipfw_sbin.patch" Index: ipfw2.c =================================================================== RCS file: /home/ncvs/src/sbin/ipfw/ipfw2.c,v retrieving revision 1.87 diff -u -d -r1.87 ipfw2.c --- ipfw2.c 31 Mar 2006 12:54:17 -0000 1.87 +++ ipfw2.c 8 May 2006 13:26:03 -0000 @@ -1950,7 +1950,7 @@ printf(" LIMIT"); break; case O_KEEP_STATE: /* bidir, no mask */ - printf(" STATE"); + printf(" STATE (%s)", d->if_name); break; } --==_Exmh_1147688197_443700 Content-Type: text/plain ; name="ipfw_module.patch" Content-Description: ipfw_module.patch Content-Disposition: attachment; filename="ipfw_module.patch" Index: ip_fw.h =================================================================== RCS file: /home/ncvs/src/sys/netinet/ip_fw.h,v retrieving revision 1.104 diff -u -d -r1.104 ip_fw.h --- ip_fw.h 14 Feb 2006 06:36:39 -0000 1.104 +++ ip_fw.h 8 May 2006 13:24:16 -0000 @@ -422,6 +422,8 @@ /* to generate keepalives) */ u_int16_t dyn_type; /* rule type */ u_int16_t count; /* refcount */ + u_short if_index; + char if_name[IFNAMSIZ]; }; /* Index: ip_fw2.c =================================================================== RCS file: /home/ncvs/src/sys/netinet/ip_fw2.c,v retrieving revision 1.127 diff -u -d -r1.127 ip_fw2.c --- ip_fw2.c 3 Mar 2006 12:10:59 -0000 1.127 +++ ip_fw2.c 9 May 2006 07:08:49 -0000 @@ -1120,7 +1120,7 @@ */ static ipfw_dyn_rule * lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int *match_direction, - struct tcphdr *tcp) + struct tcphdr *tcp, struct ifnet *ifp) { /* * stateful ipfw extensions. @@ -1139,7 +1139,9 @@ goto done; /* not found */ i = hash_packet( pkt ); for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) { - if (q->dyn_type == O_LIMIT_PARENT && q->count) + if ((q->dyn_type == O_LIMIT_PARENT && q->count) || + (q->dyn_type == O_KEEP_STATE && + q->if_index != ifp->if_index)) goto next; if (TIME_LEQ( q->expire, time_uptime)) { /* expire entry */ UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q); @@ -1263,12 +1265,12 @@ static ipfw_dyn_rule * lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, - struct tcphdr *tcp) + struct tcphdr *tcp, struct ifnet *ifp) { ipfw_dyn_rule *q; IPFW_DYN_LOCK(); - q = lookup_dyn_rule_locked(pkt, match_direction, tcp); + q = lookup_dyn_rule_locked(pkt, match_direction, tcp, ifp); if (q == NULL) IPFW_DYN_UNLOCK(); /* NB: return table locked when q is not NULL */ @@ -1315,7 +1317,8 @@ * - "parent" rules for the above (O_LIMIT_PARENT). */ static ipfw_dyn_rule * -add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule) +add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule, + struct ifnet *ifp) { ipfw_dyn_rule *r; int i; @@ -1352,6 +1355,11 @@ r->dyn_type = dyn_type; r->pcnt = r->bcnt = 0; r->count = 0; + if (dyn_type == O_KEEP_STATE) { + r->if_index = ifp->if_index; + strncpy(r->if_name, ifp->if_xname, IFNAMSIZ); + r->if_name[IFNAMSIZ] = '\0'; + } r->bucket = i; r->next = ipfw_dyn_v[i]; @@ -1402,7 +1410,7 @@ return q; } } - return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); + return add_dyn_rule(pkt, O_LIMIT_PARENT, rule, NULL); } /** @@ -1413,7 +1421,7 @@ */ static int install_state(struct ip_fw *rule, ipfw_insn_limit *cmd, - struct ip_fw_args *args) + struct ip_fw_args *args, struct ifnet *ifp) { static int last_log; @@ -1426,7 +1434,7 @@ IPFW_DYN_LOCK(); - q = lookup_dyn_rule_locked(&args->f_id, NULL, NULL); + q = lookup_dyn_rule_locked(&args->f_id, NULL, NULL, ifp); if (q != NULL) { /* should never occur */ if (last_log != time_uptime) { @@ -1454,7 +1462,7 @@ switch (cmd->o.opcode) { case O_KEEP_STATE: /* bidir rule */ - add_dyn_rule(&args->f_id, O_KEEP_STATE, rule); + add_dyn_rule(&args->f_id, O_KEEP_STATE, rule, ifp); break; case O_LIMIT: /* limit number of sessions */ @@ -1506,7 +1514,7 @@ return 1; } } - add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent); + add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent, NULL); } break; default: @@ -1514,7 +1522,7 @@ IPFW_DYN_UNLOCK(); return 1; } - lookup_dyn_rule_locked(&args->f_id, NULL, NULL); /* XXX just set lifetime */ + lookup_dyn_rule_locked(&args->f_id, NULL, NULL, ifp); /* XXX just set lifetime */ IPFW_DYN_UNLOCK(); return 0; } @@ -2929,7 +2937,8 @@ case O_LIMIT: case O_KEEP_STATE: if (install_state(f, - (ipfw_insn_limit *)cmd, args)) { + (ipfw_insn_limit *)cmd, args, oif ? oif : + m->m_pkthdr.rcvif)) { retval = IP_FW_DENY; goto done; /* error/limit violation */ } @@ -2950,7 +2959,8 @@ if (dyn_dir == MATCH_UNKNOWN && (q = lookup_dyn_rule(&args->f_id, &dyn_dir, proto == IPPROTO_TCP ? - TCP(ulp) : NULL)) + TCP(ulp) : NULL, oif ? oif : + m->m_pkthdr.rcvif)) != NULL) { /* * Found dynamic entry, update stats --==_Exmh_1147688197_443700--