From nobody@FreeBSD.ORG  Tue Oct 17 15:39:06 2000
Return-Path: <nobody@FreeBSD.ORG>
Received: by hub.freebsd.org (Postfix, from userid 32767)
	id D8B0E37B4F9; Tue, 17 Oct 2000 15:39:06 -0700 (PDT)
Message-Id: <20001017223906.D8B0E37B4F9@hub.freebsd.org>
Date: Tue, 17 Oct 2000 15:39:06 -0700 (PDT)
From: agifford@infowest.com
Sender: nobody@FreeBSD.ORG
To: freebsd-gnats-submit@FreeBSD.org
Subject: Patch to add support to ipfw for per rule overriding of dynamic keep-state rule expiration lifetimes
X-Send-Pr-Version: www-1.0

>Number:         22065
>Category:       kern
>Synopsis:       Patch to add support to ipfw for per rule overriding of dynamic keep-state rule expiration lifetimes
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    luigi
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Oct 17 15:40:01 PDT 2000
>Closed-Date:    Mon Sep 02 18:55:40 PDT 2002
>Last-Modified:  Mon Sep 02 18:55:40 PDT 2002
>Originator:     Aaron D. Gifford
>Release:        FreeBSD 4.1.1-STABLE
>Organization:
>Environment:
FreeBSD my.freebsd.box 4.1.1-STABLE FreeBSD 4.1.1-STABLE #0: Thu Oct 12 11:58:29 MDT 2000     root@my.freebsd.box:/usr/src/sys/compile/LOCAL  i386
>Description:
Hello,

When using ipfw's dynamic stateful rules, while users CAN use the sysctl
variables to control expiration times globally, it cannot yet be done
on a rule-by-rule basis.  The patch below fixes this, adding the ability
to append "lifetime X" (where X is a number) to any keep-state rule.
For keep-state rules that do NOT append this new option, the old behavior
guided by the sysctl variables remains.  For those rules that add this
option, on a rule-by-rule basis the expiration time may be customized.

While writing these patches, I came across what LOOKS like a bug but
may not. In ip_fw.c, I noticed that both TCP and non-TCP keep-state
rules are updated using the net.inet.ip.fw_syn_lifetime where I
would think that non-TCP keep-state rules should instead be using
the net.inet.ip.fw.dyn_short_lifetime variable instead.  This patch
also fixes this issue.  If this is NOT a bug but is deliberate,
I would encourage that it be documented as such.  I believe it is
around line 798 of ip_fw.c (at least on FreeBSD-4.1.1-STABLE as of
17 Oct. 2000).

This patch also adds documentation for the "lifetime X" option
to the ipfw man page.

I have been using these patches on several moderate-traffic Internet
hosts and on several firewall boxes, all running various versions of
FreeBSD 4.0 and 4.1 for several months now with absolutely no problems
at all.  I belive these changes to be VERY stable.

This change would be extremely useful.  I use it right now like this:
(This is a small subset of my rules, edited for this example)

  ipfw add check-state
  ### UDP filters:
  # Keep state on UDP sent to ICQ servers for up to 5 minutes
  # so ICQ clients won't keep getting disconnected:
  ipfw add pass udp from ${MYIP} to ${ICQ_NETWORK} out keep-state lifetime 300
  # All other UDP will use the default net.inet.ip.fw.dyn_short_lifetime
  # expiration:
  ipfw add pass udp from ${MYIP} to any out keep-state
  ### TCP filters:
  # Drop bogus "established" traffic:
  ipfw add deny tcp from any to any established
  # Keep my SSH sessions around for up to a whole day:
  ipfw add pass tcp from ${MYIP} to any out setup keep-state lifetime 86400
  # All other outgoing TCP gets the default expiration as set in the
  # system variable net.inet.ip.fw_syn_lifetime:
  ipfw add pass tcp from ${MYIP} to any out setup keep-state

I really hope that this new feature will be adopted.  It has been very
useful.  And that would also let me be more lazy -- I wouldn't have to
patch my system after each cvsup.  :)

If the patch submitted below (in the "Fix to the problem if known:" part) doesn't come through clearly, email me and I'll email the patch to you.

Sincerely,
Aaron Gifford
>How-To-Repeat:
This is a feature request
>Fix:
--- sys/netinet/ip_fw.h.orig    Mon Aug 21 18:33:18 2000
+++ sys/netinet/ip_fw.h Tue Oct 17 16:08:36 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   */
--- sys/netinet/ip_fw.c.orig    Tue Oct 17 07:44:57 2000
+++ sys/netinet/ip_fw.c Tue Oct 17 16:08:36 2000
@@ -722,7 +722,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) :
@@ -747,7 +747,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 ;
@@ -795,7 +795,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 ;

--- sbin/ipfw/ipfw.c.orig       Tue Oct 17 07:44:55 2000
+++ sbin/ipfw/ipfw.c    Tue Oct 17 16:08:36 2000
@@ -383,6 +383,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)
@@ -837,6 +839,7 @@
 "    ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
 "    tcpoptions [!]{mss|window|sack|ts|cc},...\n"
 "    icmptypes {type[,type]}...\n"
+"    keep-state [lifetime <number>]\n"
 "  pipeconfig:\n"
 "    {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
 "    {bw|bandwidth} interface_name\n"
@@ -1821,6 +1824,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))) {
--- sbin/ipfw/ipfw.8.orig       Tue Oct 17 07:09:53 2000
+++ sbin/ipfw/ipfw.8    Tue Oct 17 16:08:36 2000
@@ -605,18 +605,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 every 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:
Responsible-Changed-From-To: freebsd-bugs->luigi 
Responsible-Changed-By: johan 
Responsible-Changed-When: Sun Oct 22 11:38:30 PDT 2000 
Responsible-Changed-Why:  
Over to ipfw maintainer. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=22065 

From: Aaron D.Gifford <agifford@infowest.com>
To: freebsd-gnats-submit@FreeBSD.org, freebsd-net@freebsd.org
Cc:  
Subject: Re: kern/22065: Patch to add support to ipfw for per rule overriding of dynamic keep-state rule expiration lifetimes
Date: Sat, 17 Feb 2001 12:48:52 -0700

 Looks like this proposed patch (PR 22065) is still open.  I suppose that 
 means no one has decided whether it is useful or not, or (far more likely) 
 folks are extremely busy doing other fun and useful stuff for FreeBSD.
 
 The patch in the PR is still mostly valid for FreeBSD 4.2-STABLE with a few 
 line offset changes.  I've got an updated version of the patch against 
 4.2-STABLE as of 17 FEB 2001 at the following web address:
 
   http://www.aarongifford.com/computers/ipfwpatch.html
 
 I have been using the patch successfully on several moderate-traffic hosts 
 with no noticable problems.
 
 I do still have two questions that no one has yet answered.  Still, it would 
 be nice to know the following (with regard to the patch):
 
   1. Am I safe using the union fw_un in the ip_fw structure (in ip_fw.h) to 
 store the dynamic rule's lifetime setting or will this overlap with one of 
 the other uses of that union and thus require that I modify the patch to 
 create a new structure member for storing the setting?
 
   2. Am I correct in assuming that in ip_fw.c at roughly line 800 where UDP 
 and TCP matches to the dynamic rule extend the rule expiration by the 
 dyn_syn_lifetime amount that this should instead only extend TCP matches by 
 dyn_syn_lifetime and should extend UDP matches by dyn_short_lifetime instead? 
  I believe this to be the case.  The snippet of code from ip_fw.c in question 
 looks like (beginning at line 800 of ip_fw.c as of FreeBSD 4.2-STABLE on 17 
 FEB 2001):
  
         }
         bzero (r, sizeof (*r) );
  
         if (mask)
             r->mask = *mask ;
         r->id = *id ;
         r->expire = time_second + dyn_syn_lifetime ;  /*<<<THIS LINE<<<*/
         r->chain = chain ;
         r->type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
   
         r->bucket = i ;
         r->next = ipfw_dyn_v[i] ;
         ipfw_dyn_v[i] = r ;
         dyn_count++ ;
          DEB(printf("-- add entry 0x%08x %d -> 0x%08x %d, %d left\n",
             (r->id.src_ip), (r->id.src_port),
             (r->id.dst_ip), (r->id.dst_port),
             dyn_count ); )
     }   
 
 I assume that the line above (flagged above with "<<<THIS LINE<<<") should 
 instead read something like:
 
     r->expire = time_second + (r->id.proto == IPPROTO_TCP ?
         dyn_syn_lifetime : dyn_short_lifetime) ;
 
 My patch assumes that this is the case and modifies the behavior so that 
 non-TCP rule match expiration lifetimes are incremented by the 
 dyn_short_lifetime sysctl setting (if no per-rule lifetime is specified).
 
 I would appreciate any answers to the two above questions, question #1 in 
 particular, as well as any other commentary or suggestions.
 
 Thanks,
 Aaron out.
 
 -- 
 InfoWest, Inc.  *  596 E. Tabernacle  *  St. George, UT 84790
 Voice: 435-674-0165  *  FAX: 435-674-9654
 Web: www.infowest.com  *  E-mail: support@infowest.com
 

From: "Aaron D. Gifford" <agifford@infowest.com>
To: freebsd-gnats-submit@FreeBSD.org, agifford@infowest.com
Cc:  
Subject: Re: kern/22065: Patch to add support to ipfw for per rule overriding of dynamic keep-state rule expiration lifetimes
Date: Fri, 1 Feb 2002 17:52:35 -0700

 I suggest this PR be closed in favor of the more up-to-date one (#28713) at:
 
   http://www.FreeBSD.org/cgi/query-pr.cgi?pr=kern/28713
 
 Aaron out.
State-Changed-From-To: open->closed 
State-Changed-By: luigi 
State-Changed-When: Mon Sep 2 18:54:59 PDT 2002 
State-Changed-Why:  
this replicates kern/28713 and it is made almost completely 
unnecessary by ipfw2's keepalives. 


http://www.freebsd.org/cgi/query-pr.cgi?pr=22065 
>Unformatted:
