Page MenuHomeFreeBSD

D42361.diff
No OneTemporary

D42361.diff

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

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)

Event Timeline