/*
 * Common routines for IPsec SA maintenance routines.
 *
 * Copyright (C) 1996, 1997  John Ioannidis.
 * Copyright (C) 1998, 1999, 2000, 2001, 2002  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: ipsec_sa.c,v 1.18 2002/10/12 23:11:53 dhr Exp $
 *
 * This is the file formerly known as "ipsec_xform.h"
 *
 */

#include <linux/config.h>
#include <linux/version.h>
#include <linux/kernel.h> /* printk() */

#include "freeswan/ipsec_param.h"

#ifdef MALLOC_SLAB
# include <linux/slab.h> /* kmalloc() */
#else /* MALLOC_SLAB */
# include <linux/malloc.h> /* kmalloc() */
#endif /* MALLOC_SLAB */
#include <linux/vmalloc.h> /* vmalloc() */
#include <linux/errno.h>  /* error codes */
#include <linux/types.h>  /* size_t */
#include <linux/interrupt.h> /* mark_bh */

#include <linux/netdevice.h>   /* struct device, and other headers */
#include <linux/etherdevice.h> /* eth_type_trans */
#include <linux/ip.h>          /* struct iphdr */
#include <linux/skbuff.h>
#include <freeswan.h>
#ifdef SPINLOCK
#ifdef SPINLOCK_23
#include <linux/spinlock.h> /* *lock* */
#else /* SPINLOCK_23 */
#include <asm/spinlock.h> /* *lock* */
#endif /* SPINLOCK_23 */
#endif /* SPINLOCK */
#ifdef NET_21
#include <asm/uaccess.h>
#include <linux/in6.h>
#endif
#include <asm/checksum.h>
#include <net/ip.h>

#include "freeswan/radij.h"

#include "freeswan/ipsec_stats.h"
#include "freeswan/ipsec_life.h"
#include "freeswan/ipsec_sa.h"
#include "freeswan/ipsec_xform.h"

#include "freeswan/ipsec_encap.h"
#include "freeswan/ipsec_radij.h"
#include "freeswan/ipsec_netlink.h"
#include "freeswan/ipsec_xform.h"
#include "freeswan/ipsec_ipe4.h"
#include "freeswan/ipsec_ah.h"
#include "freeswan/ipsec_esp.h"

#include <pfkeyv2.h>
#include <pfkey.h>

#include "freeswan/ipsec_proto.h"


#ifdef CONFIG_IPSEC_DEBUG
int debug_xform = 0;
#endif /* CONFIG_IPSEC_DEBUG */

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

struct ipsec_sa *ipsec_sadb_hash[SADB_HASHMOD];
#ifdef SPINLOCK
spinlock_t tdb_lock = SPIN_LOCK_UNLOCKED;
#else /* SPINLOCK */
spinlock_t tdb_lock;
#endif /* SPINLOCK */

struct ipsec_sadb ipsec_sadb;

#if IPSEC_SA_REF_CODE

/* the sub table must be narrower (or equal) in bits than the variable type
   in the main table to count the number of unused entries in it. */
typedef struct {
	int testSizeOf_refSubTable :
		((sizeof(IPsecRefTableUnusedCount) * 8) < IPSEC_SA_REF_SUBTABLE_WIDTH ? -1 : 1);
} dummy;


/* The field where the saref will be hosted in the skb must be wide enough to
   accomodate the information it needs to store. */
typedef struct {
	int testSizeOf_refField : 
		(IPSEC_SA_REF_HOST_FIELD_WIDTH < IPSEC_SA_REF_TABLE_MAX_WIDTH ? -1 : 1 );
} dummy2;


void
ipsec_SAtest(void)
{
	IPsecSAref_t SAref = 258;
	struct ipsec_sa ips;
	ips.ips_ref = 772;

	printk("klips_debug:ipsec_SAtest: "
	       "IPSEC_SA_REF_SUBTABLE_WIDTH=%u\n"
	       "IPSEC_SA_REF_MAINTABLE_SIZE=%u\n"
	       "IPSEC_SA_REF_SUBTABLE_SIZE=%u\n"
	       "IPSEC_SA_REF_HOST_FIELD_WIDTH=%lu\n"
	       "IPSEC_SA_REF_TABLE_MASK=%x\n"
	       "IPSEC_SA_REF_ENTRY_MASK=%x\n"
	       "IPsecSAref2table(%d)=%u\n"
	       "IPsecSAref2entry(%d)=%u\n"
	       "IPsecSAref2NFmark(%d)=%u\n"
	       "IPsecSAref2SA(%d)=%p\n"
	       "IPsecSA2SAref(%p)=%d\n"
	       ,
	       IPSEC_SA_REF_SUBTABLE_WIDTH,
	       IPSEC_SA_REF_MAINTABLE_SIZE,
	       IPSEC_SA_REF_SUBTABLE_SIZE,
	       (unsigned long) IPSEC_SA_REF_HOST_FIELD_WIDTH,
	       IPSEC_SA_REF_TABLE_MASK,
	       IPSEC_SA_REF_ENTRY_MASK,
	       SAref, IPsecSAref2table(SAref),
	       SAref, IPsecSAref2entry(SAref),
	       SAref, IPsecSAref2NFmark(SAref),
	       SAref, IPsecSAref2SA(SAref),
	       (&ips), IPsecSA2SAref((&ips))
		);
	return;
}

int
ipsec_SAref_recycle(void)
{
	int table;
	int entry;
	int error = 0;

	ipsec_sadb.refFreeListHead = -1;
	ipsec_sadb.refFreeListTail = -1;

	if(ipsec_sadb.refFreeListCont == IPSEC_SA_REF_MAINTABLE_SIZE * IPSEC_SA_REF_SUBTABLE_SIZE) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_SAref_recycle: "
			    "end of table reached, continuing at start..\n");
		ipsec_sadb.refFreeListCont = 0;
	}

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_SAref_recycle: "
		    "recycling, continuing from SAref=%d (0p%p), table=%d, entry=%d.\n",
		    ipsec_sadb.refFreeListCont,
		    (ipsec_sadb.refTable[IPsecSAref2table(ipsec_sadb.refFreeListCont)] != NULL) ? IPsecSAref2SA(ipsec_sadb.refFreeListCont) : NULL,
		    IPsecSAref2table(ipsec_sadb.refFreeListCont),
		    IPsecSAref2entry(ipsec_sadb.refFreeListCont));

	for(table = IPsecSAref2table(ipsec_sadb.refFreeListCont);
	    table < IPSEC_SA_REF_MAINTABLE_SIZE;
	    table++) {
		if(ipsec_sadb.refTable[table] == NULL) {
			error = ipsec_SArefSubTable_alloc(table);
			if(error) {
				return -ENOSPC;
			}
		}
		for(entry = IPsecSAref2entry(ipsec_sadb.refFreeListCont);
		    entry < IPSEC_SA_REF_SUBTABLE_SIZE;
		    entry++) {
			if(ipsec_sadb.refTable[table]->entry[entry] == NULL) {
				ipsec_sadb.refFreeList[++ipsec_sadb.refFreeListTail] = IPsecSArefBuild(table, entry);
				if(ipsec_sadb.refFreeListTail == (IPSEC_SA_REF_FREELIST_SIZE - 1)) {
					ipsec_sadb.refFreeListHead = 0;
					ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1;
					KLIPS_PRINT(debug_xform,
						    "klips_debug:ipsec_SAref_recycle: "
						    "SArefFreeList refilled.\n");
					return 0;
				}
			}
		}
	}

	if(ipsec_sadb.refFreeListTail == -1) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_SAref_recycle: "
			    "out of room in the SArefTable.\n");

		return(-ENOSPC);
	}

	ipsec_sadb.refFreeListHead = 0;
	ipsec_sadb.refFreeListCont = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListTail] + 1;
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_SAref_recycle: "
		    "SArefFreeList partly refilled to %d of %d.\n",
		    ipsec_sadb.refFreeListTail,
		    IPSEC_SA_REF_FREELIST_SIZE);
	return 0;
}

int
ipsec_SArefSubTable_alloc(unsigned table)
{
	unsigned entry;
	struct IPsecSArefSubTable* SArefsub;

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_SArefSubTable_alloc: "
		    "allocating %lu bytes for table %u of %u.\n",
		    (unsigned long) (IPSEC_SA_REF_SUBTABLE_SIZE * sizeof(struct ipsec_sa *)),
		    table,
		    IPSEC_SA_REF_MAINTABLE_SIZE);

	/* allocate another sub-table */
	SArefsub = vmalloc(IPSEC_SA_REF_SUBTABLE_SIZE * sizeof(struct ipsec_sa *));
	if(SArefsub == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_SArefSubTable_alloc: "
			    "error allocating memory for table %u of %u!\n",
			    table,
			    IPSEC_SA_REF_MAINTABLE_SIZE);
		return -ENOMEM;
	}

	/* add this sub-table to the main table */
	ipsec_sadb.refTable[table] = SArefsub;

	/* initialise each element to NULL */
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_SArefSubTable_alloc: "
		    "initialising %u elements (2 ^ %u) of table %u.\n",
		    IPSEC_SA_REF_SUBTABLE_SIZE,
		    IPSEC_SA_REF_SUBTABLE_WIDTH,
		    table);
	for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_SIZE; entry++) {
		SArefsub->entry[entry] = NULL;
	}

	return 0;
}
#endif /* IPSEC_SA_REF_CODE */

int
ipsec_saref_freelist_init(void)
{
	int i;

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_saref_freelist_init: "
		    "initialising %u elements of FreeList.\n",
		    IPSEC_SA_REF_FREELIST_SIZE);

	for(i = 0; i < IPSEC_SA_REF_FREELIST_SIZE; i++) {
		ipsec_sadb.refFreeList[i] = -1;
	}
	ipsec_sadb.refFreeListHead = -1;
	ipsec_sadb.refFreeListCont = 0;
	ipsec_sadb.refFreeListTail = -1;
       
	return 0;
}

int
ipsec_sadb_init(void)
{
	int error = 0;
	unsigned i;

	for(i = 1; i < SADB_HASHMOD; i++) {
		ipsec_sadb_hash[i] = NULL;
	}
	/* parts above are for the old style SADB hash table */
	

#if IPSEC_SA_REF_CODE
	/* initialise SA reference table */

	/* initialise the main table */
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sadb_init: "
		    "initialising main table of size %u (2 ^ %u).\n",
		    IPSEC_SA_REF_MAINTABLE_SIZE,
		    IPSEC_SA_REF_MAINTABLE_WIDTH);
	{
		unsigned table;
		for(table = 0; table < IPSEC_SA_REF_MAINTABLE_SIZE; table++) {
			ipsec_sadb.refTable[table] = NULL;
		}
	}

	/* allocate the first sub-table */
	error = ipsec_SArefSubTable_alloc(0);
	if(error) {
		return error;
	}

	error = ipsec_saref_freelist_init();
#endif /* IPSEC_SA_REF_CODE */
	return error;
}

#if IPSEC_SA_REF_CODE
IPsecSAref_t
ipsec_SAref_alloc(void)
{
	int error = 0;
	IPsecSAref_t SAref;

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_SAref_alloc: "
		    "SAref requested... head=%d, cont=%d, tail=%d, listsize=%d.\n",
		    ipsec_sadb.refFreeListHead,
		    ipsec_sadb.refFreeListCont,
		    ipsec_sadb.refFreeListTail,
		    IPSEC_SA_REF_FREELIST_SIZE);

	if(ipsec_sadb.refFreeListHead == -1) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_SAref_alloc: "
			    "FreeList empty, recycling...\n");
		error = ipsec_SAref_recycle();
		if(error) {
			return -1;
		}
	}

	SAref = ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead];
	if(SAref == -1) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_SAref_alloc: "
			    "unexpected error, refFreeListHead = %d points to invalid entry.\n",
			    ipsec_sadb.refFreeListHead);
			return -1;
	}

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_SAref_alloc: "
		    "allocating SAref=%d, table=%u, entry=%u of %u.\n",
		    SAref,
		    IPsecSAref2table(SAref),
		    IPsecSAref2entry(SAref),
		    IPSEC_SA_REF_MAINTABLE_SIZE * IPSEC_SA_REF_SUBTABLE_SIZE);
	
	ipsec_sadb.refFreeList[ipsec_sadb.refFreeListHead] = -1;
	ipsec_sadb.refFreeListHead++;
	if(ipsec_sadb.refFreeListHead > ipsec_sadb.refFreeListTail) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_SAref_alloc: "
			    "last FreeList entry allocated, resetting list head to empty.\n");
		ipsec_sadb.refFreeListHead = -1;
	}

	return SAref;
}
#endif /* IPSEC_SA_REF_CODE */

int
ipsec_sa_print(struct ipsec_sa *ips)
{
        char sa[SATOA_BUF];
	size_t sa_len;

	printk(KERN_INFO "klips_debug:   SA:");
	if(ips == NULL) {
		printk("NULL\n");
		return -ENOENT;
	}
	printk(" ref=%d", ips->ips_ref);
	printk(" refcount=%d", atomic_read(&ips->ips_refcount));
	if(ips->ips_hnext != NULL) {
		printk(" hnext=0p%p", ips->ips_hnext);
	}
	if(ips->ips_inext != NULL) {
		printk(" inext=0p%p", ips->ips_inext);
	}
	if(ips->ips_onext != NULL) {
		printk(" onext=0p%p", ips->ips_onext);
	}
	sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
	printk(" said=%s", sa_len ? sa : " (error)");
	if(ips->ips_seq) {
		printk(" seq=%u", ips->ips_seq);
	}
	if(ips->ips_pid) {
		printk(" pid=%u", ips->ips_pid);
	}
	if(ips->ips_authalg) {
		printk(" authalg=%u", ips->ips_authalg);
	}
	if(ips->ips_encalg) {
		printk(" encalg=%u", ips->ips_encalg);
	}
	printk(" XFORM=%s%s%s", IPS_XFORM_NAME(ips));
	if(ips->ips_replaywin) {
		printk(" ooowin=%u", ips->ips_replaywin);
	}
	if(ips->ips_flags) {
		printk(" flags=%u", ips->ips_flags);
	}
	if(ips->ips_addr_s) {
		char buf[SUBNETTOA_BUF];
		addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr,
			0, buf, sizeof(buf));
		printk(" src=%s", buf);
	}
	if(ips->ips_addr_d) {
		char buf[SUBNETTOA_BUF];
		addrtoa(((struct sockaddr_in*)(ips->ips_addr_s))->sin_addr,
			0, buf, sizeof(buf));
		printk(" dst=%s", buf);
	}
	if(ips->ips_addr_p) {
		char buf[SUBNETTOA_BUF];
		addrtoa(((struct sockaddr_in*)(ips->ips_addr_p))->sin_addr,
			0, buf, sizeof(buf));
		printk(" proxy=%s", buf);
	}
	if(ips->ips_key_bits_a) {
		printk(" key_bits_a=%u", ips->ips_key_bits_a);
	}
	if(ips->ips_key_bits_e) {
		printk(" key_bits_e=%u", ips->ips_key_bits_e);
	}

	printk("\n");
	return 0;
}

struct ipsec_sa*
ipsec_sa_alloc(void)
{
	struct ipsec_sa* ips;

	if((ips = kmalloc(sizeof(*ips), GFP_ATOMIC) ) == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_alloc: "
			    "memory allocation error\n");
		return NULL;
	}
	memset((caddr_t)ips, 0, sizeof(*ips));
#if IPSEC_SA_REF_CODE
	ips->ips_ref = ipsec_SAref_alloc();
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_alloc: "
		    "allocated %lu bytes for ipsec_sa struct=0p%p ref=%d.\n",
		    (unsigned long) sizeof(*ips),
		    ips,
		    ips->ips_ref);
	if(ips->ips_ref == -1) {
		kfree(ips);
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_alloc: "
			    "SAref allocation error\n");
		return NULL;
	}

	atomic_inc(&ips->ips_refcount);
	IPsecSAref2SA(ips->ips_ref) = ips;
#endif /* IPSEC_SA_REF_CODE */

	return(ips);
}

int
ipsec_sa_free(struct ipsec_sa* ips)
{
	return ipsec_sa_wipe(ips);
}

struct ipsec_sa *
ipsec_sa_getbyid(struct sa_id *said)
{
	int hashval;
	struct ipsec_sa *ips;
        char sa[SATOA_BUF];
	size_t sa_len;

	if(said == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_error:ipsec_sa_getbyid: "
			    "null pointer passed in!\n");
		return NULL;
	}

	sa_len = satoa(*said, 0, sa, SATOA_BUF);

	hashval = (said->spi+said->dst.s_addr+said->proto) % SADB_HASHMOD;
	
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_getbyid: "
		    "linked entry in ipsec_sa table for hash=%d of SA:%s requested.\n",
		    hashval,
		    sa_len ? sa : " (error)");

	if((ips = ipsec_sadb_hash[hashval]) == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_getbyid: "
			    "no entries in ipsec_sa table for hash=%d of SA:%s.\n",
			    hashval,
			    sa_len ? sa : " (error)");
		return NULL;
	}

	for (; ips; ips = ips->ips_hnext) {
		if ((ips->ips_said.spi == said->spi) &&
		    (ips->ips_said.dst.s_addr == said->dst.s_addr) &&
		    (ips->ips_said.proto == said->proto)) {
			atomic_inc(&ips->ips_refcount);
			return ips;
		}
	}
	
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_getbyid: "
		    "no entry in linked list for hash=%d of SA:%s.\n",
		    hashval,
		    sa_len ? sa : " (error)");
	return NULL;
}

int
ipsec_sa_put(struct ipsec_sa *ips)
{
        char sa[SATOA_BUF];
	size_t sa_len;

	if(ips == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_error:ipsec_sa_put: "
			    "null pointer passed in!\n");
		return -1;
	}

	sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_put: "
		    "ipsec_sa SA:%s, ref:%d reference count decremented.\n",
		    sa_len ? sa : " (error)",
		    ips->ips_ref);

	atomic_dec(&ips->ips_refcount);

	return 0;
}

/*
  The ipsec_sa table better *NOT* be locked before it is handed in, or SMP locks will happen
*/
int
ipsec_sa_add(struct ipsec_sa *ips)
{
	int error = 0;
	unsigned int hashval;

	if(ips == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_error:ipsec_sa_add: "
			    "null pointer passed in!\n");
		return -ENODATA;
	}
	hashval = ((ips->ips_said.spi + ips->ips_said.dst.s_addr + ips->ips_said.proto) % SADB_HASHMOD);

	atomic_inc(&ips->ips_refcount);
	spin_lock_bh(&tdb_lock);
	
	ips->ips_hnext = ipsec_sadb_hash[hashval];
	ipsec_sadb_hash[hashval] = ips;
	
	spin_unlock_bh(&tdb_lock);

	return error;
}

/*
  The ipsec_sa table better be locked before it is handed in, or races might happen
*/
int
ipsec_sa_del(struct ipsec_sa *ips)
{
	unsigned int hashval;
	struct ipsec_sa *ipstp;
        char sa[SATOA_BUF];
	size_t sa_len;

	if(ips == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_error:ipsec_sa_del: "
			    "null pointer passed in!\n");
		return -ENODATA;
	}
	
	sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
	if(ips->ips_inext || ips->ips_onext) {
		KLIPS_PRINT(debug_xform,
			    "klips_error:ipsec_sa_del: "
			    "SA:%s still linked!\n",
			    sa_len ? sa : " (error)");
		return -EMLINK;
	}
	
	hashval = ((ips->ips_said.spi + ips->ips_said.dst.s_addr + ips->ips_said.proto) % SADB_HASHMOD);
	
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_del: "
		    "deleting SA:%s, hashval=%d.\n",
		    sa_len ? sa : " (error)",
		    hashval);
	if(ipsec_sadb_hash[hashval] == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_del: "
			    "no entries in ipsec_sa table for hash=%d of SA:%s.\n",
			    hashval,
			    sa_len ? sa : " (error)");
		return -ENOENT;
	}
	
	if (ips == ipsec_sadb_hash[hashval]) {
		ipsec_sadb_hash[hashval] = ipsec_sadb_hash[hashval]->ips_hnext;
		ips->ips_hnext = NULL;
		atomic_dec(&ips->ips_refcount);
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_del: "
			    "successfully deleted first ipsec_sa in chain.\n");
		return 0;
	} else {
		for (ipstp = ipsec_sadb_hash[hashval];
		     ipstp;
		     ipstp = ipstp->ips_hnext) {
			if (ipstp->ips_hnext == ips) {
				ipstp->ips_hnext = ips->ips_hnext;
				ips->ips_hnext = NULL;
				atomic_dec(&ips->ips_refcount);
				KLIPS_PRINT(debug_xform,
					    "klips_debug:ipsec_sa_del: "
					    "successfully deleted link in ipsec_sa chain.\n");
				return 0;
			}
		}
	}
	
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_del: "
		    "no entries in linked list for hash=%d of SA:%s.\n",
		    hashval,
		    sa_len ? sa : " (error)");
	return -ENOENT;
}

/*
  The ipsec_sa table better be locked before it is handed in, or races
  might happen
*/
int
ipsec_sa_delchain(struct ipsec_sa *ips)
{
	struct ipsec_sa *ipsdel;
	int error = 0;
        char sa[SATOA_BUF];
	size_t sa_len;

	if(ips == NULL) {
		KLIPS_PRINT(debug_xform,
			    "klips_error:ipsec_sa_delchain: "
			    "null pointer passed in!\n");
		return -ENODATA;
	}

	sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sa_delchain: "
		    "passed SA:%s\n",
		    sa_len ? sa : " (error)");
	while(ips->ips_onext != NULL) {
		ips = ips->ips_onext;
	}

	while(ips) {
		/* XXX send a pfkey message up to advise of deleted ipsec_sa */
		sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_delchain: "
			    "unlinking and delting SA:%s",
			    sa_len ? sa : " (error)");
		ipsdel = ips;
		ips = ips->ips_inext;
		if(ips != NULL) {
			sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
			KLIPS_PRINT(debug_xform,
				    ", inext=%s",
				    sa_len ? sa : " (error)");
			atomic_dec(&ipsdel->ips_refcount);
			ipsdel->ips_inext = NULL;
			atomic_dec(&ips->ips_refcount);
			ips->ips_onext = NULL;
		}
		KLIPS_PRINT(debug_xform,
			    ".\n");
		if((error = ipsec_sa_del(ipsdel))) {
			KLIPS_PRINT(debug_xform,
				    "klips_debug:ipsec_sa_delchain: "
				    "ipsec_sa_del returned error %d.\n", -error);
			return error;
		}
		if((error = ipsec_sa_wipe(ipsdel))) {
			KLIPS_PRINT(debug_xform,
				    "klips_debug:ipsec_sa_delchain: "
				    "ipsec_sa_wipe returned error %d.\n", -error);
			return error;
		}
	}
	return error;
}

int 
ipsec_sadb_cleanup(__u8 proto)
{
	unsigned i;
	int error = 0;
	struct ipsec_sa *ips, **ipsprev, *ipsdel;
        char sa[SATOA_BUF];
	size_t sa_len;

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sadb_cleanup: "
		    "cleaning up proto=%d.\n",
		    proto);

	spin_lock_bh(&tdb_lock);

	for (i = 0; i < SADB_HASHMOD; i++) {
		ipsprev = &(ipsec_sadb_hash[i]);
		ips = ipsec_sadb_hash[i];
		if(ips != NULL) {
			atomic_inc(&ips->ips_refcount);
		}
		for(; ips != NULL;) {
			sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
			KLIPS_PRINT(debug_xform,
				    "klips_debug:ipsec_sadb_cleanup: "
				    "checking SA:%s, hash=%d, ref=%d",
				    sa_len ? sa : " (error)",
				    i,
				    ips->ips_ref);
			ipsdel = ips;
			ips = ipsdel->ips_hnext;
			if(ips != NULL) {
				atomic_inc(&ips->ips_refcount);
				sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
				KLIPS_PRINT(debug_xform,
					    ", hnext=%s",
					    sa_len ? sa : " (error)");
			}
			if(*ipsprev != NULL) {
				sa_len = satoa((*ipsprev)->ips_said, 0, sa, SATOA_BUF);
				KLIPS_PRINT(debug_xform,
					    ", *ipsprev=%s",
					    sa_len ? sa : " (error)");
				if((*ipsprev)->ips_hnext) {
					sa_len = satoa((*ipsprev)->ips_hnext->ips_said, 0, sa, SATOA_BUF);
					KLIPS_PRINT(debug_xform,
						    ", *ipsprev->ips_hnext=%s",
						    sa_len ? sa : " (error)");
				}
			}
			KLIPS_PRINT(debug_xform,
				    ".\n");
			if(proto == 0 || (proto == ipsdel->ips_said.proto)) {
				sa_len = satoa(ipsdel->ips_said, 0, sa, SATOA_BUF);
				KLIPS_PRINT(debug_xform,
					    "klips_debug:ipsec_sadb_cleanup: "
					    "deleting SA chain:%s.\n",
					    sa_len ? sa : " (error)");
				if((error = ipsec_sa_delchain(ipsdel))) {
					SENDERR(-error);
				}
				ipsprev = &(ipsec_sadb_hash[i]);
				ips = ipsec_sadb_hash[i];

				KLIPS_PRINT(debug_xform,
					    "klips_debug:ipsec_sadb_cleanup: "
					    "deleted SA chain:%s",
					    sa_len ? sa : " (error)");
				if(ips != NULL) {
					sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
					KLIPS_PRINT(debug_xform,
						    ", ipsec_sadb_hash[%d]=%s",
						    i,
						    sa_len ? sa : " (error)");
				}
				if(*ipsprev != NULL) {
					sa_len = satoa((*ipsprev)->ips_said, 0, sa, SATOA_BUF);
					KLIPS_PRINT(debug_xform,
						    ", *ipsprev=%s",
						    sa_len ? sa : " (error)");
					if((*ipsprev)->ips_hnext != NULL) {
						sa_len = satoa((*ipsprev)->ips_hnext->ips_said, 0, sa, SATOA_BUF);
						KLIPS_PRINT(debug_xform,
							    ", *ipsprev->ips_hnext=%s",
							    sa_len ? sa : " (error)");
					}
				}
				KLIPS_PRINT(debug_xform,
					    ".\n");
			} else {
				ipsprev = &ipsdel;
			}
			if(ipsdel != NULL) {
				ipsec_sa_put(ipsdel);
			}
		}
	}
 errlab:

	spin_unlock_bh(&tdb_lock);


#if IPSEC_SA_REF_CODE
	/* clean up SA reference table */

	/* go through the ref table and clean out all the SAs */
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sadb_cleanup: "
		    "removing SAref entries and tables.");
	{
		unsigned table, entry;
		for(table = 0; table < IPSEC_SA_REF_MAINTABLE_SIZE; table++) {
			KLIPS_PRINT(debug_xform,
				    "klips_debug:ipsec_sadb_cleanup: "
				    "cleaning SAref table=%u.\n",
				    table);
			if(ipsec_sadb.refTable[table] == NULL) {
				printk("\n");
				KLIPS_PRINT(debug_xform,
					    "klips_debug:ipsec_sadb_cleanup: "
					    "cleaned %u used refTables.\n",
					    table);
				break;
			}
			for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_SIZE; entry++) {
				if(ipsec_sadb.refTable[table]->entry[entry] != NULL) {
					ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]);
					ipsec_sadb.refTable[table]->entry[entry] = NULL;
				}
			}
		}
	}
#endif /* IPSEC_SA_REF_CODE */

	return(error);
}

int 
ipsec_sadb_free(void)
{
	int error = 0;

	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sadb_free: "
		    "freeing SArefTable memory.\n");

	/* clean up SA reference table */

	/* go through the ref table and clean out all the SAs if any are
	   left and free table memory */
	KLIPS_PRINT(debug_xform,
		    "klips_debug:ipsec_sadb_free: "
		    "removing SAref entries and tables.\n");
	{
		unsigned table, entry;
		for(table = 0; table < IPSEC_SA_REF_MAINTABLE_SIZE; table++) {
			KLIPS_PRINT(debug_xform,
				    "klips_debug:ipsec_sadb_free: "
				    "removing SAref table=%u.\n",
				    table);
			if(ipsec_sadb.refTable[table] == NULL) {
				KLIPS_PRINT(debug_xform,
					    "klips_debug:ipsec_sadb_free: "
					    "removed %u used refTables.\n",
					    table);
				break;
			}
			for(entry = 0; entry < IPSEC_SA_REF_SUBTABLE_SIZE; entry++) {
				if(ipsec_sadb.refTable[table]->entry[entry] != NULL) {
					ipsec_sa_delchain(ipsec_sadb.refTable[table]->entry[entry]);
					ipsec_sadb.refTable[table]->entry[entry] = NULL;
				}
			}
			vfree(ipsec_sadb.refTable[table]);
			ipsec_sadb.refTable[table] = NULL;
		}
	}

	return(error);
}

int
ipsec_sa_wipe(struct ipsec_sa *ips)
{
	if(ips == NULL) {
		return -ENODATA;
	}

	/* if(atomic_dec_and_test(ips)) {
	}; */

#if IPSEC_SA_REF_CODE
	/* remove me from the SArefTable */
	{
		char sa[SATOA_BUF];
		size_t sa_len;
		sa_len = satoa(ips->ips_said, 0, sa, SATOA_BUF);
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_wipe: "
			    "removing SA=%s(0p%p), SAref=%d, table=%d(0p%p), entry=%d from the refTable.\n",
			    sa_len ? sa : " (error)",
			    ips,
			    ips->ips_ref,
			    IPsecSAref2table(IPsecSA2SAref(ips)),
			    ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))],
			    IPsecSAref2entry(IPsecSA2SAref(ips)));
	}
	if(ips->ips_ref == -1) {
		KLIPS_PRINT(debug_xform,
			    "klips_debug:ipsec_sa_wipe: "
			    "why does this SA not have a valid SAref?.\n");
	}
	ipsec_sadb.refTable[IPsecSAref2table(IPsecSA2SAref(ips))]->entry[IPsecSAref2entry(IPsecSA2SAref(ips))] = NULL;
	ips->ips_ref = -1;
	ipsec_sa_put(ips);
#endif /* IPSEC_SA_REF_CODE */

	/* paranoid clean up */
	if(ips->ips_addr_s != NULL) {
		memset((caddr_t)(ips->ips_addr_s), 0, ips->ips_addr_s_size);
		kfree(ips->ips_addr_s);
	}
	ips->ips_addr_s = NULL;

	if(ips->ips_addr_d != NULL) {
		memset((caddr_t)(ips->ips_addr_d), 0, ips->ips_addr_d_size);
		kfree(ips->ips_addr_d);
	}
	ips->ips_addr_d = NULL;

	if(ips->ips_addr_p != NULL) {
		memset((caddr_t)(ips->ips_addr_p), 0, ips->ips_addr_p_size);
		kfree(ips->ips_addr_p);
	}
	ips->ips_addr_p = NULL;

	if(ips->ips_key_a != NULL) {
		memset((caddr_t)(ips->ips_key_a), 0, ips->ips_key_a_size);
		kfree(ips->ips_key_a);
	}
	ips->ips_key_a = NULL;

	if(ips->ips_key_e != NULL) {
		memset((caddr_t)(ips->ips_key_e), 0, ips->ips_key_e_size);
		kfree(ips->ips_key_e);
	}
	ips->ips_key_e = NULL;

	if(ips->ips_iv != NULL) {
		memset((caddr_t)(ips->ips_iv), 0, ips->ips_iv_size);
		kfree(ips->ips_iv);
	}
	ips->ips_iv = NULL;

	if(ips->ips_ident_s.data != NULL) {
		memset((caddr_t)(ips->ips_ident_s.data),
                       0,
		       ips->ips_ident_s.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident));
		kfree(ips->ips_ident_s.data);
        }
	ips->ips_ident_s.data = NULL;
	
	if(ips->ips_ident_d.data != NULL) {
		memset((caddr_t)(ips->ips_ident_d.data),
                       0,
		       ips->ips_ident_d.len * IPSEC_PFKEYv2_ALIGN - sizeof(struct sadb_ident));
		kfree(ips->ips_ident_d.data);
        }
	ips->ips_ident_d.data = NULL;
	
	memset((caddr_t)ips, 0, sizeof(*ips));
	kfree(ips);
	ips = NULL;

	return 0;
}

/*
 * $Log: ipsec_sa.c,v $
 * Revision 1.18  2002/10/12 23:11:53  dhr
 *
 * [KenB + DHR] more 64-bit cleanup
 *
 * Revision 1.17  2002/10/07 18:31:43  rgb
 * Move field width sanity checks to ipsec_sa.c
 *
 * Revision 1.16  2002/09/20 15:41:02  rgb
 * Re-wrote most of the SAref code to eliminate Entry pointers.
 * Added SAref code compiler directive switch.
 * Added a saref test function for testing macros.
 * Switch from pfkey_alloc_ipsec_sa() to ipsec_sa_alloc().
 * Split ipsec_sadb_cleanup from new funciton ipsec_sadb_free to avoid problem
 * of freeing newly created structures when clearing the reftable upon startup
 * to start from a known state.
 * Place all ipsec sadb globals into one struct.
 * Rework saref freelist.
 * Added memory allocation debugging.
 *
 * Revision 1.15  2002/09/20 05:01:44  rgb
 * Update copyright date.
 *
 * Revision 1.14  2002/08/13 19:01:25  mcr
 * 	patches from kenb to permit compilation of FreeSWAN on ia64.
 * 	des library patched to use proper DES_LONG type for ia64.
 *
 * Revision 1.13  2002/07/29 03:06:20  mcr
 * 	get rid of variable not used warnings.
 *
 * Revision 1.12  2002/07/26 08:48:31  rgb
 * Added SA ref table code.
 *
 * Revision 1.11  2002/06/04 16:48:49  rgb
 * Tidied up pointer code for processor independance.
 *
 * Revision 1.10  2002/05/23 07:16:17  rgb
 * Added ipsec_sa_put() for releasing an ipsec_sa refcount.
 * Pointer clean-up.
 * Added refcount code.
 * Convert "usecount" to "refcount" to remove ambiguity.
 *
 * Revision 1.9  2002/05/14 02:34:49  rgb
 * Converted reference from ipsec_sa_put to ipsec_sa_add to avoid confusion
 * with "put" usage in the kernel.
 * Change all references to tdb, TDB or Tunnel Descriptor Block to ips,
 * ipsec_sa or ipsec_sa.
 * Added some preliminary refcount code.
 *
 * Revision 1.8  2002/04/24 07:55:32  mcr
 * 	#include patches and Makefiles for post-reorg compilation.
 *
 * Revision 1.7  2002/04/24 07:36:30  mcr
 * Moved from ./klips/net/ipsec/ipsec_sa.c,v
 *
 * Revision 1.6  2002/04/20 00:12:25  rgb
 * Added esp IV CBC attack fix, disabled.
 *
 * Revision 1.5  2002/01/29 17:17:56  mcr
 * 	moved include of ipsec_param.h to after include of linux/kernel.h
 * 	otherwise, it seems that some option that is set in ipsec_param.h
 * 	screws up something subtle in the include path to kernel.h, and
 * 	it complains on the snprintf() prototype.
 *
 * Revision 1.4  2002/01/29 04:00:52  mcr
 * 	more excise of kversions.h header.
 *
 * Revision 1.3  2002/01/29 02:13:18  mcr
 * 	introduction of ipsec_kversion.h means that include of
 * 	ipsec_param.h must preceed any decisions about what files to
 * 	include to deal with differences in kernel source.
 *
 * Revision 1.2  2001/11/26 09:16:15  rgb
 * Merge MCR's ipsec_sa, eroute, proc and struct lifetime changes.
 *
 * Revision 1.1.2.2  2001/10/22 21:05:41  mcr
 * 	removed phony prototype for des_set_key.
 *
 * Revision 1.1.2.1  2001/09/25 02:24:57  mcr
 * 	struct tdb -> struct ipsec_sa.
 * 	sa(tdb) manipulation functions renamed and moved to ipsec_sa.c
 * 	ipsec_xform.c removed. header file still contains useful things.
 *
 *
 *
 * CLONED from ipsec_xform.c:
 * Revision 1.53  2001/09/08 21:13:34  rgb
 * Added pfkey ident extension support for ISAKMPd. (NetCelo)
 *
 * Revision 1.52  2001/06/14 19:35:11  rgb
 * Update copyright date.
 *
 * Revision 1.51  2001/05/30 08:14:03  rgb
 * Removed vestiges of esp-null transforms.
 *
 * Revision 1.50  2001/05/03 19:43:18  rgb
 * Initialise error return variable.
 * Update SENDERR macro.
 * Fix sign of error return code for ipsec_tdbcleanup().
 * Use more appropriate return code for ipsec_tdbwipe().
 *
 * Revision 1.49  2001/04/19 18:56:17  rgb
 * Fixed tdb table locking comments.
 *
 * Revision 1.48  2001/02/27 22:24:55  rgb
 * Re-formatting debug output (line-splitting, joining, 1arg/line).
 * Check for satoa() return codes.
 *
 * Revision 1.47  2000/11/06 04:32:08  rgb
 * Ditched spin_lock_irqsave in favour of spin_lock_bh.
 *
 * Revision 1.46  2000/09/20 16:21:57  rgb
 * Cleaned up ident string alloc/free.
 *
 * Revision 1.45  2000/09/08 19:16:51  rgb
 * Change references from DEBUG_IPSEC to CONFIG_IPSEC_DEBUG.
 * Removed all references to CONFIG_IPSEC_PFKEYv2.
 *
 * Revision 1.44  2000/08/30 05:29:04  rgb
 * Compiler-define out no longer used tdb_init() in ipsec_xform.c.
 *
 * Revision 1.43  2000/08/18 21:30:41  rgb
 * Purged all tdb_spi, tdb_proto and tdb_dst macros.  They are unclear.
 *
 * Revision 1.42  2000/08/01 14:51:51  rgb
 * Removed _all_ remaining traces of DES.
 *
 * Revision 1.41  2000/07/28 14:58:31  rgb
 * Changed kfree_s to kfree, eliminating extra arg to fix 2.4.0-test5.
 *
 * Revision 1.40  2000/06/28 05:50:11  rgb
 * Actually set iv_bits.
 *
 * Revision 1.39  2000/05/10 23:11:09  rgb
 * Added netlink debugging output.
 * Added a cast to quiet down the ntohl bug.
 *
 * Revision 1.38  2000/05/10 19:18:42  rgb
 * Cast output of ntohl so that the broken prototype doesn't make our
 * compile noisy.
 *
 * Revision 1.37  2000/03/16 14:04:59  rgb
 * Hardwired CONFIG_IPSEC_PFKEYv2 on.
 *
 * Revision 1.36  2000/01/26 10:11:28  rgb
 * Fixed spacing in error text causing run-in words.
 *
 * Revision 1.35  2000/01/21 06:17:16  rgb
 * Tidied up compiler directive indentation for readability.
 * Added ictx,octx vars for simplification.(kravietz)
 * Added macros for HMAC padding magic numbers.(kravietz)
 * Fixed missing key length reporting bug.
 * Fixed bug in tdbwipe to return immediately on NULL tdbp passed in.
 *
 * Revision 1.34  1999/12/08 00:04:19  rgb
 * Fixed SA direction overwriting bug for netlink users.
 *
 * Revision 1.33  1999/12/01 22:16:44  rgb
 * Minor formatting changes in ESP MD5 initialisation.
 *
 * Revision 1.32  1999/11/25 09:06:36  rgb
 * Fixed error return messages, should be returning negative numbers.
 * Implemented SENDERR macro for propagating error codes.
 * Added debug message and separate error code for algorithms not compiled
 * in.
 *
 * Revision 1.31  1999/11/23 23:06:26  rgb
 * Sort out pfkey and freeswan headers, putting them in a library path.
 *
 * Revision 1.30  1999/11/18 04:09:20  rgb
 * Replaced all kernel version macros to shorter, readable form.
 *
 * Revision 1.29  1999/11/17 15:53:40  rgb
 * Changed all occurrences of #include "../../../lib/freeswan.h"
 * to #include <freeswan.h> which works due to -Ilibfreeswan in the
 * klips/net/ipsec/Makefile.
 *
 * Revision 1.28  1999/10/18 20:04:01  rgb
 * Clean-out unused cruft.
 *
 * Revision 1.27  1999/10/03 19:01:03  rgb
 * Spinlock support for 2.3.xx and 2.0.xx kernels.
 *
 * Revision 1.26  1999/10/01 16:22:24  rgb
 * Switch from assignment init. to functional init. of spinlocks.
 *
 * Revision 1.25  1999/10/01 15:44:54  rgb
 * Move spinlock header include to 2.1> scope.
 *
 * Revision 1.24  1999/10/01 00:03:46  rgb
 * Added tdb structure locking.
 * Minor formatting changes.
 * Add function to initialize tdb hash table.
 *
 * Revision 1.23  1999/05/25 22:42:12  rgb
 * Add deltdbchain() debugging.
 *
 * Revision 1.22  1999/05/25 21:24:31  rgb
 * Add debugging statements to deltdbchain().
 *
 * Revision 1.21  1999/05/25 03:51:48  rgb
 * Refix error return code.
 *
 * Revision 1.20  1999/05/25 03:34:07  rgb
 * Fix error return for flush.
 *
 * Revision 1.19  1999/05/09 03:25:37  rgb
 * Fix bug introduced by 2.2 quick-and-dirty patch.
 *
 * Revision 1.18  1999/05/05 22:02:32  rgb
 * Add a quick and dirty port to 2.2 kernels by Marc Boucher <marc@mbsi.ca>.
 *
 * Revision 1.17  1999/04/29 15:20:16  rgb
 * Change gettdb parameter to a pointer to reduce stack loading and
 * facilitate parameter sanity checking.
 * Add sanity checking for null pointer arguments.
 * Add debugging instrumentation.
 * Add function deltdbchain() which will take care of unlinking,
 * zeroing and deleting a chain of tdbs.
 * Add a parameter to tdbcleanup to be able to delete a class of SAs.
 * tdbwipe now actually zeroes the tdb as well as any of its pointed
 * structures.
 *
 * Revision 1.16  1999/04/16 15:36:29  rgb
 * Fix cut-and-paste error causing a memory leak in IPIP TDB freeing.
 *
 * Revision 1.15  1999/04/11 00:29:01  henry
 * GPL boilerplate
 *
 * Revision 1.14  1999/04/06 04:54:28  rgb
 * Fix/Add RCSID Id: and Log: bits to make PHMDs happy.  This includes
 * patch shell fixes.
 *
 * Revision 1.13  1999/02/19 18:23:01  rgb
 * Nix debug off compile warning.
 *
 * Revision 1.12  1999/02/17 16:52:16  rgb
 * Consolidate satoa()s for space and speed efficiency.
 * Convert DEBUG_IPSEC to KLIPS_PRINT
 * Clean out unused cruft.
 * Ditch NET_IPIP dependancy.
 * Loop for 3des key setting.
 *
 * Revision 1.11  1999/01/26 02:09:05  rgb
 * Remove ah/esp/IPIP switching on include files.
 * Removed CONFIG_IPSEC_ALGO_SWITCH macro.
 * Removed dead code.
 * Clean up debug code when switched off.
 * Remove references to INET_GET_PROTOCOL.
 * Added code exclusion macros to reduce code from unused algorithms.
 *
 * Revision 1.10  1999/01/22 06:28:55  rgb
 * Cruft clean-out.
 * Put random IV generation in kernel.
 * Added algorithm switch code.
 * Enhanced debugging.
 * 64-bit clean-up.
 *
 * Revision 1.9  1998/11/30 13:22:55  rgb
 * Rationalised all the klips kernel file headers.  They are much shorter
 * now and won't conflict under RH5.2.
 *
 * Revision 1.8  1998/11/25 04:59:06  rgb
 * Add conditionals for no IPIP tunnel code.
 * Delete commented out code.
 *
 * Revision 1.7  1998/10/31 06:50:41  rgb
 * Convert xform ASCII names to no spaces.
 * Fixed up comments in #endif directives.
 *
 * Revision 1.6  1998/10/19 14:44:28  rgb
 * Added inclusion of freeswan.h.
 * sa_id structure implemented and used: now includes protocol.
 *
 * Revision 1.5  1998/10/09 04:32:19  rgb
 * Added 'klips_debug' prefix to all klips printk debug statements.
 *
 * Revision 1.4  1998/08/12 00:11:31  rgb
 * Added new xform functions to the xform table.
 * Fixed minor debug output spelling error.
 *
 * Revision 1.3  1998/07/09 17:45:31  rgb
 * Clarify algorithm not available message.
 *
 * Revision 1.2  1998/06/23 03:00:51  rgb
 * Check for presence of IPIP protocol if it is setup one way (we don't
 * know what has been set up the other way and can only assume it will be
 * symmetrical with the exception of keys).
 *
 * Revision 1.1  1998/06/18 21:27:51  henry
 * move sources from klips/src to klips/net/ipsec, to keep stupid
 * kernel-build scripts happier in the presence of symlinks
 *
 * Revision 1.3  1998/06/11 05:54:59  rgb
 * Added transform version string pointer to xformsw initialisations.
 *
 * Revision 1.2  1998/04/21 21:28:57  rgb
 * Rearrange debug switches to change on the fly debug output from user
 * space.  Only kernel changes checked in at this time.  radij.c was also
 * changed to temporarily remove buggy debugging code in rj_delete causing
 * an OOPS and hence, netlink device open errors.
 *
 * Revision 1.1  1998/04/09 03:06:13  henry
 * sources moved up from linux/net/ipsec
 *
 * Revision 1.1.1.1  1998/04/08 05:35:02  henry
 * RGB's ipsec-0.8pre2.tar.gz ipsec-0.8
 *
 * Revision 0.5  1997/06/03 04:24:48  ji
 * Added ESP-3DES-MD5-96
 *
 * Revision 0.4  1997/01/15 01:28:15  ji
 * Added new transforms.
 *
 * Revision 0.3  1996/11/20 14:39:04  ji
 * Minor cleanups.
 * Rationalized debugging code.
 *
 * Revision 0.2  1996/11/02 00:18:33  ji
 * First limited release.
 *
 *
 */
