git: f6f116cdbd2a - main - pf: Make af-to work on outbound interface
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 27 Feb 2025 15:33:55 UTC
The branch main has been updated by ks:
URL: https://cgit.FreeBSD.org/src/commit/?id=f6f116cdbd2a406d2913df5368299ba4cdbf40a1
commit f6f116cdbd2a406d2913df5368299ba4cdbf40a1
Author: Kajetan Staszkiewicz <ks@FreeBSD.org>
AuthorDate: 2025-02-23 18:13:48 +0000
Commit: Kajetan Staszkiewicz <ks@FreeBSD.org>
CommitDate: 2025-02-27 15:28:27 +0000
pf: Make af-to work on outbound interface
Currently af-to works only on inbound interface by creating a reversed
NAT state key which is used to match traffic returning on the outbound
interface.
Such limitation is not necessary. When an af-to state is created
for an outbound rule do not reverse the NAT state key, making it work
just like if it was created for a normal NAT rule. Depending on firewall
design it might be easier and more natural to use af-to on the outbound
interface.
Reviewed by: kp
Approved by: kp (mentor)
Sponsored by: InnoGames GmbH
Differential Revision: https://reviews.freebsd.org/D49122
---
sys/net/pfvar.h | 7 +-
sys/netpfil/pf/pf.c | 245 +++++++++++++++++++++++------------------
sys/netpfil/pf/pf_lb.c | 42 ++-----
tests/sys/netpfil/pf/nat64.sh | 247 +++++++++++++++++++++++++++++++++++-------
4 files changed, 368 insertions(+), 173 deletions(-)
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index b481f767725d..b9b7f71c07d1 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1005,9 +1005,10 @@ struct pf_state_key {
TAILQ_HEAD(, pf_kstate) states[2];
};
-#define PF_REVERSED_KEY(key, family) \
- ((key[PF_SK_WIRE]->af != key[PF_SK_STACK]->af) && \
- (key[PF_SK_WIRE]->af != (family)))
+#define PF_REVERSED_KEY(state, family) \
+ (((state)->key[PF_SK_WIRE]->af != (state)->key[PF_SK_STACK]->af) && \
+ ((state)->key[PF_SK_WIRE]->af != (family)) && \
+ ((state)->direction == PF_IN))
/* Keep synced with struct pf_kstate. */
struct pf_state_cmp {
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 24ddf75936de..72e648b84b2f 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -351,9 +351,13 @@ static int pf_create_state(struct pf_krule *, struct pf_krule *,
static int pf_state_key_addr_setup(struct pf_pdesc *,
struct pf_state_key_cmp *, int);
static int pf_tcp_track_full(struct pf_kstate **,
- struct pf_pdesc *, u_short *, int *);
+ struct pf_pdesc *, u_short *, int *,
+ struct pf_state_peer *, struct pf_state_peer *,
+ u_int8_t, u_int8_t);
static int pf_tcp_track_sloppy(struct pf_kstate **,
- struct pf_pdesc *, u_short *);
+ struct pf_pdesc *, u_short *,
+ struct pf_state_peer *, struct pf_state_peer *,
+ u_int8_t, u_int8_t);
static int pf_test_state(struct pf_kstate **, struct pf_pdesc *,
u_short *);
int pf_icmp_state_lookup(struct pf_state_key_cmp *,
@@ -458,7 +462,7 @@ BOUND_IFACE(struct pf_kstate *st, struct pf_pdesc *pd)
* Initially set to all, because we don't know what interface we'll be
* sending this out when we create the state.
*/
- if (st->rule->rt == PF_REPLYTO || (pd->af != pd->naf))
+ if (st->rule->rt == PF_REPLYTO || (pd->af != pd->naf && st->direction == PF_IN))
return (V_pfi_all);
/*
@@ -1739,11 +1743,18 @@ pf_state_key_setup(struct pf_pdesc *pd, u_int16_t sport, u_int16_t dport,
*/
bzero(&(*nk)->addr[0], sizeof((*nk)->addr[0]));
bzero(&(*nk)->addr[1], sizeof((*nk)->addr[1]));
+ if (pd->dir == PF_IN) {
+ PF_ACPY(&(*nk)->addr[pd->didx], &pd->nsaddr, pd->naf);
+ PF_ACPY(&(*nk)->addr[pd->sidx], &pd->ndaddr, pd->naf);
+ (*nk)->port[pd->didx] = pd->nsport;
+ (*nk)->port[pd->sidx] = pd->ndport;
+ } else {
+ PF_ACPY(&(*nk)->addr[pd->sidx], &pd->nsaddr, pd->naf);
+ PF_ACPY(&(*nk)->addr[pd->didx], &pd->ndaddr, pd->naf);
+ (*nk)->port[pd->sidx] = pd->nsport;
+ (*nk)->port[pd->didx] = pd->ndport;
+ }
- PF_ACPY(&(*nk)->addr[pd->didx], &pd->nsaddr, pd->naf);
- PF_ACPY(&(*nk)->addr[pd->sidx], &pd->ndaddr, pd->naf);
- (*nk)->port[pd->didx] = pd->nsport;
- (*nk)->port[pd->sidx] = pd->ndport;
switch (pd->proto) {
case IPPROTO_ICMP:
(*nk)->proto = IPPROTO_ICMPV6;
@@ -5934,24 +5945,25 @@ nextrule:
nat64 = pd->af != pd->naf;
if (nat64) {
- struct pf_state_key *_sk;
int ret;
if (sk == NULL)
sk = (*sm)->key[pd->dir == PF_IN ? PF_SK_STACK : PF_SK_WIRE];
if (nk == NULL)
nk = (*sm)->key[pd->dir == PF_IN ? PF_SK_WIRE : PF_SK_STACK];
- if (pd->dir == PF_IN)
- _sk = sk;
- else
- _sk = nk;
-
- ret = pf_translate(pd,
- &_sk->addr[pd->didx],
- _sk->port[pd->didx],
- &_sk->addr[pd->sidx],
- _sk->port[pd->sidx],
- virtual_type, icmp_dir);
+
+ if (pd->dir == PF_IN) {
+ ret = pf_translate(pd, &sk->addr[pd->didx],
+ sk->port[pd->didx], &sk->addr[pd->sidx],
+ sk->port[pd->sidx], virtual_type,
+ icmp_dir);
+ } else {
+ ret = pf_translate(pd, &sk->addr[pd->sidx],
+ sk->port[pd->sidx], &sk->addr[pd->didx],
+ sk->port[pd->didx], virtual_type,
+ icmp_dir);
+ }
+
if (ret < 0)
goto cleanup;
@@ -6383,37 +6395,15 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
static int
pf_tcp_track_full(struct pf_kstate **state, struct pf_pdesc *pd,
- u_short *reason, int *copyback)
+ u_short *reason, int *copyback, struct pf_state_peer *src,
+ struct pf_state_peer *dst, u_int8_t psrc, u_int8_t pdst)
{
struct tcphdr *th = &pd->hdr.tcp;
- struct pf_state_peer *src, *dst;
u_int16_t win = ntohs(th->th_win);
u_int32_t ack, end, data_end, seq, orig_seq;
- u_int8_t sws, dws, psrc, pdst;
+ u_int8_t sws, dws;
int ackskew;
- if (pd->dir == (*state)->direction) {
- if (PF_REVERSED_KEY((*state)->key, pd->af)) {
- src = &(*state)->dst;
- dst = &(*state)->src;
- } else {
- src = &(*state)->src;
- dst = &(*state)->dst;
- }
- psrc = PF_PEER_SRC;
- pdst = PF_PEER_DST;
- } else {
- if (PF_REVERSED_KEY((*state)->key, pd->af)) {
- src = &(*state)->src;
- dst = &(*state)->dst;
- } else {
- src = &(*state)->dst;
- dst = &(*state)->src;
- }
- psrc = PF_PEER_DST;
- pdst = PF_PEER_SRC;
- }
-
if (src->wscale && dst->wscale && !(tcp_get_flags(th) & TH_SYN)) {
sws = src->wscale & PF_WSCALE_MASK;
dws = dst->wscale & PF_WSCALE_MASK;
@@ -6733,23 +6723,11 @@ pf_tcp_track_full(struct pf_kstate **state, struct pf_pdesc *pd,
}
static int
-pf_tcp_track_sloppy(struct pf_kstate **state, struct pf_pdesc *pd, u_short *reason)
+pf_tcp_track_sloppy(struct pf_kstate **state, struct pf_pdesc *pd,
+ u_short *reason, struct pf_state_peer *src, struct pf_state_peer *dst,
+ u_int8_t psrc, u_int8_t pdst)
{
struct tcphdr *th = &pd->hdr.tcp;
- struct pf_state_peer *src, *dst;
- u_int8_t psrc, pdst;
-
- if (pd->dir == (*state)->direction) {
- src = &(*state)->src;
- dst = &(*state)->dst;
- psrc = PF_PEER_SRC;
- pdst = PF_PEER_DST;
- } else {
- src = &(*state)->dst;
- dst = &(*state)->src;
- psrc = PF_PEER_DST;
- pdst = PF_PEER_SRC;
- }
if (tcp_get_flags(th) & TH_SYN)
if (src->state < TCPS_SYN_SENT)
@@ -6932,15 +6910,29 @@ pf_test_state(struct pf_kstate **state, struct pf_pdesc *pd, u_short *reason)
STATE_LOOKUP(&key, *state, pd);
if (pd->dir == (*state)->direction) {
- src = &(*state)->src;
- dst = &(*state)->dst;
- psrc = PF_PEER_SRC;
- pdst = PF_PEER_DST;
+ if (PF_REVERSED_KEY(*state, pd->af)) {
+ src = &(*state)->dst;
+ dst = &(*state)->src;
+ psrc = PF_PEER_DST;
+ pdst = PF_PEER_SRC;
+ } else {
+ src = &(*state)->src;
+ dst = &(*state)->dst;
+ psrc = PF_PEER_SRC;
+ pdst = PF_PEER_DST;
+ }
} else {
- src = &(*state)->dst;
- dst = &(*state)->src;
- psrc = PF_PEER_DST;
- pdst = PF_PEER_SRC;
+ if (PF_REVERSED_KEY(*state, pd->af)) {
+ src = &(*state)->src;
+ dst = &(*state)->dst;
+ psrc = PF_PEER_SRC;
+ pdst = PF_PEER_DST;
+ } else {
+ src = &(*state)->dst;
+ dst = &(*state)->src;
+ psrc = PF_PEER_DST;
+ pdst = PF_PEER_SRC;
+ }
}
switch (pd->virtual_proto) {
@@ -6967,13 +6959,14 @@ pf_test_state(struct pf_kstate **state, struct pf_pdesc *pd, u_short *reason)
return (PF_DROP);
}
if ((*state)->state_flags & PFSTATE_SLOPPY) {
- if (pf_tcp_track_sloppy(state, pd, reason) == PF_DROP)
+ if (pf_tcp_track_sloppy(state, pd, reason, src, dst,
+ psrc, pdst) == PF_DROP)
return (PF_DROP);
} else {
int ret;
ret = pf_tcp_track_full(state, pd, reason,
- ©back);
+ ©back, src, dst, psrc, pdst);
if (ret == PF_DROP)
return (PF_DROP);
}
@@ -7069,26 +7062,32 @@ pf_test_state(struct pf_kstate **state, struct pf_pdesc *pd, u_short *reason)
struct pf_state_key *nk;
int afto, sidx, didx;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
- sidx = afto ? pd->didx : pd->sidx;
- didx = afto ? pd->sidx : pd->didx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd->didx;
+ didx = pd->sidx;
+ } else {
+ sidx = pd->sidx;
+ didx = pd->didx;
+ }
if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) ||
nk->port[sidx] != pd->osport)
pf_change_ap(pd->m, pd->src, pd->sport, pd->ip_sum,
- pd->pcksum, &nk->addr[pd->sidx],
+ pd->pcksum, &nk->addr[sidx],
nk->port[sidx], pd->virtual_proto == IPPROTO_UDP,
pd->af, nk->af);
if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
nk->port[didx] != pd->odport)
pf_change_ap(pd->m, pd->dst, pd->dport, pd->ip_sum,
- pd->pcksum, &nk->addr[pd->didx],
+ pd->pcksum, &nk->addr[didx],
nk->port[didx], pd->virtual_proto == IPPROTO_UDP,
pd->af, nk->af);
@@ -7114,12 +7113,12 @@ pf_sctp_track(struct pf_kstate *state, struct pf_pdesc *pd,
{
struct pf_state_peer *src;
if (pd->dir == state->direction) {
- if (PF_REVERSED_KEY(state->key, pd->af))
+ if (PF_REVERSED_KEY(state, pd->af))
src = &state->dst;
else
src = &state->src;
} else {
- if (PF_REVERSED_KEY(state->key, pd->af))
+ if (PF_REVERSED_KEY(state, pd->af))
src = &state->src;
else
src = &state->dst;
@@ -7679,15 +7678,21 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
struct pf_state_key *nk;
int afto, sidx, didx;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
afto = pd->af != nk->af;
- sidx = afto ? pd->didx : pd->sidx;
- didx = afto ? pd->sidx : pd->didx;
- iidx = afto ? !iidx : iidx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd->didx;
+ didx = pd->sidx;
+ iidx = !iidx;
+ } else {
+ sidx = pd->sidx;
+ didx = pd->didx;
+ }
switch (pd->af) {
#ifdef INET
@@ -7894,7 +7899,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
STATE_LOOKUP(&key, *state, pd);
if (pd->dir == (*state)->direction) {
- if (PF_REVERSED_KEY((*state)->key, pd->af)) {
+ if (PF_REVERSED_KEY(*state, pd->af)) {
src = &(*state)->src;
dst = &(*state)->dst;
} else {
@@ -7902,7 +7907,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
dst = &(*state)->src;
}
} else {
- if (PF_REVERSED_KEY((*state)->key, pd->af)) {
+ if (PF_REVERSED_KEY(*state, pd->af)) {
src = &(*state)->dst;
dst = &(*state)->src;
} else {
@@ -7958,7 +7963,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
struct pf_state_key *nk;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
@@ -7967,8 +7972,14 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
int afto, sidx, didx;
afto = pd->af != nk->af;
- sidx = afto ? pd2.didx : pd2.sidx;
- didx = afto ? pd2.sidx : pd2.didx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd2.didx;
+ didx = pd2.sidx;
+ } else {
+ sidx = pd2.sidx;
+ didx = pd2.didx;
+ }
if (afto) {
if (pf_translate_icmp_af(nk->af,
@@ -8073,7 +8084,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
@@ -8082,8 +8093,14 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
int afto, sidx, didx;
afto = pd->af != nk->af;
- sidx = afto ? pd2.didx : pd2.sidx;
- didx = afto ? pd2.sidx : pd2.didx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd2.didx;
+ didx = pd2.sidx;
+ } else {
+ sidx = pd2.sidx;
+ didx = pd2.didx;
+ }
if (afto) {
if (pf_translate_icmp_af(nk->af,
@@ -8183,12 +8200,12 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
STATE_LOOKUP(&key, *state, pd);
if (pd->dir == (*state)->direction) {
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
src = &(*state)->src;
else
src = &(*state)->dst;
} else {
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
src = &(*state)->dst;
else
src = &(*state)->src;
@@ -8207,7 +8224,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
struct pf_state_key *nk;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
@@ -8216,8 +8233,14 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
int afto, sidx, didx;
afto = pd->af != nk->af;
- sidx = afto ? pd2.didx : pd2.sidx;
- didx = afto ? pd2.sidx : pd2.didx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd2.didx;
+ didx = pd2.sidx;
+ } else {
+ sidx = pd2.sidx;
+ didx = pd2.didx;
+ }
if (afto) {
if (pf_translate_icmp_af(nk->af,
@@ -8325,7 +8348,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
@@ -8334,9 +8357,15 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
int afto, sidx, didx;
afto = pd->af != nk->af;
- sidx = afto ? pd2.didx : pd2.sidx;
- didx = afto ? pd2.sidx : pd2.didx;
- iidx = afto ? !iidx : iidx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd2.didx;
+ didx = pd2.sidx;
+ iidx = !iidx;
+ } else {
+ sidx = pd2.sidx;
+ didx = pd2.didx;
+ }
if (afto) {
if (nk->af != AF_INET6)
@@ -8437,7 +8466,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
(*state)->key[PF_SK_STACK]) {
struct pf_state_key *nk;
- if (PF_REVERSED_KEY((*state)->key, pd->af))
+ if (PF_REVERSED_KEY(*state, pd->af))
nk = (*state)->key[pd->sidx];
else
nk = (*state)->key[pd->didx];
@@ -8446,9 +8475,15 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
int afto, sidx, didx;
afto = pd->af != nk->af;
- sidx = afto ? pd2.didx : pd2.sidx;
- didx = afto ? pd2.sidx : pd2.didx;
- iidx = afto ? !iidx : iidx;
+
+ if (afto && (*state)->direction == PF_IN) {
+ sidx = pd2.didx;
+ didx = pd2.sidx;
+ iidx = !iidx;
+ } else {
+ sidx = pd2.sidx;
+ didx = pd2.didx;
+ }
if (afto) {
if (nk->af != AF_INET)
@@ -8732,7 +8767,9 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
PF_STATE_UNLOCK(s);
return;
} else {
- skip_test = true;
+ if (r_dir == PF_IN) {
+ skip_test = true;
+ }
}
}
@@ -9014,7 +9051,9 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
PF_STATE_UNLOCK(s);
return;
} else {
- skip_test = true;
+ if (r_dir == PF_IN) {
+ skip_test = true;
+ }
}
}
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index d1ba2495dc30..cb1d7af258f3 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -1053,37 +1053,19 @@ pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc *pd)
}
if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
- if (pd->dir == PF_IN) {
- NTOHS(pd->ndport);
- if (pd->ndport == ICMP6_ECHO_REQUEST)
- pd->ndport = ICMP_ECHO;
- else if (pd->ndport == ICMP6_ECHO_REPLY)
- pd->ndport = ICMP_ECHOREPLY;
- HTONS(pd->ndport);
- } else {
- NTOHS(pd->nsport);
- if (pd->nsport == ICMP6_ECHO_REQUEST)
- pd->nsport = ICMP_ECHO;
- else if (pd->nsport == ICMP6_ECHO_REPLY)
- pd->nsport = ICMP_ECHOREPLY;
- HTONS(pd->nsport);
- }
+ NTOHS(pd->ndport);
+ if (pd->ndport == ICMP6_ECHO_REQUEST)
+ pd->ndport = ICMP_ECHO;
+ else if (pd->ndport == ICMP6_ECHO_REPLY)
+ pd->ndport = ICMP_ECHOREPLY;
+ HTONS(pd->ndport);
} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
- if (pd->dir == PF_IN) {
- NTOHS(pd->ndport);
- if (pd->ndport == ICMP_ECHO)
- pd->ndport = ICMP6_ECHO_REQUEST;
- else if (pd->ndport == ICMP_ECHOREPLY)
- pd->ndport = ICMP6_ECHO_REPLY;
- HTONS(pd->ndport);
- } else {
- NTOHS(pd->nsport);
- if (pd->nsport == ICMP_ECHO)
- pd->nsport = ICMP6_ECHO_REQUEST;
- else if (pd->nsport == ICMP_ECHOREPLY)
- pd->nsport = ICMP6_ECHO_REPLY;
- HTONS(pd->nsport);
- }
+ NTOHS(pd->ndport);
+ if (pd->ndport == ICMP_ECHO)
+ pd->ndport = ICMP6_ECHO_REQUEST;
+ else if (pd->ndport == ICMP_ECHOREPLY)
+ pd->ndport = ICMP6_ECHO_REPLY;
+ HTONS(pd->ndport);
}
/* get the destination address and port */
diff --git a/tests/sys/netpfil/pf/nat64.sh b/tests/sys/netpfil/pf/nat64.sh
index 94c6c5fd8c8f..0bba1470c4c5 100644
--- a/tests/sys/netpfil/pf/nat64.sh
+++ b/tests/sys/netpfil/pf/nat64.sh
@@ -26,7 +26,7 @@
. $(atf_get_srcdir)/utils.subr
-nat64_setup()
+nat64_setup_base()
{
pft_init
@@ -51,22 +51,70 @@ nat64_setup()
jexec dst ping -c 1 192.0.2.1
jexec rtr pfctl -e
+}
+
+nat64_setup_in()
+{
+ nat64_setup_base
pft_set_rules rtr \
"set reassemble yes" \
"set state-policy if-bound" \
"pass in on ${epair}b inet6 from any to 64:ff9b::/96 af-to inet from (${epair_link}a)"
}
-atf_test_case "icmp_echo" "cleanup"
-icmp_echo_head()
+nat64_setup_out()
+{
+ nat64_setup_base
+ jexec rtr sysctl net.inet6.ip6.forwarding=1
+ # AF translation happens post-routing, traffic must be directed
+ # towards the outbound interface using routes for the original AF.
+ # jexec rtr ifconfig ${epair_link}a inet6 2001:db8:2::1/64 up no_dad
+ jexec rtr route add -inet6 64:ff9b::/96 -iface ${epair_link}a;
+ pft_set_rules rtr \
+ "set reassemble yes" \
+ "set state-policy if-bound" \
+ "pass quick inet6 proto icmp6 icmp6-type { neighbrsol, neighbradv }" \
+ "pass in quick on ${epair}b from any to 64:ff9b::/96" \
+ "pass out quick on ${epair_link}a from any to 64:ff9b::/96 af-to inet from (${epair_link}a)" \
+ "block"
+}
+
+atf_test_case "icmp_echo_in" "cleanup"
+icmp_echo_in_head()
+{
+ atf_set descr 'Basic NAT64 ICMP echo test on inbound interface'
+ atf_set require.user root
+}
+
+icmp_echo_in_body()
+{
+ nat64_setup_in
+
+ # One ping
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ # Make sure packets make it even when state is established
+ atf_check -s exit:0 \
+ -o match:'5 packets transmitted, 5 packets received, 0.0% packet loss' \
+ ping6 -c 5 64:ff9b::192.0.2.2
+}
+
+icmp_echo_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "icmp_echo_out" "cleanup"
+icmp_echo_out_head()
{
- atf_set descr 'Basic NAT64 ICMP echo test'
+ atf_set descr 'Basic NAT64 ICMP echo test on outbound interface'
atf_set require.user root
}
-icmp_echo_body()
+icmp_echo_out_body()
{
- nat64_setup
+ nat64_setup_out
# One ping
atf_check -s exit:0 -o ignore \
@@ -78,21 +126,21 @@ icmp_echo_body()
ping6 -c 5 64:ff9b::192.0.2.2
}
-icmp_echo_cleanup()
+icmp_echo_out_cleanup()
{
pft_cleanup
}
-atf_test_case "fragmentation" "cleanup"
-fragmentation_head()
+atf_test_case "fragmentation_in" "cleanup"
+fragmentation_in_head()
{
- atf_set descr 'Test fragmented packets'
+ atf_set descr 'Test fragmented packets on inbound interface'
atf_set require.user root
}
-fragmentation_body()
+fragmentation_in_body()
{
- nat64_setup
+ nat64_setup_in
atf_check -s exit:0 -o ignore \
ping6 -c 1 -s 1280 64:ff9b::192.0.2.2
@@ -105,21 +153,48 @@ fragmentation_body()
ping6 -c 3 -s 10000 -b 20000 64:ff9b::192.0.2.2
}
-fragmentation_cleanup()
+fragmentation_in_cleanup()
{
pft_cleanup
}
-atf_test_case "tcp" "cleanup"
-tcp_head()
+atf_test_case "fragmentation_out" "cleanup"
+fragmentation_out_head()
{
- atf_set descr 'TCP NAT64 test'
+ atf_set descr 'Test fragmented packets on outbound interface'
atf_set require.user root
}
-tcp_body()
+fragmentation_out_body()
{
- nat64_setup
+ nat64_setup_out
+
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 -s 1280 64:ff9b::192.0.2.2
+
+ atf_check -s exit:0 \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
+ ping6 -c 3 -s 2000 64:ff9b::192.0.2.2
+ atf_check -s exit:0 \
+ -o match:'3 packets transmitted, 3 packets received, 0.0% packet loss' \
+ ping6 -c 3 -s 10000 -b 20000 64:ff9b::192.0.2.2
+}
+
+fragmentation_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "tcp_in" "cleanup"
+tcp_in_head()
+{
+ atf_set descr 'TCP NAT64 test on inbound interface'
+ atf_set require.user root
+}
+
+tcp_in_body()
+{
+ nat64_setup_in
echo "foo" | jexec dst nc -l 1234 &
@@ -135,21 +210,81 @@ tcp_body()
fi
}
-tcp_cleanup()
+tcp_in_cleanup()
{
pft_cleanup
}
-atf_test_case "udp" "cleanup"
-udp_head()
+atf_test_case "tcp_out" "cleanup"
+tcp_out_head()
{
- atf_set descr 'UDP NAT64 test'
+ atf_set descr 'TCP NAT64 test on outbound interface'
atf_set require.user root
}
-udp_body()
+tcp_out_body()
{
- nat64_setup
+ nat64_setup_out
+
+ echo "foo" | jexec dst nc -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(nc -w 3 -6 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to TCP server"
+ fi
+}
+
+tcp_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "udp_in" "cleanup"
+udp_in_head()
+{
+ atf_set descr 'UDP NAT64 test on inbound interface'
+ atf_set require.user root
+}
+
+udp_in_body()
+{
+ nat64_setup_in
+
+ echo "foo" | jexec dst nc -u -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(echo bar | nc -w 3 -6 -u 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to UDP server"
+ fi
+}
+
+udp_in_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "udp_out" "cleanup"
+udp_out_head()
+{
+ atf_set descr 'UDP NAT64 test on outbound interface'
+ atf_set require.user root
+}
+
+udp_out_body()
+{
+ nat64_setup_out
echo "foo" | jexec dst nc -u -l 1234 &
@@ -165,21 +300,54 @@ udp_body()
fi
}
-udp_cleanup()
+udp_out_cleanup()
+{
+ pft_cleanup
+}
+
+atf_test_case "sctp_in" "cleanup"
+sctp_in_head()
+{
+ atf_set descr 'SCTP NAT64 test on inbound interface'
+ atf_set require.user root
+}
+
+sctp_in_body()
+{
+ nat64_setup_in
+ if ! kldstat -q -m sctp; then
+ atf_skip "This test requires SCTP"
+ fi
+
+ echo "foo" | jexec dst nc --sctp -N -l 1234 &
+
+ # Sanity check & delay for nc startup
+ atf_check -s exit:0 -o ignore \
+ ping6 -c 1 64:ff9b::192.0.2.2
+
+ rcv=$(echo bar | nc --sctp -w 3 -6 64:ff9b::c000:202 1234)
+ if [ "${rcv}" != "foo" ];
+ then
+ echo "rcv=${rcv}"
+ atf_fail "Failed to connect to SCTP server"
+ fi
+}
+
+sctp_in_cleanup()
{
pft_cleanup
}
-atf_test_case "sctp" "cleanup"
-sctp_head()
+atf_test_case "sctp_out" "cleanup"
+sctp_out_head()
{
- atf_set descr 'SCTP NAT64 test'
+ atf_set descr 'SCTP NAT64 test on outbound interface'
atf_set require.user root
}
-sctp_body()
+sctp_out_body()
{
- nat64_setup
+ nat64_setup_out
if ! kldstat -q -m sctp; then
atf_skip "This test requires SCTP"
fi
@@ -198,7 +366,7 @@ sctp_body()
fi
}
-sctp_cleanup()
+sctp_out_cleanup()
{
pft_cleanup
}
@@ -212,7 +380,7 @@ tos_head()
tos_body()
{
- nat64_setup
+ nat64_setup_in
# Ensure we can distinguish ToS on the destination
jexec dst pfctl -e
@@ -862,11 +1030,16 @@ v6_gateway_cleanup()
atf_init_test_cases()
{
- atf_add_test_case "icmp_echo"
- atf_add_test_case "fragmentation"
- atf_add_test_case "tcp"
- atf_add_test_case "udp"
- atf_add_test_case "sctp"
+ atf_add_test_case "icmp_echo_in"
+ atf_add_test_case "icmp_echo_out"
+ atf_add_test_case "fragmentation_in"
+ atf_add_test_case "fragmentation_out"
+ atf_add_test_case "tcp_in"
+ atf_add_test_case "tcp_out"
+ atf_add_test_case "udp_in"
+ atf_add_test_case "udp_out"
+ atf_add_test_case "sctp_in"
+ atf_add_test_case "sctp_out"
atf_add_test_case "tos"
atf_add_test_case "no_v4"
atf_add_test_case "range"