summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw.846
-rw-r--r--sbin/ipfw/ipfw2.c104
-rw-r--r--sbin/ipfw/ipfw2.h2
-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
8 files changed, 383 insertions, 48 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index ce8823f..b3eba15 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 26, 2016
+.Dd March 15, 2017
.Dt IPFW 8
.Os
.Sh NAME
@@ -737,7 +737,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
@@ -752,6 +752,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.
@@ -1575,7 +1586,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.
@@ -1583,11 +1594,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
@@ -1595,8 +1615,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
@@ -2189,6 +2207,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
@@ -2197,23 +2221,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 951ebd3..faf7b97 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -588,7 +588,7 @@ do_cmd(int optname, void *optval, uintptr_t optlen)
* Returns 0 on success or errno otherwise.
*/
int
-do_set3(int optname, ip_fw3_opheader *op3, uintptr_t optlen)
+do_set3(int optname, ip_fw3_opheader *op3, size_t optlen)
{
if (co.test_only)
@@ -618,6 +618,7 @@ int
do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
{
int error;
+ socklen_t len;
if (co.test_only)
return (0);
@@ -629,8 +630,9 @@ do_get3(int optname, ip_fw3_opheader *op3, size_t *optlen)
op3->opcode = optname;
- error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3,
- (socklen_t *)optlen);
+ len = *optlen;
+ error = getsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, &len);
+ *optlen = len;
return (error);
}
@@ -1403,6 +1405,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 */
@@ -1472,6 +1475,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;
@@ -1589,8 +1598,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
@@ -1611,8 +1618,6 @@ show_static_rule(struct cmdline_opts *co, struct format_opts *fo,
}
case O_EXTERNAL_INSTANCE: {
- const char *ename;
-
if (has_eaction == NULL)
break;
/*
@@ -2080,6 +2085,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: {
@@ -2096,6 +2104,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;
}
@@ -2194,7 +2205,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
@@ -2835,6 +2849,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)
{
@@ -3705,6 +3731,25 @@ 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 ||
+ match_token(rule_options, *av) == TOK_COMMENT) {
+ action->arg1 = pack_object(tstate,
+ default_state_name, IPFW_TLV_STATE_NAME);
+ break;
+ }
+ if (*av[0] == ':') {
+ if (strcmp(*av + 1, "any") == 0)
+ action->arg1 = 0;
+ else if (state_check_name(*av + 1) == 0)
+ action->arg1 = pack_object(tstate, *av + 1,
+ IPFW_TLV_STATE_NAME);
+ else
+ errx(EX_DATAERR, "Invalid state name %s",
+ *av);
+ av++;
+ break;
+ }
+ errx(EX_DATAERR, "Invalid state name %s", *av);
break;
case TOK_ACCEPT:
@@ -4093,8 +4138,17 @@ chkarg:
cmd = next_cmd(cmd, &cblen);
}
- if (have_state) /* must be a check-state, we are done */
+ if (have_state) { /* must be a check-state, we are done */
+ if (*av != NULL &&
+ match_token(rule_options, *av) == TOK_COMMENT) {
+ /* check-state has a comment */
+ av++;
+ fill_comment(cmd, av, cblen);
+ cmd = next_cmd(cmd, &cblen);
+ av[0] = NULL;
+ }
goto done;
+ }
#define OR_START(target) \
if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \
@@ -4529,16 +4583,29 @@ 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 && *av[0] == ':') {
+ if (state_check_name(*av + 1) != 0)
+ errx(EX_DATAERR,
+ "Invalid state name %s", *av);
+ uidx = pack_object(tstate, *av + 1,
+ IPFW_TLV_STATE_NAME);
+ av++;
+ } else
+ uidx = pack_object(tstate, default_state_name,
+ IPFW_TLV_STATE_NAME);
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;
@@ -4569,8 +4636,18 @@ read_options:
GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX,
TOK_LIMIT, rule_options);
-
av++;
+
+ if (*av != NULL && *av[0] == ':') {
+ if (state_check_name(*av + 1) != 0)
+ errx(EX_DATAERR,
+ "Invalid state name %s", *av);
+ cmd->arg1 = pack_object(tstate, *av + 1,
+ IPFW_TLV_STATE_NAME);
+ av++;
+ } else
+ cmd->arg1 = pack_object(tstate,
+ default_state_name, IPFW_TLV_STATE_NAME);
break;
}
@@ -4777,7 +4854,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);
}
@@ -5162,6 +5239,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/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index a093505..3e90446 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -299,7 +299,7 @@ void print_flags_buffer(char *buf, size_t sz, struct _s_x *list, uint32_t set);
struct _ip_fw3_opheader;
int do_cmd(int optname, void *optval, uintptr_t optlen);
-int do_set3(int optname, struct _ip_fw3_opheader *op3, uintptr_t optlen);
+int do_set3(int optname, struct _ip_fw3_opheader *op3, size_t optlen);
int do_get3(int optname, struct _ip_fw3_opheader *op3, size_t *optlen);
struct in6_addr;
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index caa3f96..843c0cb 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -685,7 +685,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.
@@ -787,6 +788,7 @@ typedef struct _ipfw_obj_tlv {
#define IPFW_TLV_TBLENT_LIST 8
#define IPFW_TLV_RANGE 9
#define IPFW_TLV_EACTION 10
+#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 fdebef9..1c45b84 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;
@@ -2171,17 +2172,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 7dd72e4..95b3a72 100644
--- a/sys/netpfil/ipfw/ip_fw_dynamic.c
+++ b/sys/netpfil/ipfw/ip_fw_dynamic.c
@@ -281,6 +281,200 @@ hash_packet(struct ipfw_flow_id *id, int buckets)
return (i & (buckets - 1));
}
+#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.
*/
@@ -402,7 +596,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.
@@ -415,10 +609,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)) {
@@ -472,7 +669,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;
@@ -480,7 +677,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 */
@@ -590,7 +787,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;
@@ -626,7 +824,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;
@@ -639,7 +837,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;
@@ -650,7 +849,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 &&
@@ -672,7 +872,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);
}
/**
@@ -688,13 +888,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) {
@@ -715,7 +916,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 */
@@ -766,7 +968,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);
@@ -794,7 +997,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);
@@ -1411,6 +1614,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
@@ -1422,6 +1626,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 810b21f..bbc0114 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 64dba6f..77e9779 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -1693,6 +1693,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:
@@ -1790,6 +1794,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:
@@ -1934,8 +1939,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:
OpenPOWER on IntegriCloud