# FILE: ipfw2.patch # PATCHES: /usr/src/sys/netinet/ip_fw2.h # /usr/src/sys/netinet/ip_fw2.c # /usr/src/sbin/ipfw/ipfw2.c # 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. This patch does NOT, however, modify # the man page. Please apply the similar ipfw.patch to fully # and correctly patch the man page as well as add similar # functionality for the ipfw (not ipfw2) system. # --- /usr/src/sys/netinet/ip_fw2.h.orig Tue Jul 23 21:21:23 2002 +++ /usr/src/sys/netinet/ip_fw2.h Thu Aug 1 09:54:50 2002 @@ -91,7 +91,7 @@ O_IPOPTS, /* arg1 = 2*u8 bitmap */ 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. */ /* @@ -319,6 +319,7 @@ struct ipfw_flow_id id; /* (masked) flow id */ struct ip_fw *rule; /* pointer to rule */ ipfw_dyn_rule *parent; /* pointer to parent rule */ + u_int32_t lifetime; /* per-rule lifetime */ u_int32_t expire; /* expire time */ u_int64_t pcnt; /* packet match counter */ u_int64_t bcnt; /* byte match counter */ --- /usr/src/sys/netinet/ip_fw2.c.orig Tue Jul 23 21:21:23 2002 +++ /usr/src/sys/netinet/ip_fw2.c Thu Aug 1 09:54:50 2002 @@ -765,7 +765,7 @@ } } } - q->expire = time_second + dyn_ack_lifetime; + q->expire = time_second + q->lifetime; break; case BOTH_SYN | BOTH_FIN: /* both sides closed */ q->expire = time_second + dyn_fin_lifetime; @@ -782,11 +782,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) @@ -821,7 +826,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; @@ -851,7 +857,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; @@ -894,7 +913,7 @@ return q; } } - return add_dyn_rule(pkt, O_LIMIT_PARENT, rule); + return add_dyn_rule(pkt, O_LIMIT_PARENT, rule, 0); } /** @@ -942,7 +961,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 */ @@ -984,7 +1004,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: @@ -2173,7 +2193,6 @@ switch (cmd->opcode) { case O_NOP: case O_PROBE_STATE: - case O_KEEP_STATE: case O_PROTO: case O_IP_SRC_ME: case O_IP_DST_ME: @@ -2195,6 +2214,7 @@ goto bad_size; break; + case O_KEEP_STATE: case O_UID: case O_GID: case O_IP_SRC: --- /usr/src/sbin/ipfw/ipfw2.c.orig Wed Jul 31 17:35:46 2002 +++ /usr/src/sbin/ipfw/ipfw2.c Thu Aug 1 09:48:08 2002 @@ -196,6 +196,7 @@ TOK_IN, TOK_LIMIT, TOK_KEEPSTATE, + TOK_LIFETIME, TOK_LAYER2, TOK_OUT, TOK_XMIT, @@ -287,6 +288,7 @@ { "in", TOK_IN }, { "limit", TOK_LIMIT }, { "keep-state", TOK_KEEPSTATE }, + { "lifetime", TOK_LIFETIME }, { "bridged", TOK_LAYER2 }, { "layer2", TOK_LAYER2 }, { "out", TOK_OUT }, @@ -1058,6 +1060,8 @@ case O_KEEP_STATE: printf(" keep-state"); + if (cmd32->d[0]) + printf(" lifetime %u", cmd32->d[0]); break; case O_LIMIT: @@ -2166,6 +2170,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. */ @@ -2698,7 +2705,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: