/* $USAGI: pfkey_v2.c,v 1.8 2001/10/22 05:47:19 mk Exp $ */

/* this file derived from FreeS/WAN-1.9. (mk@linux-ipv6.org) */
/*
 * RFC2367 PF_KEYv2 Key management API domain socket I/F
 * Copyright (C) 1999, 2000  Richard Guy Briggs.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 */

/*
 *		Template from /usr/src/linux-2.0.36/net/unix/af_unix.c.
 *		Hints from /usr/src/linux-2.0.36/net/ipv4/udp.c.
 */


#ifdef MODULE
#include <linux/module.h>
#ifdef MODVERSIONS
# include <linux/modversions.h>
#endif /* MODVERSIONS */
#endif /* MODULE */

#include <linux/config.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/version.h>

#include <linux/major.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/socket.h>
#include <linux/un.h>
#include <linux/fcntl.h>
#include <linux/termios.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h> /* struct socket */
#include <linux/in.h>
#include <linux/fs.h>
#include <linux/malloc.h>
#include <asm/segment.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/sock.h> /* struct sock */
#include <net/af_unix.h>
#include <linux/spinlock.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif /* CONFIG_PROC_FS */

#include <linux/types.h>
 
#include <asm/uaccess.h>
#include <linux/in6.h>

#include <net/pfkeyv2.h>
#include <net/pfkey.h>
#include <net/spd.h>

#ifdef CONFIG_IPSEC_DEBUG
# ifndef DEBUG
#   define DEBUG 1
# endif
#endif /* CONFIG_IPSEC_DEBUG */

#include "pfkey_v2_msg.h"

#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)

struct proto_ops SOCKOPS_WRAPPED(pfkey_ops);
struct sock *pfkey_sock_list = NULL;
struct supported_list *pfkey_supported_list[SADB_SATYPE_MAX+1];

struct socket_list *pfkey_open_sockets = NULL;
struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];

rwlock_t pfkey_sk_lock = RW_LOCK_UNLOCKED;

/* derived from FreeS/WAN-1.9 pfkey_v2_parse.c (mk) */
int
pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg, struct sadb_msg **pfkey_reply)
{
	int error = 0;
	
	if (pfkey_msg->sadb_msg_satype > SADB_SATYPE_MAX) {
		return -EINVAL;
	}
	
	switch (pfkey_msg->sadb_msg_type) {
	case SADB_GETSPI:
		error = -EINVAL; /* XXX */
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_GETSPI\n");
		break;
	case SADB_UPDATE:
		error = -EINVAL;/* XXX */
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_UPDATE\n");
		break;
	case SADB_ADD:
		error = sadb_msg_add_parse(sk, pfkey_msg, pfkey_reply);
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_ADD\n");
		break;
	case SADB_DELETE:
		error = sadb_msg_delete_parse(sk, pfkey_msg, pfkey_reply);
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_DELETE\n");
		break;
	case SADB_FLUSH:
		error = sadb_msg_flush_parse(sk, pfkey_msg, pfkey_reply);
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_FLUSH\n");
		break;
	case SADB_REGISTER:
		error = sadb_msg_register_parse(sk, pfkey_msg, pfkey_reply);
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_REGISTER\n");
		break;
	case SADB_X_GRPSA:
		error =  -EINVAL;/* XXX */
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_X_GRPSA\n");
		break;
	case SADB_X_ADDFLOW:
		error = sadb_msg_addflow_parse(sk, pfkey_msg, pfkey_reply);
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_ADDFLOW\n");
		break;
	case SADB_X_DELFLOW:
		error = sadb_msg_delflow_parse(sk, pfkey_msg, pfkey_reply);
		pr_debug(KERN_INFO "PF_KEY:interp: SADB_DELFLOW\n");
		break;
	default:
		error = -EINVAL;
		break;
	}
	
	return error;
}


int
pfkey_list_remove_socket(struct socket *socketp, struct socket_list **sockets)
{
	struct socket_list *socket_listp,*prev;

	if (!socketp) {
		return -EINVAL;
	}

	if (!sockets) {
		return -EINVAL;
	}

	socket_listp = *sockets;
	prev = NULL;
	
	while (socket_listp != NULL) {
		if (socket_listp->socketp == socketp) {
			if (prev != NULL) {
				prev->next = socket_listp->next;
			} else {
				*sockets = socket_listp->next;
			}
			
			kfree((void*)socket_listp);
			pr_debug("pfkey_list_remove_socket: removed sock=%p\n", socketp);
			
			break;
		}
		prev = socket_listp;
		socket_listp = socket_listp->next;
	}

	return 0;
}

int
pfkey_list_insert_socket(struct socket *socketp, struct socket_list **sockets)
{
	struct socket_list *socket_listp;

	if (!socketp) {
		return -EINVAL;
	}

	if (!sockets) {
		return -EINVAL;
	}

	if ((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) {
		return -ENOMEM;
	}
	
	socket_listp->socketp = socketp;
	socket_listp->next = *sockets;
	*sockets = socket_listp;
	pr_debug("pfkey_list_insert_socket: inserted sock=%p\n", socketp);

	return 0;
}
  
int
pfkey_list_remove_supported(struct supported *supported, struct supported_list **supported_list)
{
	struct supported_list *supported_listp = *supported_list, *prev = NULL;
	
	if (!supported) {
		return -EINVAL;
	}

	if (!supported_list) {
		return -EINVAL;
	}

	while (supported_listp != NULL) {
		if (supported_listp->supportedp == supported) {
			if (prev != NULL) {
				prev->next = supported_listp->next;
			} else {
				*supported_list = supported_listp->next;
			}
			
			kfree((void*)supported_listp);
			pr_debug("pfkey_list_remove_supported: removed supported=%p\n", supported);
			
			break;
		}
		prev = supported_listp;
		supported_listp = supported_listp->next;
	}

	return 0;
}

int
pfkey_list_insert_supported(struct supported *supported, struct supported_list **supported_list)
{
	struct supported_list *supported_listp;

	if (!supported) {
		return -EINVAL;
	}

	if (!supported_list) {
		return -EINVAL;
	}

	supported_listp = (struct supported_list *)kmalloc(sizeof(struct supported_list), GFP_KERNEL);
	if (supported_listp == NULL)	{
		return -ENOMEM;
	}
	
	supported_listp->supportedp = supported;
	supported_listp->next = *supported_list;
	*supported_list = supported_listp;
	pr_debug("pfkey_list_insert_supported: inserted supported=%p\n", supported);

	return 0;
}
  
static void
pfkey_insert_socket(struct sock *sk)
{
	write_lock(&pfkey_sk_lock);
	sk->next=pfkey_sock_list;
	pfkey_sock_list=sk;
	write_unlock(&pfkey_sk_lock);
}

static void
pfkey_remove_socket(struct sock *sk)
{
	struct sock **s;
	
	write_lock(&pfkey_sk_lock);
	s=&pfkey_sock_list;

	while (*s!=NULL) {
		if (*s==sk) {
			*s=sk->next;
			sk->next=NULL;
			goto final;
		}
		s=&((*s)->next);
	}

final:
	write_unlock(&pfkey_sk_lock);
	return;
}

static void
pfkey_destroy_socket(struct sock *sk)
{
	struct sk_buff *skb;

	pfkey_remove_socket(sk);

	while (sk && ((skb=skb_dequeue(&(sk->receive_queue)))!=NULL)) {
#ifdef CONFIG_IPSEC_DEBUG
			printk(KERN_DEBUG "pfkey_destroy_socket: pfkey_skb contents:");
			printk(" next:%p", skb->next);
			printk(" prev:%p", skb->prev);
			printk(" list:%p", skb->list);
			printk(" sk:%p", skb->sk);
			printk(" stamp:%ld.%ld", skb->stamp.tv_sec, skb->stamp.tv_usec);
			printk(" dev:%p", skb->dev);
			if (skb->dev) {
				if (skb->dev->name) {
					printk(" dev->name:%s", skb->dev->name);
				} else {
					printk(" dev->name:NULL?");
				}
			} else {
				printk(" dev:NULL");
			}
			printk(" h:%p", skb->h.raw);
			printk(" nh:%p", skb->nh.raw);
			printk(" mac:%p", skb->mac.raw);
			printk(" dst:%p", skb->dst);
			if (sysctl_ipsec_debug_verbose) {
				int i;
				
				printk(" cb");
				for (i=0; i<48; i++) {
					printk(":%2x", skb->cb[i]);
				}
			}
			printk(" len:%d", skb->len);
			printk(" csum:%d", skb->csum);
			printk(" used:%d", skb->used);
			printk(" cloned:%d", skb->cloned);
			printk(" pkt_type:%d", skb->pkt_type);
			printk(" ip_summed:%d", skb->ip_summed);
			printk(" priority:%d", skb->priority);
			printk(" protocol:%d", skb->protocol);
			printk(" security:%d", skb->security);
			printk(" truesize:%d", skb->truesize);
			printk(" head:%p", skb->head);
			printk(" data:%p", skb->data);
			printk(" tail:%p", skb->tail);
			printk(" end:%p", skb->end);
			if (sysctl_ipsec_debug_verbose) {
				unsigned int i;
				printk(" data");
				for (i=(unsigned int)(skb->head); i<(unsigned int)(skb->end); i++) {
					printk(":%2x", (unsigned char)(*(char*)(i)));
				}
			}
			printk(" destructor:%p", skb->destructor);
			printk("\n");
		}
#endif /* CONFIG_IPSEC_DEBUG */
		kfree_skb(skb);
	}

	sk->dead = 1;
	sk_free(sk);
}

int
pfkey_upmsg(struct socket *sock, struct sadb_msg *pfkey_msg)
{
	int error;
	struct sk_buff * skb = NULL;
	struct sock *sk;

	if (sock == NULL) {
		return -EINVAL;
	}

	if (pfkey_msg == NULL) {
		return -EINVAL;
	}

	sk = sock->sk;

	if (sk == NULL) {
		return -EINVAL;
	}

	if (!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) {
		return -ENOBUFS;
	}
	
	skb->dev = NULL;
	
	if (skb_tailroom(skb) < pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
		printk(KERN_WARNING "klips_error:pfkey_upmsg: "
		       "tried to skb_put %ld, %d available.  This should never happen, please report.\n",
		       (unsigned long int)pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN,
		       skb_tailroom(skb));
		kfree_skb(skb);

		return -ENOBUFS;
	}
	skb->h.raw = skb_put(skb, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
	memcpy(skb->h.raw, pfkey_msg, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);

	if ((error = sock_queue_rcv_skb(sk, skb)) < 0) {
		skb->sk=NULL;
		kfree_skb(skb);
		return error;
	}
	return 0;
}

static int
pfkey_create(struct socket *sock, int protocol)
{
	struct sock *sk;

	if (sock == NULL) {
		return -EINVAL;
	}

	if (sock->type != SOCK_RAW) {
		return -ESOCKTNOSUPPORT;
	}

	if (protocol != PF_KEY_V2) {
		return -EPROTONOSUPPORT;
	}

	if ((current->uid != 0)) {
		return -EACCES;
	}

	sock->state = SS_UNCONNECTED;
#ifdef MODULE
	MOD_INC_USE_COUNT;
#endif /* MODULE */

	if ((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1)) == NULL)
	{
#ifdef MODULE
		MOD_DEC_USE_COUNT;
#endif /* MODULE */
		return -ENOMEM;
	}

	sock_init_data(sock, sk);

	sk->destruct = NULL;
	sk->reuse = 1;
	sock->ops = &SOCKOPS_WRAPPED(pfkey_ops);

	sk->zapped=0;
	sk->family = PF_KEY;
	sk->protocol = protocol;
	key_pid(sk) = current->pid;

	pfkey_insert_socket(sk);
	pfkey_list_insert_socket(sock, &pfkey_open_sockets);

	return 0;
}

static int
pfkey_release(struct socket *sock)
{
	struct sock *sk;
	int i;

	if (sock==NULL) {
		return 0; /* -EINVAL; */
	}
		
	sk=sock->sk;
	
	/* May not have data attached */
	if (sk==NULL) {
		return 0; /* -EINVAL; */
	}
		
	if (!sk->dead)
		if (sk->state_change) {
			sk->state_change(sk);
		}

	sock->sk = NULL;

	/* Try to flush out this socket. Throw out buffers at least */
	pfkey_destroy_socket(sk);
	pfkey_list_remove_socket(sock, &pfkey_open_sockets);
	for (i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
		pfkey_list_remove_socket(sock, &(pfkey_registered_sockets[i]));
	}

#ifdef MODULE
	MOD_DEC_USE_COUNT;
#endif /* MODULE */

	return 0;
}

static int
pfkey_shutdown(struct socket *sock, int mode)
{
	struct sock *sk;

	if (sock == NULL) {
		return -EINVAL;
	}

	sk=sock->sk;
	
	if (sk == NULL) {
		return -EINVAL;
	}

	mode++;
	
	if (mode&SEND_SHUTDOWN) {
		sk->shutdown|=SEND_SHUTDOWN;
		sk->state_change(sk);
	}

	if (mode&RCV_SHUTDOWN) {
		sk->shutdown|=RCV_SHUTDOWN;
		sk->state_change(sk);
	}
	return 0;
}

/*
 *	Send PF_KEY data down.
 */
		
static int
pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
{
	struct sock *sk;
	int error = 0;
	struct sadb_msg *pfkey_msg = NULL, *pfkey_reply = NULL;

	if (sock == NULL) {
		SENDERR(EINVAL);
	}
	
	sk = sock->sk;

	if (sk == NULL) {
		SENDERR(EINVAL);
	}
	
	if (msg == NULL) {
		SENDERR(EINVAL);
	}

	if (sk->err) {
		error = sock_error(sk);
		SENDERR(-error);
	}

	if ((current->uid != 0)) {
		SENDERR(EACCES);
	}

	if (msg->msg_control) {
		SENDERR(EINVAL);
	}
		
	if (sk->shutdown & SEND_SHUTDOWN) {
		send_sig(SIGPIPE, current, 0);
		SENDERR(EPIPE);
	}
	
	if (len < sizeof(struct sadb_msg)) {
		SENDERR(EMSGSIZE);
	}

	if ((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL) {
		SENDERR(ENOBUFS);
	}

	memcpy_fromiovec((void *)pfkey_msg, msg->msg_iov, len);

	if (pfkey_msg->sadb_msg_version != PF_KEY_V2) {
		kfree((void*)pfkey_msg);
		return -EINVAL;
	}

	if (len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
		SENDERR(EMSGSIZE);
	}

#if 0
	/* This check is questionable, since a downward message could be
	   the result of an ACQUIRE either from kernel (PID==0) or
	   userspace (some other PID). */
	/* check PID */
	if (pfkey_msg->sadb_msg_pid != current->pid) {
		SENDERR(EINVAL);
	}
#endif

	if (pfkey_msg->sadb_msg_reserved) {
		SENDERR(EINVAL);
	}
	
	if ((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)) {
		SENDERR(EINVAL);
	}
	
	if ((error = pfkey_msg_interp(sk, pfkey_msg, &pfkey_reply))) {
		struct socket_list *pfkey_socketsp;

		if ((pfkey_reply = (struct sadb_msg*)kmalloc(sizeof(struct sadb_msg), GFP_KERNEL)) == NULL) {
			SENDERR(ENOBUFS);
		}
		memcpy((void*)pfkey_reply, (void*)pfkey_msg, sizeof(struct sadb_msg));
		pfkey_reply->sadb_msg_errno = -error;
		pfkey_reply->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;

		for (pfkey_socketsp = pfkey_open_sockets;
		    pfkey_socketsp;
		    pfkey_socketsp = pfkey_socketsp->next) {
			int error_upmsg = 0;
			if ((error_upmsg = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
				goto errlab;
			}
		}
		
		pfkey_msg_free(&pfkey_reply);
		
		SENDERR(-error);
	}

 errlab:
	if (pfkey_msg) {
		kfree((void*)pfkey_msg);
	}
	
	if (error) {
		return error;
	} else {
		return len;
	}
}

/*
 *	Receive PF_KEY data up.
 */
		
static int
pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
{
	struct sock *sk;
	int noblock = flags & MSG_DONTWAIT;
	struct sk_buff *skb;
	int error;

	if (sock == NULL) {
		return -EINVAL;
	}

	sk = sock->sk;

	if (sk == NULL) {
		return -EINVAL;
	}

	if (msg == NULL) {
		return -EINVAL;
	}

	if (flags & ~MSG_PEEK) {
		return -EOPNOTSUPP;
	}
		
	msg->msg_namelen = 0; /* sizeof(*ska); */
		
	if (sk->err) {
		return sock_error(sk);
	}

	if ((skb = skb_recv_datagram(sk, flags, noblock, &error) ) == NULL) {
                return error;
	}

	if (size > skb->len) {
		size = skb->len;
	}

	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
        sk->stamp=skb->stamp;

	skb_free_datagram(sk, skb);
	return size;
}

struct net_proto_family pfkey_family_ops = {
	PF_KEY,
	pfkey_create
};

struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = {
	family:		PF_KEY,
	release:	pfkey_release,
	bind:		sock_no_bind,
	connect:	sock_no_connect,
	socketpair:	sock_no_socketpair,
	accept:		sock_no_accept,
	getname:	sock_no_getname,
	poll:		datagram_poll,
	ioctl:		sock_no_ioctl,
	listen:		sock_no_listen,
	shutdown:	pfkey_shutdown,
	setsockopt:	sock_no_setsockopt,
	getsockopt:	sock_no_getsockopt,
	sendmsg:	pfkey_sendmsg,
	recvmsg:	pfkey_recvmsg,
	mmap:		sock_no_mmap,
};

#include <linux/smp_lock.h>
#if 0
	SOCKOPS_WRAP(pfkey, PF_KEY);
#endif

   
#ifdef CONFIG_PROC_FS
int
pfkey_get_info(char *buffer, char **start, off_t offset, int length)
{
	off_t pos=0;
	off_t begin=0;
	int len=0;
	struct sock *sk=pfkey_sock_list;
	
	len+= sprintf(buffer,
		      "    sock   pid   socket     next     prev e n p sndbf    Flags     Type St\n");
	
	while (sk!=NULL) {
		len+=sprintf(buffer+len,
			     "%8p %5d %8p %8p %8p %d %d %d %5d %08lX %8X %2X\n",
			     sk,
			     key_pid(sk),
			     sk->socket,
			     sk->next,
			     sk->prev,
			     sk->err,
			     sk->num,
			     sk->protocol,
			     sk->sndbuf,
			     sk->socket->flags,
			     sk->socket->type,
			     sk->socket->state);
		pos=begin+len;
		if (pos<offset) {
			len=0;
			begin=pos;
		}
		if (pos>offset+length)
			break;
		sk=sk->next;
	}
	*start=buffer+(offset-begin);
	len-=(offset-begin);
	if (len>length)
		len=length;
	return len;
}

int
pfkey_supported_get_info(char *buffer, char **start, off_t offset, int length)
{
	off_t pos=0;
	off_t begin=0;
	int len=0;
	int satype;
	struct supported_list *pfkey_supported_p;
	
	len+= sprintf(buffer,
		      "satype exttype alg_id ivlen minbits maxbits\n");
	
	for (satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
		pfkey_supported_p = pfkey_supported_list[satype];
		while (pfkey_supported_p) {
			len+=sprintf(buffer+len,
				     "    %2d      %2d     %2d   %3d     %3d     %3d\n",
				     satype,
				     pfkey_supported_p->supportedp->supported_alg_exttype,
				     pfkey_supported_p->supportedp->supported_alg_id,
				     pfkey_supported_p->supportedp->supported_alg_ivlen,
				     pfkey_supported_p->supportedp->supported_alg_minbits,
				     pfkey_supported_p->supportedp->supported_alg_maxbits);
			
			pos=begin+len;
			if (pos<offset) {
				len=0;
				begin=pos;
			}
			if (pos>offset+length)
				break;
			pfkey_supported_p = pfkey_supported_p->next;
		}
	}
	*start=buffer+(offset-begin);
	len-=(offset-begin);
	if (len>length)
		len=length;
	return len;
}

int
pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length)
{
	off_t pos=0;
	off_t begin=0;
	int len=0;
	int satype;
	struct socket_list *pfkey_sockets;
	
	len+= sprintf(buffer,
		      "satype   socket   pid       sk\n");
	
	for (satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
		pfkey_sockets = pfkey_registered_sockets[satype];
		while (pfkey_sockets) {
			len+=sprintf(buffer+len,
				     "    %2d %8p %5d %8p\n",
				     satype,
				     pfkey_sockets->socketp,
				     key_pid(pfkey_sockets->socketp->sk),
				     pfkey_sockets->socketp->sk);
			
			pos=begin+len;
			if (pos<offset) {
				len=0;
				begin=pos;
			}
			if (pos>offset+length)
				break;
			pfkey_sockets = pfkey_sockets->next;
		}
	}
	*start=buffer+(offset-begin);
	len-=(offset-begin);
	if (len>length)
		len=length;
	return len;
}
#endif /* CONFIG_PROC_FS */

static int
supported_add_all(int satype, struct supported supported[], int size)
{
	int i;
	int error = 0;
	
	pr_debug("supported_add_all:\nsizeof(supported_init_<satype=%d>)[%d]/sizeof(struct supported)[%d]=%d.\n",
		satype, size, sizeof(struct supported), size/sizeof(struct supported));

	for (i = 0; i < size / sizeof(struct supported); i++) {
		pr_debug("i=%d inserting satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
			i,
			satype,
			supported[i].supported_alg_exttype,
			supported[i].supported_alg_id,
			supported[i].supported_alg_ivlen,
			supported[i].supported_alg_minbits,
			supported[i].supported_alg_maxbits);
		error |= pfkey_list_insert_supported(&(supported[i]),
					    &(pfkey_supported_list[satype]));
	}
	return error;
}

static int
supported_remove_all(int satype)
{
	int error = 0;
	struct supported *supportedp;

	while (pfkey_supported_list[satype]) {
		supportedp = pfkey_supported_list[satype]->supportedp;
		error |= pfkey_list_remove_supported(supportedp,
					    &(pfkey_supported_list[satype]));
	}
	return error;
}

int
pfkey_init(void)
{
	int error = 0;
	int i;

	static struct supported supported_init_ah[] = {
		{SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128},
		{SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160}
	};
	static struct supported supported_init_esp[] = {
		{SADB_EXT_SUPPORTED_AUTH, SADB_AALG_MD5HMAC, 0, 128, 128},
		{SADB_EXT_SUPPORTED_AUTH, SADB_AALG_SHA1HMAC, 0, 160, 160},
		{SADB_EXT_SUPPORTED_ENCRYPT, SADB_EALG_DESCBC, 64, 56, 56},
		{SADB_EXT_SUPPORTED_ENCRYPT, SADB_EALG_3DESCBC, 128, 168, 168}
	};


	for (i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
		pfkey_registered_sockets[i] = NULL;
		pfkey_supported_list[i] = NULL;
	}

	supported_add_all(SADB_SATYPE_AH, supported_init_ah, sizeof(supported_init_ah));
	supported_add_all(SADB_SATYPE_ESP, supported_init_esp, sizeof(supported_init_esp));

        sock_register(&pfkey_family_ops);

#ifdef CONFIG_PROC_FS
	proc_net_create ("pf_key", 0, pfkey_get_info);
	proc_net_create ("pf_key_supported", 0, pfkey_supported_get_info);
	proc_net_create ("pf_key_registered", 0, pfkey_registered_get_info);
#endif          /* CONFIG_PROC_FS */


	error = sadb_init();
	if (error) {
		pr_debug("pfkey_init: sadb_init failed\n");
		goto err;
	}
	error = spd_init();
	if (error) {
		pr_debug("pfkey_init: spd_init faild\n");
		goto err;
	}
err:
	return error;
}

int
pfkey_cleanup(void)
{
	int error = 0;
	
	error = spd_cleanup();
	if (error) {
		printk(KERN_DEBUG "pfkey_cleanup: spd_cleanup failed\n");
		goto err;
	}
	error = sadb_cleanup();
	if (error) {
		printk(KERN_DEBUG "pfkey_cleanup: sadb_cleanup failed\n");
		goto err;
	}

	printk(KERN_INFO "klips_info:pfkey_cleanup: "
	       "shutting down PF_KEY domain sockets.\n");

        sock_unregister(PF_KEY);

	supported_remove_all(SADB_SATYPE_AH);

#ifdef CONFIG_PROC_FS
	proc_net_remove ("pf_key");
	proc_net_remove ("pf_key_supported");
	proc_net_remove ("pf_key_registered");
#endif          /* CONFIG_PROC_FS */

	/* other module unloading cleanup happens here */
err:
	return error;
}

#ifdef MODULE
static int __init pfkey_module_init(void)
{
	int err = pfkey_init();
	return err;
}

static void __exit pfkey_module_cleanup(void)
{
	pfkey_cleanup();
}

module_init(pfkey_module_init);
module_exit(pfkey_module_cleanup);

#else /* MODULE */
void
pfkey_proto_init(struct net_proto *pro)
{
	pfkey_init();
}
#endif /* MODULE */
/*
 * Local variables:
 * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/include -Wall -Wstrict-prototypes -O6 -c  pfkey_v2.c `[ -f /usr/src/linux/include/linux/modversions.h ] && echo -DMODVERSIONS`"
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 *  c-mode-indentation-style: "linux"
 * End:
 * (mk)
 */
/*
 * $Log: pfkey_v2.c,v $
 * Revision 1.8  2001/06/11 11:09:37  mk
 * change sti/cli to write_lock/write_unlock
 *
 * Revision 1.7  2001/05/27 09:12:58  miyazawa
 * change lock style to old fashion
 *
 * Revision 1.6  2001/05/26 10:10:45  miyazawa
 * small fix around lock parameters: pfkey_v2_lock -> &pfkey_v2_lock
 *
 * Revision 1.5  2001/05/25 05:54:26  mk
 * use new lock functionary.
 * XXX: i don't know which i use.
 * (lock_sock, spin_lock,...)
 *
 * Revision 1.4  2001/05/24 13:32:36  mk
 * remove extra parts
 *
 * Revision 1.3  2001/05/23 01:53:06  miyazawa
 * add routine concernd with sa_index manipulation
 *
 * Revision 1.2  2001/05/22 11:00:01  miyazawa
 * fixed module initialize in source codes
 *
 * Revision 1.1.1.1  2001/05/22 06:14:05  miyazawa
 * kernel for ipsec without FS
 *
 * Revision 1.55  2001/02/28 05:03:28  rgb
 * Clean up and rationalise startup messages.
 *
 * Revision 1.54  2001/02/27 22:24:55  rgb
 * Re-formatting debug output (line-splitting, joining, 1arg/line).
 * Check for satoa() return codes.
 *
 * Revision 1.53  2001/02/27 06:48:18  rgb
 * Fixed pfkey socket unregister log message to reflect type and function.
 *
 * Revision 1.52  2001/02/26 22:34:38  rgb
 * Fix error return code that was getting overwritten by the error return
 * code of an upmsg.
 *
 * Revision 1.51  2001/01/30 23:42:47  rgb
 * Allow pfkey msgs from pid other than user context required for ACQUIRE
 * and subsequent ADD or UDATE.
 *
 * Revision 1.50  2001/01/23 20:22:59  rgb
 * 2.4 fix to remove removed is_clone member.
 *
 * Revision 1.49  2000/11/06 04:33:47  rgb
 * Changed non-exported functions to DEBUG_NO_STATIC.
 *
 * Revision 1.48  2000/09/29 19:47:41  rgb
 * Update copyright.
 *
 * Revision 1.47  2000/09/22 04:23:04  rgb
 * Added more debugging to pfkey_upmsg() call from pfkey_sendmsg() error.
 *
 * Revision 1.46  2000/09/21 04:20:44  rgb
 * Fixed array size off-by-one error.  (Thanks Svenning!)
 *
 * Revision 1.45  2000/09/20 04:01:26  rgb
 * Changed static functions to DEBUG_NO_STATIC for revealing function names
 * in oopsen.
 *
 * Revision 1.44  2000/09/19 00:33:17  rgb
 * 2.0 fixes.
 *
 * Revision 1.43  2000/09/16 01:28:13  rgb
 * Fixed use of 0 in p format warning.
 *
 * Revision 1.42  2000/09/16 01:09:41  rgb
 * Fixed debug format warning for pointers that was expecting ints.
 *
 * Revision 1.41  2000/09/13 15:54:00  rgb
 * Rewrote pfkey_get_info(), added pfkey_{supported,registered}_get_info().
 * Moved supported algos add and remove to functions.
 *
 * Revision 1.40  2000/09/12 18:49:28  rgb
 * Added IPIP tunnel and IPCOMP register support.
 *
 * Revision 1.39  2000/09/12 03:23:49  rgb
 * Converted #if0 debugs to sysctl.
 * Removed debug_pfkey initialisations that prevented no_debug loading or
 * linking.
 *
 * Revision 1.38  2000/09/09 06:38:02  rgb
 * Return positive errno in pfkey_reply error message.
 *
 * Revision 1.37  2000/09/08 19:19:09  rgb
 * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
 * Clean-up of long-unused crud...
 * Create pfkey error message on on failure.
 * Give pfkey_list_{insert,remove}_{socket,supported}() some error
 * checking.
 *
 * Revision 1.36  2000/09/01 18:49:38  rgb
 * Reap experimental NET_21_ bits.
 * Turned registered sockets list into an array of one list per satype.
 * Remove references to deprecated sklist_{insert,remove}_socket.
 * Removed leaking socket debugging code.
 * Removed duplicate pfkey_insert_socket in pfkey_create.
 * Removed all references to pfkey msg->msg_name, since it is not used for
 * pfkey.
 * Added a supported algorithms array lists, one per satype and registered
 * existing algorithms.
 * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
 * list.
 * Only send pfkey_expire() messages to sockets registered for that satype.
 *
 * Revision 1.35  2000/08/24 17:03:00  rgb
 * Corrected message size error return code for PF_KEYv2.
 * Removed downward error prohibition.
 *
 * Revision 1.34  2000/08/21 16:32:26  rgb
 * Re-formatted for cosmetic consistency and readability.
 *
 * Revision 1.33  2000/08/20 21:38:24  rgb
 * Added a pfkey_reply parameter to pfkey_msg_interp(). (Momchil)
 * Extended the upward message initiation of pfkey_sendmsg(). (Momchil)
 *
 * Revision 1.32  2000/07/28 14:58:31  rgb
 * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
 *
 * Revision 1.31  2000/05/16 03:04:00  rgb
 * Updates for 2.3.99pre8 from MB.
 *
 * Revision 1.30  2000/05/10 19:22:21  rgb
 * Use sklist private functions for 2.3.xx compatibility.
 *
 * Revision 1.29  2000/03/22 16:17:03  rgb
 * Fixed SOCKOPS_WRAPPED macro for SMP (MB).
 *
 * Revision 1.28  2000/02/21 19:30:45  rgb
 * Removed references to pkt_bridged for 2.3.47 compatibility.
 *
 * Revision 1.27  2000/02/14 21:07:00  rgb
 * Fixed /proc/net/pf-key legend spacing.
 *
 * Revision 1.26  2000/01/22 03:46:59  rgb
 * Fixed pfkey error return mechanism so that we are able to free the
 * local copy of the pfkey_msg, plugging a memory leak and silencing
 * the bad object free complaints.
 *
 * Revision 1.25  2000/01/21 06:19:44  rgb
 * Moved pfkey_list_remove_socket() calls to before MOD_USE_DEC_COUNT.
 * Added debugging to pfkey_upmsg.
 *
 * Revision 1.24  2000/01/10 16:38:23  rgb
 * MB fixups for 2.3.x.
 *
 * Revision 1.23  1999/12/09 23:22:16  rgb
 * Added more instrumentation for debugging 2.0 socket
 * selection/reading.
 * Removed erroneous 2.0 wait==NULL check bug in select.
 *
 * Revision 1.22  1999/12/08 20:32:16  rgb
 * Tidied up 2.0.xx support, after major pfkey work, eliminating
 * msg->msg_name twiddling in the process, since it is not defined
 * for PF_KEYv2.
 *
 * Revision 1.21  1999/12/01 22:17:19  rgb
 * Set skb->dev to zero on new skb in case it is a reused skb.
 * Added check for skb_put overflow and freeing to avoid upmsg on error.
 * Added check for wrong pfkey version and freeing to avoid upmsg on
 * error.
 * Shut off content dumping in pfkey_destroy.
 * Added debugging message for size of buffer allocated for upmsg.
 *
 * Revision 1.20  1999/11/27 12:11:00  rgb
 * Minor clean-up, enabling quiet operation of pfkey if desired.
 *
 * Revision 1.19  1999/11/25 19:04:21  rgb
 * Update proc_fs code for pfkey to use dynamic registration.
 *
 * Revision 1.18  1999/11/25 09:07:17  rgb
 * Implemented SENDERR macro for propagating error codes.
 * Fixed error return code bug.
 *
 * Revision 1.17  1999/11/23 23:07:20  rgb
 * Change name of pfkey_msg_parser to pfkey_msg_interp since it no longer
 * parses. (PJO)
 * Sort out pfkey and freeswan headers, putting them in a library path.
 *
 * Revision 1.16  1999/11/20 22:00:22  rgb
 * Moved socketlist type declarations and prototypes for shared use.
 * Renamed reformatted and generically extended for use by other socket
 * lists pfkey_{del,add}_open_socket to pfkey_list_{remove,insert}_socket.
 *
 * Revision 1.15  1999/11/18 04:15:09  rgb
 * Make pfkey_data_ready temporarily available for 2.2.x testing.
 * Clean up pfkey_destroy_socket() debugging statements.
 * Add Peter Onion's code to send messages up to all listening sockets.
 * Changed all occurrences of #include "../../../lib/freeswan.h"
 * to #include <freeswan.h> which works due to -Ilibfreeswan in the
 * klips/net/ipsec/Makefile.
 * Replaced all kernel version macros to shorter, readable form.
 * Added CONFIG_PROC_FS compiler directives in case it is shut off.
 *
 * Revision 1.14  1999/11/17 16:01:00  rgb
 * Make pfkey_data_ready temporarily available for 2.2.x testing.
 * Clean up pfkey_destroy_socket() debugging statements.
 * Add Peter Onion's code to send messages up to all listening sockets.
 * Changed #include "../../../lib/freeswan.h" to #include <freeswan.h>
 * which works due to -Ilibfreeswan in the klips/net/ipsec/Makefile.
 *
 * Revision 1.13  1999/10/27 19:59:51  rgb
 * Removed af_unix comments that are no longer relevant.
 * Added debug prink statements.
 * Added to the /proc output in pfkey_get_info.
 * Made most functions non-static to enable oops tracing.
 * Re-enable skb dequeueing and freeing.
 * Fix skb_alloc() and skb_put() size bug in pfkey_upmsg().
 *
 * Revision 1.12  1999/10/26 17:05:42  rgb
 * Complete re-ordering based on proto_ops structure order.
 * Separated out proto_ops structures for 2.0.x and 2.2.x for clarity.
 * Simplification to use built-in socket ops where possible for 2.2.x.
 * Add shorter macros for compiler directives to visually clean-up.
 * Add lots of sk skb dequeueing debugging statements.
 * Added to the /proc output in pfkey_get_info.
 *
 * Revision 1.11  1999/09/30 02:55:10  rgb
 * Bogus skb detection.
 * Fix incorrect /proc/net/ipsec-eroute printk message.
 *
 * Revision 1.10  1999/09/21 15:22:13  rgb
 * Temporary fix while I figure out the right way to destroy sockets.
 *
 * Revision 1.9  1999/07/08 19:19:44  rgb
 * Fix pointer format warning.
 * Fix missing member error under 2.0.xx kernels.
 *
 * Revision 1.8  1999/06/13 07:24:04  rgb
 * Add more debugging.
 *
 * Revision 1.7  1999/06/10 05:24:17  rgb
 * Clarified compiler directives.
 * Renamed variables to reduce confusion.
 * Used sklist_*_socket() kernel functions to simplify 2.2.x socket support.
 * Added lots of sanity checking.
 *
 * Revision 1.6  1999/06/03 18:59:50  rgb
 * More updates to 2.2.x socket support.  Almost works, oops at end of call.
 *
 * Revision 1.5  1999/05/25 22:44:05  rgb
 * Start fixing 2.2 sockets.
 *
 * Revision 1.4  1999/04/29 15:21:34  rgb
 * Move log to the end of the file.
 * Eliminate min/max redefinition in #include <net/tcp.h>.
 * Correct path for pfkey #includes
 * Standardise an error return method.
 * Add debugging instrumentation.
 * Move message type checking to pfkey_msg_parse().
 * Add check for errno incorrectly set.
 * Add check for valid PID.
 * Add check for reserved illegally set.
 * Add check for message out of bounds.
 *
 * Revision 1.3  1999/04/15 17:58:07  rgb
 * Add RCSID labels.
 *
 * Revision 1.2  1999/04/15 15:37:26  rgb
 * Forward check changes from POST1_00 branch.
 *
 * Revision 1.1.2.2  1999/04/13 20:37:12  rgb
 * Header Title correction.
 *
 * Revision 1.1.2.1  1999/03/26 20:58:55  rgb
 * Add pfkeyv2 support to KLIPS.
 *
 *
 * RFC 2367
 * PF_KEY_v2 Key Management API
 */
