Skip site navigation (1)Skip section navigation (2)
Date:      Fri,  8 Sep 2000 09:45:35 -0500 (CDT)
From:      missnglnk@sneakerz.org
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/21118: Multiple problems in ipfw's stateful code 
Message-ID:  <20000908144535.988115D006@sneakerz.org>

next in thread | raw e-mail | index | archive | help

>Number:         21118
>Category:       kern
>Synopsis:       Multiple problems in ipfw's stateful code
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Sep 08 07:50:00 PDT 2000
>Closed-Date:
>Last-Modified:
>Originator:     Lawrence N. King
>Release:        FreeBSD 4.1-FEARSOME-20000818 i386
>Organization:
>Environment:
	FreeBSD 4.0-RELEASE
	FreeBSD 4.1-STABLE
	FreeBSD 5.0-CURRENT
	Above version with IPFIREWALL defined in kernel

>Description:

	When a connection is made and kept track of (keep-state):
	1) Connections expire prematurely if dynamic ACK lifetime
	   is is greater than the keepalive interval.

	2) Expired connections aren't actually expired, but are
	   allowed to continue (TCP connections are extended by
	   the dynamic RST lifetime even though they have expired,
	   and all other protocol connections are extended by
	   the dynamic short lifetime).

	3) Expired connections aren't cleaned from the state table
	   unless the next dynamic rule added reaches the maximum
	   amount of dynamic rules.

	4) A new state is installed for any packet which matches
	   the rule (with keep-state), regardless of whether its
	   not a TCP packet with the SYN flag set, or if its an
	   ICMP query packet.

>How-To-Repeat:

	ipfw -f flush
	ipfw add check-state
	ipfw add allow ip from any to any via fxp0 out keep-state
	ipfw add deny ip from any to any

	Now issue a couple of connections, I did SSH, a new state
	was installed, the rule expired, but I still could continue
	my SSH connection since its expiration time was being extended
	by the dynamic RST lifetime.

	Instead of issuing a TCP packet with the SYN flag set, issue
	a TCP packet with any other flag set, and a new state is
	installed for this packet.

	Let a couple of dynamic rules timeout (you can check via ipfw
	show), and watch as they linger around, they aren't cleaned
	out until the amount of dynamic rules is equal to the maximum
	and a new rule is being installed.

>Fix:

	Nothing for problem 1, but solutions for 2-4 are in the below
	patch:

-- snip --
--- sys/netinet/ip_fw.c.orig	Wed Sep  6 21:01:07 2000
+++ sys/netinet/ip_fw.c	Wed Sep  6 21:40:55 2000
@@ -735,4 +735,3 @@
 	    break ;
-	default:
-#if 0
+	case TH_RST | (TH_RST << 8) :
 	    /*
@@ -741,7 +740,18 @@
 	     */
-	    if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0)
-		printf("invalid state: 0x%x\n", q->state);
-#endif
+	    printf("invalid state: 0x%x\n", q->state);
 	    q->expire = time_second + dyn_rst_lifetime ;
 	    break ;
+	default:
+	    /*
+             * A TCP packet found in unknown state, drop it.
+	     */
+	    DEB(printf("packet should be dropped (state: 0x%x)\n", q->state));
+            old_q = q ;
+            if (prev != NULL)
+                prev->next = q = q->next ;
+            else
+                ipfw_dyn_v[i] = q = q->next ;
+            dyn_count-- ;
+            free(old_q, M_IPFW);
+	    break ;
 	}
@@ -838,4 +848,7 @@
     }
-    if (dyn_count >= dyn_max) /* try remove old ones... */
-	remove_dyn_rule(NULL, 0 /* expire */);
+    /*
+     * Unconditionally remove expired states.
+     */
+    remove_dyn_rule(NULL, 0 /* expire */);
+
     if (dyn_count >= dyn_max) {
@@ -1277,4 +1290,43 @@
 		 */
-		if (q == NULL && f->fw_flg & IP_FW_F_KEEP_S)
-		    install_state(chain);
+		if (q == NULL && f->fw_flg & IP_FW_F_KEEP_S) {
+		    /*
+		     * Instead of unconditionally adding a new state,
+		     * check the protocol and flags, and add a new state
+		     * or ignore packet.
+		     */
+		    switch(proto) {
+		        case IPPROTO_TCP:
+		            if (flags & TH_SYN) {
+		                DEB(printf("-- installing state for TCP packet\n"));
+		                install_state(chain);
+		            } else {
+		                DEB(printf("-- invalid TCP connection state\n"));
+		            }
+                            break;
+		        case IPPROTO_UDP:
+		            DEB(printf("-- installing state for UDP packet\n"));
+		            install_state(chain);
+                            break;
+		        case IPPROTO_ICMP:
+		            if (is_icmp_query(ip)) {
+		                DEB(printf("-- installing state for ICMP packet\n"));
+		                install_state(chain);
+		            } else {
+		                DEB(printf("-- invalid ICMP connection state\n"));
+		            }
+                            break;
+		        default:
+		            /*
+		             * Unknown packet, if default is to accept all
+		             * packets, add a new state, otherwise ignore.
+			     */
+#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT
+		            DEB(printf("-- installing state for unknown packet\n"));
+		            install_state(chain);
+#else
+		            DEB(printf("invalid unknown protocol connection state\n"));
+#endif
+                            break;
+                    }
+		}
 #endif
-- snip --

>Release-Note:
>Audit-Trail:
>Unformatted:


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20000908144535.988115D006>