summaryrefslogtreecommitdiff
path: root/sys/netinet6/ip6_output.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet6/ip6_output.c')
-rw-r--r--sys/netinet6/ip6_output.c150
1 files changed, 99 insertions, 51 deletions
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 7a42d210450..971d6306370 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ip6_output.c,v 1.10 2000/06/18 17:31:14 itojun Exp $ */
+/* $OpenBSD: ip6_output.c,v 1.11 2000/06/19 03:43:17 itojun Exp $ */
/* $KAME: ip6_output.c,v 1.112 2000/06/18 01:50:39 itojun Exp $ */
/*
@@ -89,7 +89,19 @@
#include <netinet6/ip6_var.h>
#include <netinet6/nd6.h>
-#undef IPSEC
+#ifdef IPSEC
+#include <netinet/ip_ah.h>
+#include <netinet/ip_esp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <net/pfkeyv2.h>
+
+extern u_int8_t get_sa_require __P((struct inpcb *));
+
+extern int ipsec_auth_default_level;
+extern int ipsec_esp_trans_default_level;
+extern int ipsec_esp_network_default_level;
+#endif /* IPSEC */
#include "loop.h"
@@ -149,9 +161,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
struct in6_addr finaldst;
struct route_in6 *ro_pmtu = NULL;
int hdrsplit = 0;
- int needipsec = 0;
-#ifdef IPSEC
u_int8_t sproto = 0;
+#ifdef IPSEC
union sockaddr_union sdst;
u_int32_t sspi;
u_int8_t sa_require = 0, sa_have = 0;
@@ -190,67 +201,104 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
}
#ifdef IPSEC
+ /* Disallow nested IPsec for now */
+ if (flags & IPV6_ENCAPSULATED)
+ goto done_spd;
+
+ /*
+ * splnet is chosen over spltdb because we are not allowed to
+ * lower the level, and udp6_output calls us in splnet(). XXX check
+ */
s = splnet();
/*
- * If the higher-level protocol has cached the SA to use, we
- * can avoid the routing lookup if the source address is zero.
+ * Check if there was an outgoing SA bound to the flow
+ * from a transport protocol.
*/
- if (inp != NULL && inp->inp_tdb != NULL &&
- ip->ip_src.s_addr == INADDR_ANY &&
- tdb->tdb_dst.sa.sa_family == AF_INET &&
- tdb->tdb_dst.sin.sin_addr.s_addr != AF_INET) {
- ip->ip_src.s_addr = tdb->tdb_dst.sin.sin_addr.s_addr;
- splx(s);
- goto skip_routing;
+ ip6 = mtod(m, struct ip6_hdr *);
+ if (inp && inp->inp_tdb &&
+ inp->inp_tdb->tdb_dst.sa.sa_family == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(&inp->inp_tdb->tdb_dst.sin6.sin6_addr,
+ &ip6->ip6_dst)) {
+ tdb = inp->inp_tdb;
+ } else {
+ tdb = ipsp_spd_lookup(m, AF_INET6, sizeof(struct ip6_hdr),
+ &error);
}
- splx(s);
-#endif /* IPSEC */
+ if (tdb == NULL) {
+ splx(s);
-#ifdef IPSEC
- /* get a security policy for this packet */
- if (so == NULL)
- sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error);
- else
- sp = ipsec6_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error);
+ if (error == 0) {
+ /*
+ * No IPsec processing required, we'll just send the
+ * packet out.
+ */
+ sproto = 0;
- if (sp == NULL) {
- ipsec6stat.out_inval++;
- goto bad;
- }
+ /* Fall through to routing/multicast handling */
+ } else {
+ /*
+ * -EINVAL is used to indicate that the packet should
+ * be silently dropped, typically because we've asked
+ * key management for an SA.
+ */
+ if (error == -EINVAL) /* Should silently drop packet */
+ error = 0;
- error = 0;
+ goto freehdrs;
+ }
+ } else {
+ /* We need to do IPsec */
+ bcopy(&tdb->tdb_dst, &sdst, sizeof(sdst));
+ sspi = tdb->tdb_spi;
+ sproto = tdb->tdb_sproto;
- /* check policy */
- switch (sp->policy) {
- case IPSEC_POLICY_DISCARD:
/*
- * This packet is just discarded.
+ * If the socket has set the bypass flags and SA destination
+ * matches the IP destination, skip IPsec. This allows
+ * IKE packets to travel through IPsec tunnels.
*/
- ipsec6stat.out_polvio++;
- goto bad;
+ if (inp != NULL &&
+ inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_BYPASS &&
+ inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_BYPASS &&
+ inp->inp_seclevel[SL_ESP_NETWORK] == IPSEC_LEVEL_BYPASS &&
+ sdst.sa.sa_family == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(&sdst.sin6.sin6_addr, &ip6->ip6_dst)) {
+ splx(s);
+ sproto = 0; /* mark as no-IPsec-needed */
+ goto done_spd;
+ }
- case IPSEC_POLICY_BYPASS:
- case IPSEC_POLICY_NONE:
- /* no need to do IPsec. */
- needipsec = 0;
- break;
-
- case IPSEC_POLICY_IPSEC:
- if (sp->req == NULL) {
- /* XXX should be panic ? */
- printf("ip6_output: No IPsec request specified.\n");
- error = EINVAL;
- goto bad;
+ /* What are the socket (or default) security requirements ? */
+ if (inp == NULL)
+ sa_require = get_sa_require(NULL);
+ else
+ sa_require = inp->inp_secrequire;
+
+ /*
+ * Now we check if this tdb has all the transforms which
+ * are required by the socket or our default policy.
+ */
+ SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb);
+ splx(s);
+ if (sa_require & ~sa_have) {
+ error = EHOSTUNREACH;
+ goto freehdrs;
}
- needipsec = 1;
- break;
- case IPSEC_POLICY_ENTRUST:
- default:
- printf("ip6_output: Invalid policy found. %d\n", sp->policy);
+#if 1
+ /* if we have any extension header, we cannot perform IPsec */
+ if (exthdrs.ip6e_hbh || exthdrs.ip6e_dest1 ||
+ exthdrs.ip6e_rthdr || exthdrs.ip6e_dest2) {
+ error = EHOSTUNREACH;
+ goto freehdrs;
+ }
+#endif
}
+
+ /* Fall through to the routing/multicast handling code */
+ done_spd:
#endif /* IPSEC */
/*
@@ -269,7 +317,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifpp)
* If we need IPsec, or there is at least one extension header,
* separate IP6 header from the payload.
*/
- if ((needipsec || optlen) && !hdrsplit) {
+ if ((sproto || optlen) && !hdrsplit) {
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
m = NULL;
goto freehdrs;
@@ -1636,7 +1684,7 @@ ip6_ctloutput(op, so, level, optname, mp)
case IP_ESP_TRANS_LEVEL:
optval =
- np->inp_seclevel[SL_ESP_TRANS];
+ inp->inp_seclevel[SL_ESP_TRANS];
break;
case IP_ESP_NETWORK_LEVEL: