Page MenuHomeFreeBSD

D39880.diff
No OneTemporary

D39880.diff

diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -216,6 +216,7 @@
uint64_t states_cur;
uint64_t states_tot;
uint64_t src_nodes;
+ uint64_t src_nodes_type[PF_SN_MAX];
uint16_t return_icmp;
uint16_t return_icmp6;
@@ -373,6 +374,7 @@
uint8_t set_prio[2];
uint8_t rt;
char rt_ifname[IFNAMSIZ];
+ uint8_t src_node_flags;
};
TAILQ_HEAD(pfctl_statelist, pfctl_state);
@@ -415,6 +417,7 @@
uint64_t creation;
uint64_t expire;
struct pfctl_threshold conn_rate;
+ pf_sn_types_t type;
};
#define PF_DEVICE "/dev/pf"
diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1665,6 +1665,9 @@
{ .type = PF_RT_NAF, .off = _OUT(r.naf), .cb = snl_attr_get_uint8 },
{ .type = PF_RT_RPOOL_RT, .off = _OUT(r.route), .arg = &pool_parser, .cb = snl_attr_get_nested },
{ .type = PF_RT_RCV_IFNOT, .off = _OUT(r.rcvifnot),.cb = snl_attr_get_bool },
+ { .type = PF_RT_SRC_NODES_LIMIT, .off = _OUT(r.src_nodes_type[PF_SN_LIMIT]), .cb = snl_attr_get_uint64 },
+ { .type = PF_RT_SRC_NODES_NAT, .off = _OUT(r.src_nodes_type[PF_SN_NAT]), .cb = snl_attr_get_uint64 },
+ { .type = PF_RT_SRC_NODES_ROUTE, .off = _OUT(r.src_nodes_type[PF_SN_ROUTE]), .cb = snl_attr_get_uint64 },
};
#undef _OUT
SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, snl_f_p_empty, ap_getrule);
@@ -1910,6 +1913,7 @@
{ .type = PF_ST_DNRPIPE, .off = _OUT(dnrpipe), .cb = snl_attr_get_uint16 },
{ .type = PF_ST_RT, .off = _OUT(rt), .cb = snl_attr_get_uint8 },
{ .type = PF_ST_RT_IFNAME, .off = _OUT(rt_ifname), .cb = snl_attr_store_ifname },
+ { .type = PF_ST_SRC_NODE_FLAGS, .off = _OUT(src_node_flags), .cb = snl_attr_get_uint8 },
};
#undef _IN
#undef _OUT
@@ -3018,6 +3022,7 @@
{ .type = PF_SN_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint64 },
{ .type = PF_SN_CONNECTION_RATE, .off = _OUT(conn_rate), .arg = &pfctl_threshold_parser, .cb = snl_attr_get_nested },
{ .type = PF_SN_NAF, .off = _OUT(naf), .cb = snl_attr_get_uint8 },
+ { .type = PF_SN_NODE_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 },
};
#undef _OUT
SNL_DECLARE_PARSER(srcnode_parser, struct genlmsghdr, snl_f_p_empty, ap_srcnode);
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -245,6 +245,7 @@
uint8_t proto;
int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
int idx;
+ const char *sn_type_names[] = PF_SN_TYPE_NAMES;
#ifndef __NO_STRICT_ALIGNMENT
struct pfctl_state_key aligned_key[2];
@@ -405,10 +406,14 @@
printf(", dummynet queue (%d %d)",
s->dnpipe, s->dnrpipe);
}
- if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
- printf(", source-track");
- if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
- printf(", sticky-address");
+ if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT)
+ printf(", %s", sn_type_names[PF_SN_LIMIT]);
+ if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT_GLOBAL)
+ printf(" global");
+ if (s->src_node_flags & PFSTATE_SRC_NODE_NAT)
+ printf(", %s", sn_type_names[PF_SN_NAT]);
+ if (s->src_node_flags & PFSTATE_SRC_NODE_ROUTE)
+ printf(", %s", sn_type_names[PF_SN_ROUTE]);
if (s->log)
printf(", log");
if (s->log & PF_LOG_ALL)
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1064,6 +1064,15 @@
rule->packets[1]),
(unsigned long long)(rule->bytes[0] +
rule->bytes[1]), (uintmax_t)rule->states_cur);
+ printf(" [ Source Nodes: %-6ju "
+ "Limit: %-6ju "
+ "NAT/RDR: %-6ju "
+ "Route: %-6ju "
+ "]\n",
+ (uintmax_t)rule->src_nodes,
+ (uintmax_t)rule->src_nodes_type[PF_SN_LIMIT],
+ (uintmax_t)rule->src_nodes_type[PF_SN_NAT],
+ (uintmax_t)rule->src_nodes_type[PF_SN_ROUTE]);
if (!(opts & PF_OPT_DEBUG))
printf(" [ Inserted: uid %u pid %u "
"State Creations: %-6ju]\n",
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -651,6 +651,7 @@
{
struct pf_addr_wrap aw;
uint64_t min, sec;
+ const char *sn_type_names[] = PF_SN_TYPE_NAMES;
memset(&aw, 0, sizeof(aw));
if (sn->af == AF_INET)
@@ -699,6 +700,7 @@
printf(", filter rule %u", sn->rule);
break;
}
+ printf(", %s", sn_type_names[sn->type]);
printf("\n");
}
}
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -624,6 +624,21 @@
#define PF_ALGNMNT(off) (((off) % 2) == 0)
+/*
+ * At the moment there are no rules which have both NAT and RDR actions,
+ * apart from af-to rules, but those don't to source tracking for address
+ * translation. And the r->rdr pool is used for both NAT and RDR.
+ * So there is no PF_SN_RDR.
+ */
+enum pf_sn_types { PF_SN_LIMIT, PF_SN_NAT, PF_SN_ROUTE, PF_SN_MAX };
+typedef enum pf_sn_types pf_sn_types_t;
+#define PF_SN_TYPE_NAMES { \
+ "limit source-track", \
+ "NAT/RDR sticky-address", \
+ "route sticky-address", \
+ NULL \
+}
+
#ifdef _KERNEL
struct pf_kpooladdr {
@@ -822,7 +837,7 @@
counter_u64_t states_cur;
counter_u64_t states_tot;
- counter_u64_t src_nodes;
+ counter_u64_t src_nodes[PF_SN_MAX];
u_int16_t return_icmp;
u_int16_t return_icmp6;
@@ -904,6 +919,7 @@
sa_family_t af;
sa_family_t naf;
u_int8_t ruletype;
+ pf_sn_types_t type;
struct mtx *lock;
};
#endif
@@ -1104,8 +1120,7 @@
struct pf_udp_mapping *udp_mapping;
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 pf_ksrc_node *src_node;
- struct pf_ksrc_node *nat_src_node;
+ struct pf_ksrc_node *sns[PF_SN_MAX];/* source nodes */
u_int64_t packets[2];
u_int64_t bytes[2];
u_int64_t creation;
@@ -1118,9 +1133,10 @@
};
/*
- * Size <= fits 11 objects per page on LP64. Try to not grow the struct beyond that.
+ * 6 cache lines per struct, 10 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
/*
@@ -2367,7 +2383,7 @@
struct pf_srchash *);
extern struct pf_ksrc_node *pf_find_src_node(struct pf_addr *,
struct pf_krule *, sa_family_t,
- struct pf_srchash **, bool);
+ struct pf_srchash **, pf_sn_types_t, bool);
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 *);
@@ -2670,7 +2686,7 @@
struct pf_addr *, struct pf_addr *,
struct pfi_kkif **nkif, struct pf_addr *,
struct pf_ksrc_node **, struct pf_srchash **,
- struct pf_kpool *);
+ struct pf_kpool *, pf_sn_types_t);
int pf_get_transaddr_af(struct pf_krule *,
struct pf_pdesc *);
u_short pf_get_translation(struct pf_pdesc *,
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -649,6 +649,12 @@
#define PFSTATE_SCRUBMASK (PFSTATE_NODF|PFSTATE_RANDOMID|PFSTATE_SCRUB_TCP)
#define PFSTATE_SETMASK (PFSTATE_SETTOS|PFSTATE_SETPRIO)
+/* pfctl_state->src_node_flags */
+#define PFSTATE_SRC_NODE_LIMIT 0x01
+#define PFSTATE_SRC_NODE_NAT 0x02
+#define PFSTATE_SRC_NODE_ROUTE 0x04
+#define PFSTATE_SRC_NODE_LIMIT_GLOBAL 0x10
+
#define PFSTATE_HIWAT 100000 /* default state table size */
#define PFSTATE_ADAPT_START 60000 /* default adaptive timeout start */
#define PFSTATE_ADAPT_END 120000 /* default adaptive timeout end */
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
@@ -389,7 +389,7 @@
static u_short pf_insert_src_node(struct pf_ksrc_node **,
struct pf_srchash **, struct pf_krule *,
struct pf_addr *, sa_family_t, struct pf_addr *,
- struct pfi_kkif *);
+ struct pfi_kkif *, pf_sn_types_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);
@@ -835,25 +835,26 @@
static bool
pf_src_connlimit(struct pf_kstate *state)
{
- struct pf_overload_entry *pfoe;
- bool limited = false;
+ struct pf_overload_entry *pfoe;
+ struct pf_ksrc_node *src_node = state->sns[PF_SN_LIMIT];
+ bool limited = false;
PF_STATE_LOCK_ASSERT(state);
- PF_SRC_NODE_LOCK(state->src_node);
+ PF_SRC_NODE_LOCK(src_node);
- state->src_node->conn++;
+ src_node->conn++;
state->src.tcp_est = 1;
- pf_add_threshold(&state->src_node->conn_rate);
+ pf_add_threshold(&src_node->conn_rate);
if (state->rule->max_src_conn &&
state->rule->max_src_conn <
- state->src_node->conn) {
+ src_node->conn) {
counter_u64_add(V_pf_status.lcounters[LCNT_SRCCONN], 1);
limited = true;
}
if (state->rule->max_src_conn_rate.limit &&
- pf_check_threshold(&state->src_node->conn_rate)) {
+ pf_check_threshold(&src_node->conn_rate)) {
counter_u64_add(V_pf_status.lcounters[LCNT_SRCCONNRATE], 1);
limited = true;
}
@@ -873,7 +874,7 @@
if (pfoe == NULL)
goto done; /* too bad :( */
- bcopy(&state->src_node->addr, &pfoe->addr, sizeof(pfoe->addr));
+ bcopy(&src_node->addr, &pfoe->addr, sizeof(pfoe->addr));
pfoe->af = state->key[PF_SK_WIRE]->af;
pfoe->rule = state->rule;
pfoe->dir = state->direction;
@@ -883,7 +884,7 @@
taskqueue_enqueue(taskqueue_swi, &V_pf_overloadtask);
done:
- PF_SRC_NODE_UNLOCK(state->src_node);
+ PF_SRC_NODE_UNLOCK(src_node);
return (limited);
}
@@ -985,7 +986,7 @@
*/
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)
+ struct pf_srchash **sh, pf_sn_types_t sn_type, bool returnlocked)
{
struct pf_ksrc_node *n;
@@ -994,7 +995,7 @@
*sh = &V_pf_srchash[pf_hashsrc(src, af)];
PF_HASHROW_LOCK(*sh);
LIST_FOREACH(n, &(*sh)->nodes, entry)
- if (n->rule == rule && n->af == af &&
+ if (n->rule == rule && n->af == af && n->type == sn_type &&
((af == AF_INET && n->addr.v4.s_addr == src->v4.s_addr) ||
(af == AF_INET6 && bcmp(&n->addr, src, sizeof(*src)) == 0)))
break;
@@ -1039,27 +1040,43 @@
}
static u_short
-pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_srchash **sh,
- struct pf_krule *rule, struct pf_addr *src, sa_family_t af,
- struct pf_addr *raddr, struct pfi_kkif *rkif)
+pf_insert_src_node(struct pf_ksrc_node *sns[PF_SN_MAX],
+ struct pf_srchash *snhs[PF_SN_MAX], struct pf_krule *rule,
+ struct pf_addr *src, sa_family_t af, struct pf_addr *raddr,
+ struct pfi_kkif *rkif, pf_sn_types_t sn_type)
{
u_short reason = 0;
+ struct pf_krule *r_track = rule;
+ struct pf_ksrc_node **sn = &(sns[sn_type]);
+ struct pf_srchash **sh = &(snhs[sn_type]);
- KASSERT((rule->rule_flag & PFRULE_SRCTRACK ||
- rule->rdr.opts & PF_POOL_STICKYADDR),
- ("%s for non-tracking rule %p", __func__, rule));
+ KASSERT(sn_type != PF_SN_LIMIT || (raddr == NULL && rkif == NULL),
+ ("%s: raddr and rkif must be NULL for PF_SN_LIMIT", __func__));
+
+ KASSERT(sn_type != PF_SN_LIMIT || (rule->rule_flag & PFRULE_SRCTRACK),
+ ("%s: PF_SN_LIMIT only valid for rules with PFRULE_SRCTRACK", __func__));
+
+ /*
+ * XXX: There could be a KASSERT for
+ * sn_type == PF_SN_LIMIT || (pool->opts & PF_POOL_STICKYADDR)
+ * but we'd need to pass pool *only* for this KASSERT.
+ */
+
+ if ( (rule->rule_flag & PFRULE_SRCTRACK) &&
+ !(rule->rule_flag & PFRULE_RULESRCTRACK))
+ r_track = &V_pf_default_rule;
/*
* Request the sh to always be locked, as we might insert a new sn.
*/
if (*sn == NULL)
- *sn = pf_find_src_node(src, rule, af, sh, true);
+ *sn = pf_find_src_node(src, r_track, af, sh, sn_type, true);
if (*sn == NULL) {
PF_HASHROW_ASSERT(*sh);
- if (rule->max_src_nodes &&
- counter_u64_fetch(rule->src_nodes) >= rule->max_src_nodes) {
+ if (sn_type == PF_SN_LIMIT && rule->max_src_nodes &&
+ counter_u64_fetch(r_track->src_nodes[sn_type]) >= rule->max_src_nodes) {
counter_u64_add(V_pf_status.lcounters[LCNT_SRCNODES], 1);
reason = PFRES_SRCLIMIT;
goto done;
@@ -1082,26 +1099,28 @@
}
}
- pf_init_threshold(&(*sn)->conn_rate,
- rule->max_src_conn_rate.limit,
- rule->max_src_conn_rate.seconds);
+ if (sn_type == PF_SN_LIMIT)
+ pf_init_threshold(&(*sn)->conn_rate,
+ rule->max_src_conn_rate.limit,
+ rule->max_src_conn_rate.seconds);
MPASS((*sn)->lock == NULL);
(*sn)->lock = &(*sh)->lock;
(*sn)->af = af;
- (*sn)->rule = rule;
+ (*sn)->rule = r_track;
PF_ACPY(&(*sn)->addr, src, af);
- PF_ACPY(&(*sn)->raddr, raddr, af);
+ if (raddr != NULL)
+ PF_ACPY(&(*sn)->raddr, raddr, af);
(*sn)->rkif = rkif;
LIST_INSERT_HEAD(&(*sh)->nodes, *sn, entry);
(*sn)->creation = time_uptime;
(*sn)->ruletype = rule->action;
- if ((*sn)->rule != NULL)
- counter_u64_add((*sn)->rule->src_nodes, 1);
+ (*sn)->type = sn_type;
+ counter_u64_add(r_track->src_nodes[sn_type], 1);
counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_INSERT], 1);
} else {
- if (rule->max_src_states &&
+ if (sn_type == PF_SN_LIMIT && rule->max_src_states &&
(*sn)->states >= rule->max_src_states) {
counter_u64_add(V_pf_status.lcounters[LCNT_SRCSTATES],
1);
@@ -1126,7 +1145,7 @@
LIST_REMOVE(src, entry);
if (src->rule)
- counter_u64_add(src->rule->src_nodes, -1);
+ counter_u64_add(src->rule->src_nodes[src->type], -1);
}
u_int
@@ -2647,30 +2666,24 @@
static void
pf_src_tree_remove_state(struct pf_kstate *s)
{
- struct pf_ksrc_node *sn;
uint32_t timeout;
timeout = s->rule->timeout[PFTM_SRC_NODE] ?
s->rule->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);
- if (s->src.tcp_est)
- --sn->conn;
- if (--sn->states == 0)
- sn->expire = time_uptime + timeout;
- PF_SRC_NODE_UNLOCK(sn);
- }
- 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);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (s->sns[sn_type] == NULL)
+ continue;
+ PF_SRC_NODE_LOCK(s->sns[sn_type]);
+ if (sn_type == PF_SN_LIMIT && s->src.tcp_est)
+ --(s->sns[sn_type]->conn);
+ 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;
}
- s->src_node = s->nat_src_node = NULL;
+
}
/*
@@ -5895,7 +5908,7 @@
pd->act.rt = r->rt;
/* Don't use REASON_SET, pf_map_addr increases the reason counters */
reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr,
- &pd->act.rt_kif, NULL, &sn, &snh, pool);
+ &pd->act.rt_kif, NULL, &sn, &snh, pool, PF_SN_ROUTE);
if (reason != 0)
goto cleanup;
}
@@ -5997,14 +6010,18 @@
struct pf_udp_mapping *udp_mapping)
{
struct pf_kstate *s = NULL;
- struct pf_ksrc_node *sn = NULL;
- struct pf_srchash *snh = NULL;
- struct pf_ksrc_node *nsn = NULL;
- struct pf_srchash *nsnh = NULL;
+ struct pf_ksrc_node *sns[PF_SN_MAX] = { NULL };
+ /*
+ * XXXKS: The hash for PF_SN_LIMIT and PF_SN_ROUTE should be the same
+ * but for PF_SN_NAT it is different. Don't try optimizing it,
+ * just store all 3 hashes.
+ */
+ struct pf_srchash *snhs[PF_SN_MAX] = { NULL };
struct tcphdr *th = &pd->hdr.tcp;
u_int16_t mss = V_tcp_mssdflt;
u_short reason, sn_reason;
struct pf_krule_item *ri;
+ struct pf_kpool *pool_route = &r->route;
/* check maximums */
if (r->max_states &&
@@ -6013,18 +6030,26 @@
REASON_SET(&reason, PFRES_MAXSTATES);
goto csfailed;
}
- /* src node for filter rule */
- if ((r->rule_flag & PFRULE_SRCTRACK ||
- r->rdr.opts & PF_POOL_STICKYADDR) &&
- (sn_reason = pf_insert_src_node(&sn, &snh, r, pd->src, pd->af,
- &pd->act.rt_addr, pd->act.rt_kif)) != 0) {
+ /* src node for limits */
+ if ((r->rule_flag & PFRULE_SRCTRACK) &&
+ (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af,
+ NULL, NULL, PF_SN_LIMIT)) != 0) {
+ REASON_SET(&reason, sn_reason);
+ goto csfailed;
+ }
+ /* src node for route-to rule */
+ if (TAILQ_EMPTY(&pool_route->list)) /* Backwards compatibility. */
+ pool_route = &r->rdr;
+ if ((pool_route->opts & PF_POOL_STICKYADDR) &&
+ (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af,
+ &pd->act.rt_addr, pd->act.rt_kif, PF_SN_ROUTE)) != 0) {
REASON_SET(&reason, sn_reason);
goto csfailed;
}
/* src node for translation rule */
if (nr != NULL && (nr->rdr.opts & PF_POOL_STICKYADDR) &&
- (sn_reason = pf_insert_src_node(&nsn, &nsnh, nr, &sk->addr[pd->sidx],
- pd->af, &nk->addr[1], NULL)) != 0 ) {
+ (sn_reason = pf_insert_src_node(sns, snhs, nr, &sk->addr[pd->sidx],
+ pd->af, &nk->addr[1], NULL, PF_SN_NAT)) != 0 ) {
REASON_SET(&reason, sn_reason);
goto csfailed;
}
@@ -6166,13 +6191,11 @@
/*
* Lock order is important: first state, then source node.
*/
- if (pf_src_node_exists(&sn, snh)) {
- s->src_node = sn;
- PF_HASHROW_UNLOCK(snh);
- }
- if (pf_src_node_exists(&nsn, nsnh)) {
- s->nat_src_node = nsn;
- PF_HASHROW_UNLOCK(nsnh);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (pf_src_node_exists(&sns[sn_type], snhs[sn_type])) {
+ s->sns[sn_type] = sns[sn_type];
+ PF_HASHROW_UNLOCK(snhs[sn_type]);
+ }
}
if (tag > 0)
@@ -6223,24 +6246,17 @@
uma_zfree(V_pf_state_key_z, sk);
uma_zfree(V_pf_state_key_z, nk);
- if (pf_src_node_exists(&sn, snh)) {
- if (--sn->states == 0 && sn->expire == 0) {
- pf_unlink_src_node(sn);
- pf_free_src_node(sn);
- counter_u64_add(
- V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
- }
- PF_HASHROW_UNLOCK(snh);
- }
-
- if (sn != nsn && pf_src_node_exists(&nsn, nsnh)) {
- if (--nsn->states == 0 && nsn->expire == 0) {
- pf_unlink_src_node(nsn);
- pf_free_src_node(nsn);
- counter_u64_add(
- V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+ if (pf_src_node_exists(&sns[sn_type], snhs[sn_type])) {
+ if (--sns[sn_type]->states == 0 &&
+ sns[sn_type]->expire == 0) {
+ pf_unlink_src_node(sns[sn_type]);
+ pf_free_src_node(sns[sn_type]);
+ counter_u64_add(
+ V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
+ }
+ PF_HASHROW_UNLOCK(snhs[sn_type]);
}
- PF_HASHROW_UNLOCK(nsnh);
}
drop:
@@ -6575,7 +6591,7 @@
pf_set_protostate(*state, pdst,
TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
- (*state)->src_node != NULL &&
+ (*state)->sns[PF_SN_LIMIT] != NULL &&
pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
@@ -6746,7 +6762,7 @@
if (dst->state == TCPS_SYN_SENT) {
pf_set_protostate(*state, pdst, TCPS_ESTABLISHED);
if (src->state == TCPS_ESTABLISHED &&
- (*state)->src_node != NULL &&
+ (*state)->sns[PF_SN_LIMIT] != NULL &&
pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
@@ -6764,7 +6780,7 @@
pf_set_protostate(*state, PF_PEER_BOTH,
TCPS_ESTABLISHED);
dst->state = src->state = TCPS_ESTABLISHED;
- if ((*state)->src_node != NULL &&
+ if ((*state)->sns[PF_SN_LIMIT] != NULL &&
pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
@@ -6831,7 +6847,7 @@
(ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
REASON_SET(reason, PFRES_SYNPROXY);
return (PF_DROP);
- } else if ((*state)->src_node != NULL &&
+ } else if ((*state)->sns[PF_SN_LIMIT] != NULL &&
pf_src_connlimit(*state)) {
REASON_SET(reason, PFRES_SRCLIMIT);
return (PF_DROP);
@@ -10023,17 +10039,21 @@
pf_counter_u64_add_protected(&s->nat_rule->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 (pf_sn_types_t 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]++;
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
@@ -352,7 +352,8 @@
}
V_pf_default_rule.states_cur = counter_u64_alloc(M_WAITOK);
V_pf_default_rule.states_tot = counter_u64_alloc(M_WAITOK);
- V_pf_default_rule.src_nodes = counter_u64_alloc(M_WAITOK);
+ for (pf_sn_types_t sn_type = 0; sn_type<PF_SN_MAX; sn_type++)
+ V_pf_default_rule.src_nodes[sn_type] = counter_u64_alloc(M_WAITOK);
V_pf_default_rule.timestamp = uma_zalloc_pcpu(pf_timestamp_pcpu_zone,
M_WAITOK | M_ZERO);
@@ -1854,7 +1855,8 @@
}
counter_u64_free(rule->states_cur);
counter_u64_free(rule->states_tot);
- counter_u64_free(rule->src_nodes);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+ counter_u64_free(rule->src_nodes[sn_type]);
uma_zfree_pcpu(pf_timestamp_pcpu_zone, rule->timestamp);
mtx_destroy(&rule->nat.mtx);
@@ -2090,7 +2092,8 @@
}
rule->states_cur = counter_u64_alloc(M_WAITOK);
rule->states_tot = counter_u64_alloc(M_WAITOK);
- rule->src_nodes = counter_u64_alloc(M_WAITOK);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+ rule->src_nodes[sn_type] = counter_u64_alloc(M_WAITOK);
rule->cuid = uid;
rule->cpid = pid;
TAILQ_INIT(&rule->rdr.list);
@@ -3651,7 +3654,8 @@
}
newrule->states_cur = counter_u64_alloc(M_WAITOK);
newrule->states_tot = counter_u64_alloc(M_WAITOK);
- newrule->src_nodes = counter_u64_alloc(M_WAITOK);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+ newrule->src_nodes[sn_type] = counter_u64_alloc(M_WAITOK);
newrule->cuid = td->td_ucred->cr_ruid;
newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
TAILQ_INIT(&newrule->nat.list);
@@ -5672,9 +5676,14 @@
__func__, msg_version);
}
- if (st->src_node)
+ /*
+ * XXX Why do we bother pfsyncing source node information if source
+ * nodes are not synced? Showing users that there is source tracking
+ * when there is none seems useless.
+ */
+ if (st->sns[PF_SN_LIMIT] != NULL)
sp->pfs_1301.sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (st->nat_src_node)
+ if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE])
sp->pfs_1301.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
sp->pfs_1301.id = st->id;
@@ -5738,11 +5747,10 @@
/* 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)
+ if (st->sns[PF_SN_LIMIT] != NULL)
sp->sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (st->nat_src_node)
+ if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE] != NULL)
sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE;
-
sp->id = st->id;
sp->creatorid = st->creatorid;
pf_state_peer_hton(&st->src, &sp->src);
@@ -6007,10 +6015,13 @@
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(pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX;
+ sn_type++) {
+ if (s->sns[sn_type] &&
+ s->sns[sn_type]->expire == 1) {
+ s->sns[sn_type] = NULL;
+ }
+ }
}
PF_HASHROW_UNLOCK(ih);
}
@@ -6834,7 +6845,8 @@
}
counter_u64_free(V_pf_default_rule.states_cur);
counter_u64_free(V_pf_default_rule.states_tot);
- counter_u64_free(V_pf_default_rule.src_nodes);
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+ counter_u64_free(V_pf_default_rule.src_nodes[sn_type]);
uma_zfree_pcpu(pf_timestamp_pcpu_zone, V_pf_default_rule.timestamp);
for (int i = 0; i < PFRES_MAX; i++)
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
@@ -75,9 +75,11 @@
struct pf_poolhashkey *, sa_family_t);
static struct pf_krule *pf_match_translation(struct pf_pdesc *,
int, struct pf_kanchor_stackframe *);
-static int pf_get_sport(struct pf_pdesc *, struct pf_krule *,
- struct pf_addr *, uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **,
- struct pf_srchash **, struct pf_kpool *, struct pf_udp_mapping **);
+static int pf_get_sport(struct pf_pdesc *, struct pf_krule *,
+ struct pf_addr *, uint16_t *, uint16_t, uint16_t,
+ struct pf_ksrc_node **, struct pf_srchash **,
+ struct pf_kpool *, struct pf_udp_mapping **,
+ pf_sn_types_t);
static bool pf_islinklocal(const sa_family_t, const struct pf_addr *);
#define mix(a,b,c) \
@@ -231,7 +233,7 @@
struct pf_addr *naddr, uint16_t *nport, uint16_t low,
uint16_t high, struct pf_ksrc_node **sn,
struct pf_srchash **sh, struct pf_kpool *rpool,
- struct pf_udp_mapping **udp_mapping)
+ struct pf_udp_mapping **udp_mapping, pf_sn_types_t sn_type)
{
struct pf_state_key_cmp key;
struct pf_addr init_addr;
@@ -262,7 +264,8 @@
/* Try to find a src_node as per pf_map_addr(). */
if (*sn == NULL && rpool->opts & PF_POOL_STICKYADDR &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
- *sn = pf_find_src_node(&pd->nsaddr, r, pd->af, sh, false);
+ *sn = pf_find_src_node(&pd->nsaddr, r,
+ pd->af, sh, sn_type, false);
if (*sn != NULL)
PF_SRC_NODE_UNLOCK(*sn);
return (0);
@@ -276,7 +279,7 @@
}
if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr,
- sn, sh, rpool))
+ sn, sh, rpool, sn_type))
goto failed;
if (pd->proto == IPPROTO_ICMP) {
@@ -400,7 +403,7 @@
*/
(*sn) = NULL;
if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL,
- &init_addr, sn, sh, rpool))
+ &init_addr, sn, sh, rpool, sn_type))
return (1);
break;
case PF_POOL_NONE:
@@ -453,14 +456,14 @@
low = (i << ashift) | psmask;
if (!pf_get_sport(pd, r,
naddr, nport, low, low | highmask, sn, sh, &r->rdr,
- udp_mapping))
+ udp_mapping, PF_SN_NAT))
return (0);
}
for (i = cut - 1; i > 0; i--) {
low = (i << ashift) | psmask;
if (!pf_get_sport(pd, r,
naddr, nport, low, low | highmask, sn, sh, &r->rdr,
- udp_mapping))
+ udp_mapping, PF_SN_NAT))
return (0);
}
return (1);
@@ -642,7 +645,8 @@
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, struct pf_srchash **sh, struct pf_kpool *rpool)
+ struct pf_ksrc_node **sn, struct pf_srchash **sh, struct pf_kpool *rpool,
+ pf_sn_types_t sn_type)
{
u_short reason = 0;
@@ -655,7 +659,7 @@
*/
if (rpool->opts & PF_POOL_STICKYADDR &&
(rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
- *sn = pf_find_src_node(saddr, r, af, sh, false);
+ *sn = pf_find_src_node(saddr, r, af, sh, sn_type, false);
if (*sn != NULL) {
PF_SRC_NODE_LOCK_ASSERT(*sn);
@@ -780,7 +784,7 @@
goto notrans;
}
} else if (pf_get_sport(pd, r, naddr, nportp, low, high, &sn,
- &sh, &r->rdr, udp_mapping)) {
+ &sh, &r->rdr, udp_mapping, PF_SN_NAT)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: NAT proxy port allocation (%u-%u) failed\n",
r->rdr.proxy_port[0], r->rdr.proxy_port[1]));
@@ -868,7 +872,7 @@
uint16_t cut, low, high, nport;
reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL,
- NULL, &sn, &sh, &r->rdr);
+ NULL, &sn, &sh, &r->rdr, PF_SN_NAT);
if (reason != 0)
goto notrans;
if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
@@ -1007,7 +1011,8 @@
/* get source address and port */
if (pf_get_sport(pd, r, &nsaddr, &nport,
- r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat, NULL)) {
+ r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat,
+ NULL, PF_SN_NAT)) {
DPFPRINTF(PF_DEBUG_MISC,
("pf: af-to NAT proxy port allocation (%u-%u) failed",
r->nat.proxy_port[0], r->nat.proxy_port[1]));
@@ -1051,7 +1056,7 @@
/* get the destination address and port */
if (! TAILQ_EMPTY(&r->rdr.list)) {
if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, NULL, NULL,
- &sns, NULL, &r->rdr))
+ &sns, NULL, &r->rdr, PF_SN_NAT))
return (-1);
if (r->rdr.proxy_port[0])
pd->ndport = htons(r->rdr.proxy_port[0]);
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -131,6 +131,7 @@
PF_ST_DNRPIPE = 35, /* u16 */
PF_ST_RT = 36, /* u8 */
PF_ST_RT_IFNAME = 37, /* string */
+ PF_ST_SRC_NODE_FLAGS = 38, /* u8 */
};
enum pf_addr_type_t {
@@ -271,6 +272,9 @@
PF_RT_NAF = 76, /* u8 */
PF_RT_RPOOL_RT = 77, /* nested, pf_rpool_type_t */
PF_RT_RCV_IFNOT = 78, /* bool */
+ PF_RT_SRC_NODES_LIMIT = 79, /* u64 */
+ PF_RT_SRC_NODES_NAT = 80, /* u64 */
+ PF_RT_SRC_NODES_ROUTE = 81, /* u64 */
};
enum pf_addrule_type_t {
@@ -425,6 +429,7 @@
PF_SN_EXPIRE = 13, /* u64 */
PF_SN_CONNECTION_RATE = 14, /* nested, pf_threshold */
PF_SN_NAF = 15, /* u8 */
+ PF_SN_NODE_TYPE = 16, /* u8 */
};
enum pf_tables_t {
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
@@ -186,9 +186,9 @@
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)
+ if (s->sns[PF_SN_LIMIT] != NULL)
sync_flags |= PFSYNC_FLAG_SRCNODE;
- if (s->nat_src_node)
+ if (s->sns[PF_SN_NAT] != NULL || s->sns[PF_SN_ROUTE])
sync_flags |= PFSYNC_FLAG_NATSRCNODE;
nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
nlattr_add_u64(nw, PF_ST_ID, s->id);
@@ -210,6 +210,17 @@
nlattr_add_u8(nw, PF_ST_RT, s->act.rt);
if (s->act.rt_kif != NULL)
nlattr_add_string(nw, PF_ST_RT_IFNAME, s->act.rt_kif->pfik_name);
+ uint8_t src_node_flags = 0;
+ if (s->sns[PF_SN_LIMIT] != NULL) {
+ src_node_flags |= PFSTATE_SRC_NODE_LIMIT;
+ if (s->sns[PF_SN_LIMIT]->rule == &V_pf_default_rule)
+ src_node_flags |= PFSTATE_SRC_NODE_LIMIT_GLOBAL;
+ }
+ if (s->sns[PF_SN_NAT] != NULL)
+ src_node_flags |= PFSTATE_SRC_NODE_NAT;
+ if (s->sns[PF_SN_ROUTE] != NULL)
+ src_node_flags |= PFSTATE_SRC_NODE_ROUTE;
+ nlattr_add_u8(nw, PF_ST_SRC_NODE_FLAGS, src_node_flags);
if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
goto enomem;
@@ -854,6 +865,7 @@
struct genlmsghdr *ghdr_new;
struct pf_kruleset *ruleset;
struct pf_krule *rule;
+ u_int64_t src_nodes_total = 0;
int rs_num;
int error;
@@ -985,7 +997,12 @@
nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule));
nlattr_add_u64(nw, PF_RT_STATES_CUR, counter_u64_fetch(rule->states_cur));
nlattr_add_u64(nw, PF_RT_STATES_TOTAL, counter_u64_fetch(rule->states_tot));
- nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes));
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+ src_nodes_total += counter_u64_fetch(rule->src_nodes[sn_type]);
+ nlattr_add_u64(nw, PF_RT_SRC_NODES, src_nodes_total);
+ nlattr_add_u64(nw, PF_RT_SRC_NODES_LIMIT, counter_u64_fetch(rule->src_nodes[PF_SN_LIMIT]));
+ nlattr_add_u64(nw, PF_RT_SRC_NODES_NAT, counter_u64_fetch(rule->src_nodes[PF_SN_NAT]));
+ nlattr_add_u64(nw, PF_RT_SRC_NODES_ROUTE, counter_u64_fetch(rule->src_nodes[PF_SN_ROUTE]));
error = pf_kanchor_copyout(ruleset, rule, anchor_call, sizeof(anchor_call));
MPASS(error == 0);
@@ -1785,6 +1802,8 @@
nlattr_add_pf_threshold(nw, PF_SN_CONNECTION_RATE,
&n->conn_rate, secs);
+ nlattr_add_u8(nw, PF_SN_NODE_TYPE, n->type);
+
if (!nlmsg_end(nw)) {
PF_HASHROW_UNLOCK(sh);
nlmsg_abort(nw);
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
@@ -684,6 +684,7 @@
pf_krule_to_nvrule(struct pf_krule *rule)
{
nvlist_t *nvl, *tmp;
+ u_int64_t src_nodes_total = 0;
nvl = nvlist_create(0);
if (nvl == NULL)
@@ -759,8 +760,9 @@
counter_u64_fetch(rule->states_cur));
nvlist_add_number(nvl, "states_tot",
counter_u64_fetch(rule->states_tot));
- nvlist_add_number(nvl, "src_nodes",
- counter_u64_fetch(rule->src_nodes));
+ for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+ src_nodes_total += counter_u64_fetch(rule->src_nodes[sn_type]);
+ nvlist_add_number(nvl, "src_nodes", src_nodes_total);
nvlist_add_number(nvl, "return_icmp", rule->return_icmp);
nvlist_add_number(nvl, "return_icmp6", rule->return_icmp6);
@@ -993,9 +995,9 @@
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)
+ if (s->sns[PF_SN_LIMIT] != NULL)
flags |= PFSYNC_FLAG_SRCNODE;
- if (s->nat_src_node)
+ if (s->sns[PF_SN_NAT] != NULL || s->sns[PF_SN_ROUTE])
flags |= PFSYNC_FLAG_NATSRCNODE;
nvlist_add_number(nvl, "sync_flags", flags);
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
@@ -241,11 +241,11 @@
nodes=$(mktemp) || exit 1
jexec router pfctl -qvsS | normalize_pfctl_s > $nodes
for node_regexp in \
- '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 3$' \
- '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4$' \
- '2001:db8:44::2 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4$' \
+ '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 3, limit source-track$' \
+ '2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4, limit source-track$' \
+ '2001:db8:44::2 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, filter rule 4, limit source-track$' \
; do
- grep -qE "$node_regexp" $nodes || atf_fail "Source nodes not matching expected output"
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
done
# Check if limit counters have been properly set.
@@ -257,10 +257,157 @@
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 | normalize_pfctl_s > $nodes
+ cat $nodes
+ node_regexp='2001:db8:44::1 -> :: \( states 3, connections 3, rate [0-9/\.]+s \) age [0-9:]+, 9 pkts, [0-9]+ bytes, limit source-track'
+ grep -qE "$node_regexp" $nodes || atf_fail "Source nodes not matching expected output"
+}
+
+max_src_states_global_cleanup()
+{
+ pft_cleanup
+}
+
+route_to_head()
+{
+ atf_set descr 'Max states per source per rule with route-to'
+ atf_set require.user root
+}
+
+route_to_body()
+{
+ setup_router_dummy_ipv6
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 2001:db8:44::0/64 2001:db8:42::2
+
+ # Additional gateways for route-to.
+ rtgw=${net_server_host_server%::*}::2:1
+ jexec router ndp -s ${rtgw} 00:01:02:03:04:05
+
+ # This test will check for proper source node creation for:
+ # max-src-states -> PF_SN_LIMIT
+ # sticky-address -> PF_SN_NAT
+ # route-to -> PF_SN_ROUTE
+ # The test expands to all 8 combinations of those source nodes being
+ # present or not.
+
+ pft_set_rules router \
+ "table <rtgws> { ${rtgw} }" \
+ "table <rdrgws> { 2001:db8:45::1 }" \
+ "rdr on ${epair_tester}b inet6 proto tcp from 2001:db8:44::10/124 to 2001:db8:45::1 -> <rdrgws> port 4242 sticky-address" \
+ "block" \
+ "pass inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) inet6 proto tcp from port 4211 keep state label rule_3" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) sticky-address inet6 proto tcp from port 4212 keep state label rule_4" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) inet6 proto tcp from port 4213 keep state (max-src-states 3 source-track rule) label rule_5" \
+ "pass in quick on ${epair_tester}b route-to ( ${epair_server}a <rtgws>) sticky-address inet6 proto tcp from port 4214 keep state (max-src-states 3 source-track rule) label rule_6" \
+ "pass out quick on ${epair_server}a keep state"
+
+ # We don't check if state limits are properly enforced, this is tested
+ # by other tests in this file.
+ # Source address will not match the NAT rule
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4211 --fromaddr 2001:db8:44::01 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4212 --fromaddr 2001:db8:44::02 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4213 --fromaddr 2001:db8:44::03 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4214 --fromaddr 2001:db8:44::04 --to 2001:db8:45::1
+ # Source address will match the NAT rule
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4211 --fromaddr 2001:db8:44::11 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4212 --fromaddr 2001:db8:44::12 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4213 --fromaddr 2001:db8:44::13 --to 2001:db8:45::1
+ ping_dummy_check_request exit:0 --ping-type=tcpsyn --send-sport=4214 --fromaddr 2001:db8:44::14 --to 2001:db8:45::1
+
+ states=$(mktemp) || exit 1
+ jexec router pfctl -qvss | normalize_pfctl_s > $states
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+
+ # Order of states in output is not guaranteed, find each one separately.
+ for state_regexp in \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::1\[4211\] .* 1:0 pkts, 76:0 bytes, rule 3$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::2\[4212\] .* 1:0 pkts, 76:0 bytes, rule 4, route sticky-address$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::3\[4213\] .* 1:0 pkts, 76:0 bytes, rule 5, limit source-track$' \
+ 'all tcp 2001:db8:45::1\[9\] <- 2001:db8:44::4\[4214\] .* 1:0 pkts, 76:0 bytes, rule 6, limit source-track, route sticky-address$' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::11\[4211\] .* 1:0 pkts, 76:0 bytes, rule 3, NAT/RDR sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::12\[4212\] .* 1:0 pkts, 76:0 bytes, rule 4, NAT/RDR sticky-address, route sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::13\[4213\] .* 1:0 pkts, 76:0 bytes, rule 5, limit source-track, NAT/RDR sticky-address' \
+ 'all tcp 2001:db8:45::1\[4242\] \(2001:db8:45::1\[9\]\) <- 2001:db8:44::14\[4214\] .* 1:0 pkts, 76:0 bytes, rule 6, limit source-track, NAT/RDR sticky-address, route sticky-address' \
+ ; do
+ grep -qE "${state_regexp}" $states || atf_fail "State not found for '${state_regexp}'"
+ done
+
+ # Order of source nodes in output is not guaranteed, find each one separately.
+ for node_regexp in \
+ '2001:db8:44::2 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 4, route sticky-address' \
+ '2001:db8:44::3 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 5, limit source-track' \
+ '2001:db8:44::4 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s ) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, route sticky-address' \
+ '2001:db8:44::4 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, limit source-track' \
+ '2001:db8:44::11 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::12 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::12 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 4, route sticky-address' \
+ '2001:db8:44::13 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::13 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 5, limit source-track' \
+ '2001:db8:44::14 -> 2001:db8:45::1 \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, rdr rule 0, NAT/RDR sticky-address' \
+ '2001:db8:44::14 -> 2001:db8:43::2:1 \( states 1, connections 0, rate 0.0/0s ) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, route sticky-address' \
+ '2001:db8:44::14 -> :: \( states 1, connections 0, rate 0.0/0s \) age [0-9:]+, 1 pkts, 76 bytes, filter rule 6, limit source-track' \
+ ; do
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+ done
+
+ ! grep -q 'filter rule 3' $nodes || atf_fail "Source node found for rule 3"
+}
+
+route_to_cleanup()
+{
+ pft_cleanup
+}
+
+
atf_init_test_cases()
{
atf_add_test_case "source_track"
atf_add_test_case "kill"
atf_add_test_case "max_src_conn_rule"
atf_add_test_case "max_src_states_rule"
+ atf_add_test_case "max_src_states_global"
+ atf_add_test_case "route_to"
}

File Metadata

Mime Type
text/plain
Expires
Sun, Jan 18, 7:23 AM (14 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27706330
Default Alt Text
D39880.diff (43 KB)

Event Timeline