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>