aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2023-06-01 13:04:48 +0000
committerKristof Provost <kp@FreeBSD.org>2023-08-11 12:13:09 +0000
commitbd658cda0284209b1ce460c86b67d5b73810ec6b (patch)
tree0e366398971d4d71ba5480e7ea5ce57d7aa8db41
parentdc9da87bd334906375eb35c3adaceaf0b6b4f2e3 (diff)
downloadsrc-bd658cda0284209b1ce460c86b67d5b73810ec6b.tar.gz
src-bd658cda0284209b1ce460c86b67d5b73810ec6b.zip
pf: add SCTP NAT support
Support NAT-ing SCTP connections. This is mostly similar to UDP and TCP, but we refuse to change ports for SCTP, to avoid interfering with multihomed connections. As a result we also never copy the SCTP header back or recalculate checksums as we'd do for TCP or UDP (because we don't modify the header for SCTP). We do use the existing pf_change_ap() function to modify the packet, because we may still need to update the IPv4 header checksum. Reviewed by: tuexen MFC after: 3 weeks Sponsored by: Orange Business Services Differential Revision: https://reviews.freebsd.org/D40866 (cherry picked from commit 6053adafaa54204f91c43939fa334bde835403cb)
-rw-r--r--sys/netpfil/pf/pf.c56
-rw-r--r--sys/netpfil/pf/pf_lb.c14
2 files changed, 69 insertions, 1 deletions
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index ce5e1b813bb2..5cd97c35a3f7 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -502,6 +502,23 @@ pf_packet_rework_nat(struct mbuf *m, struct pf_pdesc *pd, int off,
m_copyback(m, off, sizeof(*uh), (caddr_t)uh);
break;
}
+ case IPPROTO_SCTP: {
+ struct sctphdr *sh = &pd->hdr.sctp;
+ uint16_t checksum = 0;
+
+ if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af)) {
+ pf_change_ap(m, pd->src, &sh->src_port, pd->ip_sum,
+ &checksum, &nk->addr[pd->sidx],
+ nk->port[pd->sidx], 1, pd->af);
+ }
+ if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af)) {
+ pf_change_ap(m, pd->dst, &sh->dest_port, pd->ip_sum,
+ &checksum, &nk->addr[pd->didx],
+ nk->port[pd->didx], 1, pd->af);
+ }
+
+ break;
+ }
case IPPROTO_ICMP: {
struct icmp *ih = &pd->hdr.icmp;
@@ -3923,6 +3940,25 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, int direction,
}
rewrite++;
break;
+ case IPPROTO_SCTP: {
+ uint16_t checksum = 0;
+
+ if (PF_ANEQ(saddr, &nk->addr[pd->sidx], af) ||
+ nk->port[pd->sidx] != sport) {
+ pf_change_ap(m, saddr, &pd->hdr.sctp.src_port,
+ pd->ip_sum, &checksum,
+ &nk->addr[pd->sidx],
+ nk->port[pd->sidx], 1, af);
+ }
+ if (PF_ANEQ(daddr, &nk->addr[pd->didx], af) ||
+ nk->port[pd->didx] != dport) {
+ pf_change_ap(m, daddr, &pd->hdr.sctp.dest_port,
+ pd->ip_sum, &checksum,
+ &nk->addr[pd->didx],
+ nk->port[pd->didx], 1, af);
+ }
+ break;
+ }
#ifdef INET
case IPPROTO_ICMP:
nk->port[0] = nk->port[1];
@@ -5880,6 +5916,26 @@ pf_test_state_sctp(struct pf_kstate **state, struct pfi_kkif *kif,
(*state)->expire = time_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];
+
+ if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) ||
+ nk->port[pd->sidx] != pd->hdr.sctp.src_port) {
+ pf_change_ap(m, pd->src, &pd->hdr.sctp.src_port,
+ pd->ip_sum, &checksum, &nk->addr[pd->sidx],
+ nk->port[pd->sidx], 1, pd->af);
+ }
+
+ if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
+ nk->port[pd->didx] != pd->hdr.sctp.dest_port) {
+ pf_change_ap(m, pd->dst, &pd->hdr.sctp.dest_port,
+ pd->ip_sum, &checksum, &nk->addr[pd->didx],
+ nk->port[pd->didx], 1, pd->af);
+ }
+ }
+
return (PF_PASS);
}
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index 3190e5311ff5..a208e752e68e 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -237,7 +237,15 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
* port search; start random, step;
* similar 2 portloop in in_pcbbind
*/
- if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP ||
+ if (proto == IPPROTO_SCTP) {
+ key.port[1] = sport;
+ if (!pf_find_state_all_exists(&key, PF_IN)) {
+ *nport = sport;
+ return (0);
+ } else {
+ return (1); /* Fail mapping. */
+ }
+ } else if (!(proto == IPPROTO_TCP || proto == IPPROTO_UDP ||
proto == IPPROTO_ICMP) || (low == 0 && high == 0)) {
/*
* XXX bug: icmp states don't use the id on both sides.
@@ -698,6 +706,10 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction,
PF_POOLMASK(naddr, naddr, &r->rpool.cur->addr.v.a.mask,
daddr, pd->af);
+ /* Do not change SCTP ports. */
+ if (pd->proto == IPPROTO_SCTP)
+ break;
+
if (r->rpool.proxy_port[1]) {
uint32_t tmp_nport;