diff options
author | Angelos D. Keromytis <angelos@cvs.openbsd.org> | 1999-12-25 04:48:17 +0000 |
---|---|---|
committer | Angelos D. Keromytis <angelos@cvs.openbsd.org> | 1999-12-25 04:48:17 +0000 |
commit | 19d8f442cc18028e750d8eccc0e9b51a134b4193 (patch) | |
tree | 4131d7e40c265102a2f6f5d29e45f75bdfbf4dc1 /sys/netinet/ip_ipsp.c | |
parent | 4a6ef205c38d6e48720b261c61dd09049cba0f65 (diff) |
Move the IPsec packet-processing loop to a separate routine, so we can
reuse it in ip6_output and the bridge. The policy-lookup code will
probably follow suit in a separate routine sometime soon.
Diffstat (limited to 'sys/netinet/ip_ipsp.c')
-rw-r--r-- | sys/netinet/ip_ipsp.c | 247 |
1 files changed, 240 insertions, 7 deletions
diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c index 9ba1f87556e..8f6b4a6bcc7 100644 --- a/sys/netinet/ip_ipsp.c +++ b/sys/netinet/ip_ipsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.c,v 1.59 1999/12/08 06:06:43 itojun Exp $ */ +/* $OpenBSD: ip_ipsp.c,v 1.60 1999/12/25 04:48:16 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -45,27 +45,28 @@ #include <sys/systm.h> #include <sys/malloc.h> #include <sys/mbuf.h> -#include <sys/domain.h> #include <sys/protosw.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/errno.h> -#include <sys/time.h> #include <sys/kernel.h> #include <sys/proc.h> #include <net/if.h> #include <net/route.h> +#ifdef INET #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/in_pcb.h> -#include <netinet/in_var.h> -#include <netinet/ip_var.h> -#include <netinet/ip_icmp.h> +#endif /* INET */ + +#ifdef INET6 +#include <netinet6/in6.h> +#include <netinet6/ip6.h> +#endif /* INET6 */ -#include <net/raw_cb.h> #include <net/pfkeyv2.h> #include <netinet/ip_ipsp.h> @@ -85,6 +86,10 @@ void tdb_hashstats(void); #define DPRINTF(x) #endif +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + #ifdef __GNUC__ #define INLINE static __inline #endif @@ -1495,3 +1500,231 @@ ipsp_address(union sockaddr_union sa) return "(unknown address family)"; } } + +/* + * Loop over a tdb chain, taking into consideration protocol tunneling. The + * fourth argument is set if the first encapsulation header is already in + * place. + */ +int +ipsp_process_packet(struct mbuf *m, struct mbuf **mp, struct tdb *tdb, int *af, + int tunalready) +{ + int i, error, off; + struct tdb *t; + +#ifdef INET + struct ip *ip; +#endif /* INET */ + +#ifdef INET6 + struct ip6_hdr *ip6; +#endif /* INET6 */ + + for (t = tdb; t != NULL; t = t->tdb_onext) + if ((t->tdb_sproto == IPPROTO_ESP && !esp_enable) || + (t->tdb_sproto == IPPROTO_AH && !ah_enable)) + { + DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy\n")); + m_freem(m); + return EHOSTUNREACH; + } + + while (tdb && tdb->tdb_xform) + { + /* Check if the SPI is invalid */ + if (tdb->tdb_flags & TDBF_INVALID) + { + DPRINTF(("ipsp_process_packet(): attempt to use invalid SA %s/%08x/%u\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto)); + m_freem(m); + return ENXIO; + } + +#ifndef INET6 + /* Sanity check */ + if (tdb->tdb_dst.sa.sa_family != AF_INET) + { + DPRINTF(("ipsp_process_packet(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family)); + m_freem(m); + return ENXIO; + } +#endif /* INET6 */ + +#ifndef INET + /* Sanity check */ + if (tdb->tdb_dst.sa.sa_family != AF_INET6) + { + DPRINTF(("ipsp_process_packet(): attempt to use SA %s/%08x/%u for protocol family %d\n", ipsp_address(tdb->tdb_dst), ntohl(tdb->tdb_spi), tdb->tdb_sproto, tdb->tdb_dst.sa.sa_family)); + m_freem(m); + return ENXIO; + } +#endif /* INET */ + + /* Register first use, setup expiration timer */ + if (tdb->tdb_first_use == 0) + { + tdb->tdb_first_use = time.tv_sec; + tdb_expiration(tdb, TDBEXP_TIMEOUT); + } + + /* Check for tunneling if we don't have the first header in place */ + if (tunalready == 0) + { + if ((*af) == tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + if ((*af) == AF_INET) + i = sizeof(struct ip); +#endif /* INET */ + +#ifdef INET6 + if ((*af) == AF_INET6) + i = sizeof(struct ip6_hdr); +#endif /* INET6 */ + + if (m->m_len < i) + { + if ((m = m_pullup(m, i)) == 0) + return ENOBUFS; + } + +#ifdef INET + ip = mtod(m, struct ip *); +#endif /* INET */ + +#ifdef INET6 + ip6 = mtod(m, struct ip6_hdr *); +#endif /* INET6 */ + } + + if ((tdb->tdb_dst.sa.sa_family != (*af)) || + ((tdb->tdb_flags & TDBF_TUNNELING) && + (tdb->tdb_xform->xf_type != XF_IP4)) || +#ifdef INET + ((tdb->tdb_dst.sa.sa_family == AF_INET) && + (tdb->tdb_dst.sin.sin_addr.s_addr != INADDR_ANY) && + (tdb->tdb_dst.sin.sin_addr.s_addr != ip->ip_dst.s_addr)) || +#endif /* INET */ +#ifdef INET6 + ((tdb->tdb_dst.sa.sa_family == AF_INET6) && + (!IN6_IS_ADDR_UNSPECIFIED(&tdb->tdb_dst.sin6.sin6_addr)) && + (!IN6_ARE_ADDR_EQUAL(&tdb->tdb_dst.sin6.sin6_addr, + &ip6->ip6_dst))) || +#endif /* INET6 */ + 0) + { +#ifdef INET + /* Fix IPv4 header checksum and length */ + if ((*af) == AF_INET) + { + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + i = ip->ip_hl << 2; + off = offsetof(struct ip, ip_p); + } +#endif /* INET */ + +#ifdef INET6 + /* Fix IPv6 header payload length */ + if ((*af) == AF_INET6) + { + ip6->ip6_plen = htons(m->m_pkthdr.len); + i = sizeof(struct ip6_hdr); + off = offsetof(struct ip6_hdr, ip6_nxt); + } +#endif /* INET6 */ + + /* Encapsulate */ + error = ipe4_output(m, tdb, mp, i, off); + if ((*mp) == NULL) + error = EFAULT; + if (error) + { + if (*mp) + m_freem(*mp); + return error; + } + + *af = tdb->tdb_dst.sa.sa_family; + m = *mp; + *mp = NULL; + } + } + else + { + tunalready = 0; + + if (tdb->tdb_xform->xf_type == XF_IP4) + continue; + } + +#ifdef INET + if (tdb->tdb_dst.sa.sa_family == AF_INET) + { + ip = mtod(m, struct ip *); + i = ip->ip_hl << 2; + off = offsetof(struct ip, ip_p); + + if (tdb->tdb_xform->xf_type == XF_IP4) + { + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_sum = in_cksum(m, i); + } + } +#endif /* INET */ + +#ifdef INET6 + if (tdb->tdb_dst.sa.sa_family == AF_INET6) + { + ip6 = mtod(m, struct ip6_hdr *); + i = sizeof(struct ip6_hdr); + off = offsetof(struct ip6_hdr, ip6_nxt); + ip6->ip6_plen = htons(m->m_pkthdr.len); + } +#endif /* INET6 */ + + error = (*(tdb->tdb_xform->xf_output))(m, tdb, mp, i, off); + if ((*mp) == NULL) + error = EFAULT; + if (error) + { + if (*mp) + m_freem(*mp); + return error; + } + + m = *mp; + *mp = NULL; + tdb = tdb->tdb_onext; + +#ifdef INET + /* Fix the header length, for AH processing */ + if ((*af) == AF_INET) + { + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + } +#endif /* INET */ + +#ifdef INET6 + /* Fix the header length, for AH processing */ + if ((*af) == AF_INET6) + { + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len); + } +#endif /* INET6 */ + } + +#ifdef INET + /* Fix checksum */ + if ((*af) == AF_INET) + { + ip = mtod(m, struct ip *); + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + } +#endif /* INET */ + + *mp = m; + return 0; +} |