aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw.846
-rw-r--r--sbin/ipfw/ipfw2.c96
-rw-r--r--sys/netinet/ip_fw.h4
-rw-r--r--sys/netpfil/ipfw/ip_fw2.c27
-rw-r--r--sys/netpfil/ipfw/ip_fw_dynamic.c237
-rw-r--r--sys/netpfil/ipfw/ip_fw_private.h2
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c9
7 files changed, 378 insertions, 43 deletions
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 @@ read_options:
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 @@ read_options:
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 @@ done:
* 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)
continue;
- if (pkt->proto != q->id.proto || q->dyn_type == O_LIMIT_PARENT)
+ if (kidx != 0 && kidx != q->kidx)
continue;
if (IS_IP6_FLOW_ID(pkt)) {
@@ -473,7 +670,7 @@ done:
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: