Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 27 Oct 2012 12:03:01 +0000 (UTC)
From:      Andre Oppermann <andre@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-user@freebsd.org
Subject:   svn commit: r242177 - in user/andre/tcp_workqueue/sys: conf netipsec
Message-ID:  <201210271203.q9RC31bf069222@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: andre
Date: Sat Oct 27 12:03:00 2012
New Revision: 242177
URL: http://svn.freebsd.org/changeset/base/242177

Log:
  Convert IPsec packet path processing into a pfil hook that can
  be inserted at runtime and loaded as kernel module.
  
  In this first step the functionality of ip_ipsec.c and the IP
  input and output path is moved into pfil hooks.
  
  This is a first rudimentary implementation approach and not yet
  functional.

Added:
  user/andre/tcp_workqueue/sys/netipsec/ipsec_pfil.c
Modified:
  user/andre/tcp_workqueue/sys/conf/files

Modified: user/andre/tcp_workqueue/sys/conf/files
==============================================================================
--- user/andre/tcp_workqueue/sys/conf/files	Sat Oct 27 10:33:51 2012	(r242176)
+++ user/andre/tcp_workqueue/sys/conf/files	Sat Oct 27 12:03:00 2012	(r242177)
@@ -3135,6 +3135,7 @@ netipsec/ipsec.c		optional ipsec inet | 
 netipsec/ipsec_input.c		optional ipsec inet | ipsec inet6
 netipsec/ipsec_mbuf.c		optional ipsec inet | ipsec inet6
 netipsec/ipsec_output.c		optional ipsec inet | ipsec inet6
+netipsec/ipsec_pfil.c		optional ipsec inet | ipsec inet6
 netipsec/key.c			optional ipsec inet | ipsec inet6
 netipsec/key_debug.c		optional ipsec inet | ipsec inet6
 netipsec/keysock.c		optional ipsec inet | ipsec inet6

Added: user/andre/tcp_workqueue/sys/netipsec/ipsec_pfil.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ user/andre/tcp_workqueue/sys/netipsec/ipsec_pfil.c	Sat Oct 27 12:03:00 2012	(r242177)
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2012 Andre Oppermann, Internet Business Solutions AG
+ * All rights reserved.
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_ipsec.h"
+#include "opt_sctp.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/pfil.h>
+#include <net/vnet.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_options.h>
+#include <netinet/ip_ipsec.h>
+
+#ifdef SCTP
+#include <netinet/sctp_crc32.h>
+#endif
+#include <machine/in_cksum.h>
+
+#include <netipsec/ipsec.h>
+#include <netipsec/xform.h>
+#include <netipsec/key.h>
+
+extern	struct protosw inetsw[];
+
+/*
+ * Implement IPSec as pfil hook for host mode.
+ */
+
+static int
+ipsec_pfil_run(void *arg, struct mbuf **m, struct ifnet *ifp, int dir,
+    struct inpcb *inp)
+{
+	struct ip *ip = mtod(*m, struct ip *);
+	struct m_tag *mtag;
+	struct tdb_ident *tdbi;
+	struct secpolicy *sp;
+	int error = 0;
+
+	switch (dir) {
+	case PFIL_IN:
+		/*
+		 * The input path doesn't do a transform.
+		 */
+		if ((inetsw[ip_protox[ip->ip_p]].pr_flags & PR_LASTHDR) != 0)
+			return (0);
+		/*
+		 * Check if the packet has already had IPsec processing
+		 * done.  If so, then just pass it along.  This tag gets
+		 * set during AH, ESP, etc. input handling, before the
+		 * packet is returned to the ip input queue for delivery.
+		 */ 
+		mtag = m_tag_find(*m, PACKET_TAG_IPSEC_IN_DONE, NULL);
+		if (mtag != NULL) {
+			tdbi = (struct tdb_ident *)(mtag + 1);
+			sp = ipsec_getpolicy(tdbi, IPSEC_DIR_INBOUND);
+		} else
+			sp = ipsec_getpolicybyaddr(*m, IPSEC_DIR_INBOUND,
+					   IP_FORWARDING, &error);   
+
+		/* Check security policy against packet attributes. */
+		if (sp != NULL) {
+			error = ipsec_in_reject(sp, *m);
+			KEY_FREESP(&sp);
+		} else
+			error = EINVAL;
+		break;
+
+	case PFIL_OUT:
+		/*
+		 * Check the security policy (SP) for the packet and, if
+		 * required, do IPsec-related processing.  There are two
+		 * cases here; the first time a packet is sent through
+		 * it will be untagged and handled by ipsec4_checkpolicy.
+		 * If the packet is resubmitted to ip_output (e.g. after
+		 * AH, ESP, etc. processing), there will be a tag to bypass
+		 * the lookup and related policy checking.
+		 */
+		mtag = m_tag_find(*m, PACKET_TAG_IPSEC_PENDING_TDB, NULL);
+		if (mtag != NULL) {
+			tdbi = (struct tdb_ident *)(mtag + 1);
+			sp = ipsec_getpolicy(tdbi, IPSEC_DIR_OUTBOUND);
+			if (sp == NULL) {
+				error = -EINVAL;	/* force silent drop */
+				goto drop;
+			}
+			m_tag_delete(*m, mtag);
+		} else
+			sp = ipsec4_checkpolicy(*m, IPSEC_DIR_OUTBOUND, 0,
+						&error, inp);
+
+		if (sp == NULL) {
+			if (error != 0) {
+				/*
+				 * Hack: -EINVAL is used to signal that a packet
+				 * should be silently discarded.  This is typically
+				 * because we asked key management for an SA and
+				 * it was delayed (e.g. kicked up to IKE).
+				 */
+				if (error == -EINVAL)
+					error = 0;
+				goto drop;
+			}
+			return (0);
+		}
+
+		/* Loop detection, check if ipsec processing already done */
+		KASSERT(sp->req != NULL, ("ip_output: no ipsec request"));
+
+		for (mtag = m_tag_first(*m); mtag != NULL;
+		     mtag = m_tag_next(*m, mtag)) {
+			if (mtag->m_tag_cookie != MTAG_ABI_COMPAT)
+				continue;
+			if (mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_DONE &&
+			    mtag->m_tag_id != PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED)
+				continue;
+			/*
+			 * Check if policy has an SA associated with it.
+			 * This can happen when an SP has yet to acquire
+			 * an SA; e.g. on first reference.  If it occurs,
+			 * then we let ipsec4_process_packet do its thing.
+			 */
+			if (sp->req->sav == NULL)
+				break;
+			tdbi = (struct tdb_ident *)(mtag + 1);
+			if (tdbi->spi == sp->req->sav->spi &&
+			    tdbi->proto == sp->req->sav->sah->saidx.proto &&
+			    bcmp(&tdbi->dst, &sp->req->sav->sah->saidx.dst,
+				 sizeof (union sockaddr_union)) == 0) {
+				/*
+				 * No IPsec processing is needed, free
+				 * reference to SP.
+				 *
+				 * NB: null pointer to avoid free at
+				 *     done: below.
+				 */
+				KEY_FREESP(&sp);
+				return (0);
+			}
+		}
+
+		/*
+		 * Do delayed checksums now because we send before
+		 * this is done in the normal processing path.
+		 */
+		if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+			in_delayed_cksum(*m);
+			(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
+		}
+#ifdef SCTP
+		if ((*m)->m_pkthdr.csum_flags & CSUM_SCTP) {
+			struct ip *ip = mtod(*m, struct ip *);
+
+			sctp_delayed_cksum(*m, (uint32_t)(ip->ip_hl << 2));
+			(*m)->m_pkthdr.csum_flags &= ~CSUM_SCTP;
+		}
+#endif
+		/* NB: callee frees mbuf */
+		error = ipsec4_process_packet(*m, sp->req, 0, 0);
+		if (error == EJUSTRETURN) {
+			/*
+			 * We had a SP with a level of 'use' and no SA. We
+			 * will just continue to process the packet without
+			 * IPsec processing and return without error.
+			 */
+			error = 0;
+			KEY_FREESP(&sp);
+			return (0);
+		}
+		/*
+		 * Preserve KAME behaviour: ENOENT can be returned
+		 * when an SA acquire is in progress.  Don't propagate
+		 * this to user-level; it confuses applications.
+		 *
+		 * XXX this will go away when the SADB is redone.
+		 */
+		if (error == ENOENT)
+			error = 0;
+		goto drop;
+
+		break;
+
+	default:
+		break;
+	}
+
+drop:
+	if (error < 0)
+		error = EACCES;
+	if (sp != NULL)
+		KEY_FREESP(&sp);
+
+	m_freem(*m);
+	return (error);
+}
+
+static int
+ipsec_pfil_hook(int af)
+{
+	struct pfil_head *pfh;
+
+	pfh = pfil_head_get(PFIL_TYPE_AF, af);
+	if (pfh == NULL)
+		return ENOENT;
+
+	pfil_add_hook_order(ipsec_pfil_run, NULL, "ipsec",
+	    (PFIL_IN | PFIL_OUT), PFIL_ORDER_FIRST, pfh);
+
+	return (0);
+}
+
+static int
+ipsec_pfil_unhook(int af)
+{
+	struct pfil_head *pfh;
+
+	pfh = pfil_head_get(PFIL_TYPE_AF, af);
+	if (pfh == NULL)
+		return ENOENT;
+
+	pfil_remove_hook(ipsec_pfil_run, NULL, (PFIL_IN | PFIL_OUT), pfh);
+
+	return (0);
+}
+
+static void
+ipsec_pfil_init(void)
+{
+
+	(void)ipsec_pfil_hook(AF_INET);
+	(void)ipsec_pfil_unhook(AF_INET);
+}
+
+SYSINIT(ipsec_pfil_init, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, ipsec_pfil_init, NULL);
+



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