Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F141035640
D40862.id124283.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
D40862.id124283.diff
View Options
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -41,6 +41,7 @@
#include <net/if.h>
#define TCPSTATES
#include <netinet/tcp_fsm.h>
+#include <netinet/sctp.h>
#include <net/pfvar.h>
#include <arpa/inet.h>
#include <netdb.h>
@@ -206,6 +207,36 @@
p->seqhi - p->seqlo);
}
+
+static const char *
+sctp_state_name(int state)
+{
+ switch (state) {
+ case SCTP_CLOSED:
+ return ("CLOSED");
+ case SCTP_BOUND:
+ return ("BOUND");
+ case SCTP_LISTEN:
+ return ("LISTEN");
+ case SCTP_COOKIE_WAIT:
+ return ("COOKIE_WAIT");
+ case SCTP_COOKIE_ECHOED:
+ return ("COOKIE_ECHOED");
+ case SCTP_ESTABLISHED:
+ return ("ESTABLISHED");
+ case SCTP_SHUTDOWN_SENT:
+ return ("SHUTDOWN_SENT");
+ case SCTP_SHUTDOWN_RECEIVED:
+ return ("SHUTDOWN_RECEIVED");
+ case SCTP_SHUTDOWN_ACK_SENT:
+ return ("SHUTDOWN_ACK_SENT");
+ case SCTP_SHUTDOWN_PENDING:
+ return ("SHUTDOWN_PENDING");
+ default:
+ return ("?");
+ }
+}
+
void
print_state(struct pfctl_state *s, int opts)
{
@@ -300,6 +331,9 @@
const char *states[] = PFUDPS_NAMES;
printf(" %s:%s\n", states[src->state], states[dst->state]);
+ } else if (proto == IPPROTO_SCTP) {
+ printf(" %s:%s\n", sctp_state_name(src->state),
+ sctp_state_name(dst->state));
#ifndef INET6
} else if (proto != IPPROTO_ICMP && src->state < PFOTHERS_NSTATES &&
dst->state < PFOTHERS_NSTATES) {
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -60,6 +60,7 @@
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
+#include <netinet/sctp.h>
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
#endif
@@ -1550,6 +1551,7 @@
union pf_headers {
struct tcphdr tcp;
struct udphdr udp;
+ struct sctphdr sctp;
struct icmp icmp;
#ifdef INET6
struct icmp6_hdr icmp6;
@@ -1579,6 +1581,14 @@
u_int8_t dir; /* direction */
u_int8_t sidx; /* key index for source */
u_int8_t didx; /* key index for destination */
+#define PFDESC_SCTP_INIT 0x0001
+#define PFDESC_SCTP_INIT_ACK 0x0002
+#define PFDESC_SCTP_COOKIE 0x0004
+#define PFDESC_SCTP_ABORT 0x0008
+#define PFDESC_SCTP_SHUTDOWN 0x0010
+#define PFDESC_SCTP_DATA 0x0020
+#define PFDESC_SCTP_OTHER 0x0040
+ u_int16_t sctp_flags;
};
#endif
@@ -2279,6 +2289,8 @@
int pf_normalize_tcp_stateful(struct mbuf *, int, struct pf_pdesc *,
u_short *, struct tcphdr *, struct pf_kstate *,
struct pf_state_peer *, struct pf_state_peer *, int *);
+int pf_normalize_sctp(int, struct pfi_kkif *, struct mbuf *, int,
+ int, void *, struct pf_pdesc *);
u_int32_t
pf_state_expires(const struct pf_kstate *);
void pf_purge_expired_fragments(void);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -309,6 +309,9 @@
static int pf_test_state_udp(struct pf_kstate **, int,
struct pfi_kkif *, struct mbuf *, int,
void *, struct pf_pdesc *);
+static int pf_test_state_sctp(struct pf_kstate **, int,
+ struct pfi_kkif *, struct mbuf *, int,
+ void *, struct pf_pdesc *, u_short *);
static int pf_test_state_icmp(struct pf_kstate **, int,
struct pfi_kkif *, struct mbuf *, int,
void *, struct pf_pdesc *, u_short *);
@@ -4235,6 +4238,11 @@
dport = pd->hdr.udp.uh_dport;
hdrlen = sizeof(pd->hdr.udp);
break;
+ case IPPROTO_SCTP:
+ sport = pd->hdr.sctp.src_port;
+ dport = pd->hdr.sctp.dest_port;
+ hdrlen = sizeof(pd->hdr.sctp);
+ break;
#ifdef INET
case IPPROTO_ICMP:
if (pd->af != AF_INET)
@@ -4702,6 +4710,11 @@
pf_set_protostate(s, PF_PEER_DST, PFUDPS_NO_TRAFFIC);
s->timeout = PFTM_UDP_FIRST_PACKET;
break;
+ case IPPROTO_SCTP:
+ pf_set_protostate(s, PF_PEER_SRC, SCTP_COOKIE_WAIT);
+ pf_set_protostate(s, PF_PEER_DST, SCTP_CLOSED);
+ s->timeout = PFTM_TCP_FIRST_PACKET;
+ break;
case IPPROTO_ICMP:
#ifdef INET6
case IPPROTO_ICMPV6:
@@ -5670,6 +5683,66 @@
return (PF_PASS);
}
+static int
+pf_test_state_sctp(struct pf_kstate **state, int direction,
+ struct pfi_kkif *kif, struct mbuf *m, int off, void *h,
+ struct pf_pdesc *pd, u_short *reason)
+{
+ struct pf_state_key_cmp key;
+ struct pf_state_peer *src; //, *dst;
+ struct sctphdr *sh = &pd->hdr.sctp;
+ u_int8_t psrc; //, pdst;
+
+ bzero(&key, sizeof(key));
+ key.af = pd->af;
+ key.proto = IPPROTO_SCTP;
+ if (direction == PF_IN) { /* wire side, straight */
+ PF_ACPY(&key.addr[0], pd->src, key.af);
+ PF_ACPY(&key.addr[1], pd->dst, key.af);
+ key.port[0] = sh->src_port;
+ key.port[1] = sh->dest_port;
+ } else { /* stack side, reverse */
+ PF_ACPY(&key.addr[1], pd->src, key.af);
+ PF_ACPY(&key.addr[0], pd->dst, key.af);
+ key.port[1] = sh->src_port;
+ key.port[0] = sh->dest_port;
+ }
+
+ STATE_LOOKUP(kif, &key, direction, *state, pd);
+
+ if (pd->dir == (*state)->direction) {
+ src = &(*state)->src;
+ psrc = PF_PEER_SRC;
+ } else {
+ src = &(*state)->dst;
+ psrc = PF_PEER_DST;
+ }
+
+ /* Track state. */
+ if (pd->sctp_flags & PFDESC_SCTP_INIT) {
+ if (src->state < SCTP_COOKIE_WAIT) {
+ pf_set_protostate(*state, psrc, SCTP_COOKIE_WAIT);
+ (*state)->timeout = PFTM_TCP_OPENING;
+ }
+ }
+ if (pd->sctp_flags & PFDESC_SCTP_COOKIE) {
+ if (src->state < SCTP_ESTABLISHED) {
+ pf_set_protostate(*state, psrc, SCTP_ESTABLISHED);
+ (*state)->timeout = PFTM_TCP_ESTABLISHED;
+ }
+ }
+ if (pd->sctp_flags & (PFDESC_SCTP_SHUTDOWN | PFDESC_SCTP_ABORT)) {
+ if (src->state < SCTP_SHUTDOWN_PENDING) {
+ pf_set_protostate(*state, psrc, SCTP_SHUTDOWN_PENDING);
+ (*state)->timeout = PFTM_TCP_CLOSING;
+ }
+ }
+
+ (*state)->expire = time_uptime;
+
+ return (PF_PASS);
+}
+
static int
pf_test_state_icmp(struct pf_kstate **state, int direction, struct pfi_kkif *kif,
struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason)
@@ -7393,6 +7466,38 @@
break;
}
+ case IPPROTO_SCTP: {
+ if (!pf_pull_hdr(m, off, &pd.hdr.sctp, sizeof(pd.hdr.sctp),
+ &action, &reason, AF_INET)) {
+ if (action != PF_PASS)
+ log = PF_LOG_FORCE;
+ goto done;
+ }
+ pd.sport = &pd.hdr.sctp.src_port;
+ pd.dport = &pd.hdr.sctp.dest_port;
+ if (pd.hdr.sctp.src_port == 0 || pd.hdr.sctp.dest_port == 0) {
+ action = PF_DROP;
+ REASON_SET(&reason, PFRES_SHORT);
+ goto done;
+ }
+ action = pf_normalize_sctp(dir, kif, m, 0, off, h, &pd);
+ if (action == PF_DROP)
+ goto done;
+ action = pf_test_state_sctp(&s, dir, kif, m, off, h, &pd,
+ &reason);
+ if (action == PF_PASS) {
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
+ r = s->rule.ptr;
+ a = s->anchor.ptr;
+ log = s->log;
+ } else {
+ action = pf_test_rule(&r, &s, dir, kif, m, off,
+ &pd, &a, &ruleset, inp);
+ }
+ break;
+ }
+
case IPPROTO_ICMP: {
if (!pf_pull_hdr(m, off, &pd.hdr.icmp, ICMP_MINLEN,
&action, &reason, AF_INET)) {
@@ -7951,6 +8056,38 @@
break;
}
+ case IPPROTO_SCTP: {
+ if (!pf_pull_hdr(m, off, &pd.hdr.sctp, sizeof(pd.hdr.sctp),
+ &action, &reason, AF_INET6)) {
+ if (action != PF_PASS)
+ log = PF_LOG_FORCE;
+ goto done;
+ }
+ pd.sport = &pd.hdr.sctp.src_port;
+ pd.dport = &pd.hdr.sctp.dest_port;
+ if (pd.hdr.sctp.src_port == 0 || pd.hdr.sctp.dest_port == 0) {
+ action = PF_DROP;
+ REASON_SET(&reason, PFRES_SHORT);
+ goto done;
+ }
+ action = pf_normalize_sctp(dir, kif, m, 0, off, h, &pd);
+ if (action == PF_DROP)
+ goto done;
+ action = pf_test_state_sctp(&s, dir, kif, m, off, h, &pd,
+ &reason);
+ if (action == PF_PASS) {
+ if (V_pfsync_update_state_ptr != NULL)
+ V_pfsync_update_state_ptr(s);
+ r = s->rule.ptr;
+ a = s->anchor.ptr;
+ log = s->log;
+ } else {
+ action = pf_test_rule(&r, &s, dir, kif, m, off,
+ &pd, &a, &ruleset, inp);
+ }
+ break;
+ }
+
case IPPROTO_ICMP: {
action = PF_DROP;
DPFPRINTF(PF_DEBUG_MISC,
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -56,6 +56,8 @@
#include <netinet/tcp.h>
#include <netinet/tcp_fsm.h>
#include <netinet/tcp_seq.h>
+#include <netinet/sctp_constants.h>
+#include <netinet/sctp_header.h>
#ifdef INET6
#include <netinet/ip6.h>
@@ -2031,6 +2033,169 @@
return (0);
}
+static int
+pf_scan_sctp(struct mbuf *m, int ipoff, int off, struct pf_pdesc *pd)
+{
+ struct sctp_chunkhdr ch = { };
+ int chunk_off = sizeof(struct sctphdr);
+ int chunk_start;
+
+ while (off + chunk_off < pd->tot_len) {
+ if (!pf_pull_hdr(m, off + chunk_off, &ch, sizeof(ch), NULL,
+ NULL, pd->af))
+ return (PF_DROP);
+
+ chunk_start = chunk_off;
+ chunk_off += roundup(ntohs(ch.chunk_length), 4);
+
+ switch (ch.chunk_type) {
+ case SCTP_INITIATION: {
+ struct sctp_init_chunk init;
+
+ if (!pf_pull_hdr(m, off + chunk_start, &init,
+ sizeof(init), NULL, NULL, pd->af))
+ return (PF_DROP);
+
+ /*
+ * RFC 9620, Section 3.3.2, "The Initiate Tag is allowed to have
+ * any value except 0."
+ */
+ if (init.init.initiate_tag == 0)
+ return (PF_DROP);
+ if (init.init.num_inbound_streams == 0)
+ return (PF_DROP);
+ if (init.init.num_outbound_streams == 0)
+ return (PF_DROP);
+ if (ntohl(init.init.a_rwnd) < SCTP_MIN_RWND)
+ return (PF_DROP);
+
+ /*
+ * RFC 9260, Section 3.1, INIT chunks MUST have zero
+ * verification tag.
+ */
+ if (pd->hdr.sctp.v_tag != 0)
+ return (PF_DROP);
+
+ pd->sctp_flags |= PFDESC_SCTP_INIT;
+ break;
+ }
+ case SCTP_INITIATION_ACK:
+ pd->sctp_flags |= PFDESC_SCTP_INIT_ACK;
+ break;
+ case SCTP_ABORT_ASSOCIATION:
+ pd->sctp_flags |= PFDESC_SCTP_ABORT;
+ break;
+ case SCTP_SHUTDOWN:
+ case SCTP_SHUTDOWN_ACK:
+ case SCTP_SHUTDOWN_COMPLETE:
+ pd->sctp_flags |= PFDESC_SCTP_SHUTDOWN;
+ break;
+ case SCTP_COOKIE_ECHO:
+ case SCTP_COOKIE_ACK:
+ pd->sctp_flags |= PFDESC_SCTP_COOKIE;
+ break;
+ case SCTP_DATA:
+ pd->sctp_flags |= PFDESC_SCTP_DATA;
+ break;
+ default:
+ pd->sctp_flags |= PFDESC_SCTP_OTHER;
+ break;
+ }
+ }
+
+ /* Validate chunk lengths vs. packet length. */
+ if (off + chunk_off != pd->tot_len)
+ return (PF_DROP);
+
+ /* INIT chunk must always be the only one in a packet. */
+ if ((pd->sctp_flags & PFDESC_SCTP_INIT) &&
+ (pd->sctp_flags & ~PFDESC_SCTP_INIT))
+ return (PF_DROP);
+
+ return (PF_PASS);
+}
+
+int
+pf_normalize_sctp(int dir, struct pfi_kkif *kif, struct mbuf *m, int ipoff,
+ int off, void *h, struct pf_pdesc *pd)
+{
+ struct pf_krule *r, *rm = NULL;
+ struct sctphdr *sh = &pd->hdr.sctp;
+ u_short reason;
+ sa_family_t af = pd->af;
+ int srs;
+
+ PF_RULES_RASSERT();
+
+ /* Unconditionally scan the SCTP packet, because we need to look for
+ * things like shutdown and asconf chunks. */
+ if (pf_scan_sctp(m, ipoff, off, pd) != PF_PASS)
+ goto sctp_drop;
+
+ r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr);
+ /* Check if there any scrub rules. Lack of scrub rules means enforced
+ * packet normalization operation just like in OpenBSD. */
+ srs = (r != NULL);
+ while (r != NULL) {
+ pf_counter_u64_add(&r->evaluations, 1);
+ if (pfi_kkif_match(r->kif, kif) == r->ifnot)
+ r = r->skip[PF_SKIP_IFP].ptr;
+ else if (r->direction && r->direction != dir)
+ r = r->skip[PF_SKIP_DIR].ptr;
+ else if (r->af && r->af != af)
+ r = r->skip[PF_SKIP_AF].ptr;
+ else if (r->proto && r->proto != pd->proto)
+ r = r->skip[PF_SKIP_PROTO].ptr;
+ else if (PF_MISMATCHAW(&r->src.addr, pd->src, af,
+ r->src.neg, kif, M_GETFIB(m)))
+ r = r->skip[PF_SKIP_SRC_ADDR].ptr;
+ else if (r->src.port_op && !pf_match_port(r->src.port_op,
+ r->src.port[0], r->src.port[1], sh->src_port))
+ r = r->skip[PF_SKIP_SRC_PORT].ptr;
+ else if (PF_MISMATCHAW(&r->dst.addr, pd->dst, af,
+ r->dst.neg, NULL, M_GETFIB(m)))
+ r = r->skip[PF_SKIP_DST_ADDR].ptr;
+ else if (r->dst.port_op && !pf_match_port(r->dst.port_op,
+ r->dst.port[0], r->dst.port[1], sh->dest_port))
+ r = r->skip[PF_SKIP_DST_PORT].ptr;
+ else {
+ rm = r;
+ break;
+ }
+ }
+
+ if (srs) {
+ /* With scrub rules present SCTP normalization happens only
+ * if one of rules has matched and it's not a "no scrub" rule */
+ if (rm == NULL || rm->action == PF_NOSCRUB)
+ return (PF_PASS);
+
+ pf_counter_u64_critical_enter();
+ pf_counter_u64_add_protected(&r->packets[dir == PF_OUT], 1);
+ pf_counter_u64_add_protected(&r->bytes[dir == PF_OUT], pd->tot_len);
+ pf_counter_u64_critical_exit();
+ }
+
+ /* Verify we're a multiple of 4 bytes long */
+ if ((pd->tot_len - off - sizeof(struct sctphdr)) % 4)
+ goto sctp_drop;
+
+ /* INIT chunk needs to be the only chunk */
+ if (pd->sctp_flags & PFDESC_SCTP_INIT)
+ if (pd->sctp_flags & ~PFDESC_SCTP_INIT)
+ goto sctp_drop;
+
+ return (PF_PASS);
+
+sctp_drop:
+ REASON_SET(&reason, PFRES_NORM);
+ if (rm != NULL && r->log)
+ PFLOG_PACKET(kif, m, AF_INET, dir, reason, r, NULL, NULL, pd,
+ 1);
+
+ return (PF_DROP);
+}
+
u_int16_t
pf_rule_to_scrub_flags(u_int32_t rule_flags)
{
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 1, 12:57 AM (10 h, 28 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27400918
Default Alt Text
D40862.id124283.diff (12 KB)
Attached To
Mode
D40862: pf: initial SCTP support
Attached
Detach File
Event Timeline
Log In to Comment