Page MenuHomeFreeBSD

D41637.id126790.diff
No OneTemporary

D41637.id126790.diff

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1531,6 +1531,9 @@
#define PFI_IFLAG_SKIP 0x0100 /* skip filtering on interface */
#ifdef _KERNEL
+struct pf_sctp_multihome_job;
+SLIST_HEAD(pf_sctp_multihome_jobs, pf_sctp_multihome_job);
+
struct pf_pdesc {
struct {
int done;
@@ -1578,10 +1581,22 @@
#define PFDESC_SCTP_SHUTDOWN 0x0010
#define PFDESC_SCTP_SHUTDOWN_COMPLETE 0x0020
#define PFDESC_SCTP_DATA 0x0040
-#define PFDESC_SCTP_OTHER 0x0080
+#define PFDESC_SCTP_ASCONF 0x0080
+#define PFDESC_SCTP_OTHER 0x0100
u_int16_t sctp_flags;
u_int32_t sctp_initiate_tag;
+
+ struct pf_sctp_multihome_jobs sctp_multihome_jobs;
+};
+
+struct pf_sctp_multihome_job {
+ SLIST_ENTRY(pf_sctp_multihome_job) next;
+ struct pf_pdesc pd;
+ struct pf_addr src;
+ struct pf_addr dst;
+ struct mbuf *m;
};
+
#endif
/* flags for RDR options */
@@ -2249,6 +2264,11 @@
int pf_refragment6(struct ifnet *, struct mbuf **, struct m_tag *, bool);
#endif /* INET6 */
+int pf_multihome_scan_init(struct mbuf *, int, int, struct pf_pdesc *,
+ struct pfi_kkif *);
+int pf_multihome_scan_asconf(struct mbuf *, int, int, struct pf_pdesc *,
+ struct pfi_kkif *);
+
u_int32_t pf_new_isn(struct pf_kstate *);
void *pf_pull_hdr(struct mbuf *, int, void *, int, u_short *, u_short *,
sa_family_t);
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
@@ -126,6 +126,8 @@
SDT_PROBE_DEFINE5(pf, ip, state, lookup, "struct pfi_kkif *",
"struct pf_state_key_cmp *", "int", "struct pf_pdesc *",
"struct pf_kstate *");
+SDT_PROBE_DEFINE4(pf, sctp, multihome, test, "struct pfi_kkif *",
+ "struct pf_krule *", "struct mbuf *", "int");
SDT_PROBE_DEFINE3(pf, eth, test_rule, entry, "int", "struct ifnet *",
"struct mbuf *");
@@ -307,6 +309,8 @@
static int pf_test_state_icmp(struct pf_kstate **,
struct pfi_kkif *, struct mbuf *, int,
void *, struct pf_pdesc *, u_short *);
+static void pf_sctp_multihome_delayed(struct pf_pdesc *, int,
+ struct pfi_kkif *, struct pf_kstate *);
static int pf_test_state_sctp(struct pf_kstate **,
struct pfi_kkif *, struct mbuf *, int,
void *, struct pf_pdesc *, u_short *);
@@ -5912,6 +5916,172 @@
return (PF_PASS);
}
+static void
+pf_sctp_multihome_delayed(struct pf_pdesc *pd, int off, struct pfi_kkif *kif,
+ struct pf_kstate *s)
+{
+ struct pf_sctp_multihome_job *j, *tmp;
+ int action;;
+ struct pf_kstate *sm = NULL;
+ struct pf_krule *ra = NULL;
+ struct pf_krule *r = &V_pf_default_rule;
+ struct pf_kruleset *rs = NULL;
+
+ PF_RULES_RLOCK_TRACKER;
+
+ SLIST_FOREACH_SAFE(j, &pd->sctp_multihome_jobs, next, tmp) {
+ PF_RULES_RLOCK();
+ action = pf_test_rule(&r, &sm, kif,
+ j->m, off, &j->pd, &ra, &rs, NULL);
+ PF_RULES_RUNLOCK();
+ SDT_PROBE4(pf, sctp, multihome, test, kif, r, j->m, action);
+ if (sm)
+ PF_STATE_UNLOCK(sm);
+
+ free(j, M_PFTEMP);
+ }
+}
+
+static int
+pf_multihome_scan(struct mbuf *m, int start, int len, struct pf_pdesc *pd,
+ struct pfi_kkif *kif)
+{
+ int off = 0;
+ struct pf_sctp_multihome_job *job;
+
+ while (off < len) {
+ struct sctp_paramhdr h;
+
+ if (!pf_pull_hdr(m, start + off, &h, sizeof(h), NULL, NULL,
+ pd->af))
+ return (PF_DROP);
+
+ /* Parameters are at least 4 bytes. */
+ if (ntohs(h.param_length) < 4)
+ return (PF_DROP);
+
+ switch (ntohs(h.param_type)) {
+ case SCTP_IPV4_ADDRESS: {
+ struct in_addr t;
+
+ if (ntohs(h.param_length) !=
+ (sizeof(struct sctp_paramhdr) + sizeof(t)))
+ return (PF_DROP);
+
+ if (!pf_pull_hdr(m, start + off + sizeof(h), &t, sizeof(t),
+ NULL, NULL, pd->af))
+ return (PF_DROP);
+
+ /*
+ * Avoid duplicating states. We'll already have
+ * created a state based on the source address of
+ * the packet, but SCTP endpoints may also list this
+ * address again in the INIT(_ACK) parameters.
+ */
+ if (t.s_addr == pd->src->v4.s_addr)
+ break;
+
+ if (in_nullhost(t))
+ t.s_addr = pd->src->v4.s_addr;
+
+ /*
+ * We hold the state lock (idhash) here, which means
+ * that we can't acquire the keyhash, or we'll get a
+ * LOR (and potentially double-lock things too). We also
+ * can't release the state lock here, so instead we'll
+ * enqueue this for async handling.
+ * There's a relatively small race here, in that a
+ * packet using the new addresses could arrive already,
+ * but that's just though luck for it.
+ */
+ job = malloc(sizeof(*job), M_PFTEMP, M_NOWAIT | M_ZERO);
+ if (! job)
+ return (PF_DROP);
+
+ memcpy(&job->pd, pd, sizeof(*pd));
+
+ // New source address!
+ memcpy(&job->src, &t, sizeof(t));
+ job->pd.src = &job->src;
+ memcpy(&job->dst, pd->dst, sizeof(job->dst));
+ job->pd.dst = &job->dst;
+ job->m = m;
+
+ SLIST_INSERT_HEAD(&pd->sctp_multihome_jobs, job, next);
+ break;
+ }
+ case SCTP_IPV6_ADDRESS: {
+ struct in6_addr t;
+
+ if (ntohs(h.param_length) !=
+ (sizeof(struct sctp_paramhdr) + sizeof(t)))
+ return (PF_DROP);
+
+ if (!pf_pull_hdr(m, start + off + sizeof(h), &t, sizeof(t),
+ NULL, NULL, pd->af))
+ return (PF_DROP);
+ if (memcmp(&t, &pd->src->v6, sizeof(t)) == 0)
+ break;
+ if (memcmp(&t, &in6addr_any, sizeof(t)) == 0)
+ memcpy(&t, &pd->src->v6, sizeof(t));
+
+ job = malloc(sizeof(*job), M_PFTEMP, M_NOWAIT | M_ZERO);
+ if (! job)
+ return (PF_DROP);
+
+ memcpy(&job->pd, pd, sizeof(*pd));
+ memcpy(&job->src, &t, sizeof(t));
+ job->pd.src = &job->src;
+ memcpy(&job->dst, pd->dst, sizeof(job->dst));
+ job->pd.dst = &job->dst;
+ job->m = m;
+
+ SLIST_INSERT_HEAD(&pd->sctp_multihome_jobs, job, next);
+ break;
+ }
+ case SCTP_ADD_IP_ADDRESS: {
+ int ret;
+ struct sctp_asconf_paramhdr ah;
+
+ if (!pf_pull_hdr(m, start + off, &ah, sizeof(ah),
+ NULL, NULL, pd->af))
+ return (PF_DROP);
+
+ ret = pf_multihome_scan(m, start + off + sizeof(ah),
+ ntohs(ah.ph.param_length) - sizeof(ah), pd, kif);
+ if (ret != PF_PASS)
+ return (ret);
+ break;
+ }
+ default:
+ break;
+ }
+
+ off += roundup(ntohs(h.param_length), 4);
+ }
+
+ return (PF_PASS);
+}
+int
+pf_multihome_scan_init(struct mbuf *m, int start, int len, struct pf_pdesc *pd,
+ struct pfi_kkif *kif)
+{
+ start += sizeof(struct sctp_init_chunk);
+ len -= sizeof(struct sctp_init_chunk);
+
+ return (pf_multihome_scan(m, start, len, pd, kif));
+}
+
+int
+pf_multihome_scan_asconf(struct mbuf *m, int start, int len,
+ struct pf_pdesc *pd, struct pfi_kkif *kif)
+{
+ start += sizeof(struct sctp_asconf_chunk);
+ len -= sizeof(struct sctp_asconf_chunk);
+
+ return (pf_multihome_scan(m, start, len, pd, kif));
+}
+
static int
pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif,
struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason)
@@ -7613,6 +7783,8 @@
pd.act.log |= PF_LOG_FORCE;
goto done;
}
+ pd.p_len = pd.tot_len - off;
+
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) {
@@ -7896,7 +8068,7 @@
/* pf_route() returns unlocked. */
if (rt) {
pf_route(m0, r, kif->pfik_ifp, s, &pd, inp);
- return (action);
+ goto out;
}
if (pf_dummynet(&pd, s, r, m0) != 0) {
action = PF_DROP;
@@ -7910,6 +8082,9 @@
if (s)
PF_STATE_UNLOCK(s);
+out:
+ pf_sctp_multihome_delayed(&pd, off, kif, s);
+
return (action);
}
#endif /* INET */
@@ -8441,7 +8616,7 @@
/* pf_route6() returns unlocked. */
if (rt) {
pf_route6(m0, r, kif->pfik_ifp, s, &pd, inp);
- return (action);
+ goto out;
}
if (pf_dummynet(&pd, s, r, m0) != 0) {
action = PF_DROP;
@@ -8458,8 +8633,11 @@
(mtag = m_tag_find(m, PACKET_TAG_PF_REASSEMBLED, NULL)) != NULL)
action = pf_refragment6(ifp, m0, mtag, pflags & PFIL_FWD);
+out:
SDT_PROBE4(pf, ip, test6, done, action, reason, r, s);
+ pf_sctp_multihome_delayed(&pd, off, kif, s);
+
return (action);
}
#endif /* INET6 */
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
@@ -2021,11 +2021,13 @@
}
static int
-pf_scan_sctp(struct mbuf *m, int ipoff, int off, struct pf_pdesc *pd)
+pf_scan_sctp(struct mbuf *m, int ipoff, int off, struct pf_pdesc *pd,
+ struct pfi_kkif *kif)
{
struct sctp_chunkhdr ch = { };
int chunk_off = sizeof(struct sctphdr);
int chunk_start;
+ int ret;
while (off + chunk_off < pd->tot_len) {
if (!pf_pull_hdr(m, off + chunk_off, &ch, sizeof(ch), NULL,
@@ -2040,7 +2042,8 @@
chunk_off += roundup(ntohs(ch.chunk_length), 4);
switch (ch.chunk_type) {
- case SCTP_INITIATION: {
+ case SCTP_INITIATION:
+ case SCTP_INITIATION_ACK: {
struct sctp_init_chunk init;
if (!pf_pull_hdr(m, off + chunk_start, &init,
@@ -2064,17 +2067,24 @@
* RFC 9260, Section 3.1, INIT chunks MUST have zero
* verification tag.
*/
- if (pd->hdr.sctp.v_tag != 0)
+ if (ch.chunk_type == SCTP_INITIATION &&
+ pd->hdr.sctp.v_tag != 0)
return (PF_DROP);
pd->sctp_initiate_tag = init.init.initiate_tag;
- pd->sctp_flags |= PFDESC_SCTP_INIT;
+ if (ch.chunk_type == SCTP_INITIATION)
+ pd->sctp_flags |= PFDESC_SCTP_INIT;
+ else
+ pd->sctp_flags |= PFDESC_SCTP_INIT_ACK;
+
+ ret = pf_multihome_scan_init(m, off + chunk_start,
+ ntohs(init.ch.chunk_length), pd, kif);
+ if (ret != PF_PASS)
+ return (ret);
+
break;
}
- case SCTP_INITIATION_ACK:
- pd->sctp_flags |= PFDESC_SCTP_INIT_ACK;
- break;
case SCTP_ABORT_ASSOCIATION:
pd->sctp_flags |= PFDESC_SCTP_ABORT;
break;
@@ -2092,6 +2102,14 @@
case SCTP_DATA:
pd->sctp_flags |= PFDESC_SCTP_DATA;
break;
+ case SCTP_ASCONF:
+ pd->sctp_flags |= PFDESC_SCTP_ASCONF;
+
+ ret = pf_multihome_scan_asconf(m, off + chunk_start,
+ ntohs(ch.chunk_length), pd, kif);
+ if (ret != PF_PASS)
+ return (ret);
+ break;
default:
pd->sctp_flags |= PFDESC_SCTP_OTHER;
break;
@@ -2133,7 +2151,7 @@
/* 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)
+ if (pf_scan_sctp(m, ipoff, off, pd, kif) != PF_PASS)
goto sctp_drop;
r = TAILQ_FIRST(pf_main_ruleset.rules[PF_RULESET_SCRUB].active.ptr);

File Metadata

Mime Type
text/plain
Expires
Thu, Apr 2, 7:56 AM (4 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30714792
Default Alt Text
D41637.id126790.diff (10 KB)

Event Timeline