summaryrefslogtreecommitdiffstats
path: root/sys/netinet/ip_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/ip_output.c')
-rw-r--r--sys/netinet/ip_output.c148
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;
OpenPOWER on IntegriCloud