Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 27 Feb 2025 15:33:55 GMT
From:      Kajetan Staszkiewicz <ks@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: f6f116cdbd2a - main - pf: Make af-to work on outbound interface
Message-ID:  <202502271533.51RFXtl7065945@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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,
-			    &copyback);
+			    &copyback, 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"



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202502271533.51RFXtl7065945>