git: d89a5d853e2b - main - pf: support nat64 for SCTP

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 17 Dec 2024 10:07:46 UTC
The branch main has been updated by kp:

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

commit d89a5d853e2bd86490268b55ecdc7e77fefc4ba9
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2024-11-07 14:05:49 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-12-17 10:07:13 +0000

    pf: support nat64 for SCTP
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D47789
---
 sys/netpfil/pf/pf.c | 88 +++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 69 insertions(+), 19 deletions(-)

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 36bb0e1975fa..234cbc0854be 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -6265,6 +6265,21 @@ pf_translate(struct pf_pdesc *pd, struct pf_addr *saddr, u_int16_t sport,
 		}
 		break;
 
+	case IPPROTO_SCTP: {
+		uint16_t checksum = 0;
+		if (afto || *pd->sport != sport) {
+			pf_change_ap(pd->m, pd->src, pd->sport, pd->ip_sum, &checksum,
+			    saddr, sport, 1, pd->af, pd->naf);
+			rewrite = 1;
+		}
+		if (afto || *pd->dport != dport) {
+			pf_change_ap(pd->m, pd->dst, pd->dport, pd->ip_sum, &checksum,
+			    daddr, dport, 1, pd->af, pd->naf);
+			rewrite = 1;
+		}
+		break;
+	}
+
 #ifdef INET
 	case IPPROTO_ICMP:
 		/* pf_translate() is also used when logging invalid packets */
@@ -7047,6 +7062,33 @@ pf_test_state_udp(struct pf_kstate **state, struct pf_pdesc *pd)
 	return (action);
 }
 
+static int
+pf_sctp_track(struct pf_kstate *state, struct pf_pdesc *pd,
+    u_short *reason)
+{
+	struct pf_state_peer	*src;
+	if (pd->dir == state->direction) {
+		if (PF_REVERSED_KEY(state->key, pd->af))
+			src = &state->dst;
+		else
+			src = &state->src;
+	} else {
+		if (PF_REVERSED_KEY(state->key, pd->af))
+			src = &state->src;
+		else
+			src = &state->dst;
+	}
+
+	if (src->scrub != NULL) {
+		if (src->scrub->pfss_v_tag == 0)
+			src->scrub->pfss_v_tag = pd->hdr.sctp.v_tag;
+		else  if (src->scrub->pfss_v_tag != pd->hdr.sctp.v_tag)
+			return (PF_DROP);
+	}
+
+	return (PF_PASS);
+}
+
 static int
 pf_test_state_sctp(struct pf_kstate **state, struct pf_pdesc *pd,
     u_short *reason)
@@ -7123,37 +7165,45 @@ pf_test_state_sctp(struct pf_kstate **state, struct pf_pdesc *pd,
 		(*state)->timeout = PFTM_SCTP_CLOSED;
 	}
 
-	if (src->scrub != NULL) {
-		if (src->scrub->pfss_v_tag == 0) {
-			src->scrub->pfss_v_tag = pd->hdr.sctp.v_tag;
-		} else  if (src->scrub->pfss_v_tag != pd->hdr.sctp.v_tag)
-			return (PF_DROP);
-	}
+	if (pf_sctp_track(*state, pd, reason) != PF_PASS)
+		return (PF_DROP);
 
 	(*state)->expire = pf_get_uptime();
 
 	/* translate source/destination address, if necessary */
 	if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
 		uint16_t checksum = 0;
-		struct pf_state_key *nk = (*state)->key[pd->didx];
+		struct pf_state_key	*nk;
+		int			 afto, sidx, didx;
 
-		if (pd->af != nk->af) {
-			/* XXX No nat64 for SCTP for now. */
-			return (PF_DROP);
-		}
+		if (PF_REVERSED_KEY((*state)->key, pd->af))
+			nk = (*state)->key[pd->sidx];
+		else
+			nk = (*state)->key[pd->didx];
 
-		if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) ||
-		    nk->port[pd->sidx] != pd->hdr.sctp.src_port) {
+		afto = pd->af != nk->af;
+		sidx = afto ? pd->didx : pd->sidx;
+		didx = afto ? pd->sidx : pd->didx;
+
+		if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) ||
+		    nk->port[sidx] != pd->hdr.sctp.src_port) {
 			pf_change_ap(pd->m, pd->src, &pd->hdr.sctp.src_port,
-			    pd->ip_sum, &checksum, &nk->addr[pd->sidx],
-			    nk->port[pd->sidx], 1, pd->af, pd->naf);
+			    pd->ip_sum, &checksum, &nk->addr[sidx],
+			    nk->port[sidx], 1, pd->af, pd->naf);
 		}
 
-		if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
-		    nk->port[pd->didx] != pd->hdr.sctp.dest_port) {
+		if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
+		    nk->port[didx] != pd->hdr.sctp.dest_port) {
 			pf_change_ap(pd->m, pd->dst, &pd->hdr.sctp.dest_port,
-			    pd->ip_sum, &checksum, &nk->addr[pd->didx],
-			    nk->port[pd->didx], 1, pd->af, pd->naf);
+			    pd->ip_sum, &checksum, &nk->addr[didx],
+			    nk->port[didx], 1, pd->af, pd->naf);
+		}
+
+		if (afto) {
+			PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af);
+			PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af);
+			pd->naf = nk->af;
+			return (PF_AFRT);
 		}
 	}