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 | |
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')
-rw-r--r-- | sys/netkey/key.c | 2270 | ||||
-rw-r--r-- | sys/netkey/key.h | 284 | ||||
-rw-r--r-- | sys/netkey/key_debug.c | 730 | ||||
-rw-r--r-- | sys/netkey/key_debug.h | 151 |
4 files changed, 3435 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) diff --git a/sys/netkey/key.h b/sys/netkey/key.h new file mode 100644 index 0000000..920f9c4 --- /dev/null +++ b/sys/netkey/key.h @@ -0,0 +1,284 @@ +/*---------------------------------------------------------------------- + * key.h : Declarations and Definitions for Key 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 Agreement 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). + +----------------------------------------------------------------------*/ + +#ifndef _netkey_key_h +#define _netkey_key_h 1 + +/* + * PF_KEY messages + */ + +#define KEY_ADD 1 +#define KEY_DELETE 2 +#define KEY_UPDATE 3 +#define KEY_GET 4 +#define KEY_ACQUIRE 5 +#define KEY_GETSPI 6 +#define KEY_REGISTER 7 +#define KEY_EXPIRE 8 +#define KEY_DUMP 9 +#define KEY_FLUSH 10 + +#define KEY_VERSION 1 +#define POLICY_VERSION 1 + +#define SECURITY_TYPE_NONE 0 + +#define KEY_TYPE_AH 1 +#define KEY_TYPE_ESP 2 +#define KEY_TYPE_RSVP 3 +#define KEY_TYPE_OSPF 4 +#define KEY_TYPE_RIPV2 5 +#define KEY_TYPE_MIPV4 6 +#define KEY_TYPE_MIPV6 7 +#define KEY_TYPE_MAX 7 + +/* + * Security association state + */ + +#define K_USED 0x1 /* Key used/not used */ +#define K_UNIQUE 0x2 /* Key unique/reusable */ +#define K_LARVAL 0x4 /* SPI assigned, but sa incomplete */ +#define K_ZOMBIE 0x8 /* sa expired but still useable */ +#define K_DEAD 0x10 /* sa marked for deletion, ready for reaping */ +#define K_INBOUND 0x20 /* sa for inbound packets, ie. dst=myhost */ +#define K_OUTBOUND 0x40 /* sa for outbound packets, ie. src=myhost */ + + +#ifndef MAX_SOCKADDR_SZ +#ifdef INET6 +#define MAX_SOCKADDR_SZ (sizeof(struct sockaddr_in6)) +#else /* INET6 */ +#define MAX_SOCKADDR_SZ (sizeof(struct sockaddr_in)) +#endif /* INET6 */ +#endif /* MAX_SOCKADDR_SZ */ + +#ifndef MAX_KEY_SZ +#define MAX_KEY_SZ 16 +#endif /* MAX_KEY_SZ */ + +#ifndef MAX_IV_SZ +#define MAX_IV_SZ 16 +#endif /* MAX_IV_SZ */ + +/* Security association data for IP Security */ +struct key_secassoc { + u_int8_t len; /* Length of the data (for radix) */ + u_int8_t type; /* Type of association */ + u_int8_t state; /* State of the association */ + u_int8_t label; /* Sensitivity label (unused) */ + u_int32_t spi; /* SPI */ + u_int8_t keylen; /* Key length */ + u_int8_t ivlen; /* Initialization vector length */ + u_int8_t algorithm; /* Algorithm switch index */ + u_int8_t lifetype; /* Type of lifetime */ + caddr_t iv; /* Initialization vector */ + caddr_t key; /* Key */ + u_int32_t lifetime1; /* Lifetime value 1 */ + u_int32_t lifetime2; /* Lifetime value 2 */ + struct sockaddr *src; /* Source host address */ + struct sockaddr *dst; /* Destination host address */ + struct sockaddr *from; /* Originator of association */ +}; + +/* + * Structure for key message header. PF_KEY message consists of key_msghdr + * followed by src struct sockaddr, dest struct sockaddr, from struct + * sockaddr, key, and iv. Assumes size of key message header less than MHLEN. + */ + +struct key_msghdr { + u_short key_msglen; /* length of message including + * src/dst/from/key/iv */ + u_char key_msgvers; /* key version number */ + u_char key_msgtype; /* key message type, eg. KEY_ADD */ + pid_t key_pid;/* process id of message sender */ + int key_seq;/* message sequence number */ + int key_errno; /* error code */ + u_int8_t type; /* type of security association */ + u_int8_t state; /* state of security association */ + u_int8_t label; /* sensitivity level */ + u_int8_t pad; /* padding for allignment */ + u_int32_t spi; /* spi value */ + u_int8_t keylen; /* key length */ + u_int8_t ivlen; /* iv length */ + u_int8_t algorithm; /* algorithm identifier */ + u_int8_t lifetype; /* type of lifetime */ + u_int32_t lifetime1; /* lifetime value 1 */ + u_int32_t lifetime2; /* lifetime value 2 */ +}; + +struct key_msgdata { + struct sockaddr *src; /* source host address */ + struct sockaddr *dst; /* destination host address */ + struct sockaddr *from; /* originator of security association */ + caddr_t iv; /* initialization vector */ + caddr_t key; /* key */ + int ivlen; /* key length */ + int keylen; /* iv length */ +}; + +struct policy_msghdr { + u_short policy_msglen; /* message length */ + u_char policy_msgvers; /* message version */ + u_char policy_msgtype; /* message type */ + int policy_seq; /* message sequence number */ + int policy_errno; /* error code */ +}; + +/* + * Key engine table structures + */ + +struct socketlist { + struct socket *socket; /* pointer to socket */ + struct socketlist *next;/* next */ +}; + +struct key_tblnode { + int alloc_count; /* number of sockets allocated to + * secassoc */ + int ref_count; /* number of sockets referencing + * secassoc */ + struct socketlist *solist; /* list of sockets allocated to + * secassoc */ + struct key_secassoc *secassoc; /* security association */ + struct key_tblnode *next; /* next node */ +}; + +struct key_allocnode { + struct key_tblnode *keynode; + struct key_allocnode *next; +}; + +struct key_so2spinode { + struct socket *socket; /* socket pointer */ + struct key_tblnode *keynode; /* pointer to tblnode containing + * secassoc */ + /* info for socket */ + struct key_so2spinode *next; +}; + +struct key_registry { + u_int8_t type; /* secassoc type that key mgnt. daemon can + * acquire */ + struct socket *socket; /* key management daemon socket pointer */ + struct key_registry *next; +}; + +struct key_acquirelist { + u_int8_t type; /* secassoc type to acquire */ + struct sockaddr *target;/* destination address of secassoc */ + u_int32_t count; /* number of acquire messages sent */ + u_long expiretime; /* expiration time for acquire + * message */ + struct key_acquirelist *next; +}; + +struct keyso_cb { + int ip4_count; /* IPv4 */ +#ifdef INET6 + int ip6_count; /* IPv6 */ +#endif /* INET6 */ + int any_count; /* Sum of above counters */ +}; + +#ifdef KERNEL +int key_inittables __P((void)); +int key_secassoc2msghdr __P((struct key_secassoc *, struct key_msghdr *, + struct key_msgdata *)); +int key_msghdr2secassoc __P((struct key_secassoc *, struct key_msghdr *, + struct key_msgdata *)); +int key_add __P((struct key_secassoc *)); +int key_delete __P((struct key_secassoc *)); +int key_get __P((u_int, struct sockaddr *, struct sockaddr *, u_int32_t, + struct key_secassoc **)); +void key_flush __P((void)); +int key_dump __P((struct socket *)); +int key_getspi __P((u_int, struct sockaddr *, struct sockaddr *, u_int32_t, + u_int32_t, u_int32_t *)); +int key_update __P((struct key_secassoc *)); +int key_register __P((struct socket *, u_int)); +void key_unregister __P((struct socket *, u_int, int)); +int key_acquire __P((u_int, struct sockaddr *, struct sockaddr *)); +int getassocbyspi __P((u_int, struct sockaddr *, struct sockaddr *, u_int32_t, + struct key_tblnode **)); +int getassocbysocket __P((u_int, struct sockaddr *, struct sockaddr *, + struct socket *, u_int, struct key_tblnode **)); +void key_free __P((struct key_tblnode *)); +int key_parse __P((struct key_msghdr ** km, struct socket * so, int *)); +#endif /* KERNEL */ + +#endif /* _netkey_key_h */ diff --git a/sys/netkey/key_debug.c b/sys/netkey/key_debug.c new file mode 100644 index 0000000..c928c84 --- /dev/null +++ b/sys/netkey/key_debug.c @@ -0,0 +1,730 @@ +/* + * in6_debug.c -- Insipired by Craig Metz's Net/2 in6_debug.c, but + * not quite as heavyweight (initially, anyway). + * + * The idea is to have globals here, and dump netinet6/ data structures. + * + * Copyright 1995 by Dan McDonald, Bao Phan, and Randall Atkinson, + * All Rights Reserved. + * All Rights under this copyright have been assigned to NRL. + */ + +/*---------------------------------------------------------------------- +# @(#)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). + +----------------------------------------------------------------------*/ + + +#define INET6_DEBUG_C + +#include <netkey/osdep_44bsd.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/mbuf.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/route.h> + +#include <netinet/in_systm.h> +#include <netinet/ip.h> +#include <netinet/in_pcb.h> + +#ifdef INET6 +#include <netinet6/in6.h> +#include <netinet6/in6_var.h> +#include <netinet6/ipv6.h> +#include <netinet6/ipv6_var.h> +#include <netinet6/ipv6_icmp.h> +#else /* INET6 */ +#if 0 +#include "in6_types.h" +#endif +#endif /* INET6 */ + +#define SA_LEN 1 +#define SIN_LEN 1 + +#ifdef KEY_DEBUG +#include <netkey/key.h> +#endif /* KEY_DEBUG */ +#ifdef IPSEC_DEBUG +#include <netsec/ipsec.h> +#endif /* IPSEC_DEBUG */ + +#if 0 +#include <netinet6/in6_debug.h> +#endif + +#ifndef DEFARGS +#define DEFARGS(arglist, args) arglist args; +#define AND ; +#endif /* DEFARGS */ + +/* + * Globals + */ + +/* The following should be sysctl-tweakable. */ + +unsigned int in6_debug_level = IDL_FINISHED + 1; /* 0 is no debugging */ + +/* + * Functions and macros. + */ + +void in6_debug_init DEFARGS((), void) +{ + /* For now, nothing. */ +} + +/*---------------------------------------------------------------------- + * dump_* dumps various data structures. These should be called within + * the context of a DDO() macro. They assume address and port fields + * are in network order. + ----------------------------------------------------------------------*/ + +#ifdef INET6 +/*---------------------------------------------------------------------- + * Dump an IPv6 address. Don't compress 0's out because of debugging. + ----------------------------------------------------------------------*/ +void dump_in_addr6 DEFARGS((in_addr6), + struct in_addr6 *in_addr6) +{ + u_short *shorts = (u_short *)in_addr6; + int i = 0; + + if (!in_addr6) { + printf("Dereference a NULL in_addr6? I don't think so.\n"); + return; + } + + printf("(conv. for printing) "); + while (i < 7) + printf("%4x:",htons(shorts[i++])); + printf("%4x\n",htons(shorts[7])); +} +#endif /* INET6 */ + +/*---------------------------------------------------------------------- + * Dump and IPv4 address in x.x.x.x form. + ----------------------------------------------------------------------*/ +void dump_in_addr DEFARGS((in_addr), + struct in_addr *in_addr) +{ + u_char *chars = (u_char *)in_addr; + int i = 0; + + if (!in_addr) { + printf("Dereference a NULL in_addr? I don't think so.\n"); + return; + } + + while (i < 3) + printf("%d.",chars[i++]); + printf("%d\n",chars[3]); +} + +#ifdef INET6 +/*---------------------------------------------------------------------- + * Dump an IPv6 socket address. + ----------------------------------------------------------------------*/ +void dump_sockaddr_in6 DEFARGS((sin6), + struct sockaddr_in6 *sin6) +{ + if (!sin6) { + printf("Dereference a NULL sockaddr_in6? I don't think so.\n"); + return; + } + + printf("sin6_len = %d, sin6_family = %d, sin6_port = %d (0x%x)\n", + sin6->sin6_len,sin6->sin6_family, htons(sin6->sin6_port), + htons(sin6->sin6_port)); + printf("sin6_flowinfo = 0x%x\n",sin6->sin6_flowinfo); + printf("sin6_addr = "); + dump_in_addr6(&sin6->sin6_addr); +} +#endif /* INET6 */ + +/*---------------------------------------------------------------------- + * Dump an IPv4 socket address. + ----------------------------------------------------------------------*/ +void dump_sockaddr_in DEFARGS((sin), + struct sockaddr_in *sin) +{ + int i; + + if (!sin) { + printf("Dereference a NULL sockaddr_in? I don't think so.\n"); + return; + } + +#ifdef SIN_LEN + printf("sin_len = %d, ", sin->sin_len); +#endif /* SIN_LEN */ + printf("sin_family = %d, sin_port (conv.) = %d (0x%x)\n", + sin->sin_family, htons(sin->sin_port), + htons(sin->sin_port)); + printf("sin_addr = "); + dump_in_addr(&sin->sin_addr); + printf("sin_zero == "); + for(i=0;i<8;i++) + printf("0x%2x ",sin->sin_zero[i]); + printf("\n"); +} + +/*---------------------------------------------------------------------- + * Dump a generic socket address. Use if no family-specific routine is + * available. + ----------------------------------------------------------------------*/ +void dump_sockaddr DEFARGS((sa), +SOCKADDR *sa) +{ + if (!sa) { + printf("Dereference a NULL sockaddr? I don't think so.\n"); + return; + } + +#ifdef SA_LEN + printf("sa_len = %d, ", sa->sa_len); +#endif /* SA_LEN */ + printf("sa_family = %d", sa->sa_family); +#ifdef SA_LEN + printf(", remaining bytes are:\n"); + { + int i; + for (i = 0; i <sa->sa_len - 2; i++) + printf("0x%2x ",(unsigned char)sa->sa_data[i]); + } +#endif /* SA_LEN */ + printf("\n"); +} + +/*---------------------------------------------------------------------- + * Dump a link-layer socket address. (Not that there are user-level link + * layer sockets, but there are plenty of link-layer addresses in the kernel.) + ----------------------------------------------------------------------*/ +void dump_sockaddr_dl DEFARGS((sdl), + struct sockaddr_dl *sdl) +{ + char buf[256]; + + if (!sdl) { + printf("Dereference a NULL sockaddr_dl? I don't think so.\n"); + return; + } + + printf("sdl_len = %d, sdl_family = %d, sdl_index = %d, sdl_type = %d,\n", + sdl->sdl_len, sdl->sdl_family, sdl->sdl_index, sdl->sdl_type); + buf[sdl->sdl_nlen] = 0; + if (sdl->sdl_nlen) + bcopy(sdl->sdl_data,buf,sdl->sdl_nlen); + printf("sdl_nlen = %d, (name = '%s'\n",sdl->sdl_nlen,buf); + printf("sdl_alen = %d, ",sdl->sdl_alen); + if (sdl->sdl_alen) + { + int i; + + printf("(addr = "); + for (i = 0; i<sdl->sdl_alen; i++) + printf("0x%2x ",(unsigned char)sdl->sdl_data[i+sdl->sdl_nlen]); + } + printf("\n"); + printf("sdl_slen = %d, ",sdl->sdl_slen); + if (sdl->sdl_slen) + { + int i; + + printf("(addr = "); + for (i = 0; i<sdl->sdl_slen; i++) + printf("0x%2x ", + (unsigned char)sdl->sdl_data[i+sdl->sdl_nlen+sdl->sdl_alen]); + } + printf("\n"); +} + +/*---------------------------------------------------------------------- + * Dump a socket address, calling a family-specific routine if available. + ----------------------------------------------------------------------*/ +void dump_smart_sockaddr DEFARGS((sa), +SOCKADDR *sa) +{ + DPRINTF(IDL_MAJOR_EVENT, ("Entering dump_smart_sockaddr\n")); + if (!sa) { + printf("Dereference a NULL sockaddr? I don't think so.\n"); + return; + } + + switch (sa->sa_family) + { +#ifdef INET6 + case AF_INET6: + dump_sockaddr_in6((struct sockaddr_in6 *)sa); + break; +#endif /* INET6 */ + case AF_INET: + dump_sockaddr_in((struct sockaddr_in *)sa); + break; + case AF_LINK: + dump_sockaddr_dl((struct sockaddr_dl *)sa); + break; + default: + dump_sockaddr(sa); + break; + } +} + +#ifdef INET6 +/*---------------------------------------------------------------------- + * Dump an IPv6 header. + ----------------------------------------------------------------------*/ +void dump_ipv6 DEFARGS((ipv6), + struct ipv6 *ipv6) +{ + if (!ipv6) { + printf("Dereference a NULL ipv6? I don't think so.\n"); + return; + } + + printf("Vers & flow label (conv to host order) 0x%x\n", + htonl(ipv6->ipv6_versfl)); + printf("Length (conv) = %d, nexthdr = %d, hoplimit = %d.\n", + htons(ipv6->ipv6_length),ipv6->ipv6_nexthdr,ipv6->ipv6_hoplimit); + printf("Src: "); + dump_in_addr6(&ipv6->ipv6_src); + printf("Dst: "); + dump_in_addr6(&ipv6->ipv6_dst); +} + +/*---------------------------------------------------------------------- + * Dump an ICMPv6 header. This function is not very smart beyond the + * type, code, and checksum. + ----------------------------------------------------------------------*/ +dump_ipv6_icmp DEFARGS((icp), + struct ipv6_icmp *icp) +{ + int i; + + if (!icp) { + printf("Dereference a NULL ipv6_icmp? I don't think so.\n"); + return; + } + + printf("type %d, code %d, cksum (conv) = 0x%x\n",icp->icmp_type, + icp->icmp_code,htons(icp->icmp_cksum)); + printf("First four bytes: 0x%x",htonl(icp->icmp_unused)); + printf("Next four bytes: 0x"); + for (i=0;i<4;i++) + printf("%x",icp->icmp_echodata[i]); + printf("\n"); +} +#endif /* INET6 */ + +/*---------------------------------------------------------------------- + * Dump only the header fields of a single mbuf. + ----------------------------------------------------------------------*/ +void dump_mbuf_hdr DEFARGS((m), + struct mbuf *m) +{ + if (!m) { + printf("Dereference a NULL mbuf? I don't think so.\n"); + return; + } + + printf("Single mbuf at %08x\n", m); + printf("m_len = %d, m_data = 0x%x, m_type = %d\n",m->m_len, + m->m_data, m->m_type); + printf("m_flags = 0x%x ",m->m_flags); + if (m->m_flags & M_PKTHDR) + printf("m_pkthdr.len = %d, m_pkthdr.rcvif = 0x%x",m->m_pkthdr.len, + m->m_pkthdr.rcvif); + if (m->m_flags & M_EXT) + printf(" (IS CLUSTER MBUF)"); + printf("\nm_next = 0x%x m_nextpkt = 0x%x\n",m->m_next, m->m_nextpkt); +} + +/*---------------------------------------------------------------------- + * Dump the entire contents of a single mbuf. + ----------------------------------------------------------------------*/ +void dump_mbuf DEFARGS((m), + struct mbuf *m) +{ + int i; + + dump_mbuf_hdr(m); + printf("m_data:\n"); + for (i = 0; i < m->m_len; i++) + printf("0x%2x%s",(unsigned char)m->m_data[i] , ((i+1) % 16)?" ":"\n"); + printf((i % 16)?"\n":""); +} + +/*---------------------------------------------------------------------- + * Dump the contents of an mbuf chain. (WARNING: Lots of text may + * result. + ----------------------------------------------------------------------*/ +void dump_mchain DEFARGS((m), + struct mbuf *m) +{ + struct mbuf *walker; + int i; + + for (walker = m, i = 0; walker != NULL && (i < 10); + walker = walker->m_next, i++) + dump_mbuf(walker); +} + +/*---------------------------------------------------------------------- + * Dump an mbuf chain's data in a format similar to tcpdump(8). + ----------------------------------------------------------------------*/ +void dump_tcpdump DEFARGS((m), + struct mbuf *m) +{ + struct mbuf *walker; + int i, j, count; + + for (i = count = 0; m && (i < 10); m = m->m_next, i++) { + for (j = 0; j < m->m_len; j++, count++) { + if (!(count % (2 * 8))) + printf("\n\t\t\t"); + if (!(count % 2)) + printf(" "); + printf("%02x", (u_int8)(m->m_data[j])); + } + } +} + +#ifdef INET6 +/*---------------------------------------------------------------------- + * Dump an IPv6 header index table, which is terminated by an entry with + * a NULL mbuf pointer. + ----------------------------------------------------------------------*/ +void dump_ihitab DEFARGS((ihi), + struct in6_hdrindex *ihi) +{ + int i=0; + + if (!ihi) { + printf("Dereference a NULL hdrindex/ihi? I don't think so.\n"); + return; + } + + /* This is dangerous, make sure ihitab was bzeroed. */ + while (ihi[i].ihi_mbuf) + { + printf("ihi_nexthdr = %d, ihi_mbuf = 0x%x.\n",ihi[i].ihi_nexthdr, + ihi[i].ihi_mbuf); + i++; + } +} +#endif /* INET6 */ + +/*---------------------------------------------------------------------- + * Dump an interface address. + ----------------------------------------------------------------------*/ +void dump_ifa DEFARGS((ifa), + struct ifaddr *ifa) +{ + if (ifa == NULL) + { + printf("ifa of NULL.\n"); + return; + } + + printf("ifa_addr: "); + dump_smart_sockaddr(ifa->ifa_addr); + printf("ifa_netmask: "); + dump_smart_sockaddr(ifa->ifa_netmask); +} + +/*---------------------------------------------------------------------- + * Dump an interface structure. + ----------------------------------------------------------------------*/ +void dump_ifp DEFARGS((ifp), + struct ifnet *ifp) +{ + if (!ifp) { + printf("Dereference a NULL ifnet/ifp? I don't think so.\n"); + return; + } + + printf("Interface name: %s.\n",ifp->if_name); + printf("Interface type: %d. ",ifp->if_type); + printf("MTU: %d.\n",ifp->if_mtu); +} + +/*---------------------------------------------------------------------- + * Dump a route structure (sockaddr/rtentry pair). + ----------------------------------------------------------------------*/ +void dump_route DEFARGS((ro), + struct route *ro) +{ + if (!ro) { + printf("Dereference a NULL route? I don't think so.\n"); + return; + } + + printf("ro_rt = 0x%x, ro_dst is:\n",ro->ro_rt); + dump_smart_sockaddr(&ro->ro_dst); +} + +/*---------------------------------------------------------------------- + * Dump a routing entry. + ----------------------------------------------------------------------*/ +void dump_rtentry DEFARGS((rt), + struct rtentry *rt) +{ + if (!rt) { + printf("Dereference a NULL rtentry? I don't think so.\n"); + return; + } + + printf("rt_key is:\n"); + dump_smart_sockaddr(rt_key(rt)); + printf("rt_mask is:\n"); + dump_smart_sockaddr(rt_mask(rt)); + printf("rt_llinfo = 0x%x ",rt->rt_llinfo); + printf("rt_rmx.rmx_mtu = %d ",rt->rt_rmx.rmx_mtu); + printf("rt_refcnt = %d ",rt->rt_refcnt); + printf("rt_flags = 0x%x\n",rt->rt_flags); + printf("rt_ifp is:\n"); + dump_ifp(rt->rt_ifp); + printf("rt_ifa is:\n"); + dump_ifa(rt->rt_ifa); +} + +/*---------------------------------------------------------------------- + * Dump an Internet (v4/v6) protocol control block. + ----------------------------------------------------------------------*/ +void dump_inpcb DEFARGS((inp), + struct inpcb *inp) +{ + if (!inp) { + printf("Dereference a NULL inpcb? I don't think so.\n"); + return; + } + + printf("inp_next = 0x%x, inp_prev = 0x%x, inp_head = 0x%x.\n",inp->inp_next, + inp->inp_prev, inp->inp_head); + printf("inp_socket = 0x%x, inp_ppcb\n",inp->inp_socket,inp->inp_ppcb); +#ifdef INET6 + printf("faddr, faddr6:\n"); + dump_in_addr(&inp->inp_faddr); dump_in_addr6(&inp->inp_faddr6); + printf("laddr, laddr6:\n"); + dump_in_addr(&inp->inp_laddr); dump_in_addr6(&inp->inp_laddr6); +#else /* INET6 */ + printf("faddr:\n"); + dump_in_addr(&inp->inp_faddr); + printf("laddr:\n"); + dump_in_addr(&inp->inp_laddr); +#endif /* INET6 */ + printf("inp_route: "); + dump_route(&inp->inp_route); +#ifdef INET6 + printf("inp_ipv6:"); + dump_ipv6(&inp->inp_ipv6); +#endif /* INET6 */ + printf("inp_ip:"); + printf("<Coming soon.>\n"); + printf("inp_options = 0x%x, inp_moptions{6,} = 0x%x,\n",inp->inp_options, + inp->inp_moptions); + printf("inp_flags = 0x%x, inp_fport = %d, inp_lport = %d.\n", + (unsigned)inp->inp_flags,inp->inp_fport, inp->inp_lport); +} + +#ifdef INET6 +/*---------------------------------------------------------------------- + * Dump an IPv6 discovery queue structure. + ----------------------------------------------------------------------*/ +void dump_discq DEFARGS((dq), + struct discq *dq) +{ + if (!dq) { + printf("Dereference a NULL discq? I don't think so.\n"); + return; + } + + printf("dq_next = 0x%x, dq_prev = 0x%x, dq_rt = 0x%x,\n",dq->dq_next, + dq->dq_prev, dq->dq_rt); + printf("dq_queue = 0x%x.\n",dq->dq_queue); + /* Dump first mbuf chain? */ + /*printf("dq_expire = %d (0x%x).\n",dq->dq_expire,dq->dq_expire);*/ +} +#endif /* INET6 */ + +/*---------------------------------------------------------------------- + * Dump a data buffer + ----------------------------------------------------------------------*/ +void dump_buf DEFARGS((buf, len), + char *buf AND + int len) +{ + int i; + + printf("buf=0x%x len=%d:\n", (unsigned int)buf, len); + for (i = 0; i < len; i++) { + printf("0x%x ", (u_int8)*(buf+i)); + } + printf("\n"); +} + + +/*---------------------------------------------------------------------- + * Dump a key_tblnode structrue + ----------------------------------------------------------------------*/ +void dump_keytblnode DEFARGS((ktblnode), + struct key_tblnode *ktblnode) +{ + if (!ktblnode) { + printf("NULL key table node pointer!\n"); + return; + } + printf("solist=0x%x ", (unsigned int)ktblnode->solist); + printf("secassoc=0x%x ", (unsigned int)ktblnode->secassoc); + printf("next=0x%x\n", (unsigned int)ktblnode->next); +} + +/*---------------------------------------------------------------------- + * Dump an ipsec_assoc structure + ----------------------------------------------------------------------*/ +void dump_secassoc DEFARGS((seca), +struct key_secassoc *seca) +{ + u_int8 *p; + int i; + + if (seca) { + printf("secassoc_len=%u ", seca->len); + printf("secassoc_type=%d ", seca->type); + printf("secassoc_state=0x%x\n", seca->state); + printf("secassoc_label=%u ", seca->label); + printf("secassoc_spi=0x%x ", (unsigned int)seca->spi); + printf("secassoc_keylen=%u\n", seca->keylen); + printf("secassoc_ivlen=%u ", seca->ivlen); + printf("secassoc_algorithm=%u ", seca->algorithm); + printf("secassoc_lifetype=%u\n", seca->lifetype); + printf("secassoc_iv=0x%x:\n", (unsigned int)seca->iv); + p = (u_int8 *)(seca->iv); + for (i = 0 ; i < seca->ivlen; i++) + printf("0x%x ", *(p + i)); + printf("secassoc_key=0x%x:\n", (unsigned int)seca->key); + p = (u_int8 *)(seca->key); + for (i = 0 ; i < seca->keylen; i++) + printf("0x%x ", *(p + i)); + printf("secassoc_lifetime1=%u ", (unsigned int)seca->lifetime1); + printf("secassoc_lifetime2=%u\n", (unsigned int)seca->lifetime2); + dump_smart_sockaddr(seca->src); + dump_smart_sockaddr(seca->dst); + dump_smart_sockaddr(seca->from); + } else + printf("can't dump null secassoc pointer!\n"); +} + + +/*---------------------------------------------------------------------- + * Dump a key_msghdr structure + ----------------------------------------------------------------------*/ +void dump_keymsghdr DEFARGS((km), + struct key_msghdr *km) +{ + if (km) { + printf("key_msglen=%d\n", km->key_msglen); + printf("key_msgvers=%d\n", km->key_msgvers); + printf("key_msgtype=%d\n", km->key_msgtype); + printf("key_pid=%d\n", km->key_pid); + printf("key_seq=%d\n", km->key_seq); + printf("key_errno=%d\n", km->key_errno); + printf("type=0x%x\n", (unsigned int)km->type); + printf("state=0x%x\n", (unsigned int)km->state); + printf("label=0x%x\n", (unsigned int)km->label); + printf("spi=0x%x\n", (unsigned int)km->spi); + printf("keylen=%d\n", km->keylen); + printf("ivlen=%d\n", km->ivlen); + printf("algorithm=%d\n", km->algorithm); + printf("lifetype=0x%x\n", (unsigned int)km->lifetype); + printf("lifetime1=%u\n", (unsigned int)km->lifetime1); + printf("lifetime2=%u\n", (unsigned int)km->lifetime2); + } else + printf("key_msghdr pointer is NULL!\n"); +} + + +/*---------------------------------------------------------------------- + * Dump a key_msgdata structure + ----------------------------------------------------------------------*/ +void dump_keymsginfo DEFARGS((kp), + struct key_msgdata *kp) +{ + int i; + + if (kp) { + printf("src addr:\n"); + dump_smart_sockaddr(kp->src); + printf("dest addr:\n"); + dump_smart_sockaddr(kp->dst); + printf("from addr:\n"); + dump_smart_sockaddr(kp->from); +#define dumpbuf(a, b) \ + { for (i= 0; i < (b); i++) \ + printf("0x%2x%s", (unsigned char)(*((caddr_t)a+i)),((i+1)%16)?" ":"\n");\ + printf("\n"); } + printf("iv is:\n"); + dumpbuf(kp->iv, kp->ivlen); + printf("key is:\n"); + dumpbuf(kp->key, kp->keylen); +#undef dumpbuf + } else + printf("key_msgdata point is NULL!\n"); +} diff --git a/sys/netkey/key_debug.h b/sys/netkey/key_debug.h new file mode 100644 index 0000000..05f1bc8 --- /dev/null +++ b/sys/netkey/key_debug.h @@ -0,0 +1,151 @@ +/* + * in6_debug.h -- Insipired by Craig Metz's Net/2 in6_debug.h, but + * not quite as heavyweight (initially, anyway). + * + * In particular, if function exit-entries are to be + * documented, do them in a lightweight fashion. + * + * Copyright 1995 by Dan McDonald, Bao Phan, and Randall Atkinson, + * All Rights Reserved. + * All Rights under this copyright have been assigned to NRL. + */ + +/*---------------------------------------------------------------------- +# @(#)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). + +----------------------------------------------------------------------*/ + +/* IDL_* is IPv6 Debug Level */ + +#define IDL_ALL 0xFFFFFFFE /* Report all messages. */ +#define IDL_NONE 0 /* Report no messages. */ + +#define IDL_CRITICAL 3 +#define IDL_ERROR 7 +#define IDL_MAJOR_EVENT 10 +#define IDL_EVENT 15 +#define IDL_GROSS_EVENT 20 +#define IDL_FINISHED 0xFFFFFFF0 + +/* + * Make sure argument for DPRINTF is in parentheses. + * + * For both DPRINTF and DDO, and attempt was made to make both macros + * be usable as normal C statments. There is a small amount of compiler + * trickery (if-else clauses with effectively null statements), which may + * cause a few compilers to complain. + */ + +#ifdef INET6_DEBUG + +/* + * DPRINTF() is a general printf statement. The "arg" is literally what + * would follow the function name printf, which means it has to be in + * parenthesis. Unlimited arguments can be used this way. + * + * EXAMPLE: + * DPRINTF(IDL_MAJOR_EVENT,("Hello, world. IP version %d.\n",vers)); + */ +#define DPRINTF(lev,arg) if ((lev) < in6_debug_level) { \ + printf arg; \ + } \ + else in6_debug_level = in6_debug_level + +/* + * DDO() executes a series of statements at a certain debug level. The + * "stmt" argument is a statement in the sense of a "statement list" in a + * C grammar. "stmt" does not have to end with a semicolon. + * + * EXAMPLE: + * DDO(IDL_CRITICAL,dump_ipv6(header), dump_inpcb(inp)); + */ +#define DDO(lev,stmt) if ((lev) < in6_debug_level) { stmt ; } \ + else in6_debug_level = in6_debug_level + +/* + * DP() is a shortcut for DPRINTF(). Basically: + * + * DP(lev, var, fmt) == DPRINTF(IDL_lev, ("var = %fmt\n", var)) + * + * It is handy for printing single variables without a lot of typing. + * + * EXAMPLE: + * + * DP(CRITICAL,length,d); + * same as DPRINTF(IDL_CRITICAL, ("length = %d\n", length)) + */ +#define DP(lev, var, fmt) DPRINTF(IDL_ ## lev, (#var " = %" #fmt "\n", var)) + +#ifndef SOCKADDR +#define SOCKADDR struct sockaddr +#endif /* SOCKADDR */ + +void dump_buf __P((char *, int)); +void dump_sockaddr __P((SOCKADDR *)); +void dump_smart_sockaddr __P((SOCKADDR *)); +void dump_keytblnode __P((struct key_tblnode *)); +void dump_secassoc __P((struct key_secassoc *)); +void dump_keymsghdr __P((struct key_msghdr *)); + +#else /* ! INET6_DEBUG */ + +#define DPRINTF(lev,arg) +#define DDO(lev, stmt) +#define DP(x, y, z) + +#endif /* INET6_DEBUG */ + +#ifndef INET6_DEBUG_C +extern unsigned int in6_debug_level; +#endif |