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 @@ -412,8 +412,27 @@ return (PF_PASS); \ } while (0) -#define BOUND_IFACE(r, k) \ - ((r)->rule_flag & PFRULE_IFBOUND) ? (k) : V_pfi_all +static struct pfi_kkif * +BOUND_IFACE(struct pf_krule *r, struct pfi_kkif *k, struct pf_pdesc *pd) +{ + /* Floating unless otherwise specified. */ + if (! (r->rule_flag & PFRULE_IFBOUND)) + return (V_pfi_all); + + /* Don't overrule the interface for states created on incoming packets. */ + if (pd->dir == PF_IN) + return (k); + + /* No route-to, so don't overrrule. */ + if (r->rt != PF_ROUTETO) + return (k); + + if (r->rpool.cur == NULL) + return (k); + + /* Bind to the route-to interface. */ + return (r->rpool.cur->kif); +} #define STATE_INC_COUNTERS(s) \ do { \ @@ -1600,7 +1619,7 @@ /* List is sorted, if-bound states before floating ones. */ TAILQ_FOREACH(s, &sk->states[idx], key_list[idx]) - if (s->kif == V_pfi_all || s->kif == kif) { + if (s->kif == V_pfi_all || s->kif == kif || s->orig_kif == kif) { PF_STATE_LOCK(s); PF_HASHROW_UNLOCK(kh); if (__predict_false(s->timeout >= PFTM_MAX)) { @@ -4999,7 +5018,7 @@ __func__, nr, sk, nk)); /* Swap sk/nk for PF_OUT. */ - if (pf_state_insert(BOUND_IFACE(r, kif), kif, + if (pf_state_insert(BOUND_IFACE(r, kif, pd), kif, (pd->dir == PF_IN) ? sk : nk, (pd->dir == PF_IN) ? nk : sk, s)) { REASON_SET(&reason, PFRES_STATEINS); diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh --- a/tests/sys/netpfil/pf/route_to.sh +++ b/tests/sys/netpfil/pf/route_to.sh @@ -365,6 +365,48 @@ pft_cleanup } +atf_test_case "ifbound" "cleanup" +ifbound_head() +{ + atf_set descr 'Test that route-to states bind the expected interface' + atf_set require.user root +} + +ifbound_body() +{ + pft_init + + j="route_to:ifbound" + + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + ifconfig ${epair_one}b up + + vnet_mkjail ${j}2 ${epair_two}b + jexec ${j}2 ifconfig ${epair_two}b inet 198.51.100.2/24 up + jexec ${j}2 ifconfig ${epair_two}b inet alias 203.0.113.1/24 + jexec ${j}2 route add default 198.51.100.1 + + vnet_mkjail $j ${epair_one}a ${epair_two}a + jexec $j ifconfig ${epair_one}a 192.0.2.1/24 up + jexec $j ifconfig ${epair_two}a 198.51.100.1/24 up + jexec $j route add default 192.0.2.2 + + jexec $j pfctl -e + pft_set_rules $j \ + "set state-policy if-bound" \ + "block" \ + "pass out route-to (${epair_two}a 198.51.100.2)" + + atf_check -s exit:0 -o ignore \ + jexec $j ping -c 3 203.0.113.1 +} + +ifbound_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "v4" @@ -373,4 +415,5 @@ atf_add_test_case "multiwanlocal" atf_add_test_case "icmp_nat" atf_add_test_case "dummynet" + atf_add_test_case "ifbound" }