# # FILE: ipfw2.patch # PATCHES: /usr/src/sys/netinet/ip_fw.h # /usr/src/sys/netinet/ip_fw2.c # /usr/src/sbin/ipfw/ipfw2.c # /usr/src/sbin/ipfw/ipfw.8 # WRITTEN BY: Aaron D. Gifford - http://www.adg.us/ # PURPOSE: Patch the FreeBSD kernel's (Luigi's) ipfw2 system to support # per-rule dynamic rule lifetime overriding. Patch the kernel # and the ipfw command, as well as the ipfw utility's man page. # # $Id: ipfw2.patch,v 1.3 2004/11/04 23:54:13 adg Exp $ # --- /usr/src/sys/netinet/ip_fw.h.orig Thu Nov 4 16:28:51 2004 +++ /usr/src/sys/netinet/ip_fw.h Thu Nov 4 16:29:50 2004 @@ -102,7 +102,7 @@ O_VERSRCREACH, /* none */ O_PROBE_STATE, /* none */ - O_KEEP_STATE, /* none */ + O_KEEP_STATE, /* u32 = optional lifetime */ O_LIMIT, /* ipfw_insn_limit */ O_LIMIT_PARENT, /* dyn_type, not an opcode. */ @@ -353,6 +353,7 @@ u_int64_t bcnt; /* byte match counter */ struct ipfw_flow_id id; /* (masked) flow id */ u_int32_t expire; /* expire time */ + u_int32_t lifetime; /* per-rule lifetime */ u_int32_t bucket; /* which bucket in hash table */ u_int32_t state; /* state of this rule (typically a * combination of TCP flags) --- /usr/src/sys/netinet/ip_fw2.c.orig Thu Nov 4 16:30:15 2004 +++ /usr/src/sys/netinet/ip_fw2.c Thu Nov 4 16:36:44 2004 @@ -915,7 +915,7 @@ } } } - q->expire = time_second + dyn_ack_lifetime; + q->expire = time_second + q->lifetime; break; case BOTH_SYN | BOTH_FIN: /* both sides closed */ @@ -938,11 +938,16 @@ q->expire = time_second + dyn_rst_lifetime; break; } - } else if (pkt->proto == IPPROTO_UDP) { - q->expire = time_second + dyn_udp_lifetime; } else { - /* other protocols */ - q->expire = time_second + dyn_short_lifetime; + /* + * UDP and other protocols: + * NOTE: The value of q->lifetime was set at the time this + * dynamic rule was created. It was either explicitly set + * by the ruleset creator to a specific value, or was pre- + * set to either dyn_udp_lifetime for UDP, or to + * dyn_short_lifetime for non-UDP protocols. + */ + q->expire = time_second + q->lifetime; } done: if (match_direction) @@ -1004,7 +1009,8 @@ * - "parent" rules for the above (O_LIMIT_PARENT). */ static ipfw_dyn_rule * -add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule) +add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule, + u_int32_t lifetime) { ipfw_dyn_rule *r; int i; @@ -1036,7 +1042,20 @@ } r->id = *id; - r->expire = time_second + dyn_syn_lifetime; + r->lifetime = lifetime; + if (r->id.proto == IPPROTO_TCP) { + r->lifetime = r->lifetime ? r->lifetime : dyn_ack_lifetime; + r->expire = time_second + dyn_syn_lifetime; + } else { + if (r->lifetime == 0) { + if (r->id.proto == IPPROTO_UDP) { + r->lifetime = dyn_udp_lifetime; + } else { + r->lifetime = dyn_short_lifetime; + } + } + r->expire = time_second + r->lifetime; + } r->rule = rule; r->dyn_type = dyn_type; r->pcnt = r->bcnt = 0; @@ -1081,7 +1100,7 @@ return q; } } - return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); + return add_dyn_rule(pkt, O_LIMIT_PARENT, rule, 0); } /** @@ -1133,7 +1152,8 @@ switch (cmd->o.opcode) { case O_KEEP_STATE: /* bidir rule */ - add_dyn_rule(&args->f_id, O_KEEP_STATE, rule); + add_dyn_rule(&args->f_id, O_KEEP_STATE, rule, + ((ipfw_insn_u32 *)cmd)->d[0]); break; case O_LIMIT: /* limit number of sessions */ @@ -1177,7 +1197,7 @@ return 1; } } - add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent); + add_dyn_rule(&args->f_id, O_LIMIT, (struct ip_fw *)parent, 0); } break; default: @@ -2866,7 +2886,6 @@ DEB(printf("ipfw: opcode %d\n", cmd->opcode);) switch (cmd->opcode) { case O_PROBE_STATE: - case O_KEEP_STATE: case O_PROTO: case O_IP_SRC_ME: case O_IP_DST_ME: @@ -2889,6 +2908,7 @@ goto bad_size; break; + case O_KEEP_STATE: case O_UID: case O_GID: case O_JAIL: --- /usr/src/sbin/ipfw/ipfw2.c.orig Fri Sep 17 08:49:08 2004 +++ /usr/src/sbin/ipfw/ipfw2.c Thu Nov 4 16:43:10 2004 @@ -208,6 +208,7 @@ TOK_IN, TOK_LIMIT, TOK_KEEPSTATE, + TOK_LIFETIME, TOK_LAYER2, TOK_OUT, TOK_XMIT, @@ -309,6 +310,7 @@ { "in", TOK_IN }, { "limit", TOK_LIMIT }, { "keep-state", TOK_KEEPSTATE }, + { "lifetime", TOK_LIFETIME }, { "bridged", TOK_LAYER2 }, { "layer2", TOK_LAYER2 }, { "out", TOK_OUT }, @@ -1312,6 +1314,8 @@ case O_KEEP_STATE: printf(" keep-state"); + if (cmd32->d[0]) + printf(" lifetime %u", cmd32->d[0]); break; case O_LIMIT: @@ -2777,6 +2781,9 @@ struct ip_fw *rule; + /* Temporary pointer to the most recent keep-state command: */ + ipfw_insn_u32 *cmd_keepstate = (ipfw_insn_u32 *)0; + /* * various flags used to record that we entered some fields. */ @@ -3379,7 +3386,20 @@ errx(EX_USAGE, "only one of keep-state " "and limit is allowed"); have_state = cmd; - fill_cmd(cmd, O_KEEP_STATE, 0, 0); + cmd->opcode = O_KEEP_STATE; + cmd->len = F_INSN_SIZE(ipfw_insn_u32); + cmd32->d[0] = 0; + cmd_keepstate = cmd32; + break; + + case TOK_LIFETIME: + if (cmd_keepstate == (ipfw_insn_u32 *)0) + errx(EX_USAGE, "lifetime must immediately " + "follow keep-state"); + NEED1("lifetime requires # of seconds"); + cmd_keepstate->d[0] = strtoul(*av, NULL, 0); + cmd_keepstate = (ipfw_insn_u32 *)0; + ac--; av++; break; case TOK_LIMIT: --- /usr/src/sbin/ipfw/ipfw.8.orig Thu Nov 4 16:37:41 2004 +++ /usr/src/sbin/ipfw/ipfw.8 Thu Nov 4 16:45:09 2004 @@ -1065,14 +1065,35 @@ .It Cm ipversion Ar ver Matches IP packets whose IP version field is .Ar ver . -.It Cm keep-state +.It Xo Cm keep-state +.Op Cm lifetime Ar number +.Xc Upon a match, the firewall will create a dynamic rule, whose 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 +variables, and the lifetime is refreshed every time a matching packet is found. +.Pp +The default limited rule lifetime behavior may be modified +for a specific rule by appending +.Cm lifetime Ar number +immediately after +.Cm keep-state . +Doing so will explicitly set the dynamic rule lifetime to the +specified number of seconds, overriding the default lifetime +behavior for the specified rule. +.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 UDP rules, it overrides +.Em net.inet.ip.fw.dyn_udp_lifetime . +For all other rules, it overrides +.Em net.inet.ip.fw.dyn_short_lifetime . .It Cm layer2 Matches only layer2 packets, i.e., those passed to .Nm