Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 6 Dec 2016 12:52:20 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r309614 - projects/ipsec/sys/netipsec
Message-ID:  <201612061252.uB6CqKnH071526@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Tue Dec  6 12:52:20 2016
New Revision: 309614
URL: https://svnweb.freebsd.org/changeset/base/309614

Log:
  Due to the changes in SADB now each SA has unique SPI.
  Previously TCP-MD5 SAs have one magic SPI = 0x1000. This was used by
  some applications. E.g. bird uses SADB_ADD and SADB_DELETE PF_KEY
  messages to configure security key used by TCP_MD5SIG socket option.
  To support old applications and configs add the workaround.
  
  When SADB_ADD, SADB_GET and SADB_DELETE messages are used for
  IPPROTO_TCP, do lookup for needed SA using its secasindex, instead of
  SPI. When several TCP-MD5 SAs added with one SPI value, use key_do_getnewspi
  to allocate new SPI value in SADB_ADD.

Modified:
  projects/ipsec/sys/netipsec/key.c

Modified: projects/ipsec/sys/netipsec/key.c
==============================================================================
--- projects/ipsec/sys/netipsec/key.c	Tue Dec  6 12:43:07 2016	(r309613)
+++ projects/ipsec/sys/netipsec/key.c	Tue Dec  6 12:52:20 2016	(r309614)
@@ -4732,6 +4732,58 @@ key_do_getnewspi(struct sadb_spirange *s
 }
 
 /*
+ * Find TCP-MD5 SA with corresponding secasindex.
+ * If not found, return NULL and fill SPI with usable value if needed.
+ */
+static struct secasvar *
+key_getsav_tcpmd5(struct secasindex *saidx, uint32_t *spi)
+{
+	SAHTREE_RLOCK_TRACKER;
+	struct secashead *sah;
+	struct secasvar *sav;
+
+	IPSEC_ASSERT(saidx->proto == IPPROTO_TCP, ("wrong proto"));
+	SAHTREE_RLOCK();
+	LIST_FOREACH(sah, SAHADDRHASH_HASH(saidx), addrhash) {
+		if (sah->saidx.proto != IPPROTO_TCP)
+			continue;
+		if (!key_sockaddrcmp(&saidx->dst.sa, &sah->saidx.dst.sa,
+		    key_portfromsaddr(&sah->saidx.dst.sa)))
+			break;
+	}
+	if (sah != NULL) {
+		if (V_key_preferred_oldsa)
+			sav = TAILQ_LAST(&sah->savtree_alive, secasvar_queue);
+		else
+			sav = TAILQ_FIRST(&sah->savtree_alive);
+		if (sav != NULL) {
+			SAV_ADDREF(sav);
+			SAHTREE_RUNLOCK();
+			return (sav);
+		}
+	}
+	if (spi == NULL) {
+		/* No SPI required */
+		SAHTREE_RUNLOCK();
+		return (NULL);
+	}
+	/* Check that SPI is unique */
+	LIST_FOREACH(sav, SAVHASH_HASH(*spi), spihash) {
+		if (sav->spi == *spi)
+			break;
+	}
+	if (sav == NULL) {
+		SAHTREE_RUNLOCK();
+		/* SPI is already unique */
+		return (NULL);
+	}
+	SAHTREE_RUNLOCK();
+	/* XXX: not optimal */
+	*spi = key_do_getnewspi(NULL, saidx);
+	return (NULL);
+}
+
+/*
  * SADB_UPDATE processing
  * receive
  *   <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
@@ -4965,7 +5017,7 @@ key_add(struct socket *so, struct mbuf *
 	struct sadb_address *src0, *dst0;
 	struct sadb_sa *sa0;
 	struct secasvar *sav;
-	uint32_t reqid;
+	uint32_t reqid, spi;
 	uint8_t mode, proto;
 	int error;
 
@@ -5046,19 +5098,36 @@ key_add(struct socket *so, struct mbuf *
 	/*
 	 * Make sure the port numbers are zero.
 	 * In case of NAT-T we will update them later if needed.
+	 * XXXAE: TCP-MD5 may set dst port.
 	 */
 	key_porttosaddr(&saidx.src.sa, 0);
 	key_porttosaddr(&saidx.dst.sa, 0);
 
-	/* We can create new SA only if SPI is different. */
-	sav = key_getsavbyspi(sa0->sadb_sa_spi);
+	spi = sa0->sadb_sa_spi;
+	/*
+	 * XXX: For TCP-MD5 SAs we don't use SPI.
+	 * Check the uniqueness using secasindex.
+	 */
+	if (proto == IPPROTO_TCP) {
+		sav = key_getsav_tcpmd5(&saidx, &spi);
+		if (sav == NULL && spi == 0) {
+			/* Failed to allocate SPI */
+			ipseclog((LOG_DEBUG, "%s: SA already exists.\n",
+			    __func__));
+			return key_senderror(so, m, EEXIST);
+		}
+		/* XXX: SPI that we report back can have another value */
+	} else {
+		/* We can create new SA only if SPI is different. */
+		sav = key_getsavbyspi(spi);
+	}
 	if (sav != NULL) {
 		key_freesav(&sav);
 		ipseclog((LOG_DEBUG, "%s: SA already exists.\n", __func__));
 		return key_senderror(so, m, EEXIST);
 	}
 
-	sav = key_newsav(mhp, &saidx, sa0->sadb_sa_spi, &error);
+	sav = key_newsav(mhp, &saidx, spi, &error);
 	if (sav == NULL)
 		return key_senderror(so, m, error);
 	KEYDBG(KEY_STAMP,
@@ -5333,7 +5402,10 @@ key_delete(struct socket *so, struct mbu
 		return (key_senderror(so, m, EINVAL));
 	}
 	sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
-	sav = key_getsavbyspi(sa0->sadb_sa_spi);
+	if (proto == IPPROTO_TCP)
+		sav = key_getsav_tcpmd5(&saidx, NULL);
+	else
+		sav = key_getsavbyspi(sa0->sadb_sa_spi);
 	if (sav == NULL) {
 		ipseclog((LOG_DEBUG, "%s: no SA found for SPI %u.\n",
 		    __func__, ntohl(sa0->sadb_sa_spi)));
@@ -5504,14 +5576,17 @@ key_get(struct socket *so, struct mbuf *
 	key_porttosaddr(&saidx.src.sa, 0);
 	key_porttosaddr(&saidx.dst.sa, 0);
 
-	sav = key_getsavbyspi(sa0->sadb_sa_spi);
+	if (proto == IPPROTO_TCP)
+		sav = key_getsav_tcpmd5(&saidx, NULL);
+	else
+		sav = key_getsavbyspi(sa0->sadb_sa_spi);
 	if (sav == NULL) {
 		ipseclog((LOG_DEBUG, "%s: no SA found.\n", __func__));
 		return key_senderror(so, m, ESRCH);
 	}
 	if (key_cmpsaidx(&sav->sah->saidx, &saidx, CMP_HEAD) == 0) {
 		ipseclog((LOG_DEBUG, "%s: saidx mismatched for SPI %u.\n",
-		    __func__, ntohl(sav->spi)));
+		    __func__, ntohl(sa0->sadb_sa_spi)));
 		key_freesav(&sav);
 		return (key_senderror(so, m, ESRCH));
 	}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201612061252.uB6CqKnH071526>