Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F161608120
D39880.id144271.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
57 KB
Referenced Files
None
Subscribers
None
D39880.id144271.diff
View Options
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -884,6 +884,8 @@
SLIST_HEAD(pf_krule_slist, pf_krule_item);
+enum pf_sn_types { PF_SN_LIMIT, PF_SN_NAT, PF_SN_ROUTE, PF_SN_MAX };
+
struct pf_ksrc_node {
LIST_ENTRY(pf_ksrc_node) entry;
struct pf_addr addr;
@@ -900,6 +902,7 @@
u_int32_t expire;
sa_family_t af;
u_int8_t ruletype;
+ enum pf_sn_types type;
struct mtx *lock;
};
#endif
@@ -1098,8 +1101,8 @@
struct pfi_kkif *kif;
struct pfi_kkif *orig_kif; /* The real kif, even if we're a floating state (i.e. if == V_pfi_all). */
struct pfi_kkif *rt_kif;
- struct pf_ksrc_node *src_node;
- struct pf_ksrc_node *nat_src_node;
+ struct pf_ksrc_node *sns[PF_SN_MAX];/* source nodes */
+ struct pf_srchash *sh; /* source nodes hash row */
u_int64_t packets[2];
u_int64_t bytes[2];
u_int64_t creation;
@@ -1113,9 +1116,10 @@
};
/*
- * Size <= fits 11 objects per page on LP64. Try to not grow the struct beyond that.
+* 6 cache lines per struct, 11 structs per page.
+* Try to not grow the struct beyond that.
*/
-_Static_assert(sizeof(struct pf_kstate) <= 372, "pf_kstate size crosses 372 bytes");
+_Static_assert(sizeof(struct pf_kstate) <= 384, "pf_kstate size crosses 384 bytes");
#endif
/*
@@ -1584,6 +1588,13 @@
struct pf_sctp_multihome_job;
TAILQ_HEAD(pf_sctp_multihome_jobs, pf_sctp_multihome_job);
+/* Variables available only during ruleset evaluation */
+struct pf_test_ctx {
+ struct pf_ksrc_node *sns[PF_SN_MAX];/* source nodes */
+ struct pf_srchash *sh; /* source nodes hash row */
+};
+
+/* Variables available during packet forwarding */
struct pf_pdesc {
struct {
int done;
@@ -1602,13 +1613,13 @@
char any[0];
} hdr;
- struct pf_krule *nat_rule; /* nat/rdr rule applied to packet */
- struct pf_addr *src; /* src address */
- struct pf_addr *dst; /* dst address */
- u_int16_t *sport;
- u_int16_t *dport;
- struct pf_mtag *pf_mtag;
- struct pf_rule_actions act;
+ struct pf_krule *nat_rule; /* nat/rdr rule applied to packet */
+ struct pf_addr *src; /* src address */
+ struct pf_addr *dst; /* dst address */
+ u_int16_t *sport;
+ u_int16_t *dport;
+ struct pf_mtag *pf_mtag;
+ struct pf_rule_actions act;
u_int32_t p_len; /* total length of payload */
u_int32_t badopts; /* v4 options or v6 routing headers */
@@ -2333,9 +2344,12 @@
*mapping);
extern void pf_udp_mapping_release(struct pf_udp_mapping
*mapping);
-extern struct pf_ksrc_node *pf_find_src_node(struct pf_addr *,
- struct pf_krule *, sa_family_t,
- struct pf_srchash **, bool);
+u_short pf_find_src_node(struct pf_test_ctx *,
+ struct pf_pdesc *, struct pf_addr *,
+ struct pf_krule *, enum pf_sn_types);
+u_short pf_insert_src_node(struct pf_test_ctx *,
+ struct pf_pdesc *pd, struct pf_addr *,
+ struct pf_krule *, enum pf_sn_types);
extern void pf_unlink_src_node(struct pf_ksrc_node *);
extern u_int pf_free_src_nodes(struct pf_ksrc_node_list *);
extern void pf_print_state(struct pf_kstate *);
@@ -2619,14 +2633,15 @@
struct pf_keth_rule **, struct pf_keth_rule **,
int *);
-u_short pf_map_addr(u_int8_t, struct pf_krule *,
+u_short pf_map_addr(struct pf_pdesc *, struct pf_krule *,
struct pf_addr *, struct pf_addr *,
struct pfi_kkif **nkif, struct pf_addr *);
-u_short pf_map_addr_sn(u_int8_t, struct pf_krule *,
- struct pf_addr *, struct pf_addr *,
- struct pfi_kkif **nkif, struct pf_addr *,
- struct pf_ksrc_node **);
-u_short pf_get_translation(struct pf_pdesc *, struct mbuf *,
+u_short pf_map_addr_sn(struct pf_test_ctx *, struct pf_pdesc *,
+ struct pf_krule *, struct pf_addr *,
+ struct pf_addr *, struct pfi_kkif **nkif,
+ struct pf_addr *, enum pf_sn_types);
+u_short pf_get_translation(struct pf_test_ctx *,
+ struct pf_pdesc *, struct mbuf *,
int, struct pfi_kkif *, struct pf_ksrc_node **,
struct pf_state_key **, struct pf_state_key **,
struct pf_addr *, struct pf_addr *,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -323,13 +323,14 @@
struct pfi_kkif *, struct mbuf *, int,
struct pf_pdesc *, struct pf_krule **,
struct pf_kruleset **, struct inpcb *, int);
-static int pf_create_state(struct pf_krule *, struct pf_krule *,
+static int pf_create_state(struct pf_test_ctx *,
+ struct pf_krule *, struct pf_krule *,
struct pf_krule *, struct pf_pdesc *,
- struct pf_ksrc_node *, struct pf_state_key *,
- struct pf_state_key *, struct mbuf *, int,
- u_int16_t, u_int16_t, int *, struct pfi_kkif *,
- struct pf_kstate **, int, u_int16_t, u_int16_t,
- int, struct pf_krule_slist *, struct pf_udp_mapping *);
+ struct pf_state_key *, struct pf_state_key *,
+ struct mbuf *, int, u_int16_t, u_int16_t, int *,
+ struct pfi_kkif *, struct pf_kstate **, int,
+ u_int16_t, u_int16_t, int, struct pf_krule_slist *,
+ struct pf_udp_mapping *);
static int pf_state_key_addr_setup(struct pf_pdesc *, struct mbuf *,
int, struct pf_state_key_cmp *, int, struct pf_addr *,
int, struct pf_addr *, int);
@@ -369,15 +370,15 @@
bool, u_int8_t);
static struct pf_kstate *pf_find_state(struct pfi_kkif *,
const struct pf_state_key_cmp *, u_int);
-static int pf_src_connlimit(struct pf_kstate **);
+static bool pf_find_src_node_ptr(struct pf_srchash *,
+ struct pf_ksrc_node *);
+static int pf_src_connlimit(struct pf_kstate *);
static int pf_match_rcvif(struct mbuf *, struct pf_krule *);
static void pf_counters_inc(int,
struct pf_pdesc *, struct pfi_kkif *,
struct pf_kstate *, struct pf_krule *,
struct pf_krule *);
static void pf_overload_task(void *v, int pending);
-static u_short pf_insert_src_node(struct pf_ksrc_node **,
- struct pf_krule *, struct pf_addr *, sa_family_t);
static u_int pf_purge_expired_states(u_int, int);
static void pf_purge_unlinked_rules(void);
static int pf_mtag_uminit(void *, int, int);
@@ -813,59 +814,71 @@
}
static int
-pf_src_connlimit(struct pf_kstate **state)
+pf_src_connlimit(struct pf_kstate *state)
{
struct pf_overload_entry *pfoe;
+ struct pf_ksrc_node *sn = state->sns[PF_SN_LIMIT];
int bad = 0;
+ int ret = 1;
- PF_STATE_LOCK_ASSERT(*state);
- /*
- * XXXKS: The src node is accessed unlocked!
- * PF_SRC_NODE_LOCK_ASSERT((*state)->src_node);
- */
+ PF_STATE_LOCK_ASSERT(state);
+ KASSERT(sn != NULL,
+ ("pf_src_connlimit: state->sns[PF_SN_LIMIT] == NULL"));
- (*state)->src_node->conn++;
- (*state)->src.tcp_est = 1;
- pf_add_threshold(&(*state)->src_node->conn_rate);
+ if (!pf_find_src_node_ptr(state->sh, sn)) {
+ ret = 0;
+ goto done;
+ }
- if ((*state)->rule.ptr->max_src_conn &&
- (*state)->rule.ptr->max_src_conn <
- (*state)->src_node->conn) {
+ sn->conn++;
+ state->src.tcp_est = 1;
+ pf_add_threshold(&(sn->conn_rate));
+
+ if (state->rule.ptr->max_src_conn &&
+ state->rule.ptr->max_src_conn < sn->conn) {
counter_u64_add(V_pf_status.lcounters[LCNT_SRCCONN], 1);
bad++;
}
- if ((*state)->rule.ptr->max_src_conn_rate.limit &&
- pf_check_threshold(&(*state)->src_node->conn_rate)) {
+ if (state->rule.ptr->max_src_conn_rate.limit &&
+ pf_check_threshold(&(sn->conn_rate))) {
counter_u64_add(V_pf_status.lcounters[LCNT_SRCCONNRATE], 1);
bad++;
}
- if (!bad)
- return (0);
+ if (!bad) {
+ ret = 0;
+ goto done;
+ }
/* Kill this state. */
- (*state)->timeout = PFTM_PURGE;
- pf_set_protostate(*state, PF_PEER_BOTH, TCPS_CLOSED);
+ state->timeout = PFTM_PURGE;
+ pf_set_protostate(state, PF_PEER_BOTH, TCPS_CLOSED);
- if ((*state)->rule.ptr->overload_tbl == NULL)
- return (1);
+ if (state->rule.ptr->overload_tbl == NULL) {
+ ret = 1;
+ goto done;
+ }
/* Schedule overloading and flushing task. */
pfoe = malloc(sizeof(*pfoe), M_PFTEMP, M_NOWAIT);
- if (pfoe == NULL)
- return (1); /* too bad :( */
+ if (pfoe == NULL) {
+ ret = 1; /* too bad :( */
+ goto done;
+ }
- bcopy(&(*state)->src_node->addr, &pfoe->addr, sizeof(pfoe->addr));
- pfoe->af = (*state)->key[PF_SK_WIRE]->af;
- pfoe->rule = (*state)->rule.ptr;
- pfoe->dir = (*state)->direction;
+ bcopy(&sn->addr, &pfoe->addr, sizeof(pfoe->addr));
+ pfoe->af = state->key[PF_SK_WIRE]->af;
+ pfoe->rule = state->rule.ptr;
+ pfoe->dir = state->direction;
PF_OVERLOADQ_LOCK();
SLIST_INSERT_HEAD(&V_pf_overloadqueue, pfoe, next);
PF_OVERLOADQ_UNLOCK();
taskqueue_enqueue(taskqueue_swi, &V_pf_overloadtask);
- return (1);
+done:
+ PF_HASHROW_UNLOCK(state->sh);
+ return ret;
}
static void
@@ -961,33 +974,61 @@
CURVNET_RESTORE();
}
-/*
- * Can return locked on failure, so that we can consistently
- * allocate and insert a new one.
- */
-struct pf_ksrc_node *
-pf_find_src_node(struct pf_addr *src, struct pf_krule *rule, sa_family_t af,
- struct pf_srchash **sh, bool returnlocked)
+u_short
+pf_find_src_node(struct pf_test_ctx *ctx, struct pf_pdesc *pd,
+ struct pf_addr *src, struct pf_krule *rule, enum pf_sn_types sn_type)
{
- struct pf_ksrc_node *n;
+ struct pf_ksrc_node *cur;
counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_SEARCH], 1);
- *sh = &V_pf_srchash[pf_hashsrc(src, af)];
- PF_HASHROW_LOCK(*sh);
- LIST_FOREACH(n, &(*sh)->nodes, entry)
- if (n->rule.ptr == rule && n->af == af &&
- ((af == AF_INET && n->addr.v4.s_addr == src->v4.s_addr) ||
- (af == AF_INET6 && bcmp(&n->addr, src, sizeof(*src)) == 0)))
- break;
+ /* First search of src node sets ctx->sh and locks it */
+ if (ctx->sh == NULL) {
+ ctx->sh = &V_pf_srchash[pf_hashsrc(src, pd->af)];
+ PF_HASHROW_LOCK(ctx->sh);
+ } else {
+ PF_HASHROW_ASSERT(ctx->sh);
+#ifdef INVARIANTS
+ if (ctx->sh != &V_pf_srchash[pf_hashsrc(src, pd->af)])
+ panic("%s: source node hash mismatch", __func__);
+#endif
+ }
- if (n != NULL) {
- n->states++;
- PF_HASHROW_UNLOCK(*sh);
- } else if (returnlocked == false)
- PF_HASHROW_UNLOCK(*sh);
+ LIST_FOREACH(cur, &(ctx->sh->nodes), entry) {
+ if (cur->rule.ptr == rule &&
+ cur->af == pd->af &&
+ cur->type == sn_type &&
+ ((pd->af == AF_INET &&
+ cur->addr.v4.s_addr == pd->src->v4.s_addr) ||
+ (pd->af == AF_INET6 && bcmp(&(cur->addr), pd->src,
+ sizeof(*(pd->src))) == 0)) &&
+ cur->expire != 1
+ ) {
+ cur->expire = (rule->timeout[PFTM_SRC_NODE] ?
+ rule->timeout[PFTM_SRC_NODE] :
+ V_pf_default_rule.timeout[PFTM_SRC_NODE]) + time_uptime;
+ ctx->sns[sn_type] = cur;
+ return (1);
+ }
+ }
- return (n);
+ return (0);
+}
+
+static bool
+pf_find_src_node_ptr(struct pf_srchash *sh, struct pf_ksrc_node *sn)
+{
+ struct pf_ksrc_node *cur;
+
+ KASSERT(sh != NULL, ("%s: sh is NULL", __func__));
+
+ counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_SEARCH], 1);
+ PF_HASHROW_LOCK(sh);
+ LIST_FOREACH(cur, &(sh->nodes), entry) {
+ if (cur == sn && cur->expire != 1)
+ return true;
+ }
+ return false;
}
static void
@@ -1001,81 +1042,122 @@
uma_zfree(V_pf_sources_z, sn);
}
-static u_short
-pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_krule *rule,
- struct pf_addr *src, sa_family_t af)
+u_short
+pf_insert_src_node(struct pf_test_ctx *ctx, struct pf_pdesc *pd,
+ struct pf_addr *src, struct pf_krule *rule, enum pf_sn_types sn_type)
{
- u_short reason = 0;
- struct pf_srchash *sh = NULL;
+ u_short reason = 0;
- KASSERT((rule->rule_flag & PFRULE_SRCTRACK ||
- rule->rpool.opts & PF_POOL_STICKYADDR),
+ KASSERT((rule->nr == -1 || rule->rule_flag & PFRULE_SRCTRACK ||
+ rule->rpool.opts & PF_POOL_STICKYADDR),
("%s for non-tracking rule %p", __func__, rule));
- if (*sn == NULL)
- *sn = pf_find_src_node(src, rule, af, &sh, true);
+ /*
+ * This function must be used after pf_find_src_node()
+ * which will set ctx->sh.
+ */
+ PF_HASHROW_ASSERT(ctx->sh);
- if (*sn == NULL) {
- PF_HASHROW_ASSERT(sh);
+ KASSERT(ctx->sns[sn_type] == NULL, ("%s: ctx->sns[%d] not NULL",
+ __func__, sn_type));
- if (rule->max_src_nodes &&
- counter_u64_fetch(rule->src_nodes) >= rule->max_src_nodes) {
- counter_u64_add(V_pf_status.lcounters[LCNT_SRCNODES], 1);
- PF_HASHROW_UNLOCK(sh);
- reason = PFRES_SRCLIMIT;
- goto done;
- }
+ ctx->sns[sn_type] = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO);
+ if (ctx->sns[sn_type] == NULL) {
+ reason = PFRES_MEMORY;
+ goto done;
+ }
- (*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO);
- if ((*sn) == NULL) {
- PF_HASHROW_UNLOCK(sh);
+ for (int i = 0; i < 2; i++) {
+ ctx->sns[sn_type]->bytes[i] = counter_u64_alloc(M_NOWAIT);
+ ctx->sns[sn_type]->packets[i] = counter_u64_alloc(M_NOWAIT);
+
+ if (ctx->sns[sn_type]->bytes[i] == NULL ||
+ ctx->sns[sn_type]->packets[i] == NULL) {
+ pf_free_src_node(ctx->sns[sn_type]);
reason = PFRES_MEMORY;
+ ctx->sns[sn_type] = NULL;
goto done;
}
+ }
- for (int i = 0; i < 2; i++) {
- (*sn)->bytes[i] = counter_u64_alloc(M_NOWAIT);
- (*sn)->packets[i] = counter_u64_alloc(M_NOWAIT);
+ pf_init_threshold(&(ctx->sns[sn_type])->conn_rate,
+ rule->max_src_conn_rate.limit,
+ rule->max_src_conn_rate.seconds);
- if ((*sn)->bytes[i] == NULL || (*sn)->packets[i] == NULL) {
- pf_free_src_node(*sn);
- PF_HASHROW_UNLOCK(sh);
- reason = PFRES_MEMORY;
- goto done;
- }
- }
+ MPASS(ctx->sns[sn_type]->lock == NULL);
+ ctx->sns[sn_type]->lock = &(ctx->sh->lock);
- pf_init_threshold(&(*sn)->conn_rate,
- rule->max_src_conn_rate.limit,
- rule->max_src_conn_rate.seconds);
+ ctx->sns[sn_type]->af = pd->af;
+ ctx->sns[sn_type]->rule.ptr = rule;
+ PF_ACPY(&(ctx->sns[sn_type]->addr), src, pd->af);
+ LIST_INSERT_HEAD(&(ctx->sh->nodes), ctx->sns[sn_type], entry);
+ ctx->sns[sn_type]->creation = time_uptime;
+ ctx->sns[sn_type]->ruletype = rule->action;
+ ctx->sns[sn_type]->type = sn_type;
+ ctx->sns[sn_type]->expire = (rule->timeout[PFTM_SRC_NODE] ?
+ rule->timeout[PFTM_SRC_NODE] :
+ V_pf_default_rule.timeout[PFTM_SRC_NODE]) + time_uptime;
- MPASS((*sn)->lock == NULL);
- (*sn)->lock = &sh->lock;
+ counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_INSERT], 1);
- (*sn)->af = af;
- (*sn)->rule.ptr = rule;
- PF_ACPY(&(*sn)->addr, src, af);
- LIST_INSERT_HEAD(&sh->nodes, *sn, entry);
- (*sn)->creation = time_uptime;
- (*sn)->ruletype = rule->action;
- (*sn)->states = 1;
- if ((*sn)->rule.ptr != NULL)
- counter_u64_add((*sn)->rule.ptr->src_nodes, 1);
- PF_HASHROW_UNLOCK(sh);
- counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_INSERT], 1);
- } else {
- if (rule->max_src_states &&
- (*sn)->states >= rule->max_src_states) {
- counter_u64_add(V_pf_status.lcounters[LCNT_SRCSTATES],
+done:
+ /* Returns locked */
+ return (reason);
+}
+
+static u_short
+pf_check_source_limits(struct pf_test_ctx *ctx, struct pf_pdesc *pd,
+ struct pf_krule *rule)
+{
+ struct pf_krule *r_track = rule;
+ u_short reason = 0;
+
+ /*
+ * Only src tracking uses this function! Load balancing code uses
+ * pf_map_addr_sn() which calls the low level functions directly.
+ */
+ KASSERT(rule->rule_flag & PFRULE_SRCTRACK,
+ ("%s for non-tracking rule %p", __func__, rule));
+
+ /*
+ * Rules with global source tracking store the counters of connected
+ * sources and their states in the default rule.
+ */
+ if (!(rule->rule_flag & PFRULE_RULESRCTRACK))
+ r_track = &V_pf_default_rule;
+
+ pf_find_src_node(ctx, pd, pd->src, r_track, PF_SN_LIMIT);
+
+ PF_HASHROW_ASSERT(ctx->sh); /* set by pf_find_src_node() */
+
+ if (ctx->sns[PF_SN_LIMIT] == NULL) {
+ if (rule->max_src_nodes &&
+ counter_u64_fetch(r_track->src_nodes) >= rule->max_src_nodes) {
+ counter_u64_add(V_pf_status.lcounters[LCNT_SRCNODES],
1);
reason = PFRES_SRCLIMIT;
goto done;
}
+ if ((reason = pf_insert_src_node(ctx, pd, pd->src, r_track, PF_SN_LIMIT)) != 0)
+ goto done;
+ if (ctx->sns[PF_SN_LIMIT]->rule.ptr != NULL)
+ counter_u64_add(ctx->sns[PF_SN_LIMIT]->rule.ptr->src_nodes, 1);
+ } else {
+ if (rule->max_src_states &&
+ ctx->sns[PF_SN_LIMIT]->states >= rule->max_src_states) {
+ counter_u64_add(V_pf_status.lcounters[LCNT_SRCSTATES],
+ 1);
+ reason = PFRES_MAXSTATES;
+ goto done;
+ }
}
+
done:
+ PF_HASHROW_UNLOCK(ctx->sh);
return (reason);
}
+
void
pf_unlink_src_node(struct pf_ksrc_node *src)
{
@@ -2533,32 +2615,33 @@
static void
pf_src_tree_remove_state(struct pf_kstate *s)
{
- struct pf_ksrc_node *sn;
- uint32_t timeout;
+ enum pf_sn_types sn_type;
+ uint32_t timeout;
+
+ PF_STATE_LOCK_ASSERT(s);
timeout = s->rule.ptr->timeout[PFTM_SRC_NODE] ?
s->rule.ptr->timeout[PFTM_SRC_NODE] :
V_pf_default_rule.timeout[PFTM_SRC_NODE];
- if (s->src_node != NULL) {
- sn = s->src_node;
- PF_SRC_NODE_LOCK(sn);
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (s->sns[sn_type] == NULL ||
+ !pf_find_src_node_ptr(s->sh, s->sns[sn_type])
+ )
+ continue;
+
if (s->src.tcp_est)
- --sn->conn;
- if (--sn->states == 0)
- sn->expire = time_uptime + timeout;
- PF_SRC_NODE_UNLOCK(sn);
+ --(s->sns[sn_type]->conn);
+ /* Last removed state sets the expiry of the src node */
+ if (--(s->sns[sn_type]->states) == 0)
+ s->sns[sn_type]->expire = time_uptime + timeout;
+ PF_SRC_NODE_UNLOCK(s->sns[sn_type]);
+ s->sns[sn_type] = NULL;
}
- if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) {
- sn = s->nat_src_node;
- PF_SRC_NODE_LOCK(sn);
- if (--sn->states == 0)
- sn->expire = time_uptime + timeout;
- PF_SRC_NODE_UNLOCK(sn);
- }
- s->src_node = s->nat_src_node = NULL;
+
}
+
/*
* Unlink and potentilly free a state. Function may be
* called with ID hash row locked, but always returns
@@ -4887,6 +4970,7 @@
struct mbuf *m, int off, struct pf_pdesc *pd, struct pf_krule **am,
struct pf_kruleset **rsm, struct inpcb *inp, int hdrlen)
{
+ struct pf_test_ctx ctx;
struct pf_krule *nr = NULL;
struct pf_addr * const saddr = pd->src;
struct pf_addr * const daddr = pd->dst;
@@ -4898,7 +4982,7 @@
struct pf_ksrc_node *nsn = NULL;
struct tcphdr *th = &pd->hdr.tcp;
struct pf_state_key *sk = NULL, *nk = NULL;
- u_short reason, transerror;
+ u_short reason, transerror, sn_reason = 0;
int rewrite = 0;
int tag = -1;
int asd = 0;
@@ -4912,6 +4996,7 @@
PF_RULES_RASSERT();
+ memset(&ctx, 0, sizeof(ctx));
SLIST_INIT(&match_rules);
if (inp != NULL) {
@@ -4975,7 +5060,7 @@
r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_FILTER].active.ptr);
/* check packet for BINAT/NAT/RDR */
- transerror = pf_get_translation(pd, m, off, kif, &nsn, &sk,
+ transerror = pf_get_translation(&ctx, pd, m, off, kif, &nsn, &sk,
&nk, saddr, daddr, sport, dport, anchor_stack, &nr, &udp_mapping);
switch (transerror) {
default:
@@ -5308,7 +5393,27 @@
(!state_icmp && (r->keep_state || nr != NULL ||
(pd->flags & PFDESC_TCP_NORM)))) {
int action;
- action = pf_create_state(r, nr, a, pd, nsn, nk, sk, m, off,
+
+ /*
+ * If this rule has source tracking, find or create a source
+ * node of PF_SN_LIMIT type. This source node will be used for
+ * providing connection limit per source.
+ */
+ if (r->rule_flag & PFRULE_SRCTRACK) {
+ if ((sn_reason = pf_check_source_limits(&ctx, pd, r)) != 0) {
+ REASON_SET(&reason, sn_reason);
+ goto cleanup;
+ }
+ }
+
+ if (r->max_states &&
+ (counter_u64_fetch(r->states_cur) > r->max_states)) {
+ counter_u64_add(V_pf_status.lcounters[LCNT_STATES], 1);
+ REASON_SET(&reason, PFRES_MAXSTATES);
+ goto cleanup;
+ }
+
+ action = pf_create_state(&ctx, r, nr, a, pd, nk, sk, m, off,
sport, dport, &rewrite, kif, sm, tag, bproto_sum, bip_sum,
hdrlen, &match_rules, udp_mapping);
if (action != PF_PASS) {
@@ -5362,42 +5467,22 @@
}
static int
-pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
- struct pf_pdesc *pd, struct pf_ksrc_node *nsn, struct pf_state_key *nk,
- struct pf_state_key *sk, struct mbuf *m, int off, u_int16_t sport,
- u_int16_t dport, int *rewrite, struct pfi_kkif *kif, struct pf_kstate **sm,
- int tag, u_int16_t bproto_sum, u_int16_t bip_sum, int hdrlen,
- struct pf_krule_slist *match_rules, struct pf_udp_mapping *udp_mapping)
+pf_create_state(struct pf_test_ctx *ctx, struct pf_krule *r,
+ struct pf_krule *nr, struct pf_krule *a,
+ struct pf_pdesc *pd, struct pf_state_key *nk, struct pf_state_key *sk,
+ struct mbuf *m, int off, u_int16_t sport, u_int16_t dport, int *rewrite,
+ struct pfi_kkif *kif, struct pf_kstate **sm, int tag, u_int16_t bproto_sum,
+ u_int16_t bip_sum, int hdrlen, struct pf_krule_slist *match_rules,
+ struct pf_udp_mapping *udp_mapping)
{
struct pf_kstate *s = NULL;
- struct pf_ksrc_node *sn = NULL;
struct tcphdr *th = &pd->hdr.tcp;
u_int16_t mss = V_tcp_mssdflt;
- u_short reason, sn_reason;
+ u_short reason;
struct pf_krule_item *ri;
+ enum pf_sn_types sn_type;
- /* check maximums */
- if (r->max_states &&
- (counter_u64_fetch(r->states_cur) >= r->max_states)) {
- counter_u64_add(V_pf_status.lcounters[LCNT_STATES], 1);
- REASON_SET(&reason, PFRES_MAXSTATES);
- goto csfailed;
- }
- /* src node for filter rule */
- if ((r->rule_flag & PFRULE_SRCTRACK ||
- r->rpool.opts & PF_POOL_STICKYADDR) &&
- (sn_reason = pf_insert_src_node(&sn, r, pd->src, pd->af)) != 0) {
- REASON_SET(&reason, sn_reason);
- goto csfailed;
- }
- /* src node for translation rule */
- if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
- (sn_reason = pf_insert_src_node(&nsn, nr, &sk->addr[pd->sidx],
- pd->af)) != 0 ) {
- REASON_SET(&reason, sn_reason);
- goto csfailed;
- }
- s = pf_alloc_state(M_NOWAIT);
+ s = pf_alloc_state(M_NOWAIT);
if (s == NULL) {
REASON_SET(&reason, PFRES_MEMORY);
goto csfailed;
@@ -5486,21 +5571,46 @@
if (r->rt) {
/* pf_map_addr increases the reason counters */
- if ((reason = pf_map_addr_sn(pd->af, r, pd->src, &s->rt_addr,
- &s->rt_kif, NULL, &sn)) != 0)
+ if ((reason = pf_map_addr_sn(ctx, pd, r, pd->src, &s->rt_addr,
+ &s->rt_kif, NULL, PF_SN_ROUTE)) != 0)
goto csfailed;
s->rt = r->rt;
}
s->creation = s->expire = pf_get_uptime();
- if (sn != NULL)
- s->src_node = sn;
- if (nsn != NULL) {
- /* XXX We only modify one side for now. */
- PF_ACPY(&nsn->raddr, &nk->addr[1], pd->af);
- s->nat_src_node = nsn;
+ /*
+ * Source nodes might have been inserted or found by:
+ * - NAT rules: pf_test() -> pf_get_translation() -> pf_map_addr_sn()
+ * - route-to rules: pf_map_addr_sn() just above
+ * - source limit tracking: pf_check_source_limits() before pf_create_state()
+ *
+ * Each of those operations sets ctx->sns[sn_type], so that we can use
+ * them now. However, those operations unlock the hash row ctx->sh.
+ * We must check if maybe source nodes have been removed in the meantime.
+ */
+ s->sh = ctx->sh;
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ /*
+ * Check if the source node has ever been allocated
+ * and is still in its hash row.
+ */
+ if (ctx->sns[sn_type] == NULL ||
+ !pf_find_src_node_ptr(ctx->sh, ctx->sns[sn_type])
+ )
+ continue;
+
+ if (sn_type == PF_SN_NAT) {
+ /* XXX We only modify one side for now. */
+ PF_ACPY(&(ctx->sns[sn_type]->raddr),
+ &nk->addr[1], pd->af);
+ }
+
+ ctx->sns[sn_type]->states++;
+ s->sns[sn_type] = ctx->sns[sn_type];
+ PF_HASHROW_UNLOCK(ctx->sh);
}
+
if (pd->proto == IPPROTO_TCP) {
if (s->state_flags & PFSTATE_SCRUB_TCP &&
pf_normalize_tcp_init(m, off, pd, th, &s->src, &s->dst)) {
@@ -5597,27 +5707,7 @@
uma_zfree(V_pf_state_key_z, sk);
uma_zfree(V_pf_state_key_z, nk);
- if (sn != NULL) {
- PF_SRC_NODE_LOCK(sn);
- if (--sn->states == 0 && sn->expire == 0) {
- pf_unlink_src_node(sn);
- uma_zfree(V_pf_sources_z, sn);
- counter_u64_add(
- V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
- }
- PF_SRC_NODE_UNLOCK(sn);
- }
-
- if (nsn != sn && nsn != NULL) {
- PF_SRC_NODE_LOCK(nsn);
- if (--nsn->states == 0 && nsn->expire == 0) {
- pf_unlink_src_node(nsn);
- uma_zfree(V_pf_sources_z, nsn);
- counter_u64_add(
- V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
- }
- PF_SRC_NODE_UNLOCK(nsn);
- }
+ /* XXX KS: re-introduce src node removal */
drop:
if (s != NULL) {
@@ -5831,8 +5921,8 @@
pf_set_protostate(*state, pdst,
TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
- (*state)->src_node != NULL &&
- pf_src_connlimit(state)) {
+ (*state)->sns[PF_SN_LIMIT] != NULL &&
+ pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
@@ -6002,8 +6092,8 @@
if (dst->state == TCPS_SYN_SENT) {
pf_set_protostate(*state, pdst, TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
- (*state)->src_node != NULL &&
- pf_src_connlimit(state)) {
+ (*state)->sns[PF_SN_LIMIT] &&
+ pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
@@ -6020,8 +6110,8 @@
pf_set_protostate(*state, PF_PEER_BOTH,
TCPS_ESTABLISHED);
dst->state = src->state = TCPS_ESTABLISHED;
- if ((*state)->src_node != NULL &&
- pf_src_connlimit(state)) {
+ if ((*state)->sns[PF_SN_LIMIT] != NULL &&
+ pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
}
@@ -6087,8 +6177,8 @@
(ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_DROP);
- } else if ((*state)->src_node != NULL &&
- pf_src_connlimit(state)) {
+ } else if ((*state)->sns[PF_SN_LIMIT] != NULL &&
+ pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
} else
@@ -7790,7 +7880,7 @@
("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__));
goto bad_locked;
}
- pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src,
+ pf_map_addr(pd, r, (struct pf_addr *)&ip->ip_src,
&naddr, &nkif, NULL);
if (!PF_AZERO(&naddr, AF_INET))
dst.sin_addr.s_addr = naddr.v4.s_addr;
@@ -8038,7 +8128,7 @@
("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__));
goto bad_locked;
}
- pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src,
+ pf_map_addr(pd, r, (struct pf_addr *)&ip6->ip6_src,
&naddr, &nkif, NULL);
if (!PF_AZERO(&naddr, AF_INET6))
PF_ACPY((struct pf_addr *)&dst.sin6_addr,
@@ -8802,17 +8892,21 @@
pf_counter_u64_add_protected(&s->nat_rule.ptr->bytes[dirndx],
pd->tot_len);
}
- if (s->src_node != NULL) {
- counter_u64_add(s->src_node->packets[dirndx],
- 1);
- counter_u64_add(s->src_node->bytes[dirndx],
- pd->tot_len);
- }
- if (s->nat_src_node != NULL) {
- counter_u64_add(s->nat_src_node->packets[dirndx],
- 1);
- counter_u64_add(s->nat_src_node->bytes[dirndx],
- pd->tot_len);
+ /*
+ * Source nodes are accessed unlocked here.
+ * But since we are operating with stateful tracking
+ * and the state is locked, those SNs could not have
+ * been freed.
+ */
+ for (enum pf_sn_types sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (s->sns[sn_type] != NULL) {
+ counter_u64_add(
+ s->sns[sn_type]->packets[dirndx],
+ 1);
+ counter_u64_add(
+ s->sns[sn_type]->bytes[dirndx],
+ pd->tot_len);
+ }
}
dirndx = (dir == s->direction) ? 0 : 1;
s->packets[dirndx]++;
@@ -8924,12 +9018,28 @@
switch (af) {
#ifdef INET
case AF_INET:
+ /* Packet normalization, reassembly, legacy scrub ruleset */
+ if (pf_normalize_ip(&m, kif, reason, pd) != PF_PASS) {
+ if (m == NULL) {
+ /* if packet sits in reassembly queue, return without error */
+ return PF_PASS;
+ }
+ return PF_DROP;
+ }
h = mtod(m, struct ip *);
ttl = h->ip_ttl;
break;
#endif
#ifdef INET6
case AF_INET6:
+ /* Packet normalization, reassembly, legacy scrub ruleset */
+ if (pf_normalize_ip6(&m, kif, reason, pd) != PF_PASS) {
+ if (m == NULL) {
+ /* if packet sits in reassembly queue, return without error */
+ return PF_PASS;
+ }
+ return PF_DROP;
+ }
h6 = mtod(m, struct ip6_hdr *);
ttl = h6->ip6_hlim;
break;
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -5548,6 +5548,7 @@
void
pfsync_state_export(union pfsync_state_union *sp, struct pf_kstate *st, int msg_version)
{
+ enum pf_sn_types sn_type;
bzero(sp, sizeof(union pfsync_state_union));
/* copy from state key */
@@ -5603,10 +5604,14 @@
__func__, msg_version);
}
- if (st->src_node)
- sp->pfs_1301.sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (st->nat_src_node)
- sp->pfs_1301.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (st->sns[sn_type] == NULL)
+ continue;
+ if (sn_type == PF_SN_LIMIT || sn_type == PF_SN_ROUTE)
+ sp->pfs_1301.sync_flags |= PFSYNC_FLAG_SRCNODE;
+ else
+ sp->pfs_1301.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ }
sp->pfs_1301.id = st->id;
sp->pfs_1301.creatorid = st->creatorid;
@@ -5635,6 +5640,7 @@
void
pf_state_export(struct pf_state_export *sp, struct pf_kstate *st)
{
+ enum pf_sn_types sn_type;
bzero(sp, sizeof(*sp));
sp->version = PF_STATE_VERSION;
@@ -5669,10 +5675,14 @@
/* 8 bits for the old libpfctl, 16 bits for the new libpfctl */
sp->state_flags_compat = st->state_flags;
sp->state_flags = htons(st->state_flags);
- if (st->src_node)
- sp->sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (st->nat_src_node)
- sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (st->sns[sn_type] == NULL)
+ continue;
+ if (sn_type == PF_SN_LIMIT || sn_type == PF_SN_ROUTE)
+ sp->sync_flags |= PFSYNC_FLAG_SRCNODE;
+ else
+ sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ }
sp->id = st->id;
sp->creatorid = st->creatorid;
@@ -5909,6 +5919,7 @@
struct pf_kstate *s;
struct pf_srchash *sh;
struct pf_ksrc_node *sn;
+ enum pf_sn_types sn_type;
int i;
for (i = 0; i <= V_pf_hashmask; i++) {
@@ -5916,8 +5927,9 @@
PF_HASHROW_LOCK(ih);
LIST_FOREACH(s, &ih->states, entry) {
- s->src_node = NULL;
- s->nat_src_node = NULL;
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ s->sns[sn_type] = NULL;
+ }
}
PF_HASHROW_UNLOCK(ih);
}
@@ -5937,6 +5949,7 @@
pf_kill_srcnodes(struct pfioc_src_node_kill *psnk)
{
struct pf_ksrc_node_list kill;
+ enum pf_sn_types sn_type;
LIST_INIT(&kill);
for (int i = 0; i <= V_pf_srchashmask; i++) {
@@ -5966,10 +5979,16 @@
PF_HASHROW_LOCK(ih);
LIST_FOREACH(s, &ih->states, entry) {
- if (s->src_node && s->src_node->expire == 1)
- s->src_node = NULL;
- if (s->nat_src_node && s->nat_src_node->expire == 1)
- s->nat_src_node = NULL;
+ for(sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ /*
+ * It's safe to check sn->expire, the SN
+ * has been unlinked while it was locked.
+ */
+ if (s->sns[sn_type] &&
+ s->sns[sn_type]->expire == 1) {
+ s->sns[sn_type] = NULL;
+ }
+ }
}
PF_HASHROW_UNLOCK(ih);
}
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -67,10 +67,11 @@
int, struct pfi_kkif *,
struct pf_addr *, u_int16_t, struct pf_addr *,
uint16_t, int, struct pf_kanchor_stackframe *);
-static int pf_get_sport(sa_family_t, uint8_t, struct pf_krule *,
- struct pf_addr *, uint16_t, struct pf_addr *, uint16_t, struct pf_addr *,
- uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **,
- struct pf_udp_mapping **);
+static int pf_get_sport(struct pf_test_ctx *, struct pf_pdesc *,
+ struct pf_krule *, struct pf_addr *, uint16_t,
+ struct pf_addr *, uint16_t, struct pf_addr *,
+ uint16_t *, uint16_t, uint16_t, enum pf_sn_types,
+ struct pf_udp_mapping **);
static bool pf_islinklocal(const sa_family_t, const struct pf_addr *);
#define mix(a,b,c) \
@@ -222,15 +223,14 @@
}
static int
-pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
+pf_get_sport(struct pf_test_ctx *ctx, struct pf_pdesc *pd, struct pf_krule *r,
struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr,
uint16_t dport, struct pf_addr *naddr, uint16_t *nport, uint16_t low,
- uint16_t high, struct pf_ksrc_node **sn,
+ uint16_t high, enum pf_sn_types sn_type,
struct pf_udp_mapping **udp_mapping)
{
struct pf_state_key_cmp key;
struct pf_addr init_addr;
- struct pf_srchash *sh = NULL;
bzero(&init_addr, sizeof(init_addr));
@@ -241,33 +241,31 @@
* from the mapping. In this case we have to look up the src_node as
* pf_map_addr would.
*/
- if (proto == IPPROTO_UDP && (r->rpool.opts & PF_POOL_ENDPI)) {
+ if (pd->proto == IPPROTO_UDP && (r->rpool.opts & PF_POOL_ENDPI)) {
struct pf_udp_endpoint_cmp udp_source;
bzero(&udp_source, sizeof(udp_source));
- udp_source.af = af;
- PF_ACPY(&udp_source.addr, saddr, af);
+ udp_source.af = pd->af;
+ PF_ACPY(&udp_source.addr, saddr, pd->af);
udp_source.port = sport;
*udp_mapping = pf_udp_mapping_find(&udp_source);
if (*udp_mapping) {
- PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, af);
+ PF_ACPY(naddr, &(*udp_mapping)->endpoints[1].addr, pd->af);
*nport = (*udp_mapping)->endpoints[1].port;
- /* Try to find a src_node as per pf_map_addr(). */
- if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR &&
- (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
- *sn = pf_find_src_node(saddr, r, af, &sh, 0);
+ pf_map_addr_sn(ctx, pd, r, saddr, naddr, NULL, &init_addr, sn_type);
return (0);
} else {
- *udp_mapping = pf_udp_mapping_create(af, saddr, sport, &init_addr, 0);
+ *udp_mapping = pf_udp_mapping_create(pd->af, saddr,
+ sport, &init_addr, 0);
if (*udp_mapping == NULL)
return (1);
}
}
- if (pf_map_addr_sn(af, r, saddr, naddr, NULL, &init_addr, sn))
+ if (pf_map_addr_sn(ctx, pd, r, saddr, naddr, NULL, &init_addr, sn_type))
goto failed;
- if (proto == IPPROTO_ICMP) {
+ if (pd->proto == IPPROTO_ICMP) {
if (*nport == htons(ICMP_ECHO)) {
low = 1;
high = 65535;
@@ -275,7 +273,7 @@
return (0); /* Don't try to modify non-echo ICMP */
}
#ifdef INET6
- if (proto == IPPROTO_ICMPV6) {
+ if (pd->proto == IPPROTO_ICMPV6) {
if (*nport == htons(ICMP6_ECHO_REQUEST)) {
low = 1;
high = 65535;
@@ -285,21 +283,21 @@
#endif /* INET6 */
bzero(&key, sizeof(key));
- key.af = af;
- key.proto = proto;
+ key.af = pd->af;
+ key.proto = pd->proto;
key.port[0] = dport;
PF_ACPY(&key.addr[0], daddr, key.af);
do {
PF_ACPY(&key.addr[1], naddr, key.af);
if (*udp_mapping)
- PF_ACPY(&(*udp_mapping)->endpoints[1].addr, naddr, af);
+ PF_ACPY(&(*udp_mapping)->endpoints[1].addr, naddr, pd->af);
/*
* port search; start random, step;
* similar 2 portloop in in_pcbbind
*/
- if (proto == IPPROTO_SCTP) {
+ if (pd->proto == IPPROTO_SCTP) {
key.port[1] = sport;
if (!pf_find_state_all_exists(&key, PF_IN)) {
*nport = sport;
@@ -307,8 +305,8 @@
} else {
return (1); /* Fail mapping. */
}
- } else if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP ||
- proto == IPPROTO_ICMP) || (low == 0 && high == 0)) {
+ } else if (!(pd->proto == IPPROTO_TCP || pd->proto == IPPROTO_UDP ||
+ pd->proto == IPPROTO_ICMP) || (low == 0 && high == 0)) {
/*
* XXX bug: icmp states don't use the id on both sides.
* (traceroute -I through nat)
@@ -361,7 +359,7 @@
}
tmp = cut;
for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) {
- if (proto == IPPROTO_UDP &&
+ if (pd->proto == IPPROTO_UDP &&
(r->rpool.opts & PF_POOL_ENDPI)) {
(*udp_mapping)->endpoints[1].port = htons(tmp);
if (pf_udp_mapping_insert(*udp_mapping) == 0) {
@@ -385,7 +383,8 @@
* pick a different source address since we're out
* of free port choices for the current one.
*/
- if (pf_map_addr_sn(af, r, saddr, naddr, NULL, &init_addr, sn))
+ if (pf_map_addr_sn(ctx, pd, r, saddr, naddr, NULL,
+ &init_addr, sn_type))
return (1);
break;
case PF_POOL_NONE:
@@ -394,7 +393,7 @@
default:
return (1);
}
- } while (! PF_AEQ(&init_addr, naddr, af) );
+ } while (! PF_AEQ(&init_addr, naddr, pd->af) );
failed:
uma_zfree(V_pf_udp_mapping_z, *udp_mapping);
@@ -411,10 +410,11 @@
}
static int
-pf_get_mape_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
- struct pf_addr *saddr, uint16_t sport, struct pf_addr *daddr,
- uint16_t dport, struct pf_addr *naddr, uint16_t *nport,
- struct pf_ksrc_node **sn, struct pf_udp_mapping **udp_mapping)
+pf_get_mape_sport(struct pf_test_ctx *ctx, struct pf_pdesc *pd,
+ struct pf_krule *r, struct pf_addr *saddr, uint16_t sport,
+ struct pf_addr *daddr, uint16_t dport, struct pf_addr *naddr,
+ uint16_t *nport, enum pf_sn_types sn_type,
+ struct pf_udp_mapping **udp_mapping)
{
uint16_t psmask, low, highmask;
uint16_t i, ahigh, cut;
@@ -433,21 +433,21 @@
for (i = cut; i <= ahigh; i++) {
low = (i << ashift) | psmask;
- if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport,
- naddr, nport, low, low | highmask, sn, udp_mapping))
+ if (!pf_get_sport(ctx, pd, r, saddr, sport, daddr, dport,
+ naddr, nport, low, low | highmask, sn_type, udp_mapping))
return (0);
}
for (i = cut - 1; i > 0; i--) {
low = (i << ashift) | psmask;
- if (!pf_get_sport(af, proto, r, saddr, sport, daddr, dport,
- naddr, nport, low, low | highmask, sn, udp_mapping))
+ if (!pf_get_sport(ctx, pd, r, saddr, sport, daddr, dport,
+ naddr, nport, low, low | highmask, sn_type, udp_mapping))
return (0);
}
return (1);
}
u_short
-pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
+pf_map_addr(struct pf_pdesc *pd, struct pf_krule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr)
{
u_short reason = PFRES_MATCH;
@@ -462,7 +462,7 @@
goto done_pool_mtx;
}
if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
- switch (af) {
+ switch (pd->af) {
#ifdef INET
case AF_INET:
if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 &&
@@ -500,14 +500,14 @@
switch (rpool->opts & PF_POOL_TYPEMASK) {
case PF_POOL_NONE:
- PF_ACPY(naddr, raddr, af);
+ PF_ACPY(naddr, raddr, pd->af);
break;
case PF_POOL_BITMASK:
- PF_POOLMASK(naddr, raddr, rmask, saddr, af);
+ PF_POOLMASK(naddr, raddr, rmask, saddr, pd->af);
break;
case PF_POOL_RANDOM:
- if (init_addr != NULL && PF_AZERO(init_addr, af)) {
- switch (af) {
+ if (init_addr != NULL && PF_AZERO(init_addr, pd->af)) {
+ switch (pd->af) {
#ifdef INET
case AF_INET:
rpool->counter.addr32[0] = htonl(arc4random());
@@ -536,20 +536,20 @@
break;
#endif /* INET6 */
}
- PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
- PF_ACPY(init_addr, naddr, af);
+ PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, pd->af);
+ PF_ACPY(init_addr, naddr, pd->af);
} else {
- PF_AINC(&rpool->counter, af);
- PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, af);
+ PF_AINC(&rpool->counter, pd->af);
+ PF_POOLMASK(naddr, raddr, rmask, &rpool->counter, pd->af);
}
break;
case PF_POOL_SRCHASH:
{
unsigned char hash[16];
- pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
- PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, af);
+ pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, pd->af);
+ PF_POOLMASK(naddr, raddr, rmask, (struct pf_addr *)&hash, pd->af);
break;
}
case PF_POOL_ROUNDROBIN:
@@ -558,13 +558,13 @@
if (rpool->cur->addr.type == PF_ADDR_TABLE) {
if (!pfr_pool_get(rpool->cur->addr.p.tbl,
- &rpool->tblidx, &rpool->counter, af, NULL))
+ &rpool->tblidx, &rpool->counter, pd->af, NULL))
goto get_addr;
} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
- &rpool->tblidx, &rpool->counter, af, pf_islinklocal))
+ &rpool->tblidx, &rpool->counter, pd->af, pf_islinklocal))
goto get_addr;
- } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
+ } else if (pf_match_addr(0, raddr, rmask, &rpool->counter, pd->af))
goto get_addr;
try_next:
@@ -575,7 +575,7 @@
if (rpool->cur->addr.type == PF_ADDR_TABLE) {
rpool->tblidx = -1;
if (pfr_pool_get(rpool->cur->addr.p.tbl,
- &rpool->tblidx, &rpool->counter, af, NULL)) {
+ &rpool->tblidx, &rpool->counter, pd->af, NULL)) {
/* table contains no address of type 'af' */
if (rpool->cur != acur)
goto try_next;
@@ -585,7 +585,7 @@
} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
rpool->tblidx = -1;
if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
- &rpool->tblidx, &rpool->counter, af, pf_islinklocal)) {
+ &rpool->tblidx, &rpool->counter, pd->af, pf_islinklocal)) {
/* table contains no address of type 'af' */
if (rpool->cur != acur)
goto try_next;
@@ -595,14 +595,14 @@
} else {
raddr = &rpool->cur->addr.v.a.addr;
rmask = &rpool->cur->addr.v.a.mask;
- PF_ACPY(&rpool->counter, raddr, af);
+ PF_ACPY(&rpool->counter, raddr, pd->af);
}
get_addr:
- PF_ACPY(naddr, &rpool->counter, af);
- if (init_addr != NULL && PF_AZERO(init_addr, af))
- PF_ACPY(init_addr, naddr, af);
- PF_AINC(&rpool->counter, af);
+ PF_ACPY(naddr, &rpool->counter, pd->af);
+ if (init_addr != NULL && PF_AZERO(init_addr, pd->af))
+ PF_ACPY(init_addr, naddr, pd->af);
+ PF_AINC(&rpool->counter, pd->af);
break;
}
}
@@ -621,45 +621,50 @@
}
u_short
-pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
- struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
- struct pf_ksrc_node **sn)
+pf_map_addr_sn(struct pf_test_ctx *ctx, struct pf_pdesc *pd,
+ struct pf_krule *r, struct pf_addr *saddr, struct pf_addr *naddr,
+ struct pfi_kkif **nkif, struct pf_addr *init_addr,
+ enum pf_sn_types sn_type)
{
- u_short reason = 0;
struct pf_kpool *rpool = &r->rpool;
- struct pf_srchash *sh = NULL;
+ u_short reason = 0;
- /* Try to find a src_node if none was given and this
- is a sticky-address rule. */
- if (*sn == NULL && r->rpool.opts & PF_POOL_STICKYADDR &&
+ /*
+ * Try to find an existing src_node if none was given and this is a
+ * sticky-address rule.
+ */
+ if (r->rpool.opts & PF_POOL_STICKYADDR &&
(r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
- *sn = pf_find_src_node(saddr, r, af, &sh, false);
+ pf_find_src_node(ctx, pd, saddr, r, sn_type);
- /* If a src_node was found or explicitly given and it has a non-zero
- route address, use this address. A zeroed address is found if the
- src node was created just a moment ago in pf_create_state and it
- needs to be filled in with routing decision calculated here. */
- if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) {
- /* If the supplied address is the same as the current one we've
+ /*
+ * If source node has been found it can be used.
+ */
+ if (ctx->sns[sn_type] != NULL) {
+ /*
+ * If the supplied address is the same as the current one we've
* been asked before, so tell the caller that there's no other
- * address to be had. */
- if (PF_AEQ(naddr, &(*sn)->raddr, af)) {
+ * address to be had.
+ */
+ if (PF_AEQ(naddr, &(ctx->sns[sn_type]->raddr), pd->af)) {
reason = PFRES_MAPFAILED;
goto done;
}
- PF_ACPY(naddr, &(*sn)->raddr, af);
+ PF_ACPY(naddr, &(ctx->sns[sn_type]->raddr), pd->af);
if (nkif)
- *nkif = (*sn)->rkif;
+ *nkif = ctx->sns[sn_type]->rkif;
if (V_pf_status.debug >= PF_DEBUG_NOISY) {
printf("pf_map_addr: src tracking maps ");
- pf_print_host(saddr, 0, af);
+ pf_print_host(saddr, 0, pd->af);
printf(" to ");
- pf_print_host(naddr, 0, af);
+ pf_print_host(naddr, 0, pd->af);
if (nkif)
printf("@%s", (*nkif)->pfik_name);
printf("\n");
}
+
+ reason = 0;
goto done;
}
@@ -667,37 +672,48 @@
* Source node has not been found. Find a new address and store it
* in variables given by the caller.
*/
- if (pf_map_addr(af, r, saddr, naddr, nkif, init_addr) != 0) {
+ if (pf_map_addr(pd, r, saddr, naddr, nkif, init_addr) != 0) {
/* pf_map_addr() sets reason counters on its own */
goto done;
}
- if (*sn != NULL) {
- PF_ACPY(&(*sn)->raddr, naddr, af);
- if (nkif)
- (*sn)->rkif = *nkif;
- }
-
- if (V_pf_status.debug >= PF_DEBUG_NOISY &&
- (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
+ if (V_pf_status.debug >= PF_DEBUG_NOISY) {
printf("pf_map_addr: selected address ");
- pf_print_host(naddr, 0, af);
+ pf_print_host(naddr, 0, pd->af);
if (nkif)
printf("@%s", (*nkif)->pfik_name);
printf("\n");
}
+ /*
+ * Now we can allocate and insert a new src node. The new address
+ * is copied from the variables given by the caller, which have been
+ * set by pf_map_addr().
+ */
+ if (ctx->sh != NULL) {
+ if (nkif)
+ *nkif = rpool->cur->kif;
+
+ if ((reason = pf_insert_src_node(ctx, pd, saddr, r, sn_type)) != 0)
+ goto done;
+
+ PF_ACPY(&(ctx->sns[sn_type]->raddr), naddr, pd->af);
+ }
+
done:
if (reason) {
counter_u64_add(V_pf_status.counters[reason], 1);
}
+ if (ctx->sh)
+ PF_HASHROW_UNLOCK(ctx->sh);
+
return (reason);
}
u_short
-pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off,
- struct pfi_kkif *kif, struct pf_ksrc_node **sn,
+pf_get_translation(struct pf_test_ctx *ctx, struct pf_pdesc *pd,
+ struct mbuf *m, int off, struct pfi_kkif *kif, struct pf_ksrc_node **sn,
struct pf_state_key **skp, struct pf_state_key **nkp,
struct pf_addr *saddr, struct pf_addr *daddr,
uint16_t sport, uint16_t dport, struct pf_kanchor_stackframe *anchor_stack,
@@ -765,8 +781,8 @@
high = r->rpool.proxy_port[1];
}
if (r->rpool.mape.offset > 0) {
- if (pf_get_mape_sport(pd->af, pd->proto, r, saddr,
- sport, daddr, dport, naddr, nportp, sn, udp_mapping)) {
+ if (pf_get_mape_sport(ctx, pd, r, saddr, sport, daddr,
+ dport, naddr, nportp, PF_SN_NAT, udp_mapping)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: MAP-E port allocation (%u/%u/%u)"
" failed\n",
@@ -776,8 +792,8 @@
reason = PFRES_MAPFAILED;
goto notrans;
}
- } else if (pf_get_sport(pd->af, pd->proto, r, saddr, sport,
- daddr, dport, naddr, nportp, low, high, sn, udp_mapping)) {
+ } else if (pf_get_sport(ctx, pd, r, saddr, sport, daddr, dport,
+ naddr, nportp, low, high, PF_SN_NAT, udp_mapping)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: NAT proxy port allocation (%u-%u) failed\n",
r->rpool.proxy_port[0], r->rpool.proxy_port[1]));
@@ -864,7 +880,8 @@
int tries;
uint16_t cut, low, high, nport;
- reason = pf_map_addr_sn(pd->af, r, saddr, naddr, NULL, NULL, sn);
+ reason = pf_map_addr_sn(ctx, pd, r, saddr, naddr, NULL, NULL,
+ PF_SN_NAT);
if (reason != 0)
goto notrans;
if ((r->rpool.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -146,7 +146,8 @@
struct nl_writer *nw = npt->nw;
int error = 0;
int af;
- struct pf_state_key *key;
+ struct pf_state_key *key;
+ enum pf_sn_types sn_type;
PF_STATE_LOCK_ASSERT(s);
@@ -184,10 +185,14 @@
nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout);
nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags);
uint8_t sync_flags = 0;
- if (s->src_node)
- sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (s->nat_src_node)
- sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (s->sns[sn_type] != NULL)
+ continue;
+ if (sn_type == PF_SN_LIMIT || sn_type == PF_SN_ROUTE)
+ sync_flags |= PFSYNC_FLAG_SRCNODE;
+ else
+ sync_flags |= PFSYNC_FLAG_NATSRCNODE;
+ }
nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
nlattr_add_u64(nw, PF_ST_ID, s->id);
nlattr_add_u32(nw, PF_ST_CREATORID, htonl(s->creatorid));
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
--- a/sys/netpfil/pf/pf_nv.c
+++ b/sys/netpfil/pf/pf_nv.c
@@ -928,8 +928,9 @@
nvlist_t *
pf_state_to_nvstate(const struct pf_kstate *s)
{
- nvlist_t *nvl, *tmp;
- uint32_t expire, flags = 0;
+ nvlist_t *nvl, *tmp;
+ uint32_t expire, flags = 0;
+ enum pf_sn_types sn_type;
nvl = nvlist_create(0);
if (nvl == NULL)
@@ -993,10 +994,14 @@
nvlist_add_number(nvl, "creatorid", s->creatorid);
nvlist_add_number(nvl, "direction", s->direction);
nvlist_add_number(nvl, "state_flags", s->state_flags);
- if (s->src_node)
- flags |= PFSYNC_FLAG_SRCNODE;
- if (s->nat_src_node)
- flags |= PFSYNC_FLAG_NATSRCNODE;
+ for (sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (s->sns[sn_type] == NULL)
+ continue;
+ if (sn_type == PF_SN_LIMIT || sn_type == PF_SN_ROUTE)
+ flags |= PFSYNC_FLAG_SRCNODE;
+ else
+ flags |= PFSYNC_FLAG_NATSRCNODE;
+ }
nvlist_add_number(nvl, "sync_flags", flags);
return (nvl);
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -6,6 +6,7 @@
ATF_TESTS_SH+= altq \
anchor \
+ counters \
debug \
divert-to \
dup \
diff --git a/tests/sys/netpfil/pf/counters.sh b/tests/sys/netpfil/pf/counters.sh
new file mode 100755
--- /dev/null
+++ b/tests/sys/netpfil/pf/counters.sh
@@ -0,0 +1,83 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2024 Kajetan Staszkiewicz <vegeta@tuxpowered.net>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+. $(atf_get_srcdir)/utils.subr
+
+atf_test_case "source_track" "cleanup"
+
+counters_head()
+{
+ atf_set descr 'Rule and table counters'
+ atf_set require.user root
+}
+
+counters_body()
+{
+ setup_router_server_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses and for tester jail
+ # to not respond with RST packets for SYN+ACKs.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+ jexec server route add -6 2001:db8:44::0/64 2001:db8:43::1
+
+ pft_set_rules router \
+ "table <sources> { 2001:db8:44::0/64 }" \
+ "nat on ${epair_server}a inet6 from <sources> to any -> ${epair_server}a" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp from <sources> keep state" \
+ "pass out on ${epair_server}a inet6 proto tcp keep state"
+
+ # Use the 3-way testing so that more than 1 packet is sent.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4201 --fromaddr 2001:db8:44::1
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss > ${states}
+ echo === states ===
+ cat $states
+
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvsS > ${nodes}
+ echo === nodes ===
+ cat $nodes
+
+ tables=$(mktemp) || exit 1
+ jexec router pfctl -qvsT > ${tables}
+ echo === tables ===
+ cat $tables
+}
+
+counters_cleanup()
+{
+ pft_cleanup
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case "counters"
+}
diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh
--- a/tests/sys/netpfil/pf/src_track.sh
+++ b/tests/sys/netpfil/pf/src_track.sh
@@ -150,28 +150,34 @@
# 2 connections from host ::1 matching rule_A will be allowed, 1 will fail to create a state.
ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4211 --fromaddr 2001:db8:44::1
ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4212 --fromaddr 2001:db8:44::1
- ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4213 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4213 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4214 --fromaddr 2001:db8:44::1
# 2 connections from host ::1 matching rule_B will be allowed, 1 will fail to create a state.
# Limits from rule_A don't interfere with rule_B.
ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4221 --fromaddr 2001:db8:44::1
ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4222 --fromaddr 2001:db8:44::1
- ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4223 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4223 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4224 --fromaddr 2001:db8:44::1
# 2 connections from host ::2 matching rule_B will be allowed, 1 will fail to create a state.
# Limits for host ::1 will not interfere with host ::2.
- ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4224 --fromaddr 2001:db8:44::2
ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4225 --fromaddr 2001:db8:44::2
- ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4226 --fromaddr 2001:db8:44::2
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4226 --fromaddr 2001:db8:44::2
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4227 --fromaddr 2001:db8:44::2
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4228 --fromaddr 2001:db8:44::2
# We will check the resulting source nodes, though.
# Order of source nodes in output is not guaranteed, find each one separately.
nodes=$(mktemp) || exit 1
jexec router pfctl -qvsS > $nodes
+ echo === Nodes start ===
+ cat $nodes
+ echo === Nodes end ===
for node_regexp in \
- '2001:db8:44::1 -> :: \( states 2, connections 2, rate [0-9/\.]+s \)\s+age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 3' \
- '2001:db8:44::1 -> :: \( states 2, connections 2, rate [0-9/\.]+s \)\s+age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 4' \
- '2001:db8:44::2 -> :: \( states 2, connections 2, rate [0-9/\.]+s \)\s+age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 4' \
+ '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \)\s+age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 3' \
+ '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \)\s+age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4' \
+ '2001:db8:44::2 -> :: \( states 3, connections 3, rate [0-9/\.]+s \)\s+age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4' \
; do
cat $nodes | tr '\n' ' ' | grep -qE "$node_regexp" || atf_fail "Source nodes not matching expected output"
done
@@ -185,9 +191,58 @@
pft_cleanup
}
+max_src_states_global_head()
+{
+ atf_set descr 'Max states per source global'
+ atf_set require.user root
+}
+
+max_src_states_global_body()
+{
+ setup_router_server_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses and for tester jail
+ # to not respond with RST packets for SYN+ACKs.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+ jexec server route add -6 2001:db8:44::0/64 2001:db8:43::1
+
+ pft_set_rules router \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in on ${epair_tester}b inet6 proto tcp from port 4210:4219 keep state (max-src-states 3 source-track global) label rule_A" \
+ "pass in on ${epair_tester}b inet6 proto tcp from port 4220:4229 keep state (max-src-states 3 source-track global) label rule_B" \
+ "pass out on ${epair_server}a keep state"
+
+ # Global source tracking creates a single source node shared between all
+ # rules for each connecting source IP address and counts states created
+ # by all rules. Each rule has its own max-src-conn value checked against
+ # that single source node.
+
+ # 3 connections from host ::1 matching rule_A will be allowed.
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4211 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4212 --fromaddr 2001:db8:44::1
+ ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4213 --fromaddr 2001:db8:44::1
+ # The 4th connection matching rule_A from host ::1 will have its state killed.
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4214 --fromaddr 2001:db8:44::1
+ # A connection matching rule_B from host ::1 will have its state killed too.
+ ping_server_check_reply exit:1 --ping-type=tcp3way --send-sport=4221 --fromaddr 2001:db8:44::1
+
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvsS > $nodes
+ node_regexp='2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \)\s+age [0-9:]+, 9 pkts, [0-9]+ bytes'
+ cat $nodes | tr '\n' ' ' | grep -qE "$node_regexp" || atf_fail "Source nodes not matching expected output"
+}
+
+max_src_states_global_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "source_track"
atf_add_test_case "max_src_conn_rule"
atf_add_test_case "max_src_states_rule"
+ atf_add_test_case "max_src_states_global"
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jul 6, 7:17 AM (2 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
34742791
Default Alt Text
D39880.id144271.diff (57 KB)
Attached To
Mode
D39880: pf: Add support for multiple source node types
Attached
Detach File
Event Timeline
Log In to Comment