From nobody Tue Oct 31 15:03:36 2023 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4SKYLJ5R6nz4yTrj; Tue, 31 Oct 2023 15:03:36 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4SKYLJ4hjrz3SSN; Tue, 31 Oct 2023 15:03:36 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1698764616; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=9MkN7jqp6agPRXCJYnb1qYgkRx1rlMyFg6f54536iWA=; b=x0cbsy70LbuLusIHzJ/t5lmBfSeu9qdynCxI4y9yGnBblfCx8lFmV3FsDiVx5YotoOk2fa eBmbZr15rjoK/JYKmYAecDKeS6KVX+ekxlihezmRR5BqwX47GGwT+oFtAKDwDhLY/ZoLHa zMU4CJNHISZ6cb9N1BTtCdtCq7Z2l2s471yDl3lHVonD6U/jf365mzbbWqRycJUB9n4/65 QbLtf0C9vUIQiW/El5oFP4yIxySvH323UnORO8aGi3RPAKZzh990GcDZzZu6buB4sSSGxJ hWIuZaA3xQa/2d2vzZOVNeLLpAFu05VwebsJsPhz5QtSCHeIG0+kvWIMOS1IjQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1698764616; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=9MkN7jqp6agPRXCJYnb1qYgkRx1rlMyFg6f54536iWA=; b=DJz4CYRdzOw0i86E28JzjmBdnEJpKCjC/LMrh5RPN6I7dFMVOWY1reNveDRRZ6KfNBzr40 /JU9z4XGpdOj7W/lNmntKutCf3r/gKaONxQU4sxOnzDxBbGzQSlUqLrRsbQeyLP61FZsfv V/dqM6ib0OlE+D/NM32trSmtLIr9brDKgoDCKpnokHgY2FyXe/W9jlyakpOL0ycrUlNEuD 9iLL8EvmFMVi2rnsTzrVfoaPYDxMx47nQIxd8+vPWppHphpoTfq4QA9gdkXBEfP+WaEDHY iJJtalScqxD5DWy85fWGafiC60XIhsw7u1CyrTrpGScORuyLFPYqKlbboKJcPA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1698764616; a=rsa-sha256; cv=none; b=q3fwvk97yWT+jvpzvzvikAD/26HUmwXUaueL+5aiFi1PFMA1uz6pydlA0kEikm1StRrQPj 9DCKG8N/lvKx0pwzwqz5QgPnRGqshZJlR0mjyyM2+nnrcNyu6wqwNO8cvvXI0vuQ8RgUEt 5g6jBY8BWPC49Ds7HnZPB7iM43MbNSsZ1UCTACbv++yD4tG6VA/ijjUUrnHbCS7K3MzU5p NzF9ULCoSupJmK1wbYbUbN9xSkYCgzXY1kBHHIhVnJFn05H3zujiiMc6H7o0H9/OfiOWh6 W14IxwUKHkw0BrSYsmtZQkMQL71EJmbIvOEyms7v4V+lQhXg/f+wrSLD7Qo4/A== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4SKYLJ3l6Cz17QT; Tue, 31 Oct 2023 15:03:36 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 39VF3aLb048195; Tue, 31 Oct 2023 15:03:36 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 39VF3a5Z048193; Tue, 31 Oct 2023 15:03:36 GMT (envelope-from git) Date: Tue, 31 Oct 2023 15:03:36 GMT Message-Id: <202310311503.39VF3a5Z048193@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kristof Provost Subject: git: d6d38b02ee57 - main - pf: fix missing SCTP multihomed states List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kp X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d6d38b02ee57920cc02a306a6d8d2aec9c4b759c Auto-Submitted: auto-generated The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=d6d38b02ee57920cc02a306a6d8d2aec9c4b759c commit d6d38b02ee57920cc02a306a6d8d2aec9c4b759c Author: Kristof Provost AuthorDate: 2023-10-17 16:10:39 +0000 Commit: Kristof Provost CommitDate: 2023-10-31 15:03:22 +0000 pf: fix missing SCTP multihomed states The existing code to create extra states when SCTP endpoints supplied extra addresses missed a case. As a result we failed to generate all of the required states. Briefly, if host A has address 1 and 2 and host B has addres 3 and 4 we generated 1 - 3 and 2 - 3, as well as 1 - 4, but not 2 - 4. Store the list of endpoints supplied by each host and use those to generate all of the connection permutations. MFC after: 1 week Sponsored by: Orange Business Services Differential Revision: https://reviews.freebsd.org/D42361 --- sys/netpfil/pf/pf.c | 253 ++++++++++++++++++++++++++++++++++++++++++++--- sys/netpfil/pf/pf_norm.c | 2 + 2 files changed, 240 insertions(+), 15 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 1cd8412193dc..ec3ac106f34d 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -179,6 +179,34 @@ VNET_DEFINE(size_t, pf_allrulecount); 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_udp(struct pf_kstate **, 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 @@ pf_cleanup(void) 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 @@ pf_detach_state(struct pf_kstate *s) 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 @@ pf_test_state_sctp(struct pf_kstate **state, 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 pf_state_peer *src, *dst; struct sctphdr *sh = &pd->hdr.sctp; u_int8_t psrc; //, pdst; @@ -5871,9 +5903,11 @@ pf_test_state_sctp(struct pf_kstate **state, struct pfi_kkif *kif, 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 @@ pf_test_state_sctp(struct pf_kstate **state, struct pfi_kkif *kif, (*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 @@ pf_test_state_sctp(struct pf_kstate **state, struct pfi_kkif *kif, 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 @@ pf_sctp_multihome_delayed(struct pf_pdesc *pd, int off, struct pfi_kkif *kif, 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 @@ pf_multihome_scan(struct mbuf *m, int start, int len, struct pf_pdesc *pd, 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 index 9963fe2b741b..fb165cf548b0 100644 --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -1596,6 +1596,8 @@ pf_normalize_sctp_init(struct mbuf *m, int off, struct pf_pdesc *pd, return (1); } + dst->scrub->pfss_v_tag = pd->sctp_initiate_tag; + return (0); }