Skip site navigation (1)Skip section navigation (2)
Date:      Mon,  5 Jun 2000 11:55:59 -0600 (MDT)
From:      "Aaron Gifford" <agifford@infowest.com>
To:        freebsd-net@freebsd.org
Subject:   ipfw per-rule keep-state dynamic rule lifetime 
Message-ID:  <20000605175559.D853620F77@ns1.infowest.com>

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

Recently on my home FreeBSD 4.0-STABLE box I rewrote my ipfw rules to
utilize ipfw's stateful dynamic rules.  I discovered, however that I
needed and wanted finer control over how the rules expired than was
available through the sysctl variables.  I prefer the default values
of the expiration control variables for most of my TCP and other IP
traffic:

  net.inet.ip.fw.dyn_ack_lifetime: 300
  net.inet.ip.fw.dyn_syn_lifetime: 20
  net.inet.ip.fw.dyn_fin_lifetime: 20
  net.inet.ip.fw.dyn_rst_lifetime: 5
  net.inet.ip.fw.dyn_short_lifetime: 5

But in a few cases (like ICQ UDP traffic and SSH TCP traffic) I need
a longer expiration lifetime on the dynamic rules.  That's how the
patch to ip_fw.h, ip_fw.c, and ipfw.c included below was born.

The patch does several things:

  1. It adds a new ipfw optional extention to rules with keep-state
     in them, the keyword "lifetime" followed by a number specifying
     a number of seconds that dynamic stateful rules will live,
     overriding the value net.inet.ip.fw.dyn_ack_lifetime in the case
     of TCP rules, and net.inet.ip.fw.dyn_short_lifetime in the case
     of other IP protocols.  For example, I wanted a 2-hour rule
     lifetime for my SSH sessions and a 5 min. timeout for my ICQ UDP
     traffic:

         ipfw add pass tcp from ${myip} to any 22 out setup \
              keep-state lifetime 7200
         ipfw add pass udp from ${myip} to ${icqnet} 4000 out \
              keep-state lifetime 300

  2. In order to store this new per-rule value, the patch adds a new
     field to the struct ip_fw member union fw_un and to the struct
     ipfw_dyn_rule where the new lifetime value is stored.  If the
     rule does NOT specify an overriding dynamic rule expiration
     lifetime, zero is stored in these new members and the normal
     sysctl variables are used to set the dynamic rule lifetime
     expiration timeouts as usual.  If a value IS specified, then
     it will override net.inet.ip.fw.dyn_ack_lifetime in the case of
     TCP dynamic rules, or net.inet.ip.fw.dyn_short_lifetime in the case
     of non-TCP dynamic rules.

  3. The ipfw command is modified to permit the new keyword and value
     and fill in the new structure members accordingly.

  4. Lastly, it looks like the original ipfw code in 4.0-STABLE would
     set the expiration for non-TCP traffic to the value of the sysctl
     variable net.inet.ip.fw.dyn_syn_lifetime even though subsequent
     matching non-TCP traffic would update the expiration using the
     variable net.inet.ip.fw.dyn_short_lifetime.  I modified it while
     adding the above functionality to use the same sysctl variable
     net.inet.ip.fw.dyn_short_lifetime for initial expiration value
     and subsequent updates for non-TCP traffic that does NOT have an
     explicitly set lifetime value specified by ipfw on the command
     line.  Was there a reason that non-TCP traffic was not using the
     net.inet.ip.fw.dyn_short_lifetime value initially?  If so, I had
     probably better change back to using the sysctl variable
     net.inet.ip.fw.dyn_syn_lifetime for initial expiration lifetime
     of non-TCP traffic.

The patch applies to three files:

   /usr/src/sys/netinet/ip_fw.c
   /usr/src/sys/netinet/ip_fw.h
   /usr/src/sbin/ipfw/ipfw.c

Once all three were successfully patched, I copied the ip_fw.h file
to /usr/include/netinet/ip_fw.h and recompiled the ipfw command and
installed the modified version, recompiled a new kernel and installed
it, and rebooted.  It is wonderful to be able to override the default
expirations on a per rule basis.  Now my SSH sessions don't get
clobbered by the dymamic rule expiring too soon, my ICQ connection
(UDP) lives on (it looks like my ICQ client sends traffic regularly
every 4 minutes) with a 5 minute timeout, and the rest of my traffic
uses the very sensible default sysctl variables.  Oh, I should say
that the new code ONLY overrides the two variables mentioned before
and that TCP sessions still use the remaining sysctl variable values
for the other states.

I suspect that the way the patches currently work may not be the best
way to store the overriding value.  I see that the next_rule_ptr struct
member is used to store a numeric dynamic rule type but it looks like
the type is not yet really used.  I considered stealing the existing
dynamic type code and using that value as the overriding expiration, but
then thought better of it.  Having two separate structure members
(one in the actual ipfw chain rule, and one in the resulting dynamic rule)
may not be the most efficient way to do it, but I don't have enough
experience with the ipfw structures and code to know if there's a quick
way during dynamic processing to look up a value in the structure of the
chain rule from which the dynamic rule was born (which would permit the
addition of only one new structure member instead of two separate members
in two separate structures).

After the changes, I did not rebuild world on my boxen.  I only updated
ipfw and the kernel.  Do I need to rebuild anything else?  I don't know
what else may utilize the modified structures in ip_fw.h.

While my limited use on several FreeBSD boxen has been stable and useful,
I really don't know if these patches break anything else.  Likewise I
don't know if they'll be useful to anyone else, but in case they are,
I thought I'd post.  I hope that the maintainer(s) of the ipfw code
consider(s) adding per-rule expiration lifetime overriding in future
updates (so my patches become obsolete) since it is a very useful feature.

Aaron out.

=== NOW FOR THE PATCHES ===
*** ip_fw.c.orig	Sun Jun  4 17:42:24 2000
--- ip_fw.c	Sun Jun  4 18:06:30 2000
*************** found:
*** 651,657 ****
  	    break ;
  	case TH_SYN | (TH_SYN << 8) :
  	    /* move to established */
! 	    q->expire = time_second + dyn_ack_lifetime ;
  	    break ;
  	case TH_SYN | (TH_SYN << 8) | TH_FIN :
  	case TH_SYN | (TH_SYN << 8) | (TH_FIN << 8) :
--- 651,657 ----
  	    break ;
  	case TH_SYN | (TH_SYN << 8) :
  	    /* move to established */
! 	    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) :
*************** found:
*** 673,679 ****
  	}
      } else {
  	/* should do something for UDP and others... */
! 	q->expire = time_second + dyn_short_lifetime ;
      }
      if (match_direction)
  	*match_direction = dir ;
--- 673,679 ----
  	}
      } else {
  	/* should do something for UDP and others... */
! 	q->expire = time_second + (q->lifetime ? q->lifetime : dyn_short_lifetime) ;
      }
      if (match_direction)
  	*match_direction = dir ;
*************** add_dyn_rule(struct ipfw_flow_id *id, st
*** 721,727 ****
      if (mask)
  	r->mask = *mask ;
      r->id = *id ;
!     r->expire = time_second + dyn_syn_lifetime ;
      r->chain = chain ;
      r->type = ((struct ip_fw_ext *)chain->rule)->dyn_type ;
  
--- 721,733 ----
      if (mask)
  	r->mask = *mask ;
      r->id = *id ;
!     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 ;
  
*** ip_fw.h.orig	Sun Jun  4 17:42:28 2000
--- ip_fw.h	Sun Jun  4 18:07:50 2000
*************** struct ip_fw {
*** 73,78 ****
--- 73,79 ----
  	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 */
  	/*
*************** struct ip_fw_ext {             /* extend
*** 121,126 ****
--- 122,128 ----
  #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;
*************** struct ipfw_dyn_rule {
*** 147,152 ****
--- 149,155 ----
      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	*/
*** ipfw.c.orig	Sun Jun  4 18:09:37 2000
--- ipfw.c	Sun Jun  4 18:16:10 2000
*************** show_ipfw(struct ip_fw *chain, int pcwid
*** 381,386 ****
--- 381,388 ----
                      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)
*************** add(ac,av)
*** 1553,1558 ****
--- 1555,1569 ----
                              (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))) { 
=== END OF PATCHES ===



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




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