Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 5 Nov 2014 09:23:29 +0000 (UTC)
From:      "Andrey V. Elsukov" <ae@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org
Subject:   svn commit: r274132 - in stable/10/sys: netinet6 netipsec
Message-ID:  <201411050923.sA59NTjY055666@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ae
Date: Wed Nov  5 09:23:29 2014
New Revision: 274132
URL: https://svnweb.freebsd.org/changeset/base/274132

Log:
  MFC r266800 by vanhu:
    IPv4-in-IPv6 and IPv6-in-IPv4 IPsec tunnels.
    For IPv6-in-IPv4, you may need to do the following command
    on the tunnel interface if it is configured as IPv4 only:
    ifconfig <interface> inet6 -ifdisabled
  
    Code logic inspired from NetBSD.
    PR: kern/169438
  
  MC r266822 by bz:
    Use IPv4 statistics in ipsec4_process_packet() rather than the IPv6
    version.  This also unbreaks the NOINET6 builds after r266800.
  
  MFC r268083 by zec:
    The assumption in ipsec4_process_packet() that the payload may be
    only IPv4 is wrong, so check the IP version before mangling the
    payload header.
  
  MFC r272394:
    Do not strip outer header when operating in transport mode.
    Instead requeue mbuf back to IPv4 protocol handler. If there is one extra IP-IP
    encapsulation, it will be handled with tunneling interface. And thus proper
    interface will be exposed into mbuf's rcvif. Also, tcpdump that listens on tunneling
    interface will see packets in both directions.
  
  PR:		194761

Modified:
  stable/10/sys/netinet6/ip6_forward.c
  stable/10/sys/netinet6/ip6_ipsec.c
  stable/10/sys/netinet6/ip6_ipsec.h
  stable/10/sys/netinet6/ip6_output.c
  stable/10/sys/netinet6/ip6_var.h
  stable/10/sys/netipsec/ipsec6.h
  stable/10/sys/netipsec/ipsec_input.c
  stable/10/sys/netipsec/ipsec_output.c
  stable/10/sys/netipsec/xform_ipip.c
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/netinet6/ip6_forward.c
==============================================================================
--- stable/10/sys/netinet6/ip6_forward.c	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netinet6/ip6_forward.c	Wed Nov  5 09:23:29 2014	(r274132)
@@ -251,7 +251,6 @@ ip6_forward(struct mbuf *m, int srcrt)
 
     {
 	struct ipsecrequest *isr = NULL;
-	struct ipsec_output_state state;
 
 	/*
 	 * when the kernel forwards a packet, it is not proper to apply
@@ -284,18 +283,27 @@ ip6_forward(struct mbuf *m, int srcrt)
 	 *
 	 * IPv6 [ESP|AH] IPv6 [extension headers] payload
 	 */
-	bzero(&state, sizeof(state));
-	state.m = m;
-	state.ro = NULL;	/* update at ipsec6_output_tunnel() */
-	state.dst = NULL;	/* update at ipsec6_output_tunnel() */
 
-	error = ipsec6_output_tunnel(&state, sp, 0);
+	/*
+	 * If we need to encapsulate the packet, do it here
+	 * ipsec6_proces_packet will send the packet using ip6_output
+	 */
+	error = ipsec6_process_packet(m, sp->req);
 
-	m = state.m;
 	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_output_tunnel. */
+		/* mbuf is already reclaimed in ipsec6_process_packet. */
 		switch (error) {
 		case EHOSTUNREACH:
 		case ENETUNREACH:
@@ -318,7 +326,6 @@ ip6_forward(struct mbuf *m, int srcrt)
 			m_freem(mcopy);
 #endif
 		}
-		m_freem(m);
 		return;
 	} else {
 		/*
@@ -330,25 +337,7 @@ ip6_forward(struct mbuf *m, int srcrt)
 		m = NULL;
 		goto freecopy;
 	}
-
-	if ((m != NULL) && (ip6 != mtod(m, struct ip6_hdr *)) ){
-		/*
-		 * now tunnel mode headers are added.  we are originating
-		 * packet instead of forwarding the packet.
-		 */
-		ip6_output(m, NULL, NULL, IPV6_FORWARDING/*XXX*/, NULL, NULL,
-		    NULL);
-		goto freecopy;
-	}
-
-	/* adjust pointer */
-	dst = (struct sockaddr_in6 *)state.dst;
-	rt = state.ro ? state.ro->ro_rt : NULL;
-	if (dst != NULL && rt != NULL)
-		ipsecrt = 1;
     }
-	if (ipsecrt)
-		goto skip_routing;
 skip_ipsec:
 #endif
 again:
@@ -371,9 +360,6 @@ again2:
 		goto bad;
 	}
 	rt = rin6.ro_rt;
-#ifdef IPSEC
-skip_routing:
-#endif
 
 	/*
 	 * Source scope check: if a packet can't be delivered to its

Modified: stable/10/sys/netinet6/ip6_ipsec.c
==============================================================================
--- stable/10/sys/netinet6/ip6_ipsec.c	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netinet6/ip6_ipsec.c	Wed Nov  5 09:23:29 2014	(r274132)
@@ -220,23 +220,22 @@ ip6_ipsec_input(struct mbuf *m, int nxt)
 
 int
 ip6_ipsec_output(struct mbuf **m, struct inpcb *inp, int *flags, int *error,
-    struct ifnet **ifp, struct secpolicy **sp)
+    struct ifnet **ifp)
 {
 #ifdef IPSEC
+	struct secpolicy *sp = NULL;
 	struct tdb_ident *tdbi;
 	struct m_tag *mtag;
 	/* XXX int s; */
-	if (sp == NULL)
-		return 1;
 	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)
+		sp = ipsec_getpolicy(tdbi, IPSEC_DIR_OUTBOUND);
+		if (sp == NULL)
 			*error = -EINVAL;	/* force silent drop */
 		m_tag_delete(*m, mtag);
 	} else {
-		*sp = ipsec4_checkpolicy(*m, IPSEC_DIR_OUTBOUND, *flags,
+		sp = ipsec4_checkpolicy(*m, IPSEC_DIR_OUTBOUND, *flags,
 					error, inp);
 	}
 
@@ -247,9 +246,9 @@ ip6_ipsec_output(struct mbuf **m, struct
 	 *    sp == NULL, error == -EINVAL  discard packet w/o error
 	 *    sp == NULL, error != 0	    discard packet, report error
 	 */
-	if (*sp != NULL) {
+	if (sp != NULL) {
 		/* Loop detection, check if ipsec processing already done */
-		KASSERT((*sp)->req != NULL, ("ip_output: no ipsec request"));
+		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)
@@ -263,12 +262,12 @@ ip6_ipsec_output(struct mbuf **m, struct
 			 * an SA; e.g. on first reference.  If it occurs,
 			 * then we let ipsec4_process_packet do its thing.
 			 */
-			if ((*sp)->req->sav == NULL)
+			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,
+			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
@@ -277,7 +276,7 @@ ip6_ipsec_output(struct mbuf **m, struct
 				 * NB: null pointer to avoid free at
 				 *     done: below.
 				 */
-				KEY_FREESP(sp), *sp = NULL;
+				KEY_FREESP(&sp), sp = NULL;
 				goto done;
 			}
 		}
@@ -285,16 +284,37 @@ ip6_ipsec_output(struct mbuf **m, struct
 		/*
 		 * Do delayed checksums now because we send before
 		 * this is done in the normal processing path.
-		 * For IPv6 we do delayed checksums in ip6_output.c.
 		 */
 #ifdef INET
 		if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
-			ipseclog((LOG_DEBUG,
-			    "%s: we do not support IPv4 over IPv6", __func__));
 			in_delayed_cksum(*m);
 			(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA;
 		}
 #endif
+		if ((*m)->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
+			in6_delayed_cksum(*m, (*m)->m_pkthdr.len - sizeof(struct ip6_hdr),
+							sizeof(struct ip6_hdr));
+			(*m)->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
+		}
+#ifdef SCTP
+		if ((*m)->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) {
+			sctp_delayed_cksum(*m, sizeof(struct ip6_hdr));
+			(*m)->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
+		}
+#endif
+
+		/* NB: callee frees mbuf */
+		*error = ipsec6_process_packet(*m, sp->req);
+
+		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 done;
+		}
 
 		/*
 		 * Preserve KAME behaviour: ENOENT can be returned
@@ -305,7 +325,7 @@ ip6_ipsec_output(struct mbuf **m, struct
 		 */
 		if (*error == ENOENT)
 			*error = 0;
-		goto do_ipsec;
+		goto reinjected;
 	} else {	/* sp == NULL */
 		if (*error != 0) {
 			/*
@@ -322,10 +342,16 @@ ip6_ipsec_output(struct mbuf **m, struct
 		}
 	}
 done:
+	if (sp != NULL)
+		KEY_FREESP(&sp);
 	return 0;
-do_ipsec:
+reinjected:
+	if (sp != NULL)
+		KEY_FREESP(&sp);
 	return -1;
 bad:
+	if (sp != NULL)
+		KEY_FREESP(&sp);
 	return 1;
 #endif /* IPSEC */
 	return 0;

Modified: stable/10/sys/netinet6/ip6_ipsec.h
==============================================================================
--- stable/10/sys/netinet6/ip6_ipsec.h	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netinet6/ip6_ipsec.h	Wed Nov  5 09:23:29 2014	(r274132)
@@ -36,7 +36,7 @@ int	ip6_ipsec_filtertunnel(struct mbuf *
 int	ip6_ipsec_fwd(struct mbuf *);
 int	ip6_ipsec_input(struct mbuf *, int);
 int	ip6_ipsec_output(struct mbuf **, struct inpcb *, int *, int *,
-	    struct ifnet **, struct secpolicy **sp);
+	    struct ifnet **);
 #if 0
 int	ip6_ipsec_mtu(struct mbuf *);
 #endif

Modified: stable/10/sys/netinet6/ip6_output.c
==============================================================================
--- stable/10/sys/netinet6/ip6_output.c	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netinet6/ip6_output.c	Wed Nov  5 09:23:29 2014	(r274132)
@@ -184,7 +184,7 @@ static int copypktopts(struct ip6_pktopt
 	}\
     } while (/*CONSTCOND*/ 0)
 
-static void
+void
 in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset)
 {
 	u_short csum;
@@ -248,15 +248,7 @@ ip6_output(struct mbuf *m0, struct ip6_p
 	u_int32_t zone;
 	struct route_in6 *ro_pmtu = NULL;
 	int hdrsplit = 0;
-	int needipsec = 0;
 	int sw_csum, tso;
-#ifdef IPSEC
-	struct ipsec_output_state state;
-	struct ip6_rthdr *rh = NULL;
-	int needipsectun = 0;
-	int segleft_org = 0;
-	struct secpolicy *sp = NULL;
-#endif /* IPSEC */
 	struct m_tag *fwd_tag = NULL;
 
 	ip6 = mtod(m, struct ip6_hdr *);
@@ -298,26 +290,12 @@ ip6_output(struct mbuf *m0, struct ip6_p
 	 * IPSec checking which handles several cases.
 	 * FAST IPSEC: We re-injected the packet.
 	 */
-	switch(ip6_ipsec_output(&m, inp, &flags, &error, &ifp, &sp))
+	switch(ip6_ipsec_output(&m, inp, &flags, &error, &ifp))
 	{
 	case 1:                 /* Bad packet */
 		goto freehdrs;
-	case -1:                /* Do IPSec */
-		needipsec = 1;
-		/*
-		 * Do delayed checksums now, as we may send before returning.
-		 */
-		if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA_IPV6) {
-			plen = m->m_pkthdr.len - sizeof(*ip6);
-			in6_delayed_cksum(m, plen, sizeof(struct ip6_hdr));
-			m->m_pkthdr.csum_flags &= ~CSUM_DELAY_DATA_IPV6;
-		}
-#ifdef SCTP
-		if (m->m_pkthdr.csum_flags & CSUM_SCTP_IPV6) {
-			sctp_delayed_cksum(m, sizeof(struct ip6_hdr));
-			m->m_pkthdr.csum_flags &= ~CSUM_SCTP_IPV6;
-		}
-#endif
+	case -1:                /* IPSec done */
+		goto done;
 	case 0:                 /* No IPSec */
 	default:
 		break;
@@ -337,15 +315,15 @@ ip6_output(struct mbuf *m0, struct ip6_p
 		optlen += exthdrs.ip6e_rthdr->m_len;
 	unfragpartlen = optlen + sizeof(struct ip6_hdr);
 
-	/* NOTE: we don't add AH/ESP length here. do that later. */
+	/* NOTE: we don't add AH/ESP length here (done in ip6_ipsec_output) */
 	if (exthdrs.ip6e_dest2)
 		optlen += exthdrs.ip6e_dest2->m_len;
 
 	/*
-	 * If we need IPsec, or there is at least one extension header,
+	 * If there is at least one extension header,
 	 * separate IP6 header from the payload.
 	 */
-	if ((needipsec || optlen) && !hdrsplit) {
+	if (optlen && !hdrsplit) {
 		if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
 			m = NULL;
 			goto freehdrs;
@@ -420,72 +398,6 @@ ip6_output(struct mbuf *m0, struct ip6_p
 	MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev, nexthdrp,
 		   IPPROTO_ROUTING);
 
-#ifdef IPSEC
-	if (!needipsec)
-		goto skip_ipsec2;
-
-	/*
-	 * pointers after IPsec headers are not valid any more.
-	 * other pointers need a great care too.
-	 * (IPsec routines should not mangle mbufs prior to AH/ESP)
-	 */
-	exthdrs.ip6e_dest2 = NULL;
-
-	if (exthdrs.ip6e_rthdr) {
-		rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
-		segleft_org = rh->ip6r_segleft;
-		rh->ip6r_segleft = 0;
-	}
-
-	bzero(&state, sizeof(state));
-	state.m = m;
-	error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
-				    &needipsectun);
-	m = state.m;
-	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.
-		 */
-		;
-	} else if (error) {
-		/* mbuf is already reclaimed in ipsec6_output_trans. */
-		m = NULL;
-		switch (error) {
-		case EHOSTUNREACH:
-		case ENETUNREACH:
-		case EMSGSIZE:
-		case ENOBUFS:
-		case ENOMEM:
-			break;
-		default:
-			printf("[%s:%d] (ipsec): error code %d\n",
-			    __func__, __LINE__, error);
-			/* FALLTHROUGH */
-		case ENOENT:
-			/* don't show these error codes to the user */
-			error = 0;
-			break;
-		}
-		goto bad;
-	} else if (!needipsectun) {
-		/*
-		 * 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 done;
-	}
-	if (exthdrs.ip6e_rthdr) {
-		/* ah6_output doesn't modify mbuf chain */
-		rh->ip6r_segleft = segleft_org;
-	}
-skip_ipsec2:;
-#endif /* IPSEC */
-
 	/*
 	 * If there is a routing header, discard the packet.
 	 */
@@ -551,77 +463,6 @@ again:
 			ip6->ip6_hlim = V_ip6_defmcasthlim;
 	}
 
-#ifdef IPSEC
-	/*
-	 * We may re-inject packets into the stack here.
-	 */
-	if (needipsec && needipsectun) {
-		struct ipsec_output_state state;
-
-		/*
-		 * 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
-		 */
-		bzero(&exthdrs, sizeof(exthdrs));
-		exthdrs.ip6e_ip6 = m;
-
-		bzero(&state, sizeof(state));
-		state.m = m;
-		state.ro = (struct route *)ro;
-		state.dst = (struct sockaddr *)dst;
-
-		error = ipsec6_output_tunnel(&state, sp, flags);
-
-		m = state.m;
-		ro = (struct route_in6 *)state.ro;
-		dst = (struct sockaddr_in6 *)state.dst;
-		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.
-			 */
-			;
-		} else if (error) {
-			/* mbuf is already reclaimed in ipsec6_output_tunnel. */
-			m0 = m = NULL;
-			m = NULL;
-			switch (error) {
-			case EHOSTUNREACH:
-			case ENETUNREACH:
-			case EMSGSIZE:
-			case ENOBUFS:
-			case ENOMEM:
-				break;
-			default:
-				printf("[%s:%d] (ipsec): error code %d\n",
-				    __func__, __LINE__, error);
-				/* FALLTHROUGH */
-			case ENOENT:
-				/* don't show these error codes to the user */
-				error = 0;
-				break;
-			}
-			goto bad;
-		} 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 done;
-		}
-
-		exthdrs.ip6e_ip6 = m;
-	}
-#endif /* IPSEC */
-
 	/* adjust pointer */
 	ip6 = mtod(m, struct ip6_hdr *);
 
@@ -1182,11 +1023,6 @@ done:
 		RO_RTFREE(ro);
 	if (ro_pmtu == &ip6route)
 		RO_RTFREE(ro_pmtu);
-#ifdef IPSEC
-	if (sp != NULL)
-		KEY_FREESP(&sp);
-#endif
-
 	return (error);
 
 freehdrs:

Modified: stable/10/sys/netinet6/ip6_var.h
==============================================================================
--- stable/10/sys/netinet6/ip6_var.h	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netinet6/ip6_var.h	Wed Nov  5 09:23:29 2014	(r274132)
@@ -456,6 +456,7 @@ int	in6_selectroute_fib(struct sockaddr_
 	    struct rtentry **, u_int);
 u_int32_t ip6_randomid(void);
 u_int32_t ip6_randomflowlabel(void);
+void in6_delayed_cksum(struct mbuf *m, uint32_t plen, u_short offset);
 #endif /* _KERNEL */
 
 #endif /* !_NETINET6_IP6_VAR_H_ */

Modified: stable/10/sys/netipsec/ipsec6.h
==============================================================================
--- stable/10/sys/netipsec/ipsec6.h	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netipsec/ipsec6.h	Wed Nov  5 09:23:29 2014	(r274132)
@@ -76,6 +76,7 @@ extern int ipsec6_output_trans __P((stru
 	struct mbuf *, struct secpolicy *, int, int *));
 extern int ipsec6_output_tunnel __P((struct ipsec_output_state *,
 	struct secpolicy *, int));
+extern int ipsec6_process_packet(struct mbuf *, struct ipsecrequest *);
 #endif /*_KERNEL*/
 
 #endif /*_NETIPSEC_IPSEC6_H_*/

Modified: stable/10/sys/netipsec/ipsec_input.c
==============================================================================
--- stable/10/sys/netipsec/ipsec_input.c	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netipsec/ipsec_input.c	Wed Nov  5 09:23:29 2014	(r274132)
@@ -295,7 +295,7 @@ int
 ipsec4_common_input_cb(struct mbuf *m, struct secasvar *sav,
 			int skip, int protoff, struct m_tag *mt)
 {
-	int prot, af, sproto;
+	int prot, af, sproto, isr_prot;
 	struct ip *ip;
 	struct m_tag *mtag;
 	struct tdb_ident *tdbi;
@@ -349,20 +349,34 @@ ipsec4_common_input_cb(struct mbuf *m, s
 	}
 	prot = ip->ip_p;
 
-#ifdef notyet
+#ifdef DEV_ENC
+	encif->if_ipackets++;
+	encif->if_ibytes += m->m_pkthdr.len;
+
+	/*
+	 * Pass the mbuf to enc0 for bpf and pfil. We will filter the IPIP
+	 * packet later after it has been decapsulated.
+	 */
+	ipsec_bpf(m, sav, AF_INET, ENC_IN|ENC_BEFORE);
+
+	if (prot != IPPROTO_IPIP)
+		if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_BEFORE)) != 0)
+			return (error);
+#endif /* DEV_ENC */
+
 	/* IP-in-IP encapsulation */
-	if (prot == IPPROTO_IPIP) {
-		struct ip ipn;
+	if (prot == IPPROTO_IPIP &&
+	    saidx->mode != IPSEC_MODE_TRANSPORT) {
 
 		if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
 			IPSEC_ISTAT(sproto, hdrops);
 			error = EINVAL;
 			goto bad;
 		}
-		/* ipn will now contain the inner IPv4 header */
-		m_copydata(m, ip->ip_hl << 2, sizeof(struct ip),
-		    (caddr_t) &ipn);
+		/* enc0: strip outer IPv4 header */
+		m_striphdr(m, 0, ip->ip_hl << 2);
 
+#ifdef notyet
 		/* XXX PROXY address isn't recorded in SAH */
 		/*
 		 * Check that the inner source address is the same as
@@ -388,21 +402,21 @@ ipsec4_common_input_cb(struct mbuf *m, s
 			error = EACCES;
 			goto bad;
 		}
+#endif /* notyet */
 	}
 #ifdef INET6
 	/* IPv6-in-IP encapsulation. */
-	if (prot == IPPROTO_IPV6) {
-		struct ip6_hdr ip6n;
+	if (prot == IPPROTO_IPV6 &&
+	    saidx->mode != IPSEC_MODE_TRANSPORT) {
 
 		if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
 			IPSEC_ISTAT(sproto, hdrops);
 			error = EINVAL;
 			goto bad;
 		}
-		/* ip6n will now contain the inner IPv6 header. */
-		m_copydata(m, ip->ip_hl << 2, sizeof(struct ip6_hdr),
-		    (caddr_t) &ip6n);
-
+		/* enc0: strip IPv4 header, keep IPv6 header only */
+		m_striphdr(m, 0, ip->ip_hl << 2);
+#ifdef notyet 
 		/*
 		 * Check that the inner source address is the same as
 		 * the proxy address, if available.
@@ -426,9 +440,9 @@ ipsec4_common_input_cb(struct mbuf *m, s
 			error = EACCES;
 			goto bad;
 		}
+#endif /* notyet */
 	}
 #endif /* INET6 */
-#endif /*XXX*/
 
 	/*
 	 * Record what we've done to the packet (under what SA it was
@@ -464,25 +478,50 @@ ipsec4_common_input_cb(struct mbuf *m, s
 
 	key_sa_recordxfer(sav, m);		/* record data transfer */
 
+	/*
+	 * In transport mode requeue decrypted mbuf back to IPv4 protocol
+	 * handler. This is necessary to correctly expose rcvif.
+	 */
+	if (saidx->mode == IPSEC_MODE_TRANSPORT)
+		prot = IPPROTO_IPIP;
 #ifdef DEV_ENC
-	encif->if_ipackets++;
-	encif->if_ibytes += m->m_pkthdr.len;
-
 	/*
-	 * Pass the mbuf to enc0 for bpf and pfil. We will filter the IPIP
-	 * packet later after it has been decapsulated.
+	 * Pass the mbuf to enc0 for bpf and pfil.
 	 */
-	ipsec_bpf(m, sav, AF_INET, ENC_IN|ENC_BEFORE);
-
-	if (prot != IPPROTO_IPIP)
-		if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_BEFORE)) != 0)
-			return (error);
+	if (prot == IPPROTO_IPIP)
+		ipsec_bpf(m, sav, AF_INET, ENC_IN|ENC_AFTER);
+#ifdef INET6
+	if (prot == IPPROTO_IPV6)
+		ipsec_bpf(m, sav, AF_INET6, ENC_IN|ENC_AFTER);
 #endif
 
+	if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_AFTER)) != 0)
+		return (error);
+#endif /* DEV_ENC */
+
 	/*
 	 * Re-dispatch via software interrupt.
 	 */
-	if ((error = netisr_queue_src(NETISR_IP, (uintptr_t)sav->spi, m))) {
+
+	switch (prot) {
+	case IPPROTO_IPIP:
+		isr_prot = NETISR_IP;
+		break;
+#ifdef INET6
+	case IPPROTO_IPV6:
+		isr_prot = NETISR_IPV6;
+		break;
+#endif
+	default:
+		DPRINTF(("%s: cannot handle inner ip proto %d\n",
+			    __func__, prot));
+		IPSEC_ISTAT(sproto, nopf);
+		error = EPFNOSUPPORT;
+		goto bad;
+	}
+
+	error = netisr_queue_src(isr_prot, (uintptr_t)sav->spi, m);
+	if (error) {
 		IPSEC_ISTAT(sproto, qfull);
 		DPRINTF(("%s: queue full; proto %u packet dropped\n",
 			__func__, sproto));
@@ -605,20 +644,34 @@ ipsec6_common_input_cb(struct mbuf *m, s
 	prot = 0;
 	m_copydata(m, protoff, 1, (unsigned char *) &prot);
 
-#ifdef notyet
+#ifdef DEV_ENC
+	encif->if_ipackets++;
+	encif->if_ibytes += m->m_pkthdr.len;
+
+	/*
+	 * Pass the mbuf to enc0 for bpf and pfil. We will filter the IPIP
+	 * packet later after it has been decapsulated.
+	 */
+	ipsec_bpf(m, sav, AF_INET6, ENC_IN|ENC_BEFORE);
+
+	/* XXX-BZ does not make sense. */
+	if (prot != IPPROTO_IPIP)
+		if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_BEFORE)) != 0)
+			return (error);
+#endif /* DEV_ENC */
+
 #ifdef INET
 	/* IP-in-IP encapsulation */
 	if (prot == IPPROTO_IPIP) {
-		struct ip ipn;
-
 		if (m->m_pkthdr.len - skip < sizeof(struct ip)) {
 			IPSEC_ISTAT(sproto, hdrops);
 			error = EINVAL;
 			goto bad;
 		}
 		/* ipn will now contain the inner IPv4 header */
-		m_copydata(m, skip, sizeof(struct ip), (caddr_t) &ipn);
-
+	 	m_striphdr(m, 0, skip);
+		skip = 0;
+#ifdef notyet
 		/*
 		 * Check that the inner source address is the same as
 		 * the proxy address, if available.
@@ -641,22 +694,20 @@ ipsec6_common_input_cb(struct mbuf *m, s
 			error = EACCES;
 			goto bad;
 		}
+#endif /* notyet */
 	}
 #endif /* INET */
-
 	/* IPv6-in-IP encapsulation */
 	if (prot == IPPROTO_IPV6) {
-		struct ip6_hdr ip6n;
-
 		if (m->m_pkthdr.len - skip < sizeof(struct ip6_hdr)) {
 			IPSEC_ISTAT(sproto, hdrops);
 			error = EINVAL;
 			goto bad;
 		}
 		/* ip6n will now contain the inner IPv6 header. */
-		m_copydata(m, skip, sizeof(struct ip6_hdr),
-		    (caddr_t) &ip6n);
-
+		m_striphdr(m, 0, skip);
+		skip = 0;
+#ifdef notyet
 		/*
 		 * Check that the inner source address is the same as
 		 * the proxy address, if available.
@@ -680,8 +731,8 @@ ipsec6_common_input_cb(struct mbuf *m, s
 			error = EACCES;
 			goto bad;
 		}
+#endif /* notyet */
 	}
-#endif /*XXX*/
 
 	/*
 	 * Record what we've done to the packet (under what SA it was
@@ -719,23 +770,22 @@ ipsec6_common_input_cb(struct mbuf *m, s
 	key_sa_recordxfer(sav, m);
 
 #ifdef DEV_ENC
-	encif->if_ipackets++;
-	encif->if_ibytes += m->m_pkthdr.len;
-
 	/*
-	 * Pass the mbuf to enc0 for bpf and pfil. We will filter the IPIP
-	 * packet later after it has been decapsulated.
+	 * Pass the mbuf to enc0 for bpf and pfil.
 	 */
-	ipsec_bpf(m, sav, AF_INET6, ENC_IN|ENC_BEFORE);
-
-	/* XXX-BZ does not make sense. */
-	if (prot != IPPROTO_IPIP)
-		if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_BEFORE)) != 0)
-			return (error);
+#ifdef INET
+	if (prot == IPPROTO_IPIP)
+		ipsec_bpf(m, sav, AF_INET, ENC_IN|ENC_AFTER);
 #endif
+	if (prot == IPPROTO_IPV6)
+		ipsec_bpf(m, sav, AF_INET6, ENC_IN|ENC_AFTER);
 
+	if ((error = ipsec_filter(&m, PFIL_IN, ENC_IN|ENC_AFTER)) != 0)
+		return (error);
+#endif /* DEV_ENC */
 	/* Retrieve new protocol */
-	m_copydata(m, protoff, sizeof(u_int8_t), (caddr_t) &nxt8);
+	/* We have stripped the IP6 header from the mbuf, we have to use the backuped proto value instead */
+	nxt8 = prot;
 
 	/*
 	 * See the end of ip6_input for this logic.

Modified: stable/10/sys/netipsec/ipsec_output.c
==============================================================================
--- stable/10/sys/netipsec/ipsec_output.c	Wed Nov  5 08:10:58 2014	(r274131)
+++ stable/10/sys/netipsec/ipsec_output.c	Wed Nov  5 09:23:29 2014	(r274132)
@@ -176,8 +176,7 @@ ipsec_process_done(struct mbuf *m, struc
 #ifdef INET6
 		case AF_INET6:
 			/* XXX */
-			ipsec6_output_trans()
-			ipsec6_output_tunnel()
+			return ipsec6_process_packet(m, isr->next);
 			/* NOTREACHED */
 #endif /* INET6 */
 #endif
@@ -498,9 +497,11 @@ ipsec4_process_packet(
 				goto bad;
 			}
 			ip = mtod(m, struct ip *);
-			ip->ip_len = htons(m->m_pkthdr.len);
-			ip->ip_sum = 0;
-			ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
+			if (ip->ip_v == IPVERSION) {
+				ip->ip_len = htons(m->m_pkthdr.len);
+				ip->ip_sum = 0;
+				ip->ip_sum = in_cksum(m, ip->ip_hl << 2);
+			}
 
 			/* Encapsulate the packet */
 			error = ipip_output(m, isr, &mp, 0, 0);
@@ -542,7 +543,7 @@ ipsec4_process_packet(
 
 #ifdef DEV_ENC
 	/* pass the mbuf to enc0 for bpf processing */
-	ipsec_bpf(m, sav, AF_INET, ENC_OUT|ENC_AFTER);
+	ipsec_bpf(m, sav, sav->sah->saidx.dst.sa.sa_family, ENC_OUT|ENC_AFTER);
 	/* pass the mbuf to enc0 for packet filtering */
 	if ((error = ipsec_filter(&m, PFIL_OUT, ENC_OUT|ENC_AFTER)) != 0)
 		goto bad;
@@ -559,9 +560,26 @@ ipsec4_process_packet(
 	 *     for reclaiming their resources.
 	 */
 	if (sav->tdb_xform->xf_type != XF_IP4) {
-		ip = mtod(m, struct ip *);
-		i = ip->ip_hl << 2;
-		off = offsetof(struct ip, ip_p);
+		union sockaddr_union *dst = &sav->sah->saidx.dst;
+		switch(dst->sa.sa_family) {
+		case AF_INET:
+			ip = mtod(m, struct ip *);
+			i = ip->ip_hl << 2;
+			off = offsetof(struct ip, ip_p);
+			break;
+#ifdef INET6
+		case AF_INET6:
+			i = sizeof(struct ip6_hdr);
+			off = offsetof(struct ip6_hdr, ip6_nxt);
+			break;
+#endif /* INET6 */
+		default:
+		DPRINTF(("%s: unsupported protocol family %u\n",
+				 __func__, dst->sa.sa_family));
+			error = EPFNOSUPPORT;
+			IPSECSTAT_INC(ips_out_inval);
+			goto bad;
+		}
 		error = (*sav->tdb_xform->xf_output)(m, isr, NULL, i, off);
 	} else {
 		error = ipsec_process_done(m, isr);
@@ -577,224 +595,50 @@ bad:
 }
 #endif
 
-#ifdef INET6
-/*
- * Chop IP6 header from the payload.
- */
-static struct mbuf *
-ipsec6_splithdr(struct mbuf *m)
-{
-	struct mbuf *mh;
-	struct ip6_hdr *ip6;
-	int hlen;
-
-	IPSEC_ASSERT(m->m_len >= sizeof (struct ip6_hdr),
-		("first mbuf too short, len %u", m->m_len));
-	ip6 = mtod(m, struct ip6_hdr *);
-	hlen = sizeof(struct ip6_hdr);
-	if (m->m_len > hlen) {
-		MGETHDR(mh, M_NOWAIT, MT_DATA);
-		if (!mh) {
-			m_freem(m);
-			return NULL;
-		}
-		M_MOVE_PKTHDR(mh, m);
-		MH_ALIGN(mh, hlen);
-		m->m_len -= hlen;
-		m->m_data += hlen;
-		mh->m_next = m;
-		m = mh;
-		m->m_len = hlen;
-		bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen);
-	} else if (m->m_len < hlen) {
-		m = m_pullup(m, hlen);
-		if (!m)
-			return NULL;
-	}
-	return m;
-}
-
-/*
- * IPsec output logic for IPv6, transport mode.
- */
-int
-ipsec6_output_trans(
-	struct ipsec_output_state *state,
-	u_char *nexthdrp,
-	struct mbuf *mprev,
-	struct secpolicy *sp,
-	int flags,
-	int *tun)
-{
-	struct ipsecrequest *isr;
-	struct secasindex saidx;
-	int error = 0;
-	struct mbuf *m;
-
-	IPSEC_ASSERT(state != NULL, ("null state"));
-	IPSEC_ASSERT(state->m != NULL, ("null m"));
-	IPSEC_ASSERT(nexthdrp != NULL, ("null nexthdrp"));
-	IPSEC_ASSERT(mprev != NULL, ("null mprev"));
-	IPSEC_ASSERT(sp != NULL, ("null sp"));
-	IPSEC_ASSERT(tun != NULL, ("null tun"));
-
-	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
-		printf("%s: applied SP\n", __func__);
-		kdebug_secpolicy(sp));
-
-	isr = sp->req;
-	if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
-		/* the rest will be handled by ipsec6_output_tunnel() */
-		*tun = 1;		/* need tunnel-mode processing */
-		return 0;
-	}
-
-	*tun = 0;
-	m = state->m;
-
-	IPSECREQUEST_LOCK(isr);		/* insure SA contents don't change */
-	isr = ipsec_nextisr(m, isr, AF_INET6, &saidx, &error);
-	if (isr == NULL) {
-		if (error != 0) {
-#ifdef notdef
-			/* XXX should notification be done for all errors ? */
-			/*
-			 * Notify the fact that the packet is discarded
-			 * to ourselves. I believe this is better than
-			 * just silently discarding. (jinmei@kame.net)
-			 * XXX: should we restrict the error to TCP packets?
-			 * XXX: should we directly notify sockets via
-			 *      pfctlinputs?
-			 */
-			icmp6_error(m, ICMP6_DST_UNREACH,
-				    ICMP6_DST_UNREACH_ADMIN, 0);
-			m = NULL;	/* NB: icmp6_error frees mbuf */
-#endif
-			goto bad;
-		}
-		return EJUSTRETURN;
-	}
-
-	error = (*isr->sav->tdb_xform->xf_output)(m, isr, NULL,
-						  sizeof (struct ip6_hdr),
-						  offsetof(struct ip6_hdr, 
-							   ip6_nxt));
-	IPSECREQUEST_UNLOCK(isr);
-	return error;
-bad:
-	if (isr)
-		IPSECREQUEST_UNLOCK(isr);
-	if (m)
-		m_freem(m);
-	state->m = NULL;
-	return error;
-}
 
+#ifdef INET6
 static int
-ipsec6_encapsulate(struct mbuf *m, struct secasvar *sav)
+in6_sa_equal_addrwithscope(const struct sockaddr_in6 *sa, const struct in6_addr *ia)
 {
-	struct ip6_hdr *oip6;
-	struct ip6_hdr *ip6;
-	size_t plen;
+	struct in6_addr ia2;
 
-	/* can't tunnel between different AFs */
-	if (sav->sah->saidx.src.sa.sa_family != AF_INET6 ||
-	    sav->sah->saidx.dst.sa.sa_family != AF_INET6) {
-		m_freem(m);
-		return EINVAL;
-	}
-	IPSEC_ASSERT(m->m_len == sizeof (struct ip6_hdr),
-		("mbuf wrong size; len %u", m->m_len));
-
-
-	/*
-	 * grow the mbuf to accomodate the new IPv6 header.
-	 */
-	plen = m->m_pkthdr.len;
-	if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
-		struct mbuf *n;
-		MGET(n, M_NOWAIT, MT_DATA);
-		if (!n) {
-			m_freem(m);
-			return ENOBUFS;
-		}
-		n->m_len = sizeof(struct ip6_hdr);
-		n->m_next = m->m_next;
-		m->m_next = n;
-		m->m_pkthdr.len += sizeof(struct ip6_hdr);
-		oip6 = mtod(n, struct ip6_hdr *);
-	} else {
-		m->m_next->m_len += sizeof(struct ip6_hdr);
-		m->m_next->m_data -= sizeof(struct ip6_hdr);
-		m->m_pkthdr.len += sizeof(struct ip6_hdr);
-		oip6 = mtod(m->m_next, struct ip6_hdr *);
-	}
-	ip6 = mtod(m, struct ip6_hdr *);
-	bcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr));
-
-	/* Fake link-local scope-class addresses */
-	if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
-		oip6->ip6_src.s6_addr16[1] = 0;
-	if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
-		oip6->ip6_dst.s6_addr16[1] = 0;
-
-	/* construct new IPv6 header. see RFC 2401 5.1.2.2 */
-	/* ECN consideration. */
-	ip6_ecn_ingress(V_ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow);
-	if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
-		ip6->ip6_plen = htons(plen);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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