diff options
author | ae <ae@FreeBSD.org> | 2014-11-05 09:23:29 +0000 |
---|---|---|
committer | ae <ae@FreeBSD.org> | 2014-11-05 09:23:29 +0000 |
commit | 6933957ccb7c9d3685e51d3aae7e3a8ab8d91c8f (patch) | |
tree | 4c59a543f83db7eb244721ea9d542127873cf105 /sys/netinet6 | |
parent | f5ef39c5536cbd1ec407ea4507e695db17fe32c1 (diff) | |
download | FreeBSD-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.c | 46 | ||||
-rw-r--r-- | sys/netinet6/ip6_ipsec.c | 62 | ||||
-rw-r--r-- | sys/netinet6/ip6_ipsec.h | 2 | ||||
-rw-r--r-- | sys/netinet6/ip6_output.c | 178 | ||||
-rw-r--r-- | sys/netinet6/ip6_var.h | 1 |
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_ */ |