diff options
Diffstat (limited to 'sys/netinet/ip_output.c')
-rw-r--r-- | sys/netinet/ip_output.c | 148 |
1 files changed, 146 insertions, 2 deletions
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index e78ef26..0a9d66a 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -79,6 +79,12 @@ static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options"); #endif #endif /*IPSEC*/ +#ifdef FAST_IPSEC +#include <netipsec/ipsec.h> +#include <netipsec/xform.h> +#include <netipsec/key.h> +#endif /*FAST_IPSEC*/ + #include <netinet/ip_fw.h> #include <netinet/ip_dummynet.h> @@ -134,6 +140,13 @@ ip_output(m0, opt, ro, flags, imo, inp) struct secpolicy *sp = NULL; struct socket *so = inp ? inp->inp_socket : NULL; #endif +#ifdef FAST_IPSEC + struct route iproute; + struct m_tag *mtag; + struct secpolicy *sp = NULL; + struct tdb_ident *tdbi; + int s; +#endif /* FAST_IPSEC */ struct ip_fw_args args; int src_was_INADDR_ANY = 0; /* as the name says... */ #ifdef PFIL_HOOKS @@ -182,6 +195,10 @@ ip_output(m0, opt, ro, flags, imo, inp) m = m0; KASSERT(!m || (m->m_flags & M_PKTHDR) != 0, ("ip_output: no HDR")); +#ifndef FAST_IPSEC + KASSERT(ro != NULL, ("ip_output: no route, proto %d", + mtod(m, struct ip *)->ip_p)); +#endif if (args.rule != NULL) { /* dummynet already saw us */ ip = mtod(m, struct ip *); @@ -216,6 +233,12 @@ ip_output(m0, opt, ro, flags, imo, inp) hlen = IP_VHL_HL(ip->ip_vhl) << 2; } +#ifdef FAST_IPSEC + if (ro == NULL) { + ro = &iproute; + bzero(ro, sizeof (*ro)); + } +#endif /* FAST_IPSEC */ dst = (struct sockaddr_in *)&ro->ro_dst; /* * If there is a cached route, @@ -569,6 +592,119 @@ sendit: ip->ip_off = ntohs(ip->ip_off); skip_ipsec: #endif /*IPSEC*/ +#ifdef FAST_IPSEC + /* + * 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); + s = splnet(); + if (mtag != NULL) { + tdbi = (struct tdb_ident *)(mtag + 1); + 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, + &error, inp); + } + /* + * 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) { + /* 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->spidx.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), sp = NULL; + splx(s); + goto spd_done; + } + } + + /* + * 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; + } + + ip->ip_len = htons(ip->ip_len); + ip->ip_off = htons(ip->ip_off); + + /* NB: callee frees mbuf */ + error = ipsec4_process_packet(m, sp->req, flags, 0); + splx(s); + goto done; + } else { + splx(s); + + 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 bad; + } else { + /* No IPsec processing for this packet. */ + } +#ifdef notyet + /* + * If deferred crypto processing is needed, check that + * the interface supports it. + */ + mtag = m_tag_find(m, PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED, NULL); + if (mtag != NULL && (ifp->if_capenable & IFCAP_IPSEC) == 0) { + /* notify IPsec to do its own crypto */ + ipsp_skipcrypto_unmark((struct tdb_ident *)(mtag + 1)); + error = EHOSTUNREACH; + goto bad; + } +#endif + } +spd_done: +#endif /* FAST_IPSEC */ /* * IpHack's section. @@ -1078,6 +1214,14 @@ done: key_freesp(sp); } #endif /* IPSEC */ +#ifdef FAST_IPSEC + if (ro == &iproute && ro->ro_rt) { + RTFREE(ro->ro_rt); + ro->ro_rt = NULL; + } + if (sp != NULL) + KEY_FREESP(&sp); +#endif /* FAST_IPSEC */ return (error); bad: m_freem(m); @@ -1340,7 +1484,7 @@ ip_ctloutput(so, sopt) } break; -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) case IP_IPSEC_POLICY: { caddr_t req; @@ -1444,7 +1588,7 @@ ip_ctloutput(so, sopt) error = ip_getmoptions(sopt, inp->inp_moptions); break; -#ifdef IPSEC +#if defined(IPSEC) || defined(FAST_IPSEC) case IP_IPSEC_POLICY: { struct mbuf *m = NULL; |