Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107291052
D42361.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
9 KB
Referenced Files
None
Subscribers
None
D42361.diff
View Options
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
@@ -179,6 +179,34 @@
VNET_DEFINE(struct pf_krule *, pf_rulemarker);
#endif
+struct pf_sctp_endpoint;
+RB_HEAD(pf_sctp_endpoints, pf_sctp_endpoint);
+struct pf_sctp_source {
+ sa_family_t af;
+ struct pf_addr addr;
+ TAILQ_ENTRY(pf_sctp_source) entry;
+};
+TAILQ_HEAD(pf_sctp_sources, pf_sctp_source);
+struct pf_sctp_endpoint
+{
+ uint32_t v_tag;
+ struct pf_sctp_sources sources;
+ RB_ENTRY(pf_sctp_endpoint) entry;
+};
+static int
+pf_sctp_endpoint_compare(struct pf_sctp_endpoint *a, struct pf_sctp_endpoint *b)
+{
+ return (a->v_tag - b->v_tag);
+}
+RB_PROTOTYPE(pf_sctp_endpoints, pf_sctp_endpoint, entry, pf_sctp_endpoint_compare);
+RB_GENERATE(pf_sctp_endpoints, pf_sctp_endpoint, entry, pf_sctp_endpoint_compare);
+VNET_DEFINE_STATIC(struct pf_sctp_endpoints, pf_sctp_endpoints);
+#define V_pf_sctp_endpoints VNET(pf_sctp_endpoints)
+static struct mtx_padalign pf_sctp_endpoints_mtx;
+MTX_SYSINIT(pf_sctp_endpoints_mtx, &pf_sctp_endpoints_mtx, "SCTP endpoints", MTX_DEF);
+#define PF_SCTP_ENDPOINTS_LOCK() mtx_lock(&pf_sctp_endpoints_mtx)
+#define PF_SCTP_ENDPOINTS_UNLOCK() mtx_unlock(&pf_sctp_endpoints_mtx)
+
/*
* Queue for pf_intr() sends.
*/
@@ -309,6 +337,7 @@
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_detach_addr(const struct pf_kstate *);
static void pf_sctp_multihome_delayed(struct pf_pdesc *, int,
struct pfi_kkif *, struct pf_kstate *, int);
static int pf_test_state_sctp(struct pf_kstate **,
@@ -1140,6 +1169,7 @@
m_freem(pfse->pfse_m);
free(pfse, M_PFTEMP);
}
+ MPASS(RB_EMPTY(&V_pf_sctp_endpoints));
uma_zdestroy(V_pf_sources_z);
uma_zdestroy(V_pf_state_z);
@@ -1359,6 +1389,8 @@
struct pf_state_key *sks = s->key[PF_SK_STACK];
struct pf_keyhash *kh;
+ pf_sctp_multihome_detach_addr(s);
+
if (sks != NULL) {
kh = &V_pf_keyhash[pf_hashkey(sks)];
PF_HASHROW_LOCK(kh);
@@ -5848,7 +5880,7 @@
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 pf_state_peer *src, *dst;
struct sctphdr *sh = &pd->hdr.sctp;
u_int8_t psrc; //, pdst;
@@ -5871,9 +5903,11 @@
if (pd->dir == (*state)->direction) {
src = &(*state)->src;
+ dst = &(*state)->dst;
psrc = PF_PEER_SRC;
} else {
src = &(*state)->dst;
+ dst = &(*state)->src;
psrc = PF_PEER_DST;
}
@@ -5884,6 +5918,12 @@
(*state)->timeout = PFTM_TCP_OPENING;
}
}
+ if (pd->sctp_flags & PFDESC_SCTP_INIT_ACK) {
+ MPASS(dst->scrub != NULL);
+ if (dst->scrub->pfss_v_tag == 0)
+ dst->scrub->pfss_v_tag = pd->sctp_initiate_tag;
+ }
+
if (pd->sctp_flags & PFDESC_SCTP_COOKIE) {
if (src->state < SCTP_ESTABLISHED) {
pf_set_protostate(*state, psrc, SCTP_ESTABLISHED);
@@ -5930,37 +5970,222 @@
return (PF_PASS);
}
+static void
+pf_sctp_multihome_detach_addr(const struct pf_kstate *s)
+{
+ struct pf_sctp_endpoint key;
+ struct pf_sctp_endpoint *ep;
+ struct pf_state_key *sks = s->key[PF_SK_STACK];
+ struct pf_sctp_source *i, *tmp;
+
+ if (sks == NULL || sks->proto != IPPROTO_SCTP || s->dst.scrub == NULL)
+ return;
+
+ PF_SCTP_ENDPOINTS_LOCK();
+
+ key.v_tag = s->dst.scrub->pfss_v_tag;
+ ep = RB_FIND(pf_sctp_endpoints, &V_pf_sctp_endpoints, &key);
+ if (ep != NULL) {
+ /* XXX Actually remove! */
+ TAILQ_FOREACH_SAFE(i, &ep->sources, entry, tmp) {
+ if (pf_addr_cmp(&i->addr,
+ &s->key[PF_SK_WIRE]->addr[s->direction == PF_OUT],
+ s->key[PF_SK_WIRE]->af) == 0) {
+ TAILQ_REMOVE(&ep->sources, i, entry);
+ free(i, M_PFTEMP);
+ break;
+ }
+ }
+
+ if (TAILQ_EMPTY(&ep->sources)) {
+ RB_REMOVE(pf_sctp_endpoints, &V_pf_sctp_endpoints, ep);
+ free(ep, M_PFTEMP);
+ }
+ }
+
+ /* Other direction. */
+ key.v_tag = s->src.scrub->pfss_v_tag;
+ ep = RB_FIND(pf_sctp_endpoints, &V_pf_sctp_endpoints, &key);
+ if (ep != NULL) {
+ TAILQ_FOREACH_SAFE(i, &ep->sources, entry, tmp) {
+ if (pf_addr_cmp(&i->addr,
+ &s->key[PF_SK_WIRE]->addr[s->direction == PF_IN],
+ s->key[PF_SK_WIRE]->af) == 0) {
+ TAILQ_REMOVE(&ep->sources, i, entry);
+ free(i, M_PFTEMP);
+ break;
+ }
+ }
+
+ if (TAILQ_EMPTY(&ep->sources)) {
+ RB_REMOVE(pf_sctp_endpoints, &V_pf_sctp_endpoints, ep);
+ free(ep, M_PFTEMP);
+ }
+ }
+
+ PF_SCTP_ENDPOINTS_UNLOCK();
+}
+
+static void
+pf_sctp_multihome_add_addr(struct pf_pdesc *pd, struct pf_addr *a, uint32_t v_tag)
+{
+ struct pf_sctp_endpoint key = {
+ .v_tag = v_tag,
+ };
+ struct pf_sctp_source *i;
+ struct pf_sctp_endpoint *ep;
+
+ PF_SCTP_ENDPOINTS_LOCK();
+
+ ep = RB_FIND(pf_sctp_endpoints, &V_pf_sctp_endpoints, &key);
+ if (ep == NULL) {
+ ep = malloc(sizeof(struct pf_sctp_endpoint),
+ M_PFTEMP, M_NOWAIT);
+ if (ep == NULL) {
+ PF_SCTP_ENDPOINTS_UNLOCK();
+ return;
+ }
+
+ ep->v_tag = v_tag;
+ TAILQ_INIT(&ep->sources);
+ RB_INSERT(pf_sctp_endpoints, &V_pf_sctp_endpoints, ep);
+ }
+
+ /* Avoid inserting duplicates. */
+ TAILQ_FOREACH(i, &ep->sources, entry) {
+ if (pf_addr_cmp(&i->addr, a, pd->af) == 0) {
+ PF_SCTP_ENDPOINTS_UNLOCK();
+ return;
+ }
+ }
+
+ i = malloc(sizeof(*i), M_PFTEMP, M_NOWAIT);
+ if (i == NULL) {
+ PF_SCTP_ENDPOINTS_UNLOCK();
+ return;
+ }
+
+ i->af = pd->af;
+ memcpy(&i->addr, a, sizeof(*a));
+ TAILQ_INSERT_TAIL(&ep->sources, i, entry);
+
+ PF_SCTP_ENDPOINTS_UNLOCK();
+}
+
static void
pf_sctp_multihome_delayed(struct pf_pdesc *pd, int off, struct pfi_kkif *kif,
struct pf_kstate *s, int action)
{
struct pf_sctp_multihome_job *j, *tmp;
+ struct pf_sctp_source *i;
int ret __unused;;
struct pf_kstate *sm = NULL;
struct pf_krule *ra = NULL;
struct pf_krule *r = &V_pf_default_rule;
struct pf_kruleset *rs = NULL;
+ bool do_extra = true;
PF_RULES_RLOCK_TRACKER;
+again:
TAILQ_FOREACH_SAFE(j, &pd->sctp_multihome_jobs, next, tmp) {
if (s == NULL || action != PF_PASS)
goto free;
+ /* Confirm we don't recurse here. */
+ MPASS(! (pd->sctp_flags & PFDESC_SCTP_ADD_IP));
+
switch (j->op) {
case SCTP_ADD_IP_ADDRESS: {
+ uint32_t v_tag = pd->sctp_initiate_tag;
+
+ if (v_tag == 0) {
+ if (s->direction == pd->dir)
+ v_tag = s->src.scrub->pfss_v_tag;
+ else
+ v_tag = s->dst.scrub->pfss_v_tag;
+ }
+
+ /*
+ * 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 (pf_addr_cmp(&j->src, pd->src, pd->af) == 0) {
+ break;
+ }
+
j->pd.sctp_flags |= PFDESC_SCTP_ADD_IP;
PF_RULES_RLOCK();
+ sm = NULL;
+ /* XXX: May generated unwanted abort if we try to insert a duplicate state. */
ret = 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, ret);
- if (sm) {
+ if (ret != PF_DROP && sm != NULL) {
/* Inherit v_tag values. */
- sm->src.scrub->pfss_v_tag = s->src.scrub->pfss_flags;
- sm->dst.scrub->pfss_v_tag = s->dst.scrub->pfss_flags;
+ if (sm->direction == s->direction) {
+ sm->src.scrub->pfss_v_tag = s->src.scrub->pfss_v_tag;
+ sm->dst.scrub->pfss_v_tag = s->dst.scrub->pfss_v_tag;
+ } else {
+ sm->src.scrub->pfss_v_tag = s->dst.scrub->pfss_v_tag;
+ sm->dst.scrub->pfss_v_tag = s->src.scrub->pfss_v_tag;
+ }
PF_STATE_UNLOCK(sm);
+ } else {
+ /* If we try duplicate inserts? */
+ break;
+ }
+
+ /* Only add the addres if we've actually allowed the state. */
+ pf_sctp_multihome_add_addr(pd, &j->src, v_tag);
+
+ if (! do_extra) {
+ break;
}
+ /*
+ * We need to do this for each of our source addresses.
+ * Find those based on the verification tag.
+ */
+ struct pf_sctp_endpoint key = {
+ .v_tag = pd->hdr.sctp.v_tag,
+ };
+ struct pf_sctp_endpoint *ep;
+
+ PF_SCTP_ENDPOINTS_LOCK();
+ ep = RB_FIND(pf_sctp_endpoints, &V_pf_sctp_endpoints, &key);
+ if (ep == NULL) {
+ PF_SCTP_ENDPOINTS_UNLOCK();
+ break;
+ }
+ MPASS(ep != NULL);
+
+ TAILQ_FOREACH(i, &ep->sources, entry) {
+ struct pf_sctp_multihome_job *nj;
+
+ /* SCTP can intermingle IPv4 and IPv6. */
+ if (i->af != pd->af)
+ continue;
+
+ nj = malloc(sizeof(*nj), M_PFTEMP, M_NOWAIT | M_ZERO);
+ if (! nj) {
+ continue;
+ }
+ memcpy(&nj->pd, &j->pd, sizeof(j->pd));
+ memcpy(&nj->src, &j->src, sizeof(nj->src));
+ nj->pd.src = &nj->src;
+ // New destination address!
+ memcpy(&nj->dst, &i->addr, sizeof(nj->dst));
+ nj->pd.dst = &nj->dst;
+ nj->m = j->m;
+ nj->op = j->op;
+
+ TAILQ_INSERT_TAIL(&pd->sctp_multihome_jobs, nj, next);
+ }
+ PF_SCTP_ENDPOINTS_UNLOCK();
+
break;
}
case SCTP_DEL_IP_ADDRESS: {
@@ -5998,11 +6223,18 @@
default:
panic("Unknown op %#x", j->op);
}
- }
+ }
-free:
+ free:
+ TAILQ_REMOVE(&pd->sctp_multihome_jobs, j, next);
free(j, M_PFTEMP);
}
+
+ /* We may have inserted extra work while processing the list. */
+ if (! TAILQ_EMPTY(&pd->sctp_multihome_jobs)) {
+ do_extra = false;
+ goto again;
+ }
}
static int
@@ -6035,15 +6267,6 @@
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;
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
@@ -1596,6 +1596,8 @@
return (1);
}
+ dst->scrub->pfss_v_tag = pd->sctp_initiate_tag;
+
return (0);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Jan 13, 2:00 AM (20 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15774945
Default Alt Text
D42361.diff (9 KB)
Attached To
Mode
D42361: pf: fix missing SCTP multihomed states
Attached
Detach File
Event Timeline
Log In to Comment