Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 13 Jul 2000 13:39:03 -0600 (MDT)
From:      "Aaron D. Gifford" <agifford@infowest.com>
To:        luigi@labinfo.iet.unipi.it, security@freebsd.org, 
Subject:   ipfw patches
Message-ID:  <20000713193903.8FC3F20F55@ns1.infowest.com>

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

Here are some patches to the kernel's ipfw code and to the ipfw
program that give fine-grained control to dynamic keep-state rule
lifetimes.  I've used them on several boxes for over a month now
with no troubles at all.  I think the changes are quite stable.

I found that the sysctl variables that controlled TCP and non-TCP
dynamic rules (using keep-state) did not give enough control over
dthe rule lifetimes.  For instance, TCP connectiongs like SSH or
TELNET on my boxes use a very long dynamic rule lifetime so that
idle sessions (those that pass no traffic for over 5 minutes, the
default lifetime of ipfw dynamic TCP sessions) while HTTP TCP
sessions use the default 5-minute rule lifetimes since that is
usually sufficient.

To the ipfw maintainer(s), would this functionality warrant
integration into the FreeBSD code base?

Here's a sample of how I use the patches:

  ...
  ipfw check-state
  ipfw add deny tcp from any to any established
  ipfw add pass tcp from any to ${ip} 22 in keep-state lifetime 3600
  ipfw add pass tcp from any to ${ip} 80 in keep-state
  ...
  ipfw add pass udp from ${ip} to any 53 out keep-state
  ipfw add pass udp from ${ip} to ${icqnet1} 4000 out keep-state lifetime 300
  ipfw add pass udp from ${ip} to ${icqnet2} 4000 out keep-state lifetime 300
  ...
  ipfw deny log all from any to any

In the above example, SSH traffic expires only after an hour of inactivity
while HTTP traffic uses the sysctl variables (which were left at the default
of 300 seconds/5-minutes in the example).  For UDP, ICQ traffic uses a
longer 5-minute expiration while DNS traffic uses the sysctl variable values.

As you can see, the patches add the "lifetime XXXX" ability to any
keep-state rules.  The number (XXXX) is just an integer representing
the number of seconds after which the rule will expire.

For TCP rules, the patches permit any individual rule to override the
use of the sysctl variable net.inet.ip.fw.dyn_ack_lifetime (which
defaults to 5 minutes or 300 seconds).

For non TCP rules, the patches override the value of the sysctl variable
net.inet.ip.fw.dyn_short_lifetime which I believe defaults to 5 seconds.

I noticed that the kernel ipfw code for non TCP dynamic rules uses an
initial lifetime from net.inet.ip.fw.dyn_syn_lifetime but subsequent
packets that match the dynamic rule use net.inet.ip.fw.dyn_short_lifetime
to update the lifetime of the rule.  That means that UDP traffic will
initally use a 20-second timeout as things work currently, then any
subsequent matching packets set the lifetime to 5 seconds.  I was unsure
whether or not this was intentional.  My patches treat this as a bug and
use only net.inet.ip.fw.dyn_short_lifetime instead (in the absense of an
explicit lifetime) specified on the command line with the "lifetime XXXX"
extension).  If this behavior was intentional and deliberate, I need
to fix my patches and this should really be documented in the man page.
With my patches on my boxes, I increase net.inet.ip.fw.dyn_short_lifetime
to 30 seconds and things work very well.  I don't think I want a 5 second
lifetime on UDP traffic anyway by default.  It works just fine (the current
method of 20 seconds then 5 seconds) for most simple UDP traffic like
DNS queries or NTP stuff, but more complex UDP traffic that relies on
more subsequent packets might be terminated by the short 5-second lifetime
after the first matching packet.

After applying the patches, just recompile your kernel and recompile the
ipfw command and install both.  As far as I know, these two items are the
only ones necessary to recompile.

Please let me know if this is useful.  Also let me know if I've overlooked
anything.

Thanks!
Aaron Gifford

===== PATCHES ARE BELOW: cd /usr/src ; patch -p < patch-file  ======
--- sys/netinet/ip_fw.c.orig	Wed May 31 14:43:58 2000
+++ sys/netinet/ip_fw.c	Mon Jun  5 08:08:51 2000
@@ -651,7 +651,7 @@
 	    break ;
 	case TH_SYN | (TH_SYN << 8) :
 	    /* move to established */
-	    q->expire = time_second + dyn_ack_lifetime ;
+	    q->expire = time_second + (q->lifetime ? q->lifetime : dyn_ack_lifetime) ;
 	    break ;
 	case TH_SYN | (TH_SYN << 8) | TH_FIN :
 	case TH_SYN | (TH_SYN << 8) | (TH_FIN << 8) :
@@ -673,7 +673,7 @@
 	}
     } else {
 	/* should do something for UDP and others... */
-	q->expire = time_second + dyn_short_lifetime ;
+	q->expire = time_second + (q->lifetime ? q->lifetime : dyn_short_lifetime) ;
     }
     if (match_direction)
 	*match_direction = dir ;
@@ -721,7 +721,13 @@
     if (mask)
 	r->mask = *mask ;
     r->id = *id ;
-    r->expire = time_second + dyn_syn_lifetime ;
+    r->lifetime = chain->rule->fw_dyn_lifetime ;
+    if (r->lifetime)
+	r->expire = time_second + r->lifetime ;
+    else if (r->id.proto == IPPROTO_TCP)
+	r->expire = time_second + dyn_syn_lifetime ;
+    else
+        r->expire = time_second + dyn_short_lifetime ;
     r->chain = chain ;
     r->type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
 
--- sys/netinet/ip_fw.h.orig	Thu Feb 10 07:17:39 2000
+++ sys/netinet/ip_fw.h	Mon Jun  5 08:08:51 2000
@@ -73,6 +73,7 @@
 	u_short fu_skipto_rule;		/* SKIPTO command rule number */
 	u_short fu_reject_code;		/* REJECT response code */
 	struct sockaddr_in fu_fwd_ip;
+	u_int32_t fu_dyn_lifetime;	/* Explicit dynamic rule lifetime */
     } fw_un;
     u_char fw_prot;			/* IP protocol */
 	/*
@@ -121,6 +122,7 @@
 #define fw_reject_code	fw_un.fu_reject_code
 #define fw_pipe_nr	fw_un.fu_pipe_nr
 #define fw_fwd_ip	fw_un.fu_fwd_ip
+#define fw_dyn_lifetime	fw_un.fu_dyn_lifetime
 
 struct ip_fw_chain {
         LIST_ENTRY(ip_fw_chain) chain;
@@ -147,6 +149,7 @@
     struct ipfw_flow_id mask ;
     struct ip_fw_chain *chain ;		/* pointer to parent rule	*/
     u_int32_t type ;			/* rule type			*/
+    u_int32_t lifetime ;		/* per-rule specified lifetime  */
     u_int32_t expire ;			/* expire time			*/
     u_int64_t pcnt, bcnt;		/* match counters		*/
     u_int32_t bucket ;			/* which bucket in hash table	*/
--- sbin/ipfw/ipfw.c.orig	Sun Feb 13 04:46:59 2000
+++ sbin/ipfw/ipfw.c	Mon Jun  5 08:09:29 2000
@@ -381,6 +381,8 @@
                     printf(" keep-state %d", (int)chain->next_rule_ptr);
                 else
                     printf(" keep-state");
+		if (chain->fw_dyn_lifetime)
+		    printf(" lifetime %d", (int)chain->fw_dyn_lifetime);
         }
         /* Direction */
         if (chain->fw_flg & IP_FW_BRIDGED)
@@ -1553,6 +1555,15 @@
                             (int)rule.next_rule_ptr = type ;
                             av++; ac--;
                         }
+			if (ac > 0 && !strncmp(*av,"lifetime",strlen(*av))) {
+			    u_long lifetime ;
+
+			    av++; ac--;
+			    if (ac > 0 && (lifetime = atoi(*av)) != 0) {
+				rule.fw_dyn_lifetime = lifetime;
+				av++; ac--;
+			    }
+			}
                         continue;
                 }
                 if (!strncmp(*av,"bridged",strlen(*av))) { 




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




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