diff options
author | wollman <wollman@FreeBSD.org> | 1996-06-14 17:22:18 +0000 |
---|---|---|
committer | wollman <wollman@FreeBSD.org> | 1996-06-14 17:22:18 +0000 |
commit | 845782b7e052a1136c206b3e7ae58942a6a725bd (patch) | |
tree | 0dd5de95373d80204fd553830859671bd3bad40f /sys/netkey/key.c | |
parent | 99066eaf6d9a560cdc3e6b0de8c328e34cb45696 (diff) | |
download | FreeBSD-src-845782b7e052a1136c206b3e7ae58942a6a725bd.zip FreeBSD-src-845782b7e052a1136c206b3e7ae58942a6a725bd.tar.gz |
This is the `netkey' kernel key-management service (the PF_KEY analogue
to PF_ROUTE) from NRL's IPv6 distribution, heavily modified by me for
better source layout, formatting, and textual conventions. I am told
that this code is no longer under active development, but it's a useful
hack for those interested in doing work on network security, key management,
etc. This code has only been tested twice, so it should be considered
highly experimental.
Obtained from: ftp.ripe.net
Diffstat (limited to 'sys/netkey/key.c')
-rw-r--r-- | sys/netkey/key.c | 2270 |
1 files changed, 2270 insertions, 0 deletions
diff --git a/sys/netkey/key.c b/sys/netkey/key.c new file mode 100644 index 0000000..2edd29a --- /dev/null +++ b/sys/netkey/key.c @@ -0,0 +1,2270 @@ +/*---------------------------------------------------------------------- + key.c : Key Management Engine for BSD + + Copyright 1995 by Bao Phan, Randall Atkinson, & Dan McDonald, + All Rights Reserved. All Rights have been assigned to the US + Naval Research Laboratory (NRL). The NRL Copyright Notice and + License governs distribution and use of this software. + + Patents are pending on this technology. NRL grants a license + to use this technology at no cost under the terms below with + the additional requirement that software, hardware, and + documentation relating to use of this technology must include + the note that: + This product includes technology developed at and + licensed from the Information Technology Division, + US Naval Research Laboratory. + +----------------------------------------------------------------------*/ +/*---------------------------------------------------------------------- +# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995 + +COPYRIGHT NOTICE + +All of the documentation and software included in this software +distribution from the US Naval Research Laboratory (NRL) are +copyrighted by their respective developers. + +This software and documentation were developed at NRL by various +people. Those developers have each copyrighted the portions that they +developed at NRL and have assigned All Rights for those portions to +NRL. Outside the USA, NRL also has copyright on the software +developed at NRL. The affected files all contain specific copyright +notices and those notices must be retained in any derived work. + +NRL LICENSE + +NRL grants permission for redistribution and use in source and binary +forms, with or without modification, of the software and documentation +created at NRL provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + + This product includes software developed at the Information + Technology Division, US Naval Research Laboratory. + +4. Neither the name of the NRL nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of the US Naval +Research Laboratory (NRL). + +----------------------------------------------------------------------*/ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/domain.h> +#include <sys/mbuf.h> +#include <sys/proc.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/time.h> + +#include <net/raw_cb.h> +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> + +#ifdef INET6 +#include <netinet6/in6.h> +#include <netinet6/in6_var.h> +#endif /* INET6 */ + +#include <netkey/key.h> +#include <netkey/key_debug.h> + +#define SOCKADDR struct sockaddr + +#define KMALLOC(p, t, n) (p = (t) malloc((unsigned long)(n), M_SECA, M_DONTWAIT)) +#define KFREE(p) free((caddr_t)p, M_SECA); + +#define CRITICAL_DCL int critical_s; +#define CRITICAL_START critical_s = splnet() +#define CRITICAL_END splx(critical_s) + +#define TIME_SECONDS time.tv_sec +#define CURRENT_PID curproc->p_pid + +#define DEFARGS(arglist, args) arglist args; +#define AND ; + +#ifdef INET6 +#define MAXHASHKEYLEN (2 * sizeof(int) + 2 * sizeof(struct sockaddr_in6)) +#else +#define MAXHASHKEYLEN (2 * sizeof(int) + 2 * sizeof(struct sockaddr_in)) +#endif + + +/* + * Not clear whether these values should be + * tweakable at kernel config time. + */ +#define KEYTBLSIZE 61 +#define KEYALLOCTBLSIZE 61 +#define SO2SPITBLSIZE 61 + +/* + * These values should be tweakable... + * perhaps by using sysctl + */ + +#define MAXLARVALTIME 240; /* Lifetime of a larval key table entry */ +#define MAXKEYACQUIRE 1; /* Max number of key acquire messages sent */ + /* per destination address */ +#define MAXACQUIRETIME 15; /* Lifetime of acquire message */ + +/* + * Key engine tables and global variables + */ + +struct key_tblnode keytable[KEYTBLSIZE]; +struct key_allocnode keyalloctbl[KEYALLOCTBLSIZE]; +struct key_so2spinode so2spitbl[SO2SPITBLSIZE]; + +struct keyso_cb keyso_cb; +struct key_tblnode nullkeynode; +struct key_registry *keyregtable; +struct key_acquirelist *key_acquirelist; +u_long maxlarvallifetime = MAXLARVALTIME; +int maxkeyacquire = MAXKEYACQUIRE; +u_long maxacquiretime = MAXACQUIRETIME; + +extern SOCKADDR key_addr; + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define ADVANCE(x, n) \ + { x += ROUNDUP(n); } + +static int my_addr __P((SOCKADDR *)); +static int key_sendup __P((struct socket *, struct key_msghdr *)); + +/*---------------------------------------------------------------------- + * key_secassoc2msghdr(): + * Copy info from a security association into a key message buffer. + * Assume message buffer is sufficiently large to hold all security + * association information including src, dst, from, key and iv. + ----------------------------------------------------------------------*/ +int +key_secassoc2msghdr(struct key_secassoc *secassoc, + struct key_msghdr *km, + struct key_msgdata *keyinfo) +{ + char *cp; + DPRINTF(IDL_FINISHED, ("Entering key_secassoc2msghdr\n")); + + if ((km == 0) || (keyinfo == 0) || (secassoc == 0)) + return(-1); + + km->type = secassoc->type; + km->state = secassoc->state; + km->label = secassoc->label; + km->spi = secassoc->spi; + km->keylen = secassoc->keylen; + km->ivlen = secassoc->ivlen; + km->algorithm = secassoc->algorithm; + km->lifetype = secassoc->lifetype; + km->lifetime1 = secassoc->lifetime1; + km->lifetime2 = secassoc->lifetime2; + + /* + * Stuff src/dst/from/key/iv in buffer after + * the message header. + */ + cp = (char *)(km + 1); + + DPRINTF(IDL_FINISHED, ("sa2msghdr: 1\n")); + keyinfo->src = (SOCKADDR *)cp; + if (secassoc->src->sa_len) { + bcopy(secassoc->src, cp, secassoc->src->sa_len); + ADVANCE(cp, secassoc->src->sa_len); + } else { + bzero(cp, MAX_SOCKADDR_SZ); + ADVANCE(cp, MAX_SOCKADDR_SZ); + } + + DPRINTF(IDL_FINISHED, ("sa2msghdr: 2\n")); + keyinfo->dst = (SOCKADDR *)cp; + if (secassoc->dst->sa_len) { + bcopy(secassoc->dst, cp, secassoc->dst->sa_len); + ADVANCE(cp, secassoc->dst->sa_len); + } else { + bzero(cp, MAX_SOCKADDR_SZ); + ADVANCE(cp, MAX_SOCKADDR_SZ); + } + + DPRINTF(IDL_FINISHED, ("sa2msghdr: 3\n")); + keyinfo->from = (SOCKADDR *)cp; + if (secassoc->from->sa_len) { + bcopy(secassoc->from, cp, secassoc->from->sa_len); + ADVANCE(cp, secassoc->from->sa_len); + } else { + bzero(cp, MAX_SOCKADDR_SZ); + ADVANCE(cp, MAX_SOCKADDR_SZ); + } + + DPRINTF(IDL_FINISHED, ("sa2msghdr: 4\n")); + + keyinfo->key = cp; + keyinfo->keylen = secassoc->keylen; + if (secassoc->keylen) { + bcopy((char *)(secassoc->key), cp, secassoc->keylen); + ADVANCE(cp, secassoc->keylen); + } + + DPRINTF(IDL_FINISHED, ("sa2msghdr: 5\n")); + keyinfo->iv = cp; + keyinfo->ivlen = secassoc->ivlen; + if (secassoc->ivlen) { + bcopy((char *)(secassoc->iv), cp, secassoc->ivlen); + ADVANCE(cp, secassoc->ivlen); + } + + DDO(IDL_FINISHED,printf("msgbuf(len=%d):\n",(char *)cp - (char *)km)); + DDO(IDL_FINISHED,dump_buf((char *)km, (char *)cp - (char *)km)); + DPRINTF(IDL_FINISHED, ("sa2msghdr: 6\n")); + return(0); +} + + +/*---------------------------------------------------------------------- + * key_msghdr2secassoc(): + * Copy info from a key message buffer into a key_secassoc + * structure + ----------------------------------------------------------------------*/ +int +key_msghdr2secassoc(struct key_secassoc *secassoc, + struct key_msghdr *km, + struct key_msgdata *keyinfo) +{ + DPRINTF(IDL_FINISHED, ("Entering key_msghdr2secassoc\n")); + + if ((km == 0) || (keyinfo == 0) || (secassoc == 0)) + return(-1); + + secassoc->len = sizeof(*secassoc); + secassoc->type = km->type; + secassoc->state = km->state; + secassoc->label = km->label; + secassoc->spi = km->spi; + secassoc->keylen = km->keylen; + secassoc->ivlen = km->ivlen; + secassoc->algorithm = km->algorithm; + secassoc->lifetype = km->lifetype; + secassoc->lifetime1 = km->lifetime1; + secassoc->lifetime2 = km->lifetime2; + + if (keyinfo->src) { + KMALLOC(secassoc->src, SOCKADDR *, keyinfo->src->sa_len); + if (!secassoc->src) { + DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for src\n")); + return(-1); + } + bcopy((char *)keyinfo->src, (char *)secassoc->src, + keyinfo->src->sa_len); + } else + secassoc->src = NULL; + + if (keyinfo->dst) { + KMALLOC(secassoc->dst, SOCKADDR *, keyinfo->dst->sa_len); + if (!secassoc->dst) { + DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for dst\n")); + return(-1); + } + bcopy((char *)keyinfo->dst, (char *)secassoc->dst, + keyinfo->dst->sa_len); + } else + secassoc->dst = NULL; + + if (keyinfo->from) { + KMALLOC(secassoc->from, SOCKADDR *, keyinfo->from->sa_len); + if (!secassoc->from) { + DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for from\n")); + return(-1); + } + bcopy((char *)keyinfo->from, (char *)secassoc->from, + keyinfo->from->sa_len); + } else + secassoc->from = NULL; + + /* + * Make copies of key and iv + */ + if (secassoc->ivlen) { + KMALLOC(secassoc->iv, caddr_t, secassoc->ivlen); + if (secassoc->iv == 0) { + DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for iv\n")); + return(-1); + } + bcopy((char *)keyinfo->iv, (char *)secassoc->iv, secassoc->ivlen); + } else + secassoc->iv = NULL; + + if (secassoc->keylen) { + KMALLOC(secassoc->key, caddr_t, secassoc->keylen); + if (secassoc->key == 0) { + DPRINTF(IDL_ERROR,("msghdr2secassoc: can't allocate mem for key\n")); + if (secassoc->iv) + KFREE(secassoc->iv); + return(-1); + } + bcopy((char *)keyinfo->key, (char *)secassoc->key, secassoc->keylen); + } else + secassoc->key = NULL; + return(0); +} + + +/*---------------------------------------------------------------------- + * addrpart_equal(): + * Determine if the address portion of two sockaddrs are equal. + * Currently handles only AF_INET and AF_INET6 address families. + ----------------------------------------------------------------------*/ +static int +addrpart_equal(SOCKADDR *sa1, SOCKADDR *sa2) +{ + if ((sa1->sa_family != sa2->sa_family) || + (sa1->sa_len != sa2->sa_len)) + return 0; + + switch(sa1->sa_family) { + case AF_INET: + return (((struct sockaddr_in *)sa1)->sin_addr.s_addr == + ((struct sockaddr_in *)sa2)->sin_addr.s_addr); +#ifdef INET6 + case AF_INET6: + return (IN6_ADDR_EQUAL(((struct sockaddr_in6 *)sa1)->sin6_addr, + ((struct sockaddr_in6 *)sa2)->sin6_addr)); +#endif /* INET6 */ + } + return(0); +} + +/*---------------------------------------------------------------------- + * key_inittables(): + * Allocate space and initialize key engine tables + ----------------------------------------------------------------------*/ +int +key_inittables(void) +{ + int i; + + KMALLOC(keyregtable, struct key_registry *, sizeof(struct key_registry)); + if (!keyregtable) + return -1; + bzero((char *)keyregtable, sizeof(struct key_registry)); + KMALLOC(key_acquirelist, struct key_acquirelist *, + sizeof(struct key_acquirelist)); + if (!key_acquirelist) + return -1; + bzero((char *)key_acquirelist, sizeof(struct key_acquirelist)); + for (i = 0; i < KEYTBLSIZE; i++) + bzero((char *)&keytable[i], sizeof(struct key_tblnode)); + for (i = 0; i < KEYALLOCTBLSIZE; i++) + bzero((char *)&keyalloctbl[i], sizeof(struct key_allocnode)); + for (i = 0; i < SO2SPITBLSIZE; i++) + bzero((char *)&so2spitbl[i], sizeof(struct key_so2spinode)); + + return 0; +} + +static int +key_freetables(void) +{ + KFREE(keyregtable); + keyregtable = NULL; + KFREE(key_acquirelist); + key_acquirelist = NULL; + return 0; +} + +/*---------------------------------------------------------------------- + * key_gethashval(): + * Determine keytable hash value. + ----------------------------------------------------------------------*/ +static int +key_gethashval(char *buf, int len, int tblsize) +{ + int i, j = 0; + + /* + * Todo: Use word size xor and check for alignment + * and zero pad if necessary. Need to also pick + * a good hash function and table size. + */ + if (len <= 0) { + DPRINTF(IDL_ERROR,("key_gethashval got bogus len!\n")); + return(-1); + } + for(i = 0; i < len; i++) { + j ^= (u_int8_t)(*(buf + i)); + } + return (j % tblsize); +} + + +/*---------------------------------------------------------------------- + * key_createkey(): + * Create hash key for hash function + * key is: type+src+dst if keytype = 1 + * type+src+dst+spi if keytype = 0 + * Uses only the address portion of the src and dst sockaddrs to + * form key. Currently handles only AF_INET and AF_INET6 sockaddrs + ----------------------------------------------------------------------*/ +static int +key_createkey(char *buf, u_int type, SOCKADDR *src, SOCKADDR *dst, + u_int32_t spi, u_int keytype) +{ + char *cp, *p; + + DPRINTF(IDL_FINISHED,("Entering key_createkey\n")); + + if (!buf || !src || !dst) + return(-1); + + cp = buf; + bcopy((char *)&type, cp, sizeof(type)); + cp += sizeof(type); + +#ifdef INET6 + /* + * Assume only IPv4 and IPv6 addresses. + */ +#define ADDRPART(a) \ + ((a)->sa_family == AF_INET6) ? \ + (char *)&(((struct sockaddr_in6 *)(a))->sin6_addr) : \ + (char *)&(((struct sockaddr_in *)(a))->sin_addr) + +#define ADDRSIZE(a) \ + ((a)->sa_family == AF_INET6) ? sizeof(struct in_addr6) : \ + sizeof(struct in_addr) +#else /* INET6 */ +#define ADDRPART(a) (char *)&(((struct sockaddr_in *)(a))->sin_addr) +#define ADDRSIZE(a) sizeof(struct in_addr) +#endif /* INET6 */ + + DPRINTF(IDL_FINISHED,("src addr:\n")); + DDO(IDL_FINISHED,dump_smart_sockaddr(src)); + DPRINTF(IDL_FINISHED,("dst addr:\n")); + DDO(IDL_FINISHED,dump_smart_sockaddr(dst)); + + p = ADDRPART(src); + bcopy(p, cp, ADDRSIZE(src)); + cp += ADDRSIZE(src); + + p = ADDRPART(dst); + bcopy(p, cp, ADDRSIZE(dst)); + cp += ADDRSIZE(dst); + +#undef ADDRPART +#undef ADDRSIZE + + if (keytype == 0) { + bcopy((char *)&spi, cp, sizeof(spi)); + cp += sizeof(spi); + } + + DPRINTF(IDL_FINISHED,("hash key:\n")); + DDO(IDL_FINISHED, dump_buf(buf, cp - buf)); + return(cp - buf); +} + + +/*---------------------------------------------------------------------- + * key_sosearch(): + * Search the so2spi table for the security association allocated to + * the socket. Returns pointer to a struct key_so2spinode which can + * be used to locate the security association entry in the keytable. + ----------------------------------------------------------------------*/ +static struct key_so2spinode * +key_sosearch(u_int type, SOCKADDR *src, SOCKADDR *dst, struct socket *so) +{ + struct key_so2spinode *np = 0; + + if (!(src && dst)) { + DPRINTF(IDL_ERROR,("key_sosearch: got null src or dst pointer!\n")); + return(NULL); + } + + for (np = so2spitbl[((u_int32_t)so) % SO2SPITBLSIZE].next; np; np = np->next) { + if ((so == np->socket) && (type == np->keynode->secassoc->type) + && addrpart_equal(src, np->keynode->secassoc->src) + && addrpart_equal(dst, np->keynode->secassoc->dst)) + return(np); + } + return(NULL); +} + + +/*---------------------------------------------------------------------- + * key_sodelete(): + * Delete entries from the so2spi table. + * flag = 1 purge all entries + * flag = 0 delete entries with socket pointer matching socket + ----------------------------------------------------------------------*/ +static void +key_sodelete(struct socket *socket, int flag) +{ + struct key_so2spinode *prevnp, *np; + CRITICAL_DCL + + CRITICAL_START; + + DPRINTF(IDL_EVENT,("Entering keysodelete w/so=0x%x flag=%d\n", + (unsigned int)socket,flag)); + + if (flag) { + int i; + + for (i = 0; i < SO2SPITBLSIZE; i++) + for(np = so2spitbl[i].next; np; np = np->next) { + KFREE(np); + } + CRITICAL_END; + return; + } + + prevnp = &so2spitbl[((u_int32_t)socket) % SO2SPITBLSIZE]; + for(np = prevnp->next; np; np = np->next) { + if (np->socket == socket) { + struct socketlist *socklp, *prevsocklp; + + (np->keynode->alloc_count)--; + + /* + * If this socket maps to a unique secassoc, + * we go ahead and delete the secassoc, since it + * can no longer be allocated or used by any other + * socket. + */ + if (np->keynode->secassoc->state & K_UNIQUE) { + if (key_delete(np->keynode->secassoc) != 0) + panic("key_sodelete"); + np = prevnp; + continue; + } + + /* + * We traverse the socketlist and remove the entry + * for this socket + */ + DPRINTF(IDL_FINISHED,("keysodelete: deleting from socklist...")); + prevsocklp = np->keynode->solist; + for (socklp = prevsocklp->next; socklp; socklp = socklp->next) { + if (socklp->socket == socket) { + prevsocklp->next = socklp->next; + KFREE(socklp); + break; + } + prevsocklp = socklp; + } + DPRINTF(IDL_FINISHED,("done\n")); + prevnp->next = np->next; + KFREE(np); + np = prevnp; + } + prevnp = np; + } + CRITICAL_END; +} + + +/*---------------------------------------------------------------------- + * key_deleteacquire(): + * Delete an entry from the key_acquirelist + ----------------------------------------------------------------------*/ +static void +key_deleteacquire(u_int type, SOCKADDR *target) +{ + struct key_acquirelist *ap, *prev; + + prev = key_acquirelist; + for(ap = key_acquirelist->next; ap; ap = ap->next) { + if (addrpart_equal(target, (SOCKADDR *)&(ap->target)) && + (type == ap->type)) { + DPRINTF(IDL_EVENT,("Deleting entry from acquire list!\n")); + prev->next = ap->next; + KFREE(ap); + ap = prev; + } + prev = ap; + } +} + + +/*---------------------------------------------------------------------- + * key_search(): + * Search the key table for an entry with same type, src addr, dest + * addr, and spi. Returns a pointer to struct key_tblnode if found + * else returns null. + ----------------------------------------------------------------------*/ +static struct key_tblnode * +key_search(u_int type, SOCKADDR *src, SOCKADDR *dst, u_int32_t spi, + int indx, struct key_tblnode **prevkeynode) +{ + struct key_tblnode *keynode, *prevnode; + + if (indx > KEYTBLSIZE || indx < 0) + return (NULL); + if (!(&keytable[indx])) + return (NULL); + +#define sec_type keynode->secassoc->type +#define sec_spi keynode->secassoc->spi +#define sec_src keynode->secassoc->src +#define sec_dst keynode->secassoc->dst + + prevnode = &keytable[indx]; + for (keynode = keytable[indx].next; keynode; keynode = keynode->next) { + if ((type == sec_type) && (spi == sec_spi) && + addrpart_equal(src, sec_src) + && addrpart_equal(dst, sec_dst)) + break; + prevnode = keynode; + } + *prevkeynode = prevnode; + return(keynode); +} + + +/*---------------------------------------------------------------------- + * key_addnode(): + * Insert a key_tblnode entry into the key table. Returns a pointer + * to the newly created key_tblnode. + ----------------------------------------------------------------------*/ +static struct key_tblnode * +key_addnode(int indx, struct key_secassoc *secassoc) +{ + struct key_tblnode *keynode; + + DPRINTF(IDL_FINISHED,("Entering key_addnode w/indx=%d secassoc=0x%x\n", + indx, (unsigned int)secassoc)); + + if (!(&keytable[indx])) + return(NULL); + if (!secassoc) { + panic("key_addnode: Someone passed in a null secassoc!\n"); + } + + KMALLOC(keynode, struct key_tblnode *, sizeof(struct key_tblnode)); + if (keynode == 0) + return(NULL); + bzero((char *)keynode, sizeof(struct key_tblnode)); + + KMALLOC(keynode->solist, struct socketlist *, sizeof(struct socketlist)); + if (keynode->solist == 0) { + KFREE(keynode); + return(NULL); + } + bzero((char *)(keynode->solist), sizeof(struct socketlist)); + + keynode->secassoc = secassoc; + keynode->solist->next = NULL; + keynode->next = keytable[indx].next; + keytable[indx].next = keynode; + return(keynode); +} + + +/*---------------------------------------------------------------------- + * key_add(): + * Add a new security association to the key table. Caller is + * responsible for allocating memory for the key_secassoc as + * well as the buffer space for the key, iv. Assumes the security + * association passed in is well-formed. + ----------------------------------------------------------------------*/ +int +key_add(struct key_secassoc *secassoc) +{ + char buf[MAXHASHKEYLEN]; + int len, indx; + int inbound = 0; + int outbound = 0; + struct key_tblnode *keynode, *prevkeynode; + struct key_allocnode *np = NULL; + CRITICAL_DCL + + DPRINTF(IDL_FINISHED, ("Entering key_add w/secassoc=0x%x\n", + (unsigned int)secassoc)); + + if (!secassoc) { + panic("key_add: who the hell is passing me a null pointer"); + } + + /* + * Should we allow a null key to be inserted into the table ? + * or can we use null key to indicate some policy action... + */ + +#if 0 + /* + * For esp using des-cbc or tripple-des we call + * des_set_odd_parity. + */ + if (secassoc->key && (secassoc->type == KEY_TYPE_ESP) && + ((secassoc->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC) || + (secassoc->algorithm == IPSEC_ALGTYPE_ESP_3DES))) + des_set_odd_parity(secassoc->key); +#endif /* 0 */ + + /* + * Check if secassoc with same spi exists before adding + */ + bzero((char *)&buf, sizeof(buf)); + len = key_createkey((char *)&buf, secassoc->type, secassoc->src, + secassoc->dst, secassoc->spi, 0); + indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); + DPRINTF(IDL_FINISHED,("keyadd: keytbl hash position=%d\n", indx)); + keynode = key_search(secassoc->type, secassoc->src, secassoc->dst, + secassoc->spi, indx, &prevkeynode); + if (keynode) { + DPRINTF(IDL_EVENT,("keyadd: secassoc already exists!\n")); + return(-2); + } + + inbound = my_addr(secassoc->dst); + outbound = my_addr(secassoc->src); + DPRINTF(IDL_FINISHED,("inbound=%d outbound=%d\n", inbound, outbound)); + + /* + * We allocate mem for an allocation entry if needed. + * This is done here instead of in the allocaton code + * segment so that we can easily recover/cleanup from a + * memory allocation error. + */ + if (outbound || (!inbound && !outbound)) { + KMALLOC(np, struct key_allocnode *, sizeof(struct key_allocnode)); + if (np == 0) { + DPRINTF(IDL_ERROR,("keyadd: can't allocate allocnode!\n")); + return(-1); + } + } + + CRITICAL_START; + + if ((keynode = key_addnode(indx, secassoc)) == NULL) { + DPRINTF(IDL_ERROR,("keyadd: key_addnode failed!\n")); + if (np) + KFREE(np); + CRITICAL_END; + return(-1); + } + DPRINTF(IDL_GROSS_EVENT,("Added new keynode:\n")); + DDO(IDL_FINISHED, dump_keytblnode(keynode)); + DDO(IDL_FINISHED, dump_secassoc(keynode->secassoc)); + + /* + * We add an entry to the allocation table for + * this secassoc if the interfaces are up, + * the secassoc is outbound. In the case + * where the interfaces are not up, we go ahead + * , do it anyways. This wastes an allocation + * entry if the secassoc later turned out to be + * inbound when the interfaces are ifconfig up. + */ + if (outbound || (!inbound && !outbound)) { + len = key_createkey((char *)&buf, secassoc->type, secassoc->src, + secassoc->dst, 0, 1); + indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); + DPRINTF(IDL_FINISHED,("keyadd: keyalloc hash position=%d\n", indx)); + np->keynode = keynode; + np->next = keyalloctbl[indx].next; + keyalloctbl[indx].next = np; + } + if (inbound) + secassoc->state |= K_INBOUND; + if (outbound) + secassoc->state |= K_OUTBOUND; + + key_deleteacquire(secassoc->type, secassoc->dst); + + CRITICAL_END; + return 0; +} + + +/*---------------------------------------------------------------------- + * key_get(): + * Get a security association from the key table. + ----------------------------------------------------------------------*/ +int +key_get(u_int type, SOCKADDR *src, SOCKADDR *dst, u_int32_t spi, + struct key_secassoc **secassoc) +{ + char buf[MAXHASHKEYLEN]; + struct key_tblnode *keynode, *prevkeynode; + int len, indx; + + bzero(&buf, sizeof(buf)); + *secassoc = NULL; + len = key_createkey((char *)&buf, type, src, dst, spi, 0); + indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); + DPRINTF(IDL_FINISHED,("keyget: indx=%d\n",indx)); + keynode = key_search(type, src, dst, spi, indx, &prevkeynode); + if (keynode) { + DPRINTF(IDL_GROSS_EVENT,("keyget: found it! keynode=0x%x", + (unsigned int)keynode)); + *secassoc = keynode->secassoc; + return(0); + } else + return(-1); /* Not found */ +} + + +/*---------------------------------------------------------------------- + * key_dump(): + * Dump all valid entries in the keytable to a pf_key socket. Each + * security associaiton is sent one at a time in a pf_key message. A + * message with seqno = 0 signifies the end of the dump transaction. + ----------------------------------------------------------------------*/ +int +key_dump(struct socket *so) +{ + int len, i; + int seq = 1; + struct key_msgdata keyinfo; + struct key_msghdr *km; + struct key_tblnode *keynode; + + /* + * Routine to dump the key table to a routing socket + * Use for debugging only! + */ + + KMALLOC(km, struct key_msghdr *, sizeof(struct key_msghdr) + + 3 * MAX_SOCKADDR_SZ + MAX_KEY_SZ + MAX_IV_SZ); + if (!km) + return(ENOBUFS); + + DPRINTF(IDL_FINISHED,("Entering key_dump()")); + /* + * We need to speed this up later. Fortunately, key_dump + * messages are not sent often. + */ + for (i = 0; i < KEYTBLSIZE; i++) { + for (keynode = keytable[i].next; keynode; keynode = keynode->next) { + /* + * We exclude dead/larval/zombie security associations for now + * but it may be useful to also send these up for debugging purposes + */ + if (keynode->secassoc->state & (K_DEAD | K_LARVAL | K_ZOMBIE)) + continue; + + len = (sizeof(struct key_msghdr) + + ROUNDUP(keynode->secassoc->src->sa_len) + + ROUNDUP(keynode->secassoc->dst->sa_len) + + ROUNDUP(keynode->secassoc->from->sa_len) + + ROUNDUP(keynode->secassoc->keylen) + + ROUNDUP(keynode->secassoc->ivlen)); + + if (key_secassoc2msghdr(keynode->secassoc, km, &keyinfo) != 0) + panic("key_dump"); + + km->key_msglen = len; + km->key_msgvers = KEY_VERSION; + km->key_msgtype = KEY_DUMP; + km->key_pid = CURRENT_PID; + km->key_seq = seq++; + km->key_errno = 0; + + key_sendup(so, km); + } + } + bzero((char *)km, sizeof(struct key_msghdr)); + km->key_msglen = sizeof(struct key_msghdr); + km->key_msgvers = KEY_VERSION; + km->key_msgtype = KEY_DUMP; + km->key_pid = CURRENT_PID; + km->key_seq = 0; + km->key_errno = 0; + + key_sendup(so, km); + KFREE(km); + DPRINTF(IDL_FINISHED,("Leaving key_dump()\n")); + return(0); +} + +/*---------------------------------------------------------------------- + * key_delete(): + * Delete a security association from the key table. + ----------------------------------------------------------------------*/ +int +key_delete(struct key_secassoc *secassoc) +{ + char buf[MAXHASHKEYLEN]; + int len, indx; + struct key_tblnode *keynode = 0; + struct key_tblnode *prevkeynode = 0; + struct socketlist *socklp, *deadsocklp; + struct key_so2spinode *np, *prevnp; + struct key_allocnode *ap, *prevap; + CRITICAL_DCL + + DPRINTF(IDL_FINISHED,("Entering key_delete w/secassoc=0x%x\n", + (unsigned int)secassoc)); + + bzero((char *)&buf, sizeof(buf)); + len = key_createkey((char *)&buf, secassoc->type, secassoc->src, + secassoc->dst, secassoc->spi, 0); + indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); + DPRINTF(IDL_FINISHED,("keydelete: keytbl hash position=%d\n", indx)); + keynode = key_search(secassoc->type, secassoc->src, secassoc->dst, + secassoc->spi, indx, &prevkeynode); + + if (keynode) { + CRITICAL_START; + DPRINTF(IDL_GROSS_EVENT,("keydelete: found keynode to delete\n")); + keynode->secassoc->state |= K_DEAD; + + if (keynode->ref_count > 0) { + DPRINTF(IDL_EVENT,("keydelete: secassoc still held, marking for deletion only!\n")); + CRITICAL_END; + return(0); + } + + prevkeynode->next = keynode->next; + + /* + * Walk the socketlist, delete the + * entries mapping sockets to this secassoc + * from the so2spi table. + */ + DPRINTF(IDL_FINISHED,("keydelete: deleting socklist...")); + for(socklp = keynode->solist->next; socklp; ) { + prevnp = &so2spitbl[((u_int32_t)(socklp->socket)) % SO2SPITBLSIZE]; + for(np = prevnp->next; np; np = np->next) { + if ((np->socket == socklp->socket) && (np->keynode == keynode)) { + prevnp->next = np->next; + KFREE(np); + break; + } + prevnp = np; + } + deadsocklp = socklp; + socklp = socklp->next; + KFREE(deadsocklp); + } + DPRINTF(IDL_FINISHED,("done\n")); + /* + * If an allocation entry exist for this + * secassoc, delete it. + */ + bzero((char *)&buf, sizeof(buf)); + len = key_createkey((char *)&buf, secassoc->type, secassoc->src, + secassoc->dst, 0, 1); + indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); + DPRINTF(IDL_FINISHED,("keydelete: alloctbl hash position=%d\n", indx)); + prevap = &keyalloctbl[indx]; + for (ap = prevap->next; ap; ap = ap->next) { + if (ap->keynode == keynode) { + prevap->next = ap->next; + KFREE(ap); + break; + } + prevap = ap; + } + + if (keynode->secassoc->iv) + KFREE(keynode->secassoc->iv); + if (keynode->secassoc->key) + KFREE(keynode->secassoc->key); + KFREE(keynode->secassoc); + if (keynode->solist) + KFREE(keynode->solist); + KFREE(keynode); + CRITICAL_END; + return(0); + } + return(-1); +} + + +/*---------------------------------------------------------------------- + * key_flush(): + * Delete all entries from the key table. + ----------------------------------------------------------------------*/ +void +key_flush(void) +{ + struct key_tblnode *keynode; + int i; + + /* + * This is slow, but simple. + */ + DPRINTF(IDL_FINISHED,("Flushing key table...")); + for (i = 0; i < KEYTBLSIZE; i++) { + while ((keynode = keytable[i].next)) + if (key_delete(keynode->secassoc) != 0) + panic("key_flush"); + } + DPRINTF(IDL_FINISHED,("done\n")); +} + + +/*---------------------------------------------------------------------- + * key_getspi(): + * Get a unique spi value for a key management daemon/program. The + * spi value, once assigned, cannot be assigned again (as long as the + * entry with that same spi value remains in the table). + ----------------------------------------------------------------------*/ +int +key_getspi(u_int type, SOCKADDR *src, SOCKADDR *dst, u_int32_t lowval, + u_int32_t highval, u_int32_t *spi) +{ + struct key_secassoc *secassoc; + struct key_tblnode *keynode, *prevkeynode; + int count, done, len, indx; + int maxcount = 1000; + u_int32_t val; + char buf[MAXHASHKEYLEN]; + CRITICAL_DCL + + DPRINTF(IDL_EVENT,("Entering getspi w/type=%d,low=%u,high=%u\n", + type, lowval, highval)); + if (!(src && dst)) + return(EINVAL); + + if ((lowval == 0) || (highval == 0)) + return(EINVAL); + + if (lowval > highval) { + u_int32_t temp; + temp = lowval; + lowval = highval; + highval = lowval; + } + + done = count = 0; + do { + count++; + /* + * This may not be "random enough". + */ + val = lowval + (random() % (highval - lowval + 1)); + + if (lowval == highval) + count = maxcount; + DPRINTF(IDL_FINISHED,("%u ",val)); + if (val) { + DPRINTF(IDL_FINISHED,("\n")); + bzero(&buf, sizeof(buf)); + len = key_createkey((char *)&buf, type, src, dst, val, 0); + indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); + if (!key_search(type, src, dst, val, indx, &prevkeynode)) { + CRITICAL_START; + KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); + if (secassoc == 0) { + DPRINTF(IDL_ERROR,("key_getspi: can't allocate memory\n")); + CRITICAL_END; + return(ENOBUFS); + } + bzero((char *)secassoc, sizeof(*secassoc)); + + DPRINTF(IDL_FINISHED,("getspi: indx=%d\n",indx)); + secassoc->len = sizeof(struct key_secassoc); + secassoc->type = type; + secassoc->spi = val; + secassoc->state |= K_LARVAL; + if (my_addr(secassoc->dst)) + secassoc->state |= K_INBOUND; + if (my_addr(secassoc->src)) + secassoc->state |= K_OUTBOUND; + + bcopy((char *)src, (char *)secassoc->src, src->sa_len); + bcopy((char *)dst, (char *)secassoc->dst, dst->sa_len); + + /* We fill this in with a plausable value now to insure + that other routines don't break. These will get + overwritten later with the correct values. */ +#ifdef INET6 + secassoc->from->sa_family = AF_INET6; + secassoc->from->sa_len = sizeof(struct sockaddr_in6); +#else /* INET6 */ + secassoc->from->sa_family = AF_INET; + secassoc->from->sa_len = sizeof(struct sockaddr_in); +#endif /* INET6 */ + + /* + * We need to add code to age these larval key table + * entries so they don't linger forever waiting for + * a KEY_UPDATE message that may not come for various + * reasons. This is another task that key_reaper can + * do once we have it coded. + */ + secassoc->lifetime1 += TIME_SECONDS + maxlarvallifetime; + + if (!(keynode = key_addnode(indx, secassoc))) { + DPRINTF(IDL_ERROR,("key_getspi: can't add node\n")); + CRITICAL_END; + return(ENOBUFS); + } + DPRINTF(IDL_FINISHED,("key_getspi: added node 0x%x\n", + (unsigned int)keynode)); + done++; + CRITICAL_END; + } + } + } while ((count < maxcount) && !done); + DPRINTF(IDL_EVENT,("getspi returns w/spi=%u,count=%d\n",val,count)); + if (done) { + *spi = val; + return(0); + } else { + *spi = 0; + return(EADDRNOTAVAIL); + } +} + + +/*---------------------------------------------------------------------- + * key_update(): + * Update a keytable entry that has an spi value assigned but is + * incomplete (e.g. no key/iv). + ----------------------------------------------------------------------*/ +int +key_update(struct key_secassoc *secassoc) +{ + struct key_tblnode *keynode, *prevkeynode; + struct key_allocnode *np = 0; + u_int8_t newstate; + int len, indx, inbound, outbound; + char buf[MAXHASHKEYLEN]; + CRITICAL_DCL + + bzero(&buf, sizeof(buf)); + len = key_createkey((char *)&buf, secassoc->type, secassoc->src, + secassoc->dst, secassoc->spi, 0); + indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); + if(!(keynode = key_search(secassoc->type, secassoc->src, secassoc->dst, + secassoc->spi, indx, &prevkeynode))) { + return(ESRCH); + } + if (keynode->secassoc->state & K_DEAD) + return(ESRCH); + + /* Should we also restrict updating of only LARVAL entries ? */ + + CRITICAL_START; + + inbound = my_addr(secassoc->dst); + outbound = my_addr(secassoc->src); + + newstate = keynode->secassoc->state; + newstate &= ~K_LARVAL; + + if (inbound) + newstate |= K_INBOUND; + if (outbound) + newstate |= K_OUTBOUND; + + if (outbound || (!inbound && !outbound)) { + KMALLOC(np, struct key_allocnode *, sizeof(struct key_allocnode)); + if (np == 0) { + DPRINTF(IDL_ERROR,("keyupdate: can't allocate allocnode!\n")); + CRITICAL_END; + return(ENOBUFS); + } + } + + /* + * Free the old key, iv if they're there. + */ + if (keynode->secassoc->key) + KFREE(keynode->secassoc->key); + if (keynode->secassoc->iv) + KFREE(keynode->secassoc->iv); + + /* + * We now copy the secassoc over. We don't need to copy + * the key, iv into new buffers since the calling routine + * does that already. + */ + + *(keynode->secassoc) = *secassoc; + keynode->secassoc->state = newstate; + + /* + * Should we allow a null key to be inserted into the table ? + * or can we use null key to indicate some policy action... + */ + +#if 0 + if (keynode->secassoc->key && + (keynode->secassoc->type == KEY_TYPE_ESP) && + ((keynode->secassoc->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC) || + (keynode->secassoc->algorithm == IPSEC_ALGTYPE_ESP_3DES))) + des_set_odd_parity(keynode->secassoc->key); +#endif /* 0 */ + + /* + * We now add an entry to the allocation table for this + * updated key table entry. + */ + if (outbound || (!inbound && !outbound)) { + len = key_createkey((char *)&buf, secassoc->type, secassoc->src, + secassoc->dst, 0, 1); + indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); + DPRINTF(IDL_FINISHED,("keyupdate: keyalloc hash position=%d\n", indx)); + np->keynode = keynode; + np->next = keyalloctbl[indx].next; + keyalloctbl[indx].next = np; + } + + key_deleteacquire(secassoc->type, (SOCKADDR *)&(secassoc->dst)); + + CRITICAL_END; + return(0); +} + +/*---------------------------------------------------------------------- + * key_register(): + * Register a socket as one capable of acquiring security associations + * for the kernel. + ----------------------------------------------------------------------*/ +int +key_register(struct socket *socket, u_int type) +{ + struct key_registry *p, *new; + CRITICAL_DCL + + CRITICAL_START; + + DPRINTF(IDL_EVENT,("Entering key_register w/so=0x%x,type=%d\n", + (unsigned int)socket,type)); + + if (!(keyregtable && socket)) + panic("key_register"); + + /* + * Make sure entry is not already in table + */ + for(p = keyregtable->next; p; p = p->next) { + if ((p->type == type) && (p->socket == socket)) { + CRITICAL_END; + return(EEXIST); + } + } + + KMALLOC(new, struct key_registry *, sizeof(struct key_registry)); + if (new == 0) { + CRITICAL_END; + return(ENOBUFS); + } + new->type = type; + new->socket = socket; + new->next = keyregtable->next; + keyregtable->next = new; + CRITICAL_END; + return(0); +} + +/*---------------------------------------------------------------------- + * key_unregister(): + * Delete entries from the registry list. + * allflag = 1 : delete all entries with matching socket + * allflag = 0 : delete only the entry matching socket, type + ----------------------------------------------------------------------*/ +void +key_unregister(struct socket *socket, u_int type, int allflag) +{ + struct key_registry *p, *prev; + CRITICAL_DCL + + CRITICAL_START; + + DPRINTF(IDL_EVENT,("Entering key_unregister w/so=0x%x,type=%d,flag=%d\n", + (unsigned int)socket, type, allflag)); + + if (!(keyregtable && socket)) + panic("key_register"); + prev = keyregtable; + for(p = keyregtable->next; p; p = p->next) { + if ((allflag && (p->socket == socket)) || + ((p->type == type) && (p->socket == socket))) { + prev->next = p->next; + KFREE(p); + p = prev; + } + prev = p; + } + CRITICAL_END; +} + + +/*---------------------------------------------------------------------- + * key_acquire(): + * Send a key_acquire message to all registered key mgnt daemons + * capable of acquire security association of type type. + * + * Return: 0 if succesfully called key mgnt. daemon(s) + * -1 if not successfull. + ----------------------------------------------------------------------*/ +int +key_acquire(u_int type, SOCKADDR *src, SOCKADDR *dst) +{ + struct key_registry *p; + struct key_acquirelist *ap, *prevap; + int success = 0, created = 0; + u_int etype; + struct key_msghdr *km = NULL; + int len; + + DPRINTF(IDL_EVENT,("Entering key_acquire()\n")); + + if (!keyregtable || !src || !dst) + return (-1); + + /* + * We first check the acquirelist to see if a key_acquire + * message has been sent for this destination. + */ + etype = type; + prevap = key_acquirelist; + for(ap = key_acquirelist->next; ap; ap = ap->next) { + if (addrpart_equal(dst, ap->target) && + (etype == ap->type)) { + DPRINTF(IDL_EVENT,("acquire message previously sent!\n")); + if (ap->expiretime < TIME_SECONDS) { + DPRINTF(IDL_EVENT,("acquire message has expired!\n")); + ap->count = 0; + break; + } + if (ap->count < maxkeyacquire) { + DPRINTF(IDL_EVENT,("max acquire messages not yet exceeded!\n")); + break; + } + return(0); + } else if (ap->expiretime < TIME_SECONDS) { + /* + * Since we're already looking at the list, we may as + * well delete expired entries as we scan through the list. + * This should really be done by a function like key_reaper() + * but until we code key_reaper(), this is a quick, dirty + * hack. + */ + DPRINTF(IDL_EVENT,("found an expired entry...deleting it!\n")); + prevap->next = ap->next; + KFREE(ap); + ap = prevap; + } + prevap = ap; + } + + /* + * Scan registry, send KEY_ACQUIRE message to + * appropriate key management daemons. + */ + for(p = keyregtable->next; p; p = p->next) { + if (p->type != type) + continue; + + if (!created) { + len = sizeof(struct key_msghdr) + ROUNDUP(src->sa_len) + + ROUNDUP(dst->sa_len); + KMALLOC(km, struct key_msghdr *, len); + if (!km) { + DPRINTF(IDL_ERROR,("key_acquire: no memory\n")); + return(-1); + } + DPRINTF(IDL_FINISHED,("key_acquire/created: 1\n")); + bzero((char *)km, len); + km->key_msglen = len; + km->key_msgvers = KEY_VERSION; + km->key_msgtype = KEY_ACQUIRE; + km->type = type; + DPRINTF(IDL_FINISHED,("key_acquire/created: 2\n")); + /* + * This is inefficient, slow. + */ + + /* + * We zero out sin_zero here for AF_INET addresses because + * ip_output() currently does not do it for performance reasons. + */ + if (src->sa_family == AF_INET) + bzero((char *)(((struct sockaddr_in *)src)->sin_zero), + sizeof(((struct sockaddr_in *)src)->sin_zero)); + if (dst->sa_family == AF_INET) + bzero((char *)(((struct sockaddr_in *)dst)->sin_zero), + sizeof(((struct sockaddr_in *)dst)->sin_zero)); + + bcopy((char *)src, (char *)(km + 1), src->sa_len); + bcopy((char *)dst, (char *)((int)(km + 1) + ROUNDUP(src->sa_len)), + dst->sa_len); + DPRINTF(IDL_FINISHED,("key_acquire/created: 3\n")); + created++; + } + if (key_sendup(p->socket, km)) + success++; + } + + if (km) + KFREE(km); + + /* + * Update the acquirelist + */ + if (success) { + if (!ap) { + DPRINTF(IDL_EVENT,("Adding new entry in acquirelist\n")); + KMALLOC(ap, struct key_acquirelist *, sizeof(struct key_acquirelist)); + if (ap == 0) + return(success ? 0 : -1); + bzero((char *)ap, sizeof(struct key_acquirelist)); + bcopy((char *)dst, (char *)ap->target, dst->sa_len); + ap->type = etype; + ap->next = key_acquirelist->next; + key_acquirelist->next = ap; + } + DPRINTF(IDL_GROSS_EVENT,("Updating acquire counter, expiration time\n")); + ap->count++; + ap->expiretime = TIME_SECONDS + maxacquiretime; + } + DPRINTF(IDL_EVENT,("key_acquire: done! success=%d\n",success)); + return(success ? 0 : -1); +} + +/*---------------------------------------------------------------------- + * key_alloc(): + * Allocate a security association to a socket. A socket requesting + * unique keying (per-socket keying) is assigned a security assocation + * exclusively for its use. Sockets not requiring unique keying are + * assigned the first security association which may or may not be + * used by another socket. + ----------------------------------------------------------------------*/ +static int +key_alloc(u_int type, SOCKADDR *src, SOCKADDR *dst, struct socket *socket, + u_int unique_key, struct key_tblnode **keynodep) +{ + struct key_tblnode *keynode; + char buf[MAXHASHKEYLEN]; + struct key_allocnode *np, *prevnp; + struct key_so2spinode *newnp; + int len; + int indx; + + DPRINTF(IDL_FINISHED,("Entering key_alloc w/type=%u!\n",type)); + if (!(src && dst)) { + DPRINTF(IDL_ERROR,("key_alloc: received null src or dst!\n")); + return(-1); + } + + /* + * Search key allocation table + */ + bzero((char *)&buf, sizeof(buf)); + len = key_createkey((char *)&buf, type, src, dst, 0, 1); + indx = key_gethashval((char *)&buf, len, KEYALLOCTBLSIZE); + +#define np_type np->keynode->secassoc->type +#define np_state np->keynode->secassoc->state +#define np_src np->keynode->secassoc->src +#define np_dst np->keynode->secassoc->dst + + prevnp = &keyalloctbl[indx]; + for (np = keyalloctbl[indx].next; np; np = np->next) { + if ((type == np_type) && addrpart_equal(src, np_src) && + addrpart_equal(dst, np_dst) && + !(np_state & (K_LARVAL | K_DEAD | K_UNIQUE))) { + if (!(unique_key)) + break; + if (!(np_state & K_USED)) + break; + } + prevnp = np; + } + + if (np) { + struct socketlist *newsp; + CRITICAL_DCL + + CRITICAL_START; + + DPRINTF(IDL_EVENT,("key_alloc: found node to allocate\n")); + keynode = np->keynode; + + KMALLOC(newnp, struct key_so2spinode *, sizeof(struct key_so2spinode)); + if (newnp == 0) { + DPRINTF(IDL_ERROR,("key_alloc: Can't alloc mem for so2spi node!\n")); + CRITICAL_END; + return(ENOBUFS); + } + KMALLOC(newsp, struct socketlist *, sizeof(struct socketlist)); + if (newsp == 0) { + DPRINTF(IDL_ERROR,("key_alloc: Can't alloc mem for socketlist!\n")); + if (newnp) + KFREE(newnp); + CRITICAL_END; + return(ENOBUFS); + } + + /* + * Add a hash entry into the so2spi table to + * map socket to allocated secassoc. + */ + DPRINTF(IDL_FINISHED,("key_alloc: adding entry to so2spi table...")); + newnp->keynode = keynode; + newnp->socket = socket; + newnp->next = so2spitbl[((u_int32_t)socket) % SO2SPITBLSIZE].next; + so2spitbl[((u_int32_t)socket) % SO2SPITBLSIZE].next = newnp; + DPRINTF(IDL_FINISHED,("done\n")); + + if (unique_key) { + /* + * Need to remove the allocation entry + * since the secassoc is now unique, + * can't be allocated to any other socket + */ + DPRINTF(IDL_EVENT,("key_alloc: making keynode unique...")); + keynode->secassoc->state |= K_UNIQUE; + prevnp->next = np->next; + KFREE(np); + DPRINTF(IDL_EVENT,("done\n")); + } + keynode->secassoc->state |= K_USED; + keynode->secassoc->state |= K_OUTBOUND; + keynode->alloc_count++; + + /* + * Add socket to list of socket using secassoc. + */ + DPRINTF(IDL_FINISHED,("key_alloc: adding so to solist...")); + newsp->socket = socket; + newsp->next = keynode->solist->next; + keynode->solist->next = newsp; + DPRINTF(IDL_FINISHED,("done\n")); + *keynodep = keynode; + CRITICAL_END; + return(0); + } + *keynodep = NULL; + return(0); +} + + +/*---------------------------------------------------------------------- + * key_free(): + * Decrement the refcount for a key table entry. If the entry is + * marked dead,, the refcount is zero, we go ahead, delete it. + ----------------------------------------------------------------------*/ +void +key_free(struct key_tblnode *keynode) +{ + DPRINTF(IDL_GROSS_EVENT,("Entering key_free w/keynode=0x%x\n", + (unsigned int)keynode)); + if (!keynode) { + DPRINTF(IDL_ERROR,("Warning: key_free got null pointer\n")); + return; + } + (keynode->ref_count)--; + if (keynode->ref_count < 0) { + DPRINTF(IDL_ERROR,("Warning: key_free decremented refcount to %d\n",keynode->ref_count)); + } + if ((keynode->secassoc->state & K_DEAD) && (keynode->ref_count <= 0)) { + DPRINTF(IDL_GROSS_EVENT,("key_free: calling key_delete\n")); + key_delete(keynode->secassoc); + } +} + +/*---------------------------------------------------------------------- + * getassocbyspi(): + * Get a security association for a given type, src, dst,, spi. + * + * Returns: 0 if sucessfull + * -1 if error/not found + * + * Caller must convert spi to host order. Function assumes spi is + * in host order! + ----------------------------------------------------------------------*/ +int +getassocbyspi(u_int type, SOCKADDR *src, SOCKADDR *dst, u_int32_t spi, + struct key_tblnode **keyentry) +{ + char buf[MAXHASHKEYLEN]; + int len, indx; + struct key_tblnode *keynode, *prevkeynode = 0; + + DPRINTF(IDL_FINISHED,("Entering getassocbyspi w/type=%u spi=%u\n",type,spi)); + + *keyentry = NULL; + bzero(&buf, sizeof(buf)); + len = key_createkey((char *)&buf, type, src, dst, spi, 0); + indx = key_gethashval((char *)&buf, len, KEYTBLSIZE); + DPRINTF(IDL_FINISHED,("getassocbyspi: indx=%d\n",indx)); + DDO(IDL_FINISHED,dump_sockaddr(src);dump_sockaddr(dst)); + keynode = key_search(type, src, dst, spi, indx, &prevkeynode); + DPRINTF(IDL_FINISHED,("getassocbyspi: keysearch ret=0x%x\n", + (unsigned int)keynode)); + if (keynode && !(keynode->secassoc->state & (K_DEAD | K_LARVAL))) { + DPRINTF(IDL_GROSS_EVENT,("getassocbyspi: found secassoc!\n")); + (keynode->ref_count)++; + keynode->secassoc->state |= K_USED; + *keyentry = keynode; + } else { + DPRINTF(IDL_EVENT,("getassocbyspi: secassoc not found!\n")); + return (-1); + } + return(0); +} + + +/*---------------------------------------------------------------------- + * getassocbysocket(): + * Get a security association for a given type, src, dst,, socket. + * If not found, try to allocate one. + * Returns: 0 if successfull + * -1 if error condition/secassoc not found (*keyentry = NULL) + * 1 if secassoc temporarily unavailable (*keynetry = NULL) + * (e.g., key mgnt. daemon(s) called) + ----------------------------------------------------------------------*/ +int +getassocbysocket(u_int type, SOCKADDR *src, SOCKADDR *dst, + struct socket *socket, u_int unique_key, + struct key_tblnode **keyentry) +{ + struct key_tblnode *keynode = 0; + struct key_so2spinode *np; + u_int realtype; + + DPRINTF(IDL_FINISHED,("Entering getassocbysocket w/type=%u so=0x%x\n", + type,(unsigned int)socket)); + + /* + * We treat esp-transport mode, esp-tunnel mode + * as a single type in the keytable. This has a side + * effect that socket using both esp-transport, + * esp-tunnel will use the same security association + * for both modes. Is this a problem? + */ + realtype = type; + if ((np = key_sosearch(type, src, dst, socket))) { + if (np->keynode && np->keynode->secassoc && + !(np->keynode->secassoc->state & (K_DEAD | K_LARVAL))) { + DPRINTF(IDL_FINISHED,("getassocbysocket: found secassoc!\n")); + (np->keynode->ref_count)++; + *keyentry = np->keynode; + return(0); + } + } + + /* + * No secassoc has been allocated to socket, + * so allocate one, if available + */ + DPRINTF(IDL_GROSS_EVENT,("getassocbyso: can't find it, trying to allocate!\n")); + if (key_alloc(realtype, src, dst, socket, unique_key, &keynode) == 0) { + if (keynode) { + DPRINTF(IDL_GROSS_EVENT,("getassocbyso: key_alloc found secassoc!\n")); + keynode->ref_count++; + *keyentry = keynode; + return(0); + } else { + /* + * Kick key mgnt. daemon(s) + * (this should be done in ipsec_output_policy() instead or + * selectively called based on a flag value) + */ + DPRINTF(IDL_FINISHED,("getassocbyso: calling key mgnt daemons!\n")); + *keyentry = NULL; + if (key_acquire(realtype, src, dst) == 0) + return (1); + else + return(-1); + } + } + *keyentry = NULL; + return(-1); +} + +/*---------------------------------------------------------------------- + * key_xdata(): + * Parse message buffer for src/dst/from/iv/key if parseflag = 0 + * else parse for src/dst only. + ----------------------------------------------------------------------*/ +static int +key_xdata(struct key_msghdr *km, struct key_msgdata *kip, int parseflag) +{ + char *cp, *cpmax; + + if (!km || (km->key_msglen <= 0)) + return (-1); + + cp = (caddr_t)(km + 1); + cpmax = (caddr_t)km + km->key_msglen; + + /* + * Assumes user process passes message with + * correct word alignment. + */ + + /* + * Need to clean up this code later. + */ + + /* Grab src addr */ + kip->src = (SOCKADDR *)cp; + if (!kip->src->sa_len) { + DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse src addr\n")); + return(-1); + } + + ADVANCE(cp, kip->src->sa_len); + + /* Grab dest addr */ + kip->dst = (SOCKADDR *)cp; + if (!kip->dst->sa_len) { + DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse dest addr\n")); + return(-1); + } + + ADVANCE(cp, kip->dst->sa_len); + if (parseflag == 1) { + kip->from = 0; + kip->key = kip->iv = 0; + kip->keylen = kip->ivlen = 0; + return(0); + } + + /* Grab from addr */ + kip->from = (SOCKADDR *)cp; + if (!kip->from->sa_len) { + DPRINTF(IDL_MAJOR_EVENT,("key_xdata couldn't parse from addr\n")); + return(-1); + } + + ADVANCE(cp, kip->from->sa_len); + + /* Grab key */ + if ((kip->keylen = km->keylen)) { + kip->key = cp; + ADVANCE(cp, km->keylen); + } else + kip->key = 0; + + /* Grab iv */ + if ((kip->ivlen = km->ivlen)) + kip->iv = cp; + else + kip->iv = 0; + + return (0); +} + + +int +key_parse(struct key_msghdr **kmp, struct socket *so, int *dstfamily) +{ + int error = 0, keyerror = 0; + struct key_msgdata keyinfo; + struct key_secassoc *secassoc = NULL; + struct key_msghdr *km = *kmp; + + DPRINTF(IDL_MAJOR_EVENT, ("Entering key_parse\n")); + +#define senderr(e) \ + { error = (e); goto flush; } + + if (km->key_msgvers != KEY_VERSION) { + DPRINTF(IDL_CRITICAL,("keyoutput: Unsupported key message version!\n")); + senderr(EPROTONOSUPPORT); + } + + km->key_pid = CURRENT_PID; + + DDO(IDL_MAJOR_EVENT, printf("keymsghdr:\n"); dump_keymsghdr(km)); + + /* + * Parse buffer for src addr, dest addr, from addr, key, iv + */ + bzero((char *)&keyinfo, sizeof(keyinfo)); + + switch (km->key_msgtype) { + case KEY_ADD: + DPRINTF(IDL_MAJOR_EVENT,("key_output got KEY_ADD msg\n")); + + if (key_xdata(km, &keyinfo, 0) < 0) + goto parsefail; + + /* + * Allocate the secassoc structure to insert + * into key table here. + */ + KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); + if (secassoc == 0) { + DPRINTF(IDL_CRITICAL,("keyoutput: No more memory!\n")); + senderr(ENOBUFS); + } + + if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) { + DPRINTF(IDL_CRITICAL,("keyoutput: key_msghdr2secassoc failed!\n")); + KFREE(secassoc); + senderr(EINVAL); + } + DPRINTF(IDL_MAJOR_EVENT,("secassoc to add:\n")); + DDO(IDL_MAJOR_EVENT,dump_secassoc(secassoc)); + + if ((keyerror = key_add(secassoc)) != 0) { + DPRINTF(IDL_CRITICAL,("keyoutput: key_add failed\n")); + if (secassoc->key) + KFREE(secassoc->key); + if (secassoc->iv) + KFREE(secassoc->iv); + KFREE(secassoc); + if (keyerror == -2) { + senderr(EEXIST); + } else { + senderr(ENOBUFS); + } + } + break; + case KEY_DELETE: + DPRINTF(IDL_MAJOR_EVENT,("key_output got KEY_DELETE msg\n")); + + if (key_xdata(km, &keyinfo, 1) < 0) + goto parsefail; + + KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); + if (secassoc == 0) { + senderr(ENOBUFS); + } + if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) { + KFREE(secassoc); + senderr(EINVAL); + } + if (key_delete(secassoc) != 0) { + if (secassoc->iv) + KFREE(secassoc->iv); + if (secassoc->key) + KFREE(secassoc->key); + KFREE(secassoc); + senderr(ESRCH); + } + if (secassoc->iv) + KFREE(secassoc->iv); + if (secassoc->key) + KFREE(secassoc->key); + KFREE(secassoc); + break; + case KEY_UPDATE: + DPRINTF(IDL_EVENT,("key_output got KEY_UPDATE msg\n")); + + if (key_xdata(km, &keyinfo, 0) < 0) + goto parsefail; + + KMALLOC(secassoc, struct key_secassoc *, sizeof(struct key_secassoc)); + if (secassoc == 0) { + senderr(ENOBUFS); + } + if (key_msghdr2secassoc(secassoc, km, &keyinfo) < 0) { + KFREE(secassoc); + senderr(EINVAL); + } + if ((keyerror = key_update(secassoc)) != 0) { + DPRINTF(IDL_CRITICAL,("Error updating key entry\n")); + if (secassoc->iv) + KFREE(secassoc->iv); + if (secassoc->key) + KFREE(secassoc->key); + KFREE(secassoc); + senderr(keyerror); + } + KFREE(secassoc); + break; + case KEY_GET: + DPRINTF(IDL_EVENT,("key_output got KEY_GET msg\n")); + + if (key_xdata(km, &keyinfo, 1) < 0) + goto parsefail; + + if (key_get(km->type, (SOCKADDR *)keyinfo.src, + (SOCKADDR *)keyinfo.dst, + km->spi, &secassoc) != 0) { + DPRINTF(IDL_EVENT,("keyoutput: can't get key\n")); + senderr(ESRCH); + } + + if (secassoc) { + int newlen; + + DPRINTF(IDL_EVENT,("keyoutput: Found secassoc!\n")); + newlen = sizeof(struct key_msghdr) + ROUNDUP(secassoc->src->sa_len) + + ROUNDUP(secassoc->dst->sa_len) + ROUNDUP(secassoc->from->sa_len) + + ROUNDUP(secassoc->keylen) + ROUNDUP(secassoc->ivlen); + DPRINTF(IDL_EVENT,("keyoutput: newlen=%d\n", newlen)); + if (newlen > km->key_msglen) { + struct key_msghdr *newkm; + + DPRINTF(IDL_EVENT,("keyoutput: Allocating new buffer!\n")); + KMALLOC(newkm, struct key_msghdr *, newlen); + if (newkm == 0) { + senderr(ENOBUFS); + } + bcopy((char *)km, (char *)newkm, km->key_msglen); + DPRINTF(IDL_FINISHED,("keyoutput: 1\n")); + KFREE(km); + *kmp = km = newkm; + DPRINTF(IDL_CRITICAL, ("km->key_msglen = %d, newlen = %d\n", + km->key_msglen, newlen)); + km->key_msglen = newlen; + } + DPRINTF(IDL_FINISHED,("keyoutput: 2\n")); + if (key_secassoc2msghdr(secassoc, km, &keyinfo)) { + DPRINTF(IDL_CRITICAL,("keyoutput: Can't create msghdr!\n")); + senderr(EINVAL); + } + DPRINTF(IDL_FINISHED,("keyoutput: 3\n")); + } + break; + case KEY_GETSPI: + DPRINTF(IDL_EVENT,("key_output got KEY_GETSPI msg\n")); + + if (key_xdata(km, &keyinfo, 1) < 0) + goto parsefail; + + if ((keyerror = key_getspi(km->type, keyinfo.src, keyinfo.dst, + km->lifetime1, km->lifetime2, + &(km->spi))) != 0) { + DPRINTF(IDL_CRITICAL,("keyoutput: getspi failed error=%d\n", keyerror)); + senderr(keyerror); + } + break; + case KEY_REGISTER: + DPRINTF(IDL_EVENT,("key_output got KEY_REGISTER msg\n")); + key_register(so, km->type); + break; + case KEY_DUMP: + DPRINTF(IDL_EVENT,("key_output got KEY_DUMP msg\n")); + error = key_dump(so); + return(error); + break; + case KEY_FLUSH: + DPRINTF(IDL_EVENT,("key_output got KEY_FLUSH msg\n")); + key_flush(); + break; + default: + DPRINTF(IDL_CRITICAL,("key_output got unsupported msg type=%d\n", + km->key_msgtype)); + senderr(EOPNOTSUPP); + } + + goto flush; + +parsefail: + keyinfo.dst = NULL; + error = EINVAL; + +flush: + if (km) + km->key_errno = error; + + if (dstfamily) + *dstfamily = keyinfo.dst ? keyinfo.dst->sa_family : 0; + + DPRINTF(IDL_MAJOR_EVENT, ("key_parse exiting with error=%d\n", error)); + return error; +} + +/* + * Definitions of protocols supported in the KEY domain. + */ + +struct sockaddr key_addr = { 2, PF_KEY, }; +struct sockproto key_proto = { PF_KEY, }; + +#define KEYREAPERINT 120 + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +static int +key_sendup(s, km) + struct socket *s; + struct key_msghdr *km; +{ + struct mbuf *m; + MGETHDR(m, M_WAIT, MT_DATA); + m->m_len = m->m_pkthdr.len = 0; + m->m_next = 0; + m->m_nextpkt = 0; + m->m_pkthdr.rcvif = 0; + m_copyback(m, 0, km->key_msglen, (caddr_t)km); + + if (sbappendaddr(&(s->so_rcv), &key_addr, m, NULL)) { + sorwakeup(s); + return 1; + } else + m_freem(m); + + return(0); +} + +#ifdef notyet +/*---------------------------------------------------------------------- + * key_reaper(): + * Scan key table, nuke unwanted entries + ----------------------------------------------------------------------*/ +static void +key_reaper(whocares) + void *whocares; +{ + DPRINTF(IDL_GROSS_EVENT,("Entering key_reaper()\n")); + + timeout(key_reaper, NULL, KEYREAPERINT * HZ); +} +#endif /* notyet */ + +/*---------------------------------------------------------------------- + * key_init(): + * Init routine for key socket, key engine + ----------------------------------------------------------------------*/ +static void +key_init(void) +{ + DPRINTF(IDL_EVENT,("Called key_init().\n")); + if (key_inittables()) + panic("key_inittables failed!\n"); +#ifdef notyet + timeout(key_reaper, NULL, HZ); +#endif /* notyet */ + bzero((char *)&keyso_cb, sizeof(keyso_cb)); +} + +/*---------------------------------------------------------------------- + * my_addr(): + * Determine if an address belongs to one of my configured interfaces. + * Currently handles only AF_INET, AF_INET6 addresses. + ----------------------------------------------------------------------*/ +static int +my_addr(sa) + SOCKADDR *sa; +{ + struct in6_ifaddr *i6a = 0; + struct in_ifaddr *ia = 0; + + switch(sa->sa_family) { +#ifdef INET6 + case AF_INET6: + for (i6a = in6_ifaddr; i6a; i6a = i6a->i6a_next) { + if (IN6_ADDR_EQUAL(((struct sockaddr_in6 *)sa)->sin6_addr, + i6a->i6a_addr.sin6_addr)) + return(1); + } + break; +#endif /* INET6 */ + case AF_INET: + for (ia = in_ifaddr; ia; ia = ia->ia_next) { + if (((struct sockaddr_in *)sa)->sin_addr.s_addr == + ia->ia_addr.sin_addr.s_addr) + return(1); + } + break; + } + return(0); +} + +/*---------------------------------------------------------------------- + * key_output(): + * Process outbound pf_key message. + ----------------------------------------------------------------------*/ +static int +key_output(struct mbuf *m, struct socket *so) +{ + struct key_msghdr *km = 0; + caddr_t cp, cplimit; + int len; + int error = 0; + int dstfamily = 0; + + DPRINTF(IDL_EVENT,("key_output() got a message len=%d.\n", m->m_pkthdr.len)); + +#undef senderr +#define senderr(e) \ + { error = (e); if (km) km->key_errno = error; goto flush; } + + if (m == 0 || ((m->m_len < sizeof(long)) && + (m = m_pullup(m, sizeof(long))) == 0)) { + DPRINTF(IDL_CRITICAL,("key_output can't pullup mbuf\n")); + return (ENOBUFS); + } + if ((m->m_flags & M_PKTHDR) == 0) + panic("key_output"); + + DDO(IDL_FINISHED,dump_mbuf(m)); + + len = m->m_pkthdr.len; + if (len < sizeof(*km) || len != mtod(m, struct key_msghdr *)->key_msglen) { + DPRINTF(IDL_CRITICAL,("keyout: Invalid length field/length mismatch!\n")); + senderr(EINVAL); + } + KMALLOC(km, struct key_msghdr *, len); + if (km == 0) { + DPRINTF(IDL_CRITICAL,("keyoutput: Can't malloc memory!\n")); + senderr(ENOBUFS); + } + + m_copydata(m, 0, len, (caddr_t)km); + + km->key_errno = error = key_parse(&km, so, &dstfamily); + DPRINTF(IDL_MAJOR_EVENT, ("Back from key_parse\n")); +flush: + key_sendup(so, km); +#if 0 + { + struct rawcb *rp = 0; + struct mbuf *m; + + if ((so->so_options & SO_USELOOPBACK) == 0) { + if (keyso_cb.any_count <= 1) { + if (km) + KFREE(km); + return (error); + } + rp = sotorawcb(so); + } + + DPRINTF(IDL_MAJOR_EVENT, ("key_output: foo\n")); + key_proto.sp_protocol = dstfamily; + + if (km) { + m = m_devget(km, len, 0, NULL, NULL); + KFREE(km); + } + + DPRINTF(IDL_MAJOR_EVENT, ("key_output: bar\n")); + if (rp) + rp->rcb_proto.sp_family = 0; /* Prevent us from receiving message */ + + raw_input(m, &key_proto, &key_addr, &key_addr); + + if (rp) + rp->rcb_proto.sp_family = PF_KEY; + } + DPRINTF(IDL_MAJOR_EVENT, ("key_output: baz\n")); +#endif /* 0 */ + return (error); +} + + +/*---------------------------------------------------------------------- + * key_usrreq(): + * Handles PRU_* for pf_key sockets. + ----------------------------------------------------------------------*/ +static int +key_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, + struct mbuf *control) +{ + register int error = 0; + register struct rawcb *rp = sotorawcb(so); + int s; + + DPRINTF(IDL_EVENT,("Entering key_usrreq, req = %d.\n",req)); + + if (req == PRU_ATTACH) { + MALLOC(rp, struct rawcb *, sizeof(*rp), M_PCB, M_WAITOK); + if (so->so_pcb = (caddr_t)rp) + bzero(so->so_pcb, sizeof(*rp)); + } + + if (req == PRU_DETACH && rp) { + int af = rp->rcb_proto.sp_protocol; + if (af == AF_INET) + keyso_cb.ip4_count--; +#ifdef INET6 + else if (af == AF_INET6) + keyso_cb.ip6_count--; +#endif /* INET6 */ + keyso_cb.any_count--; + } + s = splnet(); + error = raw_usrreq(so, req, m, nam, control); + rp = sotorawcb(so); + + if (req == PRU_ATTACH && rp) { + int af = rp->rcb_proto.sp_protocol; + if (error) { + free((caddr_t)rp, M_PCB); + splx(s); + return error; + } + if (af == AF_INET) + keyso_cb.ip4_count++; +#ifdef INET6 + else if (af == AF_INET6) + keyso_cb.ip6_count++; +#endif /* INET6 */ + keyso_cb.any_count++; + rp->rcb_faddr = &key_addr; + soisconnected(so); /* Key socket, like routing socket, must be + connected. */ + + /* Possibly set other needed flags/options at creation time in here. */ + so->so_options |= SO_USELOOPBACK; /* Like routing socket, we turn this */ + /* on by default */ + } + splx(s); + return error; +} + +/*---------------------------------------------------------------------- + * key_cbinit(): + * Control block init routine for key socket + ----------------------------------------------------------------------*/ +static void +key_cbinit(void) +{ + /* + * This is equivalent to raw_init for the routing socket. + * The key socket uses the same control block as the routing + * socket. + */ + DPRINTF(IDL_EVENT,("Called key_cbinit().\n")); +} + +/* + * Protoswitch entry for pf_key + */ + +extern struct domain keydomain; /* or at least forward */ + +struct protosw keysw[] = { +{ SOCK_RAW, &keydomain, 0, PR_ATOMIC|PR_ADDR, + raw_input, key_output, raw_ctlinput, 0, + key_usrreq, + key_cbinit +} +}; + +struct domain keydomain = + { PF_KEY, "key", key_init, 0, 0, + keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] }; + +DOMAIN_SET(key) |