Date: Thu, 5 Jul 2001 00:36:19 -0600 (MDT) From: Aaron Gifford <agifford@infowest.com> To: FreeBSD-gnats-submit@freebsd.org Subject: kern/28713: NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control Message-ID: <20010705063619.A9A31210EC@ns1.infowest.com>
next in thread | raw e-mail | index | archive | help
Note: There was a bad value `non-criticial' for the field `>Severity:'. It was set to the default value of `serious'. >Number: 28713 >Category: kern >Synopsis: NEW IPFW FEATURE [PATCHES]: Dynamic rule expiration lifetime fine-grained control >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Wed Jul 04 23:40:01 PDT 2001 >Closed-Date: >Last-Modified: >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: >Unformatted: Please read the bulk of my message in the "Description:" section below... To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010705063619.A9A31210EC>