summaryrefslogtreecommitdiffstats
path: root/sys/netinet6
diff options
context:
space:
mode:
authorae <ae@FreeBSD.org>2014-11-05 09:23:29 +0000
committerae <ae@FreeBSD.org>2014-11-05 09:23:29 +0000
commit6933957ccb7c9d3685e51d3aae7e3a8ab8d91c8f (patch)
tree4c59a543f83db7eb244721ea9d542127873cf105 /sys/netinet6
parentf5ef39c5536cbd1ec407ea4507e695db17fe32c1 (diff)
downloadFreeBSD-src-6933957ccb7c9d3685e51d3aae7e3a8ab8d91c8f.zip
FreeBSD-src-6933957ccb7c9d3685e51d3aae7e3a8ab8d91c8f.tar.gz
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
Diffstat (limited to 'sys/netinet6')
-rw-r--r--sys/netinet6/ip6_forward.c46
-rw-r--r--sys/netinet6/ip6_ipsec.c62
-rw-r--r--sys/netinet6/ip6_ipsec.h2
-rw-r--r--sys/netinet6/ip6_output.c178
-rw-r--r--sys/netinet6/ip6_var.h1
5 files changed, 69 insertions, 220 deletions
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index 95aba93..6e2bec9 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -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
diff --git a/sys/netinet6/ip6_ipsec.c b/sys/netinet6/ip6_ipsec.c
index 436af00..6109d86 100644
--- a/sys/netinet6/ip6_ipsec.c
+++ b/sys/netinet6/ip6_ipsec.c
@@ -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 inpcb *inp, int *flags, int *error,
* 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 inpcb *inp, int *flags, int *error,
* 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 inpcb *inp, int *flags, int *error,
* 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 inpcb *inp, int *flags, int *error,
/*
* 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 inpcb *inp, int *flags, int *error,
*/
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 inpcb *inp, int *flags, int *error,
}
}
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;
diff --git a/sys/netinet6/ip6_ipsec.h b/sys/netinet6/ip6_ipsec.h
index 86d1b00..a65b19a 100644
--- a/sys/netinet6/ip6_ipsec.h
+++ b/sys/netinet6/ip6_ipsec.h
@@ -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
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index b1d27d4..fe720ad 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -184,7 +184,7 @@ static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int);
}\
} 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_pktopts *opt,
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_pktopts *opt,
* 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_pktopts *opt,
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_pktopts *opt,
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:
diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h
index 70e487e..a9729f9 100644
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -456,6 +456,7 @@ int in6_selectroute_fib(struct sockaddr_in6 *, struct ip6_pktopts *,
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_ */
OpenPOWER on IntegriCloud