Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 19 Nov 2016 19:59:29 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r308864 - projects/ipsec/sys/netinet6
Message-ID:  <201611191959.uAJJxThe050482@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Sat Nov 19 19:59:28 2016
New Revision: 308864
URL: https://svnweb.freebsd.org/changeset/base/308864

Log:
  Add ip6_ipsec_forward() function and call it from ip6_forward().
  
  This function is inteded to check inbound and outbound security policies
  for forwarded packet. If inbound policy doesn't discard packet, then we
  check outbound policy. Since we act as router, we can apply only tunnel
  mode IPsec to forwarded traffic (with transport mode we will not receive
  responces from partner). So, if matched outbound policy has tunnel mode
  transform, we can handle packet with IPsec. And this packet will be
  consumed by ipsec6_process_packet(). Thus all logic related to IPsec
  and MTU calculation in ip6_forward() is just dead code.
  
  Remove ip[6]_ipsec_fwd() and all code with deep IPsec knowledge logic
  from ip6_forward(). Use ip6_ipsec_forward() instead.
  Also add IPSEC_FORWARD() wrapper macro.

Modified:
  projects/ipsec/sys/netinet6/ip6_forward.c
  projects/ipsec/sys/netinet6/ip6_ipsec.c
  projects/ipsec/sys/netinet6/ip6_ipsec.h

Modified: projects/ipsec/sys/netinet6/ip6_forward.c
==============================================================================
--- projects/ipsec/sys/netinet6/ip6_forward.c	Sat Nov 19 19:25:38 2016	(r308863)
+++ projects/ipsec/sys/netinet6/ip6_forward.c	Sat Nov 19 19:59:28 2016	(r308864)
@@ -71,9 +71,6 @@ __FBSDID("$FreeBSD$");
 
 #ifdef IPSEC
 #include <netinet6/ip6_ipsec.h>
-#include <netipsec/ipsec.h>
-#include <netipsec/ipsec6.h>
-#include <netipsec/key.h>
 #endif /* IPSEC */
 
 /*
@@ -100,9 +97,6 @@ ip6_forward(struct mbuf *m, int srcrt)
 	struct ifnet *origifp;	/* maybe unnecessary */
 	u_int32_t inzone, outzone;
 	struct in6_addr src_in6, dst_in6, odst;
-#ifdef IPSEC
-	struct secpolicy *sp = NULL;
-#endif
 	struct m_tag *fwd_tag;
 	char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
 
@@ -130,32 +124,17 @@ ip6_forward(struct mbuf *m, int srcrt)
 		m_freem(m);
 		return;
 	}
-#ifdef IPSEC
-	/*
-	 * Check if this packet has an active SA and needs to be dropped
-	 * instead of forwarded.
-	 */
-	if (ip6_ipsec_fwd(m) != 0) {
-		IP6STAT_INC(ip6s_cantforward);
-		m_freem(m);
-		return;
-	}
-#endif /* IPSEC */
 
+	if (
 #ifdef IPSTEALTH
-	if (!V_ip6stealth) {
+	    V_ip6stealth == 0 &&
 #endif
-	if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
+	    ip6->ip6_hlim <= IPV6_HLIMDEC) {
 		/* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */
 		icmp6_error(m, ICMP6_TIME_EXCEEDED,
-				ICMP6_TIME_EXCEED_TRANSIT, 0);
+		    ICMP6_TIME_EXCEED_TRANSIT, 0);
 		return;
 	}
-	ip6->ip6_hlim -= IPV6_HLIMDEC;
-
-#ifdef IPSTEALTH
-	}
-#endif
 
 	/*
 	 * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU -
@@ -168,167 +147,27 @@ ip6_forward(struct mbuf *m, int srcrt)
 	 */
 	mcopy = m_copym(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN),
 	    M_NOWAIT);
-
-#ifdef IPSEC
-	/* get a security policy for this packet */
-	sp = ipsec_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, &error);
-	if (sp == NULL) {
-		IPSEC6STAT_INC(ips_out_inval);
-		IP6STAT_INC(ip6s_cantforward);
-		if (mcopy) {
-#if 0
-			/* XXX: what icmp ? */
-#else
-			m_freem(mcopy);
+#ifdef IPSTEALTH
+	if (V_ip6stealth == 0)
 #endif
-		}
-		m_freem(m);
-		return;
-	}
+		ip6->ip6_hlim -= IPV6_HLIMDEC;
 
-	error = 0;
-
-	/* check policy */
-	switch (sp->policy) {
-	case IPSEC_POLICY_DISCARD:
-		/*
-		 * This packet is just discarded.
-		 */
-		IPSEC6STAT_INC(ips_out_polvio);
-		IP6STAT_INC(ip6s_cantforward);
-		KEY_FREESP(&sp);
-		if (mcopy) {
-#if 0
-			/* XXX: what icmp ? */
-#else
-			m_freem(mcopy);
-#endif
-		}
-		m_freem(m);
+#ifdef IPSEC
+	if (IPSEC_FORWARD(ipv6, m, &error) != 0) {
+		/* mbuf consumed by IPsec */
+		m_freem(mcopy);
 		return;
-
-	case IPSEC_POLICY_BYPASS:
-	case IPSEC_POLICY_NONE:
-		/* no need to do IPsec. */
-		KEY_FREESP(&sp);
-		goto skip_ipsec;
-
-	case IPSEC_POLICY_IPSEC:
-		if (sp->req == NULL) {
-			/* XXX should be panic ? */
-			printf("ip6_forward: No IPsec request specified.\n");
-			IP6STAT_INC(ip6s_cantforward);
-			KEY_FREESP(&sp);
-			if (mcopy) {
-#if 0
-				/* XXX: what icmp ? */
-#else
-				m_freem(mcopy);
-#endif
-			}
-			m_freem(m);
-			return;
-		}
-		/* do IPsec */
-		break;
-
-	case IPSEC_POLICY_ENTRUST:
-	default:
-		/* should be panic ?? */
-		printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
-		KEY_FREESP(&sp);
-		goto skip_ipsec;
 	}
-
-    {
-	struct ipsecrequest *isr = NULL;
-
-	/*
-	 * when the kernel forwards a packet, it is not proper to apply
-	 * IPsec transport mode to the packet. This check avoid from this.
-	 * at present, if there is even a transport mode SA request in the
-	 * security policy, the kernel does not apply IPsec to the packet.
-	 * this check is not enough because the following case is valid.
-	 *      ipsec esp/tunnel/xxx-xxx/require esp/transport//require;
-	 */
-	for (isr = sp->req; isr; isr = isr->next) {
-		if (isr->saidx.mode == IPSEC_MODE_ANY)
-			goto doipsectunnel;
-		if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
-			goto doipsectunnel;
-	}
-
-	/*
-	 * if there's no need for tunnel mode IPsec, skip.
-	 */
-	if (!isr)
-		goto skip_ipsec;
-
-    doipsectunnel:
-	/*
-	 * All the extension headers will become inaccessible
-	 * (since they can be encrypted).
-	 * Don't panic, we need no more updates to extension headers
-	 * on inner IPv6 packet (since they are now encapsulated).
-	 *
-	 * IPv6 [ESP|AH] IPv6 [extension headers] payload
-	 */
-
 	/*
-	 * If we need to encapsulate the packet, do it here
-	 * ipsec6_proces_packet will send the packet using ip6_output
+	 * mbuf wasn't consumed by IPsec, check error code.
 	 */
-	error = ipsec6_process_packet(m, sp->req);
-	/* Release SP if an error occurred */
-	if (error != 0)
-		KEY_FREESP(&sp);
-	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.
-		 */
-		error = 0;
-		goto skip_ipsec;
-	}
-
-	if (error) {
-		/* mbuf is already reclaimed in ipsec6_process_packet. */
-		switch (error) {
-		case EHOSTUNREACH:
-		case ENETUNREACH:
-		case EMSGSIZE:
-		case ENOBUFS:
-		case ENOMEM:
-			break;
-		default:
-			printf("ip6_output (ipsec): error code %d\n", error);
-			/* FALLTHROUGH */
-		case ENOENT:
-			/* don't show these error codes to the user */
-			break;
-		}
+	if (error != 0) {
 		IP6STAT_INC(ip6s_cantforward);
-		if (mcopy) {
-#if 0
-			/* XXX: what icmp ? */
-#else
-			m_freem(mcopy);
-#endif
-		}
+		m_freem(mcopy);
+		m_freem(m);
 		return;
-	} else {
-		/*
-		 * In the FAST IPSec case we have already
-		 * re-injected the packet and it has been freed
-		 * by the ipsec_done() function.  So, just clean
-		 * up after ourselves.
-		 */
-		m = NULL;
-		goto freecopy;
 	}
-    }
-skip_ipsec:
+	/* No IPsec processing required */
 #endif
 again:
 	bzero(&rin6, sizeof(struct route_in6));
@@ -540,34 +379,9 @@ pass:
 	/* See if the size was changed by the packet filter. */
 	if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) {
 		in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
-		if (mcopy) {
-			u_long mtu;
-#ifdef IPSEC
-			size_t ipsechdrsiz;
-#endif /* IPSEC */
-
-			mtu = IN6_LINKMTU(rt->rt_ifp);
-#ifdef IPSEC
-			/*
-			 * When we do IPsec tunnel ingress, we need to play
-			 * with the link value (decrement IPsec header size
-			 * from mtu value).  The code is much simpler than v4
-			 * case, as we have the outgoing interface for
-			 * encapsulated packet as "rt->rt_ifp".
-			 */
-			ipsechdrsiz = ipsec_hdrsiz(mcopy, IPSEC_DIR_OUTBOUND,
-			    NULL);
-			if (ipsechdrsiz < mtu)
-				mtu -= ipsechdrsiz;
-			/*
-			 * if mtu becomes less than minimum MTU,
-			 * tell minimum MTU (and I'll need to fragment it).
-			 */
-			if (mtu < IPV6_MMTU)
-				mtu = IPV6_MMTU;
-#endif /* IPSEC */
-			icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
-		}
+		if (mcopy)
+			icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0,
+			    IN6_LINKMTU(rt->rt_ifp));
 		goto bad;
 	}
 

Modified: projects/ipsec/sys/netinet6/ip6_ipsec.c
==============================================================================
--- projects/ipsec/sys/netinet6/ip6_ipsec.c	Sat Nov 19 19:25:38 2016	(r308863)
+++ projects/ipsec/sys/netinet6/ip6_ipsec.c	Sat Nov 19 19:59:28 2016	(r308864)
@@ -106,19 +106,6 @@ ip6_ipsec_filtertunnel(struct mbuf *m)
 }
 
 /*
- * Check if this packet has an active SA and needs to be dropped instead
- * of forwarded.
- * Called from ip6_forward().
- * 1 = drop packet, 0 = forward packet.
- */
-int
-ip6_ipsec_fwd(struct mbuf *m)
-{
-
-	return (ipsec6_in_reject(m, NULL));
-}
-
-/*
  * Check if protocol type doesn't have a further header and do IPSEC
  * decryption or reject right now.  Protocols with further headers get
  * their IPSEC treatment within the protocol specific processing.
@@ -221,53 +208,84 @@ ip6_ipsec_output(struct mbuf *m, struct 
 	return (0);
 }
 
-#if 0
 /*
- * Compute the MTU for a forwarded packet that gets IPSEC encapsulated.
- * Called from ip_forward().
- * Returns MTU suggestion for ICMP needfrag reply.
+ * Called from ip6_forward().
+ * 1 = drop packet, 0 = forward packet.
  */
 int
-ip6_ipsec_mtu(struct mbuf *m)
+ip6_ipsec_forward(struct mbuf *m, int *error)
 {
-	int mtu = 0;
+	struct secpolicy *sp;
+	int idx;
+
 	/*
-	 * If the packet is routed over IPsec tunnel, tell the
-	 * originator the tunnel MTU.
-	 *	tunnel MTU = if MTU - sizeof(IP) - ESP/AH hdrsiz
-	 * XXX quickhack!!!
-	 */
-#ifdef IPSEC
-	struct secpolicy *sp = NULL;
-	int ipsecerror;
-	int ipsechdr;
-	struct route *ro;
-	sp = ipsec_getpolicybyaddr(m,
-				   IPSEC_DIR_OUTBOUND,
-				   IP_FORWARDING,
-				   &ipsecerror);
+	 * Check if this packet has an active inbound SP and needs to be
+	 * dropped instead of forwarded.
+	 */
+	if (ipsec6_in_reject(m, NULL) != 0) {
+		*error = EACCES;
+		return (0);
+	}
+	/*
+	 * Now check outbound SP.
+	 */
+	sp = ipsec6_checkpolicy(m, NULL, error);
+	/*
+	 * There are four return cases:
+	 *    sp != NULL		    apply IPsec policy
+	 *    sp == NULL, error == 0	    no IPsec handling needed
+	 *    sp == NULL, error == -EINVAL  discard packet w/o error
+	 *    sp == NULL, error != 0	    discard packet, report error
+	 */
 	if (sp != NULL) {
-		/* count IPsec header size */
-		ipsechdr = ipsec_hdrsiz(m, IPSEC_DIR_OUTBOUND, NULL);
-
 		/*
-		 * find the correct route for outer IPv4
-		 * header, compute tunnel MTU.
+		 * We have SP with IPsec transform, but we should check that
+		 * it has tunnel mode request, because we can't use transport
+		 * mode when forwarding.
+		 *
+		 * RFC2473 says:
+		 * "A tunnel IPv6 packet resulting from the encapsulation of
+		 * an original packet is considered an IPv6 packet originating
+		 * from the tunnel entry-point node."
+		 * So, we don't need MTU checking, after IPsec processing
+		 * we will just fragment it if needed.
 		 */
-		if (sp->req != NULL &&
-		    sp->req->sav != NULL &&
-		    sp->req->sav->sah != NULL) {
-			ro = &sp->req->sav->sah->route_cache.sa_route;
-			if (ro->ro_rt && ro->ro_rt->rt_ifp) {
-				mtu = ro->ro_rt->rt_mtu ? ro->ro_rt->rt_mtu :
-				    ro->ro_rt->rt_ifp->if_mtu;
-				mtu -= ipsechdr;
-			}
+		for (idx = 0; idx < sp->tcount; idx++) {
+			if (sp->req[idx]->saidx.mode == IPSEC_MODE_TUNNEL)
+				break;
+		}
+		if (idx == sp->tcount) {
+			*error = EACCES;
+			IPSEC6STAT_INC(ips_out_inval);
+			key_freesp(&sp);
+			return (0);
 		}
-		KEY_FREESP(&sp);
+		/* NB: callee frees mbuf and releases reference to SP */
+		*error = ipsec6_process_packet(m, sp, NULL);
+		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;
+			return (0);
+		}
+		return (1);	/* mbuf consumed by IPsec */
+	} else {	/* 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;
+			m_freem(m);
+			return (1);
+		}
+		/* No IPsec processing for this packet. */
 	}
-#endif /* IPSEC */
-	/* XXX else case missing. */
-	return mtu;
+	return (0);
 }
-#endif

Modified: projects/ipsec/sys/netinet6/ip6_ipsec.h
==============================================================================
--- projects/ipsec/sys/netinet6/ip6_ipsec.h	Sat Nov 19 19:25:38 2016	(r308863)
+++ projects/ipsec/sys/netinet6/ip6_ipsec.h	Sat Nov 19 19:59:28 2016	(r308864)
@@ -32,13 +32,11 @@
 #ifndef _NETINET_IP6_IPSEC_H_
 #define _NETINET_IP6_IPSEC_H_
 
+#define	IPSEC_FORWARD(sc, m, perr)	ip6_ipsec_forward((m), (perr))
 #define	IPSEC_OUTPUT(sc, m, inp, perr)	ip6_ipsec_output((m), (inp), (perr))
 
 int	ip6_ipsec_filtertunnel(struct mbuf *);
-int	ip6_ipsec_fwd(struct mbuf *);
 int	ip6_ipsec_input(struct mbuf *, int);
+int	ip6_ipsec_forward(struct mbuf *, int *);
 int	ip6_ipsec_output(struct mbuf *, struct inpcb *, int *);
-#if 0
-int	ip6_ipsec_mtu(struct mbuf *);
-#endif
 #endif



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