Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 28 Dec 2016 16:30:09 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r310710 - projects/ipsec/sys/netipsec
Message-ID:  <201612281630.uBSGU92w087393@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Wed Dec 28 16:30:08 2016
New Revision: 310710
URL: https://svnweb.freebsd.org/changeset/base/310710

Log:
  Add ipsec_prepend() - optimized version of M_PREPEND().
  
  We use M_PREPEND() to prepend outer IP header for tunnel mode SA.
  This may lead to allocation of new mbuf, that will be aligned to
  the end of mbuf's data, i.e. all unused space is at the beginning of
  the mbuf. After IP encapsulation some IPsec transforms is performed.
  This inserts new ESP/AH/IPCOMP header just after outer IP header.
  Since mbuf is aligned, m_makespace() needs to allocate new mbuf, or
  copy all data after outer IP header into new place to prepare the
  space for ESP/AH/IPCOMP header.
  
  To avoid this, ipsec_prepend() reserves space at the beginning to be able
  place link level header, and at the end, to be able place ESP/AH/IPCOMP
  header.
  
  Also modify m_makespace() to handle the case, when at the beginning of
  mbuf is enough space to move outer IP header, instead of moving all
  remaining data to the end of mbuf.

Modified:
  projects/ipsec/sys/netipsec/ipsec_mbuf.c
  projects/ipsec/sys/netipsec/ipsec_output.c
  projects/ipsec/sys/netipsec/udpencap.c

Modified: projects/ipsec/sys/netipsec/ipsec_mbuf.c
==============================================================================
--- projects/ipsec/sys/netipsec/ipsec_mbuf.c	Wed Dec 28 16:28:09 2016	(r310709)
+++ projects/ipsec/sys/netipsec/ipsec_mbuf.c	Wed Dec 28 16:30:08 2016	(r310710)
@@ -70,7 +70,21 @@ m_makespace(struct mbuf *m0, int skip, i
 	 * the contents of m as needed.
 	 */
 	remain = m->m_len - skip;		/* data to move */
-	if (hlen > M_TRAILINGSPACE(m)) {
+	if (remain > skip &&
+	    hlen + max_linkhdr < M_LEADINGSPACE(m)) {
+		/*
+		 * mbuf has enough free space at the beginning.
+		 * XXX: which operation is the most heavy - copying of
+		 *	possible several hundred of bytes or allocation
+		 *	of new mbuf? We can remove max_linkhdr check
+		 *	here, but it is possible that this will lead
+		 *	to allocation of new mbuf in Layer 2 code.
+		 */
+		m->m_data -= hlen;
+		bcopy(mtodo(m, hlen), mtod(m, caddr_t), skip);
+		m->m_len += hlen;
+		*off = skip;
+	} else if (hlen > M_TRAILINGSPACE(m)) {
 		struct mbuf *n0, *n, **np;
 		int todo, len, done, alloc;
 
@@ -138,7 +152,7 @@ m_makespace(struct mbuf *m0, int skip, i
 		 * so there's space to write the new header.
 		 */
 		bcopy(mtod(m, caddr_t) + skip,
-		      mtod(m, caddr_t) + skip + hlen, remain);
+		    mtod(m, caddr_t) + skip + hlen, remain);
 		m->m_len += hlen;
 		*off = skip;
 	}

Modified: projects/ipsec/sys/netipsec/ipsec_output.c
==============================================================================
--- projects/ipsec/sys/netipsec/ipsec_output.c	Wed Dec 28 16:28:09 2016	(r310709)
+++ projects/ipsec/sys/netipsec/ipsec_output.c	Wed Dec 28 16:30:08 2016	(r310710)
@@ -73,6 +73,9 @@
 #include <netinet/sctp_crc32.h>
 #endif
 
+#include <netinet/udp.h>
+#include <netipsec/ah.h>
+#include <netipsec/esp.h>
 #include <netipsec/ipsec.h>
 #ifdef INET6
 #include <netipsec/ipsec6.h>
@@ -845,6 +848,52 @@ bad:
 	return (error);
 }
 
+/*
+ * ipsec_prepend() is optimized version of M_PREPEND().
+ * ipsec_encap() is called by IPsec output routine for tunnel mode SA.
+ * It is expected that after IP encapsulation some IPsec transform will
+ * be performed. Each IPsec transform inserts its variable length header
+ * just after outer IP header using m_makespace(). If given mbuf has not
+ * enough free space at the beginning, we allocate new mbuf and reserve
+ * some space at the beginning and at the end.
+ * This helps avoid allocating of new mbuf and data copying in m_makespace(),
+ * we place outer header in the middle of mbuf's data with reserved leading
+ * and trailing space:
+ *	[ LEADINGSPACE ][ Outer IP header ][ TRAILINGSPACE ]
+ * LEADINGSPACE will be used to add ethernet header, TRAILINGSPACE will
+ * be used to inject AH/ESP/IPCOMP header.
+ */
+#define	IPSEC_TRAILINGSPACE	(sizeof(struct udphdr) +/* NAT-T */	\
+    max(sizeof(struct newesp) + EALG_MAX_BLOCK_LEN,	/* ESP + IV */	\
+	sizeof(struct newah) + HASH_MAX_LEN		/* AH + ICV */))
+static struct mbuf *
+ipsec_prepend(struct mbuf *m, int len, int how)
+{
+	struct mbuf *n;
+
+	M_ASSERTPKTHDR(m);
+	IPSEC_ASSERT(len < MHLEN, ("wrong length"));
+	if (M_LEADINGSPACE(m) >= len) {
+		/* No need to allocate new mbuf. */
+		m->m_data -= len;
+		m->m_len += len;
+		m->m_pkthdr.len += len;
+		return (m);
+	}
+	n = m_gethdr(how, m->m_type);
+	if (n == NULL) {
+		m_freem(m);
+		return (NULL);
+	}
+	m_move_pkthdr(n, m);
+	n->m_next = m;
+	if (len + IPSEC_TRAILINGSPACE < M_SIZE(n))
+		m_align(n, len + IPSEC_TRAILINGSPACE);
+	n->m_len = len;
+	n->m_pkthdr.len += len;
+	return (n);
+}
+
 static int
 ipsec_encap(struct mbuf **mp, struct secasindex *saidx)
 {
@@ -896,7 +945,7 @@ ipsec_encap(struct mbuf **mp, struct sec
 		    saidx->src.sin.sin_addr.s_addr == INADDR_ANY ||
 		    saidx->dst.sin.sin_addr.s_addr == INADDR_ANY)
 			return (EINVAL);
-		M_PREPEND(*mp, sizeof(struct ip), M_NOWAIT);
+		*mp = ipsec_prepend(*mp, sizeof(struct ip), M_NOWAIT);
 		if (*mp == NULL)
 			return (ENOBUFS);
 		ip = mtod(*mp, struct ip *);
@@ -919,7 +968,7 @@ ipsec_encap(struct mbuf **mp, struct sec
 		    IN6_IS_ADDR_UNSPECIFIED(&saidx->src.sin6.sin6_addr) ||
 		    IN6_IS_ADDR_UNSPECIFIED(&saidx->dst.sin6.sin6_addr))
 			return (EINVAL);
-		M_PREPEND(*mp, sizeof(struct ip6_hdr), M_NOWAIT);
+		*mp = ipsec_prepend(*mp, sizeof(struct ip6_hdr), M_NOWAIT);
 		if (*mp == NULL)
 			return (ENOBUFS);
 		ip6 = mtod(*mp, struct ip6_hdr *);

Modified: projects/ipsec/sys/netipsec/udpencap.c
==============================================================================
--- projects/ipsec/sys/netipsec/udpencap.c	Wed Dec 28 16:28:09 2016	(r310709)
+++ projects/ipsec/sys/netipsec/udpencap.c	Wed Dec 28 16:30:08 2016	(r310710)
@@ -232,19 +232,16 @@ udp_ipsec_output(struct mbuf *m, struct 
 		DPRINTF(("%s: m_makespace for udphdr failed\n", __func__));
 		return (ENOBUFS);
 	}
-	/*
-	 * IP header should stay in the same place of mbuf m.
-	 * m_makespace() can add new mbuf or move the remaining data into
-	 * the end of mbuf space. Thus mtod() isn't required for ip pointer.
-	 */
+
 	udp = mtodo(n, off);
 	udp->uh_dport = sav->natt->dport;
 	udp->uh_sport = sav->natt->sport;
 	udp->uh_sum = 0;
 	udp->uh_ulen = htons(m->m_pkthdr.len - hlen);
+
+	ip = mtod(m, struct ip *);
 	ip->ip_len = htons(m->m_pkthdr.len);
 	ip->ip_p = IPPROTO_UDP;
-
 	return (0);
 }
 



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