From agifford@infowest.com  Wed Jul  4 23:36:20 2001
Return-Path: <agifford@infowest.com>
Received: from ns1.infowest.com (ns1.infowest.com [204.17.177.10])
	by hub.freebsd.org (Postfix) with ESMTP
	id 2A06337B405; Wed,  4 Jul 2001 23:36:20 -0700 (PDT)
	(envelope-from agifford@infowest.com)
Received: from ns1.infowest.com (ns1.infowest.com [204.17.177.10])
	by ns1.infowest.com (Postfix) with SMTP
	id A9A31210EC; Thu,  5 Jul 2001 00:36:19 -0600 (MDT)
Message-Id: <20010705063619.A9A31210EC@ns1.infowest.com>
Date: Thu,  5 Jul 2001 00:36:19 -0600 (MDT)
From: Aaron Gifford <agifford@infowest.com>
Reply-To: Aaron Gifford <agifford@infowest.com>
To: FreeBSD-gnats-submit@freebsd.org
Subject: NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control

>Number:         28713
>Category:       kern
>Synopsis:       NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    luigi
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jul 04 23:40:01 PDT 2001
>Closed-Date:    Mon Sep 02 18:52:46 PDT 2002
>Last-Modified:  Mon Sep 02 18:52:46 PDT 2002
>Originator:     Aaron Gifford
>Release:        FreeBSD 4.3-STABLE i386
>Organization:
N/A
>Environment:
System: FreeBSD my.host.name 4.3-STABLE FreeBSD 4.3-STABLE #0: Thu Jun 21 20:14:40 MDT 2001 root@my.host.name:/usr/obj/usr/src/sys/CUSTOM i386


>Description:


  NEW FEATURE:  Fine-grained control over dynamic rule expiration using
                an optional per-rule expiration lifetime


  When using stateful ipfw rules, the dynamic rule expiration times
  are governed by the values of the net.inet.ip.fw.dyn_*_lifetime
  variables.  This is an excellent attribute of the ipfw stateful
  rule system.  System administrators can tune overall system
  rule expiration times to tailor ipfw to best suit their needs.

  Unfortunately, this global tuning of rule expiration times just does
  not give the ipfw power user the fine-grained control he or she needs
  or wants.  For example, the default 300-second (5-minute) expiration
  for TCP sessions (governed by the net.inet.ip.fw.dyn_ack_lifetime
  sysctl variable) in the connected state works well for TCP protocols
  like HTTP where the TCP session will very likely not idle beyond
  that default expiration time.  But for TCP sessions like telnet or
  ssh where a user may let his or her connection idle longer than
  that, it is extremely annoying to discover the session frozen
  because the firewall expired the dynamic rule for the session.
  The system administrator could just increase the rule expiration
  time, but that breaks the elegance of the default (or admin.-tuned)
  global expiration setting that works well and is desireable for
  most connections.

  The solution is quite simple: add an option to override the default
  sysctl variable settings for TCP sessions in the connected state,
  and also for UDP sessions.  With such an option, ipfw rules that
  do not use the option behave as they do now.  Rules that use the
  option can override the default sysctl settings on a per-rule basis.

  Here's an example:

    OLD IPFW RULES:
      ipfw add check-state
      ipfw add deny tcp from any to any established
      ipfw add allow tcp from me to any keep-state out setup
      ...
      ipfw add allow udp from me to any 53 keep-state out
      ipfw add allow udp from me to ${icq} 4000 keep-state out
      ...

    With the above rules, the host can create new outbound TCP sessions
    using the default expiration times.  However, users on the host who
    make outbound telnet/ssh sessions (or other long-idleing sessions)
    will be frusterated unless the admin. increases the global default
    expiration lifetime.  And if the admin. does this, he/she increases
    consumption of system resources in cases of outbound TCP sessions
    where the remote side of the connection dies during the connected
    state without any indication or response, leaving the state as
    connected, the dynamic rule persisting longer than would otherwise
    be needed.

    NEW IPFW RULES:
      ipfw add check-state
      ipfw add deny tcp from any to any established
      ipfw add allow tcp from me to any 22,23 keep-state lifetime 3600 out setup
      ipfw add allow tcp from me to any keep-state out setup
      ...
      ipfw add allow udp from me to any 53 keep-state out
      ipfw add allow udp from me to ${icq} 4000 keep-state lifetime 600 out
      ...

    Now the administrator can use the default settings for most TCP and
    UDP sessions, but override the settings for specific uses.  This
    flexibility is very desirable.


  This feature is currently in use on several FreeBSD-STABLE machines
  where I work, as well as on my home machine.  I know that several
  other FreeBSD users have at least downloaded and looked at the patches
  that add this feature.  I do not know if anyone else is using it
  currently.

  The patches below implement this feature.  The patches included here
  are against the latest CVS versions of the various files as of Wed.
  4 July 2001 8:00 PM MST (-0600).  I also have patches against -STABLE
  if anyone wants them.  I have been running this patch (the only changes
  over time were very minimal, line offsets, etc. to keep the patch
  working against -STABLE source changes over time) for well over a
  year now and it has been rock solid on Internet web server hosts as
  well as on various border firewall machines.

  IPFW MAINTAINERS: PLEASE, please examine the patches.  If there are
  no glaring errors or omissions, please consider committing them.
  
   * The patches update the 'ipfw' man page to document the new
     feature.

   * Compatibility: using an unpatched ipfw with a patched kernel
     or using a patched ipfw with an unpatched kernel should work
     just fine with normal rules (the 'lifetime' feature will not
     work).

 
  Thank you.

  Aaron Gifford
  <agifford@infowest.com>


*** NOTE: Patches to the latest CVS versions of the relevant source
*** files are included below in the "Fix:" section.  These patches
*** should apply cleanly to FreeBSD-CURRENT.  Feel free to e-mail
*** me for patches that apply cleanly to FreeBSD-STABLE.
	
>How-To-Repeat:
        N/A - See above description or patches below
>Fix:
	Commit the patches below.

--- /usr/src/sys/netinet/ip_fw.h.orig	Tue Feb 20 11:39:17 2001
+++ /usr/src/sys/netinet/ip_fw.h	Thu Jul  5 03:46:51 2001
@@ -74,6 +74,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 */
 	/*
@@ -122,6 +123,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) next;
@@ -148,6 +150,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	*/
--- /usr/src/sys/netinet/ip_fw.c.orig	Mon Jul  2 15:50:31 2001
+++ /usr/src/sys/netinet/ip_fw.c	Thu Jul  5 03:46:51 2001
@@ -763,7 +763,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) :
@@ -788,7 +788,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 ;
@@ -834,7 +834,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 ;
 
--- /usr/src/sbin/ipfw/ipfw.c.orig	Sun Jul  1 22:00:26 2001
+++ /usr/src/sbin/ipfw/ipfw.c	Thu Jul  5 03:46:51 2001
@@ -377,6 +377,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)
@@ -917,6 +919,7 @@
 "    tcpack {acknowledgement number}\n"
 "    tcpwin {window size}\n"
 "    icmptypes {type[, type]}...\n"
+"    keep-state [lifetime {number of seconds}]\n"
 "  pipeconfig:\n"
 "    {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
 "    {bw|bandwidth} interface_name\n"
@@ -1971,6 +1974,15 @@
 			if (ac > 0 && (type = atoi(*av)) != 0) {
 			    (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--;
+			    }
 			}
 		} else if (!strncmp(*av, "bridged", strlen(*av))) {
 			rule.fw_flg |= IP_FW_BRIDGED;
--- /usr/src/sbin/ipfw/ipfw.8.orig	Wed Jun  6 20:56:56 2001
+++ /usr/src/sbin/ipfw/ipfw.8	Thu Jul  5 03:46:51 2001
@@ -640,18 +640,38 @@
 interface.
 .It Ar options :
 .Bl -tag -width indent
-.It Cm keep-state Op Ar method
+.It Xo Cm keep-state Op Ar method
+.Op Cm lifetime Ar number
+.Xc
 Upon a match, the firewall will create a dynamic rule, whose
-default behaviour is to matching bidirectional traffic between
+default behaviour is to match bidirectional traffic between
 source and destination IP/port using the same protocol.
-The rule has a limited lifetime (controlled by a set of
+The rule has a limited lifetime controlled by a set of
 .Xr sysctl 8
-variables), and the lifetime is refreshed every time a matching
-packet is found.
+variables that may be overridden on a per-rule basis.
+The lifetime is refreshed each time a matching packet is
+found.
 .Pp
 The actual behaviour can be modified by specifying a different
 .Ar method ,
 although at the moment only the default one is specified.
+.Pp
+The default rule lifetime may be overridden for a specific
+rule by appending
+.Cm lifetime Ar number
+to explicitly set the number of seconds for the dynamic rule
+lifetime.
+.Pp
+For TCP rules, explicitly setting a rule lifetime overrides the
+default setting stored in the
+.Xr sysctl 8
+variable
+.Em net.inet.ip.fw.dyn_ack_lifetime .
+For non-TCP rules, it overrides the
+.Xr sysctl 8
+variable
+.Em net.inet.ip.fw.dyn_short_lifetime
+instead.
 .It Cm bridged
 Matches only bridged packets.
 This can be useful for multicast or broadcast traffic, which

>Release-Note:
>Audit-Trail:

From: Aaron D.Gifford <agifford@infowest.com>
To: freebsd-gnats-submit@FreeBSD.org
Cc:  
Subject: Re: kern/28713: NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control
Date: Thu, 5 Jul 2001 00:45:47 -0600

 Oops, the "Severity" of this PR was supposed to be "non-critical" but a typo caused GNATS to assign it the default "serious".  My fault!
 
 Aaron Gifford

From: Aaron D.Gifford <agifford@infowest.com>
To: Luigi Rizzo <luigi@info.iet.unipi.it>
Cc: FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: kern/28713: NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control
Date: Thu, 5 Jul 2001 10:13:29 -0600

 On Thursday 05 July 2001 05:51, Luigi Rizzo wrote:
 > >   When using stateful ipfw rules, the dynamic rule expiration times
 > >   are governed by the values of the net.inet.ip.fw.dyn_*_lifetime
 > >   variables.  This is an excellent attribute of the ipfw stateful
 >
 > It is actually just half of what is needed. In addition to the
 > 'lifetime', and to avoid early expiration for idle sessions, you'd
 > need someone (maybe the firewall) to send around keepalives to
 > probe the session.
 >
 >
 > Your patch slightly improves the situation, but does not radically
 > change it or solve the problem. You still need the firewall
 > administrator to do a special configuration for your session, pick
 > a timeout value (and what do you pick ? anything less than 24hrs
 > is maybe not that significant for a session that you might forget
 > idle and you want to find active the day after), and you need
 > additional firewall rules to override the default for the specific
 > sessions.
 
 Hi,
 
 Thanks for taking time to look at this.
 
 While I will politely disagree with your characterization "..slightly 
 improves the situation, but does not radically change it.." your point is 
 valid: the administrator does indeed have to choose large enough 
 time-outs so that either keepalives keep the session open, or the 
 protocol running across the TCP or UDP session itself keeps the session 
 open.
 
 However, the patches do far more than make a slight improvement.  From my 
 own use (an office firewall and an several medium volume Internet server 
 hosts) the difference between having absolutely fine-grained no cotrol 
 exept tweaking a global value which I did NOT wish to touch made the 
 difference between choosing to use stateful firewall rules and rejecting 
 them completely in favor of an older stateless ruleset that worked but 
 not as elegantly.  I have heard from several other administrators who 
 said the same thing to me: per-rule expiration overrides were the 
 difference that made stateful ipfw rules useful.
 
 As for it being only "half" the solution, let me tell that from practical 
 experience, it ends up being more like 90-99% of the solution.  Yes, the 
 administrator does have to make smart rule lifetime expiration decisions, 
 but that's not too hard since there are usually only a very few protocols 
 that don't use the defaults.  It works on much the same principle that 
 the default settings do: the majority of the traffic will flow correctly. 
  So the defaults catch 90-99% of the sessions, then the per-rule 
 expiration catches another 90-99% of the remaining traffic/sessions.
 
 >
 > This is why i do not consider this patch that urgent and i am not so
 > inclinded to commit it.
 
 You're right, it isn't urgent.  The characterization of the "Severity:" 
 field was due to a typo on my part which caused GNATS to put in the 
 default value.  I had intended it to be "non-critical", not "serious" 
 which was the GNATS system default.  I quickly appended a note to my 
 GNATS report to indicate this.  Sorry about that.
 
 >
 > In cases like this, i'd rather suggest a better approach which is
 > to raise the default to something larger (like 1-2hr) and set the
 > keepalive interval on your client to a value that is shorter than
 > the expire interval.
 
 This does work, but there are others like me who find doing things like 
 this on a global scale really grates against the underlying security 
 ethic that "that which is not explicity permitted is denied" in that I am 
 permitting a longer expiration on sessions that I don't wish to, just to 
 get some few minority of sessions to not timeout.  It bothers me in 
 something of the same way that making all directories world-writable just 
 to give one user write access would (even though the security 
 implecations are vastly different).
 
 > The reason why a large timeout is not so problematic is that as soon
 > as the firewall sees a FIN or a RST on one side, it reverts to
 > using a much shorter timeout so in most cases, a regular or abortive
 > shutdown of the connection will result in a quick expire of the rule.
 
 A large timeout is not terribly problematic, but it is irritating since I 
 have to increase the maximum number of dynamic rules on my firewall just 
 to account for the additional rules that aren't expiring (those few 
 sessions that don't see a FIN or RST - and they do exists though few in 
 number compared to normal sessions and to those sessions that will see a 
 FIN/RST on an aborted connection).  The longer my global expiration 
 setting, the more they will eat up rule space in my table in proportion 
 to the overall traffic/session load on my firewall, which for a large 
 firewall behind which sit many machines/networks could become 
 considerable.  Again, it goes against the ethic of permitting more than I 
 really need to.
 
 As for setting the keep-alive settings on the client side, that is 
 virtually impossible.  I would have to do it on clients on many 
 Windows95/98/2000/ME/XP, Mac OS9, Mac OS X, Linux, and FreeBSD hosts 
 behind the firewall that that solution quickly becomes an administration 
 nightmare.  Or I would have to do it on all possible Internet server 
 hosts that serve as the end-point to clients behind the firewall.  Either 
 solution is not a solution, but a virtual impossibility.
 
 >
 > 	cheers
 > 	luigi
 
 Thank you for looking at the patch.  And thank you as well for your 
 excellent stateful firewal work.  Stateful rules are wonderful to use and 
 I heartily recommend going stateful to anyone and everyone.  (Of course 
 I'll plug my 'lifetime' patch to those same folks...  *grin*).
 
 Aaron Gifford

From: "Aaron D. Gifford" <agifford@infowest.com>
To: freebsd-gnats-submit@FreeBSD.org, agifford@infowest.com
Cc:  
Subject: Re: kern/28713: NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control
Date: Wed, 30 Jan 2002 18:02:46 -0700

 An updated version of this patchset against 4.5-RELEASE (and I hope to also 
 keep updated versions against 4.5-STABLE as it evolves) can be had at:
 
   http://www.aarongifford.com/computers/ipfwpatch.html
 
 Let me add my encouragement to Luigi or any other ipfw maintainers to merge 
 this into the FreeBSD code base.  There are several users of these patches 
 who think this is a good idea.
 
 REASONS FOR fine grained (per rule) timeout control:
  * It adds the ability to avoid frozen TCP sessions (SSH is the classic 
 complaint) when the global sysctl timeout is short
  * It can be desirable to use a short timeout for global rule settings in 
 certain cases (particularly with a host or firewall handling UDP dynamic 
 rules) to prevent dynamic rule table bloat.  These patches permit the system 
 administrator to set up specific exceptions (I use it to permit ICQ UDP 
 sessions through a firewall while keeping all other UDP protocols I permit on 
 a short, tight leash).
  * There is something asthetically pleasing about having an option for more 
 fine grained control available at virtually no additional cost (CPU/memory) 
 and then using that control in harmony with the computer security maxim, 
 "Everything not explicitly permitted is denied."  These extra controls give 
 admins. the power to do exactly that with their IPFW rule sets: I can use the 
 global sysctl variables as my default security policy very tightly (short 
 timeouts for most things), then make exceptions as needed.
 
 REASONS AGAINST fine grained (per rule) timeout control:
  * It is unnecessary - just set your global sysctl timeouts high enough that 
 none of the services your FreeBSD host or firewall or router handles are 
 interrupted by premature timeouts.
 
 Thank you for your time and energy spent advancing FreeBSD!
 
 Aaron out.
Responsible-Changed-From-To: freebsd-bugs->ru 
Responsible-Changed-By: sheldonh 
Responsible-Changed-When: Thu Jan 31 01:42:16 PST 2002 
Responsible-Changed-Why:  
Over to maintainer. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=28713 
Responsible-Changed-From-To: ru->luigi 
Responsible-Changed-By: sheldonh 
Responsible-Changed-When: Thu Jan 31 02:12:55 PST 2002 
Responsible-Changed-Why:  
Re-assigned to co-maintainer on request. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=28713 
Responsible-Changed-From-To: luigi->closed 
Responsible-Changed-By: luigi 
Responsible-Changed-When: Mon Sep 2 11:42:54 PDT 2002 
Responsible-Changed-Why:  


http://www.freebsd.org/cgi/query-pr.cgi?pr=28713 
State-Changed-From-To: open->closed 
State-Changed-By: luigi 
State-Changed-When: Mon Sep 2 18:50:12 PDT 2002 
State-Changed-Why:  
ipfw2 has keepalives which make this feature almost completely 
superflous. 



Responsible-Changed-From-To: closed->luigi 
Responsible-Changed-By: luigi 
Responsible-Changed-When: Mon Sep 2 18:50:12 PDT 2002 
Responsible-Changed-Why:  
wrong line used in previous attempt... 


http://www.freebsd.org/cgi/query-pr.cgi?pr=28713 
>Unformatted:
 Please read the bulk of my message in the "Description:" section below...
 
 
