Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F135906214
D20870.id81102.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
84 KB
Referenced Files
None
Subscribers
None
D20870.id81102.diff
View Options
Index: sys/netpfil/ipfw/ip_fw_dynamic.c
===================================================================
--- sys/netpfil/ipfw/ip_fw_dynamic.c
+++ sys/netpfil/ipfw/ip_fw_dynamic.c
@@ -116,108 +116,256 @@
/* By default use jenkins hash function */
#define IPFIREWALL_JENKINSHASH
+/*
+ * Check-and-cast macro
+ */
+#ifdef INVARIANTS
+#define DYN_CAST_STATE_WITH_ASSERT(h, t, c) \
+ (__predict_false(!(c)) ? \
+ (panic("Wrong dynamic state %d:%d, expected " #t " at %s:%d", \
+ (h)->type, (h)->af, __FILE__, __LINE__), (t *)NULL) \
+ : \
+ ((t *)(h)) \
+ )
+#else
+#define DYN_CAST_STATE_WITH_ASSERT(h, t, c) ((t *)(h))
+#endif
+
+/*
+ * This is "header" of any dynamic state, each dynamic state
+ * struct must start from this one to have same basic layout.
+ *
+ * After this sturcture there must be address part of state,
+ * now it could be pair of IPv4 addresses or pair of IPv6 addresses
+ * and zone id.
+ *
+ * This struct must be 28 bytes or less, because it could be followed
+ * by 2 struct in6_addr and one uint32_t zoneid and it must fit
+ * into one cache line (64 bytes), so we have left
+ * 64 - 2*sizeof(struct in6_addr) - sizeof(uint32_t) = 28.
+ *
+ */
+#define DYN_STATE_TYPE_KEEP 1 /* O_KEEP_STATE */
+#define DYN_STATE_TYPE_LIMIT 2 /* O_LIMIT */
+#define DYN_STATE_TYPE_PARENT 3 /* O_LIMIT_PARENT */
+
+typedef struct _dyn_state_hdr {
+ uint8_t type; /* State type */
+ uint8_t af; /* Address family, 4 or 6 */
+ uint8_t proto; /* UL Protocol */
+ uint8_t _pad;
+ uint16_t kidx; /* named object index */
+ uint16_t sport;
+ uint16_t dport; /* ULP source and destination ports */
+
+ uint16_t rulenum; /* parent rule number */
+ uint32_t ruleid; /* parent rule id */
+ /* 16 bytes */
+
+ /* State is never placed into two lists simultaneously */
+ union {
+ CK_SLIST_ENTRY(_dyn_state_hdr) entry;
+ SLIST_ENTRY(_dyn_state_hdr) expired;
+ };
+ /* 20 or 24 bytes */
+} dyn_state_hdr;
+SLIST_HEAD(dyn_any_state_slist, _dyn_state_hdr);
+CK_SLIST_HEAD(dyn_any_state_ck_slist, _dyn_state_hdr);
+
+/*
+ * Addresses for IPv4 states
+ */
+typedef struct _dyn_state_ipv4_addr {
+ in_addr_t src, dst; /* IPv4 source and destination */
+ /* 8 bytes */
+} dyn_state_ipv4_addr;
+
+
+/*
+ * This is common, but less used fields of each dynamic state.
+ * This struct must go after af-specific addresses.
+ *
+ */
+typedef struct _dyn_state_common {
+ uint32_t hashval; /* hash value used for hash resize */
+ /* 4 bytes */
+} dyn_state_common;
+
+
+/*
+ * Structure of all data for simple rule and some macro for it.
+ */
+struct _dyn_ipv4_parent_state;
+
#define DYN_COUNTER_INC(d, dir, pktlen) do { \
(d)->pcnt_ ## dir++; \
(d)->bcnt_ ## dir += pktlen; \
} while (0)
-#define DYN_REFERENCED 0x01
/*
* DYN_REFERENCED flag is used to show that state keeps reference to named
* object, and this reference should be released when state becomes expired.
*/
+#define DYN_REFERENCED 0x01
+
+#define ASSERT_PARENT_STATE(s) KASSERT( \
+ ((s)->d.lp->type == DYN_STATE_TYPE_PARENT) && \
+ ((s)->d.lp->af == (s)->h.af), \
+ ("Expecting parent IPv%d state, got %d:%d at %s:%d", \
+ (s)->h.af, (s)->d.lp->type, (s)->d.lp->af, \
+ __FILE__, __LINE__) \
+ )
-struct dyn_data {
- void *parent; /* pointer to parent rule */
+typedef struct _dyn_state_data {
+ union {
+ struct ip_fw *sp; /* pointer to IPFW parent rule */
+ dyn_state_hdr *lp; /* pointer to O_LIMIT_PARENT dynamic state */
+ };
uint32_t chain_id; /* cached ruleset id */
uint32_t f_pos; /* cached rule index */
- uint32_t hashval; /* hash value used for hash resize */
+ uint32_t expire; /* expire time */
+ /* expire is not placed into commom part */
+ /* because it allow to pass one less */
+ /* pointer to state processing function */
+
uint16_t fibnum; /* fib used to send keepalives */
- uint8_t _pad[3];
+ uint8_t _pad;
uint8_t flags; /* internal flags */
- uint16_t rulenum; /* parent rule number */
- uint32_t ruleid; /* parent rule id */
uint32_t state; /* TCP session state and flags */
uint32_t ack_fwd; /* most recent ACKs in forward */
uint32_t ack_rev; /* and reverse direction (used */
/* to generate keepalives) */
- uint32_t sync; /* synchronization time */
- uint32_t expire; /* expire time */
uint64_t pcnt_fwd; /* bytes counter in forward */
uint64_t bcnt_fwd; /* packets counter in forward */
uint64_t pcnt_rev; /* bytes counter in reverse */
uint64_t bcnt_rev; /* packets counter in reverse */
-};
+ /* 64 or 68 bytes */
+} dyn_state_data;
+
+/*
+ * This is data for "parent" states and some macros for it.
+ */
#define DPARENT_COUNT_DEC(p) do { \
- MPASS(p->count > 0); \
+ MPASS((p)->count > 0); \
ck_pr_dec_32(&(p)->count); \
} while (0)
#define DPARENT_COUNT_INC(p) ck_pr_inc_32(&(p)->count)
#define DPARENT_COUNT(p) ck_pr_load_32(&(p)->count)
-struct dyn_parent {
- void *parent; /* pointer to parent rule */
- uint32_t count; /* number of linked states */
- uint8_t _pad[2];
- uint16_t rulenum; /* parent rule number */
- uint32_t ruleid; /* parent rule id */
- uint32_t hashval; /* hash value used for hash resize */
+typedef struct _dyn_parent_state_data {
+ struct ip_fw *parent; /* pointer to IPFW parent rule */
uint32_t expire; /* expire time */
-};
+ /* expire is not placed into commom part */
+ /* because it allow to pass one less */
+ /* pointer to state processing function */
+ uint32_t count; /* number of linked states */
+ /* 12 or 16 bytes */
+} dyn_parent_state_data;
-struct dyn_ipv4_state {
- uint8_t type; /* State type */
- uint8_t proto; /* UL Protocol */
- uint16_t kidx; /* named object index */
- uint16_t sport, dport; /* ULP source and destination ports */
- in_addr_t src, dst; /* IPv4 source and destination */
- union {
- struct dyn_data *data;
- struct dyn_parent *limit;
- };
- CK_SLIST_ENTRY(dyn_ipv4_state) entry;
- SLIST_ENTRY(dyn_ipv4_state) expired;
-};
-CK_SLIST_HEAD(dyn_ipv4ck_slist, dyn_ipv4_state);
-VNET_DEFINE_STATIC(struct dyn_ipv4ck_slist *, dyn_ipv4);
-VNET_DEFINE_STATIC(struct dyn_ipv4ck_slist *, dyn_ipv4_parent);
+/*
+ * Structure for IPv4 state and some macro for it
+ *
+ * It must have
+ * ((h.type == DYN_STATE_TYPE_KEEP || h.type == DYN_STATE_TYPE_LIMIT) &&
+ * h.af == 4)
+ */
+#define DYN_CAST_IPV4_STATE(s) DYN_CAST_STATE_WITH_ASSERT(s, dyn_ipv4_state, \
+ (((s)->type == DYN_STATE_TYPE_KEEP || (s)->type == DYN_STATE_TYPE_LIMIT)\
+ && (s)->af == 4))
+typedef struct _dyn_ipv4_state {
+ dyn_state_hdr h; /* Header */
+ dyn_state_ipv4_addr a; /* Addresses */
+ dyn_state_common c; /* Additional common data */
+ dyn_state_data d; /* Data */
+} dyn_ipv4_state;
+
+VNET_DEFINE_STATIC(struct dyn_any_state_ck_slist *, dyn_ipv4);
+#define V_dyn_ipv4 VNET(dyn_ipv4)
+
+
+/*
+ * Structure for "parent" IPv4 state and some macro for it
+ *
+ * It must have
+ * (h.type == DYN_STATE_TYPE_PARENT && h.af == 4)
+ */
+#define DYN_CAST_IPV4_PARENT_STATE(s) DYN_CAST_STATE_WITH_ASSERT(s, \
+ dyn_ipv4_parent_state, ((s)->type == DYN_STATE_TYPE_PARENT && \
+ (s)->af == 4))
+
+typedef struct _dyn_ipv4_parent_state {
+ dyn_state_hdr h; /* Header */
+ dyn_state_ipv4_addr a; /* Addresses */
+ dyn_state_common c; /* Additional common data */
+ dyn_parent_state_data d; /* Data */
+} dyn_ipv4_parent_state;
+
+VNET_DEFINE_STATIC(struct dyn_any_state_ck_slist *, dyn_ipv4_parent);
+#define V_dyn_ipv4_parent VNET(dyn_ipv4_parent)
-SLIST_HEAD(dyn_ipv4_slist, dyn_ipv4_state);
-VNET_DEFINE_STATIC(struct dyn_ipv4_slist, dyn_expired_ipv4);
-#define V_dyn_ipv4 VNET(dyn_ipv4)
-#define V_dyn_ipv4_parent VNET(dyn_ipv4_parent)
-#define V_dyn_expired_ipv4 VNET(dyn_expired_ipv4)
#ifdef INET6
-struct dyn_ipv6_state {
- uint8_t type; /* State type */
- uint8_t proto; /* UL Protocol */
- uint16_t kidx; /* named object index */
- uint16_t sport, dport; /* ULP source and destination ports */
+/*
+ * Addresses for IPv6 states
+ */
+typedef struct _dyn_state_ipv6_addr {
struct in6_addr src, dst; /* IPv6 source and destination */
uint32_t zoneid; /* IPv6 scope zone id */
- union {
- struct dyn_data *data;
- struct dyn_parent *limit;
- };
- CK_SLIST_ENTRY(dyn_ipv6_state) entry;
- SLIST_ENTRY(dyn_ipv6_state) expired;
-};
-CK_SLIST_HEAD(dyn_ipv6ck_slist, dyn_ipv6_state);
-VNET_DEFINE_STATIC(struct dyn_ipv6ck_slist *, dyn_ipv6);
-VNET_DEFINE_STATIC(struct dyn_ipv6ck_slist *, dyn_ipv6_parent);
-
-SLIST_HEAD(dyn_ipv6_slist, dyn_ipv6_state);
-VNET_DEFINE_STATIC(struct dyn_ipv6_slist, dyn_expired_ipv6);
-#define V_dyn_ipv6 VNET(dyn_ipv6)
-#define V_dyn_ipv6_parent VNET(dyn_ipv6_parent)
-#define V_dyn_expired_ipv6 VNET(dyn_expired_ipv6)
+ /* 36 bytes */
+} dyn_state_ipv6_addr;
+
+/*
+ * Structure for IPv6 state and some macro for it
+ *
+ * It must have
+ * ((h.type == DYN_STATE_TYPE_KEEP || h.type == DYN_STATE_TYPE_LIMIT) &&
+ * h.af == 6)
+ */
+#define DYN_CAST_IPV6_STATE(s) DYN_CAST_STATE_WITH_ASSERT(s, dyn_ipv6_state, \
+ (((s)->type == DYN_STATE_TYPE_KEEP || (s)->type == DYN_STATE_TYPE_LIMIT)\
+ && (s)->af == 6))
+typedef struct _dyn_ipv6_state {
+ dyn_state_hdr h; /* Header */
+ dyn_state_ipv6_addr a; /* Addresses */
+ dyn_state_common c; /* Additional common data */
+ dyn_state_data d; /* Data */
+} dyn_ipv6_state;
+
+VNET_DEFINE_STATIC(struct dyn_any_state_ck_slist *, dyn_ipv6);
+#define V_dyn_ipv6 VNET(dyn_ipv6)
+
+
+/*
+ * Structure for "parent" IPv6 state and some macro for it
+ *
+ * It must have
+ * (h.type == DYN_STATE_TYPE_PARENT && h.af == 6)
+ */
+#define DYN_CAST_IPV6_PARENT_STATE(s) DYN_CAST_STATE_WITH_ASSERT(s, \
+ dyn_ipv6_parent_state, ((s)->type == DYN_STATE_TYPE_PARENT && \
+ (s)->af == 6))
+typedef struct _dyn_ipv6_parent_state {
+ dyn_state_hdr h; /* Header */
+ dyn_state_ipv6_addr a; /* Addresses */
+ dyn_state_common c; /* Additional common data */
+ dyn_parent_state_data d; /* Data */
+} dyn_ipv6_parent_state;
+
+VNET_DEFINE_STATIC(struct dyn_any_state_ck_slist *, dyn_ipv6_parent);
+#define V_dyn_ipv6_parent VNET(dyn_ipv6_parent)
#endif /* INET6 */
+
+/*
+ * This list contains all expired rules, of all types
+ */
+VNET_DEFINE_STATIC(struct dyn_any_state_slist, dyn_expired);
+#define V_dyn_expired VNET(dyn_expired)
+
/*
* Per-CPU pointer indicates that specified state is currently in use
* and must not be reclaimed by expiration callout.
@@ -318,16 +466,16 @@
VNET_DEFINE_STATIC(uint32_t, dyn_keep_states);
#define V_dyn_keep_states VNET(dyn_keep_states)
-VNET_DEFINE_STATIC(uma_zone_t, dyn_data_zone);
-VNET_DEFINE_STATIC(uma_zone_t, dyn_parent_zone);
+VNET_DEFINE_STATIC(uma_zone_t, dyn_ipv4_parent_zone);
VNET_DEFINE_STATIC(uma_zone_t, dyn_ipv4_zone);
+#define V_dyn_ipv4_parent_zone VNET(dyn_ipv4_parent_zone)
+#define V_dyn_ipv4_zone VNET(dyn_ipv4_zone)
#ifdef INET6
+VNET_DEFINE_STATIC(uma_zone_t, dyn_ipv6_parent_zone);
VNET_DEFINE_STATIC(uma_zone_t, dyn_ipv6_zone);
+#define V_dyn_ipv6_parent_zone VNET(dyn_ipv6_parent_zone)
#define V_dyn_ipv6_zone VNET(dyn_ipv6_zone)
#endif /* INET6 */
-#define V_dyn_data_zone VNET(dyn_data_zone)
-#define V_dyn_parent_zone VNET(dyn_parent_zone)
-#define V_dyn_ipv4_zone VNET(dyn_ipv4_zone)
/*
* Timeouts for various events in handing dynamic rules.
@@ -398,7 +546,10 @@
return (error);
V_dyn_max = nstates;
- uma_zone_set_max(V_dyn_data_zone, V_dyn_max);
+ uma_zone_set_max(V_dyn_ipv4_zone, V_dyn_max);
+#ifdef INET6
+ uma_zone_set_max(V_dyn_ipv6_zone, V_dyn_max);
+#endif
return (0);
}
@@ -415,7 +566,10 @@
return (error);
V_dyn_parent_max = nstates;
- uma_zone_set_max(V_dyn_parent_zone, V_dyn_parent_max);
+ uma_zone_set_max(V_dyn_ipv4_parent_zone, V_dyn_parent_max);
+#ifdef INET6
+ uma_zone_set_max(V_dyn_ipv6_parent_zone, V_dyn_parent_max);
+#endif
return (0);
}
@@ -497,76 +651,81 @@
#define DYN_DEBUG(fmt, ...)
#endif /* !IPFIREWALL_DYNDEBUG */
-#ifdef INET6
-/* Functions to work with IPv6 states */
-static struct dyn_ipv6_state *dyn_lookup_ipv6_state(
- const struct ipfw_flow_id *, uint32_t, const void *,
- struct ipfw_dyn_info *, int);
-static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *,
- uint32_t, const void *, int, uint32_t, uint16_t);
-static struct dyn_ipv6_state *dyn_alloc_ipv6_state(
- const struct ipfw_flow_id *, uint32_t, uint16_t, uint8_t);
-static int dyn_add_ipv6_state(void *, uint32_t, uint16_t,
- const struct ipfw_flow_id *, uint32_t, const void *, int, uint32_t,
- struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t);
-static void dyn_export_ipv6_state(const struct dyn_ipv6_state *,
- ipfw_dyn_rule *);
+/* Common and bookeeping functions for all rules (sorta polymorphic) */
+static void dyn_free_state(dyn_state_hdr *);
-static uint32_t dyn_getscopeid(const struct ip_fw_args *);
-static void dyn_make_keepalive_ipv6(struct mbuf *, const struct in6_addr *,
- const struct in6_addr *, uint32_t, uint32_t, uint32_t, uint16_t,
- uint16_t);
-static void dyn_enqueue_keepalive_ipv6(struct mbufq *,
- const struct dyn_ipv6_state *);
-static void dyn_send_keepalive_ipv6(struct ip_fw_chain *);
-
-static struct dyn_ipv6_state *dyn_lookup_ipv6_parent(
- const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t,
- uint32_t);
-static struct dyn_ipv6_state *dyn_lookup_ipv6_parent_locked(
- const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t,
- uint32_t);
-static struct dyn_ipv6_state *dyn_add_ipv6_parent(void *, uint32_t, uint16_t,
- const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t, uint16_t);
-#endif /* INET6 */
+static void dyn_export_state_data(const dyn_state_data *, ipfw_dyn_rule *);
-/* Functions to work with limit states */
-static void *dyn_get_parent_state(const struct ipfw_flow_id *, uint32_t,
- struct ip_fw *, uint32_t, uint32_t, uint16_t);
-static struct dyn_ipv4_state *dyn_lookup_ipv4_parent(
- const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t);
-static struct dyn_ipv4_state *dyn_lookup_ipv4_parent_locked(
- const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t);
-static struct dyn_parent *dyn_alloc_parent(void *, uint32_t, uint16_t,
- uint32_t);
-static struct dyn_ipv4_state *dyn_add_ipv4_parent(void *, uint32_t, uint16_t,
- const struct ipfw_flow_id *, uint32_t, uint32_t, uint16_t);
+static uint32_t dyn_update_tcp_state(dyn_state_data *,
+ const struct ipfw_flow_id *, const struct tcphdr *, int);
+static void dyn_update_proto_state(dyn_state_data *,
+ const struct ipfw_flow_id *, const void *, int, int);
+
+static dyn_state_hdr *dyn_get_parent_state(const struct ipfw_flow_id *,
+ uint32_t, struct ip_fw *, uint32_t, uint32_t, uint16_t);
static void dyn_tick(void *);
static void dyn_expire_states(struct ip_fw_chain *, ipfw_range_tlv *);
static void dyn_free_states(struct ip_fw_chain *);
-static void dyn_export_parent(const struct dyn_parent *, uint16_t, uint8_t,
- ipfw_dyn_rule *);
-static void dyn_export_data(const struct dyn_data *, uint16_t, uint8_t,
- uint8_t, ipfw_dyn_rule *);
-static uint32_t dyn_update_tcp_state(struct dyn_data *,
- const struct ipfw_flow_id *, const struct tcphdr *, int);
-static void dyn_update_proto_state(struct dyn_data *,
- const struct ipfw_flow_id *, const void *, int, int);
/* Functions to work with IPv4 states */
-struct dyn_ipv4_state *dyn_lookup_ipv4_state(const struct ipfw_flow_id *,
+static dyn_ipv4_state *dyn_lookup_ipv4_state(const struct ipfw_flow_id *,
const void *, struct ipfw_dyn_info *, int);
static int dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *,
const void *, int, uint32_t, uint16_t);
-static struct dyn_ipv4_state *dyn_alloc_ipv4_state(
+static dyn_ipv4_state *dyn_alloc_ipv4_state(
const struct ipfw_flow_id *, uint16_t, uint8_t);
static int dyn_add_ipv4_state(void *, uint32_t, uint16_t,
const struct ipfw_flow_id *, const void *, int, uint32_t,
struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t);
-static void dyn_export_ipv4_state(const struct dyn_ipv4_state *,
+static void dyn_export_ipv4_state(const dyn_ipv4_state *,
+ ipfw_dyn_rule *);
+
+/* Functions to work with IPv4 parent states */
+static dyn_ipv4_parent_state *dyn_lookup_ipv4_parent(
+ const struct ipfw_flow_id *, const struct ip_fw *, uint32_t, uint16_t,
+ uint32_t);
+static dyn_ipv4_parent_state *dyn_lookup_ipv4_parent_locked(
+ const struct ipfw_flow_id *, const struct ip_fw *, uint32_t, uint16_t,
+ uint32_t);
+static dyn_ipv4_parent_state *dyn_alloc_ipv4_parent_state(
+ const struct ipfw_flow_id *, uint16_t, uint8_t);
+static dyn_ipv4_parent_state *dyn_add_ipv4_parent(struct ip_fw *,
+ uint32_t, uint16_t, const struct ipfw_flow_id *, uint32_t, uint32_t,
+ uint16_t);
+
+#ifdef INET6
+/* Functions to work with IPv6 states */
+static uint32_t dyn_getscopeid(const struct ip_fw_args *);
+
+/* Simple IPv6 states */
+static dyn_ipv6_state *dyn_lookup_ipv6_state(
+ const struct ipfw_flow_id *, uint32_t, const void *,
+ struct ipfw_dyn_info *, int);
+static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *,
+ uint32_t, const void *, int, uint32_t, uint16_t);
+static dyn_ipv6_state *dyn_alloc_ipv6_state(
+ const struct ipfw_flow_id *, uint32_t, uint16_t, uint8_t);
+static int dyn_add_ipv6_state(void *, uint32_t, uint16_t,
+ const struct ipfw_flow_id *, uint32_t, const void *, int, uint32_t,
+ struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t);
+static void dyn_export_ipv6_state(const dyn_ipv6_state *,
ipfw_dyn_rule *);
+/* Parent IPv6 states */
+static dyn_ipv6_parent_state *dyn_lookup_ipv6_parent(
+ const struct ipfw_flow_id *, uint32_t, const struct ip_fw *, uint32_t,
+ uint16_t, uint32_t);
+static dyn_ipv6_parent_state *dyn_lookup_ipv6_parent_locked(
+ const struct ipfw_flow_id *, uint32_t, const struct ip_fw *, uint32_t,
+ uint16_t, uint32_t);
+static dyn_ipv6_parent_state *dyn_alloc_ipv6_parent_state(
+ const struct ipfw_flow_id *, uint32_t, uint16_t, uint8_t);
+static dyn_ipv6_parent_state *dyn_add_ipv6_parent(struct ip_fw *,
+ uint32_t, uint16_t, const struct ipfw_flow_id *, uint32_t, uint32_t,
+ uint32_t, uint16_t);
+#endif /* INET6 */
+
/*
* Named states support.
*/
@@ -917,6 +1076,40 @@
#define print_dyn_rule(id, dtype, prefix, postfix) \
print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix)
+static void
+dyn_free_state(dyn_state_hdr *state)
+{
+ switch (state->type) {
+ case DYN_STATE_TYPE_KEEP:
+ case DYN_STATE_TYPE_LIMIT:
+ if (state->af == 4) {
+ uma_zfree(V_dyn_ipv4_zone, state);
+ return;
+ }
+#ifdef INET6
+ if (state->af == 6) {
+ uma_zfree(V_dyn_ipv6_zone, state);
+ return;
+ }
+#endif
+ break;
+ case DYN_STATE_TYPE_PARENT:
+ if (state->af == 4) {
+ uma_zfree(V_dyn_ipv4_parent_zone, state);
+ return;
+ }
+#ifdef INET6
+ if (state->af == 6) {
+ uma_zfree(V_dyn_ipv6_parent_zone, state);
+ return;
+ }
+#endif
+ break;
+ }
+ panic("Unknown dynamic state type (%u, %u)", state->type, state->af);
+}
+
+
#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0)
#define TIME_LE(a,b) ((int)((a)-(b)) < 0)
#define _SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
@@ -928,10 +1121,11 @@
#define ACK_BOTH (ACK_FWD | ACK_REV)
static uint32_t
-dyn_update_tcp_state(struct dyn_data *data, const struct ipfw_flow_id *pkt,
+dyn_update_tcp_state(dyn_state_data *data, const struct ipfw_flow_id *pkt,
const struct tcphdr *tcp, int dir)
{
- uint32_t ack, expire;
+ uint32_t expire;
+ uint32_t ack;
uint32_t state, old;
uint8_t th_flags;
@@ -1003,8 +1197,8 @@
* are also updated here.
*/
static void
-dyn_update_proto_state(struct dyn_data *data, const struct ipfw_flow_id *pkt,
- const void *ulp, int pktlen, int dir)
+dyn_update_proto_state(dyn_state_data *data,
+ const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, int dir)
{
uint32_t expire;
@@ -1037,41 +1231,46 @@
* Lookup IPv4 state.
* Must be called in critical section.
*/
-struct dyn_ipv4_state *
+dyn_ipv4_state *
dyn_lookup_ipv4_state(const struct ipfw_flow_id *pkt, const void *ulp,
struct ipfw_dyn_info *info, int pktlen)
{
- struct dyn_ipv4_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv4_state *s;
uint32_t version, bucket;
bucket = DYN_BUCKET(info->hashval, V_curr_dyn_buckets);
info->version = DYN_BUCKET_VERSION(bucket, ipv4_add);
restart:
version = DYN_BUCKET_VERSION(bucket, ipv4_del);
- CK_SLIST_FOREACH(s, &V_dyn_ipv4[bucket], entry) {
+ CK_SLIST_FOREACH(h, &V_dyn_ipv4[bucket], entry) {
+ s = DYN_CAST_IPV4_STATE(h);
DYNSTATE_PROTECT(s);
if (version != DYN_BUCKET_VERSION(bucket, ipv4_del))
goto restart;
- if (s->proto != pkt->proto)
+ if (s->h.proto != pkt->proto)
continue;
- if (info->kidx != 0 && s->kidx != info->kidx)
+ if (info->kidx != 0 && s->h.kidx != info->kidx)
continue;
- if (s->sport == pkt->src_port && s->dport == pkt->dst_port &&
- s->src == pkt->src_ip && s->dst == pkt->dst_ip) {
+ if (s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ s->a.src == pkt->src_ip &&
+ s->a.dst == pkt->dst_ip) {
info->direction = MATCH_FORWARD;
break;
}
- if (s->sport == pkt->dst_port && s->dport == pkt->src_port &&
- s->src == pkt->dst_ip && s->dst == pkt->src_ip) {
+ if (s->h.sport == pkt->dst_port &&
+ s->h.dport == pkt->src_port &&
+ s->a.src == pkt->dst_ip &&
+ s->a.dst == pkt->src_ip) {
info->direction = MATCH_REVERSE;
break;
}
}
-
- if (s != NULL)
- dyn_update_proto_state(s->data, pkt, ulp, pktlen,
+ if (h != NULL)
+ dyn_update_proto_state(&s->d, pkt, ulp, pktlen,
info->direction);
- return (s);
+ return ((dyn_ipv4_state *)h);
}
/*
@@ -1082,43 +1281,51 @@
dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *pkt,
const void *ulp, int pktlen, uint32_t bucket, uint16_t kidx)
{
- struct dyn_ipv4_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv4_state *s;
int dir;
dir = MATCH_NONE;
DYN_BUCKET_ASSERT(bucket);
- CK_SLIST_FOREACH(s, &V_dyn_ipv4[bucket], entry) {
- if (s->proto != pkt->proto ||
- s->kidx != kidx)
+ CK_SLIST_FOREACH(h, &V_dyn_ipv4[bucket], entry) {
+ s = DYN_CAST_IPV4_STATE(h);
+ if (s->h.proto != pkt->proto ||
+ s->h.kidx != kidx)
continue;
- if (s->sport == pkt->src_port &&
- s->dport == pkt->dst_port &&
- s->src == pkt->src_ip && s->dst == pkt->dst_ip) {
+ if (s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ s->a.src == pkt->src_ip &&
+ s->a.dst == pkt->dst_ip) {
dir = MATCH_FORWARD;
break;
}
- if (s->sport == pkt->dst_port && s->dport == pkt->src_port &&
- s->src == pkt->dst_ip && s->dst == pkt->src_ip) {
+ if (s->h.sport == pkt->dst_port &&
+ s->h.dport == pkt->src_port &&
+ s->a.src == pkt->dst_ip &&
+ s->a.dst == pkt->src_ip) {
dir = MATCH_REVERSE;
break;
}
}
- if (s != NULL)
- dyn_update_proto_state(s->data, pkt, ulp, pktlen, dir);
- return (s != NULL);
+ if (h != NULL)
+ dyn_update_proto_state(&s->d, pkt, ulp, pktlen, dir);
+ return (h != NULL);
}
-struct dyn_ipv4_state *
-dyn_lookup_ipv4_parent(const struct ipfw_flow_id *pkt, const void *rule,
- uint32_t ruleid, uint16_t rulenum, uint32_t hashval)
+dyn_ipv4_parent_state *
+dyn_lookup_ipv4_parent(const struct ipfw_flow_id *pkt,
+ const struct ip_fw *rule, uint32_t ruleid, uint16_t rulenum,
+ uint32_t hashval)
{
- struct dyn_ipv4_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv4_parent_state *s;
uint32_t version, bucket;
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
restart:
version = DYN_BUCKET_VERSION(bucket, ipv4_parent_del);
- CK_SLIST_FOREACH(s, &V_dyn_ipv4_parent[bucket], entry) {
+ CK_SLIST_FOREACH(h, &V_dyn_ipv4_parent[bucket], entry) {
+ s = DYN_CAST_IPV4_PARENT_STATE(h);
DYNSTATE_PROTECT(s);
if (version != DYN_BUCKET_VERSION(bucket, ipv4_parent_del))
goto restart;
@@ -1126,42 +1333,51 @@
* NOTE: we do not need to check kidx, because parent rule
* can not create states with different kidx.
* And parent rule always created for forward direction.
+ *
+ * Compare first fields first, maybe we could skip load second half.
*/
- if (s->limit->parent == rule &&
- s->limit->ruleid == ruleid &&
- s->limit->rulenum == rulenum &&
- s->proto == pkt->proto &&
- s->sport == pkt->src_port &&
- s->dport == pkt->dst_port &&
- s->src == pkt->src_ip && s->dst == pkt->dst_ip) {
- if (s->limit->expire != time_uptime +
+ if (s->h.proto == pkt->proto &&
+ s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ s->h.ruleid == ruleid &&
+ s->h.rulenum == rulenum &&
+ s->a.src == pkt->src_ip &&
+ s->a.dst == pkt->dst_ip &&
+ s->d.parent == rule) {
+ if (s->d.expire != time_uptime +
V_dyn_short_lifetime)
- ck_pr_store_32(&s->limit->expire,
+ ck_pr_store_32(&s->d.expire,
time_uptime + V_dyn_short_lifetime);
break;
}
}
- return (s);
+ return ((dyn_ipv4_parent_state *)h);
}
-static struct dyn_ipv4_state *
+static dyn_ipv4_parent_state *
dyn_lookup_ipv4_parent_locked(const struct ipfw_flow_id *pkt,
- const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket)
+ const struct ip_fw *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket)
{
- struct dyn_ipv4_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv4_parent_state *s;
DYN_BUCKET_ASSERT(bucket);
- CK_SLIST_FOREACH(s, &V_dyn_ipv4_parent[bucket], entry) {
- if (s->limit->parent == rule &&
- s->limit->ruleid == ruleid &&
- s->limit->rulenum == rulenum &&
- s->proto == pkt->proto &&
- s->sport == pkt->src_port &&
- s->dport == pkt->dst_port &&
- s->src == pkt->src_ip && s->dst == pkt->dst_ip)
+ CK_SLIST_FOREACH(h, &V_dyn_ipv4_parent[bucket], entry) {
+ s = DYN_CAST_IPV4_PARENT_STATE(h);
+ /*
+ * Compare first fields first, maybe we could skip load second half.
+ */
+ if (s->h.proto == pkt->proto &&
+ s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ s->h.ruleid == ruleid &&
+ s->h.rulenum == rulenum &&
+ s->a.src == pkt->src_ip &&
+ s->a.dst == pkt->dst_ip &&
+ s->d.parent == rule)
break;
}
- return (s);
+ return ((dyn_ipv4_parent_state *)h);
}
#ifdef INET6
@@ -1184,42 +1400,46 @@
* Lookup IPv6 state.
* Must be called in critical section.
*/
-static struct dyn_ipv6_state *
+static dyn_ipv6_state *
dyn_lookup_ipv6_state(const struct ipfw_flow_id *pkt, uint32_t zoneid,
const void *ulp, struct ipfw_dyn_info *info, int pktlen)
{
- struct dyn_ipv6_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv6_state *s;
uint32_t version, bucket;
bucket = DYN_BUCKET(info->hashval, V_curr_dyn_buckets);
info->version = DYN_BUCKET_VERSION(bucket, ipv6_add);
restart:
version = DYN_BUCKET_VERSION(bucket, ipv6_del);
- CK_SLIST_FOREACH(s, &V_dyn_ipv6[bucket], entry) {
+ CK_SLIST_FOREACH(h, &V_dyn_ipv6[bucket], entry) {
+ s = DYN_CAST_IPV6_STATE(h);
DYNSTATE_PROTECT(s);
if (version != DYN_BUCKET_VERSION(bucket, ipv6_del))
goto restart;
- if (s->proto != pkt->proto || s->zoneid != zoneid)
+ if (s->h.proto != pkt->proto || s->a.zoneid != zoneid)
continue;
- if (info->kidx != 0 && s->kidx != info->kidx)
+ if (info->kidx != 0 && s->h.kidx != info->kidx)
continue;
- if (s->sport == pkt->src_port && s->dport == pkt->dst_port &&
- IN6_ARE_ADDR_EQUAL(&s->src, &pkt->src_ip6) &&
- IN6_ARE_ADDR_EQUAL(&s->dst, &pkt->dst_ip6)) {
+ if (s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ IN6_ARE_ADDR_EQUAL(&s->a.src, &pkt->src_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&s->a.dst, &pkt->dst_ip6)) {
info->direction = MATCH_FORWARD;
break;
}
- if (s->sport == pkt->dst_port && s->dport == pkt->src_port &&
- IN6_ARE_ADDR_EQUAL(&s->src, &pkt->dst_ip6) &&
- IN6_ARE_ADDR_EQUAL(&s->dst, &pkt->src_ip6)) {
+ if (s->h.sport == pkt->dst_port &&
+ s->h.dport == pkt->src_port &&
+ IN6_ARE_ADDR_EQUAL(&s->a.src, &pkt->dst_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&s->a.dst, &pkt->src_ip6)) {
info->direction = MATCH_REVERSE;
break;
}
}
- if (s != NULL)
- dyn_update_proto_state(s->data, pkt, ulp, pktlen,
+ if (h != NULL)
+ dyn_update_proto_state(&s->d, pkt, ulp, pktlen,
info->direction);
- return (s);
+ return ((dyn_ipv6_state *)h);
}
/*
@@ -1230,44 +1450,51 @@
dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid,
const void *ulp, int pktlen, uint32_t bucket, uint16_t kidx)
{
- struct dyn_ipv6_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv6_state *s;
int dir;
dir = MATCH_NONE;
DYN_BUCKET_ASSERT(bucket);
- CK_SLIST_FOREACH(s, &V_dyn_ipv6[bucket], entry) {
- if (s->proto != pkt->proto || s->kidx != kidx ||
- s->zoneid != zoneid)
+ CK_SLIST_FOREACH(h, &V_dyn_ipv6[bucket], entry) {
+ s = DYN_CAST_IPV6_STATE(h);
+ if (s->h.proto != pkt->proto ||
+ s->h.kidx != kidx ||
+ s->a.zoneid != zoneid)
continue;
- if (s->sport == pkt->src_port && s->dport == pkt->dst_port &&
- IN6_ARE_ADDR_EQUAL(&s->src, &pkt->src_ip6) &&
- IN6_ARE_ADDR_EQUAL(&s->dst, &pkt->dst_ip6)) {
+ if (s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ IN6_ARE_ADDR_EQUAL(&s->a.src, &pkt->src_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&s->a.dst, &pkt->dst_ip6)) {
dir = MATCH_FORWARD;
break;
}
- if (s->sport == pkt->dst_port && s->dport == pkt->src_port &&
- IN6_ARE_ADDR_EQUAL(&s->src, &pkt->dst_ip6) &&
- IN6_ARE_ADDR_EQUAL(&s->dst, &pkt->src_ip6)) {
+ if (s->h.sport == pkt->dst_port &&
+ s->h.dport == pkt->src_port &&
+ IN6_ARE_ADDR_EQUAL(&s->a.src, &pkt->dst_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&s->a.dst, &pkt->src_ip6)) {
dir = MATCH_REVERSE;
break;
}
}
- if (s != NULL)
- dyn_update_proto_state(s->data, pkt, ulp, pktlen, dir);
- return (s != NULL);
+ if (h != NULL)
+ dyn_update_proto_state(&s->d, pkt, ulp, pktlen, dir);
+ return (h != NULL);
}
-static struct dyn_ipv6_state *
+static dyn_ipv6_parent_state *
dyn_lookup_ipv6_parent(const struct ipfw_flow_id *pkt, uint32_t zoneid,
- const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t hashval)
+ const struct ip_fw *rule, uint32_t ruleid, uint16_t rulenum, uint32_t hashval)
{
- struct dyn_ipv6_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv6_parent_state *s;
uint32_t version, bucket;
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
restart:
version = DYN_BUCKET_VERSION(bucket, ipv6_parent_del);
- CK_SLIST_FOREACH(s, &V_dyn_ipv6_parent[bucket], entry) {
+ CK_SLIST_FOREACH(h, &V_dyn_ipv6_parent[bucket], entry) {
+ s = DYN_CAST_IPV6_PARENT_STATE(h);
DYNSTATE_PROTECT(s);
if (version != DYN_BUCKET_VERSION(bucket, ipv6_parent_del))
goto restart;
@@ -1275,44 +1502,53 @@
* NOTE: we do not need to check kidx, because parent rule
* can not create states with different kidx.
* Also parent rule always created for forward direction.
+ *
+ * Compare first fields first, maybe we could skip load second half.
*/
- if (s->limit->parent == rule &&
- s->limit->ruleid == ruleid &&
- s->limit->rulenum == rulenum &&
- s->proto == pkt->proto &&
- s->sport == pkt->src_port &&
- s->dport == pkt->dst_port && s->zoneid == zoneid &&
- IN6_ARE_ADDR_EQUAL(&s->src, &pkt->src_ip6) &&
- IN6_ARE_ADDR_EQUAL(&s->dst, &pkt->dst_ip6)) {
- if (s->limit->expire != time_uptime +
+ if (s->h.proto == pkt->proto &&
+ s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ s->h.ruleid == ruleid &&
+ s->h.rulenum == rulenum &&
+ s->a.zoneid == zoneid &&
+ IN6_ARE_ADDR_EQUAL(&s->a.src, &pkt->src_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&s->a.dst, &pkt->dst_ip6) &&
+ s->d.parent == rule) {
+ if (s->d.expire != time_uptime +
V_dyn_short_lifetime)
- ck_pr_store_32(&s->limit->expire,
+ ck_pr_store_32(&s->d.expire,
time_uptime + V_dyn_short_lifetime);
break;
}
}
- return (s);
+ return ((dyn_ipv6_parent_state *)h);
}
-static struct dyn_ipv6_state *
+static dyn_ipv6_parent_state *
dyn_lookup_ipv6_parent_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid,
- const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket)
+ const struct ip_fw *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket)
{
- struct dyn_ipv6_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv6_parent_state *s;
DYN_BUCKET_ASSERT(bucket);
- CK_SLIST_FOREACH(s, &V_dyn_ipv6_parent[bucket], entry) {
- if (s->limit->parent == rule &&
- s->limit->ruleid == ruleid &&
- s->limit->rulenum == rulenum &&
- s->proto == pkt->proto &&
- s->sport == pkt->src_port &&
- s->dport == pkt->dst_port && s->zoneid == zoneid &&
- IN6_ARE_ADDR_EQUAL(&s->src, &pkt->src_ip6) &&
- IN6_ARE_ADDR_EQUAL(&s->dst, &pkt->dst_ip6))
+ CK_SLIST_FOREACH(h, &V_dyn_ipv6_parent[bucket], entry) {
+ s = DYN_CAST_IPV6_PARENT_STATE(h);
+ /*
+ * Compare first fields first, maybe we could skip load second half.
+ */
+ if (s->h.proto == pkt->proto &&
+ s->h.sport == pkt->src_port &&
+ s->h.dport == pkt->dst_port &&
+ s->h.ruleid == ruleid &&
+ s->h.rulenum == rulenum &&
+ s->a.zoneid == zoneid &&
+ IN6_ARE_ADDR_EQUAL(&s->a.src, &pkt->src_ip6) &&
+ IN6_ARE_ADDR_EQUAL(&s->a.dst, &pkt->dst_ip6) &&
+ s->d.parent == rule)
break;
}
- return (s);
+ return ((dyn_ipv6_parent_state *)h);
}
#endif /* INET6 */
@@ -1330,7 +1566,8 @@
ipfw_dyn_lookup_state(const struct ip_fw_args *args, const void *ulp,
int pktlen, const ipfw_insn *cmd, struct ipfw_dyn_info *info)
{
- struct dyn_data *data;
+ dyn_state_data *data;
+ dyn_state_hdr *hdr;
struct ip_fw *rule;
IPFW_RLOCK_ASSERT(&V_layer3_chain);
@@ -1343,36 +1580,36 @@
DYNSTATE_CRITICAL_ENTER();
if (IS_IP4_FLOW_ID(&args->f_id)) {
- struct dyn_ipv4_state *s;
+ dyn_ipv4_state *s;
s = dyn_lookup_ipv4_state(&args->f_id, ulp, info, pktlen);
if (s != NULL) {
/*
* Dynamic states are created using the same 5-tuple,
- * so it is assumed, that parent rule for O_LIMIT
+ * so it is assumed, that parent rule for DYN_STATE_TYPE_LIMIT
* state has the same address family.
*/
- data = s->data;
- if (s->type == O_LIMIT) {
- s = data->parent;
- rule = s->limit->parent;
- } else
- rule = data->parent;
+ data = &s->d;
+ hdr = &s->h;
+ if (s->h.type == DYN_STATE_TYPE_LIMIT)
+ rule = DYN_CAST_IPV4_PARENT_STATE(s->d.lp)->d.parent;
+ else
+ rule = s->d.sp;
}
}
#ifdef INET6
else if (IS_IP6_FLOW_ID(&args->f_id)) {
- struct dyn_ipv6_state *s;
+ dyn_ipv6_state *s;
s = dyn_lookup_ipv6_state(&args->f_id, dyn_getscopeid(args),
ulp, info, pktlen);
if (s != NULL) {
- data = s->data;
- if (s->type == O_LIMIT) {
- s = data->parent;
- rule = s->limit->parent;
- } else
- rule = data->parent;
+ data = &s->d;
+ hdr = &s->h;
+ if (s->h.type == DYN_STATE_TYPE_LIMIT)
+ rule = DYN_CAST_IPV6_PARENT_STATE(s->d.lp)->d.parent;
+ else
+ rule = s->d.sp;
}
}
#endif
@@ -1386,7 +1623,7 @@
*/
if (data->chain_id != V_layer3_chain.id) {
data->f_pos = ipfw_find_rule(&V_layer3_chain,
- data->rulenum, data->ruleid);
+ hdr->rulenum, hdr->ruleid);
/*
* Check that found state has not orphaned.
* When chain->id being changed the parent
@@ -1452,41 +1689,43 @@
return (rule);
}
-static struct dyn_parent *
-dyn_alloc_parent(void *parent, uint32_t ruleid, uint16_t rulenum,
- uint32_t hashval)
+static __inline void
+dyn_init_state_header(dyn_state_hdr *hdr, const struct ipfw_flow_id *pkt,
+ uint16_t kidx, uint8_t type)
{
- struct dyn_parent *limit;
-
- limit = uma_zalloc(V_dyn_parent_zone, M_NOWAIT | M_ZERO);
- if (limit == NULL) {
- if (last_log != time_uptime) {
- last_log = time_uptime;
- log(LOG_DEBUG,
- "ipfw: Cannot allocate parent dynamic state, "
- "consider increasing "
- "net.inet.ip.fw.dyn_parent_max\n");
- }
- return (NULL);
+ switch (type) {
+ case O_KEEP_STATE:
+ hdr->type = DYN_STATE_TYPE_KEEP;
+ break;
+ case O_LIMIT:
+ hdr->type = DYN_STATE_TYPE_LIMIT;
+ break;
+ case O_LIMIT_PARENT:
+ hdr->type = DYN_STATE_TYPE_PARENT;
+ break;
+ default:
+ hdr->type = type;
+ break;
}
-
- limit->parent = parent;
- limit->ruleid = ruleid;
- limit->rulenum = rulenum;
- limit->hashval = hashval;
- limit->expire = time_uptime + V_dyn_short_lifetime;
- return (limit);
+ hdr->af = IS_IP4_FLOW_ID(pkt) ? 4 : 6;
+ hdr->proto = pkt->proto;
+ hdr->kidx = kidx;
+ hdr->sport = pkt->src_port;
+ hdr->dport = pkt->dst_port;
}
-static struct dyn_data *
-dyn_alloc_dyndata(void *parent, uint32_t ruleid, uint16_t rulenum,
- const struct ipfw_flow_id *pkt, const void *ulp, int pktlen,
- uint32_t hashval, uint16_t fibnum)
+static dyn_ipv4_state *
+dyn_alloc_ipv4_state(const struct ipfw_flow_id *pkt, uint16_t kidx,
+ uint8_t type)
{
- struct dyn_data *data;
+ dyn_ipv4_state *s;
- data = uma_zalloc(V_dyn_data_zone, M_NOWAIT | M_ZERO);
- if (data == NULL) {
+ if (DYN_COUNT(dyn_count) < DYN_COUNT(dyn_max))
+ s = uma_zalloc(V_dyn_ipv4_zone, M_NOWAIT | M_ZERO);
+ else
+ s = NULL;
+
+ if (s == NULL) {
if (last_log != time_uptime) {
last_log = time_uptime;
log(LOG_DEBUG,
@@ -1496,33 +1735,37 @@
return (NULL);
}
- data->parent = parent;
- data->ruleid = ruleid;
- data->rulenum = rulenum;
- data->fibnum = fibnum;
- data->hashval = hashval;
- data->expire = time_uptime + V_dyn_syn_lifetime;
- dyn_update_proto_state(data, pkt, ulp, pktlen, MATCH_FORWARD);
- return (data);
+ dyn_init_state_header(&s->h, pkt, kidx, type);
+ s->a.src = pkt->src_ip;
+ s->a.dst = pkt->dst_ip;
+ return (s);
}
-static struct dyn_ipv4_state *
-dyn_alloc_ipv4_state(const struct ipfw_flow_id *pkt, uint16_t kidx,
+static dyn_ipv4_parent_state *
+dyn_alloc_ipv4_parent_state(const struct ipfw_flow_id *pkt, uint16_t kidx,
uint8_t type)
{
- struct dyn_ipv4_state *s;
+ dyn_ipv4_parent_state *s;
+
+ if (DYN_COUNT(dyn_parent_count) < DYN_COUNT(dyn_parent_max))
+ s = uma_zalloc(V_dyn_ipv4_parent_zone, M_NOWAIT | M_ZERO);
+ else
+ s = NULL;
- s = uma_zalloc(V_dyn_ipv4_zone, M_NOWAIT | M_ZERO);
- if (s == NULL)
+ if (s == NULL) {
+ if (last_log != time_uptime) {
+ last_log = time_uptime;
+ log(LOG_DEBUG,
+ "ipfw: Cannot allocate parent dynamic state, "
+ "consider increasing "
+ "net.inet.ip.fw.dyn_parent_max\n");
+ }
return (NULL);
+ }
- s->type = type;
- s->kidx = kidx;
- s->proto = pkt->proto;
- s->sport = pkt->src_port;
- s->dport = pkt->dst_port;
- s->src = pkt->src_ip;
- s->dst = pkt->dst_ip;
+ dyn_init_state_header(&s->h, pkt, kidx, type);
+ s->a.src = pkt->src_ip;
+ s->a.dst = pkt->dst_ip;
return (s);
}
@@ -1533,13 +1776,12 @@
* When some error occurs, it returns NULL and exit from critical section
* is not needed.
*/
-static struct dyn_ipv4_state *
-dyn_add_ipv4_parent(void *rule, uint32_t ruleid, uint16_t rulenum,
- const struct ipfw_flow_id *pkt, uint32_t hashval, uint32_t version,
- uint16_t kidx)
+static dyn_ipv4_parent_state *
+dyn_add_ipv4_parent(struct ip_fw *rule, uint32_t ruleid,
+ uint16_t rulenum, const struct ipfw_flow_id *pkt, uint32_t hashval,
+ uint32_t version, uint16_t kidx)
{
- struct dyn_ipv4_state *s;
- struct dyn_parent *limit;
+ dyn_ipv4_parent_state *s;
uint32_t bucket;
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
@@ -1563,21 +1805,20 @@
}
}
- limit = dyn_alloc_parent(rule, ruleid, rulenum, hashval);
- if (limit == NULL) {
- DYN_BUCKET_UNLOCK(bucket);
- return (NULL);
- }
-
- s = dyn_alloc_ipv4_state(pkt, kidx, O_LIMIT_PARENT);
+ s = dyn_alloc_ipv4_parent_state(pkt, kidx, O_LIMIT_PARENT);
if (s == NULL) {
DYN_BUCKET_UNLOCK(bucket);
- uma_zfree(V_dyn_parent_zone, limit);
return (NULL);
}
- s->limit = limit;
- CK_SLIST_INSERT_HEAD(&V_dyn_ipv4_parent[bucket], s, entry);
+ s->h.ruleid = ruleid;
+ s->h.rulenum = rulenum;
+ s->c.hashval = hashval;
+
+ s->d.parent = rule;
+ s->d.expire = time_uptime + V_dyn_short_lifetime;
+
+ CK_SLIST_INSERT_HEAD(&V_dyn_ipv4_parent[bucket], (dyn_state_hdr *)s, entry);
DYN_COUNT_INC(dyn_parent_count);
DYN_BUCKET_VERSION_BUMP(bucket, ipv4_parent_add);
DYNSTATE_CRITICAL_ENTER();
@@ -1592,8 +1833,7 @@
uint32_t hashval, struct ipfw_dyn_info *info, uint16_t fibnum,
uint16_t kidx, uint8_t type)
{
- struct dyn_ipv4_state *s;
- void *data;
+ dyn_ipv4_state *s;
uint32_t bucket;
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
@@ -1613,22 +1853,37 @@
}
}
- data = dyn_alloc_dyndata(parent, ruleid, rulenum, pkt, ulp,
- pktlen, hashval, fibnum);
- if (data == NULL) {
- DYN_BUCKET_UNLOCK(bucket);
- return (ENOMEM);
- }
s = dyn_alloc_ipv4_state(pkt, kidx, type);
if (s == NULL) {
DYN_BUCKET_UNLOCK(bucket);
- uma_zfree(V_dyn_data_zone, data);
return (ENOMEM);
}
- s->data = data;
- CK_SLIST_INSERT_HEAD(&V_dyn_ipv4[bucket], s, entry);
+ s->h.ruleid = ruleid;
+ s->h.rulenum = rulenum;
+ s->c.hashval = hashval;
+
+ s->d.sp = parent;
+ s->d.fibnum = fibnum;
+ s->d.expire = time_uptime + V_dyn_syn_lifetime;
+
+#ifdef INVARIANTS
+ switch (s->h.type) {
+ case DYN_STATE_TYPE_KEEP:
+ break;
+ case DYN_STATE_TYPE_LIMIT:
+ ASSERT_PARENT_STATE(s);
+ break;
+ default:
+ panic("Wrong dynamic state %d:%d, expected dyn_ipv4_state at %s:%d",
+ s->h.type, s->h.af, __FILE__, __LINE__);
+ }
+#endif
+
+ dyn_update_proto_state(&s->d, pkt, ulp, pktlen, MATCH_FORWARD);
+
+ CK_SLIST_INSERT_HEAD(&V_dyn_ipv4[bucket], (dyn_state_hdr *)s, entry);
DYN_COUNT_INC(dyn_count);
DYN_BUCKET_VERSION_BUMP(bucket, ipv4_add);
DYN_BUCKET_UNLOCK(bucket);
@@ -1636,24 +1891,60 @@
}
#ifdef INET6
-static struct dyn_ipv6_state *
+static dyn_ipv6_state *
dyn_alloc_ipv6_state(const struct ipfw_flow_id *pkt, uint32_t zoneid,
uint16_t kidx, uint8_t type)
{
- struct dyn_ipv6_state *s;
+ dyn_ipv6_state *s;
+
+ if (DYN_COUNT(dyn_count) < DYN_COUNT(dyn_max))
+ s = uma_zalloc(V_dyn_ipv6_zone, M_NOWAIT | M_ZERO);
+ else
+ s = NULL;
- s = uma_zalloc(V_dyn_ipv6_zone, M_NOWAIT | M_ZERO);
- if (s == NULL)
+ if (s == NULL) {
+ if (last_log != time_uptime) {
+ last_log = time_uptime;
+ log(LOG_DEBUG,
+ "ipfw: Cannot allocate dynamic state, "
+ "consider increasing net.inet.ip.fw.dyn_max\n");
+ }
+ return (NULL);
+ }
+
+ dyn_init_state_header(&s->h, pkt, kidx, type);
+ s->a.zoneid = zoneid;
+ s->a.src = pkt->src_ip6;
+ s->a.dst = pkt->dst_ip6;
+ return (s);
+}
+
+static dyn_ipv6_parent_state *
+dyn_alloc_ipv6_parent_state(const struct ipfw_flow_id *pkt, uint32_t zoneid,
+ uint16_t kidx, uint8_t type)
+{
+ dyn_ipv6_parent_state *s;
+
+ if (DYN_COUNT(dyn_parent_count) < DYN_COUNT(dyn_parent_max))
+ s = uma_zalloc(V_dyn_ipv6_parent_zone, M_NOWAIT | M_ZERO);
+ else
+ s = NULL;
+
+ if (s == NULL) {
+ if (last_log != time_uptime) {
+ last_log = time_uptime;
+ log(LOG_DEBUG,
+ "ipfw: Cannot allocate parent dynamic state, "
+ "consider increasing "
+ "net.inet.ip.fw.dyn_parent_max\n");
+ }
return (NULL);
+ }
- s->type = type;
- s->kidx = kidx;
- s->zoneid = zoneid;
- s->proto = pkt->proto;
- s->sport = pkt->src_port;
- s->dport = pkt->dst_port;
- s->src = pkt->src_ip6;
- s->dst = pkt->dst_ip6;
+ dyn_init_state_header(&s->h, pkt, kidx, type);
+ s->a.zoneid = zoneid;
+ s->a.src = pkt->src_ip6;
+ s->a.dst = pkt->dst_ip6;
return (s);
}
@@ -1664,13 +1955,12 @@
* When some error occurs, it return NULL and exit from critical section
* is not needed.
*/
-static struct dyn_ipv6_state *
-dyn_add_ipv6_parent(void *rule, uint32_t ruleid, uint16_t rulenum,
- const struct ipfw_flow_id *pkt, uint32_t zoneid, uint32_t hashval,
- uint32_t version, uint16_t kidx)
+static dyn_ipv6_parent_state *
+dyn_add_ipv6_parent(struct ip_fw *rule, uint32_t ruleid,
+ uint16_t rulenum, const struct ipfw_flow_id *pkt, uint32_t zoneid,
+ uint32_t hashval, uint32_t version, uint16_t kidx)
{
- struct dyn_ipv6_state *s;
- struct dyn_parent *limit;
+ dyn_ipv6_parent_state *s;
uint32_t bucket;
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
@@ -1694,21 +1984,20 @@
}
}
- limit = dyn_alloc_parent(rule, ruleid, rulenum, hashval);
- if (limit == NULL) {
- DYN_BUCKET_UNLOCK(bucket);
- return (NULL);
- }
-
- s = dyn_alloc_ipv6_state(pkt, zoneid, kidx, O_LIMIT_PARENT);
+ s = dyn_alloc_ipv6_parent_state(pkt, zoneid, kidx, O_LIMIT_PARENT);
if (s == NULL) {
DYN_BUCKET_UNLOCK(bucket);
- uma_zfree(V_dyn_parent_zone, limit);
return (NULL);
}
- s->limit = limit;
- CK_SLIST_INSERT_HEAD(&V_dyn_ipv6_parent[bucket], s, entry);
+ s->h.ruleid = ruleid;
+ s->h.rulenum = rulenum;
+ s->c.hashval = hashval;
+
+ s->d.parent = rule;
+ s->d.expire = time_uptime + V_dyn_short_lifetime;
+
+ CK_SLIST_INSERT_HEAD(&V_dyn_ipv6_parent[bucket], (dyn_state_hdr *)s, entry);
DYN_COUNT_INC(dyn_parent_count);
DYN_BUCKET_VERSION_BUMP(bucket, ipv6_parent_add);
DYNSTATE_CRITICAL_ENTER();
@@ -1723,8 +2012,7 @@
int pktlen, uint32_t hashval, struct ipfw_dyn_info *info,
uint16_t fibnum, uint16_t kidx, uint8_t type)
{
- struct dyn_ipv6_state *s;
- struct dyn_data *data;
+ dyn_ipv6_state *s;
uint32_t bucket;
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
@@ -1744,22 +2032,36 @@
}
}
- data = dyn_alloc_dyndata(parent, ruleid, rulenum, pkt, ulp,
- pktlen, hashval, fibnum);
- if (data == NULL) {
- DYN_BUCKET_UNLOCK(bucket);
- return (ENOMEM);
- }
-
s = dyn_alloc_ipv6_state(pkt, zoneid, kidx, type);
if (s == NULL) {
DYN_BUCKET_UNLOCK(bucket);
- uma_zfree(V_dyn_data_zone, data);
return (ENOMEM);
}
- s->data = data;
- CK_SLIST_INSERT_HEAD(&V_dyn_ipv6[bucket], s, entry);
+ s->h.ruleid = ruleid;
+ s->h.rulenum = rulenum;
+ s->c.hashval = hashval;
+
+ s->d.sp = parent;
+ s->d.fibnum = fibnum;
+ s->d.expire = time_uptime + V_dyn_syn_lifetime;
+
+#ifdef INVARIANTS
+ switch (s->h.type) {
+ case DYN_STATE_TYPE_KEEP:
+ break;
+ case DYN_STATE_TYPE_LIMIT:
+ ASSERT_PARENT_STATE(s);
+ break;
+ default:
+ panic("Wrong dynamic state %d:%d, expected dyn_ipv4_state at %s:%d",
+ s->h.type, s->h.af, __FILE__, __LINE__);
+ }
+#endif
+
+ dyn_update_proto_state(&s->d, pkt, ulp, pktlen, MATCH_FORWARD);
+
+ CK_SLIST_INSERT_HEAD(&V_dyn_ipv6[bucket], (dyn_state_hdr *)s, entry);
DYN_COUNT_INC(dyn_count);
DYN_BUCKET_VERSION_BUMP(bucket, ipv6_add);
DYN_BUCKET_UNLOCK(bucket);
@@ -1767,13 +2069,13 @@
}
#endif /* INET6 */
-static void *
+static dyn_state_hdr *
dyn_get_parent_state(const struct ipfw_flow_id *pkt, uint32_t zoneid,
struct ip_fw *rule, uint32_t hashval, uint32_t limit, uint16_t kidx)
{
char sbuf[24];
- struct dyn_parent *p;
- void *ret;
+ dyn_parent_state_data *p;
+ dyn_state_hdr *ret;
uint32_t bucket, version;
p = NULL;
@@ -1781,7 +2083,7 @@
bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets);
DYNSTATE_CRITICAL_ENTER();
if (IS_IP4_FLOW_ID(pkt)) {
- struct dyn_ipv4_state *s;
+ dyn_ipv4_parent_state *s;
version = DYN_BUCKET_VERSION(bucket, ipv4_parent_add);
s = dyn_lookup_ipv4_parent(pkt, rule, rule->id,
@@ -1799,12 +2101,12 @@
return (NULL);
/* Now we are in critical section again. */
}
- ret = s;
- p = s->limit;
+ ret = (dyn_state_hdr *)s;
+ p = &s->d;
}
#ifdef INET6
else if (IS_IP6_FLOW_ID(pkt)) {
- struct dyn_ipv6_state *s;
+ dyn_ipv6_parent_state *s;
version = DYN_BUCKET_VERSION(bucket, ipv6_parent_add);
s = dyn_lookup_ipv6_parent(pkt, zoneid, rule, rule->id,
@@ -1823,8 +2125,8 @@
return (NULL);
/* Now we are in critical section again. */
}
- ret = s;
- p = s->limit;
+ ret = (dyn_state_hdr *)s;
+ p = &s->d;
}
#endif
else {
@@ -1839,7 +2141,7 @@
last_log = time_uptime;
snprintf(sbuf, sizeof(sbuf), "%u drop session",
rule->rulenum);
- print_dyn_rule_flags(pkt, O_LIMIT,
+ print_dyn_rule_flags(pkt, DYN_STATE_TYPE_LIMIT,
LOG_SECURITY | LOG_DEBUG, sbuf,
"too many entries");
}
@@ -1867,12 +2169,14 @@
{
struct ipfw_flow_id id;
uint32_t hashval, parent_hashval, ruleid, rulenum;
+ void *parent;
int ret;
MPASS(type == O_LIMIT || type == O_KEEP_STATE);
ruleid = rule->id;
rulenum = rule->rulenum;
+ parent = rule;
if (type == O_LIMIT) {
/* Create masked flow id and calculate bucket */
id.addr_type = pkt->addr_type;
@@ -1904,15 +2208,15 @@
return (EAFNOSUPPORT);
parent_hashval = hash_parent(&id, rule);
- rule = dyn_get_parent_state(&id, zoneid, rule, parent_hashval,
+ parent = dyn_get_parent_state(&id, zoneid, rule, parent_hashval,
limit, kidx);
- if (rule == NULL) {
+ if (parent == NULL) {
#if 0
if (V_fw_verbose && last_log != time_uptime) {
last_log = time_uptime;
snprintf(sbuf, sizeof(sbuf),
"%u drop session", rule->rulenum);
- print_dyn_rule_flags(pkt, O_LIMIT,
+ print_dyn_rule_flags(pkt, DYN_STATE_TYPE_LIMIT,
LOG_SECURITY | LOG_DEBUG, sbuf,
"too many entries");
}
@@ -1927,11 +2231,11 @@
hashval = hash_packet(pkt);
if (IS_IP4_FLOW_ID(pkt))
- ret = dyn_add_ipv4_state(rule, ruleid, rulenum, pkt,
+ ret = dyn_add_ipv4_state(parent, ruleid, rulenum, pkt,
ulp, pktlen, hashval, info, fibnum, kidx, type);
#ifdef INET6
else if (IS_IP6_FLOW_ID(pkt))
- ret = dyn_add_ipv6_state(rule, ruleid, rulenum, pkt,
+ ret = dyn_add_ipv6_state(parent, ruleid, rulenum, pkt,
zoneid, ulp, pktlen, hashval, info, fibnum, kidx, type);
#endif /* INET6 */
else
@@ -1940,7 +2244,7 @@
if (type == O_LIMIT) {
if (ret != 0) {
/*
- * We failed to create child state for O_LIMIT
+ * We failed to create child state for DYN_STATE_TYPE_LIMIT
* opcode. Since we already counted it in the parent,
* we must revert counter back. The 'rule' points to
* parent state, use it to get dyn_parent.
@@ -1951,11 +2255,13 @@
*/
if (IS_IP4_FLOW_ID(&id))
DPARENT_COUNT_DEC(
- ((struct dyn_ipv4_state *)rule)->limit);
+ &(DYN_CAST_IPV4_PARENT_STATE((dyn_state_hdr *)parent)->d)
+ );
#ifdef INET6
else if (IS_IP6_FLOW_ID(&id))
DPARENT_COUNT_DEC(
- ((struct dyn_ipv6_state *)rule)->limit);
+ &(DYN_CAST_IPV6_PARENT_STATE((dyn_state_hdr *)parent)->d)
+ );
#endif
}
}
@@ -2009,15 +2315,32 @@
}
/*
- * Free safe to remove state entries from expired lists.
+ * Check is state really expiried or not
*/
-static void
-dyn_free_states(struct ip_fw_chain *chain)
+static __inline int
+dyn_state_expired(dyn_state_hdr *hdr)
{
- struct dyn_ipv4_state *s4, *s4n;
+ if (hdr->type != DYN_STATE_TYPE_PARENT)
+ return (0);
+#ifdef INET6
+ if (hdr->af == 4)
+#endif
+ return DYN_CAST_IPV4_PARENT_STATE(hdr)->d.count != 0;
#ifdef INET6
- struct dyn_ipv6_state *s6, *s6n;
+ else
+ return DYN_CAST_IPV6_PARENT_STATE(hdr)->d.count != 0;
#endif
+ return (-1);
+
+}
+
+/*
+ * Free safe to remove state entries from expired list.
+ */
+static void
+dyn_free_states(struct ip_fw_chain *chain)
+{
+ dyn_state_hdr *s, *next, *prev;
int cached_count, i;
/*
@@ -2049,40 +2372,6 @@
cached_count++;
}
- /*
- * Free expired states that are safe to free.
- * Check each entry from previous pass in the dyn_expired_xxx
- * list, if pointer to the object is in the dyn_hp_cache array,
- * keep it until next pass. Otherwise it is safe to free the
- * object.
- *
- * XXXAE: optimize this to use SLIST_REMOVE_AFTER.
- */
-#define DYN_FREE_STATES(s, next, name) do { \
- s = SLIST_FIRST(&V_dyn_expired_ ## name); \
- while (s != NULL) { \
- next = SLIST_NEXT(s, expired); \
- for (i = 0; i < cached_count; i++) \
- if (dyn_hp_cache[i] == s) \
- break; \
- if (i == cached_count) { \
- if (s->type == O_LIMIT_PARENT && \
- s->limit->count != 0) { \
- s = next; \
- continue; \
- } \
- SLIST_REMOVE(&V_dyn_expired_ ## name, \
- s, dyn_ ## name ## _state, expired); \
- if (s->type == O_LIMIT_PARENT) \
- uma_zfree(V_dyn_parent_zone, s->limit); \
- else \
- uma_zfree(V_dyn_data_zone, s->data); \
- uma_zfree(V_dyn_ ## name ## _zone, s); \
- } \
- s = next; \
- } \
-} while (0)
-
/*
* Protect access to expired lists with DYN_EXPIRED_LOCK.
* Userland can invoke ipfw_expire_dyn_states() to delete
@@ -2093,12 +2382,37 @@
* IPFW_UH_WLOCK to protect access to these lists.
*/
DYN_EXPIRED_LOCK();
- DYN_FREE_STATES(s4, s4n, ipv4);
-#ifdef INET6
- DYN_FREE_STATES(s6, s6n, ipv6);
-#endif
+ /*
+ * Free expired states that are safe to free.
+ * Check each entry from previous pass in the dyn_expired_xxx
+ * list, if pointer to the object is in the dyn_hp_cache array,
+ * keep it until next pass. Otherwise it is safe to free the
+ * object.
+ *
+ */
+ s = SLIST_FIRST(&V_dyn_expired);
+ prev = NULL;
+ while (s != NULL) {
+ next = SLIST_NEXT(s, expired);
+ for (i = 0; i < cached_count; i++)
+ if (dyn_hp_cache[i] == s)
+ break;
+ /*
+ * Remove this only if we don't have it cached and
+ * it is not parent with children.
+ */
+ if (i == cached_count && !dyn_state_expired(s)) {
+ if (prev == NULL)
+ SLIST_REMOVE_HEAD(&V_dyn_expired, expired);
+ else
+ SLIST_REMOVE_AFTER(prev, expired);
+ dyn_free_state(s);
+ } else {
+ prev = s;
+ }
+ s = next;
+ }
DYN_EXPIRED_UNLOCK();
-#undef DYN_FREE_STATES
}
/*
@@ -2130,7 +2444,7 @@
}
static void
-dyn_acquire_rule(struct ip_fw_chain *ch, struct dyn_data *data,
+dyn_acquire_rule(struct ip_fw_chain *ch, dyn_state_data *data,
struct ip_fw *rule, uint16_t kidx)
{
struct dyn_state_obj *obj;
@@ -2157,7 +2471,7 @@
}
static void
-dyn_release_rule(struct ip_fw_chain *ch, struct dyn_data *data,
+dyn_release_rule(struct ip_fw_chain *ch, dyn_state_data *data,
struct ip_fw *rule, uint16_t kidx)
{
struct dyn_state_obj *obj;
@@ -2177,60 +2491,66 @@
/*
* We do not keep O_LIMIT_PARENT states when V_dyn_keep_states is enabled.
- * O_LIMIT state is created when new connection is going to be established
+ * DYN_STATE_TYPE_LIMIT state is created when new connection is going to be established
* and there is no matching state. So, since the old parent rule was deleted
* we can't create new states with old parent, and thus we can not account
* new connections with already established connections, and can not do
* proper limiting.
*/
static int
-dyn_match_ipv4_state(struct ip_fw_chain *ch, struct dyn_ipv4_state *s,
+dyn_match_ipv4_state(struct ip_fw_chain *ch, dyn_ipv4_state *s,
const ipfw_range_tlv *rt)
{
struct ip_fw *rule;
int ret;
- if (s->type == O_LIMIT_PARENT) {
- rule = s->limit->parent;
- return (dyn_match_range(s->limit->rulenum, rule->set, rt));
- }
-
- rule = s->data->parent;
- if (s->type == O_LIMIT)
- rule = ((struct dyn_ipv4_state *)rule)->limit->parent;
+ if (s->h.type == DYN_STATE_TYPE_LIMIT)
+ rule = DYN_CAST_IPV4_PARENT_STATE(s->d.lp)->d.parent;
+ else
+ rule = s->d.sp;
- ret = dyn_match_range(s->data->rulenum, rule->set, rt);
+ ret = dyn_match_range(s->h.rulenum, rule->set, rt);
if (ret == 0 || V_dyn_keep_states == 0 || ret > 1)
return (ret);
- dyn_acquire_rule(ch, s->data, rule, s->kidx);
+ dyn_acquire_rule(ch, &s->d, rule, s->h.kidx);
return (0);
}
+static __inline int
+dyn_match_ipv4_parent_state(struct ip_fw_chain *ch,
+ dyn_ipv4_parent_state *s, const ipfw_range_tlv *rt)
+{
+ return (dyn_match_range(s->h.rulenum, s->d.parent->set, rt));
+}
+
#ifdef INET6
static int
-dyn_match_ipv6_state(struct ip_fw_chain *ch, struct dyn_ipv6_state *s,
+dyn_match_ipv6_state(struct ip_fw_chain *ch, dyn_ipv6_state *s,
const ipfw_range_tlv *rt)
{
struct ip_fw *rule;
int ret;
- if (s->type == O_LIMIT_PARENT) {
- rule = s->limit->parent;
- return (dyn_match_range(s->limit->rulenum, rule->set, rt));
- }
-
- rule = s->data->parent;
- if (s->type == O_LIMIT)
- rule = ((struct dyn_ipv6_state *)rule)->limit->parent;
+ if (s->h.type == DYN_STATE_TYPE_LIMIT)
+ rule = DYN_CAST_IPV6_PARENT_STATE(s->d.lp)->d.parent;
+ else
+ rule = s->d.sp;
- ret = dyn_match_range(s->data->rulenum, rule->set, rt);
+ ret = dyn_match_range(s->h.rulenum, rule->set, rt);
if (ret == 0 || V_dyn_keep_states == 0 || ret > 1)
return (ret);
- dyn_acquire_rule(ch, s->data, rule, s->kidx);
+ dyn_acquire_rule(ch, &s->d, rule, s->h.kidx);
return (0);
}
+
+static __inline int
+dyn_match_ipv6_parent_state(struct ip_fw_chain *ch,
+ dyn_ipv6_parent_state *s, const ipfw_range_tlv *rt)
+{
+ return (dyn_match_range(s->h.rulenum, s->d.parent->set, rt));
+}
#endif
/*
@@ -2240,13 +2560,16 @@
static void
dyn_expire_states(struct ip_fw_chain *ch, ipfw_range_tlv *rt)
{
- struct dyn_ipv4_slist expired_ipv4;
+
+ struct dyn_any_state_slist expired;
+ dyn_state_hdr *h, *next, *prev;
+ dyn_ipv4_state *s4;
+ dyn_ipv4_parent_state *p4;
#ifdef INET6
- struct dyn_ipv6_slist expired_ipv6;
- struct dyn_ipv6_state *s6, *s6n, *s6p;
+ dyn_ipv6_state *s6;
+ dyn_ipv6_parent_state *p6;
#endif
- struct dyn_ipv4_state *s4, *s4n, *s4p;
- void *rule;
+ struct ip_fw *rule;
int bucket, removed, length, max_length;
IPFW_UH_WLOCK_ASSERT(ch);
@@ -2256,77 +2579,82 @@
* With acquired bucket lock iterate entries of each lists:
* ipv4, ipv4_parent, ipv6, and ipv6_parent. Check expired time
* and unlink entry from the list, link entry into temporary
- * expired_xxx lists then bump "del" bucket version.
+ * expired list then bump "del" bucket version.
*
* When an entry is removed, corresponding states counter is
- * decremented. If entry has O_LIMIT type, parent's reference
- * counter is decremented.
+ * decremented. If entry has DYN_STATE_TYPE_LIMIT type,
+ * parent's reference counter is decremented.
*
* NOTE: this function can be called from userspace context
* when user deletes rules. In this case all matched states
- * will be forcedly unlinked. O_LIMIT_PARENT states will be kept
- * in the expired lists until reference counter become zero.
+ * will be forcedly unlinked. DYN_STATE_TYPE_PARENT states will
+ * be kept in the expired lists until reference counter become
+ * zero.
*/
-#define DYN_UNLINK_STATES(s, prev, next, exp, af, name, extra) do { \
- length = 0; \
- removed = 0; \
- prev = NULL; \
- s = CK_SLIST_FIRST(&V_dyn_ ## name [bucket]); \
- while (s != NULL) { \
- next = CK_SLIST_NEXT(s, entry); \
- if ((TIME_LEQ((s)->exp, time_uptime) && extra) || \
- (rt != NULL && \
- dyn_match_ ## af ## _state(ch, s, rt))) { \
- if (prev != NULL) \
- CK_SLIST_REMOVE_AFTER(prev, entry); \
- else \
- CK_SLIST_REMOVE_HEAD( \
- &V_dyn_ ## name [bucket], entry); \
- removed++; \
- SLIST_INSERT_HEAD(&expired_ ## af, s, expired); \
- if (s->type == O_LIMIT_PARENT) \
- DYN_COUNT_DEC(dyn_parent_count); \
- else { \
- DYN_COUNT_DEC(dyn_count); \
- if (s->data->flags & DYN_REFERENCED) { \
- rule = s->data->parent; \
- if (s->type == O_LIMIT) \
- rule = ((__typeof(s)) \
- rule)->limit->parent;\
- dyn_release_rule(ch, s->data, \
- rule, s->kidx); \
- } \
- if (s->type == O_LIMIT) { \
- s = s->data->parent; \
- DPARENT_COUNT_DEC(s->limit); \
- } \
- } \
- } else { \
- prev = s; \
- length++; \
- } \
- s = next; \
- } \
- if (removed != 0) \
- DYN_BUCKET_VERSION_BUMP(bucket, name ## _del); \
- if (length > max_length) \
- max_length = length; \
+#define DYN_UNLINK_STATES(s, name, ucaf, extra, cleanup) do { \
+ length = 0; \
+ removed = 0; \
+ prev = NULL; \
+ h = CK_SLIST_FIRST(&V_dyn_ ## name [bucket]); \
+ while (h != NULL) { \
+ next = CK_SLIST_NEXT((dyn_state_hdr *)h, entry); \
+ s = (dyn_ ## name ## _state *)h; \
+ if ((TIME_LEQ((s)->d.expire, time_uptime) && extra) || \
+ (rt != NULL && \
+ dyn_match_ ## name ## _state(ch, s, rt))) { \
+ if (prev != NULL) \
+ CK_SLIST_REMOVE_AFTER(prev, entry); \
+ else \
+ CK_SLIST_REMOVE_HEAD( \
+ &V_dyn_ ## name [bucket], entry); \
+ removed++; \
+ SLIST_INSERT_HEAD(&expired, h, expired); \
+ cleanup(s, ucaf); \
+ } else { \
+ prev = h; \
+ length++; \
+ } \
+ h = next; \
+ } \
+ if (removed != 0) \
+ DYN_BUCKET_VERSION_BUMP(bucket, name ## _del); \
+ if (length > max_length) \
+ max_length = length; \
} while (0)
- SLIST_INIT(&expired_ipv4);
-#ifdef INET6
- SLIST_INIT(&expired_ipv6);
-#endif
+#define DYN_UNLINK_STATES_CLEANUP_STATE(s, ucaf) do { \
+ MPASS(s->h.type != DYN_STATE_TYPE_PARENT); \
+ DYN_COUNT_DEC(dyn_count); \
+ if ((s->d.flags & DYN_REFERENCED)) { \
+ if (s->h.type == DYN_STATE_TYPE_KEEP) \
+ rule = s->d.sp; \
+ if (s->h.type == DYN_STATE_TYPE_LIMIT) \
+ rule = DYN_CAST_ ## ucaf ## _PARENT_STATE(s->d.lp)->d.parent; \
+ dyn_release_rule(ch, &s->d, rule, s->h.kidx); \
+ } \
+ if (s->h.type == DYN_STATE_TYPE_LIMIT) \
+ DPARENT_COUNT_DEC(&DYN_CAST_ ## ucaf ## _PARENT_STATE(s->d.lp)->d); \
+} while(0)
+
+#define DYN_UNLINK_STATES_CLEANUP_PARENT(s, ucaf) do { \
+ MPASS(s->h.type == DYN_STATE_TYPE_PARENT); \
+ DYN_COUNT_DEC(dyn_parent_count); \
+} while(0)
+
+ SLIST_INIT(&expired);
+
max_length = 0;
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
DYN_BUCKET_LOCK(bucket);
- DYN_UNLINK_STATES(s4, s4p, s4n, data->expire, ipv4, ipv4, 1);
- DYN_UNLINK_STATES(s4, s4p, s4n, limit->expire, ipv4,
- ipv4_parent, (s4->limit->count == 0));
+ DYN_UNLINK_STATES(s4, ipv4, IPV4, 1,
+ DYN_UNLINK_STATES_CLEANUP_STATE);
+ DYN_UNLINK_STATES(p4, ipv4_parent, IPV4, (p4->d.count == 0),
+ DYN_UNLINK_STATES_CLEANUP_PARENT);
#ifdef INET6
- DYN_UNLINK_STATES(s6, s6p, s6n, data->expire, ipv6, ipv6, 1);
- DYN_UNLINK_STATES(s6, s6p, s6n, limit->expire, ipv6,
- ipv6_parent, (s6->limit->count == 0));
+ DYN_UNLINK_STATES(s6, ipv6, IPV6, 1,
+ DYN_UNLINK_STATES_CLEANUP_STATE);
+ DYN_UNLINK_STATES(p6, ipv6_parent, IPV6, (p6->d.count == 0),
+ DYN_UNLINK_STATES_CLEANUP_PARENT);
#endif
DYN_BUCKET_UNLOCK(bucket);
}
@@ -2336,15 +2664,12 @@
* Concatenate temporary lists with global expired lists.
*/
DYN_EXPIRED_LOCK();
- SLIST_CONCAT(&V_dyn_expired_ipv4, &expired_ipv4,
- dyn_ipv4_state, expired);
-#ifdef INET6
- SLIST_CONCAT(&V_dyn_expired_ipv6, &expired_ipv6,
- dyn_ipv6_state, expired);
-#endif
+ SLIST_CONCAT(&V_dyn_expired, &expired, _dyn_state_hdr, expired);
DYN_EXPIRED_UNLOCK();
#undef DYN_UNLINK_STATES
-#undef DYN_UNREF_STATES
+/*#undef DYN_UNLINK_STATES_CLEANUP*/
+#undef DYN_UNLINK_STATES_CLEANUP_STATE
+#undef DYN_UNLINK_STATES_CLEANUP_PARENT
}
static struct mbuf *
@@ -2399,17 +2724,17 @@
}
static void
-dyn_enqueue_keepalive_ipv4(struct mbufq *q, const struct dyn_ipv4_state *s)
+dyn_enqueue_keepalive_ipv4(struct mbufq *q, const dyn_ipv4_state *s)
{
struct mbuf *m;
- if ((s->data->state & ACK_FWD) == 0 && s->data->ack_fwd > 0) {
+ if ((s->d.state & ACK_FWD) == 0 && s->d.ack_fwd > 0) {
m = dyn_mgethdr(sizeof(struct ip) + sizeof(struct tcphdr),
- s->data->fibnum);
+ s->d.fibnum);
if (m != NULL) {
- dyn_make_keepalive_ipv4(m, s->dst, s->src,
- s->data->ack_fwd - 1, s->data->ack_rev,
- s->dport, s->sport);
+ dyn_make_keepalive_ipv4(m, s->a.dst, s->a.src,
+ s->d.ack_fwd - 1, s->d.ack_rev,
+ s->h.dport, s->h.sport);
if (mbufq_enqueue(q, m)) {
m_freem(m);
log(LOG_DEBUG, "ipfw: limit for IPv4 "
@@ -2419,13 +2744,13 @@
}
}
- if ((s->data->state & ACK_REV) == 0 && s->data->ack_rev > 0) {
+ if ((s->d.state & ACK_REV) == 0 && s->d.ack_rev > 0) {
m = dyn_mgethdr(sizeof(struct ip) + sizeof(struct tcphdr),
- s->data->fibnum);
+ s->d.fibnum);
if (m != NULL) {
- dyn_make_keepalive_ipv4(m, s->src, s->dst,
- s->data->ack_rev - 1, s->data->ack_fwd,
- s->sport, s->dport);
+ dyn_make_keepalive_ipv4(m, s->a.src, s->a.dst,
+ s->d.ack_rev - 1, s->d.ack_fwd,
+ s->h.sport, s->h.dport);
if (mbufq_enqueue(q, m)) {
m_freem(m);
log(LOG_DEBUG, "ipfw: limit for IPv4 "
@@ -2444,7 +2769,8 @@
{
struct mbufq q;
struct mbuf *m;
- struct dyn_ipv4_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv4_state *s;
uint32_t bucket;
mbufq_init(&q, INT_MAX);
@@ -2455,15 +2781,16 @@
* while we hold IPFW_UH_RLOCK.
*/
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- CK_SLIST_FOREACH(s, &V_dyn_ipv4[bucket], entry) {
+ CK_SLIST_FOREACH(h, &V_dyn_ipv4[bucket], entry) {
+ s = DYN_CAST_IPV4_STATE(h);
/*
* Only established TCP connections that will
* become expired withing dyn_keepalive_interval.
*/
- if (s->proto != IPPROTO_TCP ||
- (s->data->state & BOTH_SYN) != BOTH_SYN ||
+ if (s->h.proto != IPPROTO_TCP ||
+ (s->d.state & BOTH_SYN) != BOTH_SYN ||
TIME_LEQ(time_uptime + V_dyn_keepalive_interval,
- s->data->expire))
+ s->d.expire))
continue;
dyn_enqueue_keepalive_ipv4(&q, s);
}
@@ -2509,17 +2836,17 @@
}
static void
-dyn_enqueue_keepalive_ipv6(struct mbufq *q, const struct dyn_ipv6_state *s)
+dyn_enqueue_keepalive_ipv6(struct mbufq *q, const dyn_ipv6_state *s)
{
struct mbuf *m;
- if ((s->data->state & ACK_FWD) == 0 && s->data->ack_fwd > 0) {
+ if ((s->d.state & ACK_FWD) == 0 && s->d.ack_fwd > 0) {
m = dyn_mgethdr(sizeof(struct ip6_hdr) +
- sizeof(struct tcphdr), s->data->fibnum);
+ sizeof(struct tcphdr), s->d.fibnum);
if (m != NULL) {
- dyn_make_keepalive_ipv6(m, &s->dst, &s->src,
- s->zoneid, s->data->ack_fwd - 1, s->data->ack_rev,
- s->dport, s->sport);
+ dyn_make_keepalive_ipv6(m, &s->a.dst, &s->a.src,
+ s->a.zoneid, s->d.ack_fwd - 1, s->d.ack_rev,
+ s->h.dport, s->h.sport);
if (mbufq_enqueue(q, m)) {
m_freem(m);
log(LOG_DEBUG, "ipfw: limit for IPv6 "
@@ -2529,13 +2856,13 @@
}
}
- if ((s->data->state & ACK_REV) == 0 && s->data->ack_rev > 0) {
+ if ((s->d.state & ACK_REV) == 0 && s->d.ack_rev > 0) {
m = dyn_mgethdr(sizeof(struct ip6_hdr) +
- sizeof(struct tcphdr), s->data->fibnum);
+ sizeof(struct tcphdr), s->d.fibnum);
if (m != NULL) {
- dyn_make_keepalive_ipv6(m, &s->src, &s->dst,
- s->zoneid, s->data->ack_rev - 1, s->data->ack_fwd,
- s->sport, s->dport);
+ dyn_make_keepalive_ipv6(m, &s->a.src, &s->a.dst,
+ s->a.zoneid, s->d.ack_rev - 1, s->d.ack_fwd,
+ s->h.sport, s->h.dport);
if (mbufq_enqueue(q, m)) {
m_freem(m);
log(LOG_DEBUG, "ipfw: limit for IPv6 "
@@ -2551,7 +2878,8 @@
{
struct mbufq q;
struct mbuf *m;
- struct dyn_ipv6_state *s;
+ dyn_state_hdr *h;
+ dyn_ipv6_state *s;
uint32_t bucket;
mbufq_init(&q, INT_MAX);
@@ -2562,15 +2890,16 @@
* while we hold IPFW_UH_RLOCK.
*/
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- CK_SLIST_FOREACH(s, &V_dyn_ipv6[bucket], entry) {
+ CK_SLIST_FOREACH(h, &V_dyn_ipv6[bucket], entry) {
+ s = DYN_CAST_IPV6_STATE(h);
/*
* Only established TCP connections that will
* become expired withing dyn_keepalive_interval.
*/
- if (s->proto != IPPROTO_TCP ||
- (s->data->state & BOTH_SYN) != BOTH_SYN ||
+ if (s->h.proto != IPPROTO_TCP ||
+ (s->d.state & BOTH_SYN) != BOTH_SYN ||
TIME_LEQ(time_uptime + V_dyn_keepalive_interval,
- s->data->expire))
+ s->d.expire))
continue;
dyn_enqueue_keepalive_ipv6(&q, s);
}
@@ -2584,14 +2913,15 @@
static void
dyn_grow_hashtable(struct ip_fw_chain *chain, uint32_t new)
{
+ dyn_state_hdr *h;
+ struct dyn_any_state_ck_slist *ipv4;
+ struct dyn_any_state_ck_slist *ipv4_parent;
+ uint32_t *ipv4_add, *ipv4_del, *ipv4_parent_add, *ipv4_parent_del;
#ifdef INET6
- struct dyn_ipv6ck_slist *ipv6, *ipv6_parent;
+ struct dyn_any_state_ck_slist *ipv6;
+ struct dyn_any_state_ck_slist *ipv6_parent;
uint32_t *ipv6_add, *ipv6_del, *ipv6_parent_add, *ipv6_parent_del;
- struct dyn_ipv6_state *s6;
#endif
- struct dyn_ipv4ck_slist *ipv4, *ipv4_parent;
- uint32_t *ipv4_add, *ipv4_del, *ipv4_parent_add, *ipv4_parent_del;
- struct dyn_ipv4_state *s4;
struct mtx *bucket_lock;
void *tmp;
uint32_t bucket;
@@ -2604,9 +2934,9 @@
*/
bucket_lock = malloc(new * sizeof(struct mtx), M_IPFW,
M_WAITOK | M_ZERO);
- ipv4 = malloc(new * sizeof(struct dyn_ipv4ck_slist), M_IPFW,
+ ipv4 = malloc(new * sizeof(struct dyn_any_state_ck_slist), M_IPFW,
M_WAITOK | M_ZERO);
- ipv4_parent = malloc(new * sizeof(struct dyn_ipv4ck_slist), M_IPFW,
+ ipv4_parent = malloc(new * sizeof(struct dyn_any_state_ck_slist), M_IPFW,
M_WAITOK | M_ZERO);
ipv4_add = malloc(new * sizeof(uint32_t), M_IPFW, M_WAITOK | M_ZERO);
ipv4_del = malloc(new * sizeof(uint32_t), M_IPFW, M_WAITOK | M_ZERO);
@@ -2615,9 +2945,9 @@
ipv4_parent_del = malloc(new * sizeof(uint32_t), M_IPFW,
M_WAITOK | M_ZERO);
#ifdef INET6
- ipv6 = malloc(new * sizeof(struct dyn_ipv6ck_slist), M_IPFW,
+ ipv6 = malloc(new * sizeof(struct dyn_any_state_ck_slist), M_IPFW,
M_WAITOK | M_ZERO);
- ipv6_parent = malloc(new * sizeof(struct dyn_ipv6ck_slist), M_IPFW,
+ ipv6_parent = malloc(new * sizeof(struct dyn_any_state_ck_slist), M_IPFW,
M_WAITOK | M_ZERO);
ipv6_add = malloc(new * sizeof(uint32_t), M_IPFW, M_WAITOK | M_ZERO);
ipv6_del = malloc(new * sizeof(uint32_t), M_IPFW, M_WAITOK | M_ZERO);
@@ -2636,12 +2966,15 @@
#endif
}
-#define DYN_RELINK_STATES(s, hval, i, head, ohead) do { \
- while ((s = CK_SLIST_FIRST(&V_dyn_ ## ohead[i])) != NULL) { \
- CK_SLIST_REMOVE_HEAD(&V_dyn_ ## ohead[i], entry); \
- CK_SLIST_INSERT_HEAD(&head[DYN_BUCKET(s->hval, new)], \
- s, entry); \
- } \
+#define DYN_RELINK_STATES(i, aft) do { \
+ while ((h = CK_SLIST_FIRST(&V_dyn_ ## aft[i])) != NULL) { \
+ CK_SLIST_REMOVE_HEAD(&V_dyn_ ## aft[i], entry); \
+ CK_SLIST_INSERT_HEAD( \
+ &aft[ \
+ DYN_BUCKET(((dyn_ ## aft ## _state *)h)->c.hashval,new) \
+ ], \
+ h, entry); \
+ } \
} while (0)
/*
* Prevent rules changing from userland.
@@ -2654,13 +2987,11 @@
IPFW_WLOCK(chain);
/* Re-link all dynamic states */
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- DYN_RELINK_STATES(s4, data->hashval, bucket, ipv4, ipv4);
- DYN_RELINK_STATES(s4, limit->hashval, bucket, ipv4_parent,
- ipv4_parent);
+ DYN_RELINK_STATES(bucket, ipv4);
+ DYN_RELINK_STATES(bucket, ipv4_parent);
#ifdef INET6
- DYN_RELINK_STATES(s6, data->hashval, bucket, ipv6, ipv6);
- DYN_RELINK_STATES(s6, limit->hashval, bucket, ipv6_parent,
- ipv6_parent);
+ DYN_RELINK_STATES(bucket, ipv6);
+ DYN_RELINK_STATES(bucket, ipv6_parent);
#endif
}
@@ -2789,31 +3120,33 @@
ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint16_t eaction_id,
uint16_t default_id, uint16_t instance_id)
{
+ dyn_state_hdr *h;
+ dyn_ipv4_state *s4;
#ifdef INET6
- struct dyn_ipv6_state *s6;
+ dyn_ipv6_state *s6;
#endif
- struct dyn_ipv4_state *s4;
struct ip_fw *rule;
uint32_t bucket;
-#define DYN_RESET_EACTION(s, h, b) \
- CK_SLIST_FOREACH(s, &V_dyn_ ## h[b], entry) { \
- if ((s->data->flags & DYN_REFERENCED) == 0) \
- continue; \
- rule = s->data->parent; \
- if (s->type == O_LIMIT) \
- rule = ((__typeof(s))rule)->limit->parent; \
- ipfw_reset_eaction(ch, rule, eaction_id, \
- default_id, instance_id); \
+#define DYN_RESET_EACTION(s, af, ucaf, b) \
+ CK_SLIST_FOREACH(h, &V_dyn_ ## af[b], entry) { \
+ s = (dyn_ ## af ## _state *)h; \
+ if ((s->d.flags & DYN_REFERENCED) == 0) \
+ continue; \
+ rule = s->d.sp; \
+ if (s->h.type == DYN_STATE_TYPE_LIMIT) \
+ rule = DYN_CAST_ ## ucaf ## _PARENT_STATE(s->d.lp)->d.parent; \
+ ipfw_reset_eaction(ch, rule, eaction_id, \
+ default_id, instance_id); \
}
IPFW_UH_WLOCK_ASSERT(ch);
if (V_dyn_count == 0)
return;
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- DYN_RESET_EACTION(s4, ipv4, bucket);
+ DYN_RESET_EACTION(s4, ipv4, IPV4, bucket);
#ifdef INET6
- DYN_RESET_EACTION(s6, ipv6, bucket);
+ DYN_RESET_EACTION(s6, ipv6, IPV6, bucket);
#endif
}
}
@@ -2837,17 +3170,14 @@
uint32_t
ipfw_dyn_get_count(uint32_t *bmask, int *nocnt)
{
-#ifdef INET6
- struct dyn_ipv6_state *s6;
-#endif
- struct dyn_ipv4_state *s4;
+ dyn_state_hdr *h;
uint32_t bucket;
-#define DYN_COUNT_OBJECTS(s, h, b) \
- CK_SLIST_FOREACH(s, &V_dyn_ ## h[b], entry) { \
- MPASS(s->kidx != 0); \
+#define DYN_COUNT_OBJECTS(af, b) \
+ CK_SLIST_FOREACH(h, &V_dyn_ ## af[b], entry) { \
+ MPASS(h->kidx != 0); \
if (ipfw_mark_object_kidx(bmask, IPFW_TLV_STATE_NAME, \
- s->kidx) != 0) \
+ h->kidx) != 0) \
(*nocnt)++; \
}
@@ -2859,9 +3189,9 @@
return (0);
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- DYN_COUNT_OBJECTS(s4, ipv4, bucket);
+ DYN_COUNT_OBJECTS(ipv4, bucket);
#ifdef INET6
- DYN_COUNT_OBJECTS(s6, ipv6, bucket);
+ DYN_COUNT_OBJECTS(ipv6, bucket);
#endif
}
@@ -2897,23 +3227,37 @@
return (0);
}
+
static void
-dyn_export_parent(const struct dyn_parent *p, uint16_t kidx, uint8_t set,
- ipfw_dyn_rule *dst)
+dyn_export_header(const dyn_state_hdr *header, uint8_t set, ipfw_dyn_rule *dst)
{
-
- dst->dyn_type = O_LIMIT_PARENT;
- dst->kidx = kidx;
- dst->count = (uint16_t)DPARENT_COUNT(p);
- dst->expire = TIME_LEQ(p->expire, time_uptime) ? 0:
- p->expire - time_uptime;
+ switch (header->type) {
+ case DYN_STATE_TYPE_KEEP:
+ dst->dyn_type = O_KEEP_STATE;
+ break;
+ case DYN_STATE_TYPE_LIMIT:
+ dst->dyn_type = O_LIMIT;
+ break;
+ case DYN_STATE_TYPE_PARENT:
+ dst->dyn_type = O_LIMIT_PARENT;
+ break;
+ default:
+ dst->dyn_type = 0;
+ break;
+ }
+ dst->kidx = header->kidx;
/* 'rule' is used to pass up the rule number and set */
- memcpy(&dst->rule, &p->rulenum, sizeof(p->rulenum));
+ memcpy(&dst->rule, &header->rulenum, sizeof(header->rulenum));
/* store set number into high word of dst->rule pointer. */
- memcpy((char *)&dst->rule + sizeof(p->rulenum), &set, sizeof(set));
+ memcpy((char *)&dst->rule + sizeof(header->rulenum), &set, sizeof(set));
+
+}
+static void
+dyn_export_common(const dyn_state_common *common, ipfw_dyn_rule *dst)
+{
/* unused fields */
dst->pcnt = 0;
dst->bcnt = 0;
@@ -2921,7 +3265,7 @@
dst->state = 0;
dst->ack_fwd = 0;
dst->ack_rev = 0;
- dst->bucket = p->hashval;
+ dst->bucket = common->hashval;
/*
* The legacy userland code will interpret a NULL here as a marker
* for the last dynamic rule.
@@ -2930,23 +3274,14 @@
}
static void
-dyn_export_data(const struct dyn_data *data, uint16_t kidx, uint8_t type,
- uint8_t set, ipfw_dyn_rule *dst)
+dyn_export_state_data(const dyn_state_data *data, ipfw_dyn_rule *dst)
{
- dst->dyn_type = type;
- dst->kidx = kidx;
- dst->pcnt = data->pcnt_fwd + data->pcnt_rev;
- dst->bcnt = data->bcnt_fwd + data->bcnt_rev;
- dst->expire = TIME_LEQ(data->expire, time_uptime) ? 0:
+ dst->expire = TIME_LEQ(data->expire, time_uptime) ? 0 :
data->expire - time_uptime;
- /* 'rule' is used to pass up the rule number and set */
- memcpy(&dst->rule, &data->rulenum, sizeof(data->rulenum));
-
- /* store set number into high word of dst->rule pointer. */
- memcpy((char *)&dst->rule + sizeof(data->rulenum), &set, sizeof(set));
-
+ dst->pcnt = data->pcnt_fwd + data->pcnt_rev;
+ dst->bcnt = data->bcnt_fwd + data->bcnt_rev;
dst->state = data->state;
if (data->flags & DYN_REFERENCED)
dst->state |= IPFW_DYN_ORPHANED;
@@ -2956,37 +3291,51 @@
dst->ack_fwd = data->ack_fwd;
dst->ack_rev = data->ack_rev;
dst->count = 0;
- dst->bucket = data->hashval;
- /*
- * The legacy userland code will interpret a NULL here as a marker
- * for the last dynamic rule.
- */
- dst->next = (ipfw_dyn_rule *)1;
}
static void
-dyn_export_ipv4_state(const struct dyn_ipv4_state *s, ipfw_dyn_rule *dst)
+dyn_export_ipv4_state(const dyn_ipv4_state *s, ipfw_dyn_rule *dst)
{
struct ip_fw *rule;
- switch (s->type) {
- case O_LIMIT_PARENT:
- rule = s->limit->parent;
- dyn_export_parent(s->limit, s->kidx, rule->set, dst);
- break;
- default:
- rule = s->data->parent;
- if (s->type == O_LIMIT)
- rule = ((struct dyn_ipv4_state *)rule)->limit->parent;
- dyn_export_data(s->data, s->kidx, s->type, rule->set, dst);
- }
-
- dst->id.dst_ip = s->dst;
- dst->id.src_ip = s->src;
- dst->id.dst_port = s->dport;
- dst->id.src_port = s->sport;
- dst->id.fib = s->data->fibnum;
- dst->id.proto = s->proto;
+ rule = s->d.sp;
+ if (s->h.type == DYN_STATE_TYPE_LIMIT)
+ rule = DYN_CAST_IPV4_PARENT_STATE(s->d.lp)->d.parent;
+ dyn_export_header(&s->h, rule->set, dst);
+ dyn_export_common(&s->c, dst);
+ dyn_export_state_data(&s->d, dst);
+
+ dst->id.dst_ip = s->a.dst;
+ dst->id.src_ip = s->a.src;
+ dst->id.dst_port = s->h.dport;
+ dst->id.src_port = s->h.sport;
+ dst->id.fib = s->d.fibnum;
+ dst->id.proto = s->h.proto;
+ dst->id._flags = 0;
+ dst->id.addr_type = 4;
+
+ memset(&dst->id.dst_ip6, 0, sizeof(dst->id.dst_ip6));
+ memset(&dst->id.src_ip6, 0, sizeof(dst->id.src_ip6));
+ dst->id.flow_id6 = dst->id.extra = 0;
+}
+
+static void
+dyn_export_ipv4_parent_state(const dyn_ipv4_parent_state *s, ipfw_dyn_rule *dst)
+{
+ struct ip_fw *rule;
+
+ rule = s->d.parent;
+ dyn_export_header(&s->h, rule->set, dst);
+ dyn_export_common(&s->c, dst);
+
+ dst->expire = TIME_LEQ(s->d.expire, time_uptime) ? 0 :
+ s->d.expire - time_uptime;
+
+ dst->id.dst_ip = s->a.dst;
+ dst->id.src_ip = s->a.src;
+ dst->id.dst_port = s->h.dport;
+ dst->id.src_port = s->h.sport;
+ dst->id.proto = s->h.proto;
dst->id._flags = 0;
dst->id.addr_type = 4;
@@ -2997,28 +3346,47 @@
#ifdef INET6
static void
-dyn_export_ipv6_state(const struct dyn_ipv6_state *s, ipfw_dyn_rule *dst)
+dyn_export_ipv6_state(const dyn_ipv6_state *s, ipfw_dyn_rule *dst)
{
struct ip_fw *rule;
- switch (s->type) {
- case O_LIMIT_PARENT:
- rule = s->limit->parent;
- dyn_export_parent(s->limit, s->kidx, rule->set, dst);
- break;
- default:
- rule = s->data->parent;
- if (s->type == O_LIMIT)
- rule = ((struct dyn_ipv6_state *)rule)->limit->parent;
- dyn_export_data(s->data, s->kidx, s->type, rule->set, dst);
- }
-
- dst->id.src_ip6 = s->src;
- dst->id.dst_ip6 = s->dst;
- dst->id.dst_port = s->dport;
- dst->id.src_port = s->sport;
- dst->id.fib = s->data->fibnum;
- dst->id.proto = s->proto;
+ rule = s->d.sp;
+ if (s->h.type == DYN_STATE_TYPE_LIMIT)
+ rule = DYN_CAST_IPV6_PARENT_STATE(s->d.lp)->d.parent;
+ dyn_export_header(&s->h, rule->set, dst);
+ dyn_export_common(&s->c, dst);
+ dyn_export_state_data(&s->d, dst);
+
+ dst->id.src_ip6 = s->a.src;
+ dst->id.dst_ip6 = s->a.dst;
+ dst->id.dst_port = s->h.dport;
+ dst->id.src_port = s->h.sport;
+ dst->id.fib = s->d.fibnum;
+ dst->id.proto = s->h.proto;
+ dst->id._flags = 0;
+ dst->id.addr_type = 6;
+
+ dst->id.dst_ip = dst->id.src_ip = 0;
+ dst->id.flow_id6 = dst->id.extra = 0;
+}
+
+static void
+dyn_export_ipv6_parent_state(const dyn_ipv6_parent_state *s, ipfw_dyn_rule *dst)
+{
+ struct ip_fw *rule;
+
+ rule = s->d.parent;
+ dyn_export_header(&s->h, rule->set, dst);
+ dyn_export_common(&s->c, dst);
+
+ dst->expire = TIME_LEQ(s->d.expire, time_uptime) ? 0 :
+ s->d.expire - time_uptime;
+
+ dst->id.src_ip6 = s->a.src;
+ dst->id.dst_ip6 = s->a.dst;
+ dst->id.dst_port = s->h.dport;
+ dst->id.src_port = s->h.sport;
+ dst->id.proto = s->h.proto;
dst->id._flags = 0;
dst->id.addr_type = 6;
@@ -3036,10 +3404,13 @@
int
ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd)
{
+ dyn_state_hdr *h;
+ dyn_ipv4_state *s4;
+ dyn_ipv4_parent_state *p4;
#ifdef INET6
- struct dyn_ipv6_state *s6;
+ dyn_ipv6_state *s6;
+ dyn_ipv6_parent_state *p6;
#endif
- struct dyn_ipv4_state *s4;
ipfw_obj_dyntlv *dst, *last;
ipfw_obj_ctlv *ctlv;
uint32_t bucket;
@@ -3061,24 +3432,25 @@
ctlv->objsize = sizeof(ipfw_obj_dyntlv);
last = NULL;
-#define DYN_EXPORT_STATES(s, af, h, b) \
- CK_SLIST_FOREACH(s, &V_dyn_ ## h[b], entry) { \
+#define DYN_EXPORT_STATES(s, aft, b) \
+ CK_SLIST_FOREACH(h, &V_dyn_ ## aft[b], entry) { \
+ s = (dyn_ ## aft ## _state *)h; \
dst = (ipfw_obj_dyntlv *)ipfw_get_sopt_space(sd, \
sizeof(ipfw_obj_dyntlv)); \
if (dst == NULL) \
return (ENOMEM); \
- dyn_export_ ## af ## _state(s, &dst->state); \
+ dyn_export_ ## aft ##_state(s, &dst->state); \
dst->head.length = sizeof(ipfw_obj_dyntlv); \
dst->head.type = IPFW_TLV_DYN_ENT; \
last = dst; \
}
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- DYN_EXPORT_STATES(s4, ipv4, ipv4_parent, bucket);
- DYN_EXPORT_STATES(s4, ipv4, ipv4, bucket);
+ DYN_EXPORT_STATES(p4, ipv4_parent, bucket);
+ DYN_EXPORT_STATES(s4, ipv4, bucket);
#ifdef INET6
- DYN_EXPORT_STATES(s6, ipv6, ipv6_parent, bucket);
- DYN_EXPORT_STATES(s6, ipv6, ipv6, bucket);
+ DYN_EXPORT_STATES(p6, ipv6_parent, bucket);
+ DYN_EXPORT_STATES(s6, ipv6, bucket);
#endif /* INET6 */
}
@@ -3096,10 +3468,13 @@
void
ipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep)
{
+ dyn_state_hdr *h;
+ dyn_ipv4_state *s4;
+ dyn_ipv4_parent_state *p4;
#ifdef INET6
- struct dyn_ipv6_state *s6;
+ dyn_ipv6_state *s6;
+ dyn_ipv6_parent_state *p6;
#endif
- struct dyn_ipv4_state *s4;
ipfw_dyn_rule *p, *last = NULL;
char *bp;
uint32_t bucket;
@@ -3110,22 +3485,23 @@
IPFW_UH_RLOCK_ASSERT(chain);
-#define DYN_EXPORT_STATES(s, af, head, b) \
- CK_SLIST_FOREACH(s, &V_dyn_ ## head[b], entry) { \
- if (bp + sizeof(*p) > ep) \
- break; \
- p = (ipfw_dyn_rule *)bp; \
- dyn_export_ ## af ## _state(s, p); \
- last = p; \
- bp += sizeof(*p); \
+#define DYN_EXPORT_STATES(s, aft, b) \
+ CK_SLIST_FOREACH(h, &V_dyn_ ## aft[b], entry) { \
+ s = (dyn_ ## aft ## _state *)h; \
+ if (bp + sizeof(*p) > ep) \
+ break; \
+ p = (ipfw_dyn_rule *)bp; \
+ dyn_export_ ## aft ## _state(s, p); \
+ last = p; \
+ bp += sizeof(*p); \
}
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
- DYN_EXPORT_STATES(s4, ipv4, ipv4_parent, bucket);
- DYN_EXPORT_STATES(s4, ipv4, ipv4, bucket);
+ DYN_EXPORT_STATES(p4, ipv4_parent, bucket);
+ DYN_EXPORT_STATES(s4, ipv4, bucket);
#ifdef INET6
- DYN_EXPORT_STATES(s6, ipv6, ipv6_parent, bucket);
- DYN_EXPORT_STATES(s6, ipv6, ipv6, bucket);
+ DYN_EXPORT_STATES(p6, ipv6_parent, bucket);
+ DYN_EXPORT_STATES(s6, ipv6, bucket);
#endif /* INET6 */
}
@@ -3158,30 +3534,34 @@
V_dyn_keepalive = 1; /* send keepalives */
V_dyn_keepalive_last = time_uptime;
- V_dyn_data_zone = uma_zcreate("IPFW dynamic states data",
- sizeof(struct dyn_data), NULL, NULL, NULL, NULL,
- UMA_ALIGN_PTR, 0);
- uma_zone_set_max(V_dyn_data_zone, V_dyn_max);
-
- V_dyn_parent_zone = uma_zcreate("IPFW parent dynamic states",
- sizeof(struct dyn_parent), NULL, NULL, NULL, NULL,
- UMA_ALIGN_PTR, 0);
- uma_zone_set_max(V_dyn_parent_zone, V_dyn_parent_max);
+ SLIST_INIT(&V_dyn_expired);
- SLIST_INIT(&V_dyn_expired_ipv4);
V_dyn_ipv4 = NULL;
V_dyn_ipv4_parent = NULL;
- V_dyn_ipv4_zone = uma_zcreate("IPFW IPv4 dynamic states",
- sizeof(struct dyn_ipv4_state), NULL, NULL, NULL, NULL,
+
+ V_dyn_ipv4_zone = uma_zcreate("IPFW dynamic IPv4 states",
+ sizeof(dyn_ipv4_state), NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
+ uma_zone_set_max(V_dyn_ipv4_zone, V_dyn_max);
+
+ V_dyn_ipv4_parent_zone = uma_zcreate("IPFW parent dynamic IPv4 states",
+ sizeof(dyn_ipv4_parent_state), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, 0);
+ uma_zone_set_max(V_dyn_ipv4_parent_zone, V_dyn_parent_max);
#ifdef INET6
- SLIST_INIT(&V_dyn_expired_ipv6);
V_dyn_ipv6 = NULL;
V_dyn_ipv6_parent = NULL;
- V_dyn_ipv6_zone = uma_zcreate("IPFW IPv6 dynamic states",
- sizeof(struct dyn_ipv6_state), NULL, NULL, NULL, NULL,
+
+ V_dyn_ipv6_zone = uma_zcreate("IPFW dynamic IPv6 states",
+ sizeof(dyn_ipv6_state), NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
+ uma_zone_set_max(V_dyn_ipv6_zone, V_dyn_max);
+
+ V_dyn_ipv6_parent_zone = uma_zcreate("IPFW parent dynamic IPv6 states",
+ sizeof(dyn_ipv6_parent_state), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, 0);
+ uma_zone_set_max(V_dyn_ipv6_parent_zone, V_dyn_parent_max);
#endif
/* Initialize buckets. */
@@ -3202,10 +3582,7 @@
void
ipfw_dyn_uninit(int pass)
{
-#ifdef INET6
- struct dyn_ipv6_state *s6;
-#endif
- struct dyn_ipv4_state *s4;
+ dyn_state_hdr *h;
int bucket;
if (pass == 0) {
@@ -3215,39 +3592,30 @@
IPFW_DEL_OBJ_REWRITER(IS_DEFAULT_VNET(curvnet), dyn_opcodes);
DYN_EXPIRED_LOCK_DESTROY();
-#define DYN_FREE_STATES_FORCED(CK, s, af, name, en) do { \
- while ((s = CK ## SLIST_FIRST(&V_dyn_ ## name)) != NULL) { \
- CK ## SLIST_REMOVE_HEAD(&V_dyn_ ## name, en); \
- if (s->type == O_LIMIT_PARENT) \
- uma_zfree(V_dyn_parent_zone, s->limit); \
- else \
- uma_zfree(V_dyn_data_zone, s->data); \
- uma_zfree(V_dyn_ ## af ## _zone, s); \
+#define DYN_FREE_STATES_FORCED(CK, name, en) do { \
+ while ((h = CK ## SLIST_FIRST(&V_dyn_ ## name)) != NULL) { \
+ CK ## SLIST_REMOVE_HEAD(&V_dyn_ ## name, en); \
+ dyn_free_state(h); \
} \
} while (0)
for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) {
DYN_BUCKET_LOCK_DESTROY(V_dyn_bucket_lock, bucket);
- DYN_FREE_STATES_FORCED(CK_, s4, ipv4, ipv4[bucket], entry);
- DYN_FREE_STATES_FORCED(CK_, s4, ipv4, ipv4_parent[bucket],
- entry);
+ DYN_FREE_STATES_FORCED(CK_, ipv4[bucket], entry);
+ DYN_FREE_STATES_FORCED(CK_, ipv4_parent[bucket], entry);
#ifdef INET6
- DYN_FREE_STATES_FORCED(CK_, s6, ipv6, ipv6[bucket], entry);
- DYN_FREE_STATES_FORCED(CK_, s6, ipv6, ipv6_parent[bucket],
- entry);
+ DYN_FREE_STATES_FORCED(CK_, ipv6[bucket], entry);
+ DYN_FREE_STATES_FORCED(CK_, ipv6_parent[bucket], entry);
#endif /* INET6 */
}
- DYN_FREE_STATES_FORCED(, s4, ipv4, expired_ipv4, expired);
-#ifdef INET6
- DYN_FREE_STATES_FORCED(, s6, ipv6, expired_ipv6, expired);
-#endif
+ DYN_FREE_STATES_FORCED(, expired, expired);
#undef DYN_FREE_STATES_FORCED
uma_zdestroy(V_dyn_ipv4_zone);
- uma_zdestroy(V_dyn_data_zone);
- uma_zdestroy(V_dyn_parent_zone);
+ uma_zdestroy(V_dyn_ipv4_parent_zone);
#ifdef INET6
uma_zdestroy(V_dyn_ipv6_zone);
+ uma_zdestroy(V_dyn_ipv6_parent_zone);
free(V_dyn_ipv6, M_IPFW);
free(V_dyn_ipv6_parent, M_IPFW);
free(V_dyn_ipv6_add, M_IPFW);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 15, 1:03 AM (38 m, 38 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25309716
Default Alt Text
D20870.id81102.diff (84 KB)
Attached To
Mode
D20870: Refactor ipfw dynamic states in preparation for new features (and slighlty optimize)
Attached
Detach File
Event Timeline
Log In to Comment