mirror of
https://github.com/opnsense/src.git
synced 2026-06-09 08:43:19 -04:00
pf: make reply-to work with nat64
Just like route-to reply-to is problematic when used in combination with nat64.
In the normal (i.e. without nat64) flow we return immediately from pf_route().
However, with nat64 we need to continue and do a route lookup. In that case
we should not make the extra pf_test(PF_OUT) call to remain similar to the
non-nat64 flow.
We also have to fix the interface binding. We can only bind to the interface
after we've done the route lookup, not before.
Add a funcional test case, and a test for pfctl's rule printing.
Sponsored by: Rubicon Communications, LLC ("Netgate")
This commit is contained in:
parent
ca0e693453
commit
7a372bded8
7 changed files with 106 additions and 37 deletions
|
|
@ -5505,8 +5505,8 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
|
|||
problems++;
|
||||
}
|
||||
if (r->rule_flag & PFRULE_AFTO && r->rt) {
|
||||
if (r->rt != PF_ROUTETO) {
|
||||
yyerror("reply-to and dup-to "
|
||||
if (r->rt != PF_ROUTETO && r->rt != PF_REPLYTO) {
|
||||
yyerror("dup-to "
|
||||
"must not be used on af-to rules");
|
||||
problems++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -944,7 +944,8 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
|
|||
printf(" ");
|
||||
print_pool(&r->rdr, 0, 0, r->af, PF_PASS);
|
||||
print_pool(&r->route, 0, 0,
|
||||
r->rule_flag & PFRULE_AFTO ? r->naf : r->af, PF_PASS);
|
||||
r->rule_flag & PFRULE_AFTO && r->rt != PF_REPLYTO ? r->naf : r->af,
|
||||
PF_PASS);
|
||||
}
|
||||
if (r->af) {
|
||||
if (r->af == AF_INET)
|
||||
|
|
|
|||
1
sbin/pfctl/tests/files/pf1027.in
Normal file
1
sbin/pfctl/tests/files/pf1027.in
Normal file
|
|
@ -0,0 +1 @@
|
|||
pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 af-to inet from (epair0a)
|
||||
1
sbin/pfctl/tests/files/pf1027.ok
Normal file
1
sbin/pfctl/tests/files/pf1027.ok
Normal file
|
|
@ -0,0 +1 @@
|
|||
pass in on epair2b reply-to (epair0a 2001:db8::1) inet6 from any to 64:ff9b::/96 flags S/SA keep state af-to inet from (epair0a)
|
||||
|
|
@ -135,3 +135,4 @@ PFCTL_TEST(1023, "Test match log(matches)")
|
|||
PFCTL_TEST(1024, "nat64")
|
||||
PFCTL_TEST(1025, "nat64 with implicit address family")
|
||||
PFCTL_TEST(1026, "nat64 with route-to")
|
||||
PFCTL_TEST(1027, "nat64 with reply-to")
|
||||
|
|
|
|||
|
|
@ -8891,6 +8891,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
uint16_t ip_len, ip_off;
|
||||
uint16_t tmp;
|
||||
int r_dir;
|
||||
bool skip_test = false;
|
||||
|
||||
KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__));
|
||||
|
||||
|
|
@ -8941,12 +8942,15 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (((pd->act.rt == PF_REPLYTO) == (r_dir == pd->dir)) &&
|
||||
(pd->af == pd->naf)) {
|
||||
pf_dummynet(pd, s, r, m);
|
||||
if (s)
|
||||
PF_STATE_UNLOCK(s);
|
||||
return;
|
||||
if ((pd->act.rt == PF_REPLYTO) == (r_dir == pd->dir)) {
|
||||
if (pd->af == pd->naf) {
|
||||
pf_dummynet(pd, s, r, m);
|
||||
if (s)
|
||||
PF_STATE_UNLOCK(s);
|
||||
return;
|
||||
} else {
|
||||
skip_test = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -8954,9 +8958,15 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
* reply direction.
|
||||
*/
|
||||
if (pd->act.rt_kif && pd->act.rt_kif->pfik_ifp &&
|
||||
pd->af != pd->naf && r->naf != AF_INET) {
|
||||
/* Un-set ifp so we do a plain route lookup. */
|
||||
ifp = NULL;
|
||||
pd->af != pd->naf) {
|
||||
if (pd->act.rt == PF_ROUTETO && r->naf != AF_INET) {
|
||||
/* Un-set ifp so we do a plain route lookup. */
|
||||
ifp = NULL;
|
||||
}
|
||||
if (pd->act.rt == PF_REPLYTO && r->naf != AF_INET6) {
|
||||
/* Un-set ifp so we do a plain route lookup. */
|
||||
ifp = NULL;
|
||||
}
|
||||
}
|
||||
m0 = *m;
|
||||
}
|
||||
|
|
@ -8970,13 +8980,6 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
dst.sin_addr.s_addr = pd->act.rt_addr.v4.s_addr;
|
||||
|
||||
if (s != NULL){
|
||||
if (r->rule_flag & PFRULE_IFBOUND &&
|
||||
pd->act.rt == PF_REPLYTO &&
|
||||
s->kif == V_pfi_all) {
|
||||
s->kif = pd->act.rt_kif;
|
||||
s->orig_kif = oifp->if_pf_kif;
|
||||
}
|
||||
|
||||
if (ifp == NULL && (pd->af != pd->naf)) {
|
||||
/* We're in the AFTO case. Do a route lookup. */
|
||||
const struct nhop_object *nh;
|
||||
|
|
@ -9002,6 +9005,13 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
}
|
||||
}
|
||||
|
||||
if (r->rule_flag & PFRULE_IFBOUND &&
|
||||
pd->act.rt == PF_REPLYTO &&
|
||||
s->kif == V_pfi_all) {
|
||||
s->kif = pd->act.rt_kif;
|
||||
s->orig_kif = oifp->if_pf_kif;
|
||||
}
|
||||
|
||||
PF_STATE_UNLOCK(s);
|
||||
}
|
||||
|
||||
|
|
@ -9012,7 +9022,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
goto bad;
|
||||
}
|
||||
|
||||
if (pd->dir == PF_IN) {
|
||||
if (pd->dir == PF_IN && !skip_test) {
|
||||
if (pf_test(AF_INET, PF_OUT, PFIL_FWD, ifp, &m0, inp,
|
||||
&pd->act) != PF_PASS) {
|
||||
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
|
||||
|
|
@ -9162,6 +9172,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
struct ip6_hdr *ip6;
|
||||
struct ifnet *ifp = NULL;
|
||||
int r_dir;
|
||||
bool skip_test = false;
|
||||
|
||||
KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__));
|
||||
|
||||
|
|
@ -9212,12 +9223,15 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (((pd->act.rt == PF_REPLYTO) == (r_dir == pd->dir)) &&
|
||||
(pd->af == pd->naf)) {
|
||||
pf_dummynet(pd, s, r, m);
|
||||
if (s)
|
||||
PF_STATE_UNLOCK(s);
|
||||
return;
|
||||
if ((pd->act.rt == PF_REPLYTO) == (r_dir == pd->dir)) {
|
||||
if (pd->af == pd->naf) {
|
||||
pf_dummynet(pd, s, r, m);
|
||||
if (s)
|
||||
PF_STATE_UNLOCK(s);
|
||||
return;
|
||||
} else {
|
||||
skip_test = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -9225,9 +9239,15 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
* reply direction.
|
||||
*/
|
||||
if (pd->act.rt_kif && pd->act.rt_kif->pfik_ifp &&
|
||||
pd->af != pd->naf && r->naf != AF_INET6) {
|
||||
/* Un-set ifp so we do a plain route lookup. */
|
||||
ifp = NULL;
|
||||
pd->af != pd->naf) {
|
||||
if (pd->act.rt == PF_ROUTETO && r->naf != AF_INET6) {
|
||||
/* Un-set ifp so we do a plain route lookup. */
|
||||
ifp = NULL;
|
||||
}
|
||||
if (pd->act.rt == PF_REPLYTO && r->naf != AF_INET) {
|
||||
/* Un-set ifp so we do a plain route lookup. */
|
||||
ifp = NULL;
|
||||
}
|
||||
}
|
||||
m0 = *m;
|
||||
}
|
||||
|
|
@ -9241,13 +9261,6 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
PF_ACPY((struct pf_addr *)&dst.sin6_addr, &pd->act.rt_addr, AF_INET6);
|
||||
|
||||
if (s != NULL) {
|
||||
if (r->rule_flag & PFRULE_IFBOUND &&
|
||||
pd->act.rt == PF_REPLYTO &&
|
||||
s->kif == V_pfi_all) {
|
||||
s->kif = pd->act.rt_kif;
|
||||
s->orig_kif = oifp->if_pf_kif;
|
||||
}
|
||||
|
||||
if (ifp == NULL && (pd->af != pd->naf)) {
|
||||
const struct nhop_object *nh;
|
||||
nh = fib6_lookup(M_GETFIB(*m), &ip6->ip6_dst, 0, NHR_NONE, 0);
|
||||
|
|
@ -9273,6 +9286,13 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
}
|
||||
}
|
||||
|
||||
if (r->rule_flag & PFRULE_IFBOUND &&
|
||||
pd->act.rt == PF_REPLYTO &&
|
||||
s->kif == V_pfi_all) {
|
||||
s->kif = pd->act.rt_kif;
|
||||
s->orig_kif = oifp->if_pf_kif;
|
||||
}
|
||||
|
||||
PF_STATE_UNLOCK(s);
|
||||
}
|
||||
|
||||
|
|
@ -9293,7 +9313,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
|
|||
goto bad;
|
||||
}
|
||||
|
||||
if (pd->dir == PF_IN) {
|
||||
if (pd->dir == PF_IN && !skip_test) {
|
||||
if (pf_test(AF_INET6, PF_OUT, PFIL_FWD | PF_PFIL_NOREFRAGMENT,
|
||||
ifp, &m0, inp, &pd->act) != PF_PASS) {
|
||||
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
|
||||
|
|
|
|||
|
|
@ -717,6 +717,50 @@ route_to_cleanup()
|
|||
pft_cleanup
|
||||
}
|
||||
|
||||
atf_test_case "reply_to" "cleanup"
|
||||
reply_to_head()
|
||||
{
|
||||
atf_set descr 'Test reply-to on af-to rules'
|
||||
atf_set require.user root
|
||||
}
|
||||
|
||||
reply_to_body()
|
||||
{
|
||||
pft_init
|
||||
|
||||
epair_link=$(vnet_mkepair)
|
||||
epair=$(vnet_mkepair)
|
||||
|
||||
ifconfig ${epair}a inet6 2001:db8::2/64 up no_dad
|
||||
route -6 add default 2001:db8::1
|
||||
|
||||
vnet_mkjail rtr ${epair}b ${epair_link}a
|
||||
jexec rtr ifconfig ${epair}b inet6 2001:db8::1/64 up no_dad
|
||||
jexec rtr ifconfig ${epair_link}a 192.0.2.1/24 up
|
||||
|
||||
vnet_mkjail dst ${epair_link}b
|
||||
jexec dst ifconfig ${epair_link}b 192.0.2.2/24 up
|
||||
jexec dst route add default 192.0.2.1
|
||||
|
||||
# Sanity checks
|
||||
atf_check -s exit:0 -o ignore \
|
||||
ping6 -c 1 2001:db8::1
|
||||
|
||||
jexec rtr pfctl -e
|
||||
pft_set_rules rtr \
|
||||
"set reassemble yes" \
|
||||
"set state-policy if-bound" \
|
||||
"pass in on ${epair}b reply-to (${epair}b 2001:db8::2) inet6 from any to 64:ff9b::/96 af-to inet from 192.0.2.1"
|
||||
|
||||
atf_check -s exit:0 -o ignore \
|
||||
ping6 -c 3 64:ff9b::192.0.2.2
|
||||
}
|
||||
|
||||
reply_to_cleanup()
|
||||
{
|
||||
pft_cleanup
|
||||
}
|
||||
|
||||
atf_init_test_cases()
|
||||
{
|
||||
atf_add_test_case "icmp_echo"
|
||||
|
|
@ -734,4 +778,5 @@ atf_init_test_cases()
|
|||
atf_add_test_case "dummynet"
|
||||
atf_add_test_case "gateway6"
|
||||
atf_add_test_case "route_to"
|
||||
atf_add_test_case "reply_to"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue