diff options
-rw-r--r-- | sys/netinet/in.h | 8 | ||||
-rw-r--r-- | sys/netinet/in_pcb.c | 7 | ||||
-rw-r--r-- | sys/netinet/ip_ah.c | 37 | ||||
-rw-r--r-- | sys/netinet/ip_ah.h | 6 | ||||
-rw-r--r-- | sys/netinet/ip_esp.c | 25 | ||||
-rw-r--r-- | sys/netinet/ip_esp.h | 6 | ||||
-rw-r--r-- | sys/netinet/ip_ether.c | 6 | ||||
-rw-r--r-- | sys/netinet/ip_input.c | 174 | ||||
-rw-r--r-- | sys/netinet/ip_ipip.c | 50 | ||||
-rw-r--r-- | sys/netinet/ip_ipip.h | 6 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.c | 1687 | ||||
-rw-r--r-- | sys/netinet/ip_ipsp.h | 175 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 90 | ||||
-rw-r--r-- | sys/netinet/ip_spd.c | 791 | ||||
-rw-r--r-- | sys/netinet/ipsec_input.c | 152 | ||||
-rw-r--r-- | sys/netinet/ipsec_output.c | 361 | ||||
-rw-r--r-- | sys/netinet/raw_ip.c | 8 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 104 | ||||
-rw-r--r-- | sys/netinet/tcp_usrreq.c | 10 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 90 |
20 files changed, 1992 insertions, 1801 deletions
diff --git a/sys/netinet/in.h b/sys/netinet/in.h index ab6e9c2977e..6a70310720d 100644 --- a/sys/netinet/in.h +++ b/sys/netinet/in.h @@ -1,4 +1,4 @@ -/* $OpenBSD: in.h,v 1.41 2000/09/18 22:06:36 provos Exp $ */ +/* $OpenBSD: in.h,v 1.42 2000/09/19 03:20:57 angelos Exp $ */ /* $NetBSD: in.h,v 1.20 1996/02/13 23:41:47 christos Exp $ */ /* @@ -259,8 +259,6 @@ struct ip_opts { #define IP_ESP_TRANS_LEVEL 21 /* int; transport encryption */ #define IP_ESP_NETWORK_LEVEL 22 /* int; full-packet encryption */ -#define IPSEC_OUTSA 39 /* set the outbound SA for a socket */ - /* * Security levels - IPsec, not IPSO */ @@ -434,7 +432,7 @@ struct ip_mreq { #ifdef notdef /*obsolete*/ #define IPCTL_GIF_TTL 13 /* default TTL for gif encap packet */ #endif -#define IPCTL_IPSEC_ACL 14 /* ingress IPsec access control */ +#define IPCTL_IPSEC_EXPIRE_ACQUIRE 14 /* How long to wait for key mgmt. */ #define IPCTL_IPSEC_EMBRYONIC_SA_TIMEOUT 15 /* new SA lifetime */ #define IPCTL_IPSEC_REQUIRE_PFS 16 #define IPCTL_IPSEC_SOFT_ALLOCATIONS 17 @@ -466,7 +464,7 @@ struct ip_mreq { { "maxqueue", CTLTYPE_INT }, \ { "encdebug", CTLTYPE_INT }, \ { 0, 0 }, \ - { "ipsec-acl", CTLTYPE_INT }, \ + { "ipsec-expire-acquire", CTLTYPE_INT }, \ { "ipsec-invalid-life", CTLTYPE_INT }, \ { "ipsec-pfs", CTLTYPE_INT }, \ { "ipsec-soft-allocs", CTLTYPE_INT }, \ diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index f0fc288ca89..bbda223ea3a 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.43 2000/09/18 22:06:37 provos Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.44 2000/09/19 03:20:57 angelos Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -78,8 +78,6 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #ifdef IPSEC #include <netinet/ip_ipsp.h> - -extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); #endif #if 0 /*KAME IPSEC*/ @@ -499,7 +497,8 @@ in_pcbconnect(v, nam) inp->inp_fport = sin->sin_port; in_pcbrehash(inp); #ifdef IPSEC - return (check_ipsec_policy(inp, 0)); + /* XXX Find IPsec TDB */ + return (0); #else return (0); #endif diff --git a/sys/netinet/ip_ah.c b/sys/netinet/ip_ah.c index ce448dfa7c9..fd0723b47a6 100644 --- a/sys/netinet/ip_ah.c +++ b/sys/netinet/ip_ah.c @@ -1,12 +1,12 @@ -/* $OpenBSD: ip_ah.c,v 1.43 2000/08/03 08:20:59 angelos Exp $ */ +/* $OpenBSD: ip_ah.c,v 1.44 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. @@ -14,11 +14,12 @@ * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis * and Niels Provos. * - * Additional features in 1999 by Angelos D. Keromytis. + * Additional features in 1999 by Angelos D. Keromytis and Niklas Hallqvist. * - * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, + * Copyright (c) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, * Angelos D. Keromytis and Niels Provos. - * + * Copyright (c) 1999 Niklas Hallqvist. + * * Permission to use, copy, and modify this software without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or @@ -234,17 +235,17 @@ ah_massage_headers(struct mbuf **m0, int proto, int skip, int alg, int out) HTONS(ip->ip_len); HTONS(ip->ip_id); - if ((alg == CRYPTO_MD5_KPDK) || (alg == CRYPTO_SHA1_KPDK)) - ip->ip_off = htons(ip->ip_off & IP_DF); - else - ip->ip_off = 0; - } + if ((alg == CRYPTO_MD5_KPDK) || (alg == CRYPTO_SHA1_KPDK)) + ip->ip_off = htons(ip->ip_off & IP_DF); + else + ip->ip_off = 0; + } else - { - if ((alg == CRYPTO_MD5_KPDK) || (alg == CRYPTO_SHA1_KPDK)) - ip->ip_off = htons(ntohs(ip->ip_off) & IP_DF); - else - ip->ip_off = 0; + { + if ((alg == CRYPTO_MD5_KPDK) || (alg == CRYPTO_SHA1_KPDK)) + ip->ip_off = htons(ntohs(ip->ip_off) & IP_DF); + else + ip->ip_off = 0; } ptr = mtod(m, unsigned char *) + sizeof(struct ip); @@ -553,7 +554,7 @@ ah_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, TDBEXP_TIMEOUT); + tdb_delete(tdb, TDBEXP_TIMEOUT); m_freem(m); return ENXIO; } @@ -933,7 +934,7 @@ ah_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, TDBEXP_TIMEOUT); + tdb_delete(tdb, TDBEXP_TIMEOUT); m_freem(m); return EINVAL; } diff --git a/sys/netinet/ip_ah.h b/sys/netinet/ip_ah.h index 45901eea86f..c3349e54c26 100644 --- a/sys/netinet/ip_ah.h +++ b/sys/netinet/ip_ah.h @@ -1,12 +1,12 @@ -/* $OpenBSD: ip_ah.h,v 1.25 2000/03/17 10:25:22 angelos Exp $ */ +/* $OpenBSD: ip_ah.h,v 1.26 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. diff --git a/sys/netinet/ip_esp.c b/sys/netinet/ip_esp.c index 818255f7b02..a64ff79fdd6 100644 --- a/sys/netinet/ip_esp.c +++ b/sys/netinet/ip_esp.c @@ -1,12 +1,12 @@ -/* $OpenBSD: ip_esp.c,v 1.48 2000/06/18 08:23:48 angelos Exp $ */ +/* $OpenBSD: ip_esp.c,v 1.49 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. @@ -355,7 +355,7 @@ esp_input(struct mbuf *m, struct tdb *tdb, int skip, int protoff) (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, TDBEXP_TIMEOUT); + tdb_delete(tdb, TDBEXP_TIMEOUT); m_freem(m); return ENXIO; } @@ -807,7 +807,7 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, (tdb->tdb_cur_bytes >= tdb->tdb_exp_bytes)) { pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, TDBEXP_TIMEOUT); + tdb_delete(tdb, TDBEXP_TIMEOUT); m_freem(m); return EINVAL; } @@ -878,8 +878,7 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, * Add padding -- better to do it ourselves than use the crypto engine, * although if/when we support compression, we'd have to do that. */ - pad = (u_char *) m_pad(m, padding + alen, - tdb->tdb_flags & TDBF_RANDOMPADDING); + pad = (u_char *) m_pad(m, padding + alen); if (pad == NULL) { DPRINTF(("esp_output(): m_pad() failed for SA %s/%08x\n", @@ -893,6 +892,10 @@ esp_output(struct mbuf *m, struct tdb *tdb, struct mbuf **mp, int skip, for (ilen = 0; ilen < padding - 2; ilen++) pad[ilen] = ilen + 1; } + else + { + get_random_bytes((void *) pad, padding - 2); /* Random padding */ + } /* Fix padding length and Next Protocol in padding itself */ pad[padding - 2] = padding - 2; @@ -1116,12 +1119,11 @@ checkreplaywindow32(u_int32_t seq, u_int32_t initial, u_int32_t *lastseq, /* * m_pad(m, n) pads <m> with <n> bytes at the end. The packet header * length is updated, and a pointer to the first byte of the padding - * (which is guaranteed to be all in one mbuf) is returned. The third - * argument specifies whether we need randompadding or not. + * (which is guaranteed to be all in one mbuf) is returned. */ caddr_t -m_pad(struct mbuf *m, int n, int randompadding) +m_pad(struct mbuf *m, int n) { register struct mbuf *m0, *m1; register int len, pad; @@ -1176,8 +1178,5 @@ m_pad(struct mbuf *m, int n, int randompadding) m0->m_len += pad; m->m_pkthdr.len += pad; - if (randompadding) - get_random_bytes((void *) retval, n); - return retval; } diff --git a/sys/netinet/ip_esp.h b/sys/netinet/ip_esp.h index f7cae5bbe6c..04f7c55d46d 100644 --- a/sys/netinet/ip_esp.h +++ b/sys/netinet/ip_esp.h @@ -1,12 +1,12 @@ -/* $OpenBSD: ip_esp.h,v 1.32 2000/03/17 10:25:22 angelos Exp $ */ +/* $OpenBSD: ip_esp.h,v 1.33 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. diff --git a/sys/netinet/ip_ether.c b/sys/netinet/ip_ether.c index 97cc0ed77d6..645c1ee4471 100644 --- a/sys/netinet/ip_ether.c +++ b/sys/netinet/ip_ether.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ether.c,v 1.9 2000/04/18 21:37:29 angelos Exp $ */ +/* $OpenBSD: ip_ether.c,v 1.10 2000/09/19 03:20:58 angelos Exp $ */ /* * The author of this code is Angelos D. Keromytis (kermit@adk.gr) @@ -155,10 +155,6 @@ va_dcl /* Copy ethernet header */ m_copydata(m, 0, sizeof(eh), (void *) &eh); - /* tdbi is only set in ESP or AH, if next protocol is UDP or TCP */ - if (m->m_flags & (M_CONF|M_AUTH)) - m->m_pkthdr.tdbi = NULL; - m->m_flags &= ~(M_BCAST|M_MCAST); if (eh.ether_dhost[0] & 1) { diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 792dc8886a1..41495f6693a 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_input.c,v 1.57 2000/09/18 22:06:37 provos Exp $ */ +/* $OpenBSD: ip_input.c,v 1.58 2000/09/19 03:20:58 angelos Exp $ */ /* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */ /* @@ -66,6 +66,10 @@ #include <netinet/ip_icmp.h> #include <netinet/ip_ipsp.h> +#ifdef IPSEC +#include <netinet/ip_ipsp.h> +#endif /* IPSEC */ + #ifndef IPFORWARDING #ifdef GATEWAY #define IPFORWARDING 1 /* forward IP packets not for us */ @@ -76,6 +80,9 @@ #ifndef IPSENDREDIRECTS #define IPSENDREDIRECTS 1 #endif + +#define PI_MAGIC 0xdeadbeef /* XXX the horror! */ + #ifndef IPMTUDISC #define IPMTUDISC 0 #endif @@ -84,7 +91,6 @@ #endif int encdebug = 0; -int ipsec_acl = 1; int ipsec_keep_invalid = IPSEC_DEFAULT_EMBRYONIC_SA_TIMEOUT; int ipsec_require_pfs = IPSEC_DEFAULT_PFS; int ipsec_soft_allocations = IPSEC_DEFAULT_SOFT_ALLOCATIONS; @@ -95,6 +101,7 @@ int ipsec_soft_timeout = IPSEC_DEFAULT_SOFT_TIMEOUT; int ipsec_exp_timeout = IPSEC_DEFAULT_EXP_TIMEOUT; int ipsec_soft_first_use = IPSEC_DEFAULT_SOFT_FIRST_USE; int ipsec_exp_first_use = IPSEC_DEFAULT_EXP_FIRST_USE; +int ipsec_expire_acquire = IPSEC_DEFAULT_EXPIRE_ACQUIRE; char ipsec_def_enc[20]; char ipsec_def_auth[20]; @@ -293,14 +300,30 @@ ipv4_input(struct mbuf *m, ...) int hlen, mff; va_list ap; int extra; +#ifdef IPSEC + int error, s; + struct tdb *tdb; + struct tdb_ident *tdbi; +#endif /* IPSEC */ va_start(ap, m); extra = va_arg(ap, int); va_end(ap); +#ifdef IPSEC + tdbi = (struct tdb_ident *) m->m_pkthdr.tdbi; + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; +#endif /* IPSEC */ + if (extra) { struct mbuf *newpacket; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ + if (!(newpacket = m_split(m, extra, M_NOWAIT))) { m_freem(m); return; @@ -322,6 +345,10 @@ ipv4_input(struct mbuf *m, ...) if (m->m_len < sizeof (struct ip) && (m = m_pullup(m, sizeof (struct ip))) == 0) { ipstat.ips_toosmall++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } ip = mtod(m, struct ip *); @@ -337,6 +364,10 @@ ipv4_input(struct mbuf *m, ...) if (hlen > m->m_len) { if ((m = m_pullup(m, hlen)) == 0) { ipstat.ips_badhlen++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } ip = mtod(m, struct ip *); @@ -382,8 +413,13 @@ ipv4_input(struct mbuf *m, ...) */ { struct mbuf *m0 = m; - if (fr_checkp && (*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m0)) + if (fr_checkp && (*fr_checkp)(ip, hlen, m->m_pkthdr.rcvif, 0, &m0)) { +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; + } ip = mtod(m = m0, struct ip *); } #endif @@ -395,8 +431,13 @@ ipv4_input(struct mbuf *m, ...) * to be sent and the original packet to be freed). */ ip_nhops = 0; /* for source routed packets */ - if (hlen > sizeof (struct ip) && ip_dooptions(m)) - return; + if (hlen > sizeof (struct ip) && ip_dooptions(m)) { +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ + return; + } /* * Check our list of addresses, to see if the packet is for us. @@ -413,6 +454,10 @@ ipv4_input(struct mbuf *m, ...) if (m->m_flags & M_EXT) { if ((m = m_pullup(m, hlen)) == 0) { ipstat.ips_toosmall++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } ip = mtod(m, struct ip *); @@ -434,6 +479,10 @@ ipv4_input(struct mbuf *m, ...) ip->ip_id = htons(ip->ip_id); if (ip_mforward(m, m->m_pkthdr.rcvif) != 0) { ipstat.ips_cantforward++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); return; } @@ -457,6 +506,10 @@ ipv4_input(struct mbuf *m, ...) if (inm == NULL) { ipstat.ips_cantforward++; m_freem(m); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } goto ours; @@ -470,9 +523,41 @@ ipv4_input(struct mbuf *m, ...) */ if (ipforwarding == 0) { ipstat.ips_cantforward++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); - } else + } else { +#ifdef IPSEC + /* IPsec policy check for forwarded packets */ + s = splnet(); + if (tdbi == NULL) + tdb = NULL; + else + tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto); + + ipsp_spd_lookup(m, AF_INET, hlen, &error, + IPSP_DIRECTION_IN, tdb, NULL); + splx(s); + + /* Error or otherwise drop-packet indication */ + if (error) { + ipstat.ips_cantforward++; + m_freem(m); + return; + } + + if (tdbi) { + free(tdbi, M_TEMP); + m->m_pkthdr.tdbi = NULL; + } + + /* Fall through, forward packet */ +#endif /* IPSEC */ + ip_forward(m, 0); + } return; ours: @@ -487,6 +572,10 @@ ours: if (m->m_flags & M_EXT) { /* XXX */ if ((m = m_pullup(m, hlen)) == 0) { ipstat.ips_toosmall++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } ip = mtod(m, struct ip *); @@ -553,6 +642,10 @@ found: ip = ip_reass(ipqe, fp); if (ip == 0) { ipq_unlock(); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } ipstat.ips_reassembled++; @@ -565,6 +658,66 @@ found: } else ip->ip_len -= hlen; +#ifdef IPSEC + /* + * If it's a protected packet for us, skip the policy check. + * That's because we really only care about the properties of + * the protected packet, and not the intermediate versions. + * While this is not the most paranoid setting, it allows + * some flexibility in handling of nested tunnels etc. + */ + if ((ip->ip_p == IPPROTO_ESP) || (ip->ip_p == IPPROTO_AH)) + goto skipipsec; + + /* + * If the protected packet was tunneled, then we need to + * verify the protected packet's information, not the + * external headers. Thus, skip the policy lookup for the + * external packet, and keep the IPsec information linked on + * the packet header (the encapsulation routines know how + * to deal with that). + */ + if ((ip->ip_p == IPPROTO_IPIP) || (ip->ip_p == IPPROTO_IPV6)) + goto skipipsec2; + + /* + * If the protected packet is TCP or UDP, we'll do the + * policy check in the respective input routine, so we can + * check for bypass sockets. + */ + if ((ip->ip_p == IPPROTO_TCP) || (ip->ip_p == IPPROTO_UDP)) + goto skipipsec2; + + /* IPsec policy check for local-delivery packets */ + s = splnet(); + if (tdbi == NULL) + tdb = NULL; + else + tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto); + + ipsp_spd_lookup(m, AF_INET, hlen, &error, IPSP_DIRECTION_IN, + tdb, NULL); + splx(s); + + /* Error or otherwise drop-packet indication */ + if (error) { + ipstat.ips_cantforward++; + if (tdbi) + free(tdbi, M_TEMP); + m_freem(m); + return; + } + + skipipsec: + if (tdbi) { + free(tdbi, M_TEMP); + m->m_pkthdr.tdbi = NULL; + } + + skipipsec2: + /* Otherwise, just fall through and deliver the packet */ +#endif /* IPSEC */ + /* * Switch out to protocol's input routine. */ @@ -572,6 +725,10 @@ found: (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen, NULL, 0); return; bad: +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); } @@ -1546,8 +1703,6 @@ ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen) &ip_maxqueue)); case IPCTL_ENCDEBUG: return (sysctl_int(oldp, oldlenp, newp, newlen, &encdebug)); - case IPCTL_IPSEC_ACL: - return (sysctl_int(oldp, oldlenp, newp, newlen, &ipsec_acl)); case IPCTL_IPSEC_EMBRYONIC_SA_TIMEOUT: return (sysctl_int(oldp, oldlenp, newp, newlen, &ipsec_keep_invalid)); @@ -1585,6 +1740,9 @@ ip_sysctl(name, namelen, oldp, oldlenp, newp, newlen) return (sysctl_tstring(oldp, oldlenp, newp, newlen, ipsec_def_auth, sizeof(ipsec_def_auth))); + case IPCTL_IPSEC_EXPIRE_ACQUIRE: + return (sysctl_int(oldp, oldlenp, newp, newlen, + &ipsec_expire_acquire)); default: return (EOPNOTSUPP); } diff --git a/sys/netinet/ip_ipip.c b/sys/netinet/ip_ipip.c index 4ca9f239a20..f680237f672 100644 --- a/sys/netinet/ip_ipip.c +++ b/sys/netinet/ip_ipip.c @@ -1,12 +1,12 @@ -/* $OpenBSD: ip_ipip.c,v 1.8 2000/08/04 00:26:58 angelos Exp $ */ +/* $OpenBSD: ip_ipip.c,v 1.9 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. @@ -87,6 +87,8 @@ #define offsetof(s, e) ((int)&((s *)0)->e) #endif +#define PI_MAGIC 0xdeadbeef /* XXX */ + /* * We can control the acceptance of IP4 packets by altering the sysctl * net.inet.ipip.allow value. Zero means drop them, all else is acceptance. @@ -102,11 +104,18 @@ struct ipipstat ipipstat; int ip4_input6(struct mbuf **m, int *offp, int proto) { + void *tdbi = (*m)->m_pkthdr.tdbi; + + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; + /* If we do not accept IPv4 explicitly, drop. */ if (!ipip_allow && ((*m)->m_flags & (M_AUTH|M_CONF)) == 0) { DPRINTF(("ip4_input6(): dropped due to policy\n")); ipipstat.ipips_pdrops++; + if (tdbi) + free(tdbi, M_TEMP); m_freem(*m); return IPPROTO_DONE; } @@ -125,12 +134,18 @@ ip4_input(struct mbuf *m, ...) { va_list ap; int iphlen; + void *tdbi = m->m_pkthdr.tdbi; + + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; /* If we do not accept IPv4 explicitly, drop. */ if (!ipip_allow && (m->m_flags & (M_AUTH|M_CONF)) == 0) { DPRINTF(("ip4_input(): dropped due to policy\n")); ipipstat.ipips_pdrops++; + if (tdbi) + free(tdbi, M_TEMP); m_freem(m); return; } @@ -168,9 +183,13 @@ ipip_input(struct mbuf *m, int iphlen) u_int8_t otos; u_int8_t v; int hlen, s; + void *tdbi = m->m_pkthdr.tdbi; ipipstat.ipips_ipackets++; + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; + m_copydata(m, 0, 1, &v); switch (v >> 4) @@ -187,6 +206,8 @@ ipip_input(struct mbuf *m, int iphlen) break; #endif default: + if (tdbi) + free(tdbi, M_TEMP); m_freem(m); return /* EAFNOSUPPORT */; } @@ -198,7 +219,8 @@ ipip_input(struct mbuf *m, int iphlen) { DPRINTF(("ipip_input(): m_pullup() failed\n")); ipipstat.ipips_hdrops++; - m_freem(m); + if (tdbi) + free(tdbi, M_TEMP); return; } } @@ -210,6 +232,12 @@ ipip_input(struct mbuf *m, int iphlen) { if (IN_MULTICAST(((struct ip *)((char *) ipo + iphlen))->ip_dst.s_addr)) { + if (tdbi) + { + free(tdbi, M_TEMP); + m->m_pkthdr.tdbi = NULL; + } + ipip_mroute_input (m, iphlen); return; } @@ -248,6 +276,8 @@ ipip_input(struct mbuf *m, int iphlen) #endif default: + if (tdbi) + free(tdbi, M_TEMP); m_freem(m); return /* EAFNOSUPPORT */; } @@ -258,6 +288,8 @@ ipip_input(struct mbuf *m, int iphlen) if ((m = m_pullup(m, hlen)) == 0) { DPRINTF(("ipip_input(): m_pullup() failed\n")); + if (tdbi) + free(tdbi, M_TEMP); ipipstat.ipips_hdrops++; return; } @@ -314,6 +346,8 @@ ipip_input(struct mbuf *m, int iphlen) { DPRINTF(("ipip_input(): possible local address spoofing detected on packet from %s to %s (%s->%s)\n", inet_ntoa4(ipo->ip_src), inet_ntoa4(ipo->ip_dst), inet_ntoa4(ipo->ip_src), inet_ntoa4(ipo->ip_dst))); ipipstat.ipips_spoof++; + if (tdbi) + free(tdbi, M_TEMP); m_freem(m); return; } @@ -331,6 +365,8 @@ ipip_input(struct mbuf *m, int iphlen) if (IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &ip6->ip6_src)) { DPRINTF(("ipip_input(): possible local address spoofing detected on packet\n")); + if (tdbi) + free(tdbi, M_TEMP); m_freem(m); return; } @@ -344,10 +380,6 @@ ipip_input(struct mbuf *m, int iphlen) /* Statistics */ ipipstat.ipips_ibytes += m->m_pkthdr.len - iphlen; - /* tdbi is only set in ESP or AH, if the next protocol is UDP or TCP */ - if (m->m_flags & (M_CONF|M_AUTH)) - m->m_pkthdr.tdbi = NULL; - /* * Interface pointer stays the same; if no IPsec processing has * been done (or will be done), this will point to a normal @@ -377,6 +409,8 @@ ipip_input(struct mbuf *m, int iphlen) { IF_DROP(ifq); m_freem(m); + if (tdbi) + free(tdbi, M_TEMP); ipipstat.ipips_qfull++; splx(s); diff --git a/sys/netinet/ip_ipip.h b/sys/netinet/ip_ipip.h index c413e139ca2..d8a45204046 100644 --- a/sys/netinet/ip_ipip.h +++ b/sys/netinet/ip_ipip.h @@ -1,12 +1,12 @@ -/* $OpenBSD: ip_ipip.h,v 1.1 2000/01/21 03:15:05 angelos Exp $ */ +/* $OpenBSD: ip_ipip.h,v 1.2 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and * Niels Provos (provos@physnet.uni-hamburg.de). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. diff --git a/sys/netinet/ip_ipsp.c b/sys/netinet/ip_ipsp.c index b5af2ba3071..2ef04505cee 100644 --- a/sys/netinet/ip_ipsp.c +++ b/sys/netinet/ip_ipsp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.c,v 1.99 2000/08/03 08:31:39 angelos Exp $ */ +/* $OpenBSD: ip_ipsp.c,v 1.100 2000/09/19 03:20:58 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -6,8 +6,8 @@ * Niels Provos (provos@physnet.uni-hamburg.de) and * Niklas Hallqvist (niklas@appli.se). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. @@ -41,12 +41,10 @@ #include <sys/systm.h> #include <sys/malloc.h> #include <sys/mbuf.h> -#include <sys/protosw.h> #include <sys/socket.h> -#include <sys/socketvar.h> #include <sys/errno.h> #include <sys/kernel.h> -#include <sys/proc.h> +#include <sys/queue.h> #include <net/if.h> #include <net/route.h> @@ -54,9 +52,9 @@ #ifdef INET #include <netinet/in.h> #include <netinet/in_systm.h> +#include <netinet/ip_var.h> #include <netinet/ip.h> #include <netinet/in_pcb.h> -#include <netinet/ip_var.h> #endif /* INET */ #ifdef INET6 @@ -69,10 +67,7 @@ #include <net/pfkeyv2.h> #include <netinet/ip_ipsp.h> -#include <netinet/ip_ah.h> -#include <netinet/ip_esp.h> -#include <crypto/crypto.h> #include <crypto/xform.h> #include <dev/rndvar.h> @@ -98,7 +93,6 @@ void tdb_hashstats(void); int ipsp_kern __P((int, char **, int)); u_int8_t get_sa_require __P((struct inpcb *)); -int check_ipsec_policy __P((struct inpcb *, void *)); void tdb_rehash __P((void)); extern int ipsec_auth_default_level; @@ -107,11 +101,16 @@ extern int ipsec_esp_network_default_level; extern int encdebug; int ipsec_in_use = 0; +u_int64_t ipsec_last_added = 0; u_int32_t kernfs_epoch = 0; struct expclusterlist_head expclusterlist = TAILQ_HEAD_INITIALIZER(expclusterlist); struct explist_head explist = TAILQ_HEAD_INITIALIZER(explist); +struct ipsec_policy_head ipsec_policy_head = + TAILQ_HEAD_INITIALIZER(ipsec_policy_head); +struct ipsec_acquire_head ipsec_acquire_head = + TAILQ_HEAD_INITIALIZER(ipsec_acquire_head); /* * This is the proper place to define the various encapsulation transforms. @@ -141,302 +140,54 @@ struct xformsw *xformswNXFORMSW = &xformsw[sizeof(xformsw)/sizeof(xformsw[0])]; unsigned char ipseczeroes[IPSEC_ZEROES_SIZE]; /* zeroes! */ #define TDB_HASHSIZE_INIT 32 -static struct tdb **tdbh = NULL, *tdb_bypass = NULL; +static struct tdb **tdbh = NULL; +static struct tdb **tdbaddr = NULL; +static struct tdb **tdbsrc = NULL; static u_int tdb_hashmask = TDB_HASHSIZE_INIT - 1; static int tdb_count; /* - * Check which transformations are required. - */ -u_int8_t -get_sa_require(struct inpcb *inp) -{ - u_int8_t sareq = 0; - - if (inp != NULL) - { - sareq |= inp->inp_seclevel[SL_AUTH] >= IPSEC_LEVEL_USE ? - NOTIFY_SATYPE_AUTH : 0; - sareq |= inp->inp_seclevel[SL_ESP_TRANS] >= IPSEC_LEVEL_USE ? - NOTIFY_SATYPE_CONF : 0; - sareq |= inp->inp_seclevel[SL_ESP_NETWORK] >= IPSEC_LEVEL_USE ? - NOTIFY_SATYPE_TUNNEL : 0; - } - else - { - sareq |= ipsec_auth_default_level >= IPSEC_LEVEL_USE ? - NOTIFY_SATYPE_AUTH : 0; - sareq |= ipsec_esp_trans_default_level >= IPSEC_LEVEL_USE ? - NOTIFY_SATYPE_CONF : 0; - sareq |= ipsec_esp_network_default_level >= IPSEC_LEVEL_USE ? - NOTIFY_SATYPE_TUNNEL : 0; - } - - return (sareq); -} - -/* - * Check the socket policy and request a new SA with a key management - * daemon. Sometimes inp does not contain the destination address; - * in that case use dst. + * Our hashing function needs to stir things with a non-zero random multiplier + * so we cannot be DoS-attacked via choosing of the data to hash. */ -int -check_ipsec_policy(struct inpcb *inp, void *daddr) +INLINE int +tdb_hash(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto) { - struct route_enc re0, *re = &re0; - struct sockaddr_encap *dst, *gw; - u_int8_t sa_require, sa_have; - struct tdb tdb2, *tdb = NULL; - union sockaddr_union sunion; - struct socket *so; - int error, i, s; - - if (inp == NULL || ((so = inp->inp_socket) == 0)) - return (EINVAL); - - /* If IPsec is not required just use what we got */ - if (!(sa_require = inp->inp_secrequire)) - return 0; - - s = spltdb(); - if (!inp->inp_tdb) - { - bzero((caddr_t) re, sizeof(*re)); - dst = (struct sockaddr_encap *) &re->re_dst; - dst->sen_family = PF_KEY; - -#ifdef INET6 - if (inp->inp_flags & INP_IPV6) - { - dst->sen_len = SENT_IP6_LEN; - dst->sen_type = SENT_IP6; - dst->sen_ip6_src = inp->inp_laddr6; - if (inp->inp_faddr6.s6_addr) - dst->sen_ip6_dst = inp->inp_faddr6; - else - dst->sen_ip6_dst = (*((struct in6_addr *) daddr)); - - dst->sen_ip6_proto = so->so_proto->pr_protocol; - - switch (dst->sen_ip6_proto) - { - case IPPROTO_UDP: - case IPPROTO_TCP: - dst->sen_ip6_sport = htons(inp->inp_lport); - dst->sen_ip6_dport = htons(inp->inp_fport); - break; - - default: - dst->sen_ip6_sport = 0; - dst->sen_ip6_dport = 0; - } - } -#endif /* INET6 */ - -#ifdef INET - if (!(inp->inp_flags & INP_IPV6)) - { - dst->sen_len = SENT_IP4_LEN; - dst->sen_type = SENT_IP4; - dst->sen_ip_src = inp->inp_laddr; - - if (inp->inp_faddr.s_addr) - dst->sen_ip_dst = inp->inp_faddr; - else - dst->sen_ip_dst = (*((struct in_addr *) daddr)); - - dst->sen_proto = so->so_proto->pr_protocol; - - switch (dst->sen_proto) - { - case IPPROTO_UDP: - case IPPROTO_TCP: - dst->sen_sport = htons(inp->inp_lport); - dst->sen_dport = htons(inp->inp_fport); - break; - - default: - dst->sen_sport = 0; - dst->sen_dport = 0; - } - } -#endif /* INET */ - - /* Try to find a flow */ - rtalloc((struct route *) re); - - if (re->re_rt != NULL) - { - gw = (struct sockaddr_encap *) (re->re_rt->rt_gateway); - -#ifdef INET - if (gw->sen_type == SENT_IPSP) - { - bzero(&sunion, sizeof(sunion)); - sunion.sin.sin_family = AF_INET; - sunion.sin.sin_len = sizeof(struct sockaddr_in); - sunion.sin.sin_addr = gw->sen_ipsp_dst; - - tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion, - gw->sen_ipsp_sproto); - } -#endif /* INET */ - -#ifdef INET6 - if (gw->sen_type == SENT_IPSP6) - { - bzero(&sunion, sizeof(sunion)); - sunion.sin6.sin6_family = AF_INET6; - sunion.sin6.sin6_len = sizeof(struct sockaddr_in6); - sunion.sin6.sin6_addr = gw->sen_ipsp6_dst; - - tdb = (struct tdb *) gettdb(gw->sen_ipsp6_spi, &sunion, - gw->sen_ipsp6_sproto); - } -#endif /* INET6 */ - - RTFREE(re->re_rt); - } - } - else - tdb = inp->inp_tdb; - - if (tdb) - SPI_CHAIN_ATTRIB(sa_have, tdb_onext, tdb); - else - sa_have = 0; - - splx(s); - - /* Check if our requirements are met */ - if (!(sa_require & ~sa_have)) - return 0; - - error = i = 0; + static u_int32_t mult1 = 0, mult2 = 0; + u_int8_t *ptr = (u_int8_t *) dst; + int i, shift; + u_int64_t hash; + int val32 = 0; - inp->inp_secresult = SR_WAIT; + while (mult1 == 0) + mult1 = arc4random(); + while (mult2 == 0) + mult2 = arc4random(); - /* If necessary try to notify keymanagement three times */ - while (i < 3) + hash = (spi ^ proto) * mult1; + for (i = 0; i < SA_LEN(&dst->sa); i++) { - switch (dst->sen_type) - { -#ifdef INET - case SENT_IP4: - DPRINTF(("ipsec: send SA request (%d), remote IPv4 address: %s, SA type: %d\n", i + 1, inet_ntoa4(dst->sen_ip_dst), sa_require)); - break; -#endif /* INET */ - -#ifdef INET6 - case SENT_IP6: - DPRINTF(("ipsec: send SA request (%d), remote IPv6 address: %s, SA type: %d\n", i + 1, ip6_sprintf(&dst->sen_ip6_dst), sa_require)); - break; -#endif /* INET6 */ - - default: - DPRINTF(("ipsec: unsupported protocol family %d, cannot notify kkey management\n", dst->sen_type)); - return EPFNOSUPPORT; - } - - /* Initialize TDB for PF_KEY notification */ - bzero(&tdb2, sizeof(tdb2)); - tdb2.tdb_satype = get_sa_require(inp); - -#ifdef INET - if (!(inp->inp_flags & INP_IPV6)) - { - tdb2.tdb_src.sin.sin_family = AF_INET; - tdb2.tdb_src.sin.sin_len = sizeof(struct sockaddr_in); - tdb2.tdb_src.sin.sin_addr = inp->inp_laddr; - - tdb2.tdb_dst.sin.sin_family = AF_INET; - tdb2.tdb_dst.sin.sin_len = sizeof(struct sockaddr_in); - tdb2.tdb_dst.sin.sin_addr = inp->inp_faddr; - } -#endif /* INET */ - -#ifdef INET6 - if (inp->inp_flags & INP_IPV6) + val32 = (val32 << 8) | ptr[i]; + if (i % 4 == 3) { - tdb2.tdb_src.sin6.sin6_family = AF_INET6; - tdb2.tdb_src.sin6.sin6_len = sizeof(struct sockaddr_in6); - tdb2.tdb_src.sin6.sin6_addr = inp->inp_laddr6; - - tdb2.tdb_dst.sin6.sin6_family = AF_INET6; - tdb2.tdb_dst.sin6.sin6_len = sizeof(struct sockaddr_in6); - tdb2.tdb_dst.sin6.sin6_addr = inp->inp_faddr6; + hash ^= val32 * mult2; + val32 = 0; } -#endif /* INET6 */ - - error = ipsp_acquire_sa(&tdb2); - if (error) - return error; - - /* - * Wait for the keymanagement daemon to establich a new SA, - * even on error check again, perhaps some other process - * already established the necessary SA. - */ - error = tsleep((caddr_t)inp, PSOCK|PCATCH, "ipsecnotify", 30*hz); - DPRINTF(("check_ipsec: sleep %d\n", error)); - - if (error && error != EWOULDBLOCK) - break; - - /* - * A Key Management daemon returned an apropriate SA back - * to the kernel, the kernel noted that state in the waiting - * socket. - */ - if (inp->inp_secresult == SR_SUCCESS) - return (0); - - /* - * Key Management returned a permanent failure, we do not - * need to retry again. - * - * XXX when more than one key management daemon is available - * XXX we can not do that. - */ - if (inp->inp_secresult == SR_FAILED) - break; - - i++; } - return (error ? error : EWOULDBLOCK); -} - -/* - * Add an inpcb to the list of inpcb which reference this tdb directly. - */ -void -tdb_add_inp(struct tdb *tdb, struct inpcb *inp) -{ - int s = spltdb(); - - if (inp->inp_tdb) - { - if (inp->inp_tdb == tdb) - { - splx(s); - return; - } - - TAILQ_REMOVE(&inp->inp_tdb->tdb_inp, inp, inp_tdb_next); - } + if (i % 4 != 0) + hash ^= val32 * mult2; - inp->inp_tdb = tdb; - TAILQ_INSERT_TAIL(&tdb->tdb_inp, inp, inp_tdb_next); - splx(s); + shift = ffs(tdb_hashmask + 1); + while ((hash & ~tdb_hashmask) != 0) + hash = (hash >> shift) ^ (hash & tdb_hashmask); - DPRINTF(("tdb_add_inp: tdb: %p, inp: %p\n", tdb, inp)); + return hash; } /* - * Reserve an SPI; the SA is not valid yet though. We use SPI_LOCAL_USE as - * an error return value. It'll not be a problem that we also use that - * for demand-keying as that is manually specified. + * Reserve an SPI; the SA is not valid yet though. We use 0 as + * an error return value. */ u_int32_t reserve_spi(u_int32_t sspi, u_int32_t tspi, union sockaddr_union *src, @@ -460,7 +211,7 @@ reserve_spi(u_int32_t sspi, u_int32_t tspi, union sockaddr_union *src, if (sspi == tspi) /* Asking for a specific SPI */ nums = 1; else - nums = 100; /* XXX figure out some good value */ + nums = 100; /* Arbitrarily chosen */ while (nums--) { @@ -473,8 +224,7 @@ reserve_spi(u_int32_t sspi, u_int32_t tspi, union sockaddr_union *src, } /* Don't allocate reserved SPIs. */ - if (spi == SPI_LOCAL_USE || - (spi >= SPI_RESERVED_MIN && spi <= SPI_RESERVED_MAX)) + if (spi >= SPI_RESERVED_MIN && spi <= SPI_RESERVED_MAX) continue; else spi = htonl(spi); @@ -516,44 +266,6 @@ reserve_spi(u_int32_t sspi, u_int32_t tspi, union sockaddr_union *src, } /* - * Our hashing function needs to stir things with a non-zero random multiplier - * so we cannot be DoS-attacked via choosing of the data to hash. - */ -INLINE int -tdb_hash(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto) -{ - static u_int32_t mult1 = 0, mult2 = 0; - u_int8_t *ptr = (u_int8_t *) dst; - int i, shift; - u_int64_t hash; - int val32 = 0; - - while (mult1 == 0) - mult1 = arc4random(); - while (mult2 == 0) - mult2 = arc4random(); - - hash = (spi ^ proto) * mult1; - for (i = 0; i < SA_LEN(&dst->sa); i++) - { - val32 = (val32 << 8) | ptr[i]; - if (i % 4 == 3) - { - hash ^= val32 * mult2; - val32 = 0; - } - } - if (i % 4 != 0) - hash ^= val32 * mult2; - - shift = ffs(tdb_hashmask + 1); - while ((hash & ~tdb_hashmask) != 0) - hash = (hash >> shift) ^ (hash & tdb_hashmask); - - return hash; -} - -/* * An IPSP SAID is really the concatenation of the SPI found in the * packet, the destination address of the packet and the IPsec protocol. * When we receive an IPSP packet, we need to look up its tunnel descriptor @@ -567,40 +279,6 @@ gettdb(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto) { u_int32_t hashval; struct tdb *tdbp; - int s; - - if (spi == 0 && proto == 0) - { - /* - * tdb_bypass; a placeholder for bypass flows, allocate on - * first pass. - */ - if (tdb_bypass == NULL) - { - s = spltdb(); - MALLOC(tdb_bypass, struct tdb *, sizeof(struct tdb), M_TDB, - M_WAITOK); - tdb_count++; - splx(s); - - bzero(tdb_bypass, sizeof(struct tdb)); - tdb_bypass->tdb_satype = SADB_X_SATYPE_BYPASS; - tdb_bypass->tdb_established = time.tv_sec; - tdb_bypass->tdb_epoch = kernfs_epoch - 1; - tdb_bypass->tdb_flags = 0; - -#ifdef INET - tdb_bypass->tdb_dst.sa.sa_family = AF_INET; -#elif INET6 - tdb_bypass->tdb_dst.sa.sa_family = AF_INET6; -#endif - - TAILQ_INIT(&tdb_bypass->tdb_bind_in); - TAILQ_INIT(&tdb_bypass->tdb_inp); - } - - return tdb_bypass; - } if (tdbh == NULL) return (struct tdb *) NULL; @@ -616,6 +294,96 @@ gettdb(u_int32_t spi, union sockaddr_union *dst, u_int8_t proto) return tdbp; } +/* + * Get an SA given the remote address, the security protocol type, and + * the desired IDs. + */ +struct tdb * +gettdbbyaddr(union sockaddr_union *dst, u_int8_t proto, struct mbuf *m, int af) +{ + u_int32_t hashval; + struct tdb *tdbp; + + if (tdbaddr == NULL) + return (struct tdb *) NULL; + + hashval = tdb_hash(0, dst, proto); + + for (tdbp = tdbaddr[hashval]; tdbp != NULL; tdbp = tdbp->tdb_anext) + if ((tdbp->tdb_sproto == proto) && + ((tdbp->tdb_flags & TDBF_INVALID) == 0) && + (!bcmp(&tdbp->tdb_dst, dst, SA_LEN(&dst->sa)))) + { + /* + * If the IDs are not set, this was probably a manually-keyed + * SA, so it can be used for any type of traffic. + */ + if ((tdbp->tdb_srcid == NULL) && (tdbp->tdb_dstid == NULL)) + break; + + /* Not sure how to deal with half-set IDs...just skip the SA */ + if ((tdbp->tdb_srcid == NULL) || (tdbp->tdb_dstid == NULL)) + continue; + + /* We only grok addresses */ + if (((tdbp->tdb_srcid_type != SADB_IDENTTYPE_PREFIX) && + (tdbp->tdb_dstid_type != SADB_IDENTTYPE_CONNECTION)) || + ((tdbp->tdb_dstid_type != SADB_IDENTTYPE_PREFIX) && + (tdbp->tdb_dstid_type != SADB_IDENTTYPE_CONNECTION))) + continue; + + /* XXX Check the IDs ? */ + break; + } + + return tdbp; +} + +/* + * Get an SA given the source address, the security protocol type, and + * the desired IDs. + */ +struct tdb * +gettdbbysrc(union sockaddr_union *src, u_int8_t proto, struct mbuf *m, int af) +{ + u_int32_t hashval; + struct tdb *tdbp; + + if (tdbsrc == NULL) + return (struct tdb *) NULL; + + hashval = tdb_hash(0, src, proto); + + for (tdbp = tdbsrc[hashval]; tdbp != NULL; tdbp = tdbp->tdb_snext) + if ((tdbp->tdb_sproto == proto) && + ((tdbp->tdb_flags & TDBF_INVALID) == 0) && + (!bcmp(&tdbp->tdb_src, src, SA_LEN(&src->sa)))) + { + /* + * If the IDs are not set, this was probably a manually-keyed + * SA, so it can be used for any type of traffic. + */ + if ((tdbp->tdb_srcid == NULL) && (tdbp->tdb_dstid == NULL)) + break; + + /* Not sure how to deal with half-set IDs...just skip the SA */ + if ((tdbp->tdb_srcid == NULL) || (tdbp->tdb_dstid == NULL)) + continue; + + /* We only grok addresses */ + if (((tdbp->tdb_srcid_type != SADB_IDENTTYPE_PREFIX) && + (tdbp->tdb_dstid_type != SADB_IDENTTYPE_CONNECTION)) || + ((tdbp->tdb_dstid_type != SADB_IDENTTYPE_PREFIX) && + (tdbp->tdb_dstid_type != SADB_IDENTTYPE_CONNECTION))) + continue; + + /* XXX Check the IDs ? */ + break; + } + + return tdbp; +} + #if DDB void tdb_hashstats(void) @@ -658,27 +426,15 @@ tdb_walk(int (*walker)(struct tdb *, void *), void *arg) return ENOENT; for (i = 0; i <= tdb_hashmask; i++) - for (tdbp = tdbh[i]; rval == 0 && tdbp != NULL; tdbp = next) - { - next = tdbp->tdb_hnext; - rval = walker(tdbp, (void *)arg); - } + for (tdbp = tdbh[i]; rval == 0 && tdbp != NULL; tdbp = next) + { + next = tdbp->tdb_hnext; + rval = walker(tdbp, (void *)arg); + } return rval; } -struct flow * -get_flow(void) -{ - struct flow *flow; - - MALLOC(flow, struct flow *, sizeof(struct flow), M_TDB, M_WAITOK); - bzero(flow, sizeof(struct flow)); - - ipsec_in_use++; - return flow; -} - /* * Called at splsoftclock(). */ @@ -695,18 +451,23 @@ handle_expirations(void *arg) if ((tdb->tdb_flags & TDBF_TIMER) && (tdb->tdb_exp_timeout <= time.tv_sec)) { - /* If it's an "invalid" TDB, do a silent expiration */ - if (!(tdb->tdb_flags & TDBF_INVALID)) + /* + * If it's an "invalid" TDB or one that hasn't been used + * before, do a silent expiration. + */ + if ((!(tdb->tdb_flags & TDBF_INVALID)) && tdb->tdb_first_use) pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, 0); + tdb_delete(tdb, 0); continue; } else if ((tdb->tdb_flags & TDBF_FIRSTUSE) && (tdb->tdb_first_use + tdb->tdb_exp_first_use <= time.tv_sec)) { - pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); - tdb_delete(tdb, 0, 0); + /* If the TDB hasn't been used, don't renew it */ + if (tdb->tdb_first_use != 0) + pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_HARD); + tdb_delete(tdb, 0); continue; } @@ -714,7 +475,9 @@ handle_expirations(void *arg) if ((tdb->tdb_flags & TDBF_SOFT_TIMER) && (tdb->tdb_soft_timeout <= time.tv_sec)) { - pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); + /* If the TDB hasn't been used, don't renew it */ + if (tdb->tdb_first_use != 0) + pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); tdb->tdb_flags &= ~TDBF_SOFT_TIMER; tdb_expiration(tdb, TDBEXP_EARLY); } @@ -723,7 +486,9 @@ handle_expirations(void *arg) (tdb->tdb_first_use + tdb->tdb_soft_first_use <= time.tv_sec)) { - pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); + /* If the TDB hasn't been used, don't renew it */ + if (tdb->tdb_first_use != 0) + pfkeyv2_expire(tdb, SADB_EXT_LIFETIME_SOFT); tdb->tdb_flags &= ~TDBF_SOFT_FIRSTUSE; tdb_expiration(tdb, TDBEXP_EARLY); } @@ -746,37 +511,27 @@ tdb_expiration(struct tdb *tdb, int flags) u_int64_t next_timeout = 0; int s = spltdb(); - /* - * If this is the local use SPI, this is an SPD entry, so don't setup any - * timers. - */ - if (ntohl(tdb->tdb_spi) == SPI_LOCAL_USE) - { - splx(s); - return; - } - /* Find the earliest expiration. */ if ((tdb->tdb_flags & TDBF_FIRSTUSE) && tdb->tdb_first_use != 0 && (next_timeout == 0 || next_timeout > tdb->tdb_first_use + tdb->tdb_exp_first_use)) - next_timeout = tdb->tdb_first_use + tdb->tdb_exp_first_use; + next_timeout = tdb->tdb_first_use + tdb->tdb_exp_first_use; if ((tdb->tdb_flags & TDBF_SOFT_FIRSTUSE) && tdb->tdb_first_use != 0 && (next_timeout == 0 || next_timeout > tdb->tdb_first_use + tdb->tdb_soft_first_use)) - next_timeout = tdb->tdb_first_use + tdb->tdb_soft_first_use; + next_timeout = tdb->tdb_first_use + tdb->tdb_soft_first_use; if ((tdb->tdb_flags & TDBF_TIMER) && (next_timeout == 0 || next_timeout > tdb->tdb_exp_timeout)) - next_timeout = tdb->tdb_exp_timeout; + next_timeout = tdb->tdb_exp_timeout; if ((tdb->tdb_flags & TDBF_SOFT_TIMER) && (next_timeout == 0 || next_timeout > tdb->tdb_soft_timeout)) - next_timeout = tdb->tdb_soft_timeout; + next_timeout = tdb->tdb_soft_timeout; /* No change? */ if (next_timeout == tdb->tdb_timeout) { - splx(s); - return; + splx(s); + return; } /* @@ -824,6 +579,7 @@ tdb_expiration(struct tdb *tdb, int flags) TAILQ_REMOVE(&expclusterlist, tdb, tdb_expnext); tdb->tdb_expnext.tqe_prev = NULL; } + TAILQ_REMOVE(&explist, tdb, tdb_explink); } @@ -831,8 +587,8 @@ tdb_expiration(struct tdb *tdb, int flags) if (next_timeout == 0) { - splx(s); - return; + splx(s); + return; } /* @@ -902,22 +658,26 @@ tdb_expiration(struct tdb *tdb, int flags) "expclusterlist last link out of order (%p, %p)", tdb, t); } + t = TAILQ_FIRST(&explist); if (t != NULL && t->tdb_timeout > tdb->tdb_timeout) panic("tdb_expiration: explist first link out of order (%p, %p)", tdb, t); + t = TAILQ_PREV(tdb, explist_head, tdb_explink); if (t != NULL && t->tdb_timeout > tdb->tdb_timeout) panic("tdb_expiration: explist prev link out of order (%p, %p)", tdb, t); else if (t == NULL && tdb != TAILQ_FIRST(&explist)) panic("tdb_expiration: explist first link out of order (%p, %p)", tdb, TAILQ_FIRST(&explist)); + t = TAILQ_NEXT(tdb, tdb_explink); if (t != NULL && t->tdb_timeout < tdb->tdb_timeout) panic("tdb_expiration: explist next link out of order (%p, %p)", tdb, t); else if (t == NULL && tdb != TAILQ_LAST(&explist, explist_head)) panic("tdb_expiration: explist last link out of order (%p, %p)", tdb, TAILQ_LAST(&explist, explist_head)); + t = TAILQ_LAST(&explist, explist_head); if (t != tdb && t->tdb_timeout < tdb->tdb_timeout) panic("tdb_expiration: explist last link out of order (%p, %p)", tdb, t); @@ -927,86 +687,64 @@ tdb_expiration(struct tdb *tdb, int flags) } /* - * Caller is responsible for setting at least spltdb(). - */ -struct flow * -find_flow(union sockaddr_union *src, union sockaddr_union *srcmask, - union sockaddr_union *dst, union sockaddr_union *dstmask, - u_int8_t proto, struct tdb *tdb, int ingress) -{ - struct flow *flow; - - if (ingress) - flow = tdb->tdb_access; - else - flow = tdb->tdb_flow; - - for (; flow; flow = flow->flow_next) - if (!bcmp(&src->sa, &flow->flow_src.sa, SA_LEN(&src->sa)) && - !bcmp(&dst->sa, &flow->flow_dst.sa, SA_LEN(&dst->sa)) && - !bcmp(&srcmask->sa, &flow->flow_srcmask.sa, SA_LEN(&srcmask->sa)) && - !bcmp(&dstmask->sa, &flow->flow_dstmask.sa, SA_LEN(&dstmask->sa)) && - (proto == flow->flow_proto)) - return flow; - - return (struct flow *) NULL; -} - -/* - * Caller is responsible for setting at least spltdb(). - */ -struct flow * -find_global_flow(union sockaddr_union *src, union sockaddr_union *srcmask, - union sockaddr_union *dst, union sockaddr_union *dstmask, - u_int8_t proto) -{ - struct flow *flow; - struct tdb *tdb; - int i; - - if (tdbh == NULL) - return (struct flow *) NULL; - - if (tdb_bypass != NULL) - if ((flow = find_flow(src, srcmask, dst, dstmask, proto, - tdb_bypass, FLOW_EGRESS)) != (struct flow *) NULL) - return flow; - - for (i = 0; i <= tdb_hashmask; i++) - { - for (tdb = tdbh[i]; tdb != NULL; tdb = tdb->tdb_hnext) - if ((flow = find_flow(src, srcmask, dst, dstmask, proto, - tdb, FLOW_EGRESS)) != (struct flow *) NULL) - return flow; - } - return (struct flow *) NULL; -} - -/* * Caller is responsible for spltdb(). */ void tdb_rehash(void) { - struct tdb **new_tdbh, *tdbp, *tdbnp; + struct tdb **new_tdbh, **new_tdbaddr, **new_srcaddr, *tdbp, *tdbnp; u_int i, old_hashmask = tdb_hashmask; u_int32_t hashval; tdb_hashmask = (tdb_hashmask << 1) | 1; + MALLOC(new_tdbh, struct tdb **, sizeof(struct tdb *) * (tdb_hashmask + 1), M_TDB, M_WAITOK); + MALLOC(new_tdbaddr, struct tdb **, + sizeof(struct tdb *) * (tdb_hashmask + 1), M_TDB, M_WAITOK); + MALLOC(new_srcaddr, struct tdb **, + sizeof(struct tdb *) * (tdb_hashmask + 1), M_TDB, M_WAITOK); + bzero(new_tdbh, sizeof(struct tdb *) * (tdb_hashmask + 1)); + bzero(new_tdbaddr, sizeof(struct tdb *) * (tdb_hashmask + 1)); + bzero(new_srcaddr, sizeof(struct tdb *) * (tdb_hashmask + 1)); + for (i = 0; i <= old_hashmask; i++) - for (tdbp = tdbh[i]; tdbp != NULL; tdbp = tdbnp) - { - tdbnp = tdbp->tdb_hnext; - hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto); - tdbp->tdb_hnext = new_tdbh[hashval]; - new_tdbh[hashval] = tdbp; - } + { + for (tdbp = tdbh[i]; tdbp != NULL; tdbp = tdbnp) + { + tdbnp = tdbp->tdb_hnext; + hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, + tdbp->tdb_sproto); + tdbp->tdb_hnext = new_tdbh[hashval]; + new_tdbh[hashval] = tdbp; + } + + for (tdbp = tdbaddr[i]; tdbp != NULL; tdbp = tdbnp) + { + tdbnp = tdbp->tdb_anext; + hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto); + tdbp->tdb_anext = new_tdbaddr[hashval]; + new_tdbaddr[hashval] = tdbp; + } + + for (tdbp = tdbsrc[i]; tdbp != NULL; tdbp = tdbnp) + { + tdbnp = tdbp->tdb_snext; + hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto); + tdbp->tdb_snext = new_srcaddr[hashval]; + new_srcaddr[hashval] = tdbp; + } + } FREE(tdbh, M_TDB); tdbh = new_tdbh; + + FREE(tdbaddr, M_TDB); + tdbaddr = new_tdbaddr; + + FREE(tdbsrc, M_TDB); + tdbsrc = new_srcaddr; } /* @@ -1022,7 +760,16 @@ puttdb(struct tdb *tdbp) { MALLOC(tdbh, struct tdb **, sizeof(struct tdb *) * (tdb_hashmask + 1), M_TDB, M_WAITOK); + MALLOC(tdbaddr, struct tdb **, + sizeof(struct tdb *) * (tdb_hashmask + 1), + M_TDB, M_WAITOK); + MALLOC(tdbsrc, struct tdb **, + sizeof(struct tdb *) * (tdb_hashmask + 1), + M_TDB, M_WAITOK); + bzero(tdbh, sizeof(struct tdb *) * (tdb_hashmask + 1)); + bzero(tdbaddr, sizeof(struct tdb *) * (tdb_hashmask + 1)); + bzero(tdbsrc, sizeof(struct tdb *) * (tdb_hashmask + 1)); } hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto); @@ -1040,82 +787,37 @@ puttdb(struct tdb *tdbp) tdb_rehash(); hashval = tdb_hash(tdbp->tdb_spi, &tdbp->tdb_dst, tdbp->tdb_sproto); } + tdbp->tdb_hnext = tdbh[hashval]; tdbh[hashval] = tdbp; - tdb_count++; - splx(s); -} -/* - * Caller is responsible for setting at least spltdb(). - */ -void -put_flow(struct flow *flow, struct tdb *tdb, int ingress) -{ - if (ingress) - { - flow->flow_next = tdb->tdb_access; - tdb->tdb_access = flow; - } - else - { - flow->flow_next = tdb->tdb_flow; - tdb->tdb_flow = flow; - } + hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto); + tdbp->tdb_anext = tdbaddr[hashval]; + tdbaddr[hashval] = tdbp; - if (flow->flow_next) - flow->flow_next->flow_prev = flow; + hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto); + tdbp->tdb_snext = tdbsrc[hashval]; + tdbsrc[hashval] = tdbp; - flow->flow_sa = tdb; - flow->flow_prev = (struct flow *) NULL; -} - -/* - * Caller is responsible for setting at least spltdb(). - */ -void -delete_flow(struct flow *flow, struct tdb *tdb, int ingress) -{ - if (tdb) - { - if (ingress && (tdb->tdb_access == flow)) - tdb->tdb_access = flow->flow_next; - else - if (!ingress && (tdb->tdb_flow == flow)) - tdb->tdb_flow = flow->flow_next; - - if (flow->flow_prev) - flow->flow_prev->flow_next = flow->flow_next; - - if (flow->flow_next) - flow->flow_next->flow_prev = flow->flow_prev; - } + tdb_count++; - if (!ingress) - ipsec_in_use--; + ipsec_last_added = time.tv_sec; - FREE(flow, M_TDB); + splx(s); } /* * Caller is responsible to set at least spltdb(). */ void -tdb_delete(struct tdb *tdbp, int delchain, int expflags) +tdb_delete(struct tdb *tdbp, int expflags) { - struct tdb *tdbpp, *tdbpn; + struct ipsec_policy *ipo; + struct tdb *tdbpp; struct inpcb *inp; - u_int32_t hashval = tdbp->tdb_sproto + tdbp->tdb_spi; + u_int32_t hashval; int s; - /* When deleting the bypass tdb, skip the hash table code. */ - if (tdbp == tdb_bypass && tdbp != NULL) - { - s = spltdb(); - delchain = 0; - goto skip_hash; - } - if (tdbh == NULL) return; @@ -1138,122 +840,44 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) tdbp->tdb_hnext = NULL; - skip_hash: - /* - * If there was something before us in the chain pointing to us, - * make it point nowhere. - */ - if ((tdbp->tdb_inext) && - (tdbp->tdb_inext->tdb_onext == tdbp)) - tdbp->tdb_inext->tdb_onext = NULL; - - /* - * If there was something after us in the chain pointing to us, - * make it point nowhere. - */ - if ((tdbp->tdb_onext) && - (tdbp->tdb_onext->tdb_inext == tdbp)) - tdbp->tdb_onext->tdb_inext = NULL; - - tdbpn = tdbp->tdb_onext; - tdbp->tdb_inext = tdbp->tdb_onext = NULL; + hashval = tdb_hash(0, &tdbp->tdb_dst, tdbp->tdb_sproto); - if (tdbp->tdb_xform) + if (tdbaddr[hashval] == tdbp) { - (*(tdbp->tdb_xform->xf_zeroize))(tdbp); - tdbp->tdb_xform = NULL; + tdbpp = tdbp; + tdbaddr[hashval] = tdbp->tdb_anext; } + else + for (tdbpp = tdbaddr[hashval]; tdbpp != NULL; tdbpp = tdbpp->tdb_anext) + if (tdbpp->tdb_anext == tdbp) + { + tdbpp->tdb_anext = tdbp->tdb_anext; + tdbpp = tdbp; + break; + } - while (tdbp->tdb_access) - delete_flow(tdbp->tdb_access, tdbp, FLOW_INGRESS); + hashval = tdb_hash(0, &tdbp->tdb_src, tdbp->tdb_sproto); - while (tdbp->tdb_flow) + if (tdbsrc[hashval] == tdbp) { - /* Delete the flow and the routing entry that goes with it. */ - struct sockaddr_encap encapdst, encapnetmask; - - bzero(&encapdst, sizeof(struct sockaddr_encap)); - bzero(&encapnetmask, sizeof(struct sockaddr_encap)); - - encapdst.sen_family = PF_KEY; - encapnetmask.sen_family = PF_KEY; - - switch (tdbp->tdb_flow->flow_src.sa.sa_family) + tdbpp = tdbp; + tdbsrc[hashval] = tdbp->tdb_snext; + } + else + for (tdbpp = tdbsrc[hashval]; tdbpp != NULL; tdbpp = tdbpp->tdb_snext) + if (tdbpp->tdb_snext == tdbp) { - case AF_INET: - encapdst.sen_len = SENT_IP4_LEN; - encapdst.sen_type = SENT_IP4; - encapdst.sen_ip_src = tdbp->tdb_flow->flow_src.sin.sin_addr; - encapdst.sen_ip_dst = tdbp->tdb_flow->flow_dst.sin.sin_addr; - encapdst.sen_proto = tdbp->tdb_flow->flow_proto; - encapdst.sen_sport = tdbp->tdb_flow->flow_src.sin.sin_port; - encapdst.sen_dport = tdbp->tdb_flow->flow_dst.sin.sin_port; - - encapnetmask.sen_ip_src = tdbp->tdb_flow->flow_srcmask.sin.sin_addr; - encapnetmask.sen_ip_dst = tdbp->tdb_flow->flow_dstmask.sin.sin_addr; - - /* Mask transport protocol and ports if applicable */ - if (tdbp->tdb_flow->flow_proto) - { - encapnetmask.sen_proto = 0xff; - if (tdbp->tdb_flow->flow_src.sin.sin_port) - encapnetmask.sen_sport = 0xffff; - if (tdbp->tdb_flow->flow_dst.sin.sin_port) - encapnetmask.sen_dport = 0xffff; - } - break; - -#if INET6 - case AF_INET6: - encapdst.sen_len = SENT_IP6_LEN; - encapdst.sen_type = SENT_IP6; - encapdst.sen_ip6_src = tdbp->tdb_flow->flow_src.sin6.sin6_addr; - encapdst.sen_ip6_dst = tdbp->tdb_flow->flow_dst.sin6.sin6_addr; - encapdst.sen_ip6_proto = tdbp->tdb_flow->flow_proto; - encapdst.sen_ip6_sport = tdbp->tdb_flow->flow_src.sin6.sin6_port; - encapdst.sen_ip6_dport = tdbp->tdb_flow->flow_dst.sin6.sin6_port; - - encapnetmask.sen_ip6_src = tdbp->tdb_flow->flow_srcmask.sin6.sin6_addr; - encapnetmask.sen_ip6_dst = tdbp->tdb_flow->flow_dstmask.sin6.sin6_addr; - - /* Mask transport protocol and ports if applicable */ - if (tdbp->tdb_flow->flow_proto) - { - encapnetmask.sen_ip6_proto = 0xff; - if (tdbp->tdb_flow->flow_src.sin6.sin6_port) - encapnetmask.sen_ip6_sport = 0xffff; - if (tdbp->tdb_flow->flow_dst.sin6.sin6_port) - encapnetmask.sen_ip6_dport = 0xffff; - } - break; -#endif /* INET6 */ - - default: -#ifdef DIAGNOSTIC - panic("tdb_delete(): SA %s/%08x/%d has flow of unknown type %d", ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi), tdbp->tdb_sproto, tdbp->tdb_flow->flow_src.sa.sa_family); -#endif /* DIAGNOSTIC */ - delete_flow(tdbp->tdb_flow, tdbp, FLOW_EGRESS); - continue; + tdbpp->tdb_snext = tdbp->tdb_snext; + tdbpp = tdbp; + break; } - /* Always the same type for address and netmask */ - encapnetmask.sen_len = encapdst.sen_len; - encapnetmask.sen_type = encapdst.sen_type; - - rtrequest(RTM_DELETE, (struct sockaddr *) &encapdst, - (struct sockaddr *) 0, - (struct sockaddr *) &encapnetmask, - 0, (struct rtentry **) 0); + tdbp->tdb_snext = NULL; - delete_flow(tdbp->tdb_flow, tdbp, FLOW_EGRESS); - } - - /* Cleanup SA-Bindings */ - for (tdbpp = TAILQ_FIRST(&tdbp->tdb_bind_in); tdbpp; - tdbpp = TAILQ_FIRST(&tdbp->tdb_bind_in)) + if (tdbp->tdb_xform) { - TAILQ_REMOVE(&tdbpp->tdb_bind_in, tdbpp, tdb_bind_in_next); - tdbpp->tdb_bind_out = NULL; + (*(tdbp->tdb_xform->xf_zeroize))(tdbp); + tdbp->tdb_xform = NULL; } /* Cleanup inp references */ @@ -1264,8 +888,13 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) inp->inp_tdb = NULL; } - if (tdbp->tdb_bind_out) - TAILQ_REMOVE(&tdbp->tdb_bind_out->tdb_bind_in, tdbp, tdb_bind_in_next); + /* Cleanup SPD references */ + for (ipo = TAILQ_FIRST(&tdbp->tdb_policy_head); ipo; + ipo = TAILQ_FIRST(&tdbp->tdb_policy_head)) + { + TAILQ_REMOVE(&tdbp->tdb_policy_head, ipo, ipo_tdb_next); + ipo->ipo_tdb = NULL; + } /* Remove us from the expiration lists. */ if (tdbp->tdb_timeout != 0) @@ -1287,16 +916,9 @@ tdb_delete(struct tdb *tdbp, int delchain, int expflags) tdbp->tdb_dstid = NULL; } - /* If we're deleting the bypass tdb, reset the variable. */ - if (tdbp == tdb_bypass) - tdb_bypass = NULL; - FREE(tdbp, M_TDB); tdb_count--; - if (delchain && tdbpn) - tdb_delete(tdbpn, delchain, expflags); - splx(s); } @@ -1313,9 +935,10 @@ tdb_init(struct tdb *tdbp, u_int16_t alg, struct ipsecinit *ii) tdbp->tdb_epoch = kernfs_epoch - 1; /* Init Incoming SA-Binding Queues */ - TAILQ_INIT(&tdbp->tdb_bind_in); TAILQ_INIT(&tdbp->tdb_inp); + TAILQ_INIT(&tdbp->tdb_policy_head); + for (xsp = xformsw; xsp < xformswNXFORMSW; xsp++) if (xsp->xf_type == alg) return (*(xsp->xf_init))(tdbp, xsp, ii); @@ -1334,20 +957,26 @@ int ipsp_kern(int off, char **bufp, int len) { static char buffer[IPSEC_KERNFS_BUFSIZE]; - struct flow *flow; - struct tdb *tdb, *tdbp; + struct tdb *tdb; int l, i, s; - if (off == 0) - kernfs_epoch++; - - if (bufp == NULL || tdbh == NULL) + if (bufp == NULL) return 0; bzero(buffer, IPSEC_KERNFS_BUFSIZE); - *bufp = buffer; + + if (off == 0) + { + kernfs_epoch++; + l = sprintf(buffer, "Hashmask: %d, policy entries: %d\n", tdb_hashmask, + ipsec_in_use); + return l; + } + if (tdbh == NULL) + return 0; + for (i = 0; i <= tdb_hashmask; i++) { s = spltdb(); @@ -1478,49 +1107,6 @@ ipsp_kern(int off, char **bufp, int len) l += sprintf(buffer + l, "\tAssociated interface = <%s>\n", ((struct ifnet *) tdb->tdb_interface)->if_xname); - if (tdb->tdb_bind_out) - l += sprintf(buffer + l, - "\tBound SA: SPI = %08x, " - "Destination = %s, Sproto = %u\n", - ntohl(tdb->tdb_bind_out->tdb_spi), - ipsp_address(tdb->tdb_bind_out->tdb_dst), - tdb->tdb_bind_out->tdb_sproto); - for (i = 0, tdbp = TAILQ_FIRST(&tdb->tdb_bind_in); tdbp; - tdbp = TAILQ_NEXT(tdbp, tdb_bind_in_next)) - i++; - - if (i > 0) - l += sprintf(buffer + l, - "\tReferenced by %d incoming SA%s\n", - i, i == 1 ? "" : "s"); - - if (tdb->tdb_onext) - l += sprintf(buffer + l, - "\tNext SA: SPI = %08x, " - "Destination = %s, Sproto = %u\n", - ntohl(tdb->tdb_onext->tdb_spi), - ipsp_address(tdb->tdb_onext->tdb_dst), - tdb->tdb_onext->tdb_sproto); - - if (tdb->tdb_inext) - l += sprintf(buffer + l, - "\tPrevious SA: SPI = %08x, " - "Destination = %s, Sproto = %u\n", - ntohl(tdb->tdb_inext->tdb_spi), - ipsp_address(tdb->tdb_inext->tdb_dst), - tdb->tdb_inext->tdb_sproto); - - for (i = 0, flow = tdb->tdb_flow; flow; flow = flow->flow_next) - i++; - - l+= sprintf(buffer + l, "\tCurrently used by %d flows\n", i); - - for (i = 0, flow = tdb->tdb_access; flow; flow = flow->flow_next) - i++; - - l+= sprintf(buffer + l, - "\t%d ingress flows specified\n", i); - l += sprintf(buffer + l, "\t%u flows have used this SA\n", tdb->tdb_cur_allocations); @@ -1603,696 +1189,87 @@ ipsp_kern(int off, char **bufp, int len) return 0; } -char * -inet_ntoa4(struct in_addr ina) -{ - static char buf[4][4 * sizeof "123" + 4]; - unsigned char *ucp = (unsigned char *) &ina; - static int i = 3; - - i = (i + 1) % 4; - sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, - ucp[2] & 0xff, ucp[3] & 0xff); - return (buf[i]); -} - -char * -ipsp_address(union sockaddr_union sa) -{ - switch (sa.sa.sa_family) - { - case AF_INET: - return inet_ntoa4(sa.sin.sin_addr); - -#if INET6 - case AF_INET6: - return ip6_sprintf(&sa.sin6.sin6_addr); -#endif /* INET6 */ - - default: - 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. + * Check which transformations are required. */ -int -ipsp_process_packet(struct mbuf *m, struct tdb *tdb, int af, int tunalready) +u_int8_t +get_sa_require(struct inpcb *inp) { - int i, off, error; - struct mbuf *mp; - -#ifdef INET - struct ip *ip; -#endif /* INET */ -#ifdef INET6 - struct ip6_hdr *ip6; -#endif /* INET6 */ - - /* Check that the transform is allowed by the administrator */ - if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) || - (tdb->tdb_sproto == IPPROTO_AH && !ah_enable)) - { - DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy\n")); - m_freem(m); - return EHOSTUNREACH; - } - - /* Sanity check */ - if (!tdb->tdb_xform) - { - DPRINTF(("ipsp_process_packet(): uninitialized TDB\n")); - m_freem(m); - return EHOSTUNREACH; - } - - /* 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; - } - - /* Check that the network protocol is supported */ - switch (tdb->tdb_dst.sa.sa_family) - { -#ifdef INET - case AF_INET: - break; -#endif /* INET */ - -#ifdef INET6 - case AF_INET6: - break; -#endif /* INET6 */ - - default: - 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; - } - - /* Register first use if applicable, setup relevant 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. - * When doing Ethernet-over-IP, we are handed an already-encapsulated - * frame, so we don't need to re-encapsulate. - */ - if (tunalready == 0) + u_int8_t sareq = 0; + + if (inp != NULL) { - /* - * If the target protocol family is different, we know we'll be - * doing tunneling. - */ - 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 */ - - /* Bring the network header in the first mbuf */ - 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 */ - } - - /* Do the appropriate encapsulation, if necessary */ - if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ - (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling requested */ - (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */ -#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) - { - if ((m = m_pullup(m, sizeof(struct ip))) == 0) - return ENOBUFS; - - ip = mtod(m, struct ip *); - ip->ip_len = htons(m->m_pkthdr.len); - ip->ip_sum = 0; - ip->ip_sum = in_cksum(m, ip->ip_hl << 2); - } -#endif /* INET */ - -#ifdef INET6 - /* Fix IPv6 header payload length */ - if (af == AF_INET6) - { - if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) - return ENOBUFS; - - if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { - /* no jumbogram support */ - m_freem(m); - return ENXIO; /*?*/ - } - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); - } -#endif /* INET6 */ - - /* Encapsulate -- the last two arguments are unused */ - error = ipip_output(m, tdb, &mp, 0, 0); - if ((mp == NULL) && (!error)) - error = EFAULT; - if (error) - { - if (mp) - { - m_freem(mp); - mp = NULL; - } - - return error; - } - - m = mp; - mp = NULL; - } - - /* We may be done with this TDB */ - if (tdb->tdb_xform->xf_type == XF_IP4) - return ipsp_process_done(m, tdb); + sareq |= inp->inp_seclevel[SL_AUTH] >= IPSEC_LEVEL_USE ? + NOTIFY_SATYPE_AUTH : 0; + sareq |= inp->inp_seclevel[SL_ESP_TRANS] >= IPSEC_LEVEL_USE ? + NOTIFY_SATYPE_CONF : 0; + sareq |= inp->inp_seclevel[SL_ESP_NETWORK] >= IPSEC_LEVEL_USE ? + NOTIFY_SATYPE_TUNNEL : 0; } else { - /* - * If this is just an IP-IP TDB and we're told there's already an - * encapsulation header, move on. - */ - if (tdb->tdb_xform->xf_type == XF_IP4) - return ipsp_process_done(m, tdb); - } - - /* Extract some information off the headers */ - switch (tdb->tdb_dst.sa.sa_family) - { -#ifdef INET - case AF_INET: - ip = mtod(m, struct ip *); - i = ip->ip_hl << 2; - off = offsetof(struct ip, ip_p); - break; -#endif /* INET */ - -#ifdef INET6 - case AF_INET6: - ip6 = mtod(m, struct ip6_hdr *); - i = sizeof(struct ip6_hdr); - off = offsetof(struct ip6_hdr, ip6_nxt); - break; -#endif /* INET6 */ + sareq |= ipsec_auth_default_level >= IPSEC_LEVEL_USE ? + NOTIFY_SATYPE_AUTH : 0; + sareq |= ipsec_esp_trans_default_level >= IPSEC_LEVEL_USE ? + NOTIFY_SATYPE_CONF : 0; + sareq |= ipsec_esp_network_default_level >= IPSEC_LEVEL_USE ? + NOTIFY_SATYPE_TUNNEL : 0; } - - /* Invoke the IPsec transform */ - return (*(tdb->tdb_xform->xf_output))(m, tdb, NULL, i, off); + + return (sareq); } /* - * Called by the IPsec output transform callbacks, to transmit the packet - * or do further processing, as necessary. + * Add an inpcb to the list of inpcb which reference this tdb directly. */ -int -ipsp_process_done(struct mbuf *m, struct tdb *tdb) +void +tdb_add_inp(struct tdb *tdb, struct inpcb *inp) { -#ifdef INET - struct ip *ip; -#endif /* INET */ - -#ifdef INET6 - struct ip6_hdr *ip6; -#endif /* INET6 */ - - switch (tdb->tdb_dst.sa.sa_family) + if (inp->inp_tdb) { -#ifdef INET - case AF_INET: - /* Fix the header length, for AH processing */ - if (tdb->tdb_dst.sa.sa_family == AF_INET) - { - ip = mtod(m, struct ip *); - ip->ip_len = htons(m->m_pkthdr.len); - } - break; -#endif /* INET */ - -#ifdef INET6 - case AF_INET6: - /* Fix the header length, for AH processing */ - if (tdb->tdb_dst.sa.sa_family == AF_INET6) - { - if (m->m_pkthdr.len < sizeof(*ip6)) { - m_freem(m); - return ENXIO; - } - if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { - /* no jumbogram support */ - m_freem(m); - return ENXIO; /*?*/ - } - ip6 = mtod(m, struct ip6_hdr *); - ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); - } - break; -#endif /* INET6 */ + if (inp->inp_tdb == tdb) + return; - default: - m_freem(m); - DPRINTF(("ipsp_process_done(): unknown protocol family (%d)\n", - tdb->tdb_dst.sa.sa_family)); - return ENXIO; + TAILQ_REMOVE(&inp->inp_tdb->tdb_inp, inp, inp_tdb_next); } - /* If there's another TDB to apply, do so. */ - if (tdb->tdb_onext) - return ipsp_process_packet(m, tdb->tdb_onext, - tdb->tdb_onext->tdb_dst.sa.sa_family, 0); - - /* - * If we're done with IPsec processing, transmit the packet using the - * appropriate network protocol (IP or IPv6). - */ - switch (tdb->tdb_dst.sa.sa_family) - { -#ifdef INET - case AF_INET: - NTOHS(ip->ip_len); - NTOHS(ip->ip_off); - - return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, - NULL, NULL); -#endif /* INET */ - -#ifdef INET6 - case AF_INET6: - /* - * we don't need massage, IPv6 header fields are always in - * net endian - */ - return ip6_output(m, NULL, NULL, IPV6_ENCAPSULATED, NULL, NULL); -#endif /* INET6 */ - } + inp->inp_tdb = tdb; + TAILQ_INSERT_TAIL(&tdb->tdb_inp, inp, inp_tdb_next); - /* Not reached */ - return EINVAL; + DPRINTF(("tdb_add_inp: tdb: %p, inp: %p\n", tdb, inp)); } -/* - * Lookup at the SPD based on the headers contained on the mbuf. The second - * argument indicates what protocol family the header at the beginning of - * the mbuf is. hlen is the the offset of the transport protocol header - * in the mbuf. - * - * Return combinations (of return value and in *error): - * - NULL/0 -> no IPsec required on packet - * - NULL/-EINVAL -> silently drop the packet - * - NULL/errno -> drop packet and return error - * or a valid TDB value. - */ -struct tdb * -ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error) +/* Return a printable string for the IPv4 address. */ +char * +inet_ntoa4(struct in_addr ina) { - struct sockaddr_encap *ddst, *gw; - struct route_enc re0, *re = &re0; - union sockaddr_union sunion; - struct tdb *tdb, tdb2; - - /* - * If there are no flows in place, there's no point - * continuing with the SPD lookup. - */ - if (!ipsec_in_use) - { - *error = 0; - return NULL; - } - - bzero((caddr_t) re, sizeof(*re)); - ddst = (struct sockaddr_encap *) &re->re_dst; - ddst->sen_family = PF_KEY; + static char buf[4][4 * sizeof "123" + 4]; + unsigned char *ucp = (unsigned char *) &ina; + static int i = 3; + + i = (i + 1) % 4; + sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, + ucp[2] & 0xff, ucp[3] & 0xff); + return (buf[i]); +} - /* - * Do an SPD lookup -- this code should probably be moved - * to a separate function. - */ - switch (af) +/* Return a printable string for the address. */ +char * +ipsp_address(union sockaddr_union sa) +{ + switch (sa.sa.sa_family) { -#ifdef INET +#if INET case AF_INET: - ddst->sen_len = SENT_IP4_LEN; - ddst->sen_type = SENT_IP4; - m_copydata(m, offsetof(struct ip, ip_src), - sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_src)); - m_copydata(m, offsetof(struct ip, ip_dst), - sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_dst)); - m_copydata(m, offsetof(struct ip, ip_p), sizeof(u_int8_t), - (caddr_t) &(ddst->sen_proto)); - - /* If TCP/UDP, extract the port numbers to use in the lookup */ - switch (ddst->sen_proto) - { - case IPPROTO_UDP: - case IPPROTO_TCP: - /* - * Luckily, the offset of the src/dst ports in both the UDP - * and TCP headers is the same (first two 16-bit values - * in the respective headers), so we can just copy them. - */ - m_copydata(m, hlen, sizeof(u_int16_t), - (caddr_t) &(ddst->sen_sport)); - m_copydata(m, hlen + sizeof(u_int16_t), sizeof(u_int16_t), - (caddr_t) &(ddst->sen_dport)); - break; - - default: - ddst->sen_sport = 0; - ddst->sen_dport = 0; - } - - break; + return inet_ntoa4(sa.sin.sin_addr); #endif /* INET */ -#ifdef INET6 +#if INET6 case AF_INET6: - ddst->sen_len = SENT_IP6_LEN; - ddst->sen_type = SENT_IP6; - m_copydata(m, offsetof(struct ip6_hdr, ip6_src), - sizeof(struct in6_addr), - (caddr_t) &(ddst->sen_ip6_src)); - m_copydata(m, offsetof(struct ip6_hdr, ip6_dst), - sizeof(struct in6_addr), - (caddr_t) &(ddst->sen_ip6_dst)); - m_copydata(m, offsetof(struct ip6_hdr, ip6_nxt), sizeof(u_int8_t), - (caddr_t) &(ddst->sen_ip6_proto)); - - /* If TCP/UDP, extract the port numbers to use in the lookup */ - switch (ddst->sen_ip6_proto) - { - case IPPROTO_UDP: - case IPPROTO_TCP: - /* - * Luckily, the offset of the src/dst ports in both the UDP - * and TCP headers is the same (first two 16-bit values - * in the respective headers), so we can just copy them. - */ - m_copydata(m, hlen, sizeof(u_int16_t), - (caddr_t) &(ddst->sen_ip6_sport)); - m_copydata(m, hlen + sizeof(u_int16_t), sizeof(u_int16_t), - (caddr_t) &(ddst->sen_ip6_dport)); - break; - - default: - ddst->sen_ip6_sport = 0; - ddst->sen_ip6_dport = 0; - } - - break; -#endif /* INET6 */ - - default: - *error = EAFNOSUPPORT; - return NULL; - } - - /* Actual SPD lookup */ - rtalloc((struct route *) re); - if (re->re_rt == NULL) - { - *error = 0; - return NULL; /* Nothing found */ - } - - bzero(&sunion, sizeof(sunion)); - gw = (struct sockaddr_encap *) (re->re_rt->rt_gateway); - - /* Sanity check */ - if (gw == NULL || ((gw->sen_type != SENT_IPSP) && - (gw->sen_type != SENT_IPSP6))) - { - DPRINTF(("ipsp_spd_lookup(): no gw, or gw data not IPSP (%d)\n", - gw->sen_type)); - - if (re->re_rt) - RTFREE(re->re_rt); - *error = EHOSTUNREACH; - return NULL; - } - - /* - * There might be a specific route, that tells us to avoid - * doing IPsec; this is useful for specific routes that we - * don't want to have IPsec applied on, like the key - * management ports. - */ - switch (gw->sen_type) - { -#ifdef INET - case SENT_IPSP: - if ((gw->sen_ipsp_sproto == 0) && (gw->sen_ipsp_spi == 0) && - (gw->sen_ipsp_dst.s_addr == 0)) - { - *error = 0; - return NULL; - } - - sunion.sin.sin_family = AF_INET; - sunion.sin.sin_len = sizeof(struct sockaddr_in); - sunion.sin.sin_addr = gw->sen_ipsp_dst; - tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion, - gw->sen_ipsp_sproto); - break; -#endif /* INET */ - -#ifdef INET6 - case SENT_IPSP6: - if ((gw->sen_ipsp6_sproto == 0) && (gw->sen_ipsp6_spi == 0) && - IN6_IS_ADDR_UNSPECIFIED(&gw->sen_ipsp6_dst)) - { - *error = 0; - return NULL; - } - - sunion.sin6.sin6_family = AF_INET6; - sunion.sin6.sin6_len = sizeof(struct sockaddr_in6); - sunion.sin6.sin6_addr = gw->sen_ipsp6_dst; - tdb = (struct tdb *) gettdb(gw->sen_ipsp6_spi, &sunion, - gw->sen_ipsp6_sproto); - break; + return ip6_sprintf(&sa.sin6.sin6_addr); #endif /* INET6 */ default: - *error = EAFNOSUPPORT; - return NULL; - } - - /* - * At this point we have an IPSP "gateway" (tunnel) spec. - * Use the destination of the tunnel and the SPI to - * look up the necessary Tunnel Control Block. Look it up, - * and then pass it, along with the packet and the gw, - * to the appropriate transformation. - */ - /* tdb lookup is found up there */ - - /* Bypass the SA acquisition if that is what we want. */ - if (tdb && tdb->tdb_satype == SADB_X_SATYPE_BYPASS) - { - *error = 0; - return NULL; - } - - /* - * For VPNs, a route with a reserved SPI is used to - * indicate the need for an SA when none is established. - */ - if (((ntohl(gw->sen_ipsp_spi) == SPI_LOCAL_USE) && - (gw->sen_type == SENT_IPSP)) || - ((ntohl(gw->sen_ipsp6_spi) == SPI_LOCAL_USE) && - (gw->sen_type == SENT_IPSP6))) - { - if (tdb == NULL) - { - /* We will just use system defaults. */ - tdb = &tdb2; - bzero(&tdb2, sizeof(tdb2)); - - /* Default entry is for ESP */ - tdb2.tdb_satype = SADB_SATYPE_ESP; - } - - *error = ipsp_acquire_sa(tdb); - if (*error == 0) - *error = -EINVAL; /* Silently drop the packet */ - return NULL; - } - - /* Couldn't find the TDB */ - if (tdb == NULL) - { -#ifdef INET - if (gw->sen_type == SENT_IPSP) - DPRINTF(("ipsp_spd_lookup(): non-existant TDB for SA %s/%08x/%u\n", - inet_ntoa4(gw->sen_ipsp_dst), ntohl(gw->sen_ipsp_spi), - gw->sen_ipsp_sproto)); -#endif /* INET */ - -#ifdef INET6 - if (gw->sen_type == SENT_IPSP6) - DPRINTF(("ipsp_spd_lookup(): non-existant TDB for SA %s/%08x/%u\n", - ip6_sprintf(&gw->sen_ipsp6_dst), ntohl(gw->sen_ipsp6_spi), - gw->sen_ipsp6_sproto)); -#endif /* INET6 */ - - *error = EHOSTUNREACH; - return NULL; - } - - /* Done, IPsec processing necessary */ - *error = 0; - return tdb; -} - -/* - * Use PFKEY to acquire an SA. - */ -int -ipsp_acquire_sa(struct tdb *tdb) -{ - /* Check whether Perfect Forward Secrect is required */ - if (ipsec_require_pfs) - tdb->tdb_flags |= TDBF_PFS; - else - tdb->tdb_flags &= ~TDBF_PFS; - - /* Initialize expirations */ - if (ipsec_soft_allocations > 0) - tdb->tdb_soft_allocations = ipsec_soft_allocations; - else - tdb->tdb_soft_allocations = 0; - - if (ipsec_exp_allocations > 0) - tdb->tdb_exp_allocations = ipsec_exp_allocations; - else - tdb->tdb_exp_allocations = 0; - - if (ipsec_soft_bytes > 0) - tdb->tdb_soft_bytes = ipsec_soft_bytes; - else - tdb->tdb_soft_bytes = 0; - - if (ipsec_exp_bytes > 0) - tdb->tdb_exp_bytes = ipsec_exp_bytes; - else - tdb->tdb_exp_bytes = 0; - - if (ipsec_soft_timeout > 0) - tdb->tdb_soft_timeout = ipsec_soft_timeout; - else - tdb->tdb_soft_timeout = 0; - - if (ipsec_exp_timeout > 0) - tdb->tdb_exp_timeout = ipsec_exp_timeout; - else - tdb->tdb_exp_timeout = 0; - - if (ipsec_soft_first_use > 0) - tdb->tdb_soft_first_use = ipsec_soft_first_use; - else - tdb->tdb_soft_first_use = 0; - - if (ipsec_exp_first_use > 0) - tdb->tdb_exp_first_use = ipsec_exp_first_use; - else - tdb->tdb_exp_first_use = 0; - - /* - * If we don't have an existing desired encryption - * algorithm, use the default. - */ - if ((tdb->tdb_encalgxform == NULL) && - (tdb->tdb_satype & NOTIFY_SATYPE_CONF)) - { - if (!strncasecmp(ipsec_def_enc, "des", sizeof("des"))) - tdb->tdb_encalgxform = &enc_xform_des; - else - if (!strncasecmp(ipsec_def_enc, "3des", - sizeof("3des"))) - tdb->tdb_encalgxform = &enc_xform_3des; - else - if (!strncasecmp(ipsec_def_enc, "blowfish", - sizeof("blowfish"))) - tdb->tdb_encalgxform = &enc_xform_blf; - else - if (!strncasecmp(ipsec_def_enc, "cast128", - sizeof("cast128"))) - tdb->tdb_encalgxform = &enc_xform_cast5; - else - if (!strncasecmp(ipsec_def_enc, "skipjack", - sizeof("skipjack"))) - tdb->tdb_encalgxform = &enc_xform_skipjack; - } - - /* - * If we don't have an existing desired authentication - * algorithm, use the default. - */ - if ((tdb->tdb_authalgxform == NULL) && - (tdb->tdb_satype & NOTIFY_SATYPE_AUTH)) - { - if (!strncasecmp(ipsec_def_auth, "hmac-md5", - sizeof("hmac-md5"))) - tdb->tdb_authalgxform = &auth_hash_hmac_md5_96; - else - if (!strncasecmp(ipsec_def_auth, "hmac-sha1", - sizeof("hmac-sha1"))) - tdb->tdb_authalgxform = &auth_hash_hmac_sha1_96; - else - if (!strncasecmp(ipsec_def_auth, "hmac-ripemd160", - sizeof("hmac_ripemd160"))) - tdb->tdb_authalgxform = &auth_hash_hmac_ripemd_160_96; + return "(unknown address family)"; } - - /* XXX Initialize src_id/dst_id */ - - /* PF_KEYv2 notification message */ - return pfkeyv2_acquire(tdb, 0); } diff --git a/sys/netinet/ip_ipsp.h b/sys/netinet/ip_ipsp.h index 450c18ee2e1..6c2dc18231d 100644 --- a/sys/netinet/ip_ipsp.h +++ b/sys/netinet/ip_ipsp.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipsp.h,v 1.69 2000/06/18 19:05:49 angelos Exp $ */ +/* $OpenBSD: ip_ipsp.h,v 1.70 2000/09/19 03:20:59 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -6,8 +6,8 @@ * Niels Provos (provos@physnet.uni-hamburg.de) and * Niklas Hallqvist (niklas@appli.se). * - * This code was written by John Ioannidis for BSD/OS in Athens, Greece, - * in November 1995. + * The original version of this code was written by John Ioannidis + * for BSD/OS in Athens, Greece, in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. @@ -55,9 +55,6 @@ union sockaddr_union struct sockaddr_in6 sin6; }; -#define FLOW_EGRESS 0 -#define FLOW_INGRESS 1 - /* HMAC key sizes */ #define MD5HMAC96_KEYSIZE 16 #define SHA1HMAC96_KEYSIZE 20 @@ -91,6 +88,7 @@ union sockaddr_union #define IPSEC_DEFAULT_EXP_FIRST_USE 7200 #define IPSEC_DEFAULT_DEF_ENC "3des" #define IPSEC_DEFAULT_DEF_AUTH "hmac-sha1" +#define IPSEC_DEFAULT_EXPIRE_ACQUIRE 30 struct sockaddr_encap { @@ -99,63 +97,47 @@ struct sockaddr_encap u_int16_t sen_type; /* see SENT_* */ union { - u_int8_t Data[16]; /* other stuff mapped here */ - struct /* SENT_IP4 */ { + u_int8_t Direction; struct in_addr Src; struct in_addr Dst; + u_int8_t Proto; u_int16_t Sport; u_int16_t Dport; - u_int8_t Proto; - u_int8_t Filler[3]; } Sip4; struct /* SENT_IP6 */ { + u_int8_t Direction; struct in6_addr Src; struct in6_addr Dst; + u_int8_t Proto; u_int16_t Sport; u_int16_t Dport; - u_int8_t Proto; - u_int8_t Filler[3]; } Sip6; - struct /* SENT_IPSP */ - { - struct in_addr Dst; - u_int32_t Spi; - u_int8_t Sproto; - u_int8_t Filler[7]; - } Sipsp; - - struct /* SENT_IPSP6 */ - { - struct in6_addr Dst; - u_int32_t Spi; - u_int8_t Sproto; - u_int8_t Filler[7]; - } Sipsp6; + struct ipsec_policy *PolicyHead; /* SENT_IPSP */ } Sen; }; +#define IPSP_DIRECTION_IN 0x1 +#define IPSP_DIRECTION_OUT 0x2 + #define sen_data Sen.Data #define sen_ip_src Sen.Sip4.Src #define sen_ip_dst Sen.Sip4.Dst #define sen_proto Sen.Sip4.Proto #define sen_sport Sen.Sip4.Sport #define sen_dport Sen.Sip4.Dport +#define sen_direction Sen.Sip4.Direction #define sen_ip6_src Sen.Sip6.Src #define sen_ip6_dst Sen.Sip6.Dst #define sen_ip6_proto Sen.Sip6.Proto #define sen_ip6_sport Sen.Sip6.Sport #define sen_ip6_dport Sen.Sip6.Dport -#define sen_ipsp_dst Sen.Sipsp.Dst -#define sen_ipsp_spi Sen.Sipsp.Spi -#define sen_ipsp_sproto Sen.Sipsp.Sproto -#define sen_ipsp6_dst Sen.Sipsp6.Dst -#define sen_ipsp6_spi Sen.Sipsp6.Spi -#define sen_ipsp6_sproto Sen.Sipsp6.Sproto +#define sen_ip6_direction Sen.Sip6.Direction +#define sen_ipsp Sen.PolicyHead /* * The "type" is really part of the address as far as the routing @@ -168,27 +150,53 @@ struct sockaddr_encap #define SENT_IP4 0x0001 /* data is two struct in_addr */ #define SENT_IPSP 0x0002 /* data as in IP4/6 plus SPI */ #define SENT_IP6 0x0004 -#define SENT_IPSP6 0x0008 -/* - * SENT_HDRLEN is the length of the "header" - * SENT_*_LEN are the lengths of various forms of sen_data - * SENT_*_OFF are the offsets in the sen_data array of various fields - */ +#define SENT_LEN sizeof(struct sockaddr_encap) -#define SENT_HDRLEN (2 * sizeof(u_int8_t) + sizeof(u_int16_t)) +struct ipsec_acquire +{ + union sockaddr_union ipa_addr; + u_int64_t ipa_expire; + TAILQ_ENTRY(ipsec_acquire) ipa_next; +}; + +struct ipsec_policy +{ + struct sockaddr_encap ipo_addr; + struct sockaddr_encap ipo_mask; + + union sockaddr_union ipo_src; /* Local address to use */ + union sockaddr_union ipo_dst; /* Remote gateway -- if it's zeroed: + * - on output, we try to contact the + * remote host directly (if needed). + * - on input, we accept on if the + * inner source is the same as the + * outer source address, or if transport + * mode was used. + */ + + u_int64_t ipo_last_searched; /* Timestamp of last lookup */ + + u_int8_t ipo_flags; /* See IPSP_POLICY_* definitions */ + u_int8_t ipo_type; /* USE/ACQUIRE/... */ + u_int8_t ipo_sproto;/* ESP, AH; if zero we use system dflts */ -#define SENT_IP4_SRCOFF (0) -#define SENT_IP4_DSTOFF (sizeof (struct in_addr)) + struct tdb *ipo_tdb; /* Cached entry */ -#define SENT_IP6_SRCOFF (0) -#define SENT_IP6_DSTOFF (sizeof (struct in6_addr)) + TAILQ_ENTRY(ipsec_policy) ipo_tdb_next; /* List of policies on TDB */ + TAILQ_ENTRY(ipsec_policy) ipo_list; /* List of all policy entries */ +}; + +#define IPSP_POLICY_NONE 0x0000 /* No flags set */ -#define SENT_IP4_LEN 20 -#define SENT_IPSP_LEN 20 -#define SENT_IP6_LEN 44 -#define SENT_IPSP6_LEN 32 +#define IPSP_IPSEC_USE 0 /* Use if existing, don't bother establishing */ +#define IPSP_IPSEC_ACQUIRE 1 /* Try to acquire in parallel but let packet */ +#define IPSP_IPSEC_REQUIRE 2 /* Require SA */ +#define IPSP_PERMIT 3 /* Permit traffic through */ +#define IPSP_DENY 4 /* Deny traffic */ +#define IPSP_IPSEC_DONTACQ 5 /* Require, but don't acquire */ +/* Notification types */ #define NOTIFY_SOFT_EXPIRE 0 /* Soft expiration of SA */ #define NOTIFY_HARD_EXPIRE 1 /* Hard expiration of SA */ #define NOTIFY_REQUEST_SA 2 /* Establish an SA */ @@ -208,24 +216,19 @@ struct route_enc { struct sockaddr_encap re_dst; }; -struct flow -{ - struct flow *flow_next; /* Next in flow chain */ - struct flow *flow_prev; /* Previous in flow chain */ - struct tdb *flow_sa; /* Pointer to the SA */ - union sockaddr_union flow_src; /* Source address */ - union sockaddr_union flow_srcmask; /* Source netmask */ - union sockaddr_union flow_dst; /* Destination address */ - union sockaddr_union flow_dstmask; /* Destination netmask */ - u_int8_t flow_proto; /* Transport protocol, if applicable */ - u_int8_t foo[3]; /* Alignment */ -}; - struct tdb /* tunnel descriptor block */ { - struct tdb *tdb_hnext; /* Next in hash chain */ - struct tdb *tdb_onext; /* Next in output */ - struct tdb *tdb_inext; /* Previous in output */ + /* + * Each TDB is on three hash tables: one keyed on dst/spi/sproto, + * one keyed on dst/sproto, and one keyed on src/sproto. The first + * is used for finding a specific TDB, the second for finding TDBs + * TDBs for outgoing policy matching, and the third for incoming + * policy matching. The following three fields maintain the hash + * queues in those three tables. + */ + struct tdb *tdb_hnext; /* dst/spi/sproto table */ + struct tdb *tdb_anext; /* dst/sproto table */ + struct tdb *tdb_snext; /* src/sproto table */ struct xformsw *tdb_xform; /* Transformation to use */ struct enc_xform *tdb_encalgxform; /* Encryption algorithm xform */ @@ -301,13 +304,9 @@ struct tdb /* tunnel descriptor block */ u_int8_t tdb_iv[4]; /* Used for HALF-IV ESP */ caddr_t tdb_interface; - struct flow *tdb_flow; /* Which outboind flows use this SA */ - struct flow *tdb_access; /* Ingress access control */ - struct tdb *tdb_bind_out; /* Outgoing SA to use */ - TAILQ_HEAD(tdb_bind_head, tdb) tdb_bind_in; - TAILQ_ENTRY(tdb) tdb_bind_in_next; /* Refering Incoming SAs */ TAILQ_HEAD(tdb_inp_head, inpcb) tdb_inp; + TAILQ_HEAD(tdb_policy_head, ipsec_policy) tdb_policy_head; }; struct tdb_ident { @@ -396,7 +395,9 @@ extern int encdebug; extern int ipsec_acl; extern int ipsec_keep_invalid; extern int ipsec_in_use; +extern u_int64_t ipsec_last_added; extern int ipsec_require_pfs; +extern int ipsec_expire_acquire; extern int ipsec_soft_allocations; extern int ipsec_exp_allocations; @@ -421,6 +422,8 @@ extern struct auth_hash auth_hash_hmac_ripemd_160_96; extern TAILQ_HEAD(expclusterlist_head, tdb) expclusterlist; extern TAILQ_HEAD(explist_head, tdb) explist; +extern TAILQ_HEAD(ipsec_policy_head, ipsec_policy) ipsec_policy_head; +extern TAILQ_HEAD(ipsec_acquire_head, ipsec_acquire) ipsec_acquire_head; extern struct xformsw xformsw[], *xformswNXFORMSW; @@ -446,7 +449,6 @@ extern struct xformsw xformsw[], *xformswNXFORMSW; /* Misc. */ extern char *inet_ntoa4(struct in_addr); - extern char *ipsp_address(union sockaddr_union); /* TDB management routines */ @@ -454,8 +456,12 @@ extern void tdb_add_inp(struct tdb *tdb, struct inpcb *inp); extern u_int32_t reserve_spi(u_int32_t, u_int32_t, union sockaddr_union *, union sockaddr_union *, u_int8_t, int *); extern struct tdb *gettdb(u_int32_t, union sockaddr_union *, u_int8_t); +extern struct tdb *gettdbbyaddr(union sockaddr_union *, u_int8_t, + struct mbuf *, int); +extern struct tdb *gettdbbysrc(union sockaddr_union *, u_int8_t, + struct mbuf *, int); extern void puttdb(struct tdb *); -extern void tdb_delete(struct tdb *, int, int); +extern void tdb_delete(struct tdb *, int); extern int tdb_init(struct tdb *, u_int16_t, struct ipsecinit *); extern void tdb_expiration(struct tdb *, int); /* Flag values for the last argument of tdb_expiration(). */ @@ -464,18 +470,6 @@ extern void tdb_expiration(struct tdb *, int); extern int tdb_walk(int (*)(struct tdb *, void *), void *); extern void handle_expirations(void *); -/* Flow management routines */ -extern struct flow *get_flow(void); -extern void put_flow(struct flow *, struct tdb *, int); -extern void delete_flow(struct flow *, struct tdb *, int); -extern struct flow *find_flow(union sockaddr_union *, union sockaddr_union *, - union sockaddr_union *, union sockaddr_union *, - u_int8_t, struct tdb *, int); -extern struct flow *find_global_flow(union sockaddr_union *, - union sockaddr_union *, - union sockaddr_union *, - union sockaddr_union *, u_int8_t); - /* XF_IP4 */ extern int ipe4_attach(void); extern int ipe4_init(struct tdb *, struct xformsw *, struct ipsecinit *); @@ -549,7 +543,7 @@ extern int tcp_signature_tdb_output __P((struct mbuf *, struct tdb *, struct mbuf **, int, int)); /* Padding */ -extern caddr_t m_pad(struct mbuf *, int, int); +extern caddr_t m_pad(struct mbuf *, int); /* Replay window */ extern int checkreplaywindow32(u_int32_t, u_int32_t, u_int32_t *, u_int32_t, @@ -560,8 +554,17 @@ extern unsigned char ipseczeroes[]; /* Packet processing */ extern int ipsp_process_packet(struct mbuf *, struct tdb *, int, int); extern int ipsp_process_done(struct mbuf *, struct tdb *); -extern struct tdb *ipsp_spd_lookup(struct mbuf *, int, int, int *); +extern struct tdb *ipsp_spd_lookup(struct mbuf *, int, int, int *, int, + struct tdb *, struct inpcb *); extern int ipsec_common_input_cb(struct mbuf *, struct tdb *, int, int); -extern int ipsp_acquire_sa(struct tdb *); +extern int ipsp_acquire_sa(struct ipsec_policy *, union sockaddr_union *, + union sockaddr_union *); +extern struct ipsec_policy *ipsec_add_policy(struct sockaddr_encap *, + struct sockaddr_encap *, + union sockaddr_union *, int, int); +extern int ipsp_match_policy(struct tdb *, struct ipsec_policy *, + struct mbuf *, int); +extern void ipsp_acquire_expirations(void *); +extern int ipsec_delete_policy(struct ipsec_policy *); #endif /* _KERNEL */ #endif /* _NETINET_IPSP_H_ */ diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 93a75eabf13..d38374eb455 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_output.c,v 1.80 2000/09/18 22:06:37 provos Exp $ */ +/* $OpenBSD: ip_output.c,v 1.81 2000/09/19 03:20:59 angelos Exp $ */ /* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ */ /* @@ -130,7 +130,6 @@ ip_output(m0, va_alist) union sockaddr_union sdst; u_int32_t sspi; - u_int8_t sa_require = 0, sa_have = 0; struct inpcb *inp; struct tdb *tdb; int s; @@ -285,7 +284,8 @@ ip_output(m0, va_alist) &ip->ip_dst, sizeof(ip->ip_dst))) tdb = inp->inp_tdb; else - tdb = ipsp_spd_lookup(m, AF_INET, hlen, &error); + tdb = ipsp_spd_lookup(m, AF_INET, hlen, &error, + IPSP_DIRECTION_OUT, NULL, inp); if (tdb == NULL) { splx(s); @@ -317,40 +317,22 @@ ip_output(m0, va_alist) sproto = tdb->tdb_sproto; /* - * 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. + * 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. */ - 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_INET && - !bcmp(&sdst.sin.sin_addr.s_addr, &ip->ip_dst.s_addr, - sizeof(ip->ip_dst.s_addr))) { + 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_INET) && + (sdst.sin.sin_addr.s_addr == ip->ip_dst.s_addr)) { splx(s); - sproto = 0; /* mark as no-IPsec-needed */ + sproto = 0; /* mark as no-IPsec-needed */ goto done_spd; } - /* 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; - m_freem(m); - goto done; - } - /* If it's not a multicast packet, try to fast-path */ if (!IN_MULTICAST(ip->ip_dst.s_addr)) { goto sendit; @@ -570,7 +552,6 @@ sendit: /* Massage the IP header for use by the IPsec code */ ip->ip_len = htons((u_short) ip->ip_len); ip->ip_off = htons((u_short) ip->ip_off); - ip->ip_sum = 0; /* * Clear these -- they'll be set in the recursive invocation @@ -816,9 +797,6 @@ ip_ctloutput(op, so, level, optname, mp) register int optval = 0; #ifdef IPSEC struct proc *p = curproc; /* XXX */ - struct tdb *tdb; - struct tdb_ident *tdbip, tdbi; - int s; #endif int error = 0; @@ -916,26 +894,6 @@ ip_ctloutput(op, so, level, optname, mp) } } break; - case IPSEC_OUTSA: -#ifndef IPSEC - error = EINVAL; -#else - s = spltdb(); - if (m == 0 || m->m_len != sizeof(struct tdb_ident)) { - error = EINVAL; - } else { - tdbip = mtod(m, struct tdb_ident *); - tdb = gettdb(tdbip->spi, &tdbip->dst, - tdbip->proto); - if (tdb == NULL) - error = ESRCH; - else - tdb_add_inp(tdb, inp); - } - splx(s); -#endif /* IPSEC */ - break; - case IP_AUTH_LEVEL: case IP_ESP_TRANS_LEVEL: case IP_ESP_NETWORK_LEVEL: @@ -1064,26 +1022,6 @@ ip_ctloutput(op, so, level, optname, mp) *mtod(m, int *) = optval; break; - case IPSEC_OUTSA: -#ifndef IPSEC - error = EINVAL; -#else - s = spltdb(); - if (inp->inp_tdb == NULL) { - error = ENOENT; - } else { - tdbi.spi = inp->inp_tdb->tdb_spi; - tdbi.dst = inp->inp_tdb->tdb_dst; - tdbi.proto = inp->inp_tdb->tdb_sproto; - *mp = m = m_get(M_WAIT, MT_SOOPTS); - m->m_len = sizeof(tdbi); - bcopy((caddr_t)&tdbi, mtod(m, caddr_t), - (unsigned)m->m_len); - } - splx(s); -#endif /* IPSEC */ - break; - case IP_AUTH_LEVEL: case IP_ESP_TRANS_LEVEL: case IP_ESP_NETWORK_LEVEL: diff --git a/sys/netinet/ip_spd.c b/sys/netinet/ip_spd.c new file mode 100644 index 00000000000..c5500b15bb5 --- /dev/null +++ b/sys/netinet/ip_spd.c @@ -0,0 +1,791 @@ +/* $OpenBSD: ip_spd.c,v 1.1 2000/09/19 03:20:59 angelos Exp $ */ + +/* + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * Copyright (c) 2000 Angelos D. Keromytis. + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/queue.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> +#endif /* INET */ + +#ifdef INET6 +#ifndef INET +#include <netinet/in.h> +#endif +#include <netinet6/in6_var.h> +#endif /* INET6 */ + +#include <net/pfkeyv2.h> + +#include <netinet/ip_ipsp.h> + +#ifdef ENCDEBUG +#define DPRINTF(x) if (encdebug) printf x +#else +#define DPRINTF(x) +#endif + +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + +/* + * Lookup at the SPD based on the headers contained on the mbuf. The second + * argument indicates what protocol family the header at the beginning of + * the mbuf is. hlen is the the offset of the transport protocol header + * in the mbuf. + * + * Return combinations (of return value and in *error): + * - NULL/0 -> no IPsec required on packet + * - NULL/-EINVAL -> silently drop the packet + * - NULL/errno -> drop packet and return error + * or a pointer to a TDB (and 0 in *error). + * + * In the case of incoming flows, only the first three combinations are + * returned. + */ +struct tdb * +ipsp_spd_lookup(struct mbuf *m, int af, int hlen, int *error, int direction, + struct tdb *tdbp, struct inpcb *inp) +{ + struct route_enc re0, *re = &re0; + union sockaddr_union sdst, ssrc; + struct sockaddr_encap *ddst; + struct ipsec_policy *ipo; + int ignore = 0; + + /* + * If there are no flows in place, there's no point + * continuing with the SPD lookup. + */ + if (!ipsec_in_use) + { + *error = 0; + return NULL; + } + + /* If an input packet is destined to a BYPASS socket, just accept it */ + if ((inp != NULL) && (direction == IPSP_DIRECTION_IN) && + (inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_BYPASS) && + (inp->inp_seclevel[SL_ESP_NETWORK] == IPSEC_LEVEL_BYPASS) && + (inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_BYPASS)) + { + *error = 0; + return NULL; + } + + bzero((caddr_t) re, sizeof(struct route_enc)); + bzero((caddr_t) &sdst, sizeof(union sockaddr_union)); + bzero((caddr_t) &ssrc, sizeof(union sockaddr_union)); + ddst = (struct sockaddr_encap *) &re->re_dst; + ddst->sen_family = PF_KEY; + ddst->sen_len = SENT_LEN; + + switch (af) + { +#ifdef INET + case AF_INET: + ddst->sen_direction = direction; + ddst->sen_type = SENT_IP4; + + m_copydata(m, offsetof(struct ip, ip_src), + sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_src)); + m_copydata(m, offsetof(struct ip, ip_dst), + sizeof(struct in_addr), (caddr_t) &(ddst->sen_ip_dst)); + m_copydata(m, offsetof(struct ip, ip_p), sizeof(u_int8_t), + (caddr_t) &(ddst->sen_proto)); + + sdst.sin.sin_family = ssrc.sin.sin_family = AF_INET; + sdst.sin.sin_len = ssrc.sin.sin_len = sizeof(struct sockaddr_in); + ssrc.sin.sin_addr = ddst->sen_ip_src; + sdst.sin.sin_addr = ddst->sen_ip_dst; + + /* If TCP/UDP, extract the port numbers to use in the lookup */ + switch (ddst->sen_proto) + { + case IPPROTO_UDP: + case IPPROTO_TCP: + /* + * Luckily, the offset of the src/dst ports in both the UDP + * and TCP headers is the same (first two 16-bit values + * in the respective headers), so we can just copy them. + */ + m_copydata(m, hlen, sizeof(u_int16_t), + (caddr_t) &(ddst->sen_sport)); + m_copydata(m, hlen + sizeof(u_int16_t), sizeof(u_int16_t), + (caddr_t) &(ddst->sen_dport)); + break; + + default: + ddst->sen_sport = 0; + ddst->sen_dport = 0; + } + + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + ddst->sen_type = SENT_IP6; + ddst->sen_ip6_direction = direction; + + m_copydata(m, offsetof(struct ip6_hdr, ip6_src), + sizeof(struct in6_addr), + (caddr_t) &(ddst->sen_ip6_src)); + m_copydata(m, offsetof(struct ip6_hdr, ip6_dst), + sizeof(struct in6_addr), + (caddr_t) &(ddst->sen_ip6_dst)); + m_copydata(m, offsetof(struct ip6_hdr, ip6_nxt), sizeof(u_int8_t), + (caddr_t) &(ddst->sen_ip6_proto)); + + sdst.sin6.sin6_family = ssrc.sin6.sin6_family = AF_INET6; + sdst.sin6.sin6_len = ssrc.sin6.sin6_family = + sizeof(struct sockaddr_in6); + ssrc.sin6.sin6_addr = ddst->sen_ip6_src; + sdst.sin6.sin6_addr = ddst->sen_ip6_dst; + + /* If TCP/UDP, extract the port numbers to use in the lookup */ + switch (ddst->sen_ip6_proto) + { + case IPPROTO_UDP: + case IPPROTO_TCP: + /* + * Luckily, the offset of the src/dst ports in both the UDP + * and TCP headers is the same (first two 16-bit values + * in the respective headers), so we can just copy them. + */ + m_copydata(m, hlen, sizeof(u_int16_t), + (caddr_t) &(ddst->sen_ip6_sport)); + m_copydata(m, hlen + sizeof(u_int16_t), sizeof(u_int16_t), + (caddr_t) &(ddst->sen_ip6_dport)); + break; + + default: + ddst->sen_ip6_sport = 0; + ddst->sen_ip6_dport = 0; + } + + break; +#endif /* INET6 */ + + default: + *error = EAFNOSUPPORT; + return NULL; + } + + /* Actual SPD lookup */ + rtalloc((struct route *) re); + if (re->re_rt == NULL) + { + *error = 0; + return NULL; /* Nothing found -- means no IPsec needed */ + } + + /* Sanity check */ + if ((re->re_rt->rt_gateway == NULL) || + (((struct sockaddr_encap *) re->re_rt->rt_gateway)->sen_type != + SENT_IPSP)) + { + DPRINTF(("ipsp_spd_lookup(): no gw, or gw data not IPSP\n")); + RTFREE(re->re_rt); + *error = EHOSTUNREACH; + return NULL; + } + + ipo = ((struct sockaddr_encap *) (re->re_rt->rt_gateway))->sen_ipsp; + RTFREE(re->re_rt); + if (ipo == NULL) + { + DPRINTF(("ipsp_spd_lookup(): no policy present\n")); + *error = EHOSTUNREACH; + return NULL; + } + + switch (ipo->ipo_type) + { + case IPSP_PERMIT: + *error = 0; + return NULL; + + case IPSP_DENY: + *error = EHOSTUNREACH; + return NULL; + + case IPSP_IPSEC_USE: + case IPSP_IPSEC_ACQUIRE: + case IPSP_IPSEC_REQUIRE: + case IPSP_IPSEC_DONTACQ: + /* Nothing more needed here */ + break; + + default: + *error = EINVAL; + return NULL; + } + + /* + * Check for non-specific destination in the policy. If a specific + * destination was specified, use that -- otherwise, use the relevant + * information from the packet. + */ + switch (ipo->ipo_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + if (ipo->ipo_dst.sin.sin_addr.s_addr != INADDR_ANY) + { + if (direction == IPSP_DIRECTION_OUT) + bcopy(&ipo->ipo_dst, &sdst, sizeof(union sockaddr_union)); + else + bcopy(&ipo->ipo_dst, &ssrc, sizeof(union sockaddr_union)); + } + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + if (!IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_dst.sin6.sin6_addr)) + { + if (direction == IPSP_DIRECTION_OUT) + bcopy(&ipo->ipo_dst, &sdst, sizeof(union sockaddr_union)); + else + bcopy(&ipo->ipo_dst, &ssrc, sizeof(union sockaddr_union)); + } + break; +#endif /* INET6 */ + } + + switch (ipo->ipo_src.sa.sa_family) + { +#ifdef INET + case AF_INET: + if (ipo->ipo_src.sin.sin_addr.s_addr != INADDR_ANY) + { + if (direction == IPSP_DIRECTION_OUT) + bcopy(&ipo->ipo_src, &ssrc, sizeof(union sockaddr_union)); + else + bcopy(&ipo->ipo_src, &sdst, sizeof(union sockaddr_union)); + } + else + ignore = 1; + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + if (!IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_src.sin6.sin6_addr)) + { + if (direction == IPSP_DIRECTION_OUT) + bcopy(&ipo->ipo_src, &ssrc, sizeof(union sockaddr_union)); + else + bcopy(&ipo->ipo_src, &sdst, sizeof(union sockaddr_union)); + } + else + ignore = 1; + break; +#endif /* INET6 */ + } + + /* Do we have a cached entry ? If so, check if it's still valid. */ + if ((ipo->ipo_tdb) && (ipo->ipo_tdb->tdb_flags & TDBF_INVALID)) + { + TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo, ipo_tdb_next); + ipo->ipo_tdb = NULL; + } + + /* Outgoing packet SPD lookup */ + if (direction == IPSP_DIRECTION_OUT) + { + /* + * If the packet is destined for the policy-specified gateway/endhost, + * and the socket has the BYPASS option set, skip IPsec processing. + */ + if ((inp != NULL) && + (inp->inp_seclevel[SL_ESP_TRANS] == IPSEC_LEVEL_BYPASS) && + (inp->inp_seclevel[SL_ESP_NETWORK] == IPSEC_LEVEL_BYPASS) && + (inp->inp_seclevel[SL_AUTH] == IPSEC_LEVEL_BYPASS)) + { + /* Direct match */ + if (bcmp(&sdst, &ipo->ipo_dst, sizeof(union sockaddr_union)) == 0) + { + *error = 0; + return NULL; + } + + /* Same-host */ + switch (ipo->ipo_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + if (ipo->ipo_dst.sin.sin_addr.s_addr == INADDR_ANY) + { + *error = 0; + return NULL; + } + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + if (IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_dst.sin6.sin6_addr)) + { + *error = 0; + return NULL; + } + break; +#endif /* INET6 */ + } + } + + /* Check that the cached TDB (if present), is appropriate */ + if (ipo->ipo_tdb) + { + if (bcmp(&sdst, &ipo->ipo_tdb->tdb_dst, + sizeof(union sockaddr_union))) + { + TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo, + ipo_tdb_next); + ipo->ipo_tdb = NULL; + + /* Fall through to acquisition of TDB */ + } + else + return ipo->ipo_tdb; /* Cached entry is good, we're done */ + } + + /* + * If no SA has been added since the last time we did a lookup, + * there's no point searching for one. + */ + if (ipo->ipo_last_searched <= ipsec_last_added) + { + ipo->ipo_last_searched = time.tv_sec; /* "touch" the entry */ + + /* Find an appropriate SA from among the existing SAs */ + ipo->ipo_tdb = gettdbbyaddr(&sdst, ipo->ipo_sproto, m, af); + if (ipo->ipo_tdb) + { + TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head, ipo, + ipo_tdb_next); + *error = 0; + ipo->ipo_last_searched = 0; + return ipo->ipo_tdb; + } + } + + /* So, we don't have an SA -- just a policy */ + switch (ipo->ipo_type) + { + case IPSP_IPSEC_REQUIRE: + /* Acquire SA through key management */ + if (ipsp_acquire_sa(ipo, &sdst, ignore ? NULL : &ssrc) != 0) + { + *error = EACCES; + return NULL; + } + + /* Fall through */ + + case IPSP_IPSEC_DONTACQ: + *error = -EINVAL; /* Silently drop packet */ + return NULL; + + case IPSP_IPSEC_ACQUIRE: + /* Acquire SA through key management */ + if (ipsp_acquire_sa(ipo, &sdst, ignore ? NULL : &ssrc) != 0) + { + *error = EACCES; + return NULL; + } + + /* Fall through */ + + case IPSP_IPSEC_USE: + *error = 0; /* Let packet through */ + return NULL; + } + } + else /* IPSP_DIRECTION_IN */ + { + /* Check the cached entry */ + if ((ipo->ipo_tdb) && + bcmp(&ssrc, &ipo->ipo_tdb->tdb_src, sizeof(union sockaddr_union))) + { + TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo, ipo_tdb_next); + ipo->ipo_tdb = NULL; + } + + switch (ipo->ipo_type) + { + case IPSP_IPSEC_DONTACQ: + /* Does protection match stated policy ? */ + if (tdbp && ipsp_match_policy(tdbp, ipo, m, af)) + { + /* Accept packet */ + *error = 0; + return NULL; + } + + /* Silently drop packet */ + *error = EHOSTUNREACH; + return NULL; + + case IPSP_IPSEC_REQUIRE: + if (tdbp && ipsp_match_policy(tdbp, ipo, m, af)) + { + /* Accept packet */ + *error = 0; + return NULL; + } + + /* If we have a cached entry, just discard the packet */ + if (ipo->ipo_tdb) + { + *error = EHOSTUNREACH; + return NULL; + } + + /* + * Find whether there exists an appropriate SA. If so, drop + * the packet. Otherwise, try to acquire one (from below). + * + * If no SA has been added since the last time we did a lookup, + * there's no point searching for one. + */ + if (ipo->ipo_last_searched <= ipsec_last_added) + { + ipo->ipo_last_searched = time.tv_sec; /* "touch" */ + + if ((ipo->ipo_tdb = gettdbbysrc(&ssrc, ipo->ipo_sproto, + m, af)) != NULL) + { + TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head, ipo, + ipo_tdb_next); + ipo->ipo_last_searched = 0; + *error = EHOSTUNREACH; + return NULL; + } + } + + /* Acquire SA through key management */ + if ((*error = ipsp_acquire_sa(ipo, &ssrc, + ignore ? NULL : &sdst)) != 0) + return NULL; + + *error = -EINVAL; + return NULL; + + case IPSP_IPSEC_USE: + /* + * It doesn't matter what protection it had (if any), + * just accept it -- equivalent to PERMIT for input. + * This means we can't say that we want in incoming + * packet to be unprotected -- at least not directly; + * we can always have a DENY policy for ESP/AH packets. + */ + *error = 0; + return NULL; + + case IPSP_IPSEC_ACQUIRE: + /* + * We don't check for policy match, since we would + * accept clear-text packets as well. + */ + + /* If we have a cached entry, just accept the packet */ + if (ipo->ipo_tdb) + { + *error = 0; + return NULL; + } + + /* + * Find whether there exists an appropriate SA. If so, accept + * the packet. Otherwise, try to acquire one (from below). + * + * If no SA has been added since the last time we did a lookup, + * there's no point searching for one. + */ + if (ipo->ipo_last_searched <= ipsec_last_added) + { + ipo->ipo_last_searched = time.tv_sec; /* "touch" */ + + if ((ipo->ipo_tdb = gettdbbysrc(&ssrc, ipo->ipo_sproto, + m, af)) != NULL) + { + TAILQ_INSERT_TAIL(&ipo->ipo_tdb->tdb_policy_head, ipo, + ipo_tdb_next); + ipo->ipo_last_searched = 0; + *error = 0; + return NULL; + } + } + + /* Acquire SA through key management */ + if ((*error = ipsp_acquire_sa(ipo, &ssrc, + ignore ? NULL : &sdst)) != 0) + return NULL; + + /* Just accept the packet */ + *error = 0; + return NULL; + } + } + + /* Shouldn't ever get this far */ + *error = EINVAL; + return NULL; +} + + +/* + * See if a specific SA satisfies stated policy. Return 0 if false, 1 (or + * non-zero) otherwise. + */ +int +ipsp_match_policy(struct tdb *tdb, struct ipsec_policy *ipo, + struct mbuf *m, int af) +{ + union sockaddr_union peer; + int pflag = 0; + + switch (ipo->ipo_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + if (ipo->ipo_dst.sin.sin_addr.s_addr == INADDR_ANY) + pflag = 1; + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + if (IN6_IS_ADDR_UNSPECIFIED(&ipo->ipo_dst.sin6.sin6_addr)) + pflag = 1; + break; +#endif /* INET6 */ + + case 0: /* Just in case */ + pflag = 1; + break; + + default: + return 0; /* Unknown/unsupported network protocol */ + } + + if (pflag == 0) + bcopy(&ipo->ipo_dst, &peer, sizeof(union sockaddr_union)); + else + { + bzero(&peer, sizeof(union sockaddr_union)); + + /* Need to copy the source address from the packet */ + switch (af) + { +#ifdef INET + case AF_INET: + peer.sin.sin_family = AF_INET; + peer.sin.sin_len = sizeof(struct sockaddr_in); + m_copydata(m, offsetof(struct ip, ip_src), + sizeof(struct in_addr), + (caddr_t) &peer.sin.sin_addr); + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + peer.sin6.sin6_family = AF_INET6; + peer.sin6.sin6_len = sizeof(struct sockaddr_in6); + m_copydata(m, offsetof(struct ip6_hdr, ip6_src), + sizeof(struct in6_addr), + (caddr_t) &peer.sin6.sin6_addr); + break; +#endif /* INET6 */ + + default: + return 0; /* Unknown/unsupported network protocol */ + } + } + + /* + * Does the packet use the right security protocol and is coming from + * the right peer ? + */ + if ((tdb->tdb_sproto == ipo->ipo_sproto) && + (!bcmp(&tdb->tdb_src, &peer, sizeof(union sockaddr_union)))) + return 1; + + return 0; +} + +/* + * Delete a policy from the SPD. + */ +int +ipsec_delete_policy(struct ipsec_policy *ipo) +{ + int err; + + /* Delete */ + err = rtrequest(RTM_DELETE, (struct sockaddr *) &ipo->ipo_addr, + (struct sockaddr *) 0, (struct sockaddr *) &ipo->ipo_mask, + 0, (struct rtentry **) 0); + + if (ipo->ipo_tdb) + TAILQ_REMOVE(&ipo->ipo_tdb->tdb_policy_head, ipo, ipo_tdb_next); + + TAILQ_REMOVE(&ipsec_policy_head, ipo, ipo_list); + FREE(ipo, M_TDB); + ipsec_in_use--; + + return err; +} + +/* + * Add a policy to the SPD. + */ +struct ipsec_policy * +ipsec_add_policy(struct sockaddr_encap *dst, struct sockaddr_encap *mask, + union sockaddr_union *sdst, int type, int sproto) +{ + struct sockaddr_encap encapgw; + struct ipsec_policy *ipon; + + MALLOC(ipon, struct ipsec_policy *, sizeof(struct ipsec_policy), M_TDB, + M_NOWAIT); + if (ipon == NULL) + return NULL; + + bzero(ipon, sizeof(struct ipsec_policy)); + bzero((caddr_t) &encapgw, sizeof(struct sockaddr_encap)); + + encapgw.sen_len = SENT_LEN; + encapgw.sen_family = PF_KEY; + encapgw.sen_type = SENT_IPSP; + encapgw.sen_ipsp = ipon; + + if (rtrequest(RTM_ADD, (struct sockaddr *) dst, + (struct sockaddr *) &encapgw, (struct sockaddr *) mask, + RTF_UP | RTF_GATEWAY | RTF_STATIC, + (struct rtentry **) 0) != 0) + { + DPRINTF(("ipsec_add_policy: failed to add policy\n")); + FREE(ipon, M_TDB); + return NULL; + } + + ipsec_in_use++; + + bcopy(dst, &ipon->ipo_addr, sizeof(struct sockaddr_encap)); + bcopy(mask, &ipon->ipo_mask, sizeof(struct sockaddr_encap)); + bcopy(sdst, &ipon->ipo_dst, sizeof(union sockaddr_union)); + ipon->ipo_sproto = sproto; + ipon->ipo_type = type; + + TAILQ_INSERT_HEAD(&ipsec_policy_head, ipon, ipo_list); + + return ipon; +} + +/* + * Expire old acquire requests to key management. + */ +void +ipsp_acquire_expirations(void *arg) +{ + struct ipsec_acquire *ipa; + + for (ipa = TAILQ_FIRST(&ipsec_acquire_head); + ipa; + ipa = TAILQ_FIRST(&ipsec_acquire_head)) + { + if (ipa->ipa_expire <= time.tv_sec) + { + /* Remove from the list and free */ + TAILQ_REMOVE(&ipsec_acquire_head, ipa, ipa_next); + FREE(ipa, M_TDB); + } + else + { + /* Schedule us for another expiration */ + timeout(ipsp_acquire_expirations, (void *) NULL, + hz * (ipa->ipa_expire - time.tv_sec)); + return; + } + } + + /* If there's no request pending, we don't need to schedule us */ + + return; +} + +/* + * Signal key management that we need an SA. + */ +int +ipsp_acquire_sa(struct ipsec_policy *ipo, union sockaddr_union *gw, + union sockaddr_union *laddr) +{ + struct ipsec_acquire *ipa; + + /* + * Check whether request has been made already. + * XXX We need a more scalable data structure instead of a list. + */ + for (ipa = TAILQ_FIRST(&ipsec_acquire_head); + ipa; + ipa = TAILQ_NEXT(ipa, ipa_next)) + { + /* Already in process */ + if (!bcmp(gw, &ipa->ipa_addr, sizeof(union sockaddr_union))) + return 0; + } + + /* Add request in cache and proceed */ + MALLOC(ipa, struct ipsec_acquire *, sizeof(struct ipsec_acquire), + M_TDB, M_DONTWAIT); + if (ipa == NULL) + return ENOMEM; + bcopy(gw, &ipa->ipa_addr, sizeof(union sockaddr_union)); + ipa->ipa_expire = time.tv_sec + ipsec_expire_acquire; + TAILQ_INSERT_TAIL(&ipsec_acquire_head, ipa, ipa_next); + + if (TAILQ_FIRST(&ipsec_acquire_head) == ipa) + timeout(ipsp_acquire_expirations, (void *) NULL, + hz * (ipa->ipa_expire - time.tv_sec)); + + /* PF_KEYv2 notification message */ + return pfkeyv2_acquire(ipo, gw, laddr); +} diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c index bcd4b5d9e85..de37437259d 100644 --- a/sys/netinet/ipsec_input.c +++ b/sys/netinet/ipsec_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsec_input.c,v 1.31 2000/09/17 19:52:24 angelos Exp $ */ +/* $OpenBSD: ipsec_input.c,v 1.32 2000/09/19 03:20:59 angelos Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), @@ -77,6 +77,8 @@ #include "bpfilter.h" +#define PI_MAGIC 0xdeadbeef /* XXX horror! */ + int ipsec_common_input(struct mbuf *, int, int, int, int); #ifdef ENCDEBUG @@ -214,7 +216,7 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) { /* * XXX The fragment conflicts with scoped nature of IPv6, so do it for - * only for IPv4 for now + * only for IPv4 for now. */ if (tdbp->tdb_interface) m->m_pkthdr.rcvif = (struct ifnet *) tdbp->tdb_interface; @@ -229,16 +231,6 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) tdb_expiration(tdbp, TDBEXP_TIMEOUT); } - /* If we do ingress filtering and the list is empty, quick drop */ - if (ipsec_acl && (tdbp->tdb_access == NULL)) - { - DPRINTF(("ipsec_common_input(): packet dropped due to empty policy list, SA %s/%08x/%u\n", ipsp_address(tdbp->tdb_dst), ntohl(spi), tdbp->tdb_sproto)); - splx(s); - m_freem(m); - IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); - return EACCES; - } - /* * Call appropriate transform and return -- callback takes care of * everything else. @@ -262,10 +254,8 @@ ipsec_common_input(struct mbuf *m, int skip, int protoff, int af, int sproto) int ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) { - union sockaddr_union src_address, dst_address, src2, dst2; - caddr_t sport = 0, dport = 0; int prot, af, sproto; - struct flow *flow; + #if NBPFILTER > 0 struct ifnet *bpfif; #endif @@ -289,8 +279,6 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) return EINVAL; } - bcopy(&tdbp->tdb_dst, &dst_address, tdbp->tdb_dst.sa.sa_len); - #ifdef INET /* Fix IPv4 header */ if (tdbp->tdb_dst.sa.sa_family == AF_INET) @@ -455,7 +443,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) ((tdbp->tdb_src.sa.sa_family != AF_INET6) && (tdbp->tdb_src.sa.sa_family != 0))) { - DPRINTF(("ipsec_common_input_cb(): packet %s to %s does not match any ACL entries, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); + DPRINTF(("ipsec_common_input_cb(): packet %s to %s does not match any ACL entries, SA %s/%08x\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ipsp_address(tdbp->tdb_src), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); m_freem(m); IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); return EACCES; @@ -463,112 +451,26 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) } #endif /* INET6 */ - /* Access control */ - if (ipsec_acl) - { - bzero(&src_address, sizeof(src_address)); - src_address.sa.sa_family = af; - src_address.sa.sa_len = dst_address.sa.sa_len; - -#ifdef INET - if (af == AF_INET) - { - m_copydata(m, offsetof(struct ip, ip_src), sizeof(struct in_addr), - (caddr_t) &(src_address.sin.sin_addr)); - sport = (caddr_t) &src_address.sin.sin_port; - dport = (caddr_t) &dst_address.sin.sin_port; - } -#endif /* INET */ - -#ifdef INET6 - if (af == AF_INET6) - { - m_copydata(m, offsetof(struct ip6_hdr, ip6_src), - sizeof(struct in6_addr), - (caddr_t) &(src_address.sin6.sin6_addr)); - sport = (caddr_t) &src_address.sin6.sin6_port; - dport = (caddr_t) &dst_address.sin6.sin6_port; - } -#endif /* INET6 */ - - /* Save transport layer source/destination ports, if any */ - switch (prot) - { - case IPPROTO_TCP: - m_copydata(m, skip + offsetof(struct tcphdr, th_sport), - sizeof(u_int16_t), (caddr_t) sport); - m_copydata(m, skip + offsetof(struct tcphdr, th_dport), - sizeof(u_int16_t), (caddr_t) dport); - break; - - case IPPROTO_UDP: - m_copydata(m, skip + offsetof(struct udphdr, uh_sport), - sizeof(u_int16_t), (caddr_t) sport); - m_copydata(m, skip + offsetof(struct udphdr, uh_dport), - sizeof(u_int16_t), (caddr_t) dport); - break; - - default: - /* Nothing needed */ - } - - for (flow = tdbp->tdb_access; flow; flow = flow->flow_next) - { - /* Match for address family */ - if (flow->flow_src.sa.sa_family != af) - continue; - - /* Match for transport protocol */ - if (flow->flow_proto && flow->flow_proto != prot) - continue; - - /* Netmask handling */ - rt_maskedcopy(&src_address.sa, &src2.sa, &flow->flow_srcmask.sa); - rt_maskedcopy(&dst_address.sa, &dst2.sa, &flow->flow_dstmask.sa); - - /* Check addresses */ - if (bcmp(&src2, &flow->flow_src, src2.sa.sa_len) || - bcmp(&dst2, &flow->flow_dst, dst2.sa.sa_len)) - continue; - - break; /* success! */ - } - - if (flow == NULL) - { - /* Failed to match any entry in the ACL */ - DPRINTF(("ipsec_common_input_cb(): packet from %s to %s dropped due to policy, SA %s/%08x\n", ipsp_address(src_address), ipsp_address(dst_address), ipsp_address(tdbp->tdb_dst), ntohl(tdbp->tdb_spi))); - m_freem(m); - IPSEC_ISTAT(espstat.esps_pdrops, ahstat.ahs_pdrops); - return EACCES; - } - } + /* + * Record what we've done to the packet (under what SA it was + * processed). + */ + if (m->m_pkthdr.tdbi && m->m_pkthdr.tdbi != (void *) PI_MAGIC) + free(m->m_pkthdr.tdbi, M_TEMP); - if (prot == IPPROTO_TCP || prot == IPPROTO_UDP) + MALLOC(m->m_pkthdr.tdbi, void *, sizeof(struct tdb_ident), M_TEMP, + M_NOWAIT); + if (m->m_pkthdr.tdbi == NULL) { - if (tdbp->tdb_bind_out) - { - if (!(m->m_flags & M_PKTHDR)) - DPRINTF(("ipsec_common_input_cb(): mbuf is not a packet header!\n")); - - MALLOC(m->m_pkthdr.tdbi, struct tdb_ident *, - sizeof(struct tdb_ident), M_TEMP, M_NOWAIT); - - if (m->m_pkthdr.tdbi == NULL) - { - ((struct tdb_ident *) m->m_pkthdr.tdbi)->spi = - tdbp->tdb_bind_out->tdb_spi; - ((struct tdb_ident *) m->m_pkthdr.tdbi)->dst = - tdbp->tdb_bind_out->tdb_dst; - ((struct tdb_ident *) m->m_pkthdr.tdbi)->proto = - tdbp->tdb_bind_out->tdb_sproto; - } - } - else - m->m_pkthdr.tdbi = NULL; + m_freem(m); + IPSEC_ISTAT(espstat.esps_hdrops, ahstat.ahs_hdrops); + return ENOMEM; } - else - m->m_pkthdr.tdbi = NULL; + + bcopy(&tdbp->tdb_dst, &(((struct tdb_ident *) m->m_pkthdr.tdbi)->dst), + sizeof(union sockaddr_union)); + ((struct tdb_ident *) m->m_pkthdr.tdbi)->proto = tdbp->tdb_sproto; + ((struct tdb_ident *) m->m_pkthdr.tdbi)->spi = tdbp->tdb_spi; if (sproto == IPPROTO_ESP) { @@ -726,7 +628,7 @@ ah4_input_cb(struct mbuf *m, ...) if (IF_QFULL(ifq)) { IF_DROP(ifq); - if (m->m_pkthdr.tdbi) + if (m->m_pkthdr.tdbi && m->m_pkthdr.tdbi != (void *) PI_MAGIC) free(m->m_pkthdr.tdbi, M_TEMP); m_freem(m); ahstat.ahs_qfull++; @@ -768,7 +670,7 @@ esp4_input_cb(struct mbuf *m, ...) if (IF_QFULL(ifq)) { IF_DROP(ifq); - if (m->m_pkthdr.tdbi) + if (m->m_pkthdr.tdbi && m->m_pkthdr.tdbi != (void *) PI_MAGIC) free(m->m_pkthdr.tdbi, M_TEMP); m_freem(m); espstat.esps_qfull++; @@ -875,8 +777,10 @@ ah6_input_cb(struct mbuf *m, int off, int protoff) return 0; bad: + if (m->m_pkthdr.tdbi && m->m_pkthdr.tdbi != (void *) PI_MAGIC) + free(m->m_pkthdr.tdbi, M_TEMP); m_freem(m); - return EINVAL; /*?*/ + return EINVAL; } /* IPv6 ESP wrapper */ diff --git a/sys/netinet/ipsec_output.c b/sys/netinet/ipsec_output.c new file mode 100644 index 00000000000..26bccf8fa6a --- /dev/null +++ b/sys/netinet/ipsec_output.c @@ -0,0 +1,361 @@ +/* $OpenBSD: ipsec_output.c,v 1.1 2000/09/19 03:20:59 angelos Exp $ */ + +/* + * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) + * + * Copyright (c) 2000 Angelos D. Keromytis. + * + * Permission to use, copy, and modify this software without fee + * is hereby granted, provided that this entire notice is included in + * all copies of any software which is or includes a copy or + * modification of this software. + * You may use this code under the GNU public license if you so wish. Please + * contribute changes back to the authors under this freer than GPL license + * so that we may further the use of strong encryption without limitations to + * all. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE + * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR + * PURPOSE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <sys/kernel.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/ip_var.h> +#endif /* INET */ + +#ifdef INET6 +#ifndef INET +#include <netinet/in.h> +#endif +#include <netinet6/in6_var.h> +#endif /* INET6 */ + +#include <netinet/ip_ipsp.h> +#include <netinet/ip_ah.h> +#include <netinet/ip_esp.h> + +#ifdef ENCDEBUG +#define DPRINTF(x) if (encdebug) printf x +#else +#define DPRINTF(x) +#endif + +#ifndef offsetof +#define offsetof(s, e) ((int)&((s *)0)->e) +#endif + +/* + * 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 tdb *tdb, int af, int tunalready) +{ + int i, off, error; + struct mbuf *mp; + +#ifdef INET + struct ip *ip; +#endif /* INET */ +#ifdef INET6 + struct ip6_hdr *ip6; +#endif /* INET6 */ + + /* Check that the transform is allowed by the administrator */ + if ((tdb->tdb_sproto == IPPROTO_ESP && !esp_enable) || + (tdb->tdb_sproto == IPPROTO_AH && !ah_enable)) + { + DPRINTF(("ipsp_process_packet(): IPSec outbound packet dropped due to policy (check your sysctls)\n")); + m_freem(m); + return EHOSTUNREACH; + } + + /* Sanity check */ + if (!tdb->tdb_xform) + { + DPRINTF(("ipsp_process_packet(): uninitialized TDB\n")); + m_freem(m); + return EHOSTUNREACH; + } + + /* 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; + } + + /* Check that the network protocol is supported */ + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + break; +#endif /* INET6 */ + + default: + 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; + } + + /* Register first use if applicable, setup relevant 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. + * When doing Ethernet-over-IP, we are handed an already-encapsulated + * frame, so we don't need to re-encapsulate. + */ + if (tunalready == 0) + { + /* + * If the target protocol family is different, we know we'll be + * doing tunneling. + */ + 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 */ + + /* Bring the network header in the first mbuf */ + 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 */ + } + + /* Do the appropriate encapsulation, if necessary */ + if ((tdb->tdb_dst.sa.sa_family != af) || /* PF mismatch */ + (tdb->tdb_flags & TDBF_TUNNELING) || /* Tunneling requested */ + (tdb->tdb_xform->xf_type == XF_IP4) || /* ditto */ +#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) + { + if ((m = m_pullup(m, sizeof(struct ip))) == 0) + return ENOBUFS; + + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, ip->ip_hl << 2); + } +#endif /* INET */ + +#ifdef INET6 + /* Fix IPv6 header payload length */ + if (af == AF_INET6) + { + if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == 0) + return ENOBUFS; + + if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { + /* no jumbogram support */ + m_freem(m); + return ENXIO; /*?*/ + } + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); + } +#endif /* INET6 */ + + /* Encapsulate -- the last two arguments are unused */ + error = ipip_output(m, tdb, &mp, 0, 0); + if ((mp == NULL) && (!error)) + error = EFAULT; + if (error) + { + if (mp) + { + m_freem(mp); + mp = NULL; + } + + return error; + } + + m = mp; + mp = NULL; + } + + /* We may be done with this TDB */ + if (tdb->tdb_xform->xf_type == XF_IP4) + return ipsp_process_done(m, tdb); + } + else + { + /* + * If this is just an IP-IP TDB and we're told there's already an + * encapsulation header, move on. + */ + if (tdb->tdb_xform->xf_type == XF_IP4) + return ipsp_process_done(m, tdb); + } + + /* Extract some information off the headers */ + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + ip = mtod(m, struct ip *); + i = ip->ip_hl << 2; + off = offsetof(struct ip, ip_p); + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + ip6 = mtod(m, struct ip6_hdr *); + i = sizeof(struct ip6_hdr); + off = offsetof(struct ip6_hdr, ip6_nxt); + break; +#endif /* INET6 */ + } + + /* Invoke the IPsec transform */ + return (*(tdb->tdb_xform->xf_output))(m, tdb, NULL, i, off); +} + +/* + * Called by the IPsec output transform callbacks, to transmit the packet + * or do further processing, as necessary. + */ +int +ipsp_process_done(struct mbuf *m, struct tdb *tdb) +{ +#ifdef INET + struct ip *ip; +#endif /* INET */ + +#ifdef INET6 + struct ip6_hdr *ip6; +#endif /* INET6 */ + + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + /* Fix the header length, for AH processing */ + if (tdb->tdb_dst.sa.sa_family == AF_INET) + { + ip = mtod(m, struct ip *); + ip->ip_len = htons(m->m_pkthdr.len); + } + break; +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + /* Fix the header length, for AH processing */ + if (tdb->tdb_dst.sa.sa_family == AF_INET6) + { + if (m->m_pkthdr.len < sizeof(*ip6)) { + m_freem(m); + return ENXIO; + } + if (m->m_pkthdr.len - sizeof(*ip6) > IPV6_MAXPACKET) { + /* no jumbogram support */ + m_freem(m); + return ENXIO; /*?*/ + } + + ip6 = mtod(m, struct ip6_hdr *); + ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(*ip6)); + } + break; +#endif /* INET6 */ + + default: + m_freem(m); + DPRINTF(("ipsp_process_done(): unknown protocol family (%d)\n", + tdb->tdb_dst.sa.sa_family)); + return ENXIO; + } + + /* + * We're done with IPsec processing, transmit the packet using the + * appropriate network protocol (IP or IPv6). SPD lookup will be + * performed again there. + */ + switch (tdb->tdb_dst.sa.sa_family) + { +#ifdef INET + case AF_INET: + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + + return ip_output(m, NULL, NULL, IP_ENCAPSULATED | IP_RAWOUTPUT, + NULL, NULL); +#endif /* INET */ + +#ifdef INET6 + case AF_INET6: + /* + * We don't need massage, IPv6 header fields are always in + * net endian + */ + return ip6_output(m, NULL, NULL, IPV6_ENCAPSULATED, NULL, NULL); +#endif /* INET6 */ + } + + /* Not reached */ + return EINVAL; +} diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c index 6dc96735557..19d15bfac28 100644 --- a/sys/netinet/raw_ip.c +++ b/sys/netinet/raw_ip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: raw_ip.c,v 1.20 1999/12/19 02:54:29 itojun Exp $ */ +/* $OpenBSD: raw_ip.c,v 1.21 2000/09/19 03:20:59 angelos Exp $ */ /* $NetBSD: raw_ip.c,v 1.25 1996/02/18 18:58:33 christos Exp $ */ /* @@ -69,10 +69,6 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <netinet/in_var.h> #include <netinet/ip_icmp.h> -#ifdef IPSEC -extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); -#endif - #include <machine/stdarg.h> struct inpcbtable rawcbtable; @@ -448,7 +444,7 @@ rip_usrreq(so, req, m, nam, control) dst = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr; } #ifdef IPSEC - if (!(error = check_ipsec_policy(inp, dst))) + /* XXX Find an IPsec TDB */ #endif error = rip_output(m, so, dst); m = NULL; diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c index d21db5f3f32..45e28e9a560 100644 --- a/sys/netinet/tcp_input.c +++ b/sys/netinet/tcp_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_input.c,v 1.71 2000/09/18 23:59:39 fgsch Exp $ */ +/* $OpenBSD: tcp_input.c,v 1.72 2000/09/19 03:20:59 angelos Exp $ */ /* $NetBSD: tcp_input.c,v 1.23 1996/02/13 23:43:44 christos Exp $ */ /* @@ -90,6 +90,8 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <netinet/icmp6.h> #include <netinet6/nd6.h> +#define PI_MAGIC 0xdeadbeef /* XXX the horror! */ + struct tcpiphdr tcp_saveti; struct tcpipv6hdr tcp_saveti6; @@ -384,32 +386,29 @@ tcp_input(m, va_alist) int iphlen; va_list ap; register struct tcphdr *th; -#ifdef IPSEC - struct tdb *tdb = NULL; -#endif /* IPSEC */ #ifdef INET6 struct in6_addr laddr6; struct ip6_hdr *ipv6 = NULL; #endif /* INET6 */ +#ifdef IPSEC + struct tdb_ident *tdbi; + struct tdb *tdb; + int error, s; +#endif /* IPSEC */ int af; +#ifdef IPSEC + tdbi = (struct tdb_ident *) m->m_pkthdr.tdbi; + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; +#endif /* IPSEC */ + va_start(ap, m); iphlen = va_arg(ap, int); va_end(ap); tcpstat.tcps_rcvtotal++; -#ifdef IPSEC - /* Save the last SA which was used to process the mbuf */ - if ((m->m_flags & (M_CONF|M_AUTH)) && m->m_pkthdr.tdbi) { - struct tdb_ident *tdbi = m->m_pkthdr.tdbi; - /* XXX gettdb() should really be called at spltdb(). */ - /* XXX this is splsoftnet(), currently they are the same. */ - tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto); - free(m->m_pkthdr.tdbi, M_TEMP); - m->m_pkthdr.tdbi = NULL; - } -#endif /* IPSEC */ /* * Before we do ANYTHING, we have to figure out if it's TCP/IPv6 or * TCP/IPv4. @@ -424,6 +423,10 @@ tcp_input(m, va_alist) af = AF_INET; break; default: +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); return; /*EAFNOSUPPORT*/ } @@ -436,6 +439,10 @@ tcp_input(m, va_alist) case AF_INET: #ifdef DIAGNOSTIC if (iphlen < sizeof(struct ip)) { +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); return; } @@ -446,6 +453,10 @@ tcp_input(m, va_alist) iphlen = sizeof(struct ip); #else printf("extension headers are not allowed\n"); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); return; #endif @@ -456,6 +467,10 @@ tcp_input(m, va_alist) #ifdef DIAGNOSTIC if (iphlen < sizeof(struct ip6_hdr)) { m_freem(m); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } #endif /* DIAGNOSTIC */ @@ -465,6 +480,10 @@ tcp_input(m, va_alist) iphlen = sizeof(struct ip6_hdr); #else printf("extension headers are not allowed\n"); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); return; #endif @@ -472,6 +491,10 @@ tcp_input(m, va_alist) break; #endif default: +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); return; } @@ -480,6 +503,10 @@ tcp_input(m, va_alist) m = m_pullup2(m, iphlen + sizeof(struct tcphdr)); if (m == 0) { tcpstat.tcps_rcvshort++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } } @@ -567,6 +594,10 @@ tcp_input(m, va_alist) if (m->m_len < iphlen + off) { if ((m = m_pullup2(m, iphlen + off)) == 0) { tcpstat.tcps_rcvshort++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } switch (af) { @@ -779,30 +810,24 @@ findpcb: } #ifdef IPSEC - /* Check if this socket requires security for incoming packets */ - if ((inp->inp_seclevel[SL_AUTH] >= IPSEC_LEVEL_REQUIRE && - !(m->m_flags & M_AUTH)) || - (inp->inp_seclevel[SL_ESP_TRANS] >= IPSEC_LEVEL_REQUIRE && - !(m->m_flags & M_CONF))) { -#ifdef notyet - switch (af) { -#ifdef INET6 - case AF_INET6: - icmp6_error(m, ICMPV6_BLAH, ICMPV6_BLAH, 0); - break; -#endif /* INET6 */ - case AF_INET: - icmp_error(m, ICMP_BLAH, ICMP_BLAH, 0, 0); - break; - } -#endif /* notyet */ - tcpstat.tcps_rcvnosec++; + s = splnet(); + if (tdbi == NULL) + tdb = NULL; + else + tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto); + + ipsp_spd_lookup(m, af, iphlen, &error, IPSP_DIRECTION_IN, + tdb, inp); + splx(s); + + if (tdbi) + free(tdbi, M_TEMP); + tdbi = NULL; + + /* Error or otherwise drop-packet indication */ + if (error) goto drop; - } - /* Use tdb_bind_out for this inp's outbound communication */ - if (tdb) - tdb_add_inp(tdb, inp); -#endif /*IPSEC */ +#endif /* IPSEC */ /* * Segment received on connection. @@ -2080,6 +2105,9 @@ dropwithreset: return; drop: + if (tdbi) + free(tdbi, M_TEMP); + /* * Drop space held by incoming segment and return. */ diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c index c0646e89a2e..bea80fb06f2 100644 --- a/sys/netinet/tcp_usrreq.c +++ b/sys/netinet/tcp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: tcp_usrreq.c,v 1.46 2000/07/11 16:53:22 provos Exp $ */ +/* $OpenBSD: tcp_usrreq.c,v 1.47 2000/09/19 03:20:59 angelos Exp $ */ /* $NetBSD: tcp_usrreq.c,v 1.20 1996/02/13 23:44:16 christos Exp $ */ /* @@ -82,10 +82,6 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #include <netinet/tcp_debug.h> #include <dev/rndvar.h> -#ifdef IPSEC -extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); -#endif - /* * TCP protocol interface to socket abstraction. */ @@ -393,9 +389,7 @@ tcp_usrreq(so, req, m, nam, control) */ case PRU_SEND: #ifdef IPSEC - error = check_ipsec_policy(inp, 0); - if (error) - break; + /* XXX Find IPsec TDB */ #endif sbappend(&so->so_snd, m); error = tcp_output(tp); diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c index 7da01d6763e..1c4d2fa888f 100644 --- a/sys/netinet/udp_usrreq.c +++ b/sys/netinet/udp_usrreq.c @@ -1,4 +1,4 @@ -/* $OpenBSD: udp_usrreq.c,v 1.47 2000/07/27 06:29:09 itojun Exp $ */ +/* $OpenBSD: udp_usrreq.c,v 1.48 2000/09/19 03:21:00 angelos Exp $ */ /* $NetBSD: udp_usrreq.c,v 1.28 1996/03/16 23:54:03 christos Exp $ */ /* @@ -77,8 +77,6 @@ didn't get a copy, you may request one from <license@ipv6.nrl.navy.mil>. #ifdef IPSEC #include <netinet/ip_ipsp.h> - -extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); #endif #include <machine/stdarg.h> @@ -93,6 +91,8 @@ extern int check_ipsec_policy __P((struct inpcb *, u_int32_t)); #include <netinet/icmp6.h> #include <netinet6/ip6protosw.h> +#define PI_MAGIC 0xdeadbeef /* XXX the horror! */ + extern int ip6_defhlim; #endif /* INET6 */ @@ -174,7 +174,13 @@ udp_input(m, va_alist) struct ip6_hdr *ipv6; #endif /* INET6 */ #ifdef IPSEC - struct tdb *tdb = NULL; + struct tdb_ident *tdbi; + struct tdb *tdb; + int error, s; + + tdbi = (struct tdb_ident *) m->m_pkthdr.tdbi; + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; #endif /* IPSEC */ va_start(ap, m); @@ -183,18 +189,6 @@ udp_input(m, va_alist) udpstat.udps_ipackets++; -#ifdef IPSEC - /* Save the last SA which was used to process the mbuf */ - if ((m->m_flags & (M_CONF|M_AUTH)) && m->m_pkthdr.tdbi) { - struct tdb_ident *tdbi = m->m_pkthdr.tdbi; - /* XXX gettdb() should really be called at spltdb(). */ - /* XXX this is splsoftnet(), currently they are the same. */ - tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto); - free(m->m_pkthdr.tdbi, M_TEMP); - m->m_pkthdr.tdbi = NULL; - } -#endif /* IPSEC */ - switch (mtod(m, struct ip *)->ip_v) { case 4: ip = mtod(m, struct ip *); @@ -237,6 +231,10 @@ udp_input(m, va_alist) if (m->m_len < iphlen + sizeof(struct udphdr)) { if ((m = m_pullup2(m, iphlen + sizeof(struct udphdr))) == 0) { udpstat.udps_hdrops++; +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } #ifdef INET6 @@ -305,6 +303,10 @@ udp_input(m, va_alist) if ((uh->uh_sum = in_cksum(m, len + sizeof (struct ip))) != 0) { udpstat.udps_badsum++; m_freem(m); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } } else @@ -478,6 +480,10 @@ udp_input(m, va_alist) goto bad; } sorwakeup(last); +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } /* @@ -522,31 +528,37 @@ udp_input(m, va_alist) icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0); } +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ return; } } #ifdef IPSEC - /* Check if this socket requires security for incoming packets */ - if ((inp->inp_seclevel[SL_AUTH] >= IPSEC_LEVEL_REQUIRE && - !(m->m_flags & M_AUTH)) || - (inp->inp_seclevel[SL_ESP_TRANS] >= IPSEC_LEVEL_REQUIRE && - !(m->m_flags & M_CONF))) { -#ifdef notyet -#ifdef INET6 - if (ipv6) - ipv6_icmp_error(m, ICMPV6_BLAH, ICMPV6_BLAH, 0); - else -#endif /* INET6 */ - icmp_error(m, ICMP_BLAH, ICMP_BLAH, 0, 0); - m = NULL; -#endif /* notyet */ - udpstat.udps_nosec++; +#define PI_MAGIC 0xdeadbeef /* XXX the horror! */ + tdbi = (struct tdb_ident *) m->m_pkthdr.tdbi; + if (tdbi == (void *) PI_MAGIC) + tdbi = NULL; + + s = splnet(); + if (tdbi == NULL) + tdb = NULL; + else + tdb = gettdb(tdbi->spi, &tdbi->dst, tdbi->proto); + + ipsp_spd_lookup(m, srcsa.sa.sa_family, iphlen, &error, + IPSP_DIRECTION_IN, tdb, inp); + splx(s); + + if (tdbi) + free(tdbi, M_TEMP); + tdbi = NULL; + + /* Error or otherwise drop-packet indication */ + if (error) goto bad; - } - /* Use tdb_bind_out for this inp's outbound communication */ - if (tdb) - tdb_add_inp(tdb, inp); #endif /*IPSEC */ opts = NULL; @@ -590,6 +602,10 @@ udp_input(m, va_alist) sorwakeup(inp->inp_socket); return; bad: +#ifdef IPSEC + if (tdbi) + free(tdbi, M_TEMP); +#endif /* IPSEC */ m_freem(m); if (opts) m_freem(opts); @@ -1161,9 +1177,7 @@ udp_usrreq(so, req, m, addr, control) case PRU_SEND: #ifdef IPSEC - error = check_ipsec_policy(inp,0); - if (error) - return (error); + /* XXX Find IPsec TDB */ #endif #ifdef INET6 if (inp->inp_flags & INP_IPV6) |