aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2023-05-31 14:03:39 +0000
committerKristof Provost <kp@FreeBSD.org>2023-08-11 12:13:09 +0000
commit540c8cd7adc5b30fa11ea47822c333863b0663b5 (patch)
tree6529cfe98b1164b5ef9e0da2b232f9f3c7c56aac
parenta9e6ca87f104bb127b324b9c30ddf507d112d235 (diff)
downloadsrc-540c8cd7adc5b30fa11ea47822c333863b0663b5.tar.gz
src-540c8cd7adc5b30fa11ea47822c333863b0663b5.zip
pf: support 'return' for SCTP
Send an SCTP Abort message if we're refusing a connection, just like we send a RST for TCP. MFC after: 3 weeks Sponsored by: Orange Business Services Differential Revision: https://reviews.freebsd.org/D40864 (cherry picked from commit d1bc1e9e1ae04016e16154884914d839566ebaec)
-rw-r--r--sys/net/pfvar.h1
-rw-r--r--sys/netpfil/pf/pf.c117
-rw-r--r--sys/netpfil/pf/pf_norm.c2
3 files changed, 120 insertions, 0 deletions
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 55bd25a3d29e..99c504d99368 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1330,6 +1330,7 @@ struct pf_pdesc {
#define PFDESC_SCTP_DATA 0x0040
#define PFDESC_SCTP_OTHER 0x0080
u_int16_t sctp_flags;
+ u_int32_t sctp_initiate_tag;
};
#endif
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 16e0ee762f6a..ce5e1b813bb2 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -2911,6 +2911,120 @@ pf_build_tcp(const struct pf_krule *r, sa_family_t af,
return (m);
}
+static void
+pf_send_sctp_abort(sa_family_t af, struct pf_pdesc *pd,
+ uint8_t ttl, int rtableid)
+{
+ struct mbuf *m;
+#ifdef INET
+ struct ip *h = NULL;
+#endif /* INET */
+#ifdef INET6
+ struct ip6_hdr *h6 = NULL;
+#endif /* INET6 */
+ struct sctphdr *hdr;
+ struct sctp_chunkhdr *chunk;
+ struct pf_send_entry *pfse;
+ int off = 0;
+
+ MPASS(af == pd->af);
+
+ m = m_gethdr(M_NOWAIT, MT_DATA);
+ if (m == NULL)
+ return;
+
+ m->m_data += max_linkhdr;
+ m->m_flags |= M_SKIP_FIREWALL;
+ /* The rest of the stack assumes a rcvif, so provide one.
+ * This is a locally generated packet, so .. close enough. */
+ m->m_pkthdr.rcvif = V_loif;
+
+ /* IPv4|6 header */
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ bzero(m->m_data, sizeof(struct ip) + sizeof(*hdr) + sizeof(*chunk));
+
+ h = mtod(m, struct ip *);
+
+ /* IP header fields included in the TCP checksum */
+
+ h->ip_p = IPPROTO_SCTP;
+ h->ip_len = htons(sizeof(*h) + sizeof(*hdr) + sizeof(*chunk));
+ h->ip_ttl = ttl ? ttl : V_ip_defttl;
+ h->ip_src = pd->dst->v4;
+ h->ip_dst = pd->src->v4;
+
+ off += sizeof(struct ip);
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ bzero(m->m_data, sizeof(struct ip6_hdr) + sizeof(*hdr) + sizeof(*chunk));
+
+ h6 = mtod(m, struct ip6_hdr *);
+
+ /* IP header fields included in the TCP checksum */
+ h6->ip6_vfc |= IPV6_VERSION;
+ h6->ip6_nxt = IPPROTO_SCTP;
+ h6->ip6_plen = htons(sizeof(*h6) + sizeof(*hdr) + sizeof(*chunk));
+ h6->ip6_hlim = ttl ? ttl : V_ip6_defhlim;
+ memcpy(&h6->ip6_src, &pd->dst->v6, sizeof(struct in6_addr));
+ memcpy(&h6->ip6_dst, &pd->src->v6, sizeof(struct in6_addr));
+
+ off += sizeof(struct ip6_hdr);
+ break;
+#endif /* INET6 */
+ }
+
+ /* SCTP header */
+ hdr = mtodo(m, off);
+
+ hdr->src_port = pd->hdr.sctp.dest_port;
+ hdr->dest_port = pd->hdr.sctp.src_port;
+ hdr->v_tag = pd->sctp_initiate_tag;
+ hdr->checksum = 0;
+
+ /* Abort chunk. */
+ off += sizeof(struct sctphdr);
+ chunk = mtodo(m, off);
+
+ chunk->chunk_type = SCTP_ABORT_ASSOCIATION;
+ chunk->chunk_length = htons(sizeof(*chunk));
+
+ /* SCTP checksum */
+ off += sizeof(*chunk);
+ m->m_pkthdr.len = m->m_len = off;
+
+ pf_sctp_checksum(m, off - sizeof(*hdr) - sizeof(*chunk));;
+
+ if (rtableid >= 0)
+ M_SETFIB(m, rtableid);
+
+ /* Allocate outgoing queue entry, mbuf and mbuf tag. */
+ pfse = malloc(sizeof(*pfse), M_PFTEMP, M_NOWAIT);
+ if (pfse == NULL) {
+ m_freem(m);
+ return;
+ }
+
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ pfse->pfse_type = PFSE_IP;
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ pfse->pfse_type = PFSE_IP6;
+ break;
+#endif /* INET6 */
+ }
+
+ pfse->pfse_m = m;
+ pf_send(pfse);
+}
+
void
pf_send_tcp(const struct pf_krule *r, sa_family_t af,
const struct pf_addr *saddr, const struct pf_addr *daddr,
@@ -3014,6 +3128,9 @@ pf_return(struct pf_krule *r, struct pf_krule *nr, struct pf_pdesc *pd,
ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0,
r->return_ttl, 1, 0);
}
+ } else if (pd->proto == IPPROTO_SCTP &&
+ (r->rule_flag & PFRULE_RETURN)) {
+ pf_send_sctp_abort(af, pd, r->return_ttl, r->rtableid);
} else if (pd->proto != IPPROTO_ICMP && af == AF_INET &&
r->return_icmp)
pf_send_icmp(m, r->return_icmp >> 8,
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index 06aa577b45a7..ac02896d0762 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -2037,6 +2037,8 @@ pf_scan_sctp(struct mbuf *m, int ipoff, int off, struct pf_pdesc *pd)
if (pd->hdr.sctp.v_tag != 0)
return (PF_DROP);
+ pd->sctp_initiate_tag = init.init.initiate_tag;
+
pd->sctp_flags |= PFDESC_SCTP_INIT;
break;
}