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>