diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index bb7919ea7d62..7a27be769f4d 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,7 +1,7 @@ .\" .\" $FreeBSD$ .\" -.Dd July 18, 2016 +.Dd July 19, 2016 .Dt IPFW 8 .Os .Sh NAME @@ -750,7 +750,7 @@ will be executed when the packet matches the body of the rule. .It Cm allow | accept | pass | permit Allow packets that match rule. The search terminates. -.It Cm check-state +.It Cm check-state Op Ar flowname | Cm any Checks the packet against the dynamic ruleset. If a match is found, execute the action associated with the rule which generated this dynamic rule, otherwise @@ -765,6 +765,17 @@ rule is found, the dynamic ruleset is checked at the first or .Cm limit rule. +The +.Ar flowname +is symbolic name assigned to dynamic rule by +.Cm keep-state +opcode. +The special flowname +.Cm any +can be used to ignore states flowname when matching. +The +.Cm default +keyword is special name used for compatibility with old rulesets. .It Cm count Update counters for all packets that match rule. The search continues with the next rule. @@ -1593,7 +1604,7 @@ specified in the same way as .It Cm ipversion Ar ver Matches IP packets whose IP version field is .Ar ver . -.It Cm keep-state +.It Cm keep-state Op Ar flowname 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. @@ -1601,11 +1612,20 @@ 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. +The +.Ar flowname +is used to assign additional to addresses, ports and protocol parameter +to dynamic rule. It can be used for more accurate matching by +.Cm check-state +rule. +The +.Cm default +keyword is special name used for compatibility with old rulesets. .It Cm layer2 Matches only layer2 packets, i.e., those passed to .Nm from ether_demux() and ether_output_frame(). -.It Cm limit Bro Cm src-addr | src-port | dst-addr | dst-port Brc Ar N +.It Cm limit Bro Cm src-addr | src-port | dst-addr | dst-port Brc Ar N Op Ar flowname The firewall will only allow .Ar N connections with the same @@ -1613,8 +1633,6 @@ set of parameters as specified in the rule. One or more of source and destination addresses and ports can be specified. -Currently, -only IPv4 flows are supported. .It Cm lookup Bro Cm dst-ip | dst-port | src-ip | src-port | uid | jail Brc Ar name Search an entry in lookup table .Ar name @@ -2207,6 +2225,12 @@ and .Em dst are used here only to denote the initial match addresses, but they are completely equivalent afterwards). +Rules created by +.Cm keep-state +option also have a +.Ar flowname +taken from it. +This name is used in matching together with addresses, ports and protocol. Dynamic rules will be checked at the first .Cm check-state, keep-state or @@ -2215,23 +2239,23 @@ occurrence, and the action performed upon a match will be the same as in the parent rule. .Pp Note that no additional attributes other than protocol and IP addresses -and ports are checked on dynamic rules. +and ports and flowname are checked on dynamic rules. .Pp The typical use of dynamic rules is to keep a closed firewall configuration, but let the first TCP SYN packet from the inside network install a dynamic rule for the flow so that packets belonging to that session will be allowed through the firewall: .Pp -.Dl "ipfw add check-state" -.Dl "ipfw add allow tcp from my-subnet to any setup keep-state" +.Dl "ipfw add check-state OUTBOUND" +.Dl "ipfw add allow tcp from my-subnet to any setup keep-state OUTBOUND" .Dl "ipfw add deny tcp from any to any" .Pp A similar approach can be used for UDP, where an UDP packet coming from the inside will install a dynamic rule to let the response through the firewall: .Pp -.Dl "ipfw add check-state" -.Dl "ipfw add allow udp from my-subnet to any keep-state" +.Dl "ipfw add check-state OUTBOUND" +.Dl "ipfw add allow udp from my-subnet to any keep-state OUTBOUND" .Dl "ipfw add deny udp from any to any" .Pp Dynamic rules expire after some time, which depends on the status diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c index affc56759663..2288686079cf 100644 --- a/sbin/ipfw/ipfw2.c +++ b/sbin/ipfw/ipfw2.c @@ -1404,6 +1404,7 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, int l; ipfw_insn *cmd, *has_eaction = NULL, *tagptr = NULL; const char *comment = NULL; /* ptr to comment if we have one */ + const char *ename; int proto = 0; /* default */ int flags = 0; /* prerequisites */ ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ @@ -1473,6 +1474,12 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, switch(cmd->opcode) { case O_CHECK_STATE: bprintf(bp, "check-state"); + if (cmd->arg1 != 0) + ename = object_search_ctlv(fo->tstate, + cmd->arg1, IPFW_TLV_STATE_NAME); + else + ename = NULL; + bprintf(bp, " %s", ename ? ename: "any"); /* avoid printing anything else */ flags = HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP | HAVE_IP; @@ -1587,8 +1594,6 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, break; case O_EXTERNAL_ACTION: { - const char *ename; - /* * The external action can consists of two following * each other opcodes - O_EXTERNAL_ACTION and @@ -1609,8 +1614,6 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, } case O_EXTERNAL_INSTANCE: { - const char *ename; - if (has_eaction == NULL) break; /* @@ -2066,6 +2069,9 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, case O_KEEP_STATE: bprintf(bp, " keep-state"); + bprintf(bp, " %s", + object_search_ctlv(fo->tstate, cmd->arg1, + IPFW_TLV_STATE_NAME)); break; case O_LIMIT: { @@ -2082,6 +2088,9 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo, comma = ","; } bprint_uint_arg(bp, " ", c->conn_limit); + bprintf(bp, " %s", + object_search_ctlv(fo->tstate, cmd->arg1, + IPFW_TLV_STATE_NAME)); break; } @@ -2180,7 +2189,10 @@ show_dyn_state(struct cmdline_opts *co, struct format_opts *fo, bprintf(bp, " <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, sizeof(buf)), d->id.dst_port); } else - bprintf(bp, " UNKNOWN <-> UNKNOWN\n"); + bprintf(bp, " UNKNOWN <-> UNKNOWN"); + if (d->kidx != 0) + bprintf(bp, " %s", object_search_ctlv(fo->tstate, + d->kidx, IPFW_TLV_STATE_NAME)); } static int @@ -2821,6 +2833,18 @@ ipfw_check_object_name(const char *name) return (0); } +static char *default_state_name = "default"; +static int +state_check_name(const char *name) +{ + + if (ipfw_check_object_name(name) != 0) + return (EINVAL); + if (strcmp(name, "any") == 0) + return (EINVAL); + return (0); +} + static int eaction_check_name(const char *name) { @@ -3682,6 +3706,24 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) case TOK_CHECKSTATE: have_state = action; action->opcode = O_CHECK_STATE; + if (*av == NULL) { + action->arg1 = pack_object(tstate, + default_state_name, IPFW_TLV_STATE_NAME); + break; + } + if (strcmp(*av, "any") == 0) + action->arg1 = 0; + else if (match_token(rule_options, *av) != -1) { + action->arg1 = pack_object(tstate, + default_state_name, IPFW_TLV_STATE_NAME); + warn("Ambiguous state name '%s', '%s' used instead.\n", + *av, default_state_name); + } else if (state_check_name(*av) == 0) + action->arg1 = pack_object(tstate, *av, + IPFW_TLV_STATE_NAME); + else + errx(EX_DATAERR, "Invalid state name %s", *av); + av++; break; case TOK_ACCEPT: @@ -4502,16 +4544,35 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) av++; break; - case TOK_KEEPSTATE: + case TOK_KEEPSTATE: { + uint16_t uidx; + if (open_par) errx(EX_USAGE, "keep-state cannot be part " "of an or block"); if (have_state) errx(EX_USAGE, "only one of keep-state " "and limit is allowed"); + if (*av == NULL || + match_token(rule_options, *av) != -1) { + if (*av != NULL) + warn("Ambiguous state name '%s'," + " '%s' used instead.\n", *av, + default_state_name); + uidx = pack_object(tstate, default_state_name, + IPFW_TLV_STATE_NAME); + } else { + if (state_check_name(*av) != 0) + errx(EX_DATAERR, + "Invalid state name %s", *av); + uidx = pack_object(tstate, *av, + IPFW_TLV_STATE_NAME); + av++; + } have_state = cmd; - fill_cmd(cmd, O_KEEP_STATE, 0, 0); + fill_cmd(cmd, O_KEEP_STATE, 0, uidx); break; + } case TOK_LIMIT: { ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; @@ -4542,8 +4603,24 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX, TOK_LIMIT, rule_options); - av++; + + if (*av == NULL || + match_token(rule_options, *av) != -1) { + if (*av != NULL) + warn("Ambiguous state name '%s'," + " '%s' used instead.\n", *av, + default_state_name); + cmd->arg1 = pack_object(tstate, + default_state_name, IPFW_TLV_STATE_NAME); + } else { + if (state_check_name(*av) != 0) + errx(EX_DATAERR, + "Invalid state name %s", *av); + cmd->arg1 = pack_object(tstate, *av, + IPFW_TLV_STATE_NAME); + av++; + } break; } @@ -4749,7 +4826,7 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate) * generate O_PROBE_STATE if necessary */ if (have_state && have_state->opcode != O_CHECK_STATE) { - fill_cmd(dst, O_PROBE_STATE, 0, 0); + fill_cmd(dst, O_PROBE_STATE, 0, have_state->arg1); dst = next_cmd(dst, &rblen); } @@ -5134,6 +5211,7 @@ static struct _s_x intcmds[] = { static struct _s_x otypes[] = { { "EACTION", IPFW_TLV_EACTION }, + { "DYNSTATE", IPFW_TLV_STATE_NAME }, { NULL, 0 } }; diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index a3427b19804d..6b07d1b3f177 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -688,7 +688,8 @@ struct _ipfw_dyn_rule { /* to generate keepalives) */ u_int16_t dyn_type; /* rule type */ u_int16_t count; /* refcount */ -}; + u_int16_t kidx; /* index of named object */ +} __packed __aligned(8); /* * Definitions for IP option names. @@ -790,6 +791,7 @@ typedef struct _ipfw_obj_tlv { #define IPFW_TLV_RANGE 9 #define IPFW_TLV_EACTION 10 #define IPFW_TLV_COUNTERS 11 +#define IPFW_TLV_STATE_NAME 14 #define IPFW_TLV_EACTION_BASE 1000 #define IPFW_TLV_EACTION_NAME(arg) (IPFW_TLV_EACTION_BASE + (arg)) diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c index 60a04d119edc..6b4a34b20b46 100644 --- a/sys/netpfil/ipfw/ip_fw2.c +++ b/sys/netpfil/ipfw/ip_fw2.c @@ -971,6 +971,7 @@ ipfw_chk(struct ip_fw_args *args) * MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL) */ int dyn_dir = MATCH_UNKNOWN; + uint16_t dyn_name = 0; ipfw_dyn_rule *q = NULL; struct ip_fw_chain *chain = &V_layer3_chain; @@ -2113,17 +2114,35 @@ do { \ /* * dynamic rules are checked at the first * keep-state or check-state occurrence, - * with the result being stored in dyn_dir. + * with the result being stored in dyn_dir + * and dyn_name. * The compiler introduces a PROBE_STATE * instruction for us when we have a * KEEP_STATE (because PROBE_STATE needs * to be run first). + * + * (dyn_dir == MATCH_UNKNOWN) means this is + * first lookup for such f_id. Do lookup. + * + * (dyn_dir != MATCH_UNKNOWN && + * dyn_name != 0 && dyn_name != cmd->arg1) + * means previous lookup didn't find dynamic + * rule for specific state name and current + * lookup will search rule with another state + * name. Redo lookup. + * + * (dyn_dir != MATCH_UNKNOWN && dyn_name == 0) + * means previous lookup was for `any' name + * and it didn't find rule. No need to do + * lookup again. */ - if (dyn_dir == MATCH_UNKNOWN && + if ((dyn_dir == MATCH_UNKNOWN || + (dyn_name != 0 && + dyn_name != cmd->arg1)) && (q = ipfw_lookup_dyn_rule(&args->f_id, &dyn_dir, proto == IPPROTO_TCP ? - TCP(ulp) : NULL)) - != NULL) { + TCP(ulp): NULL, + (dyn_name = cmd->arg1))) != NULL) { /* * Found dynamic entry, update stats * and jump to the 'action' part of diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c index 53df5e20adbb..af6cc085b9ca 100644 --- a/sys/netpfil/ipfw/ip_fw_dynamic.c +++ b/sys/netpfil/ipfw/ip_fw_dynamic.c @@ -282,6 +282,200 @@ hash_packet(struct ipfw_flow_id *id, int buckets) return i; } +#if 0 +#define DYN_DEBUG(fmt, ...) do { \ + printf("%s: " fmt "\n", __func__, __VA_ARGS__); \ +} while (0) +#else +#define DYN_DEBUG(fmt, ...) +#endif + +static char *default_state_name = "default"; +struct dyn_state_obj { + struct named_object no; + char name[64]; +}; + +#define DYN_STATE_OBJ(ch, cmd) \ + ((struct dyn_state_obj *)SRV_OBJECT(ch, (cmd)->arg1)) +/* + * Classifier callback. + * Return 0 if opcode contains object that should be referenced + * or rewritten. + */ +static int +dyn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) +{ + + DYN_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); + /* Don't rewrite "check-state any" */ + if (cmd->arg1 == 0 && + cmd->opcode == O_CHECK_STATE) + return (1); + + *puidx = cmd->arg1; + *ptype = 0; + return (0); +} + +static void +dyn_update(ipfw_insn *cmd, uint16_t idx) +{ + + cmd->arg1 = idx; + DYN_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); +} + +static int +dyn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, + struct named_object **pno) +{ + ipfw_obj_ntlv *ntlv; + const char *name; + + DYN_DEBUG("uidx %d", ti->uidx); + if (ti->uidx != 0) { + if (ti->tlvs == NULL) + return (EINVAL); + /* Search ntlv in the buffer provided by user */ + ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, + IPFW_TLV_STATE_NAME); + if (ntlv == NULL) + return (EINVAL); + name = ntlv->name; + } else + name = default_state_name; + /* + * Search named object with corresponding name. + * Since states objects are global - ignore the set value + * and use zero instead. + */ + *pno = ipfw_objhash_lookup_name_type(CHAIN_TO_SRV(ch), 0, + IPFW_TLV_STATE_NAME, name); + /* + * We always return success here. + * The caller will check *pno and mark object as unresolved, + * then it will automatically create "default" object. + */ + return (0); +} + +static struct named_object * +dyn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) +{ + + DYN_DEBUG("kidx %d", idx); + return (ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), idx)); +} + +static int +dyn_create(struct ip_fw_chain *ch, struct tid_info *ti, + uint16_t *pkidx) +{ + struct namedobj_instance *ni; + struct dyn_state_obj *obj; + struct named_object *no; + ipfw_obj_ntlv *ntlv; + char *name; + + DYN_DEBUG("uidx %d", ti->uidx); + if (ti->uidx != 0) { + if (ti->tlvs == NULL) + return (EINVAL); + ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, + IPFW_TLV_STATE_NAME); + if (ntlv == NULL) + return (EINVAL); + name = ntlv->name; + } else + name = default_state_name; + + ni = CHAIN_TO_SRV(ch); + obj = malloc(sizeof(*obj), M_IPFW, M_WAITOK | M_ZERO); + obj->no.name = obj->name; + obj->no.etlv = IPFW_TLV_STATE_NAME; + strlcpy(obj->name, name, sizeof(obj->name)); + + IPFW_UH_WLOCK(ch); + no = ipfw_objhash_lookup_name_type(ni, 0, + IPFW_TLV_STATE_NAME, name); + if (no != NULL) { + /* + * Object is already created. + * Just return its kidx and bump refcount. + */ + *pkidx = no->kidx; + no->refcnt++; + IPFW_UH_WUNLOCK(ch); + free(obj, M_IPFW); + DYN_DEBUG("\tfound kidx %d", *pkidx); + return (0); + } + if (ipfw_objhash_alloc_idx(ni, &obj->no.kidx) != 0) { + DYN_DEBUG("\talloc_idx failed for %s", name); + IPFW_UH_WUNLOCK(ch); + free(obj, M_IPFW); + return (ENOSPC); + } + ipfw_objhash_add(ni, &obj->no); + IPFW_WLOCK(ch); + SRV_OBJECT(ch, obj->no.kidx) = obj; + IPFW_WUNLOCK(ch); + obj->no.refcnt++; + *pkidx = obj->no.kidx; + IPFW_UH_WUNLOCK(ch); + DYN_DEBUG("\tcreated kidx %d", *pkidx); + return (0); +} + +static void +dyn_destroy(struct ip_fw_chain *ch, struct named_object *no) +{ + struct dyn_state_obj *obj; + + IPFW_UH_WLOCK_ASSERT(ch); + + KASSERT(no->refcnt == 1, + ("Destroying object '%s' (type %u, idx %u) with refcnt %u", + no->name, no->etlv, no->kidx, no->refcnt)); + + DYN_DEBUG("kidx %d", no->kidx); + IPFW_WLOCK(ch); + obj = SRV_OBJECT(ch, no->kidx); + SRV_OBJECT(ch, no->kidx) = NULL; + IPFW_WUNLOCK(ch); + ipfw_objhash_del(CHAIN_TO_SRV(ch), no); + ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), no->kidx); + + free(obj, M_IPFW); +} + +static struct opcode_obj_rewrite dyn_opcodes[] = { + { + O_KEEP_STATE, IPFW_TLV_STATE_NAME, + dyn_classify, dyn_update, + dyn_findbyname, dyn_findbykidx, + dyn_create, dyn_destroy + }, + { + O_CHECK_STATE, IPFW_TLV_STATE_NAME, + dyn_classify, dyn_update, + dyn_findbyname, dyn_findbykidx, + dyn_create, dyn_destroy + }, + { + O_PROBE_STATE, IPFW_TLV_STATE_NAME, + dyn_classify, dyn_update, + dyn_findbyname, dyn_findbykidx, + dyn_create, dyn_destroy + }, + { + O_LIMIT, IPFW_TLV_STATE_NAME, + dyn_classify, dyn_update, + dyn_findbyname, dyn_findbykidx, + dyn_create, dyn_destroy + }, +}; /** * Print customizable flow id description via log(9) facility. */ @@ -403,7 +597,7 @@ dyn_update_proto_state(ipfw_dyn_rule *q, const struct ipfw_flow_id *id, */ static ipfw_dyn_rule * lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction, - struct tcphdr *tcp) + struct tcphdr *tcp, uint16_t kidx) { /* * Stateful ipfw extensions. @@ -416,10 +610,13 @@ lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction, dir = MATCH_NONE; for (prev = NULL, q = V_ipfw_dyn_v[i].head; q; prev = q, q = q->next) { - if (q->dyn_type == O_LIMIT_PARENT && q->count) + if (q->dyn_type == O_LIMIT_PARENT) continue; - if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT) + if (pkt->proto != q->id.proto) + continue; + + if (kidx != 0 && kidx != q->kidx) continue; if (IS_IP6_FLOW_ID(pkt)) { @@ -473,7 +670,7 @@ lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction, ipfw_dyn_rule * ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, - struct tcphdr *tcp) + struct tcphdr *tcp, uint16_t kidx) { ipfw_dyn_rule *q; int i; @@ -481,7 +678,7 @@ ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, i = hash_packet(pkt, V_curr_dyn_buckets); IPFW_BUCK_LOCK(i); - q = lookup_dyn_rule_locked(pkt, i, match_direction, tcp); + q = lookup_dyn_rule_locked(pkt, i, match_direction, tcp, kidx); if (q == NULL) IPFW_BUCK_UNLOCK(i); /* NB: return table locked when q is not NULL */ @@ -591,7 +788,8 @@ resize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) * - "parent" rules for the above (O_LIMIT_PARENT). */ static ipfw_dyn_rule * -add_dyn_rule(struct ipfw_flow_id *id, int i, u_int8_t dyn_type, struct ip_fw *rule) +add_dyn_rule(struct ipfw_flow_id *id, int i, uint8_t dyn_type, + struct ip_fw *rule, uint16_t kidx) { ipfw_dyn_rule *r; @@ -627,7 +825,7 @@ add_dyn_rule(struct ipfw_flow_id *id, int i, u_int8_t dyn_type, struct ip_fw *ru r->dyn_type = dyn_type; IPFW_ZERO_DYN_COUNTER(r); r->count = 0; - + r->kidx = kidx; r->bucket = i; r->next = V_ipfw_dyn_v[i].head; V_ipfw_dyn_v[i].head = r; @@ -640,7 +838,8 @@ add_dyn_rule(struct ipfw_flow_id *id, int i, u_int8_t dyn_type, struct ip_fw *ru * If the lookup fails, then install one. */ static ipfw_dyn_rule * -lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule) +lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule, + uint16_t kidx) { ipfw_dyn_rule *q; int i, is_v6; @@ -651,7 +850,8 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule) IPFW_BUCK_LOCK(i); for (q = V_ipfw_dyn_v[i].head ; q != NULL ; q=q->next) if (q->dyn_type == O_LIMIT_PARENT && - rule== q->rule && + kidx == q->kidx && + rule == q->rule && pkt->proto == q->id.proto && pkt->src_port == q->id.src_port && pkt->dst_port == q->id.dst_port && @@ -673,7 +873,7 @@ lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule) } /* Add virtual limiting rule */ - return add_dyn_rule(pkt, i, O_LIMIT_PARENT, rule); + return add_dyn_rule(pkt, i, O_LIMIT_PARENT, rule, kidx); } /** @@ -689,13 +889,14 @@ ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, ipfw_dyn_rule *q; int i; - DEB(print_dyn_rule(&args->f_id, cmd->o.opcode, "install_state", "");) - + DEB(print_dyn_rule(&args->f_id, cmd->o.opcode, "install_state", + (cmd->o.arg1 == 0 ? "": DYN_STATE_OBJ(chain, &cmd->o)->name));) + i = hash_packet(&args->f_id, V_curr_dyn_buckets); IPFW_BUCK_LOCK(i); - q = lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL); + q = lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL, cmd->o.arg1); if (q != NULL) { /* should never occur */ DEB( if (last_log != time_uptime) { @@ -716,7 +917,8 @@ ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, switch (cmd->o.opcode) { case O_KEEP_STATE: /* bidir rule */ - q = add_dyn_rule(&args->f_id, i, O_KEEP_STATE, rule); + q = add_dyn_rule(&args->f_id, i, O_KEEP_STATE, rule, + cmd->o.arg1); break; case O_LIMIT: { /* limit number of sessions */ @@ -767,7 +969,8 @@ ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, */ IPFW_BUCK_UNLOCK(i); - if ((parent = lookup_dyn_parent(&id, &pindex, rule)) == NULL) { + parent = lookup_dyn_parent(&id, &pindex, rule, cmd->o.arg1); + if (parent == NULL) { printf("ipfw: %s: add parent failed\n", __func__); IPFW_BUCK_UNLOCK(pindex); return (1); @@ -795,7 +998,7 @@ ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, IPFW_BUCK_LOCK(i); q = add_dyn_rule(&args->f_id, i, O_LIMIT, - (struct ip_fw *)parent); + (struct ip_fw *)parent, cmd->o.arg1); if (q == NULL) { /* Decrement index and notify caller */ IPFW_BUCK_UNLOCK(i); @@ -1412,6 +1615,7 @@ ipfw_dyn_init(struct ip_fw_chain *chain) * being added to chain. */ resize_dynamic_table(chain, V_curr_dyn_buckets); + IPFW_ADD_OBJ_REWRITER(IS_DEFAULT_VNET(curvnet), dyn_opcodes); } void @@ -1423,6 +1627,7 @@ ipfw_dyn_uninit(int pass) callout_drain(&V_ipfw_timeout); return; } + IPFW_DEL_OBJ_REWRITER(IS_DEFAULT_VNET(curvnet), dyn_opcodes); if (V_ipfw_dyn_v != NULL) { /* diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h index e90781acf87a..1c3cdd28ed7a 100644 --- a/sys/netpfil/ipfw/ip_fw_private.h +++ b/sys/netpfil/ipfw/ip_fw_private.h @@ -189,7 +189,7 @@ struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *, int ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg); ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, - int *match_direction, struct tcphdr *tcp); + int *match_direction, struct tcphdr *tcp, uint16_t kidx); void ipfw_remove_dyn_children(struct ip_fw *rule); void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep); int ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd); diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c index d186ba5cd97d..4af0fe4bb94b 100644 --- a/sys/netpfil/ipfw/ip_fw_sockopt.c +++ b/sys/netpfil/ipfw/ip_fw_sockopt.c @@ -1679,6 +1679,10 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) switch (cmd->opcode) { case O_PROBE_STATE: case O_KEEP_STATE: + if (cmdlen != F_INSN_SIZE(ipfw_insn)) + goto bad_size; + ci->object_opcodes++; + break; case O_PROTO: case O_IP_SRC_ME: case O_IP_DST_ME: @@ -1776,6 +1780,7 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) case O_LIMIT: if (cmdlen != F_INSN_SIZE(ipfw_insn_limit)) goto bad_size; + ci->object_opcodes++; break; case O_LOG: @@ -1906,8 +1911,10 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci) if (cmdlen != F_INSN_SIZE(ipfw_insn_nat)) goto bad_size; goto check_action; - case O_FORWARD_MAC: /* XXX not implemented yet */ case O_CHECK_STATE: + ci->object_opcodes++; + /* FALLTHROUGH */ + case O_FORWARD_MAC: /* XXX not implemented yet */ case O_COUNT: case O_ACCEPT: case O_DENY: