Page MenuHomeFreeBSD

D53231.id164677.diff
No OneTemporary

D53231.id164677.diff

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2435,6 +2435,7 @@
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_free_src_node(struct pf_ksrc_node *);
extern void pf_print_state(struct pf_kstate *);
extern void pf_print_flags(uint16_t);
extern int pf_addr_wrap_neq(struct pf_addr_wrap *,
@@ -2521,6 +2522,9 @@
void pfr_initialize(void);
void pfr_cleanup(void);
+struct pfr_kentry *
+ pfr_kentry_byaddr(struct pfr_ktable *, struct pf_addr *, sa_family_t,
+ int);
int pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
void pfr_update_stats(struct pfr_ktable *, struct pf_addr *, sa_family_t,
u_int64_t, int, int, int);
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
@@ -1007,7 +1007,7 @@
return (false);
}
-static void
+void
pf_free_src_node(struct pf_ksrc_node *sn)
{
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
@@ -535,6 +535,63 @@
return (1);
}
+static __inline u_short
+pf_check_src_node_valid(struct pf_ksrc_node *sn, struct pf_kpool *rpool)
+{
+ struct pf_addr *raddr, *rmask;
+ struct pf_addr *caddr; /* cached redirection address */
+ struct pf_kpooladdr *pa;
+ sa_family_t raf;
+ sa_family_t caf; /* cached redirection AF */
+ u_short valid = 0;
+
+ KASSERT(sn != NULL, ("sn is NULL"));
+ KASSERT(rpool != NULL, ("rpool is NULL"));
+
+ /* check if the cached entry is still valid */
+
+ if (sn->type == PF_SN_LIMIT) {
+ /* Always valid as it does not store redirection address */
+ return (1);
+ }
+
+ mtx_lock(&rpool->mtx);
+ caddr = &(sn->raddr);
+ caf = sn->raf;
+
+ TAILQ_FOREACH(pa, &rpool->list, entries) {
+ if (PF_AZERO(caddr, caf)) {
+ valid = 1;
+ goto done;
+ } else if (pa->addr.type == PF_ADDR_DYNIFTL) {
+ if (pfr_kentry_byaddr(pa->addr.p.dyn->pfid_kt, caddr, caf, 0)) {
+ valid = 1;
+ goto done;
+ }
+ } else if (pa->addr.type == PF_ADDR_TABLE) {
+ if (pfr_kentry_byaddr(pa->addr.p.tbl, caddr, caf, 0)) {
+ valid = 1;
+ goto done;
+ }
+ } else if (pa->addr.type != PF_ADDR_NOROUTE) {
+ /* PF_ADDR_URPFFAILED, PF_ADDR_RANGE, PF_ADDR_ADDRMASK */
+ raddr = &(pa->addr.v.a.addr);
+ rmask = &(pa->addr.v.a.mask);
+ raf = pa->af;
+ if (raf == caf && pf_match_addr(0, raddr, rmask, caddr, caf)) {
+ valid = 1;
+ goto done;
+ }
+ }
+ /* else PF_ADDR_NOROUTE */
+ }
+
+done:
+ mtx_unlock(&rpool->mtx);
+
+ return (valid);
+}
+
u_short
pf_map_addr(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr,
struct pf_addr *naddr, struct pfi_kkif **nkif, sa_family_t *naf,
@@ -874,6 +931,45 @@
if (sn != NULL) {
PF_SRC_NODE_LOCK_ASSERT(sn);
+ /*
+ * Check if source node's redirection address still exists
+ * in pool from which the SN was created. If not, delete it.
+ * Similar to pf_kill_srcnodes(). Unlink the source node
+ * from tree, unlink it from states, then free it. Do not
+ * overlap source node and state locks to avoid LOR.
+ */
+ if (!pf_check_src_node_valid(sn, rpool)) {
+ pf_unlink_src_node(sn);
+ PF_SRC_NODE_UNLOCK(sn);
+ if (V_pf_status.debug >= PF_DEBUG_NOISY) {
+ printf("%s: stale src tracking (%d) ",
+ __func__, sn_type);
+ pf_print_host(saddr, 0, saf);
+ printf(" to ");
+ pf_print_host(&(sn->raddr), 0, sn->raf);
+ if (nkif)
+ printf("@%s", sn->rkif->pfik_name);
+ printf("\n");
+ }
+
+ for (int i = 0; i <= V_pf_hashmask; i++) {
+ struct pf_idhash *ih = &V_pf_idhash[i];
+ struct pf_kstate *st;
+
+ PF_HASHROW_LOCK(ih);
+ LIST_FOREACH(st, &ih->states, entry) {
+ if (st->sns[sn->type] == sn) {
+ st->sns[sn->type] = NULL;
+ }
+ }
+ PF_HASHROW_UNLOCK(ih);
+ }
+ pf_free_src_node(sn);
+ counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
+ sn = NULL;
+ goto map_addr;
+ }
+
(*naf) = sn->raf;
/* If the supplied address is the same as the current one we've
@@ -902,9 +998,10 @@
goto done;
}
+map_addr:
/*
- * Source node has not been found. Find a new address and store it
- * in variables given by the caller.
+ * Source node has not been found or is invalid. Find a new address
+ * and store it in variables given by the caller.
*/
if ((reason = pf_map_addr(saf, r, saddr, naddr, nkif, naf, init_addr,
rpool)) != 0) {
diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c
--- a/sys/netpfil/pf/pf_table.c
+++ b/sys/netpfil/pf/pf_table.c
@@ -2071,7 +2071,7 @@
(struct pfr_ktable *)tbl));
}
-static struct pfr_kentry *
+struct pfr_kentry *
pfr_kentry_byaddr(struct pfr_ktable *kt, struct pf_addr *a, sa_family_t af,
int exact)
{
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
@@ -588,6 +588,79 @@
pft_cleanup
}
+atf_test_case "check_valid" "cleanup"
+check_valid_head()
+{
+ atf_set descr 'Test if source node is invalidated on change in redirection pool'
+ atf_set require.user root
+ atf_set require.progs python3 scapy
+}
+
+check_valid_body()
+{
+ setup_router_server_nat64
+
+ # Clients will connect from another network behind the router.
+ # This allows for using multiple source addresses.
+ jexec router route add -6 ${net_clients_6}::/${net_clients_6_mask} ${net_tester_6_host_tester}
+
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:1/128 alias
+ jexec server1 ifconfig ${epair_server1}b inet6 ${net_server1_6}::42:2/128 alias
+
+ jexec router pfctl -e
+ pft_set_rules router \
+ "set debug loud " \
+ "set state-policy if-bound" \
+ "table <targets> { ${net_server1_6}::42:1 }" \
+ "pass in on ${epair_tester}b \
+ route-to { (${epair_server1}a <targets>) } \
+ sticky-address \
+ proto tcp \
+ keep state"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4201
+
+ # A source node is created using the original redirection target
+ nodes=$(mktemp) || exit 1
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+ node_regexp='2001:db8:44::1 -> 2001:db8:4201::42:1 .* states 1,.* route sticky-address'
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+
+ # Change contents of the redirection table
+ echo ${net_server1_6}::42:2 | jexec router pfctl -Tr -t targets -f -
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4202
+
+ # The original source node was deleted, a new one was created.
+ # It has 1 states.
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+ node_regexp='2001:db8:44::1 -> 2001:db8:4201::42:2 .* states 1,.* route sticky-address'
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+
+ atf_check -s exit:0 ${common_dir}/pft_ping.py \
+ --sendif ${epair_tester}a --replyif ${epair_tester}a \
+ --fromaddr ${net_clients_6}::1 --to ${host_server_6} \
+ --ping-type=tcp3way --send-sport=4203
+
+ # Without redirection table change the source node is reused.
+ # It has 2 states.
+ jexec router pfctl -qvvsS | normalize_pfctl_s > $nodes
+ node_regexp='2001:db8:44::1 -> 2001:db8:4201::42:2 .* states 2,.* route sticky-address'
+ grep -qE "${node_regexp}" $nodes || atf_fail "Source node not found for '${node_regexp}'"
+}
+
+check_valid_cleanup()
+{
+ pft_cleanup
+}
+
+
atf_init_test_cases()
{
atf_add_test_case "source_track"
@@ -598,4 +671,5 @@
atf_add_test_case "sn_types_compat"
atf_add_test_case "sn_types_pass"
atf_add_test_case "mixed_af"
+ atf_add_test_case "check_valid"
}

File Metadata

Mime Type
text/plain
Expires
Thu, Jan 22, 12:14 AM (4 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27824374
Default Alt Text
D53231.id164677.diff (7 KB)

Event Timeline