The branch main has been updated by ks:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=f6f116cdbd2a406d2913df5368299ba4cdbf40a1

commit f6f116cdbd2a406d2913df5368299ba4cdbf40a1
Author:     Kajetan Staszkiewicz <k...@freebsd.org>
AuthorDate: 2025-02-23 18:13:48 +0000
Commit:     Kajetan Staszkiewicz <k...@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"

Reply via email to