summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorAngelos D. Keromytis <angelos@cvs.openbsd.org>2002-06-15 02:26:40 +0000
committerAngelos D. Keromytis <angelos@cvs.openbsd.org>2002-06-15 02:26:40 +0000
commite8453a1205c8727a1e4487639c0d65aafd97641e (patch)
tree671aca7f8bc67adf71cd798bf8c57cf22d85ca18 /sys
parent60f1678facd1c144d43bf40470fb108b0f8d7bda (diff)
Transparent IPsec processing on the bridge; for now works only with
static keys.
Diffstat (limited to 'sys')
-rw-r--r--sys/net/if_bridge.c247
1 files changed, 232 insertions, 15 deletions
diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c
index 77fe44ba83f..ebfec700173 100644
--- a/sys/net/if_bridge.c
+++ b/sys/net/if_bridge.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_bridge.c,v 1.96 2002/06/11 04:27:11 art Exp $ */
+/* $OpenBSD: if_bridge.c,v 1.97 2002/06/15 02:26:39 angelos Exp $ */
/*
* Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
@@ -63,8 +63,11 @@
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/if_ether.h>
-#include <netinet/ip_ipsp.h>
#include <netinet/ip_icmp.h>
+#endif
+
+#ifdef IPSEC
+#include <netinet/ip_ipsp.h>
#include <net/if_enc.h>
#endif
@@ -163,6 +166,9 @@ void bridge_fragment(struct bridge_softc *, struct ifnet *,
void bridge_send_icmp_err(struct bridge_softc *, struct ifnet *,
struct ether_header *, struct mbuf *, int, struct llc *, int, int);
#endif
+#ifdef IPSEC
+int bridge_ipsec(int, int, int, struct mbuf *);
+#endif
#define ETHERADDR_IS_IP_MCAST(a) \
/* struct etheraddr *a; */ \
@@ -2058,7 +2064,199 @@ bridge_flushrule(bif)
return (0);
}
-#if NPF > 0
+#ifdef IPSEC
+int
+bridge_ipsec(dir, af, hlen, m)
+ int dir, af, hlen;
+ struct mbuf *m;
+{
+ union sockaddr_union dst;
+ struct timeval tv;
+ struct tdb *tdb;
+ u_int32_t spi;
+ u_int16_t cpi;
+ int error, off;
+ u_int8_t proto = 0;
+#ifdef INET
+ struct ip *ip;
+#endif /* INET */
+#ifdef INET6
+ struct ip6_hdr *ip6;
+#endif /* INET6 */
+
+ if (dir == BRIDGE_IN) {
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ if (m->m_pkthdr.len - hlen < 2 * sizeof(u_int32_t))
+ break;
+
+ ip = mtod(m, struct ip *);
+ proto = ip->ip_p;
+ off = offsetof(struct ip, ip_p);
+
+ if (proto != IPPROTO_ESP && proto != IPPROTO_AH &&
+ proto != IPPROTO_IPCOMP)
+ goto skiplookup;
+
+ bzero(&dst, sizeof(union sockaddr_union));
+ dst.sa.sa_family = AF_INET;
+ dst.sin.sin_len = sizeof(struct sockaddr_in);
+ m_copydata(m, offsetof(struct ip, ip_dst),
+ sizeof(struct in_addr),
+ (caddr_t)&(dst.sin.sin_addr));
+
+ if (ip->ip_p == IPPROTO_ESP)
+ m_copydata(m, hlen, sizeof(u_int32_t),
+ (caddr_t)&spi);
+ else if (ip->ip_p == IPPROTO_AH)
+ m_copydata(m, hlen + sizeof(u_int32_t),
+ sizeof(u_int32_t), (caddr_t)&spi);
+ else if (ip->ip_p == IPPROTO_IPCOMP) {
+ m_copydata(m, hlen + sizeof(u_int16_t),
+ sizeof(u_int16_t), (caddr_t)&cpi);
+ spi = ntohl(htons(cpi));
+ }
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ if (m->m_pkthdr.len - hlen < 2 * sizeof(u_int32_t))
+ break;
+
+ ip6 = mtod(m, struct ip6_hdr *);
+
+ /* XXX We should chase down the header chain */
+ proto = ip6->ip6_nxt;
+ off = offsetof(struct ip6_hdr, ip6_nxt);
+
+ if (proto != IPPROTO_ESP && proto != IPPROTO_AH &&
+ proto != IPPROTO_IPCOMP)
+ goto skiplookup;
+
+ bzero(&dst, sizeof(union sockaddr_union));
+ dst.sa.sa_family = AF_INET6;
+ dst.sin6.sin6_len = sizeof(struct sockaddr_in6);
+ m_copydata(m, offsetof(struct ip6_hdr, ip6_nxt),
+ sizeof(struct in6_addr),
+ (caddr_t)&(dst.sin6.sin6_addr));
+
+ if (proto == IPPROTO_ESP)
+ m_copydata(m, hlen, sizeof(u_int32_t),
+ (caddr_t)&spi);
+ else if (proto == IPPROTO_AH)
+ m_copydata(m, hlen + sizeof(u_int32_t),
+ sizeof(u_int32_t), (caddr_t)&spi);
+ else if (proto == IPPROTO_IPCOMP) {
+ m_copydata(m, hlen + sizeof(u_int16_t),
+ sizeof(u_int16_t), (caddr_t)&cpi);
+ spi = ntohl(htons(cpi));
+ }
+ break;
+#endif /* INET6 */
+ default:
+ return (0);
+ }
+
+ if (proto == 0)
+ goto skiplookup;
+
+ tdb = gettdb(spi, &dst, proto);
+ if (tdb != NULL && (tdb->tdb_flags & TDBF_INVALID) == 0 &&
+ tdb->tdb_xform != NULL) {
+ if (tdb->tdb_first_use == 0) {
+ int pri;
+
+ pri = splhigh();
+ tdb->tdb_first_use = time.tv_sec;
+ splx(pri);
+
+ tv.tv_usec = 0;
+
+ /* Check for wrap-around. */
+ if (tdb->tdb_exp_first_use + tdb->tdb_first_use
+ < tdb->tdb_first_use)
+ tv.tv_sec = ((unsigned long)-1) / 2;
+ else
+ tv.tv_sec = tdb->tdb_exp_first_use +
+ tdb->tdb_first_use;
+
+ if (tdb->tdb_flags & TDBF_FIRSTUSE)
+ timeout_add(&tdb->tdb_first_tmo,
+ hzto(&tv));
+
+ /* Check for wrap-around. */
+ if (tdb->tdb_first_use +
+ tdb->tdb_soft_first_use
+ < tdb->tdb_first_use)
+ tv.tv_sec = ((unsigned long)-1) / 2;
+ else
+ tv.tv_sec = tdb->tdb_first_use +
+ tdb->tdb_soft_first_use;
+
+ if (tdb->tdb_flags & TDBF_SOFT_FIRSTUSE)
+ timeout_add(&tdb->tdb_sfirst_tmo,
+ hzto(&tv));
+ }
+
+ (*(tdb->tdb_xform->xf_input))(m, tdb, hlen, off);
+ return (1);
+ } else {
+ skiplookup:
+ /* XXX do an input policy lookup */
+ return (0);
+ }
+ } else { /* Outgoing from the bridge. */
+ tdb = ipsp_spd_lookup(m, af, hlen, &error,
+ IPSP_DIRECTION_OUT, NULL, NULL);
+ if (tdb != NULL) {
+ /*
+ * We don't need to do loop detection, the
+ * bridge will do that for us.
+ */
+#if NFP > 0
+ switch (af) {
+#ifdef INET
+ case AF_INET:
+ if (pf_test(dir, &encif[0].sc_if,
+ &m) != PF_PASS) {
+ m_freem(m);
+ return (1);
+ }
+ break;
+#endif /* INET */
+#ifdef INET6
+ case AF_INET6:
+ if (pf_test6(dir, &encif[0].sc_if,
+ &m) != PF_PASS) {
+ m_freem(m);
+ return (1);
+ }
+ break;
+#endif /* INET6 */
+ }
+ if (m == NULL)
+ return (1);
+#endif /* NPF */
+#ifdef INET
+ if (af == AF_INET) {
+ ip = mtod(m, struct ip *);
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_id);
+ HTONS(ip->ip_off);
+ }
+#endif /* INET */
+ error = ipsp_process_packet(m, tdb, af, 0);
+ return (1);
+ } else
+ return (0);
+ }
+
+ return (0);
+}
+#endif /* IPSEC */
+
+#if NPF > 0 || defined(IPSEC)
/*
* Filter IP packets by peeking into the ethernet frame. This violates
* the ISO model, but allows us to act as a IP filter at the data link
@@ -2121,17 +2319,18 @@ bridge_filter(sc, dir, ifp, eh, m)
return (NULL);
}
ip = mtod(m, struct ip *);
-
+
if (ip->ip_v != IPVERSION) {
ipstat.ips_badvers++;
goto dropit;
}
-
+
hlen = ip->ip_hl << 2; /* get whole header length */
if (hlen < sizeof(struct ip)) {
ipstat.ips_badhlen++;
goto dropit;
}
+
if (hlen > m->m_len) {
if ((m = m_pullup(m, hlen)) == NULL) {
ipstat.ips_badhlen++;
@@ -2139,18 +2338,18 @@ bridge_filter(sc, dir, ifp, eh, m)
}
ip = mtod(m, struct ip *);
}
-
+
if ((ip->ip_sum = in_cksum(m, hlen)) != 0) {
ipstat.ips_badsum++;
goto dropit;
}
-
+
NTOHS(ip->ip_len);
if (ip->ip_len < hlen)
goto dropit;
NTOHS(ip->ip_id);
NTOHS(ip->ip_off);
-
+
if (m->m_pkthdr.len < ip->ip_len)
goto dropit;
if (m->m_pkthdr.len > ip->ip_len) {
@@ -2160,14 +2359,22 @@ bridge_filter(sc, dir, ifp, eh, m)
} else
m_adj(m, ip->ip_len - m->m_pkthdr.len);
}
-
+
+#ifdef IPSEC
+ if ((sc->sc_if.if_flags & IFF_LINK2) == IFF_LINK2 &&
+ bridge_ipsec(dir, AF_INET, hlen, m))
+ return (NULL);
+#endif /* IPSEC */
+
+#if NPF > 0
/* Finally, we get to filter the packet! */
m->m_pkthdr.rcvif = ifp;
if (pf_test(dir, ifp, &m) != PF_PASS)
goto dropit;
if (m == NULL)
goto dropit;
-
+#endif /* NPF */
+
/* Rebuild the IP header */
if (m->m_len < hlen && ((m = m_pullup(m, hlen)) == NULL))
return (NULL);
@@ -2193,29 +2400,39 @@ bridge_filter(sc, dir, ifp, eh, m)
return (NULL);
}
}
-
+
ip6 = mtod(m, struct ip6_hdr *);
-
+
if ((ip6->ip6_vfc & IPV6_VERSION_MASK) != IPV6_VERSION) {
ip6stat.ip6s_badvers++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr);
goto dropit;
}
+#ifdef IPSEC
+ hlen = sizeof(struct ip6_hdr);
+
+ if ((sc->sc_if.if_flags & IFF_LINK2) == IFF_LINK2 &&
+ bridge_ipsec(dir, AF_INET6, hlen, m))
+ return (NULL);
+#endif /* IPSEC */
+
+#if NPF > 0
if (pf_test6(dir, ifp, &m) != PF_PASS)
goto dropit;
if (m == NULL)
return (NULL);
-
+#endif /* NPF */
+
break;
}
#endif /* INET6 */
-
+
default:
goto dropit;
break;
}
-
+
/* Reattach SNAP header */
if (hassnap) {
M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);