/*
 * RFC2367 PF_KEYv2 Key management API domain socket I/F
 * Copyright (C) 1999, 2000, 2001  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.
 *
 * RCSID $Id: pfkey_v2.c,v 1.60 2001/06/14 19:35:12 rgb Exp $
 */

/*
 *		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.
 */

#define __NO_VERSION__
#include <linux/module.h>
#include <linux/version.h>

#include <linux/config.h>
#include <linux/kernel.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/tcp.h> */
#include <net/af_unix.h>
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
#endif /* CONFIG_PROC_FS */

#include <linux/types.h>
 
#include <freeswan.h>
#ifdef NET_21
#include <asm/uaccess.h>
#include <linux/in6.h>
#endif /* NET_21 */
#include <pfkeyv2.h>
#include <pfkey.h>
#include "radij.h"
#include "ipsec_encap.h"
#include "ipsec_netlink.h"

#ifdef CONFIG_IPSEC_DEBUG
int debug_pfkey = 0;
extern int sysctl_ipsec_debug_verbose;
#endif /* CONFIG_IPSEC_DEBUG */

#define min(a,b)	(((a)<(b))?(a):(b))

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

#ifndef SOCKOPS_WRAPPED
#define SOCKOPS_WRAPPED(name) name
#endif /* SOCKOPS_WRAPPED */

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];

int pfkey_msg_interp(struct sock *, struct sadb_msg *, struct sadb_msg **);

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

	if(!socketp) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_remove_socket: "
			    "NULL socketp handed in, failed.\n");
		return -EINVAL;
	}

	if(!sockets) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_remove_socket: "
			    "NULL sockets list handed in, failed.\n");
		return -EINVAL;
	}

	socket_listp = *sockets;
	prev = NULL;
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_list_remove_socket: "
		    "removing sock=%p\n",
		    socketp);
	
	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);
			
			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) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_insert_socket: "
			    "NULL socketp handed in, failed.\n");
		return -EINVAL;
	}

	if(!sockets) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_insert_socket: "
			    "NULL sockets list handed in, failed.\n");
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_list_insert_socket: "
		    "socketp=%p\n",socketp);
	
	if((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_insert_socket: "
			    "memory allocation error.\n");
		return -ENOMEM;
	}
	
	socket_listp->socketp = socketp;
	socket_listp->next = *sockets;
	*sockets = socket_listp;

	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) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_remove_supported: "
			    "NULL supported handed in, failed.\n");
		return -EINVAL;
	}

	if(!supported_list) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_remove_supported: "
			    "NULL supported_list handed in, failed.\n");
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_list_remove_supported: "
		    "removing supported=%p\n",
		    supported);
	
	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);
			
			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) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_insert_supported: "
			    "NULL supported handed in, failed.\n");
		return -EINVAL;
	}

	if(!supported_list) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_insert_supported: "
			    "NULL supported_list handed in, failed.\n");
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_list_insert_supported: "
		    "incoming, supported=%p, supported_list=%p\n",
		    supported,
		    supported_list);
	
	supported_listp = (struct supported_list *)kmalloc(sizeof(struct supported_list), GFP_KERNEL);
	if(supported_listp == NULL)	{
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_list_insert_supported: "
			    "memory allocation error.\n");
		return -ENOMEM;
	}
	
	supported_listp->supportedp = supported;
	supported_listp->next = *supported_list;
	*supported_list = supported_listp;
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_list_insert_supported: "
		    "outgoing, supported=%p, supported_list=%p\n",
		    supported,
		    supported_list);

	return 0;
}
  
#ifndef NET_21
DEBUG_NO_STATIC void
pfkey_state_change(struct sock *sk)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_state_change: .\n");
	if(!sk->dead) {
		wake_up_interruptible(sk->sleep);
	}
}
#endif /* !NET_21 */

#ifndef NET_21
DEBUG_NO_STATIC void
pfkey_data_ready(struct sock *sk, int len)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_data_ready: "
		    "sk=%p len=%d\n",
		    sk,
		    len);
	if(!sk->dead) {
		wake_up_interruptible(sk->sleep);
		sock_wake_async(sk->socket, 1);
	}
}

DEBUG_NO_STATIC void
pfkey_write_space(struct sock *sk)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_write_space: .\n");
	if(!sk->dead) {
		wake_up_interruptible(sk->sleep);
		sock_wake_async(sk->socket, 2);
	}
}
#endif /* !NET_21 */

DEBUG_NO_STATIC void
pfkey_insert_socket(struct sock *sk)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_insert_socket: "
		    "sk=%p\n",
		    sk);
	cli();
	sk->next=pfkey_sock_list;
	pfkey_sock_list=sk;
	sti();
}

DEBUG_NO_STATIC void
pfkey_remove_socket(struct sock *sk)
{
	struct sock **s;
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_remove_socket: .\n");
	cli();
	s=&pfkey_sock_list;

	while(*s!=NULL) {
		if(*s==sk) {
			*s=sk->next;
			sk->next=NULL;
			sti();
			KLIPS_PRINT(debug_pfkey,
				    "klips_debug:pfkey_remove_socket: "
				    "succeeded.\n");
			return;
		}
		s=&((*s)->next);
	}
	sti();
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_remove_socket: "
		    "not found.\n");
	return;
}

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

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_destroy_socket: .\n");
	pfkey_remove_socket(sk);
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_destroy_socket: "
		    "pfkey_remove_socket called.\n");
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_destroy_socket: "
		    "sk(%p)->(&%p)receive_queue.{next=%p,prev=%p}.\n",
		    sk,
		    &(sk->receive_queue),
		    sk->receive_queue.next,
		    sk->receive_queue.prev);
	while(sk && ((skb=skb_dequeue(&(sk->receive_queue)))!=NULL)) {
#ifdef NET_21
#ifdef CONFIG_IPSEC_DEBUG
		if(debug_pfkey && sysctl_ipsec_debug_verbose) {
			KLIPS_PRINT(debug_pfkey,
				    "klips_debug:pfkey_destroy_socket: "
				    "skb=%p dequeued.\n", skb);
			printk(KERN_INFO "klips_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);
#ifndef NETDEV_23
			printk(" used:%d", skb->used);
			printk(" is_clone:%d", skb->is_clone);
#endif /* NETDEV_23 */
			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 */
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_destroy_socket: "
			    "skb=%p freed.\n",
			    skb);
		kfree_skb(skb);

#else /* NET_21 */
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_destroy_socket: "
			    "skb=%p dequeued and freed.\n",
			    skb);
		kfree_skb(skb, FREE_WRITE);

#endif /* NET_21 */
	}

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

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_destroy_socket: destroyed.\n");
}

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

	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_upmsg: "
			    "NULL socket passed in.\n");
		return -EINVAL;
	}

	if(pfkey_msg == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_upmsg: "
			    "NULL pfkey_msg passed in.\n");
		return -EINVAL;
	}

#ifdef NET_21
	sk = sock->sk;
#else /* NET_21 */
	sk = sock->data;
#endif /* NET_21 */

	if(sk == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_upmsg: "
			    "NULL sock passed in.\n");
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_upmsg: "
		    "allocating %d bytes...\n",
		    pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
	if(!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_upmsg: "
			    "no buffers left to send up a message.\n");
		return -ENOBUFS;
	}
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_upmsg: "
		    "...allocated at %p.\n",
		    skb);
	
	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));
#ifdef NET_21
		kfree_skb(skb);
#else /* NET_21 */
		kfree_skb(skb, FREE_WRITE);
#endif /* NET_21 */
		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);

#ifndef NET_21
	skb->free = 1;
#endif /* !NET_21 */

	if((error = sock_queue_rcv_skb(sk, skb)) < 0) {
		skb->sk=NULL;
#ifdef NET_21
		kfree_skb(skb);
#else /* NET_21 */
		kfree_skb(skb, FREE_WRITE);
#endif /* NET_21 */
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_upmsg: "
			    "error=%d calling sock_queue_rcv_skb with skb=%p.\n",
			    error,
			    skb);
		return error;
	}
	return error;
}

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

	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_create: "
			    "socket NULL.\n");
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_create: "
		    "sock=%p type:%d state:%d flags:%ld protocol:%d\n",
		    sock,
		    sock->type,
		    (unsigned int)(sock->state),
		    sock->flags, protocol);

	if(sock->type != SOCK_RAW) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_create: "
			    "only SOCK_RAW supported.\n");
		return -ESOCKTNOSUPPORT;
	}

	if(protocol != PF_KEY_V2) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_create: "
			    "protocol not PF_KEY_V2.\n");
		return -EPROTONOSUPPORT;
	}

	if((current->uid != 0)) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_create: "
			    "must be root to open pfkey sockets.\n");
		return -EACCES;
	}

#ifdef NET_21
	sock->state = SS_UNCONNECTED;
#endif /* NET_21 */
	MOD_INC_USE_COUNT;
#ifdef NET_21
	if((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1)) == NULL)
#else /* NET_21 */
	if((sk=(struct sock *)sk_alloc(GFP_KERNEL)) == NULL)
#endif /* NET_21 */
	{
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_create: "
			    "Out of memory trying to allocate.\n");
		MOD_DEC_USE_COUNT;
		return -ENOMEM;
	}

#ifndef NET_21
	memset(sk, 0, sizeof(*sk));
#endif /* !NET_21 */

#ifdef NET_21
	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->num = protocol; */
	sk->protocol = protocol;
	key_pid(sk) = current->pid;
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_create: "
		    "sock->fasync_list=%p sk->sleep=%p.\n",
		    sock->fasync_list,
		    sk->sleep);
#else /* NET_21 */
	sk->type=sock->type;
	init_timer(&sk->timer);
	skb_queue_head_init(&sk->write_queue);
	skb_queue_head_init(&sk->receive_queue);
	skb_queue_head_init(&sk->back_log);
	sk->rcvbuf=SK_RMEM_MAX;
	sk->sndbuf=SK_WMEM_MAX;
	sk->allocation=GFP_KERNEL;
	sk->state=TCP_CLOSE;
	sk->priority=SOPRI_NORMAL;
	sk->state_change=pfkey_state_change;
	sk->data_ready=pfkey_data_ready;
	sk->write_space=pfkey_write_space;
	sk->error_report=pfkey_state_change;
	sk->mtu=4096;
	sk->socket=sock;
	sock->data=(void *)sk;
	sk->sleep=sock->wait;
#endif /* NET_21 */

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

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_create: "
		    "Socket sock=%p sk=%p initialised.\n", sock, sk);
	return 0;
}

#ifndef NET_21
DEBUG_NO_STATIC int
pfkey_dup(struct socket *newsock, struct socket *oldsock)
{
	struct sock *sk;

	if(newsock==NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_dup: "
			    "No new socket attached.\n");
		return -EINVAL;
	}
		
	if(oldsock==NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_dup: "
			    "No old socket attached.\n");
		return -EINVAL;
	}
		
#ifdef NET_21
	sk=oldsock->sk;
#else /* NET_21 */
	sk=oldsock->data;
#endif /* NET_21 */
	
	/* May not have data attached */
	if(sk==NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_dup: "
			    "No sock attached to old socket.\n");
		return -EINVAL;
	}
		
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_dup: .\n");

	return pfkey_create(newsock, sk->protocol);
}
#endif /* !NET_21 */

DEBUG_NO_STATIC int
#ifdef NETDEV_23
pfkey_release(struct socket *sock)
#else /* NETDEV_23 */
pfkey_release(struct socket *sock, struct socket *peersock)
#endif /* NETDEV_23 */
{
	struct sock *sk;
	int i;

	if(sock==NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_release: "
			    "No socket attached.\n");
		return 0; /* -EINVAL; */
	}
		
#ifdef NET_21
	sk=sock->sk;
#else /* NET_21 */
	sk=sock->data;
#endif /* NET_21 */
	
	/* May not have data attached */
	if(sk==NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_release: "
			    "No sk attached to sock=%p.\n", sock);
		return 0; /* -EINVAL; */
	}
		
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_release: "
		    "sock=%p sk=%p\n", sock, sk);

#ifdef NET_21
	if(!sk->dead)
#endif /* NET_21 */
		if(sk->state_change) {
			sk->state_change(sk);
		}

#ifdef NET_21
	sock->sk = NULL;
#else /* NET_21 */
	sock->data = NULL;
#endif /* NET_21 */

	/* 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]));
	}

	MOD_DEC_USE_COUNT;
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_release: "
		    "succeeded.\n");

	return 0;
}

#ifndef NET_21
DEBUG_NO_STATIC int
pfkey_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_bind: "
		    "operation not supported.\n");
	return -EINVAL;
}

DEBUG_NO_STATIC int
pfkey_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_connect: "
		    "operation not supported.\n");
	return -EINVAL;
}

DEBUG_NO_STATIC int
pfkey_socketpair(struct socket *a, struct socket *b)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_socketpair: "
		    "operation not supported.\n");
	return -EINVAL;
}

DEBUG_NO_STATIC int
pfkey_accept(struct socket *sock, struct socket *newsock, int flags)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_aaccept: "
		    "operation not supported.\n");
	return -EINVAL;
}

DEBUG_NO_STATIC int
pfkey_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len,
		int peer)
{
	struct sockaddr *ska = (struct sockaddr*)uaddr;
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_getname: .\n");
	ska->sa_family = PF_KEY;
	*uaddr_len = sizeof(*ska);
	return 0;
}

DEBUG_NO_STATIC int
pfkey_select(struct socket *sock, int sel_type, select_table *wait)
{
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_select: "
		    ".sock=%p sk=%p sel_type=%d\n",
		    sock,
		    sock->data,
		    sel_type);
	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_select: "
			    "Null socket passed in.\n");
		return -EINVAL;
	}
	return datagram_select(sock->data, sel_type, wait);
}

DEBUG_NO_STATIC int
pfkey_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_ioctl: "
		    "not supported.\n");
	return -EINVAL;
}

DEBUG_NO_STATIC int
pfkey_listen(struct socket *sock, int backlog)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_listen: "
		    "not supported.\n");
	return -EINVAL;
}
#endif /* !NET_21 */

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

	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_shutdown: "
			    "NULL socket passed in.\n");
		return -EINVAL;
	}

#ifdef NET_21
	sk=sock->sk;
#else /* NET_21 */
	sk=sock->data;
#endif /* NET_21 */
	
	if(sk == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_shutdown: "
			    "No sock attached to socket.\n");
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_shutdown: "
		    "mode=%x.\n", mode);
	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;
}

#ifndef NET_21
DEBUG_NO_STATIC int
pfkey_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
{
#ifndef NET_21
	struct sock *sk;

	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_setsockopt: "
			    "Null socket passed in.\n");
		return -EINVAL;
	}
	
	sk=sock->data;
	
	if(sk == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_setsockopt: "
			    "Null sock passed in.\n");
		return -EINVAL;
	}
#endif /* !NET_21 */
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_setsockopt: .\n");
	if(level!=SOL_SOCKET) {
		return -EOPNOTSUPP;
	}
#ifdef NET_21
	return sock_setsockopt(sock, level, optname, optval, optlen);
#else /* NET_21 */
	return sock_setsockopt(sk, level, optname, optval, optlen);
#endif /* NET_21 */
}

DEBUG_NO_STATIC int
pfkey_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
#ifndef NET_21
	struct sock *sk;

	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_setsockopt: "
			    "Null socket passed in.\n");
		return -EINVAL;
	}
	
	sk=sock->data;
	
	if(sk == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_setsockopt: "
			    "Null sock passed in.\n");
		return -EINVAL;
	}
#endif /* !NET_21 */

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_getsockopt: .\n");
	if(level!=SOL_SOCKET) {
		return -EOPNOTSUPP;
	}
#ifdef NET_21
	return sock_getsockopt(sock, level, optname, optval, optlen);
#else /* NET_21 */
	return sock_getsockopt(sk, level, optname, optval, optlen);
#endif /* NET_21 */
}

DEBUG_NO_STATIC int
pfkey_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_fcntl: "
		    "not supported.\n");
	return -EINVAL;
}
#endif /* !NET_21 */

/*
 *	Send PF_KEY data down.
 */
		
DEBUG_NO_STATIC int
#ifdef NET_21
pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
#else /* NET_21 */
pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
#endif /* NET_21 */
{
	struct sock *sk;
	int error = 0;
	struct sadb_msg *pfkey_msg = NULL, *pfkey_reply = NULL;
	
	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "Null socket passed in.\n");
		SENDERR(EINVAL);
	}
	
#ifdef NET_21
	sk = sock->sk;
#else /* NET_21 */
	sk = sock->data;
#endif /* NET_21 */

	if(sk == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "Null sock passed in.\n");
		SENDERR(EINVAL);
	}
	
	if(msg == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "Null msghdr passed in.\n");
		SENDERR(EINVAL);
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_sendmsg: .\n");
	if(sk->err) {
		error = sock_error(sk);
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "sk->err is non-zero, returns %d.\n",
			    error);
		SENDERR(-error);
	}

	if((current->uid != 0)) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "must be root to send messages to pfkey sockets.\n");
		SENDERR(EACCES);
	}

#ifdef NET_21
	if(msg->msg_control)
#else /* NET_21 */
	if(flags || msg->msg_control)
#endif /* NET_21 */
	{
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "can't set flags or set msg_control.\n");
		SENDERR(EINVAL);
	}
		
	if(sk->shutdown & SEND_SHUTDOWN) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "shutdown.\n");
		send_sig(SIGPIPE, current, 0);
		SENDERR(EPIPE);
	}
	
	if(len < sizeof(struct sadb_msg)) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "bogus msg len of %d, too small.\n", len);
		SENDERR(EMSGSIZE);
	}

	if((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "memory allocation error.\n");
		SENDERR(ENOBUFS);
	}

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

	if(pfkey_msg->sadb_msg_version != PF_KEY_V2) {
		KLIPS_PRINT(1 || debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "not PF_KEY_V2 msg, found %d, should be %d.\n",
			    pfkey_msg->sadb_msg_version,
			    PF_KEY_V2);
		kfree((void*)pfkey_msg);
		return -EINVAL;
	}

	if(len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "bogus msg len of %d, not %d byte aligned.\n",
			    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) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "pid (%d) does not equal sending process pid (%d).\n",
			    pfkey_msg->sadb_msg_pid, current->pid);
		SENDERR(EINVAL);
	}
#endif

	if(pfkey_msg->sadb_msg_reserved) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "reserved field must be zero, set to %d.\n",
			    pfkey_msg->sadb_msg_reserved);
		SENDERR(EINVAL);
	}
	
	if((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)){
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "msg type too large or small:%d.\n",
			    pfkey_msg->sadb_msg_type);
		SENDERR(EINVAL);
	}
	
	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_sendmsg: "
		    "msg sent for parsing.\n");
	
	if((error = pfkey_msg_interp(sk, pfkey_msg, &pfkey_reply))) {
		struct socket_list *pfkey_socketsp;

		KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
			    "pfkey_msg_parse returns %d.\n",
			    error);

		if((pfkey_reply = (struct sadb_msg*)kmalloc(sizeof(struct sadb_msg), GFP_KERNEL)) == NULL) {
			KLIPS_PRINT(debug_pfkey,
				    "klips_debug:pfkey_sendmsg: "
				    "memory allocation error.\n");
			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;
			KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
				    "sending up error=%d message=%p to socket=%p.\n",
				    error,
				    pfkey_reply,
				    pfkey_socketsp->socketp);
			if((error_upmsg = pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply))) {
				KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
					    "sending up error message to socket=%p failed with error=%d.\n",
					    pfkey_socketsp->socketp,
					    error_upmsg);
				pfkey_msg_free(&pfkey_reply);
				SENDERR(-error);
			}
			KLIPS_PRINT(debug_pfkey, "klips_debug:pfkey_sendmsg: "
				    "sending up error message to socket=%p succeeded.\n",
				    pfkey_socketsp->socketp);
		}
		
		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.
 */
		
DEBUG_NO_STATIC int
#ifdef NET_21
pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
#else /* NET_21 */
pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
#endif /* NET_21 */
{
	struct sock *sk;
#ifdef NET_21
	int noblock = flags & MSG_DONTWAIT;
#endif /* NET_21 */
	struct sk_buff *skb;
	int error;

	if(sock == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_recvmsg: "
			    "Null socket passed in.\n");
		return -EINVAL;
	}

#ifdef NET_21
	sk = sock->sk;
#else /* NET_21 */
	sk = sock->data;
#endif /* NET_21 */

	if(sk == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_recvmsg: "
			    "Null sock passed in for sock=%p.\n", sock);
		return -EINVAL;
	}

	if(msg == NULL) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_recvmsg: "
			    "Null msghdr passed in for sock=%p, sk=%p.\n",
			    sock, sk);
		return -EINVAL;
	}

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:pfkey_recvmsg: sock=%p sk=%p msg=%p size=%d.\n",
		    sock, sk, msg, size);
	if(flags & ~MSG_PEEK) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "flags (%d) other than MSG_PEEK not supported.\n",
			    flags);
		return -EOPNOTSUPP;
	}
		
#ifdef NET_21
	msg->msg_namelen = 0; /* sizeof(*ska); */
#else /* NET_21 */
	if(addr_len) {
		*addr_len = 0; /* sizeof(*ska); */
	}
#endif /* NET_21 */
		
	if(sk->err) {
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:pfkey_sendmsg: "
			    "sk->err=%d.\n", 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;
	}
#ifdef NET_21
	else if(size <skb->len) {
		msg->msg_flags |= MSG_TRUNC;
	}
#endif /* NET_21 */

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

	skb_free_datagram(sk, skb);
	return size;
}

#ifdef NET_21
struct net_proto_family pfkey_family_ops = {
	PF_KEY,
	pfkey_create
};

struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = {
#ifdef NETDEV_23
	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,
#else /* NETDEV_23 */
	PF_KEY,
	sock_no_dup,
	pfkey_release,
	sock_no_bind,
	sock_no_connect,
	sock_no_socketpair,
	sock_no_accept,
	sock_no_getname,
	datagram_poll,
	sock_no_ioctl,
	sock_no_listen,
	pfkey_shutdown,
	sock_no_setsockopt,
	sock_no_getsockopt,
	sock_no_fcntl,
	pfkey_sendmsg,
	pfkey_recvmsg
#endif /* NETDEV_23 */
};

#ifdef NETDEV_23
#include <linux/smp_lock.h>
SOCKOPS_WRAP(pfkey, PF_KEY);
#endif /* NETDEV_23 */

#else /* NET_21 */
struct proto_ops pfkey_proto_ops = {
	PF_KEY,
	pfkey_create,
	pfkey_dup,
	pfkey_release,
	pfkey_bind,
	pfkey_connect,
	pfkey_socketpair,
	pfkey_accept,
	pfkey_getname,
	pfkey_select,
	pfkey_ioctl,
	pfkey_listen,
	pfkey_shutdown,
	pfkey_setsockopt,
	pfkey_getsockopt,
	pfkey_fcntl,
	pfkey_sendmsg,
	pfkey_recvmsg
};
#endif /* NET_21 */
   
#ifdef CONFIG_PROC_FS
#ifndef PROC_FS_2325
DEBUG_NO_STATIC
#endif /* PROC_FS_2325 */
int
pfkey_get_info(char *buffer, char **start, off_t offset, int length
#ifndef  PROC_NO_DUMMY
, int dummy
#endif /* !PROC_NO_DUMMY */
)
{
	off_t pos=0;
	off_t begin=0;
	int len=0;
	struct sock *sk=pfkey_sock_list;
	
#ifdef CONFIG_IPSEC_DEBUG
	if(!sysctl_ipsec_debug_verbose) {
#endif /* CONFIG_IPSEC_DEBUG */
	len+= sprintf(buffer,
		      "    sock   pid   socket     next     prev e n p sndbf    Flags     Type St\n");
#ifdef CONFIG_IPSEC_DEBUG
	} else {
	len+= sprintf(buffer,
		      "    sock   pid d    sleep   socket     next     prev e r z n p sndbf    stamp    Flags     Type St\n");
	}
#endif /* CONFIG_IPSEC_DEBUG */
	
	while(sk!=NULL) {
#ifdef CONFIG_IPSEC_DEBUG
		if(!sysctl_ipsec_debug_verbose) {
#endif /* CONFIG_IPSEC_DEBUG */
		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);
#ifdef CONFIG_IPSEC_DEBUG
		} else {
		len+=sprintf(buffer+len,
			     "%8p %5d %d %8p %8p %8p %8p %d %d %d %d %d %5d %d.%06d %08lX %8X %2X\n",
			     sk,
			     key_pid(sk),
			     sk->dead,
			     sk->sleep,
			     sk->socket,
			     sk->next,
			     sk->prev,
			     sk->err,
			     sk->reuse,
			     sk->zapped,
			     sk->num,
			     sk->protocol,
			     sk->sndbuf,
			     (unsigned int)sk->stamp.tv_sec,
			     (unsigned int)sk->stamp.tv_usec,
			     sk->socket->flags,
			     sk->socket->type,
			     sk->socket->state);
		}
#endif /* CONFIG_IPSEC_DEBUG */
		
		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;
}

#ifndef PROC_FS_2325
DEBUG_NO_STATIC
#endif /* PROC_FS_2325 */
int
pfkey_supported_get_info(char *buffer, char **start, off_t offset, int length
#ifndef  PROC_NO_DUMMY
, int dummy
#endif /* !PROC_NO_DUMMY */
)
{
	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;
}

#ifndef PROC_FS_2325
DEBUG_NO_STATIC
#endif /* PROC_FS_2325 */
int
pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length
#ifndef  PROC_NO_DUMMY
, int dummy
#endif /* !PROC_NO_DUMMY */
)
{
	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) {
#ifdef NET_21
			len+=sprintf(buffer+len,
				     "    %2d %8p %5d %8p\n",
				     satype,
				     pfkey_sockets->socketp,
				     key_pid(pfkey_sockets->socketp->sk),
				     pfkey_sockets->socketp->sk);
#else /* NET_21 */
			len+=sprintf(buffer+len,
				     "    %2d %8p   N/A %8p\n",
				     satype,
				     pfkey_sockets->socketp,
#if 0
				     key_pid((pfkey_sockets->socketp)->data),
#endif
				     (pfkey_sockets->socketp)->data);
#endif /* NET_21 */
			
			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;
}

#ifndef PROC_FS_2325
struct proc_dir_entry proc_net_pfkey =
{
	0,
	6, "pf_key",
	S_IFREG | S_IRUGO, 1, 0, 0,
	0, &proc_net_inode_operations,
	pfkey_get_info
};
struct proc_dir_entry proc_net_pfkey_supported =
{
	0,
	16, "pf_key_supported",
	S_IFREG | S_IRUGO, 1, 0, 0,
	0, &proc_net_inode_operations,
	pfkey_supported_get_info
};
struct proc_dir_entry proc_net_pfkey_registered =
{
	0,
	17, "pf_key_registered",
	S_IFREG | S_IRUGO, 1, 0, 0,
	0, &proc_net_inode_operations,
	pfkey_registered_get_info
};
#endif /* !PROC_FS_2325 */
#endif /* CONFIG_PROC_FS */

DEBUG_NO_STATIC int
supported_add_all(int satype, struct supported supported[], int size)
{
	int i;
	int error = 0;

	KLIPS_PRINT(debug_pfkey,
		    "klips_debug:init_pfkey: "
		    "sizeof(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++) {
		
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:init_pfkey: "
			    "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;
}

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

	while(pfkey_supported_list[satype]) {
		supportedp = pfkey_supported_list[satype]->supportedp;
		KLIPS_PRINT(debug_pfkey,
			    "klips_debug:init_pfkey: "
			    "removing satype=%d exttype=%d id=%d ivlen=%d minbits=%d maxbits=%d.\n",
			    satype,
			    supportedp->supported_alg_exttype,
			    supportedp->supported_alg_id,
			    supportedp->supported_alg_ivlen,
			    supportedp->supported_alg_minbits,
			    supportedp->supported_alg_maxbits);
			    
		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_3DESCBC, 128, 168, 168}
	};
	static struct supported supported_init_ipip[] = {
		{SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv4, 0, 32, 32}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
		, {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv4, 0, 128, 32}
		, {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv4_in_IPv6, 0, 32, 128}
		, {SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_TALG_IPv6_in_IPv6, 0, 128, 128}
#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
	};
#ifdef CONFIG_IPSEC_IPCOMP
	static struct supported supported_init_ipcomp[] = {
		{SADB_EXT_SUPPORTED_ENCRYPT, SADB_X_CALG_DEFLATE, 0, 1, 1}
	};
#endif /* CONFIG_IPSEC_IPCOMP */

#if 0
        printk(KERN_INFO
	       "klips_info:pfkey_init: "
	       "FreeS/WAN: initialising PF_KEYv2 domain sockets.\n");
#endif

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

	error |= supported_add_all(SADB_SATYPE_AH, supported_init_ah, sizeof(supported_init_ah));
	error |= supported_add_all(SADB_SATYPE_ESP, supported_init_esp, sizeof(supported_init_esp));
#ifdef CONFIG_IPSEC_IPCOMP
	error |= supported_add_all(SADB_X_SATYPE_COMP, supported_init_ipcomp, sizeof(supported_init_ipcomp));
#endif /* CONFIG_IPSEC_IPCOMP */
	error |= supported_add_all(SADB_X_SATYPE_IPIP, supported_init_ipip, sizeof(supported_init_ipip));

#ifdef NET_21
        error |= sock_register(&pfkey_family_ops);
#else /* NET_21 */
        error |= sock_register(pfkey_proto_ops.family, &pfkey_proto_ops);
#endif /* NET_21 */

#ifdef CONFIG_PROC_FS
#  ifndef PROC_FS_2325
#    ifdef PROC_FS_21
	error |= proc_register(proc_net, &proc_net_pfkey);
	error |= proc_register(proc_net, &proc_net_pfkey_supported);
	error |= proc_register(proc_net, &proc_net_pfkey_registered);
#    else /* PROC_FS_21 */
	error |= proc_register_dynamic(&proc_net, &proc_net_pfkey);
	error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_supported);
	error |= proc_register_dynamic(&proc_net, &proc_net_pfkey_registered);
#    endif /* PROC_FS_21 */
#  else /* !PROC_FS_2325 */
	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 /* !PROC_FS_2325 */
#endif /* CONFIG_PROC_FS */

	return error;
}

int
pfkey_cleanup(void)
{
	int error = 0;
	
        printk(KERN_INFO "klips_info:pfkey_cleanup: "
	       "shutting down PF_KEY domain sockets.\n");
#ifdef NET_21
        error |= sock_unregister(PF_KEY);
#else /* NET_21 */
        error |= sock_unregister(pfkey_proto_ops.family);
#endif /* NET_21 */

	error |= supported_remove_all(SADB_SATYPE_AH);
	error |= supported_remove_all(SADB_SATYPE_ESP);
#ifdef CONFIG_IPSEC_IPCOMP
	error |= supported_remove_all(SADB_X_SATYPE_COMP);
#endif /* CONFIG_IPSEC_IPCOMP */
	error |= supported_remove_all(SADB_X_SATYPE_IPIP);

#ifdef CONFIG_PROC_FS
#  ifndef PROC_FS_2325
	if (proc_net_unregister(proc_net_pfkey.low_ino) != 0)
		printk("klips_debug:pfkey_cleanup: "
		       "cannot unregister /proc/net/pf_key\n");
	if (proc_net_unregister(proc_net_pfkey_supported.low_ino) != 0)
		printk("klips_debug:pfkey_cleanup: "
		       "cannot unregister /proc/net/pf_key_supported\n");
	if (proc_net_unregister(proc_net_pfkey_registered.low_ino) != 0)
		printk("klips_debug:pfkey_cleanup: "
		       "cannot unregister /proc/net/pf_key_registered\n");
#  else /* !PROC_FS_2325 */
	proc_net_remove ("pf_key");
	proc_net_remove ("pf_key_supported");
	proc_net_remove ("pf_key_registered");
#  endif /* !PROC_FS_2325 */
#endif /* CONFIG_PROC_FS */

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

#ifdef MODULE
#if 0
int
init_module(void)
{
	pfkey_init();
	return 0;
}

void
cleanup_module(void)
{
	pfkey_cleanup();
}
#endif /* 0 */
#else /* MODULE */
void
pfkey_proto_init(struct net_proto *pro)
{
	pfkey_init();
}
#endif /* MODULE */

/*
 * $Log: pfkey_v2.c,v $
 * Revision 1.60  2001/06/14 19:35:12  rgb
 * Update copyright date.
 *
 * Revision 1.59  2001/06/13 15:35:48  rgb
 * Fixed #endif comments.
 *
 * Revision 1.58  2001/05/04 16:37:24  rgb
 * Remove erroneous checking of return codes for proc_net_* in 2.4.
 *
 * Revision 1.57  2001/05/03 19:43:36  rgb
 * Initialise error return variable.
 * Check error return codes in startup and shutdown.
 * Standardise on SENDERR() macro.
 *
 * Revision 1.56  2001/04/21 23:05:07  rgb
 * Define out skb->used for 2.4 kernels.
 *
 * 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
 */
