changeset 1382:56b339272cd4

* include/net/if_gif.h include/net/if_sec.h include/net/zlib.h include/netinet/ip_ecn.h include/netinet6/ah.h include/netinet6/esp.h include/netinet6/esp_rijndael.h include/netinet6/esp_twofish.h include/netinet6/in6_gif.h include/netinet6/ipcomp.h include/netkey/key.h include/netkey/key_debug.h include/netkey/keysock.h src/sys/netkey src/sys/netinet/ip_ecn.c src/sys/netinet6/ah_core.c src/sys/netinet6/ah_input.c src/sys/netinet6/ah_output.c src/sys/netinet6/esp_core.c src/sys/netinet6/esp_input.c src/sys/netinet6/esp_output.c src/sys/netinet6/esp_rijndael.c src/sys/netinet6/esp_twofish.c src/sys/netinet6/in6_gif.c src/sys/netinet6/ipcomp_core.c src/sys/netinet6/ipcomp_input.c src/sys/netinet6/ipcomp_output.c src/sys/netinet6/ipsec.c: Added back the original KAME FreeBSD IPSEC files. * cdl/freebsd_net.cdl: CDl to control the compilation of IPSEC * include/sys/malloc.h: New memory type needed by IPSEC * include/sys/param.h: Name munging for IPSEC symbols * src/ecos/support.c: read_random_unlimited() should return the number of random bytes in the buffer. * src/sys/kern/kern_sysctl.c: Added missing copyright header. * src/sys/netinet/ip_output.c: Removed user() checks in IPSEC code. * src/sys/netinet6/ip6_output.c: Compiler warning fixes.
author asl
date Sat, 22 Nov 2003 12:59:20 +0000
parents 219ebc4f4562
children d2184fdf0d51
files packages/net/bsd_tcpip/current/ChangeLog packages/net/bsd_tcpip/current/cdl/freebsd_net.cdl packages/net/bsd_tcpip/current/include/net/if_gif.h packages/net/bsd_tcpip/current/include/net/if_sec.h packages/net/bsd_tcpip/current/include/net/zlib.h packages/net/bsd_tcpip/current/include/netinet/ip_ecn.h packages/net/bsd_tcpip/current/include/netinet6/ah.h packages/net/bsd_tcpip/current/include/netinet6/esp.h packages/net/bsd_tcpip/current/include/netinet6/esp_rijndael.h packages/net/bsd_tcpip/current/include/netinet6/esp_twofish.h packages/net/bsd_tcpip/current/include/netinet6/in6_gif.h packages/net/bsd_tcpip/current/include/netinet6/ipcomp.h packages/net/bsd_tcpip/current/include/netinet6/ipsec.h packages/net/bsd_tcpip/current/include/netkey/key.h packages/net/bsd_tcpip/current/include/netkey/key_debug.h packages/net/bsd_tcpip/current/include/netkey/keysock.h packages/net/bsd_tcpip/current/include/sys/malloc.h packages/net/bsd_tcpip/current/include/sys/param.h packages/net/bsd_tcpip/current/src/ecos/support.c packages/net/bsd_tcpip/current/src/sys/kern/kern_sysctl.c packages/net/bsd_tcpip/current/src/sys/netinet/ip_ecn.c packages/net/bsd_tcpip/current/src/sys/netinet/ip_output.c packages/net/bsd_tcpip/current/src/sys/netinet6/ah_core.c packages/net/bsd_tcpip/current/src/sys/netinet6/ah_input.c packages/net/bsd_tcpip/current/src/sys/netinet6/ah_output.c packages/net/bsd_tcpip/current/src/sys/netinet6/esp_core.c packages/net/bsd_tcpip/current/src/sys/netinet6/esp_input.c packages/net/bsd_tcpip/current/src/sys/netinet6/esp_output.c packages/net/bsd_tcpip/current/src/sys/netinet6/esp_rijndael.c packages/net/bsd_tcpip/current/src/sys/netinet6/esp_twofish.c packages/net/bsd_tcpip/current/src/sys/netinet6/in6_gif.c packages/net/bsd_tcpip/current/src/sys/netinet6/ip6_output.c packages/net/bsd_tcpip/current/src/sys/netinet6/ipcomp_core.c packages/net/bsd_tcpip/current/src/sys/netinet6/ipcomp_input.c packages/net/bsd_tcpip/current/src/sys/netinet6/ipcomp_output.c packages/net/bsd_tcpip/current/src/sys/netinet6/ipsec.c packages/net/bsd_tcpip/current/src/sys/netkey/key.c packages/net/bsd_tcpip/current/src/sys/netkey/key_debug.c packages/net/bsd_tcpip/current/src/sys/netkey/keydb.c packages/net/bsd_tcpip/current/src/sys/netkey/keysock.c
diffstat 40 files changed, 23652 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/packages/net/bsd_tcpip/current/ChangeLog
+++ b/packages/net/bsd_tcpip/current/ChangeLog
@@ -1,3 +1,28 @@
+2003-11-22  Andrew Lunn  <andrew.lunn@ascom.ch>
+
+	* include/net/if_gif.h include/net/if_sec.h include/net/zlib.h
+	include/netinet/ip_ecn.h include/netinet6/ah.h
+	include/netinet6/esp.h include/netinet6/esp_rijndael.h
+	include/netinet6/esp_twofish.h include/netinet6/in6_gif.h
+	include/netinet6/ipcomp.h include/netkey/key.h
+	include/netkey/key_debug.h include/netkey/keysock.h src/sys/netkey
+	src/sys/netinet/ip_ecn.c src/sys/netinet6/ah_core.c
+	src/sys/netinet6/ah_input.c src/sys/netinet6/ah_output.c
+	src/sys/netinet6/esp_core.c src/sys/netinet6/esp_input.c
+	src/sys/netinet6/esp_output.c src/sys/netinet6/esp_rijndael.c
+	src/sys/netinet6/esp_twofish.c src/sys/netinet6/in6_gif.c
+	src/sys/netinet6/ipcomp_core.c src/sys/netinet6/ipcomp_input.c
+	src/sys/netinet6/ipcomp_output.c src/sys/netinet6/ipsec.c: 
+	Added back the original KAME FreeBSD IPSEC files.  
+	* cdl/freebsd_net.cdl: CDl to control the compilation of IPSEC 
+	* include/sys/malloc.h: New memory type needed by IPSEC 
+	* include/sys/param.h: Name munging for IPSEC symbols 
+	* src/ecos/support.c: read_random_unlimited() should return the
+	number of random bytes in the buffer.
+	* src/sys/kern/kern_sysctl.c: Added missing copyright header.
+	* src/sys/netinet/ip_output.c: Removed user() checks in IPSEC code.
+	* src/sys/netinet6/ip6_output.c: Compiler warning fixes.
+
 2003-11-08  Andrew Lunn  <andrew.lunn@ascom.ch>
 
 	* src/sys/kern/kern_sysctl.c (sysctl): Update the oldlen value at
--- a/packages/net/bsd_tcpip/current/cdl/freebsd_net.cdl
+++ b/packages/net/bsd_tcpip/current/cdl/freebsd_net.cdl
@@ -42,9 +42,18 @@ cdl_package CYGPKG_NET_FREEBSD_STACK {
     requires      CYGPKG_IO_FILEIO
     description   "Basic networking support, including TCP/IP."
 
+    cdl_interface CYGINT_NET_IPSEC_BSD_CRYPTO {
+        display   "Implementation of BSD CRYPTO functions"
+        description "
+          In order to avoid potential export problems of crypto
+          functions, they are distributed in a separate package which
+          must implement this interface"
+    }
+
     implements    CYGPKG_NET_STACK
     implements    CYGPKG_NET_STACK_INET
     implements    CYGPKG_NET_STACK_INET6
+
     # Note: separating the stack implementation from the common support leads
     # to some rather incestious config file relationships.
     define_proc {
@@ -152,6 +161,50 @@ cdl_package CYGPKG_NET_FREEBSD_STACK {
 ##              sys/netinet6/ip6_fw.c \
 ##
         }
+        cdl_option CYGPKG_NET_FREEBSD_IPSEC {
+            display       "IPSEC support"
+            requires      CYGPKG_COMPRESS_ZLIB
+            implements    CYGPKG_NET_STACK_IPSEC
+
+            flavor        bool
+            default_value CYGPKG_NET_IPSEC_BSD_CRYPTO
+
+            description   "
+                This option enables support for IPSEC."
+            # These files were derived from FreeBSD/KAME and carry their copyright
+            compile \
+              sys/netkey/key.c \
+              sys/netkey/key_debug.c \
+              sys/netkey/keydb.c\
+              sys/netkey/keysock.c \
+              sys/netinet6/ipsec.c \
+              sys/netinet6/ah_core.c \
+              sys/netinet6/ah_input.c \
+              sys/netinet6/ah_output.c \
+              sys/netinet6/ipcomp_core.c \
+              sys/netinet6/esp_core.c \
+              sys/netinet6/esp_output.c \
+              sys/netinet6/esp_input.c \
+              sys/netinet6/esp_rijndael.c \
+              sys/netinet6/esp_twofish.c \
+              sys/netinet6/ipcomp_core.c \
+              sys/netinet6/ipcomp_output.c \
+              sys/netinet6/ipcomp_input.c \
+              sys/netinet/ip_ecn.c
+        }
+        cdl_option CYGPKG_NET_FREEBSD_IPSEC6 {
+            display       "IPSEC support for IPv6"
+            active_if     CYGPKG_NET_INET6
+            active_if     CYGPKG_NET_FREEBSD_IPSEC
+
+            flavor        bool
+            default_value 1
+            description   "
+                This option enables support for IPSEC with IPv6"
+            compile \
+              sys/netinet6/in6_gif.c
+
+        }
         cdl_option CYGPKG_NET_FREEBSD_SYSCTL {
             display       "sysctl support"
             flavor        bool
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/net/if_gif.h
@@ -0,0 +1,120 @@
+//==========================================================================
+//
+//      include/net/ip_gif.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: if_gif.h,v 1.28 2001/10/23 12:41:29 jinmei Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * if_gif.h
+ */
+
+#ifndef _NET_IF_GIF_H_
+#define _NET_IF_GIF_H_
+
+#include <netinet/in.h>
+/* xxx sigh, why route have struct route instead of pointer? */
+
+struct encaptab;
+
+struct gif_softc {
+	struct ifnet	gif_if;	   /* common area - must be at the top */
+	struct sockaddr	*gif_psrc; /* Physical src addr */
+	struct sockaddr	*gif_pdst; /* Physical dst addr */
+	union {
+		struct route  gifscr_ro;    /* xxx */
+#ifdef INET6
+#if defined(NEW_STRUCT_ROUTE) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
+		struct route gifscr_ro6; /* xxx */
+#else
+		struct route_in6 gifscr_ro6; /* xxx */
+#endif
+#endif
+	} gifsc_gifscr;
+	const struct encaptab *encap_cookie4;
+	const struct encaptab *encap_cookie6;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 4
+	struct resource *r_unit;	/* resource allocated for this unit */
+#endif
+	LIST_ENTRY(gif_softc) gif_list; /* all gif's are linked */
+
+	time_t rtcache_expire;	/* expiration time of the cached route */
+};
+
+#define gif_ro gifsc_gifscr.gifscr_ro
+#ifdef INET6
+#define gif_ro6 gifsc_gifscr.gifscr_ro6
+#endif
+
+#define GIF_MTU		(1280)	/* Default MTU */
+#define	GIF_MTU_MIN	(1280)	/* Minimum MTU */
+#define	GIF_MTU_MAX	(8192)	/* Maximum MTU */
+
+extern int ngif;
+extern struct gif_softc *gif_softc;
+
+/* Prototypes */
+void gifattach0 __P((struct gif_softc *));
+#ifndef __OpenBSD__
+void gif_input __P((struct mbuf *, int, struct ifnet *));
+#endif
+int gif_output __P((struct ifnet *, struct mbuf *,
+		    struct sockaddr *, struct rtentry *));
+#if defined(__FreeBSD__) && __FreeBSD__ < 3
+int gif_ioctl __P((struct ifnet *, int, caddr_t));
+#else
+int gif_ioctl __P((struct ifnet *, u_long, caddr_t));
+#endif
+int gif_set_tunnel __P((struct ifnet *, struct sockaddr *, struct sockaddr *));
+void gif_delete_tunnel __P((struct ifnet *));
+#ifdef __OpenBSD__
+void gif_start __P((struct ifnet *));
+#endif
+int gif_encapcheck __P((const struct mbuf *, int, int, void *));
+
+#endif /* _NET_IF_GIF_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/net/if_sec.h
@@ -0,0 +1,71 @@
+//==========================================================================
+//
+//      include/net/if_sec.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: if_sec.h,v 1.3 2001/07/27 09:54:51 itojun Exp $	*/
+
+/*
+ * Copyright (C) 2001 WIDE Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NET_IF_SEC_H_
+#define _NET_IF_SEC_H_
+
+#include <net/if_gif.h>
+
+struct sec_softc {
+	struct gif_softc sc_gif;
+	int sc_refcnt;		/* references other than ifchain */
+};
+
+/* Prototypes */
+struct ifnet *sec_establish __P((struct sockaddr *, struct sockaddr *));
+int sec_demolish __P((struct ifnet *));
+#if defined(__FreeBSD__) && __FreeBSD__ < 3
+int sec_ioctl __P((struct ifnet *, int, caddr_t));
+#else
+int sec_ioctl __P((struct ifnet *, u_long, caddr_t));
+#endif
+
+#endif /* _NET_IF_SEC_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/net/zlib.h
@@ -0,0 +1,41 @@
+#ifndef CYGONCE_NET_BSD_TCPIP_NET_ZLIB_H
+#define CYGONCE_NET_BSD_TCPIP_NET_ZLIB_H
+
+//==========================================================================
+//
+//      include/net/zlib.h
+//
+//      zlib compression 
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD or other sources,
+// and are covered by the appropriate copyright disclaimers included herein.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    Andrew Lunn
+// Contributors: Andrew Lunn
+// Date:         2003-11-08
+// Purpose:      
+// Description:  
+//              
+//
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <cyg/compress/zlib.h>
+
+#endif //CYGONCE_NET_BSD_TCPIP_NET_ZLIB_H
+
+
+
+
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet/ip_ecn.h
@@ -0,0 +1,73 @@
+//==========================================================================
+//
+//      include/netinet/ip_ecn.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ip_ecn.h,v 1.7 2001/12/06 02:18:47 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ * ECN consideration on tunnel ingress/egress operation.
+ * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
+ */
+
+#ifndef _NETINET_IP_ECN_H_
+#define _NETINET_IP_ECN_H_
+
+#define ECN_ALLOWED	1	/* ECN allowed */
+#define ECN_FORBIDDEN	0	/* ECN forbidden */
+#define ECN_NOCARE	(-1)	/* no consideration to ECN */
+
+#if defined(KERNEL) || defined(_KERNEL)
+extern void ip_ecn_ingress __P((int, u_int8_t *, const u_int8_t *));
+extern void ip_ecn_egress __P((int, const u_int8_t *, u_int8_t *));
+#ifdef INET6
+extern void ip6_ecn_ingress __P((int, u_int32_t *, const u_int32_t *));
+extern void ip6_ecn_egress __P((int, const u_int32_t *, u_int32_t *));
+#endif
+#endif
+#endif
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet6/ah.h
@@ -0,0 +1,128 @@
+//==========================================================================
+//
+//      include/netinet6/ah.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+
+/*	$KAME: ah.h,v 1.16 2001/09/04 08:43:19 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#ifndef _NETINET6_AH_H_
+#define _NETINET6_AH_H_
+
+struct ah {
+	u_int8_t	ah_nxt;		/* Next Header */
+	u_int8_t	ah_len;		/* Length of data, in 32bit */
+	u_int16_t	ah_reserve;	/* Reserved for future use */
+	u_int32_t	ah_spi;		/* Security parameter index */
+	/* variable size, 32bit bound*/	/* Authentication data */
+};
+
+struct newah {
+	u_int8_t	ah_nxt;		/* Next Header */
+	u_int8_t	ah_len;		/* Length of data + 1, in 32bit */
+	u_int16_t	ah_reserve;	/* Reserved for future use */
+	u_int32_t	ah_spi;		/* Security parameter index */
+	u_int32_t	ah_seq;		/* Sequence number field */
+	/* variable size, 32bit bound*/	/* Authentication data */
+};
+
+#ifdef _KERNEL
+struct secasvar;
+
+struct ah_algorithm_state {
+	struct secasvar *sav;
+	void* foo;	/* per algorithm data - maybe */
+};
+
+struct ah_algorithm {
+	int (*sumsiz) __P((struct secasvar *));
+	int (*mature) __P((struct secasvar *));
+	int keymin;	/* in bits */
+	int keymax;	/* in bits */
+	const char *name;
+	int (*init) __P((struct ah_algorithm_state *, struct secasvar *));
+	void (*update) __P((struct ah_algorithm_state *, caddr_t, size_t));
+	void (*result) __P((struct ah_algorithm_state *, caddr_t));
+};
+
+#define	AH_MAXSUMSIZE	16
+
+extern const struct ah_algorithm *ah_algorithm_lookup __P((int));
+
+/* cksum routines */
+extern int ah_hdrlen __P((struct secasvar *));
+
+extern size_t ah_hdrsiz __P((struct ipsecrequest *));
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
+extern void ah4_input __P((struct mbuf *, int));
+#else
+extern void ah4_input __P((struct mbuf *, ...));
+#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+extern int ah4_output __P((struct mbuf *, struct ipsecrequest *));
+extern int ah4_calccksum __P((struct mbuf *, caddr_t, size_t,
+	const struct ah_algorithm *, struct secasvar *));
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 105080000	/* 1.5H */
+extern void *ah4_ctlinput __P((int, struct sockaddr *, void *));
+#endif
+
+#ifdef INET6
+extern int ah6_input __P((struct mbuf **, int *, int));
+extern int ah6_output __P((struct mbuf *, u_char *, struct mbuf *,
+	struct ipsecrequest *));
+extern int ah6_calccksum __P((struct mbuf *, caddr_t, size_t,
+	const struct ah_algorithm *, struct secasvar *));
+
+extern void ah6_ctlinput __P((int, struct sockaddr *, void *));
+#endif /* INET6 */
+
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_AH_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet6/esp.h
@@ -0,0 +1,141 @@
+//==========================================================================
+//
+//      include/netinet6/esp.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp.h,v 1.19 2001/09/04 08:43:19 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#ifndef _NETINET6_ESP_H_
+#define _NETINET6_ESP_H_
+
+struct esp {
+	u_int32_t	esp_spi;	/* ESP */
+	/* variable size, 32bit bound */	/* Initialization Vector */
+	/* variable size */		/* Payload data */
+	/* variable size */		/* padding */
+	/* 8bit */			/* pad size */
+	/* 8bit */			/* next header */
+	/* 8bit */			/* next header */
+	/* variable size, 32bit bound */ /* Authentication data (new IPsec) */
+};
+
+struct newesp {
+	u_int32_t	esp_spi;	/* ESP */
+	u_int32_t	esp_seq;	/* Sequence number */
+	/* variable size */		/* (IV and) Payload data */
+	/* variable size */		/* padding */
+	/* 8bit */			/* pad size */
+	/* 8bit */			/* next header */
+	/* 8bit */			/* next header */
+	/* variable size, 32bit bound *//* Authentication data */
+};
+
+struct esptail {
+	u_int8_t	esp_padlen;	/* pad length */
+	u_int8_t	esp_nxt;	/* Next header */
+	/* variable size, 32bit bound *//* Authentication data (new IPsec)*/
+};
+
+#ifdef _KERNEL
+struct secasvar;
+
+struct esp_algorithm {
+	size_t padbound;	/* pad boundary, in byte */
+	int ivlenval;		/* iv length, in byte */
+	int (*mature) __P((struct secasvar *));
+	int keymin;	/* in bits */
+	int keymax;	/* in bits */
+	int (*schedlen) __P((const struct esp_algorithm *));
+	const char *name;
+	int (*ivlen) __P((const struct esp_algorithm *, struct secasvar *));
+	int (*decrypt) __P((struct mbuf *, size_t,
+		struct secasvar *, const struct esp_algorithm *, int));
+	int (*encrypt) __P((struct mbuf *, size_t, size_t,
+		struct secasvar *, const struct esp_algorithm *, int));
+	/* not supposed to be called directly */
+	int (*schedule) __P((const struct esp_algorithm *, struct secasvar *));
+	int (*blockdecrypt) __P((const struct esp_algorithm *,
+		struct secasvar *, u_int8_t *, u_int8_t *));
+	int (*blockencrypt) __P((const struct esp_algorithm *,
+		struct secasvar *, u_int8_t *, u_int8_t *));
+};
+
+extern const struct esp_algorithm *esp_algorithm_lookup __P((int));
+extern int esp_max_ivlen __P((void));
+
+/* crypt routines */
+extern int esp4_output __P((struct mbuf *, struct ipsecrequest *));
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
+extern void esp4_input __P((struct mbuf *, int));
+#else
+extern void esp4_input __P((struct mbuf *, ...));
+#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+extern size_t esp_hdrsiz __P((struct ipsecrequest *));
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 105080000	/* 1.5H */
+extern void *esp4_ctlinput __P((int, struct sockaddr *, void *));
+#endif
+
+#ifdef INET6
+extern int esp6_output __P((struct mbuf *, u_char *, struct mbuf *,
+	struct ipsecrequest *));
+extern int esp6_input __P((struct mbuf **, int *, int));
+
+extern void esp6_ctlinput __P((int, struct sockaddr *, void *));
+#endif /* INET6 */
+
+extern int esp_schedule __P((const struct esp_algorithm *, struct secasvar *));
+extern int esp_auth __P((struct mbuf *, size_t, size_t,
+	struct secasvar *, u_char *));
+#endif /* _KERNEL */
+
+#endif /* _NETINET6_ESP_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet6/esp_rijndael.h
@@ -0,0 +1,59 @@
+//==========================================================================
+//
+//      include/netinet6/esp_rijndael.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_rijndael.h,v 1.1 2000/09/20 18:15:22 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+s */
+
+int esp_rijndael_schedlen __P((const struct esp_algorithm *));
+int esp_rijndael_schedule __P((const struct esp_algorithm *,
+	struct secasvar *));
+int esp_rijndael_blockdecrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+int esp_rijndael_blockencrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet6/esp_twofish.h
@@ -0,0 +1,59 @@
+//==========================================================================
+//
+//      include/netinet6/esp_twofish.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_twofish.h,v 1.1 2000/09/20 18:15:23 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+int esp_twofish_schedlen __P((const struct esp_algorithm *));
+int esp_twofish_schedule __P((const struct esp_algorithm *,
+	struct secasvar *));
+int esp_twofish_blockdecrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+int esp_twofish_blockencrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet6/in6_gif.h
@@ -0,0 +1,68 @@
+//==========================================================================
+//
+//      include/netinet6/in6_gif.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: in6_gif.h,v 1.10 2001/12/21 03:32:34 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETINET6_IN6_GIF_H_
+#define _NETINET6_IN6_GIF_H_
+
+#define GIF_HLIM	30
+extern int	ip6_gif_hlim;		/* Hop limit for gif encap packet */
+
+struct gif_softc;
+struct sockaddr;
+int in6_gif_input __P((struct mbuf **, int *, int));
+int in6_gif_output __P((struct ifnet *, int, struct mbuf *));
+int gif_encapcheck6 __P((const struct mbuf *, int, int, void *));
+int in6_gif_attach __P((struct gif_softc *));
+int in6_gif_detach __P((struct gif_softc *));
+void in6_gif_ctlinput __P((int, struct sockaddr *, void *));
+
+#endif /* _NETINET6_IN6_GIF_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netinet6/ipcomp.h
@@ -0,0 +1,97 @@
+//==========================================================================
+//
+//      include/netinet6/ipcomp.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+
+//==========================================================================
+
+/*	$KAME: ipcomp.h,v 1.11 2001/09/04 08:43:19 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#ifndef _NETINET6_IPCOMP_H_
+#define _NETINET6_IPCOMP_H_
+
+struct ipcomp {
+	u_int8_t comp_nxt;	/* Next Header */
+	u_int8_t comp_flags;	/* reserved, must be zero */
+	u_int16_t comp_cpi;	/* Compression parameter index */
+};
+
+/* well-known algorithm number (in CPI), from RFC2409 */
+#define IPCOMP_OUI	1	/* vendor specific */
+#define IPCOMP_DEFLATE	2	/* RFC2394 */
+#define IPCOMP_LZS	3	/* RFC2395 */
+#define IPCOMP_MAX	4
+
+#define IPCOMP_CPI_NEGOTIATE_MIN	256
+
+#if defined(KERNEL) || defined(_KERNEL)
+struct ipcomp_algorithm {
+	int (*compress) __P((struct mbuf *, struct mbuf *, size_t *));
+	int (*decompress) __P((struct mbuf *, struct mbuf *, size_t *));
+	size_t minplen;		/* minimum required length for compression */
+};
+
+struct ipsecrequest;
+extern const struct ipcomp_algorithm *ipcomp_algorithm_lookup __P((int));
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
+extern void ipcomp4_input __P((struct mbuf *, int));
+#else
+extern void ipcomp4_input __P((struct mbuf *, ...));
+#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+extern int ipcomp4_output __P((struct mbuf *, struct ipsecrequest *));
+#ifdef INET6
+extern int ipcomp6_input __P((struct mbuf **, int *, int));
+extern int ipcomp6_output __P((struct mbuf *, u_char *, struct mbuf *,
+	struct ipsecrequest *));
+#endif
+#endif /* KERNEL */
+
+#endif /* _NETINET6_IPCOMP_H_ */
--- a/packages/net/bsd_tcpip/current/include/netinet6/ipsec.h
+++ b/packages/net/bsd_tcpip/current/include/netinet6/ipsec.h
@@ -57,18 +57,6 @@
 #ifndef _NETINET6_IPSEC_H_
 #define _NETINET6_IPSEC_H_
 
-#if defined(_KERNEL) && !defined(_LKM) && !defined(KLD_MODULE)
-#if defined(__FreeBSD__) && __FreeBSD__ >= 3
-#include "opt_inet.h"
-#include "opt_inet6.h"
-#include "opt_ipsec.h"
-#endif
-#if defined(__NetBSD__)
-#include "opt_inet.h"
-#include "opt_ipsec.h"
-#endif
-#endif
-
 #include <net/pfkeyv2.h>
 #include <netkey/keydb.h>
 
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netkey/key.h
@@ -0,0 +1,112 @@
+//==========================================================================
+//
+//      include/netkey/key.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: key.h,v 1.21 2001/07/27 03:51:30 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETKEY_KEY_H_
+#define _NETKEY_KEY_H_
+
+#ifdef _KERNEL
+
+extern struct key_cb key_cb;
+
+struct secpolicy;
+struct secpolicyindex;
+struct ipsecrequest;
+struct secasvar;
+struct sockaddr;
+struct socket;
+struct sadb_msg;
+struct sadb_x_policy;
+
+extern struct secpolicy *key_allocsp __P((struct secpolicyindex *, u_int));
+extern struct secpolicy *key_gettunnel __P((struct sockaddr *,
+	struct sockaddr *, struct sockaddr *, struct sockaddr *));
+extern int key_checkrequest
+	__P((struct ipsecrequest *isr, struct secasindex *));
+extern struct secasvar *key_allocsa __P((u_int, caddr_t, caddr_t,
+					u_int, u_int32_t));
+extern void key_freesp __P((struct secpolicy *));
+extern void key_freeso __P((struct socket *));
+extern void key_freesav __P((struct secasvar *));
+extern struct secpolicy *key_newsp __P((void));
+extern struct secpolicy *key_msg2sp __P((struct sadb_x_policy *,
+	size_t, int *));
+extern struct mbuf *key_sp2msg __P((struct secpolicy *));
+extern int key_ismyaddr __P((struct sockaddr *));
+extern int key_cmpspidx_exactly
+	__P((struct secpolicyindex *, struct secpolicyindex *));
+extern int key_cmpspidx_withmask
+	__P((struct secpolicyindex *, struct secpolicyindex *));
+extern int key_spdacquire __P((struct secpolicy *));
+extern void key_timehandler __P((void));
+extern u_long key_random __P((void));
+extern void key_randomfill __P((void *, size_t));
+extern void key_freereg __P((struct socket *));
+extern int key_parse __P((struct mbuf *, struct socket *));
+extern void key_init __P((void));
+extern int key_checktunnelsanity __P((struct secasvar *, u_int,
+					caddr_t, caddr_t));
+extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *));
+extern void key_sa_routechange __P((struct sockaddr *));
+extern void key_sa_stir_iv __P((struct secasvar *));
+
+#ifdef __FreeBSD__
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_SECA);
+#endif /* MALLOC_DECLARE */
+#endif
+
+#if defined(__bsdi__) || defined(__NetBSD__)
+extern int key_sysctl __P((int *, u_int, void *, size_t *, void *, size_t));
+#endif
+
+#endif /* defined(_KERNEL) */
+#endif /* _NETKEY_KEY_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netkey/key_debug.h
@@ -0,0 +1,108 @@
+//==========================================================================
+//
+//      include/netkey/key_debug.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: key_debug.h,v 1.10 2001/08/05 08:37:52 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETKEY_KEY_DEBUG_H_
+#define _NETKEY_KEY_DEBUG_H_
+
+#ifdef _KERNEL
+/* debug flags */
+#define KEYDEBUG_STAMP		0x00000001 /* path */
+#define KEYDEBUG_DATA		0x00000002 /* data */
+#define KEYDEBUG_DUMP		0x00000004 /* dump */
+
+#define KEYDEBUG_KEY		0x00000010 /* key processing */
+#define KEYDEBUG_ALG		0x00000020 /* ciph & auth algorithm */
+#define KEYDEBUG_IPSEC		0x00000040 /* ipsec processing */
+
+#define KEYDEBUG_KEY_STAMP	(KEYDEBUG_KEY | KEYDEBUG_STAMP)
+#define KEYDEBUG_KEY_DATA	(KEYDEBUG_KEY | KEYDEBUG_DATA)
+#define KEYDEBUG_KEY_DUMP	(KEYDEBUG_KEY | KEYDEBUG_DUMP)
+#define KEYDEBUG_ALG_STAMP	(KEYDEBUG_ALG | KEYDEBUG_STAMP)
+#define KEYDEBUG_ALG_DATA	(KEYDEBUG_ALG | KEYDEBUG_DATA)
+#define KEYDEBUG_ALG_DUMP	(KEYDEBUG_ALG | KEYDEBUG_DUMP)
+#define KEYDEBUG_IPSEC_STAMP	(KEYDEBUG_IPSEC | KEYDEBUG_STAMP)
+#define KEYDEBUG_IPSEC_DATA	(KEYDEBUG_IPSEC | KEYDEBUG_DATA)
+#define KEYDEBUG_IPSEC_DUMP	(KEYDEBUG_IPSEC | KEYDEBUG_DUMP)
+
+#define KEYDEBUG(lev,arg) \
+	do { if ((key_debug_level & (lev)) == (lev)) { arg; } } while (0)
+
+extern u_int32_t key_debug_level;
+#endif /*_KERNEL*/
+
+struct sadb_msg;
+struct sadb_ext;
+extern void kdebug_sadb __P((struct sadb_msg *));
+extern void kdebug_sadb_x_policy __P((struct sadb_ext *));
+
+#ifdef _KERNEL
+struct secpolicy;
+struct secpolicyindex;
+struct secasindex;
+struct secasvar;
+struct secreplay;
+struct mbuf;
+extern void kdebug_secpolicy __P((struct secpolicy *));
+extern void kdebug_secpolicyindex __P((struct secpolicyindex *));
+extern void kdebug_secasindex __P((struct secasindex *));
+extern void kdebug_secasv __P((struct secasvar *));
+extern void kdebug_mbufhdr __P((struct mbuf *));
+extern void kdebug_mbuf __P((struct mbuf *));
+#endif /*_KERNEL*/
+
+struct sockaddr;
+extern void kdebug_sockaddr __P((struct sockaddr *));
+
+extern void ipsec_hexdump __P((caddr_t, int));
+extern void ipsec_bindump __P((caddr_t, int));
+
+#endif /* _NETKEY_KEY_DEBUG_H_ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/include/netkey/keysock.h
@@ -0,0 +1,107 @@
+//==========================================================================
+//
+//      include/netkey/keysock.h
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: keysock.h,v 1.8 2000/03/27 05:11:06 sumikawa Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _NETKEY_KEYSOCK_H_
+#define _NETKEY_KEYSOCK_H_
+
+/* statistics for pfkey socket */
+struct pfkeystat {
+	/* kernel -> userland */
+	u_quad_t out_total;		/* # of total calls */
+	u_quad_t out_bytes;		/* total bytecount */
+	u_quad_t out_msgtype[256];	/* message type histogram */
+	u_quad_t out_invlen;		/* invalid length field */
+	u_quad_t out_invver;		/* invalid version field */
+	u_quad_t out_invmsgtype;	/* invalid message type field */
+	u_quad_t out_tooshort;		/* msg too short */
+	u_quad_t out_nomem;		/* memory allocation failure */
+	u_quad_t out_dupext;		/* duplicate extension */
+	u_quad_t out_invexttype;	/* invalid extension type */
+	u_quad_t out_invsatype;		/* invalid sa type */
+	u_quad_t out_invaddr;		/* invalid address extension */
+	/* userland -> kernel */
+	u_quad_t in_total;		/* # of total calls */
+	u_quad_t in_bytes;		/* total bytecount */
+	u_quad_t in_msgtype[256];	/* message type histogram */
+	u_quad_t in_msgtarget[3];	/* one/all/registered */
+	u_quad_t in_nomem;		/* memory allocation failure */
+	/* others */
+	u_quad_t sockerr;		/* # of socket related errors */
+};
+
+#define KEY_SENDUP_ONE		0
+#define KEY_SENDUP_ALL		1
+#define KEY_SENDUP_REGISTERED	2
+
+#ifdef _KERNEL
+struct keycb {
+	struct rawcb kp_raw;	/* rawcb */
+	int kp_promisc;		/* promiscuous mode */
+	int kp_registered;	/* registered socket */
+};
+
+extern struct pfkeystat pfkeystat;
+
+extern int key_output __P((struct mbuf *, ...));
+#ifndef __NetBSD__
+extern int key_usrreq __P((struct socket *,
+	int, struct mbuf *, struct mbuf *, struct mbuf *));
+#else
+extern int key_usrreq __P((struct socket *,
+	int, struct mbuf *, struct mbuf *, struct mbuf *, struct proc *));
+#endif
+
+extern int key_sendup __P((struct socket *, struct sadb_msg *, u_int, int));
+extern int key_sendup_mbuf __P((struct socket *, struct mbuf *, int));
+#endif /* _KERNEL */
+
+#endif /*_NETKEY_KEYSOCK_H_*/
--- a/packages/net/bsd_tcpip/current/include/sys/malloc.h
+++ b/packages/net/bsd_tcpip/current/include/sys/malloc.h
@@ -62,5 +62,6 @@ externC void cyg_net_free(caddr_t addr, 
 #define M_FTABLE        88
 #define M_SYSCTLOID    100     
 #define M_SYSCTL       101     
+#define M_SECA         102
 
 #endif // _SYS_MALLOC_H_
--- a/packages/net/bsd_tcpip/current/include/sys/param.h
+++ b/packages/net/bsd_tcpip/current/include/sys/param.h
@@ -31,11 +31,6 @@
 // polution to a minimum
 
 // TEXT symbols
-#define MD5Final cyg_MD5Final
-#define MD5Init cyg_MD5Init
-#define MD5Pad cyg_MD5Pad
-#define MD5Transform cyg_MD5Transform
-#define MD5Update cyg_MD5Update
 #define _ip_mcast_src cyg__ip_mcast_src
 #define _ip_mforward cyg__ip_mforward
 #define _ip_mrouter_done cyg__ip_mrouter_done
@@ -704,6 +699,65 @@
 #define udpstat cyg_udpstat
 #endif
 
+// Symbols from the BSD_CRYPTO package
+
+// TEXT symbols
+
+#define SHA256_Data cyg_SHA256_Data
+#define SHA256_End cyg_SHA256_End
+#define SHA256_Final cyg_SHA256_Final
+#define SHA256_Init cyg_SHA256_Init
+#define SHA256_Transform cyg_SHA256_Transform
+#define SHA256_Update cyg_SHA256_Update
+#define SHA384_Data cyg_SHA384_Data
+#define SHA384_End cyg_SHA384_End
+#define SHA384_Final cyg_SHA384_Final
+#define SHA384_Init cyg_SHA384_Init
+#define SHA384_Update cyg_SHA384_Update
+#define SHA512_Data cyg_SHA512_Data
+#define SHA512_End cyg_SHA512_End
+#define SHA512_Final cyg_SHA512_Final
+#define SHA512_Init cyg_SHA512_Init
+#define SHA512_Last cyg_SHA512_Last
+#define SHA512_Transform cyg_SHA512_Transform
+#define SHA512_Update cyg_SHA512_Update
+#define md5_init cyg_md5_init
+#define md5_loop cyg_md5_loop
+#define md5_pad cyg_md5_pad
+#define md5_result cyg_md5_result
+#define sha1_init cyg_sha1_init
+#define sha1_loop cyg_sha1_loop
+#define sha1_pad cyg_sha1_pad
+#define sha1_result cyg_sha1_result
+#define cast128_decrypt_round12 cyg_cast128_decrypt_round12
+#define cast128_decrypt_round16 cyg_cast128_decrypt_round16
+#define cast128_encrypt_round12 cyg_cast128_encrypt_round12
+#define cast128_encrypt_round16 cyg_cast128_encrypt_round16
+#define set_cast128_subkey cyg_set_cast128_subkey
+#define twofish_TableOp cyg_twofish_TableOp
+#define twofish_blockDecrypt cyg_twofish_blockDecrypt
+#define twofish_blockEncrypt cyg_twofish_blockEncrypt
+#define twofish_cipherInit cyg_twofish_cipherInit
+#define twofish_makeKey cyg_twofish_makeKey
+#define twofish_reKey cyg_twofish_reKey
+#define rijndaelDecrypt cyg_rijndaelDecrypt
+#define rijndaelEncrypt cyg_rijndaelEncrypt
+#define rijndaelKeyEncToDec cyg_rijndaelKeyEncToDec
+#define rijndaelKeySched cyg_rijndaelKeySched
+#define rijndael_blockDecrypt cyg_rijndael_blockDecrypt
+#define rijndael_blockEncrypt cyg_rijndael_blockEncrypt
+#define rijndael_cipherInit cyg_rijndael_cipherInit
+#define rijndael_makeKey cyg_rijndael_makeKey
+#define rijndael_padDecrypt cyg_rijndael_padDecrypt
+#define rijndael_padEncrypt cyg_rijndael_padEncrypt
+
+// Data symbols
+
+#define numRounds cyg_numRounds
+
+// BSS symbols
+
+
 #include <pkgconf/net.h>
 #include <cyg/infra/cyg_type.h>  // Standard eCos types
 
@@ -878,7 +932,7 @@ extern void get_mono_time(void);
 extern int arc4random(void);
 extern void get_random_bytes(void *buf, size_t len);
 extern void read_random(void *buf, size_t len);
-extern void read_random_unlimited(void *buf, size_t len);
+extern int read_random_unlimited(void *buf, size_t len);
 extern void *hashinit(int elements, void *type, u_long *hashmask);
 
 // Initialization support - cross between RedBoot & FreeBSD
--- a/packages/net/bsd_tcpip/current/src/ecos/support.c
+++ b/packages/net/bsd_tcpip/current/src/ecos/support.c
@@ -556,10 +556,11 @@ get_random_bytes(void *buf, size_t len)
     }
 }
 
-void
+int
 read_random_unlimited(void *buf, size_t len)
 {
     get_random_bytes(buf, len);
+    return len;
 }
 
 void read_random(void *buf, size_t len) 
--- a/packages/net/bsd_tcpip/current/src/sys/kern/kern_sysctl.c
+++ b/packages/net/bsd_tcpip/current/src/sys/kern/kern_sysctl.c
@@ -1,3 +1,24 @@
+//==========================================================================
+//
+//      src/sys/kern/kerm_sysctl.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
 /*-
  * Copyright (c) 1982, 1986, 1989, 1993
  *	The Regents of the University of California.  All rights reserved.
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet/ip_ecn.c
@@ -0,0 +1,154 @@
+//==========================================================================
+//
+//      src/sys/netinet/ip_ecn.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ip_ecn.c,v 1.11 2001/05/03 16:09:29 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ * ECN consideration on tunnel ingress/egress operation.
+ * http://www.aciri.org/floyd/papers/draft-ipsec-ecn-00.txt
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+
+#include <netinet/ip_ecn.h>
+
+/*
+ * modify outer ECN (TOS) field on ingress operation (tunnel encapsulation).
+ */
+void
+ip_ecn_ingress(mode, outer, inner)
+	int mode;
+	u_int8_t *outer;
+	const u_int8_t *inner;
+{
+	if (!outer || !inner)
+		panic("NULL pointer passed to ip_ecn_ingress");
+
+	*outer = *inner;
+	switch (mode) {
+	case ECN_ALLOWED:		/* ECN allowed */
+		*outer &= ~IPTOS_CE;
+		break;
+	case ECN_FORBIDDEN:		/* ECN forbidden */
+		*outer &= ~(IPTOS_ECT | IPTOS_CE);
+		break;
+	case ECN_NOCARE:	/* no consideration to ECN */
+		break;
+	}
+}
+
+/*
+ * modify inner ECN (TOS) field on egress operation (tunnel decapsulation).
+ */
+void
+ip_ecn_egress(mode, outer, inner)
+	int mode;
+	const u_int8_t *outer;
+	u_int8_t *inner;
+{
+	if (!outer || !inner)
+		panic("NULL pointer passed to ip_ecn_egress");
+
+	switch (mode) {
+	case ECN_ALLOWED:
+		if (*outer & IPTOS_CE)
+			*inner |= IPTOS_CE;
+		break;
+	case ECN_FORBIDDEN:		/* ECN forbidden */
+	case ECN_NOCARE:	/* no consideration to ECN */
+		break;
+	}
+}
+
+#ifdef INET6
+void
+ip6_ecn_ingress(mode, outer, inner)
+	int mode;
+	u_int32_t *outer;
+	const u_int32_t *inner;
+{
+	u_int8_t outer8, inner8;
+
+	if (!outer || !inner)
+		panic("NULL pointer passed to ip6_ecn_ingress");
+
+	inner8 = (ntohl(*inner) >> 20) & 0xff;
+	ip_ecn_ingress(mode, &outer8, &inner8);
+	*outer &= ~htonl(0xff << 20);
+	*outer |= htonl((u_int32_t)outer8 << 20);
+}
+
+void
+ip6_ecn_egress(mode, outer, inner)
+	int mode;
+	const u_int32_t *outer;
+	u_int32_t *inner;
+{
+	u_int8_t outer8, inner8;
+
+	if (!outer || !inner)
+		panic("NULL pointer passed to ip6_ecn_egress");
+
+	outer8 = (ntohl(*outer) >> 20) & 0xff;
+	ip_ecn_egress(mode, &outer8, &inner8);
+	*inner &= ~htonl(0xff << 20);
+	*inner |= htonl((u_int32_t)inner8 << 20);
+}
+#endif
--- a/packages/net/bsd_tcpip/current/src/sys/netinet/ip_output.c
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet/ip_output.c
@@ -1249,8 +1249,7 @@ ip_ctloutput(so, sopt)
 				break;
 			if ((error = soopt_mcopyin(sopt, m)) != 0) /* XXX */
 				break;
-			priv = (sopt->sopt_p != NULL &&
-				suser(sopt->sopt_p) != 0) ? 0 : 1;
+			priv = 1;
 			req = mtod(m, caddr_t);
 			len = m->m_len;
 			optname = sopt->sopt_name;
@@ -1350,6 +1349,17 @@ ip_ctloutput(so, sopt)
 			struct mbuf *m = NULL;
 			caddr_t req = NULL;
 			size_t len = 0;
+                        size_t ovalsize = sopt->sopt_valsize;
+			caddr_t oval = (caddr_t)sopt->sopt_val;
+
+                        error = soopt_getm(sopt, &m); /* XXX */
+                        if (error != 0)
+                                break;
+                        error = soopt_mcopyin(sopt, m); /* XXX */
+                        if (error != 0)
+				break;
+                        sopt->sopt_valsize = ovalsize;
+                        sopt->sopt_val = oval;
 
 			if (m != 0) {
 				req = mtod(m, caddr_t);
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ah_core.c
@@ -0,0 +1,1666 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ah_core.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ah_core.c,v 1.47 2001/10/29 04:43:08 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+/* TODO: have shared routines  for hmac-* algorithms */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+#include <sys/kernel.h>
+#endif
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#ifdef IPSEC_ESP
+#include <netinet6/esp.h>
+#endif
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#ifdef HAVE_MD5
+#include <sys/md5.h>
+#else
+#include <crypto/md5.h>
+#endif
+#ifdef HAVE_SHA1
+#include <sys/sha1.h>
+#define SHA1_RESULTLEN	20
+#else
+#include <crypto/sha1.h>
+#endif
+#include <crypto/sha2/sha2.h>
+
+#define	HMACSIZE	16
+
+static int ah_sumsiz_1216 __P((struct secasvar *));
+static int ah_sumsiz_zero __P((struct secasvar *));
+static int ah_none_mature __P((struct secasvar *));
+static int ah_none_init __P((struct ah_algorithm_state *, struct secasvar *));
+static void ah_none_loop __P((struct ah_algorithm_state *, caddr_t, size_t));
+static void ah_none_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_keyed_md5_mature __P((struct secasvar *));
+static int ah_keyed_md5_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_keyed_md5_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_keyed_md5_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_keyed_sha1_mature __P((struct secasvar *));
+static int ah_keyed_sha1_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_keyed_sha1_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_keyed_sha1_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_md5_mature __P((struct secasvar *));
+static int ah_hmac_md5_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_hmac_md5_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_hmac_md5_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_sha1_mature __P((struct secasvar *));
+static int ah_hmac_sha1_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_hmac_sha1_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_hmac_sha1_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_sha2_256_mature __P((struct secasvar *));
+static int ah_hmac_sha2_256_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_hmac_sha2_256_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_hmac_sha2_256_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_sha2_384_mature __P((struct secasvar *));
+static int ah_hmac_sha2_384_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_hmac_sha2_384_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_hmac_sha2_384_result __P((struct ah_algorithm_state *, caddr_t));
+static int ah_hmac_sha2_512_mature __P((struct secasvar *));
+static int ah_hmac_sha2_512_init __P((struct ah_algorithm_state *,
+	struct secasvar *));
+static void ah_hmac_sha2_512_loop __P((struct ah_algorithm_state *, caddr_t,
+	size_t));
+static void ah_hmac_sha2_512_result __P((struct ah_algorithm_state *, caddr_t));
+
+static void ah_update_mbuf __P((struct mbuf *, int, int,
+	const struct ah_algorithm *, struct ah_algorithm_state *));
+
+const struct ah_algorithm *
+ah_algorithm_lookup(idx)
+	int idx;
+{
+	/* checksum algorithms */
+	static struct ah_algorithm ah_algorithms[] = {
+		{ ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, "hmac-md5",
+			ah_hmac_md5_init, ah_hmac_md5_loop,
+			ah_hmac_md5_result, },
+		{ ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, "hmac-sha1",
+			ah_hmac_sha1_init, ah_hmac_sha1_loop,
+			ah_hmac_sha1_result, },
+		{ ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, "keyed-md5",
+			ah_keyed_md5_init, ah_keyed_md5_loop,
+			ah_keyed_md5_result, },
+		{ ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, "keyed-sha1",
+			ah_keyed_sha1_init, ah_keyed_sha1_loop,
+			ah_keyed_sha1_result, },
+		{ ah_sumsiz_zero, ah_none_mature, 0, 2048, "none",
+			ah_none_init, ah_none_loop, ah_none_result, },
+		{ ah_sumsiz_1216, ah_hmac_sha2_256_mature, 256, 256,
+			"hmac-sha2-256",
+			ah_hmac_sha2_256_init, ah_hmac_sha2_256_loop,
+			ah_hmac_sha2_256_result, },
+		{ ah_sumsiz_1216, ah_hmac_sha2_384_mature, 384, 384,
+			"hmac-sha2-384",
+			ah_hmac_sha2_384_init, ah_hmac_sha2_384_loop,
+			ah_hmac_sha2_384_result, },
+		{ ah_sumsiz_1216, ah_hmac_sha2_512_mature, 512, 512,
+			"hmac-sha2-512",
+			ah_hmac_sha2_512_init, ah_hmac_sha2_512_loop,
+			ah_hmac_sha2_512_result, },
+	};
+
+	switch (idx) {
+	case SADB_AALG_MD5HMAC:
+		return &ah_algorithms[0];
+	case SADB_AALG_SHA1HMAC:
+		return &ah_algorithms[1];
+	case SADB_X_AALG_MD5:
+		return &ah_algorithms[2];
+	case SADB_X_AALG_SHA:
+		return &ah_algorithms[3];
+	case SADB_X_AALG_NULL:
+		return &ah_algorithms[4];
+	case SADB_X_AALG_SHA2_256:
+		return &ah_algorithms[5];
+	case SADB_X_AALG_SHA2_384:
+		return &ah_algorithms[6];
+	case SADB_X_AALG_SHA2_512:
+		return &ah_algorithms[7];
+	default:
+		return NULL;
+	}
+}
+
+
+static int
+ah_sumsiz_1216(sav)
+	struct secasvar *sav;
+{
+	if (!sav)
+		return -1;
+	if (sav->flags & SADB_X_EXT_OLD)
+		return 16;
+	else
+		return 12;
+}
+
+static int
+ah_sumsiz_zero(sav)
+	struct secasvar *sav;
+{
+	if (!sav)
+		return -1;
+	return 0;
+}
+
+static int
+ah_none_mature(sav)
+	struct secasvar *sav;
+{
+	if (sav->sah->saidx.proto == IPPROTO_AH) {
+		ipseclog((LOG_ERR,
+		    "ah_none_mature: protocol and algorithm mismatch.\n"));
+		return 1;
+	}
+	return 0;
+}
+
+static int
+ah_none_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	state->foo = NULL;
+	return 0;
+}
+
+static void
+ah_none_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+}
+
+static void
+ah_none_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+}
+
+static int
+ah_keyed_md5_mature(sav)
+	struct secasvar *sav;
+{
+	/* anything is okay */
+	return 0;
+}
+
+static int
+ah_keyed_md5_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	size_t padlen;
+	size_t keybitlen;
+	u_int8_t buf[32];
+
+	if (!state)
+		panic("ah_keyed_md5_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
+	if (state->foo == NULL)
+		return ENOBUFS;
+
+	MD5Init((MD5_CTX *)state->foo);
+	if (state->sav) {
+		MD5Update((MD5_CTX *)state->foo,
+			(u_int8_t *)_KEYBUF(state->sav->key_auth),
+			(u_int)_KEYLEN(state->sav->key_auth));
+
+		/*
+		 * Pad after the key.
+		 * We cannot simply use md5_pad() since the function
+		 * won't update the total length.
+		 */
+		if (_KEYLEN(state->sav->key_auth) < 56)
+			padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
+		else
+			padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
+		keybitlen = _KEYLEN(state->sav->key_auth);
+		keybitlen *= 8;
+
+		buf[0] = 0x80;
+		MD5Update((MD5_CTX *)state->foo, &buf[0], 1);
+		padlen--;
+
+		bzero(buf, sizeof(buf));
+		while (sizeof(buf) < padlen) {
+			MD5Update((MD5_CTX *)state->foo, &buf[0], sizeof(buf));
+			padlen -= sizeof(buf);
+		}
+		if (padlen) {
+			MD5Update((MD5_CTX *)state->foo, &buf[0], padlen);
+		}
+
+		buf[0] = (keybitlen >> 0) & 0xff;
+		buf[1] = (keybitlen >> 8) & 0xff;
+		buf[2] = (keybitlen >> 16) & 0xff;
+		buf[3] = (keybitlen >> 24) & 0xff;
+		MD5Update((MD5_CTX *)state->foo, buf, 8);
+	}
+
+	return 0;
+}
+
+static void
+ah_keyed_md5_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	if (!state)
+		panic("ah_keyed_md5_loop: what?");
+
+	MD5Update((MD5_CTX *)state->foo, addr, len);
+}
+
+static void
+ah_keyed_md5_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[16];
+
+	if (!state)
+		panic("ah_keyed_md5_result: what?");
+
+	if (state->sav) {
+		MD5Update((MD5_CTX *)state->foo,
+			(u_int8_t *)_KEYBUF(state->sav->key_auth),
+			(u_int)_KEYLEN(state->sav->key_auth));
+	}
+	MD5Final(&digest[0], (MD5_CTX *)state->foo);
+	free(state->foo, M_TEMP);
+	bcopy(&digest[0], (void *)addr, sizeof(digest));
+}
+
+static int
+ah_keyed_sha1_mature(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+
+	if (!sav->key_auth) {
+		ipseclog((LOG_ERR, "ah_keyed_sha1_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR, "ah_keyed_sha1_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (sav->key_auth->sadb_key_bits < algo->keymin
+	 || algo->keymax < sav->key_auth->sadb_key_bits) {
+		ipseclog((LOG_ERR,
+		    "ah_keyed_sha1_mature: invalid key length %d.\n",
+		    sav->key_auth->sadb_key_bits));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+ah_keyed_sha1_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	SHA1_CTX *ctxt;
+	size_t padlen;
+	size_t keybitlen;
+	u_int8_t buf[32];
+
+	if (!state)
+		panic("ah_keyed_sha1_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT);
+	if (!state->foo)
+		return ENOBUFS;
+
+	ctxt = (SHA1_CTX *)state->foo;
+	SHA1Init(ctxt);
+
+	if (state->sav) {
+		SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
+			(u_int)_KEYLEN(state->sav->key_auth));
+
+		/*
+		 * Pad after the key.
+		 */
+		if (_KEYLEN(state->sav->key_auth) < 56)
+			padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
+		else
+			padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
+		keybitlen = _KEYLEN(state->sav->key_auth);
+		keybitlen *= 8;
+
+		buf[0] = 0x80;
+		SHA1Update(ctxt, &buf[0], 1);
+		padlen--;
+
+		bzero(buf, sizeof(buf));
+		while (sizeof(buf) < padlen) {
+			SHA1Update(ctxt, &buf[0], sizeof(buf));
+			padlen -= sizeof(buf);
+		}
+		if (padlen) {
+			SHA1Update(ctxt, &buf[0], padlen);
+		}
+
+		buf[0] = (keybitlen >> 0) & 0xff;
+		buf[1] = (keybitlen >> 8) & 0xff;
+		buf[2] = (keybitlen >> 16) & 0xff;
+		buf[3] = (keybitlen >> 24) & 0xff;
+		SHA1Update(ctxt, buf, 8);
+	}
+
+	return 0;
+}
+
+static void
+ah_keyed_sha1_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	SHA1_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_keyed_sha1_loop: what?");
+	ctxt = (SHA1_CTX *)state->foo;
+
+	SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_keyed_sha1_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[SHA1_RESULTLEN];	/* SHA-1 generates 160 bits */
+	SHA1_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_keyed_sha1_result: what?");
+	ctxt = (SHA1_CTX *)state->foo;
+
+	if (state->sav) {
+		SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
+			(u_int)_KEYLEN(state->sav->key_auth));
+	}
+	SHA1Final((caddr_t)&digest[0], ctxt);
+	bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+	free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_md5_mature(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+
+	if (!sav->key_auth) {
+		ipseclog((LOG_ERR, "ah_hmac_md5_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR, "ah_hmac_md5_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (sav->key_auth->sadb_key_bits < algo->keymin
+	 || algo->keymax < sav->key_auth->sadb_key_bits) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_md5_mature: invalid key length %d.\n",
+		    sav->key_auth->sadb_key_bits));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+ah_hmac_md5_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	u_char *ipad;
+	u_char *opad;
+	u_char tk[16];
+	u_char *key;
+	size_t keylen;
+	size_t i;
+	MD5_CTX *ctxt;
+
+	if (!state)
+		panic("ah_hmac_md5_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
+	if (!state->foo)
+		return ENOBUFS;
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (MD5_CTX *)(opad + 64);
+
+	/* compress the key if necessery */
+	if (64 < _KEYLEN(state->sav->key_auth)) {
+		MD5Init(ctxt);
+		MD5Update(ctxt, _KEYBUF(state->sav->key_auth),
+			_KEYLEN(state->sav->key_auth));
+		MD5Final(&tk[0], ctxt);
+		key = &tk[0];
+		keylen = 16;
+	} else {
+		key = _KEYBUF(state->sav->key_auth);
+		keylen = _KEYLEN(state->sav->key_auth);
+	}
+
+	bzero(ipad, 64);
+	bzero(opad, 64);
+	bcopy(key, ipad, keylen);
+	bcopy(key, opad, keylen);
+	for (i = 0; i < 64; i++) {
+		ipad[i] ^= 0x36;
+		opad[i] ^= 0x5c;
+	}
+
+	MD5Init(ctxt);
+	MD5Update(ctxt, ipad, 64);
+
+	return 0;
+}
+
+static void
+ah_hmac_md5_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	MD5_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_md5_loop: what?");
+	ctxt = (MD5_CTX *)(((caddr_t)state->foo) + 128);
+	MD5Update(ctxt, addr, len);
+}
+
+static void
+ah_hmac_md5_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[16];
+	u_char *ipad;
+	u_char *opad;
+	MD5_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_md5_result: what?");
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (MD5_CTX *)(opad + 64);
+
+	MD5Final(&digest[0], ctxt);
+
+	MD5Init(ctxt);
+	MD5Update(ctxt, opad, 64);
+	MD5Update(ctxt, &digest[0], sizeof(digest));
+	MD5Final(&digest[0], ctxt);
+
+	bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+	free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_sha1_mature(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+
+	if (!sav->key_auth) {
+		ipseclog((LOG_ERR, "ah_hmac_sha1_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR, "ah_hmac_sha1_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (sav->key_auth->sadb_key_bits < algo->keymin
+	 || algo->keymax < sav->key_auth->sadb_key_bits) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha1_mature: invalid key length %d.\n",
+		    sav->key_auth->sadb_key_bits));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+ah_hmac_sha1_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	u_char *ipad;
+	u_char *opad;
+	SHA1_CTX *ctxt;
+	u_char tk[SHA1_RESULTLEN];	/* SHA-1 generates 160 bits */
+	u_char *key;
+	size_t keylen;
+	size_t i;
+
+	if (!state)
+		panic("ah_hmac_sha1_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(64 + 64 + sizeof(SHA1_CTX),
+			M_TEMP, M_NOWAIT);
+	if (!state->foo)
+		return ENOBUFS;
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA1_CTX *)(opad + 64);
+
+	/* compress the key if necessery */
+	if (64 < _KEYLEN(state->sav->key_auth)) {
+		SHA1Init(ctxt);
+		SHA1Update(ctxt, _KEYBUF(state->sav->key_auth),
+			_KEYLEN(state->sav->key_auth));
+		SHA1Final(&tk[0], ctxt);
+		key = &tk[0];
+		keylen = SHA1_RESULTLEN;
+	} else {
+		key = _KEYBUF(state->sav->key_auth);
+		keylen = _KEYLEN(state->sav->key_auth);
+	}
+
+	bzero(ipad, 64);
+	bzero(opad, 64);
+	bcopy(key, ipad, keylen);
+	bcopy(key, opad, keylen);
+	for (i = 0; i < 64; i++) {
+		ipad[i] ^= 0x36;
+		opad[i] ^= 0x5c;
+	}
+
+	SHA1Init(ctxt);
+	SHA1Update(ctxt, ipad, 64);
+
+	return 0;
+}
+
+static void
+ah_hmac_sha1_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	SHA1_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha1_loop: what?");
+
+	ctxt = (SHA1_CTX *)(((u_char *)state->foo) + 128);
+	SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_hmac_sha1_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[SHA1_RESULTLEN];	/* SHA-1 generates 160 bits */
+	u_char *ipad;
+	u_char *opad;
+	SHA1_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha1_result: what?");
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA1_CTX *)(opad + 64);
+
+	SHA1Final((caddr_t)&digest[0], ctxt);
+
+	SHA1Init(ctxt);
+	SHA1Update(ctxt, opad, 64);
+	SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
+	SHA1Final((caddr_t)&digest[0], ctxt);
+
+	bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+	free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_sha2_256_mature(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+
+	if (!sav->key_auth) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_256_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_256_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (sav->key_auth->sadb_key_bits < algo->keymin ||
+	    algo->keymax < sav->key_auth->sadb_key_bits) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_256_mature: invalid key length %d.\n",
+		    sav->key_auth->sadb_key_bits));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+ah_hmac_sha2_256_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	u_char *ipad;
+	u_char *opad;
+	SHA256_CTX *ctxt;
+	u_char tk[SHA256_DIGEST_LENGTH];
+	u_char *key;
+	size_t keylen;
+	size_t i;
+
+	if (!state)
+		panic("ah_hmac_sha2_256_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(64 + 64 + sizeof(SHA256_CTX),
+	    M_TEMP, M_NOWAIT);
+	if (!state->foo)
+		return ENOBUFS;
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA256_CTX *)(opad + 64);
+
+	/* compress the key if necessery */
+	if (64 < _KEYLEN(state->sav->key_auth)) {
+		bzero(tk, sizeof(tk));
+		bzero(ctxt, sizeof(*ctxt));
+		SHA256_Init(ctxt);
+		SHA256_Update(ctxt, _KEYBUF(state->sav->key_auth),
+		    _KEYLEN(state->sav->key_auth));
+		SHA256_Final(&tk[0], ctxt);
+		key = &tk[0];
+		keylen = sizeof(tk) < 64 ? sizeof(tk) : 64;
+	} else {
+		key = _KEYBUF(state->sav->key_auth);
+		keylen = _KEYLEN(state->sav->key_auth);
+	}
+
+	bzero(ipad, 64);
+	bzero(opad, 64);
+	bcopy(key, ipad, keylen);
+	bcopy(key, opad, keylen);
+	for (i = 0; i < 64; i++) {
+		ipad[i] ^= 0x36;
+		opad[i] ^= 0x5c;
+	}
+
+	bzero(ctxt, sizeof(*ctxt));
+	SHA256_Init(ctxt);
+	SHA256_Update(ctxt, ipad, 64);
+
+	return 0;
+}
+
+static void
+ah_hmac_sha2_256_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	SHA256_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha2_256_loop: what?");
+
+	ctxt = (SHA256_CTX *)(((u_char *)state->foo) + 128);
+	SHA256_Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_hmac_sha2_256_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[SHA256_DIGEST_LENGTH];
+	u_char *ipad;
+	u_char *opad;
+	SHA256_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha2_256_result: what?");
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA256_CTX *)(opad + 64);
+
+	SHA256_Final((caddr_t)&digest[0], ctxt);
+
+	bzero(ctxt, sizeof(*ctxt));
+	SHA256_Init(ctxt);
+	SHA256_Update(ctxt, opad, 64);
+	SHA256_Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
+	SHA256_Final((caddr_t)&digest[0], ctxt);
+
+	bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+	free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_sha2_384_mature(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+
+	if (!sav->key_auth) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_384_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_384_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (sav->key_auth->sadb_key_bits < algo->keymin ||
+	    algo->keymax < sav->key_auth->sadb_key_bits) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_384_mature: invalid key length %d.\n",
+		    sav->key_auth->sadb_key_bits));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+ah_hmac_sha2_384_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	u_char *ipad;
+	u_char *opad;
+	SHA384_CTX *ctxt;
+	u_char tk[SHA384_DIGEST_LENGTH];
+	u_char *key;
+	size_t keylen;
+	size_t i;
+
+	if (!state)
+		panic("ah_hmac_sha2_384_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(64 + 64 + sizeof(SHA384_CTX),
+	    M_TEMP, M_NOWAIT);
+	if (!state->foo)
+		return ENOBUFS;
+	bzero(state->foo, 64 + 64 + sizeof(SHA384_CTX));
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA384_CTX *)(opad + 64);
+
+	/* compress the key if necessery */
+	if (64 < _KEYLEN(state->sav->key_auth)) {
+		bzero(tk, sizeof(tk));
+		bzero(ctxt, sizeof(*ctxt));
+		SHA384_Init(ctxt);
+		SHA384_Update(ctxt, _KEYBUF(state->sav->key_auth),
+		    _KEYLEN(state->sav->key_auth));
+		SHA384_Final(&tk[0], ctxt);
+		key = &tk[0];
+		keylen = sizeof(tk) < 64 ? sizeof(tk) : 64;
+	} else {
+		key = _KEYBUF(state->sav->key_auth);
+		keylen = _KEYLEN(state->sav->key_auth);
+	}
+
+	bzero(ipad, 64);
+	bzero(opad, 64);
+	bcopy(key, ipad, keylen);
+	bcopy(key, opad, keylen);
+	for (i = 0; i < 64; i++) {
+		ipad[i] ^= 0x36;
+		opad[i] ^= 0x5c;
+	}
+
+	bzero(ctxt, sizeof(*ctxt));
+	SHA384_Init(ctxt);
+	SHA384_Update(ctxt, ipad, 64);
+
+	return 0;
+}
+
+static void
+ah_hmac_sha2_384_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	SHA384_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha2_384_loop: what?");
+
+	ctxt = (SHA384_CTX *)(((u_char *)state->foo) + 128);
+	SHA384_Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_hmac_sha2_384_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[SHA384_DIGEST_LENGTH];
+	u_char *ipad;
+	u_char *opad;
+	SHA384_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha2_384_result: what?");
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA384_CTX *)(opad + 64);
+
+	SHA384_Final((caddr_t)&digest[0], ctxt);
+
+	bzero(ctxt, sizeof(*ctxt));
+	SHA384_Init(ctxt);
+	SHA384_Update(ctxt, opad, 64);
+	SHA384_Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
+	SHA384_Final((caddr_t)&digest[0], ctxt);
+
+	bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+	free(state->foo, M_TEMP);
+}
+
+static int
+ah_hmac_sha2_512_mature(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+
+	if (!sav->key_auth) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_512_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_512_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (sav->key_auth->sadb_key_bits < algo->keymin ||
+	    algo->keymax < sav->key_auth->sadb_key_bits) {
+		ipseclog((LOG_ERR,
+		    "ah_hmac_sha2_512_mature: invalid key length %d.\n",
+		    sav->key_auth->sadb_key_bits));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+ah_hmac_sha2_512_init(state, sav)
+	struct ah_algorithm_state *state;
+	struct secasvar *sav;
+{
+	u_char *ipad;
+	u_char *opad;
+	SHA512_CTX *ctxt;
+	u_char tk[SHA512_DIGEST_LENGTH];
+	u_char *key;
+	size_t keylen;
+	size_t i;
+
+	if (!state)
+		panic("ah_hmac_sha2_512_init: what?");
+
+	state->sav = sav;
+	state->foo = (void *)malloc(64 + 64 + sizeof(SHA512_CTX),
+	    M_TEMP, M_NOWAIT);
+	if (!state->foo)
+		return ENOBUFS;
+	bzero(state->foo, 64 + 64 + sizeof(SHA512_CTX));
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA512_CTX *)(opad + 64);
+
+	/* compress the key if necessery */
+	if (64 < _KEYLEN(state->sav->key_auth)) {
+		bzero(tk, sizeof(tk));
+		bzero(ctxt, sizeof(*ctxt));
+		SHA512_Init(ctxt);
+		SHA512_Update(ctxt, _KEYBUF(state->sav->key_auth),
+		    _KEYLEN(state->sav->key_auth));
+		SHA512_Final(&tk[0], ctxt);
+		key = &tk[0];
+		keylen = sizeof(tk) < 64 ? sizeof(tk) : 64;
+	} else {
+		key = _KEYBUF(state->sav->key_auth);
+		keylen = _KEYLEN(state->sav->key_auth);
+	}
+
+	bzero(ipad, 64);
+	bzero(opad, 64);
+	bcopy(key, ipad, keylen);
+	bcopy(key, opad, keylen);
+	for (i = 0; i < 64; i++) {
+		ipad[i] ^= 0x36;
+		opad[i] ^= 0x5c;
+	}
+
+	bzero(ctxt, sizeof(*ctxt));
+	SHA512_Init(ctxt);
+	SHA512_Update(ctxt, ipad, 64);
+
+	return 0;
+}
+
+static void
+ah_hmac_sha2_512_loop(state, addr, len)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+	size_t len;
+{
+	SHA512_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha2_512_loop: what?");
+
+	ctxt = (SHA512_CTX *)(((u_char *)state->foo) + 128);
+	SHA512_Update(ctxt, (caddr_t)addr, (size_t)len);
+}
+
+static void
+ah_hmac_sha2_512_result(state, addr)
+	struct ah_algorithm_state *state;
+	caddr_t addr;
+{
+	u_char digest[SHA512_DIGEST_LENGTH];
+	u_char *ipad;
+	u_char *opad;
+	SHA512_CTX *ctxt;
+
+	if (!state || !state->foo)
+		panic("ah_hmac_sha2_512_result: what?");
+
+	ipad = (u_char *)state->foo;
+	opad = (u_char *)(ipad + 64);
+	ctxt = (SHA512_CTX *)(opad + 64);
+
+	SHA512_Final((caddr_t)&digest[0], ctxt);
+
+	bzero(ctxt, sizeof(*ctxt));
+	SHA512_Init(ctxt);
+	SHA512_Update(ctxt, opad, 64);
+	SHA512_Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
+	SHA512_Final((caddr_t)&digest[0], ctxt);
+
+	bcopy(&digest[0], (void *)addr, HMACSIZE);
+
+	free(state->foo, M_TEMP);
+}
+
+/*------------------------------------------------------------*/
+
+/*
+ * go generate the checksum.
+ */
+static void
+ah_update_mbuf(m, off, len, algo, algos)
+	struct mbuf *m;
+	int off;
+	int len;
+	const struct ah_algorithm *algo;
+	struct ah_algorithm_state *algos;
+{
+	struct mbuf *n;
+	int tlen;
+
+	/* easy case first */
+	if (off + len <= m->m_len) {
+		(algo->update)(algos, mtod(m, caddr_t) + off, len);
+		return;
+	}
+
+	for (n = m; n; n = n->m_next) {
+		if (off < n->m_len)
+			break;
+
+		off -= n->m_len;
+	}
+
+	if (!n)
+		panic("ah_update_mbuf: wrong offset specified");
+
+	for (/* nothing */; n && len > 0; n = n->m_next) {
+		if (n->m_len == 0)
+			continue;
+		if (n->m_len - off < len)
+			tlen = n->m_len - off;
+		else
+			tlen = len;
+
+		(algo->update)(algos, mtod(n, caddr_t) + off, tlen);
+
+		len -= tlen;
+		off = 0;
+	}
+}
+
+#ifdef INET
+/*
+ * Go generate the checksum. This function won't modify the mbuf chain
+ * except AH itself.
+ *
+ * NOTE: the function does not free mbuf on failure.
+ * Don't use m_copy(), it will try to share cluster mbuf by using refcnt.
+ */
+int
+ah4_calccksum(m, ahdat, len, algo, sav)
+	struct mbuf *m;
+	caddr_t ahdat;
+	size_t len;
+	const struct ah_algorithm *algo;
+	struct secasvar *sav;
+{
+	int off;
+	int hdrtype;
+	size_t advancewidth;
+	struct ah_algorithm_state algos;
+	u_char sumbuf[AH_MAXSUMSIZE];
+	int error = 0;
+	int ahseen;
+	struct mbuf *n = NULL;
+
+	if ((m->m_flags & M_PKTHDR) == 0)
+		return EINVAL;
+
+	ahseen = 0;
+	hdrtype = -1;	/* dummy, it is called IPPROTO_IP */
+
+	off = 0;
+
+	error = (algo->init)(&algos, sav);
+	if (error)
+		return error;
+
+	advancewidth = 0;	/* safety */
+
+again:
+	/* gory. */
+	switch (hdrtype) {
+	case -1:	/* first one only */
+	    {
+		/*
+		 * copy ip hdr, modify to fit the AH checksum rule,
+		 * then take a checksum.
+		 */
+		struct ip iphdr;
+		size_t hlen;
+
+		m_copydata(m, off, sizeof(iphdr), (caddr_t)&iphdr);
+#ifdef _IP_VHL
+		hlen = IP_VHL_HL(iphdr.ip_vhl) << 2;
+#else
+		hlen = iphdr.ip_hl << 2;
+#endif
+		iphdr.ip_ttl = 0;
+		iphdr.ip_sum = htons(0);
+		if (ip4_ah_cleartos)
+			iphdr.ip_tos = 0;
+		iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask);
+		(algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip));
+
+		if (hlen != sizeof(struct ip)) {
+			u_char *p;
+			int i, l, skip;
+
+			if (hlen > MCLBYTES) {
+				error = EMSGSIZE;
+				goto fail;
+			}
+			MGET(n, M_DONTWAIT, MT_DATA);
+			if (n && hlen > MLEN) {
+				MCLGET(n, M_DONTWAIT);
+				if ((n->m_flags & M_EXT) == 0) {
+					m_free(n);
+					n = NULL;
+				}
+			}
+			if (n == NULL) {
+				error = ENOBUFS;
+				goto fail;
+			}
+			m_copydata(m, off, hlen, mtod(n, caddr_t));
+
+			/*
+			 * IP options processing.
+			 * See RFC2402 appendix A.
+			 */
+			p = mtod(n, u_char *);
+			i = sizeof(struct ip);
+			while (i < hlen) {
+				if (i + IPOPT_OPTVAL >= hlen) {
+					ipseclog((LOG_ERR, "ah4_calccksum: "
+					    "invalid IP option\n"));
+					error = EINVAL;
+					goto fail;
+				}
+				if (p[i + IPOPT_OPTVAL] == IPOPT_EOL ||
+				    p[i + IPOPT_OPTVAL] == IPOPT_NOP ||
+				    i + IPOPT_OLEN < hlen)
+					;
+				else {
+					ipseclog((LOG_ERR,
+					    "ah4_calccksum: invalid IP option "
+					    "(type=%02x)\n",
+					    p[i + IPOPT_OPTVAL]));
+					error = EINVAL;
+					goto fail;
+				}
+
+				skip = 1;
+				switch (p[i + IPOPT_OPTVAL]) {
+				case IPOPT_EOL:
+				case IPOPT_NOP:
+					l = 1;
+					skip = 0;
+					break;
+				case IPOPT_SECURITY:	/* 0x82 */
+				case 0x85:	/* Extended security */
+				case 0x86:	/* Commercial security */
+				case 0x94:	/* Router alert */
+				case 0x95:	/* RFC1770 */
+					l = p[i + IPOPT_OLEN];
+					if (l < 2)
+						goto invalopt;
+					skip = 0;
+					break;
+				default:
+					l = p[i + IPOPT_OLEN];
+					if (l < 2)
+						goto invalopt;
+					skip = 1;
+					break;
+				}
+				if (l < 1 || hlen - i < l) {
+			invalopt:
+					ipseclog((LOG_ERR,
+					    "ah4_calccksum: invalid IP option "
+					    "(type=%02x len=%02x)\n",
+					    p[i + IPOPT_OPTVAL],
+					    p[i + IPOPT_OLEN]));
+					error = EINVAL;
+					goto fail;
+				}
+				if (skip)
+					bzero(p + i, l);
+				if (p[i + IPOPT_OPTVAL] == IPOPT_EOL)
+					break;
+				i += l;
+			}
+			p = mtod(n, u_char *) + sizeof(struct ip);
+			(algo->update)(&algos, p, hlen - sizeof(struct ip));
+
+			m_free(n);
+			n = NULL;
+		}
+
+		hdrtype = (iphdr.ip_p) & 0xff;
+		advancewidth = hlen;
+		break;
+	    }
+
+	case IPPROTO_AH:
+	    {
+		struct ah ah;
+		int siz;
+		int hdrsiz;
+		int totlen;
+
+		m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
+		hdrsiz = (sav->flags & SADB_X_EXT_OLD)
+				? sizeof(struct ah)
+				: sizeof(struct newah);
+		siz = (*algo->sumsiz)(sav);
+		totlen = (ah.ah_len + 2) << 2;
+
+		/*
+		 * special treatment is necessary for the first one, not others
+		 */
+		if (!ahseen) {
+			if (totlen > m->m_pkthdr.len - off ||
+			    totlen > MCLBYTES) {
+				error = EMSGSIZE;
+				goto fail;
+			}
+			MGET(n, M_DONTWAIT, MT_DATA);
+			if (n && totlen > MLEN) {
+				MCLGET(n, M_DONTWAIT);
+				if ((n->m_flags & M_EXT) == 0) {
+					m_free(n);
+					n = NULL;
+				}
+			}
+			if (n == NULL) {
+				error = ENOBUFS;
+				goto fail;
+			}
+			m_copydata(m, off, totlen, mtod(n, caddr_t));
+			n->m_len = totlen;
+			bzero(mtod(n, caddr_t) + hdrsiz, siz);
+			(algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+			m_free(n);
+			n = NULL;
+		} else
+			ah_update_mbuf(m, off, totlen, algo, &algos);
+		ahseen++;
+
+		hdrtype = ah.ah_nxt;
+		advancewidth = totlen;
+		break;
+	    }
+
+	default:
+		ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo, &algos);
+		advancewidth = m->m_pkthdr.len - off;
+		break;
+	}
+
+	off += advancewidth;
+	if (off < m->m_pkthdr.len)
+		goto again;
+
+	if (len < (*algo->sumsiz)(sav)) {
+		error = EINVAL;
+		goto fail;
+	}
+
+	(algo->result)(&algos, &sumbuf[0]);
+	bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
+
+	if (n)
+		m_free(n);
+	return error;
+
+fail:
+	if (n)
+		m_free(n);
+	return error;
+}
+#endif
+
+#ifdef INET6
+/*
+ * Go generate the checksum. This function won't modify the mbuf chain
+ * except AH itself.
+ *
+ * NOTE: the function does not free mbuf on failure.
+ * Don't use m_copy(), it will try to share cluster mbuf by using refcnt.
+ */
+int
+ah6_calccksum(m, ahdat, len, algo, sav)
+	struct mbuf *m;
+	caddr_t ahdat;
+	size_t len;
+	const struct ah_algorithm *algo;
+	struct secasvar *sav;
+{
+	int newoff, off;
+	int proto, nxt;
+	struct mbuf *n = NULL;
+	int error;
+	int ahseen;
+	struct ah_algorithm_state algos;
+	u_char sumbuf[AH_MAXSUMSIZE];
+
+	if ((m->m_flags & M_PKTHDR) == 0)
+		return EINVAL;
+
+	error = (algo->init)(&algos, sav);
+	if (error)
+		return error;
+
+	off = 0;
+	proto = IPPROTO_IPV6;
+	nxt = -1;
+	ahseen = 0;
+
+ again:
+	newoff = ip6_nexthdr(m, off, proto, &nxt);
+	if (newoff < 0)
+		newoff = m->m_pkthdr.len;
+	else if (newoff <= off) {
+		error = EINVAL;
+		goto fail;
+	}
+
+	switch (proto) {
+	case IPPROTO_IPV6:
+		/*
+		 * special treatment is necessary for the first one, not others
+		 */
+		if (off == 0) {
+			struct ip6_hdr ip6copy;
+
+			if (newoff - off != sizeof(struct ip6_hdr)) {
+				error = EINVAL;
+				goto fail;
+			}
+
+			m_copydata(m, off, newoff - off, (caddr_t)&ip6copy);
+			/* RFC2402 */
+			ip6copy.ip6_flow = 0;
+			ip6copy.ip6_vfc &= ~IPV6_VERSION_MASK;
+			ip6copy.ip6_vfc |= IPV6_VERSION;
+			ip6copy.ip6_hlim = 0;
+			if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src))
+				ip6copy.ip6_src.s6_addr16[1] = 0x0000;
+			if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst))
+				ip6copy.ip6_dst.s6_addr16[1] = 0x0000;
+			(algo->update)(&algos, (caddr_t)&ip6copy,
+				       sizeof(struct ip6_hdr));
+		} else {
+			newoff = m->m_pkthdr.len;
+			ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo,
+			    &algos);
+		}
+		break;
+
+	case IPPROTO_AH:
+	    {
+		int siz;
+		int hdrsiz;
+
+		hdrsiz = (sav->flags & SADB_X_EXT_OLD)
+				? sizeof(struct ah)
+				: sizeof(struct newah);
+		siz = (*algo->sumsiz)(sav);
+
+		/*
+		 * special treatment is necessary for the first one, not others
+		 */
+		if (!ahseen) {
+			if (newoff - off > MCLBYTES) {
+				error = EMSGSIZE;
+				goto fail;
+			}
+			MGET(n, M_DONTWAIT, MT_DATA);
+			if (n && newoff - off > MLEN) {
+				MCLGET(n, M_DONTWAIT);
+				if ((n->m_flags & M_EXT) == 0) {
+					m_free(n);
+					n = NULL;
+				}
+			}
+			if (n == NULL) {
+				error = ENOBUFS;
+				goto fail;
+			}
+			m_copydata(m, off, newoff - off, mtod(n, caddr_t));
+			n->m_len = newoff - off;
+			bzero(mtod(n, caddr_t) + hdrsiz, siz);
+			(algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+			m_free(n);
+			n = NULL;
+		} else
+			ah_update_mbuf(m, off, newoff - off, algo, &algos);
+		ahseen++;
+		break;
+	    }
+
+	 case IPPROTO_HOPOPTS:
+	 case IPPROTO_DSTOPTS:
+	 {
+		struct ip6_ext *ip6e;
+		int hdrlen, optlen;
+		u_int8_t *p, *optend, *optp;
+
+		if (newoff - off > MCLBYTES) {
+			error = EMSGSIZE;
+			goto fail;
+		}
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (n && newoff - off > MLEN) {
+			MCLGET(n, M_DONTWAIT);
+			if ((n->m_flags & M_EXT) == 0) {
+				m_free(n);
+				n = NULL;
+			}
+		}
+		if (n == NULL) {
+			error = ENOBUFS;
+			goto fail;
+		}
+		m_copydata(m, off, newoff - off, mtod(n, caddr_t));
+		n->m_len = newoff - off;
+
+		ip6e = mtod(n, struct ip6_ext *);
+		hdrlen = (ip6e->ip6e_len + 1) << 3;
+		if (newoff - off < hdrlen) {
+			 error = EINVAL;
+			 m_free(n);
+			 n = NULL;
+			 goto fail;
+		}
+		p = mtod(n, u_int8_t *);
+		optend = p + hdrlen;
+
+		/*
+		 * ICV calculation for the options header including all
+		 * options.  This part is a little tricky since there are
+		 * two type of options; mutable and immutable.  We try to
+		 * null-out mutable ones here.
+		 */
+		optp = p + 2;
+		while (optp < optend) {
+			if (optp[0] == IP6OPT_PAD1)
+				optlen = 1;
+			else {
+				if (optp + 2 > optend) {
+					error = EINVAL;
+					m_free(n);
+					n = NULL;
+					goto fail;
+				}
+				optlen = optp[1] + 2;
+			}
+
+			if (optp + optlen > optend) {
+				error = EINVAL;
+				m_free(n);
+				n = NULL;
+				goto fail;
+			}
+
+			if (optp[0] & IP6OPT_MUTABLE)
+				bzero(optp + 2, optlen - 2);
+
+			optp += optlen;
+		}
+
+		(algo->update)(&algos, mtod(n, caddr_t), n->m_len);
+		m_free(n);
+		n = NULL;
+		break;
+	 }
+
+	 case IPPROTO_ROUTING:
+		/*
+		 * For an input packet, we can just calculate `as is'.
+		 * For an output packet, we assume ip6_output have already
+		 * made packet how it will be received at the final
+		 * destination.
+		 */
+		/* FALLTHROUGH */
+
+	default:
+		ah_update_mbuf(m, off, newoff - off, algo, &algos);
+		break;
+	}
+
+	if (newoff < m->m_pkthdr.len) {
+		proto = nxt;
+		off = newoff;
+		goto again;
+	}
+
+	if (len < (*algo->sumsiz)(sav)) {
+		error = EINVAL;
+		goto fail;
+	}
+
+	(algo->result)(&algos, &sumbuf[0]);
+	bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
+
+	/* just in case */
+	if (n)
+		m_free(n);
+	return 0;
+fail:
+	/* just in case */
+	if (n)
+		m_free(n);
+	return error;
+}
+#endif
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ah_input.c
@@ -0,0 +1,1145 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ah_input.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ah_input.c,v 1.66 2001/12/07 07:07:08 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_ecn.h>
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 105080000	/* 1.5H */
+#include <netinet/ip_icmp.h>
+#endif
+
+#include <netinet/ip6.h>
+
+#ifdef INET6
+#include <netinet6/ip6_var.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6protosw.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+
+#define IPLEN_FLIPPED
+
+#ifdef INET
+extern struct protosw inetsw[];
+#if defined(__bsdi__) || defined(__NetBSD__)
+extern u_char ip_protox[];
+#endif
+
+void
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
+ah4_input(m, off)
+	struct mbuf *m;
+	int off;
+#else
+#if __STDC__
+ah4_input(struct mbuf *m, ...)
+#else
+ah4_input(m, va_alist)
+	struct mbuf *m;
+	va_dcl
+#endif
+#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+{
+	struct ip *ip;
+	struct ah *ah;
+	u_int32_t spi;
+	const struct ah_algorithm *algo;
+	size_t siz;
+	size_t siz1;
+	u_char *cksum;
+	struct secasvar *sav = NULL;
+	u_int16_t nxt;
+	size_t hlen;
+	int s;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+	int off, proto;
+	va_list ap;
+#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+	size_t stripsiz = 0;
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+	va_start(ap, m);
+	off = va_arg(ap, int);
+	proto = va_arg(ap, int);
+	va_end(ap);
+#endif
+
+#ifndef PULLDOWN_TEST
+	if (m->m_len < off + sizeof(struct newah)) {
+		m = m_pullup(m, off + sizeof(struct newah));
+		if (!m) {
+			ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
+				"dropping the packet for simplicity\n"));
+			ipsecstat.in_inval++;
+			goto fail;
+		}
+	}
+
+	ip = mtod(m, struct ip *);
+	ah = (struct ah *)(((caddr_t)ip) + off);
+#else
+	ip = mtod(m, struct ip *);
+	IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
+	if (ah == NULL) {
+		ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup;"
+			"dropping the packet for simplicity\n"));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+#endif
+	nxt = ah->ah_nxt;
+#ifdef _IP_VHL
+	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	hlen = ip->ip_hl << 2;
+#endif
+
+	/* find the sassoc. */
+	spi = ah->ah_spi;
+
+	if ((sav = key_allocsa(AF_INET,
+	                      (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+	                      IPPROTO_AH, spi)) == 0) {
+		ipseclog((LOG_WARNING,
+		    "IPv4 AH input: no key association found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsecstat.in_nosa++;
+		goto fail;
+	}
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ah4_input called to allocate SA:%p\n", sav));
+	if (sav->state != SADB_SASTATE_MATURE
+	 && sav->state != SADB_SASTATE_DYING) {
+		ipseclog((LOG_DEBUG,
+		    "IPv4 AH input: non-mature/dying SA found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsecstat.in_badspi++;
+		goto fail;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_DEBUG, "IPv4 AH input: "
+		    "unsupported authentication algorithm for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsecstat.in_badspi++;
+		goto fail;
+	}
+
+	siz = (*algo->sumsiz)(sav);
+	siz1 = ((siz + 3) & ~(4 - 1));
+
+	/*
+	 * sanity checks for header, 1.
+	 */
+    {
+	int sizoff;
+
+	sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+	/*
+	 * Here, we do not do "siz1 == siz".  This is because the way
+	 * RFC240[34] section 2 is written.  They do not require truncation
+	 * to 96 bits.
+	 * For example, Microsoft IPsec stack attaches 160 bits of
+	 * authentication data for both hmac-md5 and hmac-sha1.  For hmac-sha1,
+	 * 32 bits of padding is attached.
+	 *
+	 * There are two downsides to this specification.
+	 * They have no real harm, however, they leave us fuzzy feeling.
+	 * - if we attach more than 96 bits of authentication data onto AH,
+	 *   we will never notice about possible modification by rogue
+	 *   intermediate nodes.
+	 *   Since extra bits in AH checksum is never used, this constitutes
+	 *   no real issue, however, it is wacky.
+	 * - even if the peer attaches big authentication data, we will never
+	 *   notice the difference, since longer authentication data will just
+	 *   work.
+	 *
+	 * We may need some clarification in the spec.
+	 */
+	if (siz1 < siz) {
+		ipseclog((LOG_NOTICE, "sum length too short in IPv4 AH input "
+		    "(%lu, should be at least %lu): %s\n",
+		    (u_long)siz1, (u_long)siz,
+		    ipsec4_logpacketstr(ip, spi)));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+	if ((ah->ah_len << 2) - sizoff != siz1) {
+		ipseclog((LOG_NOTICE, "sum length mismatch in IPv4 AH input "
+		    "(%d should be %lu): %s\n",
+		    (ah->ah_len << 2) - sizoff, (u_long)siz1,
+		    ipsec4_logpacketstr(ip, spi)));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+
+#ifndef PULLDOWN_TEST
+	if (m->m_len < off + sizeof(struct ah) + sizoff + siz1) {
+		m = m_pullup(m, off + sizeof(struct ah) + sizoff + siz1);
+		if (!m) {
+			ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
+			ipsecstat.in_inval++;
+			goto fail;
+		}
+
+		ip = mtod(m, struct ip *);
+		ah = (struct ah *)(((caddr_t)ip) + off);
+	}
+#else
+	IP6_EXTHDR_GET(ah, struct ah *, m, off,
+		sizeof(struct ah) + sizoff + siz1);
+	if (ah == NULL) {
+		ipseclog((LOG_DEBUG, "IPv4 AH input: can't pullup\n"));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+#endif
+    }
+
+	/*
+	 * check for sequence number.
+	 */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+		if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
+			; /* okey */
+		else {
+			ipsecstat.in_ahreplay++;
+			ipseclog((LOG_WARNING,
+			    "replay packet in IPv4 AH input: %s %s\n",
+			    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+			goto fail;
+		}
+	}
+
+	/*
+	 * alright, it seems sane.  now we are going to check the
+	 * cryptographic checksum.
+	 */
+	cksum = malloc(siz1, M_TEMP, M_NOWAIT);
+	if (!cksum) {
+		ipseclog((LOG_DEBUG, "IPv4 AH input: "
+		    "couldn't alloc temporary region for cksum\n"));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+	
+	/*
+	 * some of IP header fields are flipped to the host endian.
+	 * convert them back to network endian.  VERY stupid.
+	 */
+#ifdef WITH_CONVERT_AND_STRIP_IP_LEN
+	ip->ip_len = htons(ip->ip_len + hlen);
+#elif defined(WITH_CONVERT_IP_LEN)
+	ip->ip_len = htons(ip->ip_len);
+#endif
+#ifdef WITH_CONVERT_IP_ID
+	ip->ip_id = htons(ip->ip_id);
+#endif
+#ifdef WITH_CONVERT_IP_OFF
+	ip->ip_off = htons(ip->ip_off);
+#endif
+	if (ah4_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
+		free(cksum, M_TEMP);
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+	ipsecstat.in_ahhist[sav->alg_auth]++;
+	/*
+	 * flip them back.
+	 */
+#ifdef WITH_CONVERT_AND_STRIP_IP_LEN
+	ip->ip_len = ntohs(ip->ip_len) - hlen;
+#elif defined(WITH_CONVERT_IP_LEN)
+	ip->ip_len = ntohs(ip->ip_len);
+#endif
+#ifdef WITH_CONVERT_IP_ID
+	ip->ip_id = ntohs(ip->ip_id);
+#endif
+#ifdef WITH_CONVERT_IP_OFF
+	ip->ip_off = ntohs(ip->ip_off);
+#endif
+
+    {
+	caddr_t sumpos = NULL;
+
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1826 */
+		sumpos = (caddr_t)(ah + 1);
+	} else {
+		/* RFC 2402 */
+		sumpos = (caddr_t)(((struct newah *)ah) + 1);
+	}
+
+	if (bcmp(sumpos, cksum, siz) != 0) {
+		ipseclog((LOG_WARNING,
+		    "checksum mismatch in IPv4 AH input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		free(cksum, M_TEMP);
+		ipsecstat.in_ahauthfail++;
+		goto fail;
+	}
+    }
+
+	free(cksum, M_TEMP);
+
+	m->m_flags |= M_AUTHIPHDR;
+	m->m_flags |= M_AUTHIPDGM;
+
+#if 0
+	/*
+	 * looks okey, but we need more sanity check.
+	 * XXX should elaborate.
+	 */
+	if (ah->ah_nxt == IPPROTO_IPIP || ah->ah_nxt == IPPROTO_IP) {
+		struct ip *nip;
+		size_t sizoff;
+
+		sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+		if (m->m_len < off + sizeof(struct ah) + sizoff + siz1 + hlen) {
+			m = m_pullup(m, off + sizeof(struct ah)
+					+ sizoff + siz1 + hlen);
+			if (!m) {
+				ipseclog((LOG_DEBUG,
+				    "IPv4 AH input: can't pullup\n"));
+				ipsecstat.in_inval++;
+				goto fail;
+			}
+		}
+
+		nip = (struct ip *)((u_char *)(ah + 1) + sizoff + siz1);
+		if (nip->ip_src.s_addr != ip->ip_src.s_addr
+		 || nip->ip_dst.s_addr != ip->ip_dst.s_addr) {
+			m->m_flags &= ~M_AUTHIPHDR;
+			m->m_flags &= ~M_AUTHIPDGM;
+		}
+	}
+#ifdef INET6
+	else if (ah->ah_nxt == IPPROTO_IPV6) {
+		m->m_flags &= ~M_AUTHIPHDR;
+		m->m_flags &= ~M_AUTHIPDGM;
+	}
+#endif /* INET6 */
+#endif /* 0 */
+
+	if (m->m_flags & M_AUTHIPHDR
+	 && m->m_flags & M_AUTHIPDGM) {
+#if 0
+		ipseclog((LOG_DEBUG,
+		    "IPv4 AH input: authentication succeess\n"));
+#endif
+		ipsecstat.in_ahauthsucc++;
+	} else {
+		ipseclog((LOG_WARNING,
+		    "authentication failed in IPv4 AH input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		ipsecstat.in_ahauthfail++;
+		goto fail;
+	}
+
+	/*
+	 * update sequence number.
+	 */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+		if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
+			ipsecstat.in_ahreplay++;
+			goto fail;
+		}
+	}
+
+	/* was it transmitted over the IPsec tunnel SA? */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1826 */
+		stripsiz = sizeof(struct ah) + siz1;
+	} else {
+		/* RFC 2402 */
+		stripsiz = sizeof(struct newah) + siz1;
+	}
+	if (!ipsec_tunnel_device &&
+	    ipsec4_tunnel_validate(m, off + stripsiz, nxt, sav)) {
+		/*
+		 * strip off all the headers that precedes AH.
+		 *	IP xx AH IP' payload -> IP' payload
+		 *
+		 * XXX more sanity checks
+		 * XXX relationship with gif?
+		 */
+		u_int8_t tos;
+
+		tos = ip->ip_tos;
+		m_adj(m, off + stripsiz);
+		if (m->m_len < sizeof(*ip)) {
+			m = m_pullup(m, sizeof(*ip));
+			if (!m) {
+				ipsecstat.in_inval++;
+				goto fail;
+			}
+		}
+		ip = mtod(m, struct ip *);
+		/* ECN consideration. */
+		ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
+		if (!key_checktunnelsanity(sav, AF_INET,
+			    (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
+			ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
+			    "in IPv4 AH input: %s %s\n",
+			    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+			ipsecstat.in_inval++;
+			goto fail;
+		}
+
+#if 1
+		/*
+		 * Should the inner packet be considered authentic?
+		 * My current answer is: NO.
+		 *
+		 * host1 -- gw1 === gw2 -- host2
+		 *	In this case, gw2 can trust the	authenticity of the
+		 *	outer packet, but NOT inner.  Packet may be altered
+		 *	between host1 and gw1.
+		 *
+		 * host1 -- gw1 === host2
+		 *	This case falls into the same scenario as above.
+		 *
+		 * host1 === host2
+		 *	This case is the only case when we may be able to leave
+		 *	M_AUTHIPHDR and M_AUTHIPDGM set.
+		 *	However, if host1 is wrongly configured, and allows
+		 *	attacker to inject some packet with src=host1 and
+		 *	dst=host2, you are in risk.
+		 */
+		m->m_flags &= ~M_AUTHIPHDR;
+		m->m_flags &= ~M_AUTHIPDGM;
+#endif
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
+		    ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
+			ipsecstat.in_nomem++;
+			goto fail;
+		}
+
+		s = splimp();
+		if (IF_QFULL(&ipintrq)) {
+			ipsecstat.in_inval++;
+			splx(s);
+			goto fail;
+		}
+		IF_ENQUEUE(&ipintrq, m);
+		m = NULL;
+		schednetisr(NETISR_IP);	/* can be skipped but to make sure */
+		splx(s);
+		nxt = IPPROTO_DONE;
+	} else {
+		/*
+		 * strip off AH.
+		 */
+
+		ip = mtod(m, struct ip *);
+#ifndef PULLDOWN_TEST
+		/*
+		 * We do deep-copy since KAME requires that
+		 * the packet is placed in a single external mbuf.
+		 */
+		ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
+		m->m_data += stripsiz;
+		m->m_len -= stripsiz;
+		m->m_pkthdr.len -= stripsiz;
+#else
+		/*
+		 * even in m_pulldown case, we need to strip off AH so that
+		 * we can compute checksum for multiple AH correctly.
+		 */
+		if (m->m_len >= stripsiz + off) {
+			ovbcopy((caddr_t)ip, ((caddr_t)ip) + stripsiz, off);
+			m->m_data += stripsiz;
+			m->m_len -= stripsiz;
+			m->m_pkthdr.len -= stripsiz;
+		} else {
+			/*
+			 * this comes with no copy if the boundary is on
+			 * cluster
+			 */
+			struct mbuf *n;
+
+			n = m_split(m, off, M_DONTWAIT);
+			if (n == NULL) {
+				/* m is retained by m_split */
+				goto fail;
+			}
+			m_adj(n, stripsiz);
+			m_cat(m, n);
+			/* m_cat does not update m_pkthdr.len */
+			m->m_pkthdr.len += n->m_pkthdr.len;
+		}
+#endif
+
+		if (m->m_len < sizeof(*ip)) {
+			m = m_pullup(m, sizeof(*ip));
+			if (m == NULL) {
+				ipsecstat.in_inval++;
+				goto fail;
+			}
+		}
+		ip = mtod(m, struct ip *);
+#ifdef IPLEN_FLIPPED
+		ip->ip_len = ip->ip_len - stripsiz;
+#else
+		ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
+#endif
+		ip->ip_p = nxt;
+		/* forget about IP hdr checksum, the check has already been passed */
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
+			ipsecstat.in_nomem++;
+			goto fail;
+		}
+
+		if (nxt != IPPROTO_DONE) {
+			if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
+			    ipsec4_in_reject(m, NULL)) {
+				ipsecstat.in_polvio++;
+				goto fail;
+			}
+#if defined(__FreeBSD__) && __FreeBSD__ >= 4
+			(*inetsw[ip_protox[nxt]].pr_input)(m, off);
+#else
+			(*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
+#endif
+		} else
+			m_freem(m);
+		m = NULL;
+	}
+
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ah4_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	ipsecstat.in_success++;
+	return;
+
+fail:
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ah4_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	if (m)
+		m_freem(m);
+	return;
+}
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 105080000	/* 1.5H */
+/* assumes that ip header and ah header are contiguous on mbuf */
+void *
+ah4_ctlinput(cmd, sa, v)
+	int cmd;
+	struct sockaddr *sa;
+	void *v;
+{
+	struct ip *ip = v;
+	struct ah *ah;
+	struct icmp *icp;
+	struct secasvar *sav;
+
+	if (sa->sa_family != AF_INET ||
+	    sa->sa_len != sizeof(struct sockaddr_in))
+		return NULL;
+	if ((unsigned)cmd >= PRC_NCMDS)
+		return NULL;
+	if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
+		/*
+		 * Check to see if we have a valid SA corresponding to
+		 * the address in the ICMP message payload.
+		 */
+		ah = (struct ah *)((caddr_t)ip + (ip->ip_hl << 2));
+		if ((sav = key_allocsa(AF_INET,
+				       (caddr_t) &ip->ip_src,
+				       (caddr_t) &ip->ip_dst,
+				       IPPROTO_AH, ah->ah_spi)) == NULL)
+			return NULL;
+		if (sav->state != SADB_SASTATE_MATURE &&
+		    sav->state != SADB_SASTATE_DYING) {
+			key_freesav(sav);
+			return NULL;
+		}
+
+		/* XXX Further validation? */
+
+		key_freesav(sav);
+
+		/*
+		 * Now that we've validated that we are actually communicating
+		 * with the host indicated in the ICMP message, locate the
+		 * ICMP header, recalculate the new MTU, and create the
+		 * corresponding routing entry.
+		 */
+		icp = (struct icmp *)((caddr_t)ip -
+		    offsetof(struct icmp, icmp_ip));
+		icmp_mtudisc(icp, ip->ip_dst);
+
+		return NULL;
+	}
+
+	return NULL;
+}
+#endif /* NetBSD 1.5H or later */
+#endif /* INET */
+
+#ifdef INET6
+int
+ah6_input(mp, offp, proto)
+	struct mbuf **mp;
+	int *offp, proto;
+{
+	struct mbuf *m = *mp;
+	int off = *offp;
+	struct ip6_hdr *ip6;
+	struct ah *ah;
+	u_int32_t spi;
+	const struct ah_algorithm *algo;
+	size_t siz;
+	size_t siz1;
+	u_char *cksum;
+	struct secasvar *sav = NULL;
+	u_int16_t nxt;
+	int s;
+	size_t stripsiz = 0;
+
+#ifndef PULLDOWN_TEST
+	IP6_EXTHDR_CHECK(m, off, sizeof(struct ah), IPPROTO_DONE);
+	ah = (struct ah *)(mtod(m, caddr_t) + off);
+#else
+	IP6_EXTHDR_GET(ah, struct ah *, m, off, sizeof(struct newah));
+	if (ah == NULL) {
+		ipseclog((LOG_DEBUG, "IPv6 AH input: can't pullup\n"));
+		ipsec6stat.in_inval++;
+		return IPPROTO_DONE;
+	}
+#endif
+	ip6 = mtod(m, struct ip6_hdr *);
+	nxt = ah->ah_nxt;
+
+	/* find the sassoc. */
+	spi = ah->ah_spi;
+
+	if (ntohs(ip6->ip6_plen) == 0) {
+		ipseclog((LOG_ERR, "IPv6 AH input: "
+		    "AH with IPv6 jumbogram is not supported.\n"));
+		ipsec6stat.in_inval++;
+		goto fail;
+	}
+
+	if ((sav = key_allocsa(AF_INET6,
+	                      (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
+	                      IPPROTO_AH, spi)) == 0) {
+		ipseclog((LOG_WARNING,
+		    "IPv6 AH input: no key association found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsec6stat.in_nosa++;
+		goto fail;
+	}
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ah6_input called to allocate SA:%p\n", sav));
+	if (sav->state != SADB_SASTATE_MATURE
+	 && sav->state != SADB_SASTATE_DYING) {
+		ipseclog((LOG_DEBUG,
+		    "IPv6 AH input: non-mature/dying SA found for spi %u; ",
+		    (u_int32_t)ntohl(spi)));
+		ipsec6stat.in_badspi++;
+		goto fail;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_DEBUG, "IPv6 AH input: "
+		    "unsupported authentication algorithm for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsec6stat.in_badspi++;
+		goto fail;
+	}
+
+	siz = (*algo->sumsiz)(sav);
+	siz1 = ((siz + 3) & ~(4 - 1));
+
+	/*
+	 * sanity checks for header, 1.
+	 */
+    {
+	int sizoff;
+
+	sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+	/*
+	 * Here, we do not do "siz1 == siz".  See ah4_input() for complete
+	 * description.
+	 */
+	if (siz1 < siz) {
+		ipseclog((LOG_NOTICE, "sum length too short in IPv6 AH input "
+		    "(%lu, should be at least %lu): %s\n",
+		    (u_long)siz1, (u_long)siz,
+		    ipsec6_logpacketstr(ip6, spi)));
+		ipsec6stat.in_inval++;
+		goto fail;
+	}
+	if ((ah->ah_len << 2) - sizoff != siz1) {
+		ipseclog((LOG_NOTICE, "sum length mismatch in IPv6 AH input "
+		    "(%d should be %lu): %s\n",
+		    (ah->ah_len << 2) - sizoff, (u_long)siz1,
+		    ipsec6_logpacketstr(ip6, spi)));
+		ipsec6stat.in_inval++;
+		goto fail;
+	}
+#ifndef PULLDOWN_TEST
+	IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1, IPPROTO_DONE);
+#else
+	IP6_EXTHDR_GET(ah, struct ah *, m, off,
+		sizeof(struct ah) + sizoff + siz1);
+	if (ah == NULL) {
+		ipseclog((LOG_NOTICE, "couldn't pullup gather IPv6 AH checksum part"));
+		ipsec6stat.in_inval++;
+		m = NULL;
+		goto fail;
+	}
+#endif
+    }
+
+	/*
+	 * check for sequence number.
+	 */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+		if (ipsec_chkreplay(ntohl(((struct newah *)ah)->ah_seq), sav))
+			; /* okey */
+		else {
+			ipsec6stat.in_ahreplay++;
+			ipseclog((LOG_WARNING,
+			    "replay packet in IPv6 AH input: %s %s\n",
+			    ipsec6_logpacketstr(ip6, spi),
+			    ipsec_logsastr(sav)));
+			goto fail;
+		}
+	}
+
+	/*
+	 * alright, it seems sane.  now we are going to check the
+	 * cryptographic checksum.
+	 */
+	cksum = malloc(siz1, M_TEMP, M_NOWAIT);
+	if (!cksum) {
+		ipseclog((LOG_DEBUG, "IPv6 AH input: "
+		    "couldn't alloc temporary region for cksum\n"));
+		ipsec6stat.in_inval++;
+		goto fail;
+	}
+	
+	if (ah6_calccksum(m, (caddr_t)cksum, siz1, algo, sav)) {
+		free(cksum, M_TEMP);
+		ipsec6stat.in_inval++;
+		goto fail;
+	}
+	ipsec6stat.in_ahhist[sav->alg_auth]++;
+
+    {
+	caddr_t sumpos = NULL;
+
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1826 */
+		sumpos = (caddr_t)(ah + 1);
+	} else {
+		/* RFC 2402 */
+		sumpos = (caddr_t)(((struct newah *)ah) + 1);
+	}
+
+	if (bcmp(sumpos, cksum, siz) != 0) {
+		ipseclog((LOG_WARNING,
+		    "checksum mismatch in IPv6 AH input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		free(cksum, M_TEMP);
+		ipsec6stat.in_ahauthfail++;
+		goto fail;
+	}
+    }
+
+	free(cksum, M_TEMP);
+
+	m->m_flags |= M_AUTHIPHDR;
+	m->m_flags |= M_AUTHIPDGM;
+
+#if 0
+	/*
+	 * looks okey, but we need more sanity check.
+	 * XXX should elaborate.
+	 */
+	if (ah->ah_nxt == IPPROTO_IPV6) {
+		struct ip6_hdr *nip6;
+		size_t sizoff;
+
+		sizoff = (sav->flags & SADB_X_EXT_OLD) ? 0 : 4;
+
+		IP6_EXTHDR_CHECK(m, off, sizeof(struct ah) + sizoff + siz1
+				+ sizeof(struct ip6_hdr), IPPROTO_DONE);
+
+		nip6 = (struct ip6_hdr *)((u_char *)(ah + 1) + sizoff + siz1);
+		if (!IN6_ARE_ADDR_EQUAL(&nip6->ip6_src, &ip6->ip6_src)
+		 || !IN6_ARE_ADDR_EQUAL(&nip6->ip6_dst, &ip6->ip6_dst)) {
+			m->m_flags &= ~M_AUTHIPHDR;
+			m->m_flags &= ~M_AUTHIPDGM;
+		}
+	} else if (ah->ah_nxt == IPPROTO_IPIP) {
+		m->m_flags &= ~M_AUTHIPHDR;
+		m->m_flags &= ~M_AUTHIPDGM;
+	} else if (ah->ah_nxt == IPPROTO_IP) {
+		m->m_flags &= ~M_AUTHIPHDR;
+		m->m_flags &= ~M_AUTHIPDGM;
+	}
+#endif
+
+	if (m->m_flags & M_AUTHIPHDR
+	 && m->m_flags & M_AUTHIPDGM) {
+#if 0
+		ipseclog((LOG_DEBUG,
+		    "IPv6 AH input: authentication succeess\n"));
+#endif
+		ipsec6stat.in_ahauthsucc++;
+	} else {
+		ipseclog((LOG_WARNING,
+		    "authentication failed in IPv6 AH input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		ipsec6stat.in_ahauthfail++;
+		goto fail;
+	}
+
+	/*
+	 * update sequence number.
+	 */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+		if (ipsec_updatereplay(ntohl(((struct newah *)ah)->ah_seq), sav)) {
+			ipsec6stat.in_ahreplay++;
+			goto fail;
+		}
+	}
+
+	/* was it transmitted over the IPsec tunnel SA? */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1826 */
+		stripsiz = sizeof(struct ah) + siz1;
+	} else {
+		/* RFC 2402 */
+		stripsiz = sizeof(struct newah) + siz1;
+	}
+	if (!ipsec_tunnel_device &&
+	    ipsec6_tunnel_validate(m, off + stripsiz, nxt, sav)) {
+		/*
+		 * strip off all the headers that precedes AH.
+		 *	IP6 xx AH IP6' payload -> IP6' payload
+		 *
+		 * XXX more sanity checks
+		 * XXX relationship with gif?
+		 */
+		u_int32_t flowinfo;	/* net endian */
+
+		flowinfo = ip6->ip6_flow;
+		m_adj(m, off + stripsiz);
+		if (m->m_len < sizeof(*ip6)) {
+			/*
+			 * m_pullup is prohibited in KAME IPv6 input processing
+			 * but there's no other way!
+			 */
+			m = m_pullup(m, sizeof(*ip6));
+			if (!m) {
+				ipsec6stat.in_inval++;
+				goto fail;
+			}
+		}
+		ip6 = mtod(m, struct ip6_hdr *);
+		/* ECN consideration. */
+		ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
+		if (!key_checktunnelsanity(sav, AF_INET6,
+			    (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
+			ipseclog((LOG_NOTICE, "ipsec tunnel address mismatch "
+			    "in IPv6 AH input: %s %s\n",
+			    ipsec6_logpacketstr(ip6, spi),
+			    ipsec_logsastr(sav)));
+			ipsec6stat.in_inval++;
+			goto fail;
+		}
+
+#if 1
+		/*
+		 * should the inner packet be considered authentic?
+		 * see comment in ah4_input().
+		 */
+		m->m_flags &= ~M_AUTHIPHDR;
+		m->m_flags &= ~M_AUTHIPDGM;
+#endif
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0 ||
+		    ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
+			ipsec6stat.in_nomem++;
+			goto fail;
+		}
+
+		s = splimp();
+		if (IF_QFULL(&ip6intrq)) {
+			ipsec6stat.in_inval++;
+			splx(s);
+			goto fail;
+		}
+		IF_ENQUEUE(&ip6intrq, m);
+		m = NULL;
+		schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
+		splx(s);
+		nxt = IPPROTO_DONE;
+	} else {
+		/*
+		 * strip off AH.
+		 */
+		char *prvnxtp;
+
+		/*
+		 * Copy the value of the next header field of AH to the
+		 * next header field of the previous header.
+		 * This is necessary because AH will be stripped off below.
+		 */
+		prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
+		*prvnxtp = nxt;
+
+		ip6 = mtod(m, struct ip6_hdr *);
+#ifndef PULLDOWN_TEST
+		/*
+		 * We do deep-copy since KAME requires that
+		 * the packet is placed in a single mbuf.
+		 */
+		ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+		m->m_data += stripsiz;
+		m->m_len -= stripsiz;
+		m->m_pkthdr.len -= stripsiz;
+#else
+		/*
+		 * even in m_pulldown case, we need to strip off AH so that
+		 * we can compute checksum for multiple AH correctly.
+		 */
+		if (m->m_len >= stripsiz + off) {
+			ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+			m->m_data += stripsiz;
+			m->m_len -= stripsiz;
+			m->m_pkthdr.len -= stripsiz;
+		} else {
+			/*
+			 * this comes with no copy if the boundary is on
+			 * cluster
+			 */
+			struct mbuf *n;
+
+			n = m_split(m, off, M_DONTWAIT);
+			if (n == NULL) {
+				/* m is retained by m_split */
+				goto fail;
+			}
+			m_adj(n, stripsiz);
+			m_cat(m, n);
+			/* m_cat does not update m_pkthdr.len */
+			m->m_pkthdr.len += n->m_pkthdr.len;
+		}
+#endif
+		ip6 = mtod(m, struct ip6_hdr *);
+		/* XXX jumbogram */
+		ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_AH, spi) != 0) {
+			ipsec6stat.in_nomem++;
+			goto fail;
+		}
+	}
+
+	*offp = off;
+	*mp = m;
+
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ah6_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	ipsec6stat.in_success++;
+	return nxt;
+
+fail:
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ah6_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	if (m)
+		m_freem(m);
+	return IPPROTO_DONE;
+}
+
+void
+ah6_ctlinput(cmd, sa, d)
+	int cmd;
+	struct sockaddr *sa;
+	void *d;
+{
+	const struct newah *ahp;
+	struct newah ah;
+	struct secasvar *sav;
+	struct ip6_hdr *ip6;
+	struct mbuf *m;
+	struct ip6ctlparam *ip6cp = NULL;
+	int off=0;
+	struct sockaddr_in6 sa6_src, sa6_dst;
+
+	if (sa->sa_family != AF_INET6 ||
+	    sa->sa_len != sizeof(struct sockaddr_in6))
+		return;
+	if ((unsigned)cmd >= PRC_NCMDS)
+		return;
+
+	/* if the parameter is from icmp6, decode it. */
+	if (d != NULL) {
+		ip6cp = (struct ip6ctlparam *)d;
+		m = ip6cp->ip6c_m;
+		ip6 = ip6cp->ip6c_ip6;
+		off = ip6cp->ip6c_off;
+	} else {
+		m = NULL;
+		ip6 = NULL;
+	}
+
+	if (ip6) {
+		/*
+		 * XXX: We assume that when ip6 is non NULL,
+		 * M and OFF are valid.
+		 */
+
+		/* check if we can safely examine src and dst ports */
+		if (m->m_pkthdr.len < off + sizeof(ah))
+			return;
+
+		if (m->m_len < off + sizeof(ah)) {
+			/*
+			 * this should be rare case,
+			 * so we compromise on this copy...
+			 */
+			m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
+			ahp = &ah;
+		} else
+			ahp = (struct newah *)(mtod(m, caddr_t) + off);
+
+		if (cmd == PRC_MSGSIZE) {
+			int valid = 0;
+
+			/*
+			 * Check to see if we have a valid SA corresponding to
+			 * the address in the ICMP message payload.
+			 */
+			sav = key_allocsa(AF_INET6,
+					  (caddr_t)&sa6_src.sin6_addr,
+					  (caddr_t)&sa6_dst.sin6_addr,
+					  IPPROTO_AH, ahp->ah_spi);
+			if (sav) {
+				if (sav->state == SADB_SASTATE_MATURE ||
+				    sav->state == SADB_SASTATE_DYING)
+					valid++;
+				key_freesav(sav);
+			}
+
+			/* XXX Further validation? */
+
+			/*
+			 * Depending on the value of "valid" and routing table
+			 * size (mtudisc_{hi,lo}wat), we will:
+			 * - recalcurate the new MTU and create the
+			 *   corresponding routing entry, or
+			 * - ignore the MTU change notification.
+			 */
+			icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
+		}
+
+		/* we normally notify single pcb here */
+	} else {
+		/* we normally notify any pcb here */
+	}
+}
+#endif /* INET6 */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ah_output.c
@@ -0,0 +1,595 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ah_output.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ah_output.c,v 1.31 2001/07/26 06:53:15 jinmei Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1826/2402 authentication header.
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+#ifdef INET
+static struct in_addr *ah4_finaldst __P((struct mbuf *));
+#endif
+
+/*
+ * compute AH header size.
+ * transport mode only.  for tunnel mode, we should implement
+ * virtual interface, and control MTU/MSS by the interface MTU.
+ */
+size_t
+ah_hdrsiz(isr)
+	struct ipsecrequest *isr;
+{
+	const struct ah_algorithm *algo;
+	size_t hdrsiz;
+
+	/* sanity check */
+	if (isr == NULL)
+		panic("ah_hdrsiz: NULL was passed.\n");
+
+	if (isr->saidx.proto != IPPROTO_AH)
+		panic("unsupported mode passed to ah_hdrsiz");
+
+	if (isr->sav == NULL)
+		goto estimate;
+	if (isr->sav->state != SADB_SASTATE_MATURE
+	 && isr->sav->state != SADB_SASTATE_DYING)
+		goto estimate;
+
+	/* we need transport mode AH. */
+	algo = ah_algorithm_lookup(isr->sav->alg_auth);
+	if (!algo)
+		goto estimate;
+
+	/*
+	 * XXX
+	 * right now we don't calcurate the padding size.  simply
+	 * treat the padding size as constant, for simplicity.
+	 *
+	 * XXX variable size padding support
+	 */
+	hdrsiz = (((*algo->sumsiz)(isr->sav) + 3) & ~(4 - 1));
+	if (isr->sav->flags & SADB_X_EXT_OLD)
+		hdrsiz += sizeof(struct ah);
+	else
+		hdrsiz += sizeof(struct newah);
+
+	return hdrsiz;
+
+    estimate:
+	/* ASSUMING:
+	 *	sizeof(struct newah) > sizeof(struct ah).
+	 *	16 = (16 + 3) & ~(4 - 1).
+	 */
+	return sizeof(struct newah) + 16;
+}
+
+#ifdef INET
+/*
+ * Modify the packet so that it includes the authentication data.
+ * The mbuf passed must start with IPv4 header.
+ *
+ * assumes that the first mbuf contains IPv4 header + option only.
+ * the function does not modify m.
+ */
+int
+ah4_output(m, isr)
+	struct mbuf *m;
+	struct ipsecrequest *isr;
+{
+	struct secasvar *sav = isr->sav;
+	const struct ah_algorithm *algo;
+	u_int32_t spi;
+	u_char *ahdrpos;
+	u_char *ahsumpos = NULL;
+	size_t hlen = 0;	/* IP header+option in bytes */
+	size_t plen = 0;	/* AH payload size in bytes */
+	size_t ahlen = 0;	/* plen + sizeof(ah) */
+	struct ip *ip;
+	struct in_addr dst;
+	struct in_addr *finaldst;
+	int error;
+
+	/* sanity checks */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
+		struct ip *ip;
+
+		ip = mtod(m, struct ip *);
+		ipseclog((LOG_DEBUG, "ah4_output: internal error: "
+			"sav->replay is null: %x->%x, SPI=%u\n",
+			(u_int32_t)ntohl(ip->ip_src.s_addr),
+			(u_int32_t)ntohl(ip->ip_dst.s_addr),
+			(u_int32_t)ntohl(sav->spi)));
+		ipsecstat.out_inval++;
+		m_freem(m);
+		return EINVAL;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR, "ah4_output: unsupported algorithm: "
+		    "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
+		ipsecstat.out_inval++;
+		m_freem(m);
+		return EINVAL;
+	}
+	spi = sav->spi;
+
+	/*
+	 * determine the size to grow.
+	 */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1826 */
+		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
+		ahlen = plen + sizeof(struct ah);
+	} else {
+		/* RFC 2402 */
+		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1); /* XXX pad to 8byte? */
+		ahlen = plen + sizeof(struct newah);
+	}
+
+	/*
+	 * grow the mbuf to accomodate AH.
+	 */
+	ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	hlen = ip->ip_hl << 2;
+#endif
+
+	if (m->m_len != hlen)
+		panic("ah4_output: assumption failed (first mbuf length)");
+	if (M_LEADINGSPACE(m->m_next) < ahlen) {
+		struct mbuf *n;
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (!n) {
+			ipseclog((LOG_DEBUG, "ENOBUFS in ah4_output %d\n",
+			    __LINE__));
+			m_freem(m);
+			return ENOBUFS;
+		}
+		n->m_len = ahlen;
+		n->m_next = m->m_next;
+		m->m_next = n;
+		m->m_pkthdr.len += ahlen;
+		ahdrpos = mtod(n, u_char *);
+	} else {
+		m->m_next->m_len += ahlen;
+		m->m_next->m_data -= ahlen;
+		m->m_pkthdr.len += ahlen;
+		ahdrpos = mtod(m->m_next, u_char *);
+	}
+
+	ip = mtod(m, struct ip *);	/* just to be sure */
+
+	/*
+	 * initialize AH.
+	 */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		struct ah *ahdr;
+
+		ahdr = (struct ah *)ahdrpos;
+		ahsumpos = (u_char *)(ahdr + 1);
+		ahdr->ah_len = plen >> 2;
+		ahdr->ah_nxt = ip->ip_p;
+		ahdr->ah_reserve = htons(0);
+		ahdr->ah_spi = spi;
+		bzero(ahdr + 1, plen);
+	} else {
+		struct newah *ahdr;
+
+		ahdr = (struct newah *)ahdrpos;
+		ahsumpos = (u_char *)(ahdr + 1);
+		ahdr->ah_len = (plen >> 2) + 1;	/* plus one for seq# */
+		ahdr->ah_nxt = ip->ip_p;
+		ahdr->ah_reserve = htons(0);
+		ahdr->ah_spi = spi;
+		if (sav->replay->count == ~0) {
+			if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+				/* XXX Is it noisy ? */
+				ipseclog((LOG_WARNING,
+				    "replay counter overflowed. %s\n",
+				    ipsec_logsastr(sav)));
+				ipsecstat.out_inval++;
+				m_freem(m);
+				return EINVAL;
+			}
+		}
+		sav->replay->count++;
+		/*
+		 * XXX sequence number must not be cycled, if the SA is
+		 * installed by IKE daemon.
+		 */
+		ahdr->ah_seq = htonl(sav->replay->count);
+		bzero(ahdr + 1, plen);
+	}
+
+	/*
+	 * modify IPv4 header.
+	 */
+	ip->ip_p = IPPROTO_AH;
+	if (ahlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
+		ip->ip_len = htons(ntohs(ip->ip_len) + ahlen);
+	else {
+		ipseclog((LOG_ERR, "IPv4 AH output: size exceeds limit\n"));
+		ipsecstat.out_inval++;
+		m_freem(m);
+		return EMSGSIZE;
+	}
+
+	/*
+	 * If there is source routing option, update destination field in
+	 * the IPv4 header to the final destination.
+	 * Note that we do not need to update source routing option itself
+	 * (as done in IPv4 AH processing -- see ip6_output()), since
+	 * source routing option is not part of the ICV computation.
+	 */
+	finaldst = ah4_finaldst(m);
+	if (finaldst) {
+		dst.s_addr = ip->ip_dst.s_addr;
+		ip->ip_dst.s_addr = finaldst->s_addr;
+	}
+
+	/*
+	 * calcurate the checksum, based on security association
+	 * and the algorithm specified.
+	 */
+	error = ah4_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
+	if (error) {
+		ipseclog((LOG_ERR,
+		    "error after ah4_calccksum, called from ah4_output"));
+		m_freem(m);
+		m = NULL;
+		ipsecstat.out_inval++;
+		return error;
+	}
+
+	if (finaldst) {
+		ip = mtod(m, struct ip *);	/* just to make sure */
+		ip->ip_dst.s_addr = dst.s_addr;
+	}
+	ipsecstat.out_success++;
+	ipsecstat.out_ahhist[sav->alg_auth]++;
+	key_sa_recordxfer(sav, m);
+
+	return 0;
+}
+#endif
+
+/* Calculate AH length */
+int
+ah_hdrlen(sav)
+	struct secasvar *sav;
+{
+	const struct ah_algorithm *algo;
+	int plen, ahlen;
+	
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo)
+		return 0;
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1826 */
+		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1);	/* XXX pad to 8byte? */
+		ahlen = plen + sizeof(struct ah);
+	} else {
+		/* RFC 2402 */
+		plen = ((*algo->sumsiz)(sav) + 3) & ~(4 - 1);	/* XXX pad to 8byte? */
+		ahlen = plen + sizeof(struct newah);
+	}
+
+	return(ahlen);
+}
+
+#ifdef INET6
+/*
+ * Fill in the Authentication Header and calculate checksum.
+ */
+int
+ah6_output(m, nexthdrp, md, isr)
+	struct mbuf *m;
+	u_char *nexthdrp;
+	struct mbuf *md;
+	struct ipsecrequest *isr;
+{
+	struct mbuf *mprev;
+	struct mbuf *mah;
+	struct secasvar *sav = isr->sav;
+	const struct ah_algorithm *algo;
+	u_int32_t spi;
+	u_char *ahsumpos = NULL;
+	size_t plen;	/* AH payload size in bytes */
+	int error = 0;
+	int ahlen;
+	struct ip6_hdr *ip6;
+
+	if (m->m_len < sizeof(struct ip6_hdr)) {
+		ipseclog((LOG_DEBUG, "ah6_output: first mbuf too short\n"));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	ahlen = ah_hdrlen(sav);
+	if (ahlen == 0)
+		return 0;
+
+	for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+		;
+	if (!mprev || mprev->m_next != md) {
+		ipseclog((LOG_DEBUG, "ah6_output: md is not in chain\n"));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	MGET(mah, M_DONTWAIT, MT_DATA);
+	if (!mah) {
+		m_freem(m);
+		return ENOBUFS;
+	}
+	if (ahlen > MLEN) {
+		MCLGET(mah, M_DONTWAIT);
+		if ((mah->m_flags & M_EXT) == 0) {
+			m_free(mah);
+			m_freem(m);
+			return ENOBUFS;
+		}
+	}
+	mah->m_len = ahlen;
+	mah->m_next = md;
+	mprev->m_next = mah;
+	m->m_pkthdr.len += ahlen;
+
+	/* fix plen */
+	if (m->m_pkthdr.len - sizeof(struct ip6_hdr) > IPV6_MAXPACKET) {
+		ipseclog((LOG_ERR,
+		    "ip6_output: AH with IPv6 jumbogram is not supported\n"));
+		m_freem(m);
+		return EINVAL;
+	}
+	ip6 = mtod(m, struct ip6_hdr *);
+	ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
+
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
+		ipseclog((LOG_DEBUG, "ah6_output: internal error: "
+			"sav->replay is null: SPI=%u\n",
+			(u_int32_t)ntohl(sav->spi)));
+		ipsec6stat.out_inval++;
+		m_freem(m);
+		return EINVAL;
+	}
+
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR, "ah6_output: unsupported algorithm: "
+		    "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
+		ipsec6stat.out_inval++;
+		m_freem(m);
+		return EINVAL;
+	}
+	spi = sav->spi;
+
+	/*
+	 * initialize AH.
+	 */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		struct ah *ahdr = mtod(mah, struct ah *);
+
+		plen = mah->m_len - sizeof(struct ah);
+		ahsumpos = (u_char *)(ahdr + 1);
+		ahdr->ah_nxt = *nexthdrp;
+		*nexthdrp = IPPROTO_AH;
+		ahdr->ah_len = plen >> 2;
+		ahdr->ah_reserve = htons(0);
+		ahdr->ah_spi = spi;
+		bzero(ahdr + 1, plen);
+	} else {
+		struct newah *ahdr = mtod(mah, struct newah *);
+
+		plen = mah->m_len - sizeof(struct newah);
+		ahsumpos = (u_char *)(ahdr + 1);
+		ahdr->ah_nxt = *nexthdrp;
+		*nexthdrp = IPPROTO_AH;
+		ahdr->ah_len = (plen >> 2) + 1;	/* plus one for seq# */
+		ahdr->ah_reserve = htons(0);
+		ahdr->ah_spi = spi;
+		if (sav->replay->count == ~0) {
+			if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+				/* XXX Is it noisy ? */
+				ipseclog((LOG_WARNING,
+				    "replay counter overflowed. %s\n",
+				    ipsec_logsastr(sav)));
+				ipsec6stat.out_inval++;
+				m_freem(m);
+				return EINVAL;
+			}
+		}
+		sav->replay->count++;
+		/*
+		 * XXX sequence number must not be cycled, if the SA is
+		 * installed by IKE daemon.
+		 */
+		ahdr->ah_seq = htonl(sav->replay->count);
+		bzero(ahdr + 1, plen);
+	}
+
+	/*
+	 * calcurate the checksum, based on security association
+	 * and the algorithm specified.
+	 */
+	error = ah6_calccksum(m, (caddr_t)ahsumpos, plen, algo, sav);
+	if (error) {
+		ipsec6stat.out_inval++;
+		m_freem(m);
+	} else {
+		ipsec6stat.out_success++;
+		key_sa_recordxfer(sav, m);
+	}
+	ipsec6stat.out_ahhist[sav->alg_auth]++;
+
+	return(error);
+}
+#endif
+
+#ifdef INET
+/*
+ * Find the final destination if there is loose/strict source routing option.
+ * Returns NULL if there's no source routing options.
+ * Returns NULL on errors too.
+ * Note that this function will return a pointer INTO the given parameter,
+ * struct mbuf *m.
+ * The mbuf must be pulled up toward, at least, ip option part.
+ */
+static struct in_addr *
+ah4_finaldst(m)
+	struct mbuf *m;
+{
+	struct ip *ip;
+	int optlen;
+	u_char *q;
+	int i;
+	int hlen;
+
+	if (!m)
+		panic("ah4_finaldst: m == NULL");
+	ip = mtod(m, struct ip *);
+	hlen = (ip->ip_hl << 2);
+
+	if (m->m_len < hlen) {
+		ipseclog((LOG_DEBUG,
+		    "ah4_finaldst: parameter mbuf wrong (not pulled up)\n"));
+		return NULL;
+	}
+
+	if (hlen == sizeof(struct ip))
+		return NULL;
+
+	optlen = hlen - sizeof(struct ip);
+	if (optlen < 0) {
+		ipseclog((LOG_DEBUG, "ah4_finaldst: wrong optlen %d\n",
+		    optlen));
+		return NULL;
+	}
+
+	q = (u_char *)(ip + 1);
+	i = 0;
+	while (i < optlen) {
+		if (i + IPOPT_OPTVAL >= optlen)
+			return NULL;
+		if (q[i + IPOPT_OPTVAL] == IPOPT_EOL ||
+		    q[i + IPOPT_OPTVAL] == IPOPT_NOP ||
+		    i + IPOPT_OLEN < optlen)
+			;
+		else
+			return NULL;
+
+		switch (q[i + IPOPT_OPTVAL]) {
+		case IPOPT_EOL:
+			i = optlen;	/* bye */
+			break;
+		case IPOPT_NOP:
+			i++;
+			break;
+		case IPOPT_LSRR:
+		case IPOPT_SSRR:
+			if (q[i + IPOPT_OLEN] < 2 + sizeof(struct in_addr) ||
+			    optlen - i < q[i + IPOPT_OLEN]) {
+				ipseclog((LOG_ERR,
+				    "ip_finaldst: invalid IP option "
+				    "(code=%02x len=%02x)\n",
+				    q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
+				return NULL;
+			}
+			i += q[i + IPOPT_OLEN] - sizeof(struct in_addr);
+			return (struct in_addr *)(q + i);
+		default:
+			if (q[i + IPOPT_OLEN] < 2 ||
+			    optlen - i < q[i + IPOPT_OLEN]) {
+				ipseclog((LOG_ERR,
+				    "ip_finaldst: invalid IP option "
+				    "(code=%02x len=%02x)\n",
+				    q[i + IPOPT_OPTVAL], q[i + IPOPT_OLEN]));
+				return NULL;
+			}
+			i += q[i + IPOPT_OLEN];
+			break;
+		}
+	}
+	return NULL;
+}
+#endif
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/esp_core.c
@@ -0,0 +1,1149 @@
+//==========================================================================
+//
+//      src/sys/netinet6/esp_core.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_core.c,v 1.54 2001/12/07 07:07:08 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#include <netinet6/esp.h>
+#include <netinet6/esp_twofish.h>
+#include <netinet6/esp_rijndael.h>
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key.h>
+#include <crypto/des/des.h>
+#include <crypto/blowfish/blowfish.h>
+#include <crypto/cast128/cast128.h>
+
+static int esp_null_mature __P((struct secasvar *));
+static int esp_null_decrypt __P((struct mbuf *, size_t,
+	struct secasvar *, const struct esp_algorithm *, int));
+static int esp_null_encrypt __P((struct mbuf *, size_t, size_t,
+	struct secasvar *, const struct esp_algorithm *, int));
+static int esp_descbc_mature __P((struct secasvar *));
+static int esp_descbc_ivlen __P((const struct esp_algorithm *,
+	struct secasvar *));
+static int esp_des_schedule __P((const struct esp_algorithm *,
+	struct secasvar *));
+static int esp_des_schedlen __P((const struct esp_algorithm *));
+static int esp_des_blockdecrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_des_blockencrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_cbc_mature __P((struct secasvar *));
+static int esp_blowfish_schedule __P((const struct esp_algorithm *,
+	struct secasvar *));
+static int esp_blowfish_schedlen __P((const struct esp_algorithm *));
+static int esp_blowfish_blockdecrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_blowfish_blockencrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_cast128_schedule __P((const struct esp_algorithm *,
+	struct secasvar *));
+static int esp_cast128_schedlen __P((const struct esp_algorithm *));
+static int esp_cast128_blockdecrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_cast128_blockencrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_3des_schedule __P((const struct esp_algorithm *,
+	struct secasvar *));
+static int esp_3des_schedlen __P((const struct esp_algorithm *));
+static int esp_3des_blockdecrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_3des_blockencrypt __P((const struct esp_algorithm *,
+	struct secasvar *, u_int8_t *, u_int8_t *));
+static int esp_common_ivlen __P((const struct esp_algorithm *,
+	struct secasvar *));
+static int esp_cbc_decrypt __P((struct mbuf *, size_t,
+	struct secasvar *, const struct esp_algorithm *, int));
+static int esp_cbc_encrypt __P((struct mbuf *, size_t, size_t,
+	struct secasvar *, const struct esp_algorithm *, int));
+
+#define MAXIVLEN	16
+
+static const struct esp_algorithm esp_algorithms[] = {
+	{ 8, -1, esp_descbc_mature, 64, 64, esp_des_schedlen,
+		"des-cbc",
+		esp_descbc_ivlen, esp_cbc_decrypt,
+		esp_cbc_encrypt, esp_des_schedule,
+		esp_des_blockdecrypt, esp_des_blockencrypt, },
+	{ 8, 8, esp_cbc_mature, 192, 192, esp_3des_schedlen,
+		"3des-cbc",
+		esp_common_ivlen, esp_cbc_decrypt,
+		esp_cbc_encrypt, esp_3des_schedule,
+		esp_3des_blockdecrypt, esp_3des_blockencrypt, },
+	{ 1, 0, esp_null_mature, 0, 2048, 0, "null",
+		esp_common_ivlen, esp_null_decrypt,
+		esp_null_encrypt, NULL, },
+	{ 8, 8, esp_cbc_mature, 40, 448, esp_blowfish_schedlen, "blowfish-cbc",
+		esp_common_ivlen, esp_cbc_decrypt,
+		esp_cbc_encrypt, esp_blowfish_schedule,
+		esp_blowfish_blockdecrypt, esp_blowfish_blockencrypt, },
+	{ 8, 8, esp_cbc_mature, 40, 128, esp_cast128_schedlen,
+		"cast128-cbc",
+		esp_common_ivlen, esp_cbc_decrypt,
+		esp_cbc_encrypt, esp_cast128_schedule,
+		esp_cast128_blockdecrypt, esp_cast128_blockencrypt, },
+	{ 16, 16, esp_cbc_mature, 128, 256, esp_rijndael_schedlen,
+		"rijndael-cbc",
+		esp_common_ivlen, esp_cbc_decrypt,
+		esp_cbc_encrypt, esp_rijndael_schedule,
+		esp_rijndael_blockdecrypt, esp_rijndael_blockencrypt },
+	{ 16, 16, esp_cbc_mature, 128, 256, esp_twofish_schedlen,
+		"twofish-cbc",
+		esp_common_ivlen, esp_cbc_decrypt,
+		esp_cbc_encrypt, esp_twofish_schedule,
+		esp_twofish_blockdecrypt, esp_twofish_blockencrypt },
+};
+
+const struct esp_algorithm *
+esp_algorithm_lookup(idx)
+	int idx;
+{
+
+	switch (idx) {
+	case SADB_EALG_DESCBC:
+		return &esp_algorithms[0];
+	case SADB_EALG_3DESCBC:
+		return &esp_algorithms[1];
+	case SADB_EALG_NULL:
+		return &esp_algorithms[2];
+	case SADB_X_EALG_BLOWFISHCBC:
+		return &esp_algorithms[3];
+	case SADB_X_EALG_CAST128CBC:
+		return &esp_algorithms[4];
+	case SADB_X_EALG_RIJNDAELCBC:
+		return &esp_algorithms[5];
+	case SADB_X_EALG_TWOFISHCBC:
+		return &esp_algorithms[6];
+	default:
+		return NULL;
+	}
+}
+
+int
+esp_max_ivlen()
+{
+	int idx;
+	int ivlen;
+
+	ivlen = 0;
+	for (idx = 0; idx < sizeof(esp_algorithms)/sizeof(esp_algorithms[0]);
+	     idx++) {
+		if (esp_algorithms[idx].ivlenval > ivlen)
+			ivlen = esp_algorithms[idx].ivlenval;
+	}
+
+	return ivlen;
+}
+
+int
+esp_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+	int error;
+
+	/* check for key length */
+	if (_KEYBITS(sav->key_enc) < algo->keymin ||
+	    _KEYBITS(sav->key_enc) > algo->keymax) {
+		ipseclog((LOG_ERR,
+		    "esp_schedule %s: unsupported key length %d: "
+		    "needs %d to %d bits\n", algo->name, _KEYBITS(sav->key_enc),
+		    algo->keymin, algo->keymax));
+		return EINVAL;
+	}
+
+	/* already allocated */
+	if (sav->sched && sav->schedlen != 0)
+		return 0;
+	/* no schedule necessary */
+	if (!algo->schedule || !algo->schedlen)
+		return 0;
+
+	sav->schedlen = (*algo->schedlen)(algo);
+	if (sav->schedlen < 0)
+		return EINVAL;
+	sav->sched = malloc(sav->schedlen, M_SECA, M_DONTWAIT);
+	if (!sav->sched) {
+		sav->schedlen = 0;
+		return ENOBUFS;
+	}
+
+	error = (*algo->schedule)(algo, sav);
+	if (error) {
+		ipseclog((LOG_ERR, "esp_schedule %s: error %d\n",
+		    algo->name, error));
+		free(sav->sched, M_SECA);
+		sav->sched = NULL;
+		sav->schedlen = 0;
+	}
+	return error;
+}
+
+static int
+esp_null_mature(sav)
+	struct secasvar *sav;
+{
+
+	/* anything is okay */
+	return 0;
+}
+
+static int
+esp_null_decrypt(m, off, sav, algo, ivlen)
+	struct mbuf *m;
+	size_t off;		/* offset to ESP header */
+	struct secasvar *sav;
+	const struct esp_algorithm *algo;
+	int ivlen;
+{
+
+	return 0; /* do nothing */
+}
+
+static int
+esp_null_encrypt(m, off, plen, sav, algo, ivlen)
+	struct mbuf *m;
+	size_t off;	/* offset to ESP header */
+	size_t plen;	/* payload length (to be encrypted) */
+	struct secasvar *sav;
+	const struct esp_algorithm *algo;
+	int ivlen;
+{
+
+	return 0; /* do nothing */
+}
+
+static int
+esp_descbc_mature(sav)
+	struct secasvar *sav;
+{
+	const struct esp_algorithm *algo;
+
+	if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B)) {
+		ipseclog((LOG_ERR, "esp_cbc_mature: "
+		    "algorithm incompatible with 4 octets IV length\n"));
+		return 1;
+	}
+
+	if (!sav->key_enc) {
+		ipseclog((LOG_ERR, "esp_descbc_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = esp_algorithm_lookup(sav->alg_enc);
+	if (!algo) {
+		ipseclog((LOG_ERR,
+		    "esp_descbc_mature: unsupported algorithm.\n"));
+		return 1;
+	}
+
+	if (_KEYBITS(sav->key_enc) < algo->keymin ||
+	    _KEYBITS(sav->key_enc) > algo->keymax) {
+		ipseclog((LOG_ERR,
+		    "esp_descbc_mature: invalid key length %d.\n",
+		    _KEYBITS(sav->key_enc)));
+		return 1;
+	}
+
+	/* weak key check */
+	if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc))) {
+		ipseclog((LOG_ERR,
+		    "esp_descbc_mature: weak key was passed.\n"));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+esp_descbc_ivlen(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+
+	if (!sav)
+		return 8;
+	if ((sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_IV4B))
+		return 4;
+	if (!(sav->flags & SADB_X_EXT_OLD) && (sav->flags & SADB_X_EXT_DERIV))
+		return 4;
+	return 8;
+}
+
+static int
+esp_des_schedlen(algo)
+	const struct esp_algorithm *algo;
+{
+
+	return sizeof(des_key_schedule);
+}
+
+static int
+esp_des_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+
+	if (des_key_sched((des_cblock *)_KEYBUF(sav->key_enc),
+	    *(des_key_schedule *)sav->sched))
+		return EINVAL;
+	else
+		return 0;
+}
+
+static int
+esp_des_blockdecrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+
+	/* assumption: d has a good alignment */
+	bcopy(s, d, sizeof(DES_LONG) * 2);
+	des_ecb_encrypt((des_cblock *)d, (des_cblock *)d,
+	    *(des_key_schedule *)sav->sched, DES_DECRYPT);
+	return 0;
+}
+
+static int
+esp_des_blockencrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+
+	/* assumption: d has a good alignment */
+	bcopy(s, d, sizeof(DES_LONG) * 2);
+	des_ecb_encrypt((des_cblock *)d, (des_cblock *)d,
+	    *(des_key_schedule *)sav->sched, DES_ENCRYPT);
+	return 0;
+}
+
+static int
+esp_cbc_mature(sav)
+	struct secasvar *sav;
+{
+	int keylen;
+	const struct esp_algorithm *algo;
+
+	if (sav->flags & SADB_X_EXT_OLD) {
+		ipseclog((LOG_ERR,
+		    "esp_cbc_mature: algorithm incompatible with esp-old\n"));
+		return 1;
+	}
+	if (sav->flags & SADB_X_EXT_DERIV) {
+		ipseclog((LOG_ERR,
+		    "esp_cbc_mature: algorithm incompatible with derived\n"));
+		return 1;
+	}
+
+	if (!sav->key_enc) {
+		ipseclog((LOG_ERR, "esp_cbc_mature: no key is given.\n"));
+		return 1;
+	}
+
+	algo = esp_algorithm_lookup(sav->alg_enc);
+	if (!algo) {
+		ipseclog((LOG_ERR,
+		    "esp_cbc_mature %s: unsupported algorithm.\n", algo->name));
+		return 1;
+	}
+
+	keylen = sav->key_enc->sadb_key_bits;
+	if (keylen < algo->keymin || algo->keymax < keylen) {
+		ipseclog((LOG_ERR,
+		    "esp_cbc_mature %s: invalid key length %d.\n",
+		    algo->name, sav->key_enc->sadb_key_bits));
+		return 1;
+	}
+	switch (sav->alg_enc) {
+	case SADB_EALG_3DESCBC:
+		/* weak key check */
+		if (des_is_weak_key((des_cblock *)_KEYBUF(sav->key_enc)) ||
+		    des_is_weak_key((des_cblock *)(_KEYBUF(sav->key_enc) + 8)) ||
+		    des_is_weak_key((des_cblock *)(_KEYBUF(sav->key_enc) + 16))) {
+			ipseclog((LOG_ERR,
+			    "esp_cbc_mature %s: weak key was passed.\n",
+			    algo->name));
+			return 1;
+		}
+		break;
+	case SADB_X_EALG_BLOWFISHCBC:
+	case SADB_X_EALG_CAST128CBC:
+	case SADB_X_EALG_TWOFISHCBC:
+		break;
+	case SADB_X_EALG_RIJNDAELCBC:
+		/* allows specific key sizes only */
+		if (!(keylen == 128 || keylen == 192 || keylen == 256)) {
+			ipseclog((LOG_ERR,
+			    "esp_cbc_mature %s: invalid key length %d.\n",
+			    algo->name, keylen));
+			return 1;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int
+esp_blowfish_schedlen(algo)
+	const struct esp_algorithm *algo;
+{
+
+	return sizeof(BF_KEY);
+}
+
+static int
+esp_blowfish_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+
+	BF_set_key((BF_KEY *)sav->sched, _KEYLEN(sav->key_enc),
+	    _KEYBUF(sav->key_enc));
+	return 0;
+}
+
+static int
+esp_blowfish_blockdecrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	/* HOLY COW!  BF_encrypt() takes values in host byteorder */
+	BF_LONG t[2];
+
+	bcopy(s, t, sizeof(t));
+	t[0] = ntohl(t[0]);
+	t[1] = ntohl(t[1]);
+	BF_encrypt(t, (BF_KEY *)sav->sched, BF_DECRYPT);
+	t[0] = htonl(t[0]);
+	t[1] = htonl(t[1]);
+	bcopy(t, d, sizeof(t));
+	return 0;
+}
+
+static int
+esp_blowfish_blockencrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	/* HOLY COW!  BF_encrypt() takes values in host byteorder */
+	BF_LONG t[2];
+
+	bcopy(s, t, sizeof(t));
+	t[0] = ntohl(t[0]);
+	t[1] = ntohl(t[1]);
+	BF_encrypt(t, (BF_KEY *)sav->sched, BF_ENCRYPT);
+	t[0] = htonl(t[0]);
+	t[1] = htonl(t[1]);
+	bcopy(t, d, sizeof(t));
+	return 0;
+}
+
+static int
+esp_cast128_schedlen(algo)
+	const struct esp_algorithm *algo;
+{
+
+	return sizeof(u_int32_t) * 32;
+}
+
+static int
+esp_cast128_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+
+	set_cast128_subkey((u_int32_t *)sav->sched, _KEYBUF(sav->key_enc),
+		_KEYLEN(sav->key_enc));
+	return 0;
+}
+
+static int
+esp_cast128_blockdecrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+
+	if (_KEYLEN(sav->key_enc) <= 80 / 8)
+		cast128_decrypt_round12(d, s, (u_int32_t *)sav->sched);
+	else
+		cast128_decrypt_round16(d, s, (u_int32_t *)sav->sched);
+	return 0;
+}
+
+static int
+esp_cast128_blockencrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+
+	if (_KEYLEN(sav->key_enc) <= 80 / 8)
+		cast128_encrypt_round12(d, s, (u_int32_t *)sav->sched);
+	else
+		cast128_encrypt_round16(d, s, (u_int32_t *)sav->sched);
+	return 0;
+}
+
+static int
+esp_3des_schedlen(algo)
+	const struct esp_algorithm *algo;
+{
+
+	return sizeof(des_key_schedule) * 3;
+}
+
+static int
+esp_3des_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+	int error;
+	des_key_schedule *p;
+	int i;
+	char *k;
+
+	p = (des_key_schedule *)sav->sched;
+	k = _KEYBUF(sav->key_enc);
+	for (i = 0; i < 3; i++) {
+		error = des_key_sched((des_cblock *)(k + 8 * i), p[i]);
+		if (error)
+			return EINVAL;
+	}
+	return 0;
+}
+
+static int
+esp_3des_blockdecrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	des_key_schedule *p;
+
+	/* assumption: d has a good alignment */
+	p = (des_key_schedule *)sav->sched;
+	bcopy(s, d, sizeof(DES_LONG) * 2);
+	des_ecb3_encrypt((des_cblock *)d, (des_cblock *)d, 
+			 p[0], p[1], p[2], DES_DECRYPT);
+	return 0;
+}
+
+static int
+esp_3des_blockencrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	des_key_schedule *p;
+
+	/* assumption: d has a good alignment */
+	p = (des_key_schedule *)sav->sched;
+	bcopy(s, d, sizeof(DES_LONG) * 2);
+	des_ecb3_encrypt((des_cblock *)d, (des_cblock *)d, 
+			 p[0], p[1], p[2], DES_ENCRYPT);
+	return 0;
+}
+
+static int
+esp_common_ivlen(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+
+	if (!algo)
+		panic("esp_common_ivlen: unknown algorithm");
+	return algo->ivlenval;
+}
+
+static int
+esp_cbc_decrypt(m, off, sav, algo, ivlen)
+	struct mbuf *m;
+	size_t off;
+	struct secasvar *sav;
+	const struct esp_algorithm *algo;
+	int ivlen;
+{
+	struct mbuf *s;
+	struct mbuf *d, *d0, *dp;
+	int soff, doff;	/* offset from the head of chain, to head of this mbuf */
+	int sn, dn;	/* offset from the head of the mbuf, to meat */
+	size_t ivoff, bodyoff;
+	u_int8_t iv[MAXIVLEN], *ivp;
+	u_int8_t sbuf[MAXIVLEN], *sp;
+	u_int8_t *p, *q;
+	struct mbuf *scut;
+	int scutoff;
+	int i;
+	int blocklen;
+	int derived;
+
+	if (ivlen != sav->ivlen || ivlen > sizeof(iv)) {
+		ipseclog((LOG_ERR, "esp_cbc_decrypt %s: "
+		    "unsupported ivlen %d\n", algo->name, ivlen));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	/* assumes blocklen == padbound */
+	blocklen = algo->padbound;
+
+#ifdef DIAGNOSTIC
+	if (blocklen > sizeof(iv)) {
+		ipseclog((LOG_ERR, "esp_cbc_decrypt %s: "
+		    "unsupported blocklen %d\n", algo->name, blocklen));
+		m_freem(m);
+		return EINVAL;
+	}
+#endif
+
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1827 */
+		ivoff = off + sizeof(struct esp);
+		bodyoff = off + sizeof(struct esp) + ivlen;
+		derived = 0;
+	} else {
+		/* RFC 2406 */
+		if (sav->flags & SADB_X_EXT_DERIV) {
+			/*
+			 * draft-ietf-ipsec-ciph-des-derived-00.txt
+			 * uses sequence number field as IV field.
+			 */
+			ivoff = off + sizeof(struct esp);
+			bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t);
+			ivlen = sizeof(u_int32_t);
+			derived = 1;
+		} else {
+			ivoff = off + sizeof(struct newesp);
+			bodyoff = off + sizeof(struct newesp) + ivlen;
+			derived = 0;
+		}
+	}
+
+	/* grab iv */
+	m_copydata(m, ivoff, ivlen, iv);
+
+	/* extend iv */
+	if (ivlen == blocklen)
+		;
+	else if (ivlen == 4 && blocklen == 8) {
+		bcopy(&iv[0], &iv[4], 4);
+		iv[4] ^= 0xff;
+		iv[5] ^= 0xff;
+		iv[6] ^= 0xff;
+		iv[7] ^= 0xff;
+	} else {
+		ipseclog((LOG_ERR, "esp_cbc_encrypt %s: "
+		    "unsupported ivlen/blocklen: %d %d\n",
+		    algo->name, ivlen, blocklen));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	if (m->m_pkthdr.len < bodyoff) {
+		ipseclog((LOG_ERR, "esp_cbc_decrypt %s: bad len %d/%lu\n",
+		    algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
+		m_freem(m);
+		return EINVAL;
+	}
+	if ((m->m_pkthdr.len - bodyoff) % blocklen) {
+		ipseclog((LOG_ERR, "esp_cbc_decrypt %s: "
+		    "payload length must be multiple of %d\n",
+		    algo->name, blocklen));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	s = m;
+	d = d0 = dp = NULL;
+	soff = doff = sn = dn = 0;
+	ivp = sp = NULL;
+
+	/* skip bodyoff */
+	while (soff < bodyoff) {
+		if (soff + s->m_len > bodyoff) {
+			sn = bodyoff - soff;
+			break;
+		}
+
+		soff += s->m_len;
+		s = s->m_next;
+	}
+	scut = s;
+	scutoff = sn;
+
+	/* skip over empty mbuf */
+	while (s && s->m_len == 0)
+		s = s->m_next;
+
+	while (soff < m->m_pkthdr.len) {
+		/* source */
+		if (sn + blocklen <= s->m_len) {
+			/* body is continuous */
+			sp = mtod(s, u_int8_t *) + sn;
+		} else {
+			/* body is non-continuous */
+			m_copydata(s, sn, blocklen, sbuf);
+			sp = sbuf;
+		}
+
+		/* destination */
+		if (!d || dn + blocklen > d->m_len) {
+			if (d)
+				dp = d;
+			MGET(d, M_DONTWAIT, MT_DATA);
+			i = m->m_pkthdr.len - (soff + sn);
+			if (d && i > MLEN) {
+				MCLGET(d, M_DONTWAIT);
+				if ((d->m_flags & M_EXT) == 0) {
+					m_free(d);
+					d = NULL;
+				}
+			}
+			if (!d) {
+				m_freem(m);
+				if (d0)
+					m_freem(d0);
+				return ENOBUFS;
+			}
+			if (!d0)
+				d0 = d;
+			if (dp)
+				dp->m_next = d;
+			d->m_len = 0;
+			d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
+			if (d->m_len > i)
+				d->m_len = i;
+			dn = 0;
+		}
+
+		/* decrypt */
+		(*algo->blockdecrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn);
+
+		/* xor */
+		p = ivp ? ivp : iv;
+		q = mtod(d, u_int8_t *) + dn;
+		for (i = 0; i < blocklen; i++)
+			q[i] ^= p[i];
+
+		/* next iv */
+		if (sp == sbuf) {
+			bcopy(sbuf, iv, blocklen);
+			ivp = NULL;
+		} else
+			ivp = sp;
+
+		sn += blocklen;
+		dn += blocklen;
+
+		/* find the next source block */
+		while (s && sn >= s->m_len) {
+			sn -= s->m_len;
+			soff += s->m_len;
+			s = s->m_next;
+		}
+
+		/* skip over empty mbuf */
+		while (s && s->m_len == 0)
+			s = s->m_next;
+	}
+
+	m_freem(scut->m_next);
+	scut->m_len = scutoff;
+	scut->m_next = d0;
+
+	/* just in case */
+	bzero(iv, sizeof(iv));
+	bzero(sbuf, sizeof(sbuf));
+
+	return 0;
+}
+
+static int
+esp_cbc_encrypt(m, off, plen, sav, algo, ivlen)
+	struct mbuf *m;
+	size_t off;
+	size_t plen;
+	struct secasvar *sav;
+	const struct esp_algorithm *algo;
+	int ivlen;
+{
+	struct mbuf *s;
+	struct mbuf *d, *d0, *dp;
+	int soff, doff;	/* offset from the head of chain, to head of this mbuf */
+	int sn, dn;	/* offset from the head of the mbuf, to meat */
+	size_t ivoff, bodyoff;
+	u_int8_t iv[MAXIVLEN], *ivp;
+	u_int8_t sbuf[MAXIVLEN], *sp;
+	u_int8_t *p, *q;
+	struct mbuf *scut;
+	int scutoff;
+	int i;
+	int blocklen;
+	int derived;
+
+	if (ivlen != sav->ivlen || ivlen > sizeof(iv)) {
+		ipseclog((LOG_ERR, "esp_cbc_encrypt %s: "
+		    "unsupported ivlen %d\n", algo->name, ivlen));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	/* assumes blocklen == padbound */
+	blocklen = algo->padbound;
+
+#ifdef DIAGNOSTIC
+	if (blocklen > sizeof(iv)) {
+		ipseclog((LOG_ERR, "esp_cbc_encrypt %s: "
+		    "unsupported blocklen %d\n", algo->name, blocklen));
+		m_freem(m);
+		return EINVAL;
+	}
+#endif
+
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1827 */
+		ivoff = off + sizeof(struct esp);
+		bodyoff = off + sizeof(struct esp) + ivlen;
+		derived = 0;
+	} else {
+		/* RFC 2406 */
+		if (sav->flags & SADB_X_EXT_DERIV) {
+			/*
+			 * draft-ietf-ipsec-ciph-des-derived-00.txt
+			 * uses sequence number field as IV field.
+			 */
+			ivoff = off + sizeof(struct esp);
+			bodyoff = off + sizeof(struct esp) + sizeof(u_int32_t);
+			ivlen = sizeof(u_int32_t);
+			derived = 1;
+		} else {
+			ivoff = off + sizeof(struct newesp);
+			bodyoff = off + sizeof(struct newesp) + ivlen;
+			derived = 0;
+		}
+	}
+
+	/* put iv into the packet.  if we are in derived mode, use seqno. */
+	if (derived)
+		m_copydata(m, ivoff, ivlen, iv);
+	else {
+		bcopy(sav->iv, iv, ivlen);
+		/* maybe it is better to overwrite dest, not source */
+		m_copyback(m, ivoff, ivlen, iv);
+	}
+
+	/* extend iv */
+	if (ivlen == blocklen)
+		;
+	else if (ivlen == 4 && blocklen == 8) {
+		bcopy(&iv[0], &iv[4], 4);
+		iv[4] ^= 0xff;
+		iv[5] ^= 0xff;
+		iv[6] ^= 0xff;
+		iv[7] ^= 0xff;
+	} else {
+		ipseclog((LOG_ERR, "esp_cbc_encrypt %s: "
+		    "unsupported ivlen/blocklen: %d %d\n",
+		    algo->name, ivlen, blocklen));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	if (m->m_pkthdr.len < bodyoff) {
+		ipseclog((LOG_ERR, "esp_cbc_encrypt %s: bad len %d/%lu\n",
+		    algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
+		m_freem(m);
+		return EINVAL;
+	}
+	if ((m->m_pkthdr.len - bodyoff) % blocklen) {
+		ipseclog((LOG_ERR, "esp_cbc_encrypt %s: "
+		    "payload length must be multiple of %lu\n",
+		    algo->name, (unsigned long)algo->padbound));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	s = m;
+	d = d0 = dp = NULL;
+	soff = doff = sn = dn = 0;
+	ivp = sp = NULL;
+
+	/* skip bodyoff */
+	while (soff < bodyoff) {
+		if (soff + s->m_len > bodyoff) {
+			sn = bodyoff - soff;
+			break;
+		}
+
+		soff += s->m_len;
+		s = s->m_next;
+	}
+	scut = s;
+	scutoff = sn;
+
+	/* skip over empty mbuf */
+	while (s && s->m_len == 0)
+		s = s->m_next;
+
+	while (soff < m->m_pkthdr.len) {
+		/* source */
+		if (sn + blocklen <= s->m_len) {
+			/* body is continuous */
+			sp = mtod(s, u_int8_t *) + sn;
+		} else {
+			/* body is non-continuous */
+			m_copydata(s, sn, blocklen, sbuf);
+			sp = sbuf;
+		}
+
+		/* destination */
+		if (!d || dn + blocklen > d->m_len) {
+			if (d)
+				dp = d;
+			MGET(d, M_DONTWAIT, MT_DATA);
+			i = m->m_pkthdr.len - (soff + sn);
+			if (d && i > MLEN) {
+				MCLGET(d, M_DONTWAIT);
+				if ((d->m_flags & M_EXT) == 0) {
+					m_free(d);
+					d = NULL;
+				}
+			}
+			if (!d) {
+				m_freem(m);
+				if (d0)
+					m_freem(d0);
+				return ENOBUFS;
+			}
+			if (!d0)
+				d0 = d;
+			if (dp)
+				dp->m_next = d;
+			d->m_len = 0;
+			d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
+			if (d->m_len > i)
+				d->m_len = i;
+			dn = 0;
+		}
+
+		/* xor */
+		p = ivp ? ivp : iv;
+		q = sp;
+		for (i = 0; i < blocklen; i++)
+			q[i] ^= p[i];
+
+		/* encrypt */
+		(*algo->blockencrypt)(algo, sav, sp, mtod(d, u_int8_t *) + dn);
+
+		/* next iv */
+		ivp = mtod(d, u_int8_t *) + dn;
+
+		sn += blocklen;
+		dn += blocklen;
+
+		/* find the next source block */
+		while (s && sn >= s->m_len) {
+			sn -= s->m_len;
+			soff += s->m_len;
+			s = s->m_next;
+		}
+
+		/* skip over empty mbuf */
+		while (s && s->m_len == 0)
+			s = s->m_next;
+	}
+
+	m_freem(scut->m_next);
+	scut->m_len = scutoff;
+	scut->m_next = d0;
+
+	/* just in case */
+	bzero(iv, sizeof(iv));
+	bzero(sbuf, sizeof(sbuf));
+
+	key_sa_stir_iv(sav);
+
+	return 0;
+}
+
+/*------------------------------------------------------------*/
+
+/* does not free m0 on error */
+int
+esp_auth(m0, skip, length, sav, sum)
+	struct mbuf *m0;
+	size_t skip;	/* offset to ESP header */
+	size_t length;	/* payload length */
+	struct secasvar *sav;
+	u_char *sum;
+{
+	struct mbuf *m;
+	size_t off;
+	struct ah_algorithm_state s;
+	u_char sumbuf[AH_MAXSUMSIZE];
+	const struct ah_algorithm *algo;
+	size_t siz;
+	int error;
+
+	/* sanity checks */
+	if (m0->m_pkthdr.len < skip) {
+		ipseclog((LOG_DEBUG, "esp_auth: mbuf length < skip\n"));
+		return EINVAL;
+	}
+	if (m0->m_pkthdr.len < skip + length) {
+		ipseclog((LOG_DEBUG,
+		    "esp_auth: mbuf length < skip + length\n"));
+		return EINVAL;
+	}
+	/*
+	 * length of esp part (excluding authentication data) must be 4n,
+	 * since nexthdr must be at offset 4n+3.
+	 */
+	if (length % 4) {
+		ipseclog((LOG_ERR, "esp_auth: length is not multiple of 4\n"));
+		return EINVAL;
+	}
+	if (!sav) {
+		ipseclog((LOG_DEBUG, "esp_auth: NULL SA passed\n"));
+		return EINVAL;
+	}
+	algo = ah_algorithm_lookup(sav->alg_auth);
+	if (!algo) {
+		ipseclog((LOG_ERR,
+		    "esp_auth: bad ESP auth algorithm passed: %d\n",
+		    sav->alg_auth));
+		return EINVAL;
+	}
+
+	m = m0;
+	off = 0;
+
+	siz = (((*algo->sumsiz)(sav) + 3) & ~(4 - 1));
+	if (sizeof(sumbuf) < siz) {
+		ipseclog((LOG_DEBUG,
+		    "esp_auth: AH_MAXSUMSIZE is too small: siz=%lu\n",
+		    (u_long)siz));
+		return EINVAL;
+	}
+
+	/* skip the header */
+	while (skip) {
+		if (!m)
+			panic("mbuf chain?");
+		if (m->m_len <= skip) {
+			skip -= m->m_len;
+			m = m->m_next;
+			off = 0;
+		} else {
+			off = skip;
+			skip = 0;
+		}
+	}
+
+	error = (*algo->init)(&s, sav);
+	if (error)
+		return error;
+
+	while (0 < length) {
+		if (!m)
+			panic("mbuf chain?");
+
+		if (m->m_len - off < length) {
+			(*algo->update)(&s, mtod(m, u_char *) + off,
+				m->m_len - off);
+			length -= m->m_len - off;
+			m = m->m_next;
+			off = 0;
+		} else {
+			(*algo->update)(&s, mtod(m, u_char *) + off, length);
+			break;
+		}
+	}
+	(*algo->result)(&s, sumbuf);
+	bcopy(sumbuf, sum, siz);	/* XXX */
+	
+	return 0;
+}
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/esp_input.c
@@ -0,0 +1,1071 @@
+//==========================================================================
+//
+//      src/sys/netinet6/esp_input.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_input.c,v 1.61 2001/10/19 05:20:55 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#include <sys/param.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+#include <sys/malloc.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_ecn.h>
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 105080000	/* 1.5H */
+#include <netinet/ip_icmp.h>
+#endif
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet/in_pcb.h>
+#include <netinet6/in6_pcb.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/ip6protosw.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#include <netinet6/esp.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+
+#define IPLEN_FLIPPED
+
+#define ESPMAXLEN \
+	(sizeof(struct esp) < sizeof(struct newesp) \
+		? sizeof(struct newesp) : sizeof(struct esp))
+
+#ifdef INET
+extern struct protosw inetsw[];
+#if defined(__bsdi__) || defined(__NetBSD__)
+extern u_char ip_protox[];
+#endif
+
+void
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
+esp4_input(m, off)
+	struct mbuf *m;
+	int off;
+#else
+#if __STDC__
+esp4_input(struct mbuf *m, ...)
+#else
+esp4_input(m, va_alist)
+	struct mbuf *m;
+	va_dcl
+#endif
+#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+{
+	struct ip *ip;
+	struct esp *esp;
+	struct esptail esptail;
+	u_int32_t spi;
+	struct secasvar *sav = NULL;
+	size_t taillen;
+	u_int16_t nxt;
+	const struct esp_algorithm *algo;
+	int ivlen;
+	size_t hlen;
+	size_t esplen;
+	int s;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+	va_list ap;
+	int off, proto;
+#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+	va_start(ap, m);
+	off = va_arg(ap, int);
+	proto = va_arg(ap, int);
+	va_end(ap);
+#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+
+	/* sanity check for alignment. */
+	if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) {
+		ipseclog((LOG_ERR, "IPv4 ESP input: packet alignment problem "
+			"(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len));
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+
+	if (m->m_len < off + ESPMAXLEN) {
+		m = m_pullup(m, off + ESPMAXLEN);
+		if (!m) {
+			ipseclog((LOG_DEBUG,
+			    "IPv4 ESP input: can't pullup in esp4_input\n"));
+			ipsecstat.in_inval++;
+			goto bad;
+		}
+	}
+
+	ip = mtod(m, struct ip *);
+	esp = (struct esp *)(((u_int8_t *)ip) + off);
+#ifdef _IP_VHL
+	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	hlen = ip->ip_hl << 2;
+#endif
+
+	/* find the sassoc. */
+	spi = esp->esp_spi;
+
+	if ((sav = key_allocsa(AF_INET,
+	                      (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
+	                      IPPROTO_ESP, spi)) == 0) {
+		ipseclog((LOG_WARNING,
+		    "IPv4 ESP input: no key association found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsecstat.in_nosa++;
+		goto bad;
+	}
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP esp4_input called to allocate SA:%p\n", sav));
+	if (sav->state != SADB_SASTATE_MATURE
+	 && sav->state != SADB_SASTATE_DYING) {
+		ipseclog((LOG_DEBUG,
+		    "IPv4 ESP input: non-mature/dying SA found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsecstat.in_badspi++;
+		goto bad;
+	}
+	algo = esp_algorithm_lookup(sav->alg_enc);
+	if (!algo) {
+		ipseclog((LOG_DEBUG, "IPv4 ESP input: "
+		    "unsupported encryption algorithm for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsecstat.in_badspi++;
+		goto bad;
+	}
+
+	/* check if we have proper ivlen information */
+	ivlen = sav->ivlen;
+	if (ivlen < 0) {
+		ipseclog((LOG_ERR, "inproper ivlen in IPv4 ESP input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+
+	if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay
+	 && (sav->alg_auth && sav->key_auth)))
+		goto noreplaycheck;
+
+	if (sav->alg_auth == SADB_X_AALG_NULL ||
+	    sav->alg_auth == SADB_AALG_NONE)
+		goto noreplaycheck;
+
+	/*
+	 * check for sequence number.
+	 */
+	if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav))
+		; /* okey */
+	else {
+		ipsecstat.in_espreplay++;
+		ipseclog((LOG_WARNING,
+		    "replay packet in IPv4 ESP input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		goto bad;
+	}
+
+	/* check ICV */
+    {
+	u_char sum0[AH_MAXSUMSIZE];
+	u_char sum[AH_MAXSUMSIZE];
+	const struct ah_algorithm *sumalgo;
+	size_t siz;
+
+	sumalgo = ah_algorithm_lookup(sav->alg_auth);
+	if (!sumalgo)
+		goto noreplaycheck;
+	siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1));
+	if (AH_MAXSUMSIZE < siz) {
+		ipseclog((LOG_DEBUG,
+		    "internal error: AH_MAXSUMSIZE must be larger than %lu\n",
+		    (u_long)siz));
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+
+	m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]);
+
+	if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) {
+		ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		ipsecstat.in_espauthfail++;
+		goto bad;
+	}
+
+	if (bcmp(sum0, sum, siz) != 0) {
+		ipseclog((LOG_WARNING, "auth fail in IPv4 ESP input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		ipsecstat.in_espauthfail++;
+		goto bad;
+	}
+
+	/* strip off the authentication data */
+	m_adj(m, -siz);
+	ip = mtod(m, struct ip *);
+#ifdef IPLEN_FLIPPED
+	ip->ip_len = ip->ip_len - siz;
+#else
+	ip->ip_len = htons(ntohs(ip->ip_len) - siz);
+#endif
+	m->m_flags |= M_AUTHIPDGM;
+	ipsecstat.in_espauthsucc++;
+    }
+
+	/*
+	 * update sequence number.
+	 */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+		if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) {
+			ipsecstat.in_espreplay++;
+			goto bad;
+		}
+	}
+
+noreplaycheck:
+
+	/* process main esp header. */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1827 */
+		esplen = sizeof(struct esp);
+	} else {
+		/* RFC 2406 */
+		if (sav->flags & SADB_X_EXT_DERIV)
+			esplen = sizeof(struct esp);
+		else
+			esplen = sizeof(struct newesp);
+	}
+
+	if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) {
+		ipseclog((LOG_WARNING,
+		    "IPv4 ESP input: packet too short\n"));
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+
+	if (m->m_len < off + esplen + ivlen) {
+		m = m_pullup(m, off + esplen + ivlen);
+		if (!m) {
+			ipseclog((LOG_DEBUG,
+			    "IPv4 ESP input: can't pullup in esp4_input\n"));
+			ipsecstat.in_inval++;
+			goto bad;
+		}
+	}
+
+	/*
+	 * pre-compute and cache intermediate key
+	 */
+	if (esp_schedule(algo, sav) != 0) {
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+
+	/*
+	 * decrypt the packet.
+	 */
+	if (!algo->decrypt)
+		panic("internal error: no decrypt function");
+	if ((*algo->decrypt)(m, off, sav, algo, ivlen)) {
+		/* m is already freed */
+		m = NULL;
+		ipseclog((LOG_ERR, "decrypt fail in IPv4 ESP input: %s\n",
+		    ipsec_logsastr(sav)));
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+	ipsecstat.in_esphist[sav->alg_enc]++;
+
+	m->m_flags |= M_DECRYPTED;
+
+	/*
+	 * find the trailer of the ESP.
+	 */
+	m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail),
+	     (caddr_t)&esptail);
+	nxt = esptail.esp_nxt;
+	taillen = esptail.esp_padlen + sizeof(esptail);
+
+	if (m->m_pkthdr.len < taillen
+	 || m->m_pkthdr.len - taillen < hlen) {	/* ? */
+		ipseclog((LOG_WARNING,
+		    "bad pad length in IPv4 ESP input: %s %s\n",
+		    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+		ipsecstat.in_inval++;
+		goto bad;
+	}
+
+	/* strip off the trailing pad area. */
+	m_adj(m, -taillen);
+
+#ifdef IPLEN_FLIPPED
+	ip->ip_len = ip->ip_len - taillen;
+#else
+	ip->ip_len = htons(ntohs(ip->ip_len) - taillen);
+#endif
+
+	/* was it transmitted over the IPsec tunnel SA? */
+	if (!ipsec_tunnel_device &&
+	    ipsec4_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) {
+		/*
+		 * strip off all the headers that precedes ESP header.
+		 *	IP4 xx ESP IP4' payload -> IP4' payload
+		 *
+		 * XXX more sanity checks
+		 * XXX relationship with gif?
+		 */
+		u_int8_t tos;
+
+		tos = ip->ip_tos;
+		m_adj(m, off + esplen + ivlen);
+		if (m->m_len < sizeof(*ip)) {
+			m = m_pullup(m, sizeof(*ip));
+			if (!m) {
+				ipsecstat.in_inval++;
+				goto bad;
+			}
+		}
+		ip = mtod(m, struct ip *);
+		/* ECN consideration. */
+		ip_ecn_egress(ip4_ipsec_ecn, &tos, &ip->ip_tos);
+		if (!key_checktunnelsanity(sav, AF_INET,
+			    (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst)) {
+			ipseclog((LOG_ERR, "ipsec tunnel address mismatch "
+			    "in IPv4 ESP input: %s %s\n",
+			    ipsec4_logpacketstr(ip, spi), ipsec_logsastr(sav)));
+			ipsecstat.in_inval++;
+			goto bad;
+		}
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0 ||
+		    ipsec_addhist(m, IPPROTO_IPV4, 0) != 0) {
+			ipsecstat.in_nomem++;
+			goto bad;
+		}
+
+		s = splimp();
+		if (IF_QFULL(&ipintrq)) {
+			ipsecstat.in_inval++;
+			splx(s);
+			goto bad;
+		}
+		IF_ENQUEUE(&ipintrq, m);
+		m = NULL;
+		schednetisr(NETISR_IP); /* can be skipped but to make sure */
+		splx(s);
+		nxt = IPPROTO_DONE;
+	} else {
+		/*
+		 * strip off ESP header and IV.
+		 * even in m_pulldown case, we need to strip off ESP so that
+		 * we can always compute checksum for AH correctly.
+		 */
+		size_t stripsiz;
+
+		stripsiz = esplen + ivlen;
+
+		ip = mtod(m, struct ip *);
+		ovbcopy((caddr_t)ip, (caddr_t)(((u_char *)ip) + stripsiz), off);
+		m->m_data += stripsiz;
+		m->m_len -= stripsiz;
+		m->m_pkthdr.len -= stripsiz;
+
+		ip = mtod(m, struct ip *);
+#ifdef IPLEN_FLIPPED
+		ip->ip_len = ip->ip_len - stripsiz;
+#else
+		ip->ip_len = htons(ntohs(ip->ip_len) - stripsiz);
+#endif
+		ip->ip_p = nxt;
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) {
+			ipsecstat.in_nomem++;
+			goto bad;
+		}
+
+		if (nxt != IPPROTO_DONE) {
+			if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
+			    ipsec4_in_reject(m, NULL)) {
+				ipsecstat.in_polvio++;
+				goto bad;
+			}
+#if defined(__FreeBSD__) && __FreeBSD__ >= 4
+			(*inetsw[ip_protox[nxt]].pr_input)(m, off);
+#else
+			(*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
+#endif
+		} else
+			m_freem(m);
+		m = NULL;
+	}
+
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP esp4_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	ipsecstat.in_success++;
+	return;
+
+bad:
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP esp4_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	if (m)
+		m_freem(m);
+	return;
+}
+
+#if defined(__NetBSD__) && __NetBSD_Version__ >= 105080000	/* 1.5H */
+/* assumes that ip header and esp header are contiguous on mbuf */
+void *
+esp4_ctlinput(cmd, sa, v)
+	int cmd;
+	struct sockaddr *sa;
+	void *v;
+{
+	struct ip *ip = v;
+	struct esp *esp;
+	struct icmp *icp;
+	struct secasvar *sav;
+
+	if (sa->sa_family != AF_INET ||
+	    sa->sa_len != sizeof(struct sockaddr_in))
+		return NULL;
+	if ((unsigned)cmd >= PRC_NCMDS)
+		return NULL;
+	if (cmd == PRC_MSGSIZE && ip_mtudisc && ip && ip->ip_v == 4) {
+		/*
+		 * Check to see if we have a valid SA corresponding to
+		 * the address in the ICMP message payload.
+		 */
+		esp = (struct esp *)((caddr_t)ip + (ip->ip_hl << 2));
+		if ((sav = key_allocsa(AF_INET,
+				       (caddr_t) &ip->ip_src,
+				       (caddr_t) &ip->ip_dst,
+				       IPPROTO_ESP, esp->esp_spi)) == NULL)
+			return NULL;
+		if (sav->state != SADB_SASTATE_MATURE &&
+		    sav->state != SADB_SASTATE_DYING) {
+			key_freesav(sav);
+			return NULL;
+		}
+
+		/* XXX Further validation? */
+
+		key_freesav(sav);
+
+		/*
+		 * Now that we've validated that we are actually communicating
+		 * with the host indicated in the ICMP message, locate the
+		 * ICMP header, recalculate the new MTU, and create the
+		 * corresponding routing entry.
+		 */
+		icp = (struct icmp *)((caddr_t)ip -
+		    offsetof(struct icmp, icmp_ip));
+		icmp_mtudisc(icp, ip->ip_dst);
+
+		return NULL;
+	}
+
+	return NULL;
+}
+#endif /* NetBSD 1.5H or later */
+#endif /* INET */
+
+#ifdef INET6
+int
+esp6_input(mp, offp, proto)
+	struct mbuf **mp;
+	int *offp, proto;
+{
+	struct mbuf *m = *mp;
+	int off = *offp;
+	struct ip6_hdr *ip6;
+	struct esp *esp;
+	struct esptail esptail;
+	u_int32_t spi;
+	struct secasvar *sav = NULL;
+	size_t taillen;
+	u_int16_t nxt;
+	const struct esp_algorithm *algo;
+	int ivlen;
+	size_t esplen;
+	int s;
+
+	/* sanity check for alignment. */
+	if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) {
+		ipseclog((LOG_ERR, "IPv6 ESP input: packet alignment problem "
+			"(off=%d, pktlen=%d)\n", off, m->m_pkthdr.len));
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+
+#ifndef PULLDOWN_TEST
+	IP6_EXTHDR_CHECK(m, off, ESPMAXLEN, IPPROTO_DONE);
+	esp = (struct esp *)(mtod(m, caddr_t) + off);
+#else
+	IP6_EXTHDR_GET(esp, struct esp *, m, off, ESPMAXLEN);
+	if (esp == NULL) {
+		ipsec6stat.in_inval++;
+		return IPPROTO_DONE;
+	}
+#endif
+	ip6 = mtod(m, struct ip6_hdr *);
+
+	if (ntohs(ip6->ip6_plen) == 0) {
+		ipseclog((LOG_ERR, "IPv6 ESP input: "
+		    "ESP with IPv6 jumbogram is not supported.\n"));
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+
+	/* find the sassoc. */
+	spi = esp->esp_spi;
+
+	if ((sav = key_allocsa(AF_INET6,
+	                      (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
+	                      IPPROTO_ESP, spi)) == 0) {
+		ipseclog((LOG_WARNING,
+		    "IPv6 ESP input: no key association found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsec6stat.in_nosa++;
+		goto bad;
+	}
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP esp6_input called to allocate SA:%p\n", sav));
+	if (sav->state != SADB_SASTATE_MATURE
+	 && sav->state != SADB_SASTATE_DYING) {
+		ipseclog((LOG_DEBUG,
+		    "IPv6 ESP input: non-mature/dying SA found for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsec6stat.in_badspi++;
+		goto bad;
+	}
+	algo = esp_algorithm_lookup(sav->alg_enc);
+	if (!algo) {
+		ipseclog((LOG_DEBUG, "IPv6 ESP input: "
+		    "unsupported encryption algorithm for spi %u\n",
+		    (u_int32_t)ntohl(spi)));
+		ipsec6stat.in_badspi++;
+		goto bad;
+	}
+
+	/* check if we have proper ivlen information */
+	ivlen = sav->ivlen;
+	if (ivlen < 0) {
+		ipseclog((LOG_ERR, "inproper ivlen in IPv6 ESP input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		ipsec6stat.in_badspi++;
+		goto bad;
+	}
+
+	if (!((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay
+	 && (sav->alg_auth && sav->key_auth)))
+		goto noreplaycheck;
+
+	if (sav->alg_auth == SADB_X_AALG_NULL ||
+	    sav->alg_auth == SADB_AALG_NONE)
+		goto noreplaycheck;
+
+	/*
+	 * check for sequence number.
+	 */
+	if (ipsec_chkreplay(ntohl(((struct newesp *)esp)->esp_seq), sav))
+		; /* okey */
+	else {
+		ipsec6stat.in_espreplay++;
+		ipseclog((LOG_WARNING,
+		    "replay packet in IPv6 ESP input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		goto bad;
+	}
+
+	/* check ICV */
+    {
+	u_char sum0[AH_MAXSUMSIZE];
+	u_char sum[AH_MAXSUMSIZE];
+	const struct ah_algorithm *sumalgo;
+	size_t siz;
+
+	sumalgo = ah_algorithm_lookup(sav->alg_auth);
+	if (!sumalgo)
+		goto noreplaycheck;
+	siz = (((*sumalgo->sumsiz)(sav) + 3) & ~(4 - 1));
+	if (AH_MAXSUMSIZE < siz) {
+		ipseclog((LOG_DEBUG,
+		    "internal error: AH_MAXSUMSIZE must be larger than %lu\n",
+		    (u_long)siz));
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+
+	m_copydata(m, m->m_pkthdr.len - siz, siz, &sum0[0]);
+
+	if (esp_auth(m, off, m->m_pkthdr.len - off - siz, sav, sum)) {
+		ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		ipsec6stat.in_espauthfail++;
+		goto bad;
+	}
+
+	if (bcmp(sum0, sum, siz) != 0) {
+		ipseclog((LOG_WARNING, "auth fail in IPv6 ESP input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		ipsec6stat.in_espauthfail++;
+		goto bad;
+	}
+
+	/* strip off the authentication data */
+	m_adj(m, -siz);
+	ip6 = mtod(m, struct ip6_hdr *);
+	ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - siz);
+
+	m->m_flags |= M_AUTHIPDGM;
+	ipsec6stat.in_espauthsucc++;
+    }
+
+	/*
+	 * update sequence number.
+	 */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && sav->replay) {
+		if (ipsec_updatereplay(ntohl(((struct newesp *)esp)->esp_seq), sav)) {
+			ipsec6stat.in_espreplay++;
+			goto bad;
+		}
+	}
+
+noreplaycheck:
+
+	/* process main esp header. */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1827 */
+		esplen = sizeof(struct esp);
+	} else {
+		/* RFC 2406 */
+		if (sav->flags & SADB_X_EXT_DERIV)
+			esplen = sizeof(struct esp);
+		else
+			esplen = sizeof(struct newesp);
+	}
+
+	if (m->m_pkthdr.len < off + esplen + ivlen + sizeof(esptail)) {
+		ipseclog((LOG_WARNING,
+		    "IPv6 ESP input: packet too short\n"));
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+
+#ifndef PULLDOWN_TEST
+	IP6_EXTHDR_CHECK(m, off, esplen + ivlen, IPPROTO_DONE);	/* XXX */
+#else
+	IP6_EXTHDR_GET(esp, struct esp *, m, off, esplen + ivlen);
+	if (esp == NULL) {
+		ipsec6stat.in_inval++;
+		m = NULL;
+		goto bad;
+	}
+#endif
+	ip6 = mtod(m, struct ip6_hdr *);	/* set it again just in case */
+
+	/*
+	 * pre-compute and cache intermediate key
+	 */
+	if (esp_schedule(algo, sav) != 0) {
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+
+	/*
+	 * decrypt the packet.
+	 */
+	if (!algo->decrypt)
+		panic("internal error: no decrypt function");
+	if ((*algo->decrypt)(m, off, sav, algo, ivlen)) {
+		/* m is already freed */
+		m = NULL;
+		ipseclog((LOG_ERR, "decrypt fail in IPv6 ESP input: %s\n",
+		    ipsec_logsastr(sav)));
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+	ipsec6stat.in_esphist[sav->alg_enc]++;
+
+	m->m_flags |= M_DECRYPTED;
+
+	/*
+	 * find the trailer of the ESP.
+	 */
+	m_copydata(m, m->m_pkthdr.len - sizeof(esptail), sizeof(esptail),
+	     (caddr_t)&esptail);
+	nxt = esptail.esp_nxt;
+	taillen = esptail.esp_padlen + sizeof(esptail);
+
+	if (m->m_pkthdr.len < taillen
+	 || m->m_pkthdr.len - taillen < sizeof(struct ip6_hdr)) {	/* ? */
+		ipseclog((LOG_WARNING,
+		    "bad pad length in IPv6 ESP input: %s %s\n",
+		    ipsec6_logpacketstr(ip6, spi), ipsec_logsastr(sav)));
+		ipsec6stat.in_inval++;
+		goto bad;
+	}
+
+	/* strip off the trailing pad area. */
+	m_adj(m, -taillen);
+
+	ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - taillen);
+
+	/* was it transmitted over the IPsec tunnel SA? */
+	if (!ipsec_tunnel_device &&
+	    ipsec6_tunnel_validate(m, off + esplen + ivlen, nxt, sav)) {
+		/*
+		 * strip off all the headers that precedes ESP header.
+		 *	IP6 xx ESP IP6' payload -> IP6' payload
+		 *
+		 * XXX more sanity checks
+		 * XXX relationship with gif?
+		 */
+		u_int32_t flowinfo;	/* net endian */
+		flowinfo = ip6->ip6_flow;
+		m_adj(m, off + esplen + ivlen);
+		if (m->m_len < sizeof(*ip6)) {
+#ifndef PULLDOWN_TEST
+			/*
+			 * m_pullup is prohibited in KAME IPv6 input processing
+			 * but there's no other way!
+			 */
+#else
+			/* okay to pullup in m_pulldown style */
+#endif
+			m = m_pullup(m, sizeof(*ip6));
+			if (!m) {
+				ipsec6stat.in_inval++;
+				goto bad;
+			}
+		}
+		ip6 = mtod(m, struct ip6_hdr *);
+		/* ECN consideration. */
+		ip6_ecn_egress(ip6_ipsec_ecn, &flowinfo, &ip6->ip6_flow);
+		if (!key_checktunnelsanity(sav, AF_INET6,
+			    (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst)) {
+			ipseclog((LOG_ERR, "ipsec tunnel address mismatch "
+			    "in IPv6 ESP input: %s %s\n",
+			    ipsec6_logpacketstr(ip6, spi),
+			    ipsec_logsastr(sav)));
+			ipsec6stat.in_inval++;
+			goto bad;
+		}
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0 || 
+		    ipsec_addhist(m, IPPROTO_IPV6, 0) != 0) {
+			ipsec6stat.in_nomem++;
+			goto bad;
+		}
+
+		s = splimp();
+		if (IF_QFULL(&ip6intrq)) {
+			ipsec6stat.in_inval++;
+			splx(s);
+			goto bad;
+		}
+		IF_ENQUEUE(&ip6intrq, m);
+		m = NULL;
+		schednetisr(NETISR_IPV6); /* can be skipped but to make sure */
+		splx(s);
+		nxt = IPPROTO_DONE;
+	} else {
+		/*
+		 * strip off ESP header and IV.
+		 * even in m_pulldown case, we need to strip off ESP so that
+		 * we can always compute checksum for AH correctly.
+		 */
+		size_t stripsiz;
+		char *prvnxtp;
+
+		/*
+		 * Set the next header field of the previous header correctly.
+		 */
+		prvnxtp = ip6_get_prevhdr(m, off); /* XXX */
+		*prvnxtp = nxt;
+
+		stripsiz = esplen + ivlen;
+
+		ip6 = mtod(m, struct ip6_hdr *);
+		if (m->m_len >= stripsiz + off) {
+			ovbcopy((caddr_t)ip6, ((caddr_t)ip6) + stripsiz, off);
+			m->m_data += stripsiz;
+			m->m_len -= stripsiz;
+			m->m_pkthdr.len -= stripsiz;
+		} else {
+			/*
+			 * this comes with no copy if the boundary is on
+			 * cluster
+			 */
+			struct mbuf *n;
+
+			n = m_split(m, off, M_DONTWAIT);
+			if (n == NULL) {
+				/* m is retained by m_split */
+				goto bad;
+			}
+			m_adj(n, stripsiz);
+			m_cat(m, n);
+			/* m_cat does not update m_pkthdr.len */
+			m->m_pkthdr.len += n->m_pkthdr.len;
+		}
+
+#ifndef PULLDOWN_TEST
+		/*
+		 * KAME requires that the packet to be contiguous on the
+		 * mbuf.  We need to make that sure.
+		 * this kind of code should be avoided.
+		 * XXX other conditions to avoid running this part?
+		 */
+		if (m->m_len != m->m_pkthdr.len) {
+			struct mbuf *n = NULL;
+			int maxlen;
+
+			MGETHDR(n, M_DONTWAIT, MT_HEADER);
+			maxlen = MHLEN;
+			if (n)
+				M_COPY_PKTHDR(n, m);
+			if (n && m->m_pkthdr.len > maxlen) {
+				MCLGET(n, M_DONTWAIT);
+				maxlen = MCLBYTES;
+				if ((n->m_flags & M_EXT) == 0) {
+					m_free(n);
+					n = NULL;
+				}
+			}
+			if (!n) {
+				printf("esp6_input: mbuf allocation failed\n");
+				goto bad;
+			}
+
+			if (m->m_pkthdr.len <= maxlen) {
+				m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t));
+				n->m_len = m->m_pkthdr.len;
+				n->m_pkthdr.len = m->m_pkthdr.len;
+				n->m_next = NULL;
+				m_freem(m);
+			} else {
+				m_copydata(m, 0, maxlen, mtod(n, caddr_t));
+				m_adj(m, maxlen);
+				n->m_len = maxlen;
+				n->m_pkthdr.len = m->m_pkthdr.len;
+				n->m_next = m;
+				m->m_flags &= ~M_PKTHDR;
+			}
+			m = n;
+		}
+#endif
+
+		ip6 = mtod(m, struct ip6_hdr *);
+		ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - stripsiz);
+
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_ESP, spi) != 0) {
+			ipsec6stat.in_nomem++;
+			goto bad;
+		}
+	}
+
+	*offp = off;
+	*mp = m;
+
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP esp6_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	ipsec6stat.in_success++;
+	return nxt;
+
+bad:
+	if (sav) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP esp6_input call free SA:%p\n", sav));
+		key_freesav(sav);
+	}
+	if (m)
+		m_freem(m);
+	return IPPROTO_DONE;
+}
+
+void
+esp6_ctlinput(cmd, sa, d)
+	int cmd;
+	struct sockaddr *sa;
+	void *d;
+{
+	const struct newesp *espp;
+	struct newesp esp;
+	struct ip6ctlparam *ip6cp = NULL, ip6cp1;
+	struct secasvar *sav;
+	struct ip6_hdr *ip6;
+	struct mbuf *m;
+	int off=0;
+	struct sockaddr_in6 sa6_src, sa6_dst;
+
+	if (sa->sa_family != AF_INET6 ||
+	    sa->sa_len != sizeof(struct sockaddr_in6))
+		return;
+	if ((unsigned)cmd >= PRC_NCMDS)
+		return;
+
+	/* if the parameter is from icmp6, decode it. */
+	if (d != NULL) {
+		ip6cp = (struct ip6ctlparam *)d;
+		m = ip6cp->ip6c_m;
+		ip6 = ip6cp->ip6c_ip6;
+		off = ip6cp->ip6c_off;
+	} else {
+		m = NULL;
+		ip6 = NULL;
+	}
+
+	if (ip6) {
+		/*
+		 * Notify the error to all possible sockets via pfctlinput2.
+		 * Since the upper layer information (such as protocol type,
+		 * source and destination ports) is embedded in the encrypted
+		 * data and might have been cut, we can't directly call
+		 * an upper layer ctlinput function. However, the pcbnotify
+		 * function will consider source and destination addresses
+		 * as well as the flow info value, and may be able to find
+		 * some PCB that should be notified.
+		 * Although pfctlinput2 will call esp6_ctlinput(), there is
+		 * no possibility of an infinite loop of function calls,
+		 * because we don't pass the inner IPv6 header.
+		 */
+		bzero(&ip6cp1, sizeof(ip6cp1));
+		ip6cp1.ip6c_src = ip6cp->ip6c_src;
+		pfctlinput2(cmd, sa, (void *)&ip6cp1);
+
+		/*
+		 * Then go to special cases that need ESP header information.
+		 * XXX: We assume that when ip6 is non NULL,
+		 * M and OFF are valid.
+		 */
+
+		/* check if we can safely examine src and dst ports */
+		if (m->m_pkthdr.len < off + sizeof(esp))
+			return;
+
+		if (m->m_len < off + sizeof(esp)) {
+			/*
+			 * this should be rare case,
+			 * so we compromise on this copy...
+			 */
+			m_copydata(m, off, sizeof(esp), (caddr_t)&esp);
+			espp = &esp;
+		} else
+			espp = (struct newesp*)(mtod(m, caddr_t) + off);
+
+		if (cmd == PRC_MSGSIZE) {
+			int valid = 0;
+
+			/*
+			 * Check to see if we have a valid SA corresponding to
+			 * the address in the ICMP message payload.
+			 */
+			sav = key_allocsa(AF_INET6,
+					  (caddr_t)&sa6_src.sin6_addr,
+					  (caddr_t)&sa6_dst, IPPROTO_ESP,
+					  espp->esp_spi);
+			if (sav) {
+				if (sav->state == SADB_SASTATE_MATURE ||
+				    sav->state == SADB_SASTATE_DYING)
+					valid++;
+				key_freesav(sav);
+			}
+
+			/* XXX Further validation? */
+
+			/*
+			 * Depending on the value of "valid" and routing table
+			 * size (mtudisc_{hi,lo}wat), we will:
+			 * - recalcurate the new MTU and create the
+			 *   corresponding routing entry, or
+			 * - ignore the MTU change notification.
+			 */
+			icmp6_mtudisc_update((struct ip6ctlparam *)d, valid);
+		}
+	} else {
+		/* we normally notify any pcb here */
+	}
+}
+#endif /* INET6 */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/esp_output.c
@@ -0,0 +1,727 @@
+//==========================================================================
+//
+//      src/sys/netinet6/esp_output.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_output.c,v 1.44 2001/07/26 06:53:15 jinmei Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+#include <sys/param.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+#include <sys/malloc.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#include <netinet6/esp.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+static int esp_output __P((struct mbuf *, u_char *, struct mbuf *,
+	struct ipsecrequest *, int));
+
+/*
+ * compute ESP header size.
+ */
+size_t
+esp_hdrsiz(isr)
+	struct ipsecrequest *isr;
+{
+	struct secasvar *sav;
+	const struct esp_algorithm *algo;
+	const struct ah_algorithm *aalgo;
+	size_t ivlen;
+	size_t authlen;
+	size_t hdrsiz;
+
+	/* sanity check */
+	if (isr == NULL)
+		panic("esp_hdrsiz: NULL was passed.\n");
+
+	sav = isr->sav;
+
+	if (isr->saidx.proto != IPPROTO_ESP)
+		panic("unsupported mode passed to esp_hdrsiz");
+
+	if (sav == NULL)
+		goto estimate;
+	if (sav->state != SADB_SASTATE_MATURE
+	 && sav->state != SADB_SASTATE_DYING)
+		goto estimate;
+
+	/* we need transport mode ESP. */
+	algo = esp_algorithm_lookup(sav->alg_enc);
+	if (!algo)
+		goto estimate;
+	ivlen = sav->ivlen;
+	if (ivlen < 0)
+		goto estimate;
+
+	/*
+	 * XXX
+	 * right now we don't calcurate the padding size.  simply
+	 * treat the padding size as constant, for simplicity.
+	 *
+	 * XXX variable size padding support
+	 */
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1827 */
+		hdrsiz = sizeof(struct esp) + ivlen + 9;
+	} else {
+		/* RFC 2406 */
+		aalgo = ah_algorithm_lookup(sav->alg_auth);
+		if (aalgo && sav->replay && sav->key_auth)
+			authlen = (aalgo->sumsiz)(sav);
+		else
+			authlen = 0;
+		hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen;
+	}
+
+	return hdrsiz;
+
+   estimate:
+	/*
+	 * ASSUMING:
+	 *	sizeof(struct newesp) > sizeof(struct esp).
+	 *	esp_max_ivlen() = max ivlen for CBC mode
+	 *	9 = (maximum padding length without random padding length)
+	 *	   + (Pad Length field) + (Next Header field).
+	 *	16 = maximum ICV we support.
+	 */
+	return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16;
+}
+
+/*
+ * Modify the packet so that the payload is encrypted.
+ * The mbuf (m) must start with IPv4 or IPv6 header.
+ * On failure, free the given mbuf and return NULL.
+ *
+ * on invocation:
+ *	m   nexthdrp md
+ *	v   v        v
+ *	IP ......... payload
+ * during the encryption:
+ *	m   nexthdrp mprev md
+ *	v   v        v     v
+ *	IP ............... esp iv payload pad padlen nxthdr
+ *	                   <--><-><------><--------------->
+ *	                   esplen plen    extendsiz
+ *	                       ivlen
+ *	                   <-----> esphlen
+ *	<-> hlen
+ *	<-----------------> espoff
+ */
+static int
+esp_output(m, nexthdrp, md, isr, af)
+	struct mbuf *m;
+	u_char *nexthdrp;
+	struct mbuf *md;
+	struct ipsecrequest *isr;
+	int af;
+{
+	struct mbuf *n;
+	struct mbuf *mprev;
+	struct esp *esp;
+	struct esptail *esptail;
+	struct secasvar *sav = isr->sav;
+	const struct esp_algorithm *algo;
+	u_int32_t spi;
+	u_int8_t nxt = 0;
+	size_t plen;	/* payload length to be encrypted */
+	size_t espoff;
+	int ivlen;
+	int afnumber;
+	size_t extendsiz;
+	int error = 0;
+	struct ipsecstat *stat;
+
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		afnumber = 4;
+		stat = &ipsecstat;
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		afnumber = 6;
+		stat = &ipsec6stat;
+		break;
+#endif
+	default:
+		ipseclog((LOG_ERR, "esp_output: unsupported af %d\n", af));
+		return 0;	/* no change at all */
+	}
+
+	/* some sanity check */
+	if ((sav->flags & SADB_X_EXT_OLD) == 0 && !sav->replay) {
+		switch (af) {
+#ifdef INET
+		case AF_INET:
+		    {
+			struct ip *ip;
+
+			ip = mtod(m, struct ip *);
+			ipseclog((LOG_DEBUG, "esp4_output: internal error: "
+				"sav->replay is null: %x->%x, SPI=%u\n",
+				(u_int32_t)ntohl(ip->ip_src.s_addr),
+				(u_int32_t)ntohl(ip->ip_dst.s_addr),
+				(u_int32_t)ntohl(sav->spi)));
+			ipsecstat.out_inval++;
+			break;
+		    }
+#endif /* INET */
+#ifdef INET6
+		case AF_INET6:
+			ipseclog((LOG_DEBUG, "esp6_output: internal error: "
+				"sav->replay is null: SPI=%u\n",
+				(u_int32_t)ntohl(sav->spi)));
+			ipsec6stat.out_inval++;
+			break;
+#endif /* INET6 */
+		default:
+			panic("esp_output: should not reach here");
+		}
+		m_freem(m);
+		return EINVAL;
+	}
+
+	algo = esp_algorithm_lookup(sav->alg_enc);
+	if (!algo) {
+		ipseclog((LOG_ERR, "esp_output: unsupported algorithm: "
+		    "SPI=%u\n", (u_int32_t)ntohl(sav->spi)));
+		m_freem(m);
+		return EINVAL;
+	}
+	spi = sav->spi;
+	ivlen = sav->ivlen;
+	/* should be okey */
+	if (ivlen < 0) {
+		panic("invalid ivlen");
+	}
+
+    {
+	/*
+	 * insert ESP header.
+	 * XXX inserts ESP header right after IPv4 header.  should
+	 * chase the header chain.
+	 * XXX sequential number
+	 */
+#ifdef INET
+	struct ip *ip = NULL;
+#endif
+#ifdef INET6
+	struct ip6_hdr *ip6 = NULL;
+#endif
+	size_t esplen;	/* sizeof(struct esp/newesp) */
+	size_t esphlen;	/* sizeof(struct esp/newesp) + ivlen */
+	size_t hlen = 0;	/* ip header len */
+
+	if (sav->flags & SADB_X_EXT_OLD) {
+		/* RFC 1827 */
+		esplen = sizeof(struct esp);
+	} else {
+		/* RFC 2406 */
+		if (sav->flags & SADB_X_EXT_DERIV)
+			esplen = sizeof(struct esp);
+		else
+			esplen = sizeof(struct newesp);
+	}
+	esphlen = esplen + ivlen;
+
+	for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+		;
+	if (mprev == NULL || mprev->m_next != md) {
+		ipseclog((LOG_DEBUG, "esp%d_output: md is not in chain\n",
+		    afnumber));
+		m_freem(m);
+		return EINVAL;
+	}
+
+	plen = 0;
+	for (n = md; n; n = n->m_next)
+		plen += n->m_len;
+
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+		hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+		hlen = ip->ip_hl << 2;
+#endif
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		ip6 = mtod(m, struct ip6_hdr *);
+		hlen = sizeof(*ip6);
+		break;
+#endif
+	}
+
+	/* make the packet over-writable */
+	mprev->m_next = NULL;
+	if ((md = ipsec_copypkt(md)) == NULL) {
+		m_freem(m);
+		error = ENOBUFS;
+		goto fail;
+	}
+	mprev->m_next = md;
+
+	espoff = m->m_pkthdr.len - plen;
+
+	/*
+	 * grow the mbuf to accomodate ESP header.
+	 * before: IP ... payload
+	 * after:  IP ... ESP IV payload
+	 */
+	if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) {
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (!n) {
+			m_freem(m);
+			error = ENOBUFS;
+			goto fail;
+		}
+		n->m_len = esphlen;
+		mprev->m_next = n;
+		n->m_next = md;
+		m->m_pkthdr.len += esphlen;
+		esp = mtod(n, struct esp *);
+	} else {
+		md->m_len += esphlen;
+		md->m_data -= esphlen;
+		m->m_pkthdr.len += esphlen;
+		esp = mtod(md, struct esp *);
+	}
+	
+	nxt = *nexthdrp;
+	*nexthdrp = IPPROTO_ESP;
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		if (esphlen < (IP_MAXPACKET - ntohs(ip->ip_len)))
+			ip->ip_len = htons(ntohs(ip->ip_len) + esphlen);
+		else {
+			ipseclog((LOG_ERR,
+			    "IPv4 ESP output: size exceeds limit\n"));
+			ipsecstat.out_inval++;
+			m_freem(m);
+			error = EMSGSIZE;
+			goto fail;
+		}
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		/* total packet length will be computed in ip6_output() */
+		break;
+#endif
+	}
+    }
+
+	/* initialize esp header. */
+	esp->esp_spi = spi;
+	if ((sav->flags & SADB_X_EXT_OLD) == 0) {
+		struct newesp *nesp;
+		nesp = (struct newesp *)esp;
+		if (sav->replay->count == ~0) {
+			if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+				/* XXX Is it noisy ? */
+				ipseclog((LOG_WARNING,
+				    "replay counter overflowed. %s\n",
+				    ipsec_logsastr(sav)));
+				stat->out_inval++;
+				m_freem(m);
+				return EINVAL;
+			}
+		}
+		sav->replay->count++;
+		/*
+		 * XXX sequence number must not be cycled, if the SA is
+		 * installed by IKE daemon.
+		 */
+		nesp->esp_seq = htonl(sav->replay->count);
+	}
+
+    {
+	/*
+	 * find the last mbuf. make some room for ESP trailer.
+	 */
+#ifdef INET
+	struct ip *ip = NULL;
+#endif
+	size_t padbound;
+	u_char *extend;
+	int i;
+	int randpadmax;
+
+	if (algo->padbound)
+		padbound = algo->padbound;
+	else
+		padbound = 4;
+	/* ESP packet, including nxthdr field, must be length of 4n */
+	if (padbound < 4)
+		padbound = 4;
+	
+	extendsiz = padbound - (plen % padbound);
+	if (extendsiz == 1)
+		extendsiz = padbound + 1;
+
+	/* random padding */
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		randpadmax = ip4_esp_randpad;
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		randpadmax = ip6_esp_randpad;
+		break;
+#endif
+	default:
+		randpadmax = -1;
+		break;
+	}
+	if (randpadmax < 0 || plen + extendsiz >= randpadmax)
+		;
+	else {
+		int n;
+
+		/* round */
+		randpadmax = (randpadmax / padbound) * padbound;
+		n = (randpadmax - plen + extendsiz) / padbound;
+
+		if (n > 0)
+			n = (random() % n) * padbound;
+		else
+			n = 0;
+
+		/*
+		 * make sure we do not pad too much.
+		 * MLEN limitation comes from the trailer attachment
+		 * code below.
+		 * 256 limitation comes from sequential padding.
+		 * also, the 1-octet length field in ESP trailer imposes
+		 * limitation (but is less strict than sequential padding
+		 * as length field do not count the last 2 octets).
+		 */
+		if (extendsiz + n <= MLEN && extendsiz + n < 256)
+			extendsiz += n;
+	}
+
+#ifdef DIAGNOSTIC
+	if (extendsiz > MLEN || extendsiz >= 256)
+		panic("extendsiz too big in esp_output");
+#endif
+
+	n = m;
+	while (n->m_next)
+		n = n->m_next;
+
+	/*
+	 * if M_EXT, the external mbuf data may be shared among
+	 * two consequtive TCP packets, and it may be unsafe to use the
+	 * trailing space.
+	 */
+	if (!(n->m_flags & M_EXT) && extendsiz < M_TRAILINGSPACE(n)) {
+		extend = mtod(n, u_char *) + n->m_len;
+		n->m_len += extendsiz;
+		m->m_pkthdr.len += extendsiz;
+	} else {
+		struct mbuf *nn;
+
+		MGET(nn, M_DONTWAIT, MT_DATA);
+		if (!nn) {
+			ipseclog((LOG_DEBUG, "esp%d_output: can't alloc mbuf",
+			    afnumber));
+			m_freem(m);
+			error = ENOBUFS;
+			goto fail;
+		}
+		extend = mtod(nn, u_char *);
+		nn->m_len = extendsiz;
+		nn->m_next = NULL;
+		n->m_next = nn;
+		n = nn;
+		m->m_pkthdr.len += extendsiz;
+	}
+	switch (sav->flags & SADB_X_EXT_PMASK) {
+	case SADB_X_EXT_PRAND:
+		key_randomfill(extend, extendsiz);
+		break;
+	case SADB_X_EXT_PZERO:
+		bzero(extend, extendsiz);
+		break;
+	case SADB_X_EXT_PSEQ:
+		for (i = 0; i < extendsiz; i++)
+			extend[i] = (i + 1) & 0xff;
+		break;
+	}
+
+	/* initialize esp trailer. */
+	esptail = (struct esptail *)
+		(mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail));
+	esptail->esp_nxt = nxt;
+	esptail->esp_padlen = extendsiz - 2;
+
+	/* modify IP header (for ESP header part only) */
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		ip = mtod(m, struct ip *);
+		if (extendsiz < (IP_MAXPACKET - ntohs(ip->ip_len)))
+			ip->ip_len = htons(ntohs(ip->ip_len) + extendsiz);
+		else {
+			ipseclog((LOG_ERR,
+			    "IPv4 ESP output: size exceeds limit\n"));
+			ipsecstat.out_inval++;
+			m_freem(m);
+			error = EMSGSIZE;
+			goto fail;
+		}
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		/* total packet length will be computed in ip6_output() */
+		break;
+#endif
+	}
+    }
+
+	/*
+	 * pre-compute and cache intermediate key
+	 */
+	error = esp_schedule(algo, sav);
+	if (error) {
+		m_freem(m);
+		stat->out_inval++;
+		goto fail;
+	}
+
+	/*
+	 * encrypt the packet, based on security association
+	 * and the algorithm specified.
+	 */
+	if (!algo->encrypt)
+		panic("internal error: no encrypt function");
+	if ((*algo->encrypt)(m, espoff, plen + extendsiz, sav, algo, ivlen)) {
+		/* m is already freed */
+		ipseclog((LOG_ERR, "packet encryption failure\n"));
+		stat->out_inval++;
+		error = EINVAL;
+		goto fail;
+	}
+
+	/*
+	 * calculate ICV if required.
+	 */
+	if (!sav->replay)
+		goto noantireplay;
+	if (!sav->key_auth)
+		goto noantireplay;
+	if (sav->key_auth == SADB_AALG_NONE)
+		goto noantireplay;
+
+    {
+	const struct ah_algorithm *aalgo;
+	u_char authbuf[AH_MAXSUMSIZE];
+	struct mbuf *n;
+	u_char *p;
+	size_t siz;
+#ifdef INET
+	struct ip *ip;
+#endif
+
+	aalgo = ah_algorithm_lookup(sav->alg_auth);
+	if (!aalgo)
+		goto noantireplay;
+	siz = ((aalgo->sumsiz)(sav) + 3) & ~(4 - 1);
+	if (AH_MAXSUMSIZE < siz)
+		panic("assertion failed for AH_MAXSUMSIZE");
+
+	if (esp_auth(m, espoff, m->m_pkthdr.len - espoff, sav, authbuf)) {
+		ipseclog((LOG_ERR, "ESP checksum generation failure\n"));
+		m_freem(m);
+		error = EINVAL;
+		stat->out_inval++;
+		goto fail;
+	}
+
+	n = m;
+	while (n->m_next)
+		n = n->m_next;
+
+	if (!(n->m_flags & M_EXT) && siz < M_TRAILINGSPACE(n)) { /* XXX */
+		n->m_len += siz;
+		m->m_pkthdr.len += siz;
+		p = mtod(n, u_char *) + n->m_len - siz;
+	} else {
+		struct mbuf *nn;
+
+		MGET(nn, M_DONTWAIT, MT_DATA);
+		if (!nn) {
+			ipseclog((LOG_DEBUG, "can't alloc mbuf in esp%d_output",
+			    afnumber));
+			m_freem(m);
+			error = ENOBUFS;
+			goto fail;
+		}
+		nn->m_len = siz;
+		nn->m_next = NULL;
+		n->m_next = nn;
+		n = nn;
+		m->m_pkthdr.len += siz;
+		p = mtod(nn, u_char *);
+	}
+	bcopy(authbuf, p, siz);
+
+	/* modify IP header (for ESP header part only) */
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		ip = mtod(m, struct ip *);
+		if (siz < (IP_MAXPACKET - ntohs(ip->ip_len)))
+			ip->ip_len = htons(ntohs(ip->ip_len) + siz);
+		else {
+			ipseclog((LOG_ERR,
+			    "IPv4 ESP output: size exceeds limit\n"));
+			ipsecstat.out_inval++;
+			m_freem(m);
+			error = EMSGSIZE;
+			goto fail;
+		}
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		/* total packet length will be computed in ip6_output() */
+		break;
+#endif
+	}
+    }
+
+noantireplay:
+	if (!m) {
+		ipseclog((LOG_ERR,
+		    "NULL mbuf after encryption in esp%d_output", afnumber));
+	} else
+		stat->out_success++;
+	stat->out_esphist[sav->alg_enc]++;
+	key_sa_recordxfer(sav, m);
+	return 0;
+
+fail:
+#if 1
+	return error;
+#else
+	panic("something bad in esp_output");
+#endif
+}
+
+#ifdef INET
+int
+esp4_output(m, isr)
+	struct mbuf *m;
+	struct ipsecrequest *isr;
+{
+	struct ip *ip;
+	if (m->m_len < sizeof(struct ip)) {
+		ipseclog((LOG_DEBUG, "esp4_output: first mbuf too short\n"));
+		m_freem(m);
+		return 0;
+	}
+	ip = mtod(m, struct ip *);
+	/* XXX assumes that m->m_next points to payload */
+	return esp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+esp6_output(m, nexthdrp, md, isr)
+	struct mbuf *m;
+	u_char *nexthdrp;
+	struct mbuf *md;
+	struct ipsecrequest *isr;
+{
+	if (m->m_len < sizeof(struct ip6_hdr)) {
+		ipseclog((LOG_DEBUG, "esp6_output: first mbuf too short\n"));
+		m_freem(m);
+		return 0;
+	}
+	return esp_output(m, nexthdrp, md, isr, AF_INET6);
+}
+#endif /* INET6 */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/esp_rijndael.c
@@ -0,0 +1,130 @@
+//==========================================================================
+//
+//      src/sys/netinet6/esp_rijndael.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_rijndael.c,v 1.4 2001/03/02 05:53:05 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet6/ipsec.h>
+#include <netinet6/esp.h>
+#include <netinet6/esp_rijndael.h>
+
+#include <crypto/rijndael/rijndael.h>
+
+/* as rijndael uses assymetric scheduled keys, we need to do it twice. */
+int
+esp_rijndael_schedlen(algo)
+	const struct esp_algorithm *algo;
+{
+
+	return sizeof(keyInstance) * 2;
+}
+
+int
+esp_rijndael_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+	keyInstance *k;
+
+	k = (keyInstance *)sav->sched;
+	if (rijndael_makeKey(&k[0], DIR_DECRYPT, _KEYLEN(sav->key_enc) * 8,
+	    _KEYBUF(sav->key_enc)) < 0)
+		return -1;
+	if (rijndael_makeKey(&k[1], DIR_ENCRYPT, _KEYLEN(sav->key_enc) * 8,
+	    _KEYBUF(sav->key_enc)) < 0)
+		return -1;
+	return 0;
+}
+
+int
+esp_rijndael_blockdecrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	cipherInstance c;
+	keyInstance *p;
+
+	/* does not take advantage of CBC mode support */
+	bzero(&c, sizeof(c));
+	if (rijndael_cipherInit(&c, MODE_ECB, NULL) < 0)
+		return -1;
+	p = (keyInstance *)sav->sched;
+	if (rijndael_blockDecrypt(&c, &p[0], s, algo->padbound * 8, d) < 0)
+		return -1;
+	return 0;
+}
+
+int
+esp_rijndael_blockencrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	cipherInstance c;
+	keyInstance *p;
+
+	/* does not take advantage of CBC mode support */
+	bzero(&c, sizeof(c));
+	if (rijndael_cipherInit(&c, MODE_ECB, NULL) < 0)
+		return -1;
+	p = (keyInstance *)sav->sched;
+	if (rijndael_blockEncrypt(&c, &p[1], s, algo->padbound * 8, d) < 0)
+		return -1;
+	return 0;
+}
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/esp_twofish.c
@@ -0,0 +1,133 @@
+//==========================================================================
+//
+//      src/sys/netinet6/esp_twofish.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: esp_twofish.c,v 1.4 2001/03/02 07:04:57 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet6/ipsec.h>
+#include <netinet6/esp.h>
+#include <netinet6/esp_twofish.h>
+
+#include <crypto/twofish/twofish.h>
+
+/*
+ * twofish actually do not use assymetric scheduled keys, however, AES C API
+ * suggests assymetric key setup.
+ */
+int
+esp_twofish_schedlen(algo)
+	const struct esp_algorithm *algo;
+{
+
+	return sizeof(keyInstance) * 2;
+}
+
+int
+esp_twofish_schedule(algo, sav)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+{
+	keyInstance *k;
+
+	k = (keyInstance *)sav->sched;
+	if (twofish_makeKey(&k[0], DIR_DECRYPT, _KEYLEN(sav->key_enc) * 8,
+	    _KEYBUF(sav->key_enc)) < 0)
+		return -1;
+	if (twofish_makeKey(&k[1], DIR_ENCRYPT, _KEYLEN(sav->key_enc) * 8,
+	    _KEYBUF(sav->key_enc)) < 0)
+		return -1;
+	return 0;
+}
+
+int
+esp_twofish_blockdecrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	cipherInstance c;
+	keyInstance *p;
+
+	/* does not take advantage of CBC mode support */
+	bzero(&c, sizeof(c));
+	if (twofish_cipherInit(&c, MODE_ECB, NULL) < 0)
+		return -1;
+	p = (keyInstance *)sav->sched;
+	if (twofish_blockDecrypt(&c, &p[0], s, algo->padbound * 8, d) < 0)
+		return -1;
+	return 0;
+}
+
+int
+esp_twofish_blockencrypt(algo, sav, s, d)
+	const struct esp_algorithm *algo;
+	struct secasvar *sav;
+	u_int8_t *s;
+	u_int8_t *d;
+{
+	cipherInstance c;
+	keyInstance *p;
+
+	/* does not take advantage of CBC mode support */
+	bzero(&c, sizeof(c));
+	if (twofish_cipherInit(&c, MODE_ECB, NULL) < 0)
+		return -1;
+	p = (keyInstance *)sav->sched;
+	if (twofish_blockEncrypt(&c, &p[1], s, algo->padbound * 8, d) < 0)
+		return -1;
+	return 0;
+}
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/in6_gif.c
@@ -0,0 +1,661 @@
+//==========================================================================
+//
+//      src/sys/netinet6/in6_gif.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: in6_gif.c,v 1.88 2001/12/21 03:32:34 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* define it if you want to use encap_attach_func (it helps *BSD merge) */
+/*#define USE_ENCAPCHECK*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/errno.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include <sys/ioctl.h>
+#endif
+#include <sys/queue.h>
+#include <sys/protosw.h>
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/malloc.h>
+#endif
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#ifdef INET
+#include <netinet/ip.h>
+#endif
+#include <netinet/ip_encap.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/in6_gif.h>
+#include <netinet6/in6_var.h>
+#endif
+#include <netinet6/ip6protosw.h>
+#include <netinet/ip_ecn.h>
+#ifdef __OpenBSD__
+#include <netinet/ip_ipsp.h>
+#endif
+
+#include <net/if_gif.h>
+
+#ifdef __OpenBSD__
+#include "bridge.h"
+#endif
+
+static int gif_validate6 __P((const struct ip6_hdr *, struct gif_softc *,
+	struct ifnet *));
+static int in6_gif_rtcachettl = 300; /* XXX see in_gif.c */
+
+int	ip6_gif_hlim = GIF_HLIM;
+
+extern struct domain inet6domain;
+struct ip6protosw in6_gif_protosw =
+{ SOCK_RAW,	&inet6domain,	0/* IPPROTO_IPV[46] */,	PR_ATOMIC|PR_ADDR,
+  in6_gif_input, rip6_output,	in6_gif_ctlinput, rip6_ctloutput,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+  0,
+#else
+  rip6_usrreq,
+#endif
+  0,            0,              0,              0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+  &rip6_usrreqs
+#endif
+};
+
+extern LIST_HEAD(, gif_softc) gif_softc_list;
+
+#ifndef offsetof
+#define offsetof(s, e) ((int)&((s *)0)->e)
+#endif
+
+int
+in6_gif_output(ifp, family, m)
+	struct ifnet *ifp;
+	int family; /* family of the packet to be encapsulate. */
+	struct mbuf *m;
+{
+#ifdef __OpenBSD__
+	struct gif_softc *sc = (struct gif_softc*)ifp;
+        struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
+	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
+	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
+	struct tdb tdb;
+	struct xformsw xfs;
+	int error;
+	int hlen, poff;
+	struct mbuf *mp;
+	long time_second;
+
+	if (sin6_src == NULL || sin6_dst == NULL ||
+	    sin6_src->sin6_family != AF_INET6 ||
+	    sin6_dst->sin6_family != AF_INET6) {
+		m_freem(m);
+		return EAFNOSUPPORT;
+	}
+
+	/* setup dummy tdb.  it highly depends on ipip_output() code. */
+	bzero(&tdb, sizeof(tdb));
+	bzero(&xfs, sizeof(xfs));
+	tdb.tdb_src.sin6.sin6_family = AF_INET6;
+	tdb.tdb_src.sin6.sin6_len = sizeof(struct sockaddr_in6);
+	tdb.tdb_src.sin6.sin6_addr = sin6_src->sin6_addr;
+	tdb.tdb_dst.sin6.sin6_family = AF_INET6;
+	tdb.tdb_dst.sin6.sin6_len = sizeof(struct sockaddr_in6);
+	tdb.tdb_dst.sin6.sin6_addr = sin6_dst->sin6_addr;
+	tdb.tdb_xform = &xfs;
+	xfs.xf_type = -1;	/* not XF_IP4 */
+
+	switch (family) {
+#ifdef INET
+	case AF_INET:
+	    {
+		if (m->m_len < sizeof(struct ip)) {
+			m = m_pullup(m, sizeof(struct ip));
+			if (!m)
+				return ENOBUFS;
+		}
+		hlen = (mtod(m, struct ip *)->ip_hl) << 2;
+		poff = offsetof(struct ip, ip_p);
+		break;
+	    }
+#endif
+#ifdef INET6
+	case AF_INET6:
+	    {
+		hlen = sizeof(struct ip6_hdr);
+		poff = offsetof(struct ip6_hdr, ip6_nxt);
+		break;
+	    }
+#endif
+#if NBRIDGE > 0
+	case AF_LINK:
+		break;
+#endif /* NBRIDGE */
+	default:
+#ifdef DEBUG
+		printf("in6_gif_output: warning: unknown family %d passed\n",
+			family);
+#endif
+		m_freem(m);
+		return EAFNOSUPPORT;
+	}
+	
+#if NBRIDGE > 0
+	if (family == AF_LINK) {
+	        mp = NULL;
+		error = etherip_output(m, &tdb, &mp, 0, 0);
+		if (error)
+		        return error;
+		else if (mp == NULL)
+		        return EFAULT;
+
+		m = mp;
+		goto sendit;
+	}
+#endif /* NBRIDGE */
+
+	/* encapsulate into IPv6 packet */
+	mp = NULL;
+	error = ipip_output(m, &tdb, &mp, hlen, poff);
+	if (error)
+	        return error;
+	else if (mp == NULL)
+	        return EFAULT;
+
+	m = mp;
+
+#if NBRIDGE > 0
+ sendit:
+#endif /* NBRIDGE */
+	/*
+	 * compare address family just for safety.  other validity checks
+	 * are made in in6_selectsrc() called from ip6_output().
+	 */
+	if (sc->gif_ro6.ro_rt && (dst->sin6_family != sin6_dst->sin6_family ||
+				  sc->rtcache_expire == 0 ||
+				  time_second >= sc->rtcache_expire)) {
+		RTFREE(sc->gif_ro6.ro_rt);
+		sc->gif_ro6.ro_rt = NULL;
+	}
+
+#ifdef IPV6_MINMTU
+	/*
+	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
+	 * it is too painful to ask for resend of inner packet, to achieve
+	 * path MTU discovery for encapsulated packets.
+	 */
+	error = ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL);
+#else
+	error = ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL);
+#endif
+
+	if (sc->gif_ro6.ro_rt && time_second >= sc->rtcache_expire)
+		sc->rtcache_expire = time_second + in6_gif_rtcachettl;
+
+	return(error);
+#else	/* !__OpenBSD__ */
+	struct gif_softc *sc = (struct gif_softc*)ifp;
+	struct sockaddr_in6 *dst = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
+	struct sockaddr_in6 *sin6_src = (struct sockaddr_in6 *)sc->gif_psrc;
+	struct sockaddr_in6 *sin6_dst = (struct sockaddr_in6 *)sc->gif_pdst;
+	struct ip6_hdr *ip6;
+	int proto, error;
+	u_int8_t itos, otos;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+	long time_second;
+#endif
+
+	if (sin6_src == NULL || sin6_dst == NULL ||
+	    sin6_src->sin6_family != AF_INET6 ||
+	    sin6_dst->sin6_family != AF_INET6) {
+		m_freem(m);
+		return EAFNOSUPPORT;
+	}
+
+	switch (family) {
+#ifdef INET
+	case AF_INET:
+	    {
+		struct ip *ip;
+
+		proto = IPPROTO_IPV4;
+		if (m->m_len < sizeof(*ip)) {
+			m = m_pullup(m, sizeof(*ip));
+			if (!m)
+				return ENOBUFS;
+		}
+		ip = mtod(m, struct ip *);
+		itos = ip->ip_tos;
+		break;
+	    }
+#endif
+#ifdef INET6
+	case AF_INET6:
+	    {
+		struct ip6_hdr *ip6;
+		proto = IPPROTO_IPV6;
+		if (m->m_len < sizeof(*ip6)) {
+			m = m_pullup(m, sizeof(*ip6));
+			if (!m)
+				return ENOBUFS;
+		}
+		ip6 = mtod(m, struct ip6_hdr *);
+		itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
+		break;
+	    }
+#endif
+#if defined(__NetBSD__) && defined(ISO)
+	case AF_ISO:
+		proto = IPPROTO_EON;
+		itos = 0;
+		break;
+#endif
+	default:
+#ifdef DEBUG
+		printf("in6_gif_output: warning: unknown family %d passed\n",
+			family);
+#endif
+		m_freem(m);
+		return EAFNOSUPPORT;
+	}
+	
+	/* prepend new IP header */
+	M_PREPEND(m, sizeof(struct ip6_hdr), M_DONTWAIT);
+	if (m && m->m_len < sizeof(struct ip6_hdr))
+		m = m_pullup(m, sizeof(struct ip6_hdr));
+	if (m == NULL)
+		return ENOBUFS;
+
+	ip6 = mtod(m, struct ip6_hdr *);
+	ip6->ip6_flow	= 0;
+	ip6->ip6_vfc	&= ~IPV6_VERSION_MASK;
+	ip6->ip6_vfc	|= IPV6_VERSION;
+	ip6->ip6_plen	= htons((u_short)m->m_pkthdr.len);
+	ip6->ip6_nxt	= proto;
+	ip6->ip6_hlim	= ip6_gif_hlim;
+	ip6->ip6_src	= sin6_src->sin6_addr;
+	/* bidirectional configured tunnel mode */
+	if (!IN6_IS_ADDR_UNSPECIFIED(&sin6_dst->sin6_addr))
+		ip6->ip6_dst = sin6_dst->sin6_addr;
+	else  {
+		m_freem(m);
+		return ENETUNREACH;
+	}
+	if (ifp->if_flags & IFF_LINK1)
+		ip_ecn_ingress(ECN_ALLOWED, &otos, &itos);
+	else
+		ip_ecn_ingress(ECN_NOCARE, &otos, &itos);
+	ip6->ip6_flow &= ~ntohl(0xff00000);
+	ip6->ip6_flow |= htonl((u_int32_t)otos << 20);
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+	time_second = time.tv_sec;
+#endif
+	/*
+	 * compare address family just for safety.  other validity checks
+	 * are made in in6_selectsrc() called from ip6_output().
+	 */
+	if (sc->gif_ro6.ro_rt && (dst->sin6_family != sin6_dst->sin6_family ||
+				  sc->rtcache_expire == 0 ||
+				  time_second >= sc->rtcache_expire)) {
+		/*
+		 * If the cached route is not valid or has expired,
+		 * clear the stale cache and let ip6_output make a new cached
+		 * route.
+		 */
+		RTFREE(sc->gif_ro6.ro_rt);
+		sc->gif_ro6.ro_rt = NULL;
+	}
+
+#ifdef IPV6_MINMTU
+	/*
+	 * force fragmentation to minimum MTU, to avoid path MTU discovery.
+	 * it is too painful to ask for resend of inner packet, to achieve
+	 * path MTU discovery for encapsulated packets.
+	 */
+	error = ip6_output(m, 0, &sc->gif_ro6, IPV6_MINMTU, 0, NULL);
+#else
+	error = ip6_output(m, 0, &sc->gif_ro6, 0, 0, NULL);
+#endif
+
+	/*
+	 * if a (new) cached route has been created in ip6_output(), extend
+	 * the expiration time.
+	 */
+	if (sc->gif_ro6.ro_rt && time_second >= sc->rtcache_expire)
+		sc->rtcache_expire = time_second + in6_gif_rtcachettl;
+
+	return(error);
+#endif	/* __OpenBSD__ */
+}
+
+int in6_gif_input(mp, offp, proto)
+	struct mbuf **mp;
+	int *offp, proto;
+{
+	struct mbuf *m = *mp;
+	struct ifnet *gifp = NULL;
+	struct ip6_hdr *ip6;
+#ifndef __OpenBSD__
+	int af = 0;
+	u_int32_t otos;
+#endif
+
+	ip6 = mtod(m, struct ip6_hdr *);
+
+	gifp = (struct ifnet *)encap_getarg(m);
+
+	if (gifp == NULL || (gifp->if_flags & IFF_UP) == 0) {
+		m_freem(m);
+		ip6stat.ip6s_nogif++;
+		return IPPROTO_DONE;
+	}
+#ifndef USE_ENCAPCHECK
+	if (!gif_validate6(ip6, (struct gif_softc *)gifp, m->m_pkthdr.rcvif)) {
+		m_freem(m);
+		ip6stat.ip6s_nogif++;
+		return IPPROTO_DONE;
+	}
+#endif
+
+#ifdef __OpenBSD__
+	m->m_pkthdr.rcvif = gifp;
+	gifp->if_ipackets++;
+	gifp->if_ibytes += m->m_pkthdr.len;
+	ipip_input(m, *offp, gifp);
+	return IPPROTO_DONE;
+#else
+	otos = ip6->ip6_flow;
+	m_adj(m, *offp);
+
+	switch (proto) {
+#ifdef INET
+	case IPPROTO_IPV4:
+	    {
+		struct ip *ip;
+		u_int8_t otos8;
+		af = AF_INET;
+		otos8 = (ntohl(otos) >> 20) & 0xff;
+		if (m->m_len < sizeof(*ip)) {
+			m = m_pullup(m, sizeof(*ip));
+			if (!m)
+				return IPPROTO_DONE;
+		}
+		ip = mtod(m, struct ip *);
+		if (gifp->if_flags & IFF_LINK1)
+			ip_ecn_egress(ECN_ALLOWED, &otos8, &ip->ip_tos);
+		else
+			ip_ecn_egress(ECN_NOCARE, &otos8, &ip->ip_tos);
+		break;
+	    }
+#endif /* INET */
+#ifdef INET6
+	case IPPROTO_IPV6:
+	    {
+		struct ip6_hdr *ip6;
+		af = AF_INET6;
+		if (m->m_len < sizeof(*ip6)) {
+			m = m_pullup(m, sizeof(*ip6));
+			if (!m)
+				return IPPROTO_DONE;
+		}
+		ip6 = mtod(m, struct ip6_hdr *);
+		if (gifp->if_flags & IFF_LINK1)
+			ip6_ecn_egress(ECN_ALLOWED, &otos, &ip6->ip6_flow);
+		else
+			ip6_ecn_egress(ECN_NOCARE, &otos, &ip6->ip6_flow);
+		break;
+	    }
+#endif
+#if defined(__NetBSD__) && defined(ISO)
+	case IPPROTO_EON:
+		af = AF_ISO;
+		break;
+#endif
+	default:
+		ip6stat.ip6s_nogif++;
+		m_freem(m);
+		return IPPROTO_DONE;
+	}
+		
+	gif_input(m, af, gifp);
+	return IPPROTO_DONE;
+#endif
+}
+
+/*
+ * validate outer address.
+ */
+static int
+gif_validate6(ip6, sc, ifp)
+	const struct ip6_hdr *ip6;
+	struct gif_softc *sc;
+	struct ifnet *ifp;
+{
+	struct sockaddr_in6 *src, *dst;
+
+	src = (struct sockaddr_in6 *)sc->gif_psrc;
+	dst = (struct sockaddr_in6 *)sc->gif_pdst;
+
+	/* check for address match */
+	if (!IN6_ARE_ADDR_EQUAL(&src->sin6_addr, &ip6->ip6_dst) ||
+	    !IN6_ARE_ADDR_EQUAL(&dst->sin6_addr, &ip6->ip6_src))
+		return 0;
+
+	/* martian filters on outer source - done in ip6_input */
+
+	/* ingress filters on outer source */
+	if ((sc->gif_if.if_flags & IFF_LINK2) == 0 && ifp) {
+		struct sockaddr_in6 sin6;
+		struct rtentry *rt;
+
+		bzero(&sin6, sizeof(sin6));
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_len = sizeof(struct sockaddr_in6);
+		sin6.sin6_addr = ip6->ip6_src;
+		/* XXX scopeid */
+#ifdef __FreeBSD__
+		rt = rtalloc1((struct sockaddr *)&sin6, 0, 0UL);
+#else
+		rt = rtalloc1((struct sockaddr *)&sin6, 0);
+#endif
+		if (!rt || rt->rt_ifp != ifp) {
+#if 0
+			log(LOG_WARNING, "%s: packet from %s dropped "
+			    "due to ingress filter\n", if_name(&sc->gif_if),
+			    ip6_sprintf(&sin6.sin6_addr));
+#endif
+			if (rt)
+				rtfree(rt);
+			return 0;
+		}
+		rtfree(rt);
+	}
+
+	return 128 * 2;
+}
+
+/*
+ * we know that we are in IFF_UP, outer address available, and outer family
+ * matched the physical addr family.  see gif_encapcheck().
+ */
+int
+gif_encapcheck6(m, off, proto, arg)
+	const struct mbuf *m;
+	int off;
+	int proto;
+	void *arg;
+{
+	struct ip6_hdr ip6;
+	struct gif_softc *sc;
+	struct ifnet *ifp;
+
+	/* sanity check done in caller */
+	sc = (struct gif_softc *)arg;
+
+	/* LINTED const cast */
+	m_copydata((struct mbuf *)m, 0, sizeof(ip6), (caddr_t)&ip6);
+	ifp = ((m->m_flags & M_PKTHDR) != 0) ? m->m_pkthdr.rcvif : NULL;
+
+	return gif_validate6(&ip6, sc, ifp);
+}
+
+int
+in6_gif_attach(sc)
+	struct gif_softc *sc;
+{
+#ifndef USE_ENCAPCHECK
+	struct sockaddr_in6 mask6;
+
+	bzero(&mask6, sizeof(mask6));
+	mask6.sin6_len = sizeof(struct sockaddr_in6);
+	mask6.sin6_addr.s6_addr32[0] = mask6.sin6_addr.s6_addr32[1] = 
+	    mask6.sin6_addr.s6_addr32[2] = mask6.sin6_addr.s6_addr32[3] = ~0;
+
+	if (!sc->gif_psrc || !sc->gif_pdst)
+		return EINVAL;
+	sc->encap_cookie6 = encap_attach(AF_INET6, -1, sc->gif_psrc,
+	    (struct sockaddr *)&mask6, sc->gif_pdst, (struct sockaddr *)&mask6,
+	    (struct protosw *)&in6_gif_protosw, sc);
+#else
+	sc->encap_cookie6 = encap_attach_func(AF_INET6, -1, gif_encapcheck,
+	    (struct protosw *)&in6_gif_protosw, sc);
+#endif
+	if (sc->encap_cookie6 == NULL)
+		return EEXIST;
+	return 0;
+}
+
+int
+in6_gif_detach(sc)
+	struct gif_softc *sc;
+{
+	int error;
+
+	error = encap_detach(sc->encap_cookie6);
+	if (error == 0)
+		sc->encap_cookie6 = NULL;
+	return error;
+}
+
+void
+in6_gif_ctlinput(cmd, sa, d)
+	int cmd;
+	struct sockaddr *sa;
+	void *d;
+{
+	struct gif_softc *sc;
+	struct ip6ctlparam *ip6cp = NULL;
+	struct mbuf *m;
+	struct ip6_hdr *ip6;
+	int off;
+	void *cmdarg;
+	const struct sockaddr_in6 *sa6_src = NULL;
+	struct sockaddr_in6 *dst6;
+
+	if (sa->sa_family != AF_INET6 ||
+	    sa->sa_len != sizeof(struct sockaddr_in6))
+		return;
+
+	if ((unsigned)cmd >= PRC_NCMDS)
+		return;
+	if (cmd == PRC_HOSTDEAD)
+		d = NULL;
+	else if (inet6ctlerrmap[cmd] == 0)
+		return;
+
+	/* if the parameter is from icmp6, decode it. */
+	if (d != NULL) {
+		ip6cp = (struct ip6ctlparam *)d;
+		m = ip6cp->ip6c_m;
+		ip6 = ip6cp->ip6c_ip6;
+		off = ip6cp->ip6c_off;
+		cmdarg = ip6cp->ip6c_cmdarg;
+		sa6_src = ip6cp->ip6c_src;
+	} else {
+		m = NULL;
+		ip6 = NULL;
+		cmdarg = NULL;
+		sa6_src = &sa6_any;
+	}
+
+	if (!ip6)
+		return;
+
+	/*
+	 * for now we don't care which type it was, just flush the route cache.
+	 * XXX slow.  sc (or sc->encap_cookie6) should be passed from
+	 * ip_encap.c.
+	 */
+	for (sc = LIST_FIRST(&gif_softc_list); sc;
+	     sc = LIST_NEXT(sc, gif_list)) {
+		if ((sc->gif_if.if_flags & IFF_RUNNING) == 0)
+			continue;
+		if (sc->gif_psrc->sa_family != AF_INET6)
+			continue;
+		if (!sc->gif_ro6.ro_rt)
+			continue;
+
+		dst6 = (struct sockaddr_in6 *)&sc->gif_ro6.ro_dst;
+		/* XXX scope */
+		if (IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst6->sin6_addr)) {
+			/* flush route cache */
+			RTFREE(sc->gif_ro6.ro_rt);
+			sc->gif_ro6.ro_rt = NULL;
+		}
+	}
+}
--- a/packages/net/bsd_tcpip/current/src/sys/netinet6/ip6_output.c
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ip6_output.c
@@ -2775,10 +2775,10 @@ do { \
 				struct mbuf **mp = &m;
 
 				error = soopt_getm(sopt, &m); /* XXX */
-				if (error != NULL)
+				if (error != 0)
 					break;
 				error = soopt_mcopyin(sopt, m); /* XXX */
-				if (error != NULL)
+				if (error != 0)
 					break;
 #endif
 				if (m) {
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ipcomp_core.c
@@ -0,0 +1,358 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ipcomp_core.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ipcomp_core.c,v 1.25 2001/07/26 06:53:17 jinmei Exp $	*/
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <net/zlib.h>
+
+#include <netinet6/ipcomp.h>
+#include <netinet6/ipsec.h>
+
+static void *deflate_alloc __P((void *, u_int, u_int));
+static void deflate_free __P((void *, void *));
+static int deflate_common __P((struct mbuf *, struct mbuf *, size_t *, int));
+static int deflate_compress __P((struct mbuf *, struct mbuf *, size_t *));
+static int deflate_decompress __P((struct mbuf *, struct mbuf *, size_t *));
+
+/*
+ * We need to use default window size (2^15 = 32Kbytes as of writing) for
+ * inbound case.  Otherwise we get interop problem.
+ * Use negative value to avoid Adler32 checksum.  This is an undocumented
+ * feature in zlib (see ipsec wg mailing list archive in January 2000).
+ */
+static int deflate_policy = Z_DEFAULT_COMPRESSION;
+static int deflate_window_out = -12;
+static const int deflate_window_in = -1 * MAX_WBITS;	/* don't change it */
+static int deflate_memlevel = MAX_MEM_LEVEL;
+
+static const struct ipcomp_algorithm ipcomp_algorithms[] = {
+	{ deflate_compress, deflate_decompress, 90 },
+};
+
+const struct ipcomp_algorithm *
+ipcomp_algorithm_lookup(idx)
+	int idx;
+{
+
+	if (idx == SADB_X_CALG_DEFLATE)
+		return &ipcomp_algorithms[0];
+	return NULL;
+}
+
+static void *
+deflate_alloc(aux, items, siz)
+	void *aux;
+	u_int items;
+	u_int siz;
+{
+	void *ptr;
+	ptr = malloc(items * siz, M_TEMP, M_NOWAIT);
+	return ptr;
+}
+
+static void
+deflate_free(aux, ptr)
+	void *aux;
+	void *ptr;
+{
+	free(ptr, M_TEMP);
+}
+
+static int
+deflate_common(m, md, lenp, mode)
+	struct mbuf *m;
+	struct mbuf *md;
+	size_t *lenp;
+	int mode;	/* 0: compress 1: decompress */
+{
+	struct mbuf *mprev;
+	struct mbuf *p;
+	struct mbuf *n = NULL, *n0 = NULL, **np;
+	z_stream zs;
+	int error = 0;
+	int zerror;
+	size_t offset;
+
+#define MOREBLOCK() \
+do { \
+	/* keep the reply buffer into our chain */		\
+	if (n) {						\
+		n->m_len = zs.total_out - offset;		\
+		offset = zs.total_out;				\
+		*np = n;					\
+		np = &n->m_next;				\
+		n = NULL;					\
+	}							\
+								\
+	/* get a fresh reply buffer */				\
+	MGET(n, M_DONTWAIT, MT_DATA);				\
+	if (n) {						\
+		MCLGET(n, M_DONTWAIT);				\
+	}							\
+	if (!n) {						\
+		error = ENOBUFS;				\
+		goto fail;					\
+	}							\
+	n->m_len = 0;						\
+	n->m_len = M_TRAILINGSPACE(n);				\
+	n->m_next = NULL;					\
+	/* 							\
+	 * if this is the first reply buffer, reserve		\
+	 * region for ipcomp header.				\
+	 */							\
+	if (*np == NULL) {					\
+		n->m_len -= sizeof(struct ipcomp);		\
+		n->m_data += sizeof(struct ipcomp);		\
+	}							\
+								\
+	zs.next_out = mtod(n, u_int8_t *);			\
+	zs.avail_out = n->m_len;				\
+} while (0)
+
+	for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+		;
+	if (!mprev)
+		panic("md is not in m in deflate_common");
+
+	bzero(&zs, sizeof(zs));
+	zs.zalloc = deflate_alloc;
+	zs.zfree = deflate_free;
+
+	zerror = mode ? inflateInit2(&zs, deflate_window_in)
+		      : deflateInit2(&zs, deflate_policy, Z_DEFLATED,
+				deflate_window_out, deflate_memlevel,
+				Z_DEFAULT_STRATEGY);
+	if (zerror != Z_OK) {
+		error = ENOBUFS;
+		goto fail;
+	}
+
+	n0 = n = NULL;
+	np = &n0;
+	offset = 0;
+	zerror = 0;
+	p = md;
+	while (p && p->m_len == 0) {
+		p = p->m_next;
+	}
+
+	/* input stream and output stream are available */
+	while (p && zs.avail_in == 0) {
+		/* get input buffer */
+		if (p && zs.avail_in == 0) {
+			zs.next_in = mtod(p, u_int8_t *);
+			zs.avail_in = p->m_len;
+			p = p->m_next;
+			while (p && p->m_len == 0) {
+				p = p->m_next;
+			}
+		}
+
+		/* get output buffer */
+		if (zs.next_out == NULL || zs.avail_out == 0) {
+			MOREBLOCK();
+		}
+
+		zerror = mode ? inflate(&zs, Z_NO_FLUSH)
+			      : deflate(&zs, Z_NO_FLUSH);
+
+		if (zerror == Z_STREAM_END)
+			; /* once more. */
+		else if (zerror == Z_OK) {
+			/* inflate: Z_OK can indicate the end of decode */
+			if (mode && !p && zs.avail_out != 0)
+				goto terminate;
+			else
+				; /* once more. */
+		} else {
+			if (zs.msg) {
+				ipseclog((LOG_ERR, "ipcomp_%scompress: "
+				    "%sflate(Z_NO_FLUSH): %s\n",
+				    mode ? "de" : "", mode ? "in" : "de",
+				    zs.msg));
+			} else {
+				ipseclog((LOG_ERR, "ipcomp_%scompress: "
+				    "%sflate(Z_NO_FLUSH): unknown error (%d)\n",
+				    mode ? "de" : "", mode ? "in" : "de",
+				    zerror));
+			}
+			mode ? inflateEnd(&zs) : deflateEnd(&zs);
+			error = EINVAL;
+			goto fail;
+		}
+	}
+
+	if (zerror == Z_STREAM_END)
+		goto terminate;
+
+	/* termination */
+	while (1) {
+		/* get output buffer */
+		if (zs.next_out == NULL || zs.avail_out == 0) {
+			MOREBLOCK();
+		}
+
+		zerror = mode ? inflate(&zs, Z_FINISH)
+			      : deflate(&zs, Z_FINISH);
+
+		if (zerror == Z_STREAM_END)
+			break;
+		else if (zerror == Z_OK)
+			; /* once more. */
+		else {
+			if (zs.msg) {
+				ipseclog((LOG_ERR, "ipcomp_%scompress: "
+				    "%sflate(Z_FINISH): %s\n",
+				    mode ? "de" : "", mode ? "in" : "de",
+				    zs.msg));
+			} else {
+				ipseclog((LOG_ERR, "ipcomp_%scompress: "
+				    "%sflate(Z_FINISH): unknown error (%d)\n",
+				    mode ? "de" : "", mode ? "in" : "de",
+				    zerror));
+			}
+			mode ? inflateEnd(&zs) : deflateEnd(&zs);
+			error = EINVAL;
+			goto fail;
+		}
+	}
+
+terminate:
+	zerror = mode ? inflateEnd(&zs) : deflateEnd(&zs);
+	if (zerror != Z_OK) {
+		if (zs.msg) {
+			ipseclog((LOG_ERR, "ipcomp_%scompress: "
+			    "%sflateEnd: %s\n",
+			    mode ? "de" : "", mode ? "in" : "de",
+			    zs.msg));
+		} else {
+			ipseclog((LOG_ERR, "ipcomp_%scompress: "
+			    "%sflateEnd: unknown error (%d)\n",
+			    mode ? "de" : "", mode ? "in" : "de",
+			    zerror));
+		}
+		error = EINVAL;
+		goto fail;
+	}
+	/* keep the final reply buffer into our chain */
+	if (n) {
+		n->m_len = zs.total_out - offset;
+		offset = zs.total_out;
+		*np = n;
+		np = &n->m_next;
+		n = NULL;
+	}
+
+	/* switch the mbuf to the new one */
+	mprev->m_next = n0;
+	m_freem(md);
+	*lenp = zs.total_out;
+
+	return 0;
+
+fail:
+	if (m)
+		m_freem(m);
+	if (n)
+		m_freem(n);
+	if (n0)
+		m_freem(n0);
+	return error;
+#undef MOREBLOCK
+}
+
+static int
+deflate_compress(m, md, lenp)
+	struct mbuf *m;
+	struct mbuf *md;
+	size_t *lenp;
+{
+	if (!m)
+		panic("m == NULL in deflate_compress");
+	if (!md)
+		panic("md == NULL in deflate_compress");
+	if (!lenp)
+		panic("lenp == NULL in deflate_compress");
+
+	return deflate_common(m, md, lenp, 0);
+}
+
+static int
+deflate_decompress(m, md, lenp)
+	struct mbuf *m;
+	struct mbuf *md;
+	size_t *lenp;
+{
+	if (!m)
+		panic("m == NULL in deflate_decompress");
+	if (!md)
+		panic("md == NULL in deflate_decompress");
+	if (!lenp)
+		panic("lenp == NULL in deflate_decompress");
+
+	return deflate_common(m, md, lenp, 1);
+}
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ipcomp_input.c
@@ -0,0 +1,382 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ipcomp_input.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ipcomp_input.c,v 1.29 2001/09/04 08:43:19 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#include <sys/param.h>
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+#include <sys/malloc.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <net/zlib.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_ecn.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet6/ipcomp.h>
+
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+#define IPLEN_FLIPPED
+
+#ifdef INET
+extern struct protosw inetsw[];
+#if defined(__bsdi__) || defined(__NetBSD__)
+extern u_char ip_protox[];
+#endif
+
+void
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 4)
+ipcomp4_input(m, off)
+	struct mbuf *m;
+	int off;
+#else
+#if __STDC__
+ipcomp4_input(struct mbuf *m, ...)
+#else
+ipcomp4_input(m, va_alist)
+	struct mbuf *m;
+	va_dcl
+#endif
+#endif /* (defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+{
+	struct mbuf *md;
+	struct ip *ip;
+	struct ipcomp *ipcomp;
+	const struct ipcomp_algorithm *algo;
+	u_int16_t cpi;	/* host order */
+	u_int16_t nxt;
+	size_t hlen;
+	int error;
+	size_t newlen, olen;
+	struct secasvar *sav = NULL;
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+	int off, proto;
+	va_list ap;
+#endif /* !(defined(__FreeBSD__) && __FreeBSD__ >= 4) */
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 4)
+	va_start(ap, m);
+	off = va_arg(ap, int);
+	proto = va_arg(ap, int);
+	va_end(ap);
+#endif
+
+	if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) {
+		ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed "
+		    "(packet too short)\n"));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+
+	md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
+	if (!m) {
+		m = NULL;	/* already freed */
+		ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed "
+		    "(pulldown failure)\n"));
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+	ipcomp = mtod(md, struct ipcomp *);
+	ip = mtod(m, struct ip *);
+	nxt = ipcomp->comp_nxt;
+#ifdef _IP_VHL
+	hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	hlen = ip->ip_hl << 2;
+#endif
+
+	cpi = ntohs(ipcomp->comp_cpi);
+
+	if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
+		sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src,
+			(caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi));
+		if (sav != NULL
+		 && (sav->state == SADB_SASTATE_MATURE
+		  || sav->state == SADB_SASTATE_DYING)) {
+			cpi = sav->alg_enc;	/* XXX */
+			/* other parameters to look at? */
+		}
+	}
+	algo = ipcomp_algorithm_lookup(cpi);
+	if (!algo) {
+		ipseclog((LOG_WARNING, "IPv4 IPComp input: unknown cpi %u\n",
+			cpi));
+		ipsecstat.in_nosa++;
+		goto fail;
+	}
+
+	/* chop ipcomp header */
+	ipcomp = NULL;
+	md->m_data += sizeof(struct ipcomp);
+	md->m_len -= sizeof(struct ipcomp);
+	m->m_pkthdr.len -= sizeof(struct ipcomp);
+#ifdef IPLEN_FLIPPED
+	ip->ip_len -= sizeof(struct ipcomp);
+#else
+	ip->ip_len = htons(ntohs(ip->ip_len) - sizeof(struct ipcomp));
+#endif
+
+	olen = m->m_pkthdr.len;
+	newlen = m->m_pkthdr.len - off;
+	error = (*algo->decompress)(m, m->m_next, &newlen);
+	if (error != 0) {
+		if (error == EINVAL)
+			ipsecstat.in_inval++;
+		else if (error == ENOBUFS)
+			ipsecstat.in_nomem++;
+		m = NULL;
+		goto fail;
+	}
+	ipsecstat.in_comphist[cpi]++;
+
+	/*
+	 * returning decompressed packet onto icmp is meaningless.
+	 * mark it decrypted to prevent icmp from attaching original packet.
+	 */
+	m->m_flags |= M_DECRYPTED;
+
+	m->m_pkthdr.len = off + newlen;
+	ip = mtod(m, struct ip *);
+    {
+	size_t len;
+#ifdef IPLEN_FLIPPED
+	len = ip->ip_len;
+#else
+	len = ntohs(ip->ip_len);
+#endif
+	/*
+	 * be careful about underflow.  also, do not assign exact value
+	 * as ip_len is manipulated differently on *BSDs.
+	 */
+	len += m->m_pkthdr.len;
+	len -= olen;
+	if (len & ~0xffff) {
+		/* packet too big after decompress */
+		ipsecstat.in_inval++;
+		goto fail;
+	}
+#ifdef IPLEN_FLIPPED
+	ip->ip_len = len & 0xffff;
+#else
+	ip->ip_len = htons(len & 0xffff);
+#endif
+	ip->ip_p = nxt;
+    }
+
+	if (sav) {
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
+			ipsecstat.in_nomem++;
+			goto fail;
+		}
+		key_freesav(sav);
+		sav = NULL;
+	}
+
+	if (nxt != IPPROTO_DONE) {
+		if ((inetsw[ip_protox[nxt]].pr_flags & PR_LASTHDR) != 0 &&
+		    ipsec4_in_reject(m, NULL)) {
+			ipsecstat.in_polvio++;
+			goto fail;
+		}
+#if defined(__FreeBSD__) && __FreeBSD__ >= 4
+		(*inetsw[ip_protox[nxt]].pr_input)(m, off);
+#else
+		(*inetsw[ip_protox[nxt]].pr_input)(m, off, nxt);
+#endif
+	} else
+		m_freem(m);
+	m = NULL;
+
+	ipsecstat.in_success++;
+	return;
+
+fail:
+	if (sav)
+		key_freesav(sav);
+	if (m)
+		m_freem(m);
+	return;
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+ipcomp6_input(mp, offp, proto)
+	struct mbuf **mp;
+	int *offp, proto;
+{
+	struct mbuf *m, *md;
+	int off;
+	struct ip6_hdr *ip6;
+	struct ipcomp *ipcomp;
+	const struct ipcomp_algorithm *algo;
+	u_int16_t cpi;	/* host order */
+	u_int16_t nxt;
+	int error;
+	size_t newlen;
+	struct secasvar *sav = NULL;
+	char *prvnxtp;
+
+	m = *mp;
+	off = *offp;
+
+	md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
+	if (!m) {
+		m = NULL;	/* already freed */
+		ipseclog((LOG_DEBUG, "IPv6 IPComp input: assumption failed "
+		    "(pulldown failure)\n"));
+		ipsec6stat.in_inval++;
+		goto fail;
+	}
+	ipcomp = mtod(md, struct ipcomp *);
+	ip6 = mtod(m, struct ip6_hdr *);
+	nxt = ipcomp->comp_nxt;
+
+	cpi = ntohs(ipcomp->comp_cpi);
+
+	if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
+		sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src,
+			(caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi));
+		if (sav != NULL
+		 && (sav->state == SADB_SASTATE_MATURE
+		  || sav->state == SADB_SASTATE_DYING)) {
+			cpi = sav->alg_enc;	/* XXX */
+			/* other parameters to look at? */
+		}
+	}
+	algo = ipcomp_algorithm_lookup(cpi);
+	if (!algo) {
+		ipseclog((LOG_WARNING, "IPv6 IPComp input: unknown cpi %u; "
+			"dropping the packet for simplicity\n", cpi));
+		ipsec6stat.in_nosa++;
+		goto fail;
+	}
+
+	/* chop ipcomp header */
+	ipcomp = NULL;
+	md->m_data += sizeof(struct ipcomp);
+	md->m_len -= sizeof(struct ipcomp);
+	m->m_pkthdr.len -= sizeof(struct ipcomp);
+
+	newlen = m->m_pkthdr.len - off;
+	error = (*algo->decompress)(m, md, &newlen);
+	if (error != 0) {
+		if (error == EINVAL)
+			ipsec6stat.in_inval++;
+		else if (error == ENOBUFS)
+			ipsec6stat.in_nomem++;
+		m = NULL;
+		goto fail;
+	}
+	ipsec6stat.in_comphist[cpi]++;
+	m->m_pkthdr.len = off + newlen;
+
+	/*
+	 * returning decompressed packet onto icmp is meaningless.
+	 * mark it decrypted to prevent icmp from attaching original packet.
+	 */
+	m->m_flags |= M_DECRYPTED;
+
+	/* update next header field */
+	prvnxtp = ip6_get_prevhdr(m, off);
+	*prvnxtp = nxt;
+
+	/*
+	 * no need to adjust payload length, as all the IPv6 protocols
+	 * look at m->m_pkthdr.len
+	 */
+
+	if (sav) {
+		key_sa_recordxfer(sav, m);
+		if (ipsec_addhist(m, IPPROTO_IPCOMP, (u_int32_t)cpi) != 0) {
+			ipsec6stat.in_nomem++;
+			goto fail;
+		}
+		key_freesav(sav);
+		sav = NULL;
+	}
+	*offp = off;
+	*mp = m;
+	ipsec6stat.in_success++;
+	return nxt;
+
+fail:
+	if (m)
+		m_freem(m);
+	if (sav)
+		key_freesav(sav);
+	return IPPROTO_DONE;
+}
+#endif /* INET6 */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ipcomp_output.c
@@ -0,0 +1,388 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ipcomp_output.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ipcomp_output.c,v 1.24 2001/07/26 06:53:18 jinmei Exp $	*/
+
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC2393 IP payload compression protocol (IPComp).
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/netisr.h>
+#include <net/zlib.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_ecn.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet6/ipcomp.h>
+
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+
+static int ipcomp_output __P((struct mbuf *, u_char *, struct mbuf *,
+	struct ipsecrequest *, int));
+
+/*
+ * Modify the packet so that the payload is compressed.
+ * The mbuf (m) must start with IPv4 or IPv6 header.
+ * On failure, free the given mbuf and return non-zero.
+ *
+ * on invocation:
+ *	m   nexthdrp md
+ *	v   v        v
+ *	IP ......... payload
+ * during the encryption:
+ *	m   nexthdrp mprev md
+ *	v   v        v     v
+ *	IP ............... ipcomp payload
+ *	                   <-----><----->
+ *	                   complen  plen
+ *	<-> hlen
+ *	<-----------------> compoff
+ */
+static int
+ipcomp_output(m, nexthdrp, md, isr, af)
+	struct mbuf *m;
+	u_char *nexthdrp;
+	struct mbuf *md;
+	struct ipsecrequest *isr;
+	int af;
+{
+	struct mbuf *n;
+	struct mbuf *md0;
+	struct mbuf *mcopy;
+	struct mbuf *mprev;
+	struct ipcomp *ipcomp;
+	struct secasvar *sav = isr->sav;
+	const struct ipcomp_algorithm *algo;
+	u_int16_t cpi;		/* host order */
+	size_t plen0, plen;	/* payload length to be compressed */
+	size_t compoff;
+	int afnumber;
+	int error = 0;
+	struct ipsecstat *stat;
+
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		afnumber = 4;
+		stat = &ipsecstat;
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		afnumber = 6;
+		stat = &ipsec6stat;
+		break;
+#endif
+	default:
+		ipseclog((LOG_ERR, "ipcomp_output: unsupported af %d\n", af));
+		return 0;	/* no change at all */
+	}
+
+	/* grab parameters */
+	algo = ipcomp_algorithm_lookup(sav->alg_enc);
+	if ((ntohl(sav->spi) & ~0xffff) != 0 || !algo) {
+		stat->out_inval++;
+		m_freem(m);
+		return EINVAL;
+	}
+	if ((sav->flags & SADB_X_EXT_RAWCPI) == 0)
+		cpi = sav->alg_enc;
+	else
+		cpi = ntohl(sav->spi) & 0xffff;
+
+	/* compute original payload length */
+	plen = 0;
+	for (n = md; n; n = n->m_next)
+		plen += n->m_len;
+
+	/* if the payload is short enough, we don't need to compress */
+	if (plen < algo->minplen)
+		return 0;
+
+	/*
+	 * retain the original packet for two purposes:
+	 * (1) we need to backout our changes when compression is not necessary.
+	 * (2) byte lifetime computation should use the original packet.
+	 *     see RFC2401 page 23.
+	 * compromise two m_copym().  we will be going through every byte of
+	 * the payload during compression process anyways.
+	 */
+	mcopy = m_copym(m, 0, M_COPYALL, M_NOWAIT);
+	if (mcopy == NULL) {
+		error = ENOBUFS;
+		return 0;
+	}
+	md0 = m_copym(md, 0, M_COPYALL, M_NOWAIT);
+	if (md0 == NULL) {
+		m_freem(mcopy);
+		error = ENOBUFS;
+		return 0;
+	}
+	plen0 = plen;
+
+	/* make the packet over-writable */
+	for (mprev = m; mprev && mprev->m_next != md; mprev = mprev->m_next)
+		;
+	if (mprev == NULL || mprev->m_next != md) {
+		ipseclog((LOG_DEBUG, "ipcomp%d_output: md is not in chain\n",
+		    afnumber));
+		stat->out_inval++;
+		m_freem(m);
+		m_freem(md0);
+		m_freem(mcopy);
+		return EINVAL;
+	}
+	mprev->m_next = NULL;
+	if ((md = ipsec_copypkt(md)) == NULL) {
+		m_freem(m);
+		m_freem(md0);
+		m_freem(mcopy);
+		error = ENOBUFS;
+		goto fail;
+	}
+	mprev->m_next = md;
+
+	/* compress data part */
+	if ((*algo->compress)(m, md, &plen) || mprev->m_next == NULL) {
+		ipseclog((LOG_ERR, "packet compression failure\n"));
+		m = NULL;
+		m_freem(md0);
+		m_freem(mcopy);
+		stat->out_inval++;
+		error = EINVAL;
+		goto fail;
+	}
+	stat->out_comphist[sav->alg_enc]++;
+	md = mprev->m_next;
+
+	/*
+	 * if the packet became bigger, meaningless to use IPComp.
+	 * we've only wasted our cpu time.
+	 */
+	if (plen0 < plen) {
+		m_freem(md);
+		m_freem(mcopy);
+		mprev->m_next = md0;
+		return 0;
+	}
+
+	/*
+	 * no need to backout change beyond here.
+	 */
+	m_freem(md0);
+	md0 = NULL;
+
+	m->m_pkthdr.len -= plen0;
+	m->m_pkthdr.len += plen;
+
+    {
+	/*
+	 * insert IPComp header.
+	 */
+#ifdef INET
+	struct ip *ip = NULL;
+#endif
+#ifdef INET6
+	struct ip6_hdr *ip6 = NULL;
+#endif
+	size_t hlen = 0;	/* ip header len */
+	size_t complen = sizeof(struct ipcomp);
+
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+		hlen = IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+		hlen = ip->ip_hl << 2;
+#endif
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		ip6 = mtod(m, struct ip6_hdr *);
+		hlen = sizeof(*ip6);
+		break;
+#endif
+	}
+
+	compoff = m->m_pkthdr.len - plen;
+
+	/*
+	 * grow the mbuf to accomodate ipcomp header.
+	 * before: IP ... payload
+	 * after:  IP ... ipcomp payload
+	 */
+	if (M_LEADINGSPACE(md) < complen) {
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (!n) {
+			m_freem(m);
+			error = ENOBUFS;
+			goto fail;
+		}
+		n->m_len = complen;
+		mprev->m_next = n;
+		n->m_next = md;
+		m->m_pkthdr.len += complen;
+		ipcomp = mtod(n, struct ipcomp *);
+	} else {
+		md->m_len += complen;
+		md->m_data -= complen;
+		m->m_pkthdr.len += complen;
+		ipcomp = mtod(md, struct ipcomp *);
+	}
+	
+	bzero(ipcomp, sizeof(*ipcomp));
+	ipcomp->comp_nxt = *nexthdrp;
+	*nexthdrp = IPPROTO_IPCOMP;
+	ipcomp->comp_cpi = htons(cpi);
+	switch (af) {
+#ifdef INET
+	case AF_INET:
+		if (compoff + complen + plen < IP_MAXPACKET)
+			ip->ip_len = htons(compoff + complen + plen);
+		else {
+			ipseclog((LOG_ERR,
+			    "IPv4 ESP output: size exceeds limit\n"));
+			ipsecstat.out_inval++;
+			m_freem(m);
+			error = EMSGSIZE;
+			goto fail;
+		}
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		/* total packet length will be computed in ip6_output() */
+		break;
+#endif
+	}
+    }
+
+	if (!m) {
+		ipseclog((LOG_DEBUG,
+		    "NULL mbuf after compression in ipcomp%d_output",
+		    afnumber));
+		stat->out_inval++;
+	}
+		stat->out_success++;
+
+	/* compute byte lifetime against original packet */
+	key_sa_recordxfer(sav, mcopy);
+	m_freem(mcopy);
+
+	return 0;
+
+fail:
+#if 1
+	return error;
+#else
+	panic("something bad in ipcomp_output");
+#endif
+}
+
+#ifdef INET
+int
+ipcomp4_output(m, isr)
+	struct mbuf *m;
+	struct ipsecrequest *isr;
+{
+	struct ip *ip;
+	if (m->m_len < sizeof(struct ip)) {
+		ipseclog((LOG_DEBUG, "ipcomp4_output: first mbuf too short\n"));
+		ipsecstat.out_inval++;
+		m_freem(m);
+		return 0;
+	}
+	ip = mtod(m, struct ip *);
+	/* XXX assumes that m->m_next points to payload */
+	return ipcomp_output(m, &ip->ip_p, m->m_next, isr, AF_INET);
+}
+#endif /* INET */
+
+#ifdef INET6
+int
+ipcomp6_output(m, nexthdrp, md, isr)
+	struct mbuf *m;
+	u_char *nexthdrp;
+	struct mbuf *md;
+	struct ipsecrequest *isr;
+{
+	if (m->m_len < sizeof(struct ip6_hdr)) {
+		ipseclog((LOG_DEBUG, "ipcomp6_output: first mbuf too short\n"));
+		ipsec6stat.out_inval++;
+		m_freem(m);
+		return 0;
+	}
+	return ipcomp_output(m, nexthdrp, md, isr, AF_INET6);
+}
+#endif /* INET6 */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netinet6/ipsec.c
@@ -0,0 +1,4207 @@
+//==========================================================================
+//
+//      src/sys/netinet6/ipsec.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: ipsec.c,v 1.134 2001/12/03 09:08:47 jinmei Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * IPsec controller part.
+ */
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/in_var.h>
+#include <netinet/udp.h>
+#include <netinet/udp_var.h>
+#include <netinet/ip_ecn.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include <netinet/ip6.h>
+#ifdef INET6
+#include <netinet6/ip6_var.h>
+#endif
+#include <netinet/in_pcb.h>
+#ifdef INET6
+#if !((defined(__FreeBSD__) && __FreeBSD__ >= 3) || defined(__OpenBSD__) || (defined(__bsdi__) && _BSDI_VERSION >= 199802))
+#include <netinet6/in6_pcb.h>
+#endif
+#include <netinet/icmp6.h>
+#endif
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#ifdef IPSEC_ESP
+#include <netinet6/esp.h>
+#endif
+#include <netinet6/ipcomp.h>
+#include <netkey/key.h>
+#include <netkey/keydb.h>
+#include <netkey/key_debug.h>
+
+#ifdef HAVE_NRL_INPCB
+#define in6pcb	inpcb
+#define in6p_sp	inp_sp
+#define in6p_socket	inp_socket
+#define sotoin6pcb(so)	((struct inpcb *)(so)->so_pcb)
+#endif
+
+#ifdef IPSEC_DEBUG
+int ipsec_debug = 1;
+#else
+int ipsec_debug = 0;
+#endif
+
+/* 1 if we decapsulate tunnels by sec* device */
+#if defined(NSEC) && NSEC > 0
+#define USE_SEC_DEVICE	1
+#else
+#define USE_SEC_DEVICE	0
+#endif
+
+int ipsec_tunnel_device = USE_SEC_DEVICE;
+
+#ifndef offsetof		/* XXX */
+#define	offsetof(type, member)	((size_t)(&((type *)0)->member))
+#endif
+
+struct ipsecstat ipsecstat;
+int ip4_ah_cleartos = 1;
+int ip4_ah_offsetmask = 0;	/* maybe IP_DF? */
+int ip4_ipsec_dfbit = 0;	/* DF bit on encap. 0: clear 1: set 2: copy */
+int ip4_esp_trans_deflev = IPSEC_LEVEL_USE;
+int ip4_esp_net_deflev = IPSEC_LEVEL_USE;
+int ip4_ah_trans_deflev = IPSEC_LEVEL_USE;
+int ip4_ah_net_deflev = IPSEC_LEVEL_USE;
+struct secpolicy ip4_def_policy;
+int ip4_ipsec_ecn = 0;		/* ECN ignore(-1)/forbidden(0)/allowed(1) */
+int ip4_esp_randpad = -1;
+
+static int sp_cachegen = 1;	/* cache generation # */
+
+#ifdef __FreeBSD__
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_net_inet_ipsec);
+#ifdef INET6
+SYSCTL_DECL(_net_inet6_ipsec6);
+#endif
+#endif
+
+/* net.inet.ipsec */
+SYSCTL_STRUCT(_net_inet_ipsec, IPSECCTL_STATS,
+	stats, CTLFLAG_RD,	&ipsecstat,	ipsecstat, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_POLICY,
+	def_policy, CTLFLAG_RW,	&ip4_def_policy.policy,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
+	CTLFLAG_RW, &ip4_esp_trans_deflev,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev,
+	CTLFLAG_RW, &ip4_esp_net_deflev,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev,
+	CTLFLAG_RW, &ip4_ah_trans_deflev,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
+	CTLFLAG_RW, &ip4_ah_net_deflev,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_CLEARTOS,
+	ah_cleartos, CTLFLAG_RW,	&ip4_ah_cleartos,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_AH_OFFSETMASK,
+	ah_offsetmask, CTLFLAG_RW,	&ip4_ah_offsetmask,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DFBIT,
+	dfbit, CTLFLAG_RW,	&ip4_ipsec_dfbit,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ECN,
+	ecn, CTLFLAG_RW,	&ip4_ipsec_ecn,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_DEBUG,
+	debug, CTLFLAG_RW,	&ipsec_debug,	0, "");
+SYSCTL_INT(_net_inet_ipsec, IPSECCTL_ESP_RANDPAD,
+	esp_randpad, CTLFLAG_RW,	&ip4_esp_randpad,	0, "");
+#endif /* __FreeBSD__ */
+
+#ifdef INET6
+struct ipsecstat ipsec6stat;
+int ip6_esp_trans_deflev = IPSEC_LEVEL_USE;
+int ip6_esp_net_deflev = IPSEC_LEVEL_USE;
+int ip6_ah_trans_deflev = IPSEC_LEVEL_USE;
+int ip6_ah_net_deflev = IPSEC_LEVEL_USE;
+struct secpolicy ip6_def_policy;
+int ip6_ipsec_ecn = 0;		/* ECN ignore(-1)/forbidden(0)/allowed(1) */
+int ip6_esp_randpad = -1;
+
+#ifdef __FreeBSD__
+/* net.inet6.ipsec6 */
+SYSCTL_STRUCT(_net_inet6_ipsec6, IPSECCTL_STATS,
+	stats, CTLFLAG_RD, &ipsec6stat, ipsecstat, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_POLICY,
+	def_policy, CTLFLAG_RW,	&ip6_def_policy.policy,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_TRANSLEV, esp_trans_deflev,
+	CTLFLAG_RW, &ip6_esp_trans_deflev,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_ESP_NETLEV, esp_net_deflev,
+	CTLFLAG_RW, &ip6_esp_net_deflev,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_TRANSLEV, ah_trans_deflev,
+	CTLFLAG_RW, &ip6_ah_trans_deflev,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEF_AH_NETLEV, ah_net_deflev,
+	CTLFLAG_RW, &ip6_ah_net_deflev,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ECN,
+	ecn, CTLFLAG_RW,	&ip6_ipsec_ecn,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_DEBUG,
+	debug, CTLFLAG_RW,	&ipsec_debug,	0, "");
+SYSCTL_INT(_net_inet6_ipsec6, IPSECCTL_ESP_RANDPAD,
+	esp_randpad, CTLFLAG_RW,	&ip6_esp_randpad,	0, "");
+#endif /* __FreeBSD__ */
+#endif /* INET6 */
+
+static struct secpolicy *ipsec_checkpcbcache __P((struct mbuf *,
+	struct inpcbpolicy *pcbsp, int));
+static int ipsec_fillpcbcache __P((struct inpcbpolicy *, struct mbuf *,
+	struct secpolicy *, int));
+static int ipsec_invalpcbcache __P((struct inpcbpolicy *, int));
+static int ipsec_setspidx_mbuf
+	__P((struct secpolicyindex *, u_int, u_int, struct mbuf *, int));
+static int ipsec4_setspidx_inpcb __P((struct mbuf *, struct inpcb *pcb));
+#ifdef INET6
+static int ipsec6_setspidx_in6pcb __P((struct mbuf *, struct in6pcb *pcb));
+#endif
+static int ipsec_setspidx __P((struct mbuf *, struct secpolicyindex *, int));
+static void ipsec4_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int));
+static int ipsec4_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *));
+#ifdef INET6
+static void ipsec6_get_ulp __P((struct mbuf *m, struct secpolicyindex *, int));
+static int ipsec6_setspidx_ipaddr __P((struct mbuf *, struct secpolicyindex *));
+#endif
+static struct inpcbpolicy *ipsec_newpcbpolicy __P((void));
+static void ipsec_delpcbpolicy __P((struct inpcbpolicy *));
+static struct secpolicy *ipsec_deepcopy_policy __P((struct secpolicy *src));
+static int ipsec_set_policy __P((struct secpolicy **pcb_sp,
+	int optname, caddr_t request, size_t len, int priv));
+static int ipsec_get_policy __P((struct secpolicy *pcb_sp, struct mbuf **mp));
+static void vshiftl __P((unsigned char *, int, int));
+static int ipsec_in_reject __P((struct secpolicy *, struct mbuf *));
+static size_t ipsec_hdrsiz __P((struct secpolicy *));
+#ifdef INET
+static struct mbuf *ipsec4_splithdr __P((struct mbuf *));
+#endif
+#ifdef INET6
+static struct mbuf *ipsec6_splithdr __P((struct mbuf *));
+#endif
+#ifdef INET
+static int ipsec4_encapsulate __P((struct mbuf *, struct secasvar *));
+#endif
+#ifdef INET6
+static int ipsec6_encapsulate __P((struct mbuf *, struct secasvar *));
+#endif
+static struct mbuf *ipsec_addaux __P((struct mbuf *));
+static struct mbuf *ipsec_findaux __P((struct mbuf *));
+static void ipsec_optaux __P((struct mbuf *, struct mbuf *));
+
+/*
+ * try to validate and use cached policy on a pcb.
+ */
+static struct secpolicy *
+ipsec_checkpcbcache(m, pcbsp, dir)
+	struct mbuf *m;
+	struct inpcbpolicy *pcbsp;
+	int dir;
+{
+	struct secpolicyindex spidx;
+#if defined(__FreeBSD__) && __FreeBSD__ > 2
+	struct timeval mono_time;
+
+	microtime(&mono_time);
+#endif
+
+	switch (dir) {
+	case IPSEC_DIR_INBOUND:
+	case IPSEC_DIR_OUTBOUND:
+		break;
+	default:
+		return NULL;
+	}
+#ifdef DIAGNOSTIC
+	if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0]))
+		panic("dir too big in ipsec_checkpcbcache");
+#endif
+	/* SPD table change invalidates all the caches */
+	if (pcbsp->cachegen[dir] == 0 || sp_cachegen > pcbsp->cachegen[dir]) {
+		ipsec_invalpcbcache(pcbsp, dir);
+		return NULL;
+	}
+	if (!pcbsp->cache[dir])
+		return NULL;
+	if ((pcbsp->cacheflags & IPSEC_PCBSP_CONNECTED) == 0) {
+		if (!pcbsp->cache[dir])
+			return NULL;
+		if (ipsec_setspidx(m, &spidx, 1) != 0)
+			return NULL;
+		if (bcmp(&pcbsp->cacheidx[dir], &spidx, sizeof(spidx))) {
+			if (!key_cmpspidx_withmask(&pcbsp->cache[dir]->spidx,
+			    &spidx))
+				return NULL;
+			pcbsp->cacheidx[dir] = spidx;
+		}
+	} else {
+		/*
+		 * The pcb is connected, and the L4 code is sure that:
+		 * - outgoing side uses inp_[lf]addr
+		 * - incoming side looks up policy after inpcb lookup
+		 * and address pair is known to be stable.  We do not need
+		 * to generate spidx again, nor check the address match again.
+		 *
+		 * For IPv4/v6 SOCK_STREAM sockets, this assumption holds
+		 * and there are calls to ipsec_pcbconn() from in_pcbconnect().
+		 */
+	}
+
+	pcbsp->cache[dir]->lastused = mono_time.tv_sec;
+	pcbsp->cache[dir]->refcnt++;
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ipsec_checkpcbcache cause refcnt++:%d SP:%p\n",
+		pcbsp->cache[dir]->refcnt, pcbsp->cache[dir]));
+	return pcbsp->cache[dir];
+}
+
+static int
+ipsec_fillpcbcache(pcbsp, m, sp, dir)
+	struct inpcbpolicy *pcbsp;
+	struct mbuf *m;
+	struct secpolicy *sp;
+	int dir;
+{
+
+	switch (dir) {
+	case IPSEC_DIR_INBOUND:
+	case IPSEC_DIR_OUTBOUND:
+		break;
+	default:
+		return EINVAL;
+	}
+#ifdef DIAGNOSTIC
+	if (dir >= sizeof(pcbsp->cache)/sizeof(pcbsp->cache[0]))
+		panic("dir too big in ipsec_checkpcbcache");
+#endif
+
+	if (pcbsp->cache[dir])
+		key_freesp(pcbsp->cache[dir]);
+	pcbsp->cache[dir] = NULL;
+	if (ipsec_setspidx(m, &pcbsp->cacheidx[dir], 1) != 0) {
+		return EINVAL;
+	}
+	pcbsp->cache[dir] = sp;
+	if (pcbsp->cache[dir]) {
+		pcbsp->cache[dir]->refcnt++;
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ipsec_fillpcbcache cause refcnt++:%d SP:%p\n",
+			pcbsp->cache[dir]->refcnt, pcbsp->cache[dir]));
+	}
+	pcbsp->cachegen[dir] = sp_cachegen;
+
+	return 0;
+}
+
+static int
+ipsec_invalpcbcache(pcbsp, dir)
+	struct inpcbpolicy *pcbsp;
+	int dir;
+{
+	int i;
+
+	for (i = IPSEC_DIR_INBOUND; i <= IPSEC_DIR_OUTBOUND; i++) {
+		if (dir != IPSEC_DIR_ANY && i != dir)
+			continue;
+		if (pcbsp->cache[i])
+			key_freesp(pcbsp->cache[i]);
+		pcbsp->cache[i] = NULL;
+		pcbsp->cachegen[i] = 0;
+		bzero(&pcbsp->cacheidx[i], sizeof(pcbsp->cacheidx[i]));
+	}
+	return 0;
+}
+
+int
+ipsec_pcbconn(pcbsp)
+	struct inpcbpolicy *pcbsp;
+{
+
+	pcbsp->cacheflags |= IPSEC_PCBSP_CONNECTED;
+	ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
+	return 0;
+}
+
+int
+ipsec_pcbdisconn(pcbsp)
+	struct inpcbpolicy *pcbsp;
+{
+
+	pcbsp->cacheflags &= ~IPSEC_PCBSP_CONNECTED;
+	ipsec_invalpcbcache(pcbsp, IPSEC_DIR_ANY);
+	return 0;
+}
+
+int
+ipsec_invalpcbcacheall()
+{
+
+	sp_cachegen++;
+	return 0;
+}
+
+/*
+ * For OUTBOUND packet having a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * OUT:	NULL:	no apropreate SP found, the following value is set to error.
+ *		0	: bypass
+ *		EACCES	: discard packet.
+ *		ENOENT	: ipsec_acquire() in progress, maybe.
+ *		others	: error occured.
+ *	others:	a pointer to SP
+ *
+ * NOTE: IPv6 mapped adddress concern is implemented here.
+ */
+struct secpolicy *
+ipsec4_getpolicybysock(m, dir, so, error)
+	struct mbuf *m;
+	u_int dir;
+	struct socket *so;
+	int *error;
+{
+	struct inpcbpolicy *pcbsp = NULL;
+	struct secpolicy *currsp = NULL;	/* policy on socket */
+	struct secpolicy *kernsp = NULL;	/* policy on kernel */
+
+	/* sanity check */
+	if (m == NULL || so == NULL || error == NULL)
+		panic("ipsec4_getpolicybysock: NULL pointer was passed.\n");
+
+	switch (so->so_proto->pr_domain->dom_family) {
+	case AF_INET:
+		pcbsp = sotoinpcb(so)->inp_sp;
+		break;
+#ifdef INET6
+	case AF_INET6:
+		pcbsp = sotoin6pcb(so)->in6p_sp;
+		break;
+#endif
+	}
+
+	/* if we have a cached entry, and if it is still valid, use it. */
+	ipsecstat.spdcachelookup++;
+	currsp = ipsec_checkpcbcache(m, pcbsp, dir);
+	if (currsp) {
+		*error = 0;
+		return currsp;
+	}
+	ipsecstat.spdcachemiss++;
+
+	/*
+	 * XXX why is it necessary to do this, per-packet?
+	 * we are of course sure that the socket policy do match the packet,
+	 * because policy-on-pcb does not contain selectors, and
+	 * in_pcblookup (inbound) ensures the packet-pcb matching.
+	 */
+	switch (so->so_proto->pr_domain->dom_family) {
+	case AF_INET:
+		/* set spidx in pcb */
+		*error = ipsec4_setspidx_inpcb(m, sotoinpcb(so));
+		break;
+#ifdef INET6
+	case AF_INET6:
+		/* set spidx in pcb */
+		*error = ipsec6_setspidx_in6pcb(m, sotoin6pcb(so));
+		break;
+#endif
+	default:
+		panic("ipsec4_getpolicybysock: unsupported address family\n");
+	}
+	if (*error)
+		return NULL;
+
+	/* sanity check */
+	if (pcbsp == NULL)
+		panic("ipsec4_getpolicybysock: pcbsp is NULL.\n");
+
+	switch (dir) {
+	case IPSEC_DIR_INBOUND:
+		currsp = pcbsp->sp_in;
+		break;
+	case IPSEC_DIR_OUTBOUND:
+		currsp = pcbsp->sp_out;
+		break;
+	default:
+		panic("ipsec4_getpolicybysock: illegal direction.\n");
+	}
+
+	/* sanity check */
+	if (currsp == NULL)
+		panic("ipsec4_getpolicybysock: currsp is NULL.\n");
+
+	/* when privilieged socket */
+	if (pcbsp->priv) {
+		switch (currsp->policy) {
+		case IPSEC_POLICY_BYPASS:
+			currsp->refcnt++;
+			*error = 0;
+			ipsec_fillpcbcache(pcbsp, m, currsp, dir);
+			return currsp;
+
+		case IPSEC_POLICY_ENTRUST:
+			/* look for a policy in SPD */
+			kernsp = key_allocsp(&currsp->spidx, dir);
+
+			/* SP found */
+			if (kernsp != NULL) {
+				KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+					printf("DP ipsec4_getpolicybysock called "
+					       "to allocate SP:%p\n", kernsp));
+				*error = 0;
+				ipsec_fillpcbcache(pcbsp, m, kernsp, dir);
+				return kernsp;
+			}
+
+			/* no SP found */
+			if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD
+			 && ip4_def_policy.policy != IPSEC_POLICY_NONE) {
+				ipseclog((LOG_INFO,
+				    "fixed system default policy: %d->%d\n",
+				    ip4_def_policy.policy, IPSEC_POLICY_NONE));
+				ip4_def_policy.policy = IPSEC_POLICY_NONE;
+			}
+			ip4_def_policy.refcnt++;
+			*error = 0;
+			ipsec_fillpcbcache(pcbsp, m, &ip4_def_policy, dir);
+			return &ip4_def_policy;
+			
+		case IPSEC_POLICY_IPSEC:
+			currsp->refcnt++;
+			*error = 0;
+			ipsec_fillpcbcache(pcbsp, m, currsp, dir);
+			return currsp;
+
+		default:
+			ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
+			      "Invalid policy for PCB %d\n", currsp->policy));
+			*error = EINVAL;
+			return NULL;
+		}
+		/* NOTREACHED */
+	}
+
+	/* when non-privilieged socket */
+	/* look for a policy in SPD */
+	kernsp = key_allocsp(&currsp->spidx, dir);
+
+	/* SP found */
+	if (kernsp != NULL) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ipsec4_getpolicybysock called "
+			       "to allocate SP:%p\n", kernsp));
+		*error = 0;
+		ipsec_fillpcbcache(pcbsp, m, kernsp, dir);
+		return kernsp;
+	}
+
+	/* no SP found */
+	switch (currsp->policy) {
+	case IPSEC_POLICY_BYPASS:
+		ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
+		       "Illegal policy for non-priviliged defined %d\n",
+			currsp->policy));
+		*error = EINVAL;
+		return NULL;
+
+	case IPSEC_POLICY_ENTRUST:
+		if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD
+		 && ip4_def_policy.policy != IPSEC_POLICY_NONE) {
+			ipseclog((LOG_INFO,
+			    "fixed system default policy: %d->%d\n",
+			    ip4_def_policy.policy, IPSEC_POLICY_NONE));
+			ip4_def_policy.policy = IPSEC_POLICY_NONE;
+		}
+		ip4_def_policy.refcnt++;
+		*error = 0;
+		ipsec_fillpcbcache(pcbsp, m, &ip4_def_policy, dir);
+		return &ip4_def_policy;
+
+	case IPSEC_POLICY_IPSEC:
+		currsp->refcnt++;
+		*error = 0;
+		ipsec_fillpcbcache(pcbsp, m, currsp, dir);
+		return currsp;
+
+	default:
+		ipseclog((LOG_ERR, "ipsec4_getpolicybysock: "
+		   "Invalid policy for PCB %d\n", currsp->policy));
+		*error = EINVAL;
+		return NULL;
+	}
+	/* NOTREACHED */
+}
+
+/*
+ * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * OUT:	positive: a pointer to the entry for security policy leaf matched.
+ *	NULL:	no apropreate SP found, the following value is set to error.
+ *		0	: bypass
+ *		EACCES	: discard packet.
+ *		ENOENT	: ipsec_acquire() in progress, maybe.
+ *		others	: error occured.
+ */
+struct secpolicy *
+ipsec4_getpolicybyaddr(m, dir, flag, error)
+	struct mbuf *m;
+	u_int dir;
+	int flag;
+	int *error;
+{
+	struct secpolicy *sp = NULL;
+
+	/* sanity check */
+	if (m == NULL || error == NULL)
+		panic("ipsec4_getpolicybyaddr: NULL pointer was passed.\n");
+
+    {
+	struct secpolicyindex spidx;
+
+	bzero(&spidx, sizeof(spidx));
+
+	/* make a index to look for a policy */
+	*error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET, m,
+	    (flag & IP_FORWARDING) ? 0 : 1);
+
+	if (*error != 0)
+		return NULL;
+
+	sp = key_allocsp(&spidx, dir);
+    }
+
+	/* SP found */
+	if (sp != NULL) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ipsec4_getpolicybyaddr called "
+			       "to allocate SP:%p\n", sp));
+		*error = 0;
+		return sp;
+	}
+
+	/* no SP found */
+	if (ip4_def_policy.policy != IPSEC_POLICY_DISCARD
+	 && ip4_def_policy.policy != IPSEC_POLICY_NONE) {
+		ipseclog((LOG_INFO, "fixed system default policy:%d->%d\n",
+			ip4_def_policy.policy,
+			IPSEC_POLICY_NONE));
+		ip4_def_policy.policy = IPSEC_POLICY_NONE;
+	}
+	ip4_def_policy.refcnt++;
+	*error = 0;
+	return &ip4_def_policy;
+}
+
+#ifdef INET6
+/*
+ * For OUTBOUND packet having a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * OUT:	NULL:	no apropreate SP found, the following value is set to error.
+ *		0	: bypass
+ *		EACCES	: discard packet.
+ *		ENOENT	: ipsec_acquire() in progress, maybe.
+ *		others	: error occured.
+ *	others:	a pointer to SP
+ */
+struct secpolicy *
+ipsec6_getpolicybysock(m, dir, so, error)
+	struct mbuf *m;
+	u_int dir;
+	struct socket *so;
+	int *error;
+{
+	struct inpcbpolicy *pcbsp = NULL;
+	struct secpolicy *currsp = NULL;	/* policy on socket */
+	struct secpolicy *kernsp = NULL;	/* policy on kernel */
+
+	/* sanity check */
+	if (m == NULL || so == NULL || error == NULL)
+		panic("ipsec6_getpolicybysock: NULL pointer was passed.\n");
+
+#ifdef DIAGNOSTIC
+	if (so->so_proto->pr_domain->dom_family != AF_INET6)
+		panic("ipsec6_getpolicybysock: socket domain != inet6\n");
+#endif
+
+	pcbsp = sotoin6pcb(so)->in6p_sp;
+
+	/* if we have a cached entry, and if it is still valid, use it. */
+	ipsec6stat.spdcachelookup++;
+	currsp = ipsec_checkpcbcache(m, pcbsp, dir);
+	if (currsp) {
+		*error = 0;
+		return currsp;
+	}
+	ipsec6stat.spdcachemiss++;
+
+	/* set spidx in pcb */
+	/* XXX why is it necessary to do this? */
+	ipsec6_setspidx_in6pcb(m, sotoin6pcb(so));
+
+	/* sanity check */
+	if (pcbsp == NULL)
+		panic("ipsec6_getpolicybysock: pcbsp is NULL.\n");
+
+	switch (dir) {
+	case IPSEC_DIR_INBOUND:
+		currsp = pcbsp->sp_in;
+		break;
+	case IPSEC_DIR_OUTBOUND:
+		currsp = pcbsp->sp_out;
+		break;
+	default:
+		panic("ipsec6_getpolicybysock: illegal direction.\n");
+	}
+
+	/* sanity check */
+	if (currsp == NULL)
+		panic("ipsec6_getpolicybysock: currsp is NULL.\n");
+
+	/* when privilieged socket */
+	if (pcbsp->priv) {
+		switch (currsp->policy) {
+		case IPSEC_POLICY_BYPASS:
+			currsp->refcnt++;
+			*error = 0;
+			ipsec_fillpcbcache(pcbsp, m, currsp, dir);
+			return currsp;
+
+		case IPSEC_POLICY_ENTRUST:
+			/* look for a policy in SPD */
+			kernsp = key_allocsp(&currsp->spidx, dir);
+
+			/* SP found */
+			if (kernsp != NULL) {
+				KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+					printf("DP ipsec6_getpolicybysock called "
+					       "to allocate SP:%p\n", kernsp));
+				*error = 0;
+				ipsec_fillpcbcache(pcbsp, m, kernsp, dir);
+				return kernsp;
+			}
+
+			/* no SP found */
+			if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD
+			 && ip6_def_policy.policy != IPSEC_POLICY_NONE) {
+				ipseclog((LOG_INFO,
+				    "fixed system default policy: %d->%d\n",
+				    ip6_def_policy.policy, IPSEC_POLICY_NONE));
+				ip6_def_policy.policy = IPSEC_POLICY_NONE;
+			}
+			ip6_def_policy.refcnt++;
+			*error = 0;
+			ipsec_fillpcbcache(pcbsp, m, &ip6_def_policy, dir);
+			return &ip6_def_policy;
+			
+		case IPSEC_POLICY_IPSEC:
+			currsp->refcnt++;
+			*error = 0;
+			ipsec_fillpcbcache(pcbsp, m, currsp, dir);
+			return currsp;
+
+		default:
+			ipseclog((LOG_ERR, "ipsec6_getpolicybysock: "
+			    "Invalid policy for PCB %d\n", currsp->policy));
+			*error = EINVAL;
+			return NULL;
+		}
+		/* NOTREACHED */
+	}
+
+	/* when non-privilieged socket */
+	/* look for a policy in SPD */
+	kernsp = key_allocsp(&currsp->spidx, dir);
+
+	/* SP found */
+	if (kernsp != NULL) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ipsec6_getpolicybysock called "
+			       "to allocate SP:%p\n", kernsp));
+		*error = 0;
+		ipsec_fillpcbcache(pcbsp, m, kernsp, dir);
+		return kernsp;
+	}
+
+	/* no SP found */
+	switch (currsp->policy) {
+	case IPSEC_POLICY_BYPASS:
+		ipseclog((LOG_ERR, "ipsec6_getpolicybysock: "
+		    "Illegal policy for non-priviliged defined %d\n",
+		    currsp->policy));
+		*error = EINVAL;
+		return NULL;
+
+	case IPSEC_POLICY_ENTRUST:
+		if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD
+		 && ip6_def_policy.policy != IPSEC_POLICY_NONE) {
+			ipseclog((LOG_INFO,
+			    "fixed system default policy: %d->%d\n",
+			    ip6_def_policy.policy, IPSEC_POLICY_NONE));
+			ip6_def_policy.policy = IPSEC_POLICY_NONE;
+		}
+		ip6_def_policy.refcnt++;
+		*error = 0;
+		ipsec_fillpcbcache(pcbsp, m, &ip6_def_policy, dir);
+		return &ip6_def_policy;
+
+	case IPSEC_POLICY_IPSEC:
+		currsp->refcnt++;
+		*error = 0;
+		ipsec_fillpcbcache(pcbsp, m, currsp, dir);
+		return currsp;
+
+	default:
+		ipseclog((LOG_ERR,
+		    "ipsec6_policybysock: Invalid policy for PCB %d\n",
+		    currsp->policy));
+		*error = EINVAL;
+		return NULL;
+	}
+	/* NOTREACHED */
+}
+
+/*
+ * For FORWADING packet or OUTBOUND without a socket. Searching SPD for packet,
+ * and return a pointer to SP.
+ * `flag' means that packet is to be forwarded whether or not.
+ *	flag = 1: forwad
+ * OUT:	positive: a pointer to the entry for security policy leaf matched.
+ *	NULL:	no apropreate SP found, the following value is set to error.
+ *		0	: bypass
+ *		EACCES	: discard packet.
+ *		ENOENT	: ipsec_acquire() in progress, maybe.
+ *		others	: error occured.
+ */
+#ifndef IP_FORWARDING
+#define IP_FORWARDING 1
+#endif
+
+struct secpolicy *
+ipsec6_getpolicybyaddr(m, dir, flag, error)
+	struct mbuf *m;
+	u_int dir;
+	int flag;
+	int *error;
+{
+	struct secpolicy *sp = NULL;
+
+	/* sanity check */
+	if (m == NULL || error == NULL)
+		panic("ipsec6_getpolicybyaddr: NULL pointer was passed.\n");
+
+	/* get a policy entry matched with the packet */
+    {
+	struct secpolicyindex spidx;
+
+	bzero(&spidx, sizeof(spidx));
+
+	/* make a index to look for a policy */
+	*error = ipsec_setspidx_mbuf(&spidx, dir, AF_INET6, m,
+	    (flag & IP_FORWARDING) ? 0 : 1);
+
+	if (*error != 0)
+		return NULL;
+
+	sp = key_allocsp(&spidx, dir);
+    }
+
+	/* SP found */
+	if (sp != NULL) {
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP ipsec6_getpolicybyaddr called "
+			       "to allocate SP:%p\n", sp));
+		*error = 0;
+		return sp;
+	}
+
+	/* no SP found */
+	if (ip6_def_policy.policy != IPSEC_POLICY_DISCARD
+	 && ip6_def_policy.policy != IPSEC_POLICY_NONE) {
+		ipseclog((LOG_INFO, "fixed system default policy: %d->%d\n",
+		    ip6_def_policy.policy, IPSEC_POLICY_NONE));
+		ip6_def_policy.policy = IPSEC_POLICY_NONE;
+	}
+	ip6_def_policy.refcnt++;
+	*error = 0;
+	return &ip6_def_policy;
+}
+#endif /* INET6 */
+
+/*
+ * set IP address into spidx from mbuf.
+ * When Forwarding packet and ICMP echo reply, this function is used.
+ *
+ * IN:	get the followings from mbuf.
+ *	protocol family, src, dst, next protocol
+ * OUT:
+ *	0:	success.
+ *	other:	failure, and set errno.
+ */
+int
+ipsec_setspidx_mbuf(spidx, dir, family, m, needport)
+	struct secpolicyindex *spidx;
+	u_int dir, family;
+	struct mbuf *m;
+	int needport;
+{
+	int error;
+
+	/* sanity check */
+	if (spidx == NULL || m == NULL)
+		panic("ipsec_setspidx_mbuf: NULL pointer was passed.\n");
+
+	bzero(spidx, sizeof(*spidx));
+
+	error = ipsec_setspidx(m, spidx, needport);
+	if (error)
+		goto bad;
+	spidx->dir = dir;
+
+	return 0;
+
+    bad:
+	/* XXX initialize */
+	bzero(spidx, sizeof(*spidx));
+	return EINVAL;
+}
+
+static int
+ipsec4_setspidx_inpcb(m, pcb)
+	struct mbuf *m;
+	struct inpcb *pcb;
+{
+	struct secpolicyindex *spidx;
+	int error;
+
+	/* sanity check */
+	if (pcb == NULL)
+		panic("ipsec4_setspidx_inpcb: no PCB found.\n");
+	if (pcb->inp_sp == NULL)
+		panic("ipsec4_setspidx_inpcb: no inp_sp found.\n");
+	if (pcb->inp_sp->sp_out == NULL || pcb->inp_sp->sp_in == NULL)
+		panic("ipsec4_setspidx_inpcb: no sp_in/out found.\n");
+
+	bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx));
+	bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx));
+
+	spidx = &pcb->inp_sp->sp_in->spidx;
+	error = ipsec_setspidx(m, spidx, 1);
+	if (error)
+		goto bad;
+	spidx->dir = IPSEC_DIR_INBOUND;
+
+	spidx = &pcb->inp_sp->sp_out->spidx;
+	error = ipsec_setspidx(m, spidx, 1);
+	if (error)
+		goto bad;
+	spidx->dir = IPSEC_DIR_OUTBOUND;
+
+	return 0;
+
+bad:
+	bzero(&pcb->inp_sp->sp_in->spidx, sizeof(*spidx));
+	bzero(&pcb->inp_sp->sp_out->spidx, sizeof(*spidx));
+	return error;
+}
+
+#ifdef INET6
+static int
+ipsec6_setspidx_in6pcb(m, pcb)
+	struct mbuf *m;
+	struct in6pcb *pcb;
+{
+	struct secpolicyindex *spidx;
+	int error;
+
+	/* sanity check */
+	if (pcb == NULL)
+		panic("ipsec6_setspidx_in6pcb: no PCB found.\n");
+	if (pcb->in6p_sp == NULL)
+		panic("ipsec6_setspidx_in6pcb: no in6p_sp found.\n");
+	if (pcb->in6p_sp->sp_out == NULL || pcb->in6p_sp->sp_in == NULL)
+		panic("ipsec6_setspidx_in6pcb: no sp_in/out found.\n");
+
+	bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx));
+	bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx));
+
+	spidx = &pcb->in6p_sp->sp_in->spidx;
+	error = ipsec_setspidx(m, spidx, 1);
+	if (error)
+		goto bad;
+	spidx->dir = IPSEC_DIR_INBOUND;
+
+	spidx = &pcb->in6p_sp->sp_out->spidx;
+	error = ipsec_setspidx(m, spidx, 1);
+	if (error)
+		goto bad;
+	spidx->dir = IPSEC_DIR_OUTBOUND;
+
+	return 0;
+
+bad:
+	bzero(&pcb->in6p_sp->sp_in->spidx, sizeof(*spidx));
+	bzero(&pcb->in6p_sp->sp_out->spidx, sizeof(*spidx));
+	return error;
+}
+#endif
+
+/*
+ * configure security policy index (src/dst/proto/sport/dport)
+ * by looking at the content of mbuf.
+ * the caller is responsible for error recovery (like clearing up spidx).
+ */
+static int
+ipsec_setspidx(m, spidx, needport)
+	struct mbuf *m;
+	struct secpolicyindex *spidx;
+	int needport;
+{
+	struct ip *ip = NULL;
+	struct ip ipbuf;
+	u_int v;
+	struct mbuf *n;
+	int len;
+	int error;
+
+	if (m == NULL)
+		panic("ipsec_setspidx: m == 0 passed.\n");
+
+	bzero(spidx, sizeof(*spidx));
+
+	/*
+	 * validate m->m_pkthdr.len.  we see incorrect length if we
+	 * mistakenly call this function with inconsistent mbuf chain
+	 * (like 4.4BSD tcp/udp processing).  XXX should we panic here?
+	 */
+	len = 0;
+	for (n = m; n; n = n->m_next)
+		len += n->m_len;
+	if (m->m_pkthdr.len != len) {
+		KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+			printf("ipsec_setspidx: "
+			       "total of m_len(%d) != pkthdr.len(%d), "
+			       "ignored.\n",
+				len, m->m_pkthdr.len));
+		return EINVAL;
+	}
+
+	if (m->m_pkthdr.len < sizeof(struct ip)) {
+		KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+			printf("ipsec_setspidx: "
+			    "pkthdr.len(%d) < sizeof(struct ip), ignored.\n",
+			    m->m_pkthdr.len));
+		return EINVAL;
+	}
+
+	if (m->m_len >= sizeof(*ip))
+		ip = mtod(m, struct ip *);
+	else {
+		m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf);
+		ip = &ipbuf;
+	}
+#ifdef _IP_VHL
+	v = _IP_VHL_V(ip->ip_vhl);
+#else
+	v = ip->ip_v;
+#endif
+	switch (v) {
+	case 4:
+		error = ipsec4_setspidx_ipaddr(m, spidx);
+		if (error)
+			return error;
+		ipsec4_get_ulp(m, spidx, needport);
+		return 0;
+#ifdef INET6
+	case 6:
+		if (m->m_pkthdr.len < sizeof(struct ip6_hdr)) {
+			KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+				printf("ipsec_setspidx: "
+				    "pkthdr.len(%d) < sizeof(struct ip6_hdr), "
+				    "ignored.\n", m->m_pkthdr.len));
+			return EINVAL;
+		}
+		error = ipsec6_setspidx_ipaddr(m, spidx);
+		if (error)
+			return error;
+		ipsec6_get_ulp(m, spidx, needport);
+		return 0;
+#endif
+	default:
+		KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+			printf("ipsec_setspidx: "
+			    "unknown IP version %u, ignored.\n", v));
+		return EINVAL;
+	}
+}
+
+static void
+ipsec4_get_ulp(m, spidx, needport)
+	struct mbuf *m;
+	struct secpolicyindex *spidx;
+	int needport;
+{
+	struct ip ip;
+	struct ip6_ext ip6e;
+	u_int8_t nxt;
+	int off;
+	struct tcphdr th;
+	struct udphdr uh;
+
+	/* sanity check */
+	if (m == NULL)
+		panic("ipsec4_get_ulp: NULL pointer was passed.\n");
+	if (m->m_pkthdr.len < sizeof(ip))
+		panic("ipsec4_get_ulp: too short\n");
+
+	/* set default */
+	spidx->ul_proto = IPSEC_ULPROTO_ANY;
+	((struct sockaddr_in *)&spidx->src)->sin_port = IPSEC_PORT_ANY;
+	((struct sockaddr_in *)&spidx->dst)->sin_port = IPSEC_PORT_ANY;
+
+	m_copydata(m, 0, sizeof(ip), (caddr_t)&ip);
+	/* ip_input() flips it into host endian XXX need more checking */
+	if (ip.ip_off & (IP_MF | IP_OFFMASK))
+		return;
+
+	nxt = ip.ip_p;
+#ifdef _IP_VHL
+	off = _IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	off = ip.ip_hl << 2;
+#endif
+	while (off < m->m_pkthdr.len) {
+		switch (nxt) {
+		case IPPROTO_TCP:
+			spidx->ul_proto = nxt;
+			if (!needport)
+				return;
+			if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
+				return;
+			m_copydata(m, off, sizeof(th), (caddr_t)&th);
+			((struct sockaddr_in *)&spidx->src)->sin_port =
+			    th.th_sport;
+			((struct sockaddr_in *)&spidx->dst)->sin_port =
+			    th.th_dport;
+			return;
+		case IPPROTO_UDP:
+			spidx->ul_proto = nxt;
+			if (!needport)
+				return;
+			if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
+				return;
+			m_copydata(m, off, sizeof(uh), (caddr_t)&uh);
+			((struct sockaddr_in *)&spidx->src)->sin_port =
+			    uh.uh_sport;
+			((struct sockaddr_in *)&spidx->dst)->sin_port =
+			    uh.uh_dport;
+			return;
+		case IPPROTO_AH:
+			if (m->m_pkthdr.len > off + sizeof(ip6e))
+				return;
+			m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+			off += (ip6e.ip6e_len + 2) << 2;
+			nxt = ip6e.ip6e_nxt;
+			break;
+		case IPPROTO_ICMP:
+		default:
+			/* XXX intermediate headers??? */
+			spidx->ul_proto = nxt;
+			return;
+		}
+	}
+}
+
+/* assumes that m is sane */
+static int
+ipsec4_setspidx_ipaddr(m, spidx)
+	struct mbuf *m;
+	struct secpolicyindex *spidx;
+{
+	struct ip *ip = NULL;
+	struct ip ipbuf;
+	struct sockaddr_in *sin;
+
+	if (m->m_len >= sizeof(*ip))
+		ip = mtod(m, struct ip *);
+	else {
+		m_copydata(m, 0, sizeof(ipbuf), (caddr_t)&ipbuf);
+		ip = &ipbuf;
+	}
+
+	sin = (struct sockaddr_in *)&spidx->src;
+	bzero(sin, sizeof(*sin));
+	sin->sin_family = AF_INET;
+	sin->sin_len = sizeof(struct sockaddr_in);
+	bcopy(&ip->ip_src, &sin->sin_addr, sizeof(ip->ip_src));
+	spidx->prefs = sizeof(struct in_addr) << 3;
+
+	sin = (struct sockaddr_in *)&spidx->dst;
+	bzero(sin, sizeof(*sin));
+	sin->sin_family = AF_INET;
+	sin->sin_len = sizeof(struct sockaddr_in);
+	bcopy(&ip->ip_dst, &sin->sin_addr, sizeof(ip->ip_dst));
+	spidx->prefd = sizeof(struct in_addr) << 3;
+	return 0;
+}
+
+#ifdef INET6
+static void
+ipsec6_get_ulp(m, spidx, needport)
+	struct mbuf *m;
+	struct secpolicyindex *spidx;
+	int needport;
+{
+	int off, nxt;
+	struct tcphdr th;
+	struct udphdr uh;
+	struct icmp6_hdr ih;
+
+	/* sanity check */
+	if (m == NULL)
+		panic("ipsec6_get_ulp: NULL pointer was passed.\n");
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+		printf("ipsec6_get_ulp:\n"); kdebug_mbuf(m));
+
+	/* set default */
+	spidx->ul_proto = IPSEC_ULPROTO_ANY;
+	((struct sockaddr_in6 *)&spidx->src)->sin6_port = IPSEC_PORT_ANY;
+	((struct sockaddr_in6 *)&spidx->dst)->sin6_port = IPSEC_PORT_ANY;
+
+	nxt = -1;
+	off = ip6_lasthdr(m, 0, IPPROTO_IPV6, &nxt);
+	if (off < 0 || m->m_pkthdr.len < off)
+		return;
+
+	switch (nxt) {
+	case IPPROTO_TCP:
+		spidx->ul_proto = nxt;
+		if (!needport)
+			break;
+		if (off + sizeof(struct tcphdr) > m->m_pkthdr.len)
+			break;
+		m_copydata(m, off, sizeof(th), (caddr_t)&th);
+		((struct sockaddr_in6 *)&spidx->src)->sin6_port = th.th_sport;
+		((struct sockaddr_in6 *)&spidx->dst)->sin6_port = th.th_dport;
+		break;
+	case IPPROTO_UDP:
+		spidx->ul_proto = nxt;
+		if (!needport)
+			break;
+		if (off + sizeof(struct udphdr) > m->m_pkthdr.len)
+			break;
+		m_copydata(m, off, sizeof(uh), (caddr_t)&uh);
+		((struct sockaddr_in6 *)&spidx->src)->sin6_port = uh.uh_sport;
+		((struct sockaddr_in6 *)&spidx->dst)->sin6_port = uh.uh_dport;
+		break;
+	case IPPROTO_ICMPV6:
+		spidx->ul_proto = nxt;
+		if (off + sizeof(struct icmp6_hdr) > m->m_pkthdr.len)
+			break;
+		m_copydata(m, off, sizeof(ih), (caddr_t)&ih);
+		((struct sockaddr_in6 *)&spidx->src)->sin6_port =
+			htons((u_short)ih.icmp6_type);
+		((struct sockaddr_in6 *)&spidx->dst)->sin6_port =
+			htons((u_short)ih.icmp6_code);
+		break;
+	default:
+		/* XXX intermediate headers??? */
+		spidx->ul_proto = nxt;
+		break;
+	}
+}
+
+/* assumes that m is sane */
+static int
+ipsec6_setspidx_ipaddr(m, spidx)
+	struct mbuf *m;
+	struct secpolicyindex *spidx;
+{
+	struct ip6_hdr *ip6 = NULL;
+	struct ip6_hdr ip6buf;
+	struct sockaddr_in6 *sin6;
+
+	if (m->m_len >= sizeof(*ip6))
+		ip6 = mtod(m, struct ip6_hdr *);
+	else {
+		m_copydata(m, 0, sizeof(ip6buf), (caddr_t)&ip6buf);
+		ip6 = &ip6buf;
+	}
+
+	sin6 = (struct sockaddr_in6 *)&spidx->src;
+	bzero(sin6, sizeof(*sin6));
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_len = sizeof(struct sockaddr_in6);
+	bcopy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(ip6->ip6_src));
+	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+		sin6->sin6_addr.s6_addr16[1] = 0;
+		sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
+	}
+	spidx->prefs = sizeof(struct in6_addr) << 3;
+
+	sin6 = (struct sockaddr_in6 *)&spidx->dst;
+	bzero(sin6, sizeof(*sin6));
+	sin6->sin6_family = AF_INET6;
+	sin6->sin6_len = sizeof(struct sockaddr_in6);
+	bcopy(&ip6->ip6_dst, &sin6->sin6_addr, sizeof(ip6->ip6_dst));
+	if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+		sin6->sin6_addr.s6_addr16[1] = 0;
+		sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
+	}
+	spidx->prefd = sizeof(struct in6_addr) << 3;
+
+	return 0;
+}
+#endif
+
+static struct inpcbpolicy *
+ipsec_newpcbpolicy()
+{
+	struct inpcbpolicy *p;
+
+	p = (struct inpcbpolicy *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+	return p;
+}
+
+static void
+ipsec_delpcbpolicy(p)
+	struct inpcbpolicy *p;
+{
+
+	free(p, M_SECA);
+}
+
+/* initialize policy in PCB */
+int
+ipsec_init_policy(so, pcb_sp)
+	struct socket *so;
+	struct inpcbpolicy **pcb_sp;
+{
+	struct inpcbpolicy *new;
+
+	/* sanity check. */
+	if (so == NULL || pcb_sp == NULL)
+		panic("ipsec_init_policy: NULL pointer was passed.\n");
+
+	new = ipsec_newpcbpolicy();
+	if (new == NULL) {
+		ipseclog((LOG_DEBUG, "ipsec_init_policy: No more memory.\n"));
+		return ENOBUFS;
+	}
+	bzero(new, sizeof(*new));
+        
+        new->priv = 1;
+
+	if ((new->sp_in = key_newsp()) == NULL) {
+		ipsec_delpcbpolicy(new);
+		return ENOBUFS;
+	}
+	new->sp_in->state = IPSEC_SPSTATE_ALIVE;
+	new->sp_in->policy = IPSEC_POLICY_ENTRUST;
+
+	if ((new->sp_out = key_newsp()) == NULL) {
+		key_freesp(new->sp_in);
+		ipsec_delpcbpolicy(new);
+		return ENOBUFS;
+	}
+	new->sp_out->state = IPSEC_SPSTATE_ALIVE;
+	new->sp_out->policy = IPSEC_POLICY_ENTRUST;
+
+	*pcb_sp = new;
+
+	return 0;
+}
+
+/* copy old ipsec policy into new */
+int
+ipsec_copy_policy(old, new)
+	struct inpcbpolicy *old, *new;
+{
+	struct secpolicy *sp;
+
+	sp = ipsec_deepcopy_policy(old->sp_in);
+	if (sp) {
+		key_freesp(new->sp_in);
+		new->sp_in = sp;
+	} else
+		return ENOBUFS;
+
+	sp = ipsec_deepcopy_policy(old->sp_out);
+	if (sp) {
+		key_freesp(new->sp_out);
+		new->sp_out = sp;
+	} else
+		return ENOBUFS;
+
+	new->priv = old->priv;
+
+	return 0;
+}
+
+/* deep-copy a policy in PCB */
+static struct secpolicy *
+ipsec_deepcopy_policy(src)
+	struct secpolicy *src;
+{
+	struct ipsecrequest *newchain = NULL;
+	struct ipsecrequest *p;
+	struct ipsecrequest **q;
+	struct ipsecrequest *r;
+	struct secpolicy *dst;
+
+	dst = key_newsp();
+	if (src == NULL || dst == NULL)
+		return NULL;
+
+	/*
+	 * deep-copy IPsec request chain.  This is required since struct
+	 * ipsecrequest is not reference counted.
+	 */
+	q = &newchain;
+	for (p = src->req; p; p = p->next) {
+		*q = (struct ipsecrequest *)malloc(sizeof(struct ipsecrequest),
+			M_SECA, M_NOWAIT);
+		if (*q == NULL)
+			goto fail;
+		bzero(*q, sizeof(**q));
+		(*q)->next = NULL;
+
+		(*q)->saidx.proto = p->saidx.proto;
+		(*q)->saidx.mode = p->saidx.mode;
+		(*q)->level = p->level;
+		(*q)->saidx.reqid = p->saidx.reqid;
+
+		bcopy(&p->saidx.src, &(*q)->saidx.src, sizeof((*q)->saidx.src));
+		bcopy(&p->saidx.dst, &(*q)->saidx.dst, sizeof((*q)->saidx.dst));
+
+		(*q)->sav = NULL;
+		(*q)->sp = dst;
+
+		q = &((*q)->next);
+	}
+
+	dst->req = newchain;
+	dst->state = src->state;
+	dst->policy = src->policy;
+	/* do not touch the refcnt fields */
+
+	return dst;
+
+fail:
+	for (p = newchain; p; p = r) {
+		r = p->next;
+		free(p, M_SECA);
+		p = NULL;
+	}
+	return NULL;
+}
+
+/* set policy and ipsec request if present. */
+static int
+ipsec_set_policy(pcb_sp, optname, request, len, priv)
+	struct secpolicy **pcb_sp;
+	int optname;
+	caddr_t request;
+	size_t len;
+	int priv;
+{
+	struct sadb_x_policy *xpl;
+	struct secpolicy *newsp = NULL;
+	int error;
+
+	/* sanity check. */
+	if (pcb_sp == NULL || *pcb_sp == NULL || request == NULL)
+		return EINVAL;
+	if (len < sizeof(*xpl))
+		return EINVAL;
+	xpl = (struct sadb_x_policy *)request;
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+		printf("ipsec_set_policy: passed policy\n");
+		kdebug_sadb_x_policy((struct sadb_ext *)xpl));
+
+	/* check policy type */
+	/* ipsec_set_policy() accepts IPSEC, ENTRUST and BYPASS. */
+	if (xpl->sadb_x_policy_type == IPSEC_POLICY_DISCARD
+	 || xpl->sadb_x_policy_type == IPSEC_POLICY_NONE)
+		return EINVAL;
+
+	/* check privileged socket */
+	if (priv == 0 && xpl->sadb_x_policy_type == IPSEC_POLICY_BYPASS)
+		return EACCES;
+
+	/* allocation new SP entry */
+	if ((newsp = key_msg2sp(xpl, len, &error)) == NULL)
+		return error;
+
+	newsp->state = IPSEC_SPSTATE_ALIVE;
+
+	/* clear old SP and set new SP */
+	key_freesp(*pcb_sp);
+	*pcb_sp = newsp;
+	KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+		printf("ipsec_set_policy: new policy\n");
+		kdebug_secpolicy(newsp));
+
+	return 0;
+}
+
+static int
+ipsec_get_policy(pcb_sp, mp)
+	struct secpolicy *pcb_sp;
+	struct mbuf **mp;
+{
+
+	/* sanity check. */
+	if (pcb_sp == NULL || mp == NULL)
+		return EINVAL;
+
+	*mp = key_sp2msg(pcb_sp);
+	if (!*mp) {
+		ipseclog((LOG_DEBUG, "ipsec_get_policy: No more memory.\n"));
+		return ENOBUFS;
+	}
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	(*mp)->m_type = MT_DATA;
+#else
+	(*mp)->m_type = MT_SOOPTS;
+#endif
+	KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+		printf("ipsec_get_policy:\n");
+		kdebug_mbuf(*mp));
+
+	return 0;
+}
+
+int
+ipsec4_set_policy(inp, optname, request, len, priv)
+	struct inpcb *inp;
+	int optname;
+	caddr_t request;
+	size_t len;
+	int priv;
+{
+	struct sadb_x_policy *xpl;
+	struct secpolicy **pcb_sp;
+
+	/* sanity check. */
+	if (inp == NULL || request == NULL)
+		return EINVAL;
+	if (len < sizeof(*xpl))
+		return EINVAL;
+	xpl = (struct sadb_x_policy *)request;
+
+	/* select direction */
+	switch (xpl->sadb_x_policy_dir) {
+	case IPSEC_DIR_INBOUND:
+		pcb_sp = &inp->inp_sp->sp_in;
+		break;
+	case IPSEC_DIR_OUTBOUND:
+		pcb_sp = &inp->inp_sp->sp_out;
+		break;
+	default:
+		ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n",
+			xpl->sadb_x_policy_dir));
+		return EINVAL;
+	}
+
+	ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY);
+	return ipsec_set_policy(pcb_sp, optname, request, len, priv);
+}
+
+int
+ipsec4_get_policy(inp, request, len, mp)
+	struct inpcb *inp;
+	caddr_t request;
+	size_t len;
+	struct mbuf **mp;
+{
+	struct sadb_x_policy *xpl;
+	struct secpolicy *pcb_sp;
+
+	/* sanity check. */
+	if (inp == NULL || request == NULL || mp == NULL)
+		return EINVAL;
+	if (inp->inp_sp == NULL)
+		panic("policy in PCB is NULL\n");
+	if (len < sizeof(*xpl))
+		return EINVAL;
+	xpl = (struct sadb_x_policy *)request;
+
+	/* select direction */
+	switch (xpl->sadb_x_policy_dir) {
+	case IPSEC_DIR_INBOUND:
+		pcb_sp = inp->inp_sp->sp_in;
+		break;
+	case IPSEC_DIR_OUTBOUND:
+		pcb_sp = inp->inp_sp->sp_out;
+		break;
+	default:
+		ipseclog((LOG_ERR, "ipsec4_set_policy: invalid direction=%u\n",
+			xpl->sadb_x_policy_dir));
+		return EINVAL;
+	}
+
+	return ipsec_get_policy(pcb_sp, mp);
+}
+
+/* delete policy in PCB */
+int
+ipsec4_delete_pcbpolicy(inp)
+	struct inpcb *inp;
+{
+	/* sanity check. */
+	if (inp == NULL)
+		panic("ipsec4_delete_pcbpolicy: NULL pointer was passed.\n");
+
+	if (inp->inp_sp == NULL)
+		return 0;
+
+	if (inp->inp_sp->sp_in != NULL) {
+		key_freesp(inp->inp_sp->sp_in);
+		inp->inp_sp->sp_in = NULL;
+	}
+
+	if (inp->inp_sp->sp_out != NULL) {
+		key_freesp(inp->inp_sp->sp_out);
+		inp->inp_sp->sp_out = NULL;
+	}
+
+	ipsec_invalpcbcache(inp->inp_sp, IPSEC_DIR_ANY);
+
+	ipsec_delpcbpolicy(inp->inp_sp);
+	inp->inp_sp = NULL;
+
+	return 0;
+}
+
+#ifdef INET6
+int
+ipsec6_set_policy(in6p, optname, request, len, priv)
+	struct in6pcb *in6p;
+	int optname;
+	caddr_t request;
+	size_t len;
+	int priv;
+{
+	struct sadb_x_policy *xpl;
+	struct secpolicy **pcb_sp;
+
+	/* sanity check. */
+	if (in6p == NULL || request == NULL)
+		return EINVAL;
+	if (len < sizeof(*xpl))
+		return EINVAL;
+	xpl = (struct sadb_x_policy *)request;
+
+	/* select direction */
+	switch (xpl->sadb_x_policy_dir) {
+	case IPSEC_DIR_INBOUND:
+		pcb_sp = &in6p->in6p_sp->sp_in;
+		break;
+	case IPSEC_DIR_OUTBOUND:
+		pcb_sp = &in6p->in6p_sp->sp_out;
+		break;
+	default:
+		ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n",
+			xpl->sadb_x_policy_dir));
+		return EINVAL;
+	}
+
+	ipsec_invalpcbcache(in6p->in6p_sp, IPSEC_DIR_ANY);
+	return ipsec_set_policy(pcb_sp, optname, request, len, priv);
+}
+
+int
+ipsec6_get_policy(in6p, request, len, mp)
+	struct in6pcb *in6p;
+	caddr_t request;
+	size_t len;
+	struct mbuf **mp;
+{
+	struct sadb_x_policy *xpl;
+	struct secpolicy *pcb_sp;
+
+	/* sanity check. */
+	if (in6p == NULL || request == NULL || mp == NULL)
+		return EINVAL;
+	if (in6p->in6p_sp == NULL)
+		panic("policy in PCB is NULL\n");
+	if (len < sizeof(*xpl))
+		return EINVAL;
+	xpl = (struct sadb_x_policy *)request;
+
+	/* select direction */
+	switch (xpl->sadb_x_policy_dir) {
+	case IPSEC_DIR_INBOUND:
+		pcb_sp = in6p->in6p_sp->sp_in;
+		break;
+	case IPSEC_DIR_OUTBOUND:
+		pcb_sp = in6p->in6p_sp->sp_out;
+		break;
+	default:
+		ipseclog((LOG_ERR, "ipsec6_set_policy: invalid direction=%u\n",
+			xpl->sadb_x_policy_dir));
+		return EINVAL;
+	}
+
+	return ipsec_get_policy(pcb_sp, mp);
+}
+
+int
+ipsec6_delete_pcbpolicy(in6p)
+	struct in6pcb *in6p;
+{
+	/* sanity check. */
+	if (in6p == NULL)
+		panic("ipsec6_delete_pcbpolicy: NULL pointer was passed.\n");
+
+	if (in6p->in6p_sp == NULL)
+		return 0;
+
+	if (in6p->in6p_sp->sp_in != NULL) {
+		key_freesp(in6p->in6p_sp->sp_in);
+		in6p->in6p_sp->sp_in = NULL;
+	}
+
+	if (in6p->in6p_sp->sp_out != NULL) {
+		key_freesp(in6p->in6p_sp->sp_out);
+		in6p->in6p_sp->sp_out = NULL;
+	}
+
+	ipsec_invalpcbcache(in6p->in6p_sp, IPSEC_DIR_ANY);
+
+	ipsec_delpcbpolicy(in6p->in6p_sp);
+	in6p->in6p_sp = NULL;
+
+	return 0;
+}
+#endif
+
+/*
+ * return current level.
+ * Either IPSEC_LEVEL_USE or IPSEC_LEVEL_REQUIRE are always returned.
+ */
+u_int
+ipsec_get_reqlevel(isr)
+	struct ipsecrequest *isr;
+{
+	u_int level = 0;
+	u_int esp_trans_deflev=0, esp_net_deflev=0, ah_trans_deflev, ah_net_deflev;
+
+	/* sanity check */
+	if (isr == NULL || isr->sp == NULL)
+		panic("ipsec_get_reqlevel: NULL pointer is passed.\n");
+	if (((struct sockaddr *)&isr->sp->spidx.src)->sa_family
+			!= ((struct sockaddr *)&isr->sp->spidx.dst)->sa_family)
+		panic("ipsec_get_reqlevel: family mismatched.\n");
+
+/* XXX note that we have ipseclog() expanded here - code sync issue */
+#define IPSEC_CHECK_DEFAULT(lev) \
+	(((lev) != IPSEC_LEVEL_USE && (lev) != IPSEC_LEVEL_REQUIRE	      \
+			&& (lev) != IPSEC_LEVEL_UNIQUE)			      \
+		? (lev) = IPSEC_LEVEL_REQUIRE,			              \
+		  (lev)						              \
+		: (lev))
+
+	/* set default level */
+	switch (((struct sockaddr *)&isr->sp->spidx.src)->sa_family) {
+#ifdef INET
+	case AF_INET:
+		esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_trans_deflev);
+		esp_net_deflev = IPSEC_CHECK_DEFAULT(ip4_esp_net_deflev);
+		ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_trans_deflev);
+		ah_net_deflev = IPSEC_CHECK_DEFAULT(ip4_ah_net_deflev);
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		esp_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_trans_deflev);
+		esp_net_deflev = IPSEC_CHECK_DEFAULT(ip6_esp_net_deflev);
+		ah_trans_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_trans_deflev);
+		ah_net_deflev = IPSEC_CHECK_DEFAULT(ip6_ah_net_deflev);
+		break;
+#endif /* INET6 */
+	default:
+		panic("key_get_reqlevel: Unknown family. %d\n",
+			((struct sockaddr *)&isr->sp->spidx.src)->sa_family);
+	}
+
+#undef IPSEC_CHECK_DEFAULT
+
+	/* set level */
+	switch (isr->level) {
+	case IPSEC_LEVEL_DEFAULT:
+		switch (isr->saidx.proto) {
+		case IPPROTO_ESP:
+			if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
+				level = esp_net_deflev;
+			else
+				level = esp_trans_deflev;
+			break;
+		case IPPROTO_AH:
+			if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
+				level = ah_net_deflev;
+			else
+				level = ah_trans_deflev;
+		case IPPROTO_IPCOMP:
+			/*
+			 * we don't really care, as IPcomp document says that
+			 * we shouldn't compress small packets
+			 */
+			level = IPSEC_LEVEL_USE;
+			break;
+		default:
+			panic("ipsec_get_reqlevel: "
+				"Illegal protocol defined %u\n",
+				isr->saidx.proto);
+		}
+		break;
+
+	case IPSEC_LEVEL_USE:
+	case IPSEC_LEVEL_REQUIRE:
+		level = isr->level;
+		break;
+	case IPSEC_LEVEL_UNIQUE:
+		level = IPSEC_LEVEL_REQUIRE;
+		break;
+
+	default:
+		panic("ipsec_get_reqlevel: Illegal IPsec level %u\n",
+			isr->level);
+	}
+
+	return level;
+}
+
+/*
+ * Check AH/ESP integrity.
+ * OUT:
+ *	0: valid
+ *	1: invalid
+ */
+static int
+ipsec_in_reject(sp, m)
+	struct secpolicy *sp;
+	struct mbuf *m;
+{
+	struct ipsecrequest *isr;
+	u_int level;
+	int need_auth, need_conf, need_icv;
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec_in_reject: using SP\n");
+		kdebug_secpolicy(sp));
+
+	/* check policy */
+	switch (sp->policy) {
+	case IPSEC_POLICY_DISCARD:
+		return 1;
+	case IPSEC_POLICY_BYPASS:
+	case IPSEC_POLICY_NONE:
+		return 0;
+	
+	case IPSEC_POLICY_IPSEC:
+		break;
+
+	case IPSEC_POLICY_ENTRUST:
+	default:
+		panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy);
+	}
+
+	need_auth = 0;
+	need_conf = 0;
+	need_icv = 0;
+
+	/* XXX should compare policy against ipsec header history */
+
+	for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+		/* get current level */
+		level = ipsec_get_reqlevel(isr);
+
+		switch (isr->saidx.proto) {
+		case IPPROTO_ESP:
+			if (level == IPSEC_LEVEL_REQUIRE) {
+				need_conf++;
+
+				if (isr->sav != NULL
+				 && isr->sav->flags == SADB_X_EXT_NONE
+				 && isr->sav->alg_auth != SADB_AALG_NONE)
+					need_icv++;
+			}
+			break;
+		case IPPROTO_AH:
+			if (level == IPSEC_LEVEL_REQUIRE) {
+				need_auth++;
+				need_icv++;
+			}
+			break;
+		case IPPROTO_IPCOMP:
+			/*
+			 * we don't really care, as IPcomp document says that
+			 * we shouldn't compress small packets, IPComp policy
+			 * should always be treated as being in "use" level.
+			 */
+			break;
+		}
+	}
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DUMP,
+		printf("ipsec_in_reject: auth:%d conf:%d icv:%d m_flags:%x\n",
+			need_auth, need_conf, need_icv, m->m_flags));
+
+	if ((need_conf && !(m->m_flags & M_DECRYPTED))
+	 || (!need_auth && need_icv && !(m->m_flags & M_AUTHIPDGM))
+	 || (need_auth && !(m->m_flags & M_AUTHIPHDR)))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * Check AH/ESP integrity.
+ * This function is called from tcp_input(), udp_input(),
+ * and {ah,esp}4_input for tunnel mode
+ */
+int
+ipsec4_in_reject_so(m, so)
+	struct mbuf *m;
+	struct socket *so;
+{
+	struct secpolicy *sp = NULL;
+	int error;
+	int result;
+
+	/* sanity check */
+	if (m == NULL)
+		return 0;	/* XXX should be panic ? */
+
+	/* get SP for this packet.
+	 * When we are called from ip_forward(), we call
+	 * ipsec4_getpolicybyaddr() with IP_FORWARDING flag.
+	 */
+	if (so == NULL)
+		sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error);
+	else
+		sp = ipsec4_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error);
+
+	if (sp == NULL)
+		return 0;	/* XXX should be panic ?
+				 * -> No, there may be error. */
+
+	result = ipsec_in_reject(sp, m);
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ipsec4_in_reject_so call free SP:%p\n", sp));
+	key_freesp(sp);
+
+	return result;
+}
+
+int
+ipsec4_in_reject(m, inp)
+	struct mbuf *m;
+	struct inpcb *inp;
+{
+	if (inp == NULL)
+		return ipsec4_in_reject_so(m, NULL);
+	if (inp->inp_socket)
+		return ipsec4_in_reject_so(m, inp->inp_socket);
+	else
+		panic("ipsec4_in_reject: invalid inpcb/socket");
+        return 0;
+}
+
+#ifdef INET6
+/*
+ * Check AH/ESP integrity.
+ * This function is called from tcp6_input(), udp6_input(),
+ * and {ah,esp}6_input for tunnel mode
+ */
+int
+ipsec6_in_reject_so(m, so)
+	struct mbuf *m;
+	struct socket *so;
+{
+	struct secpolicy *sp = NULL;
+	int error;
+	int result;
+
+	/* sanity check */
+	if (m == NULL)
+		return 0;	/* XXX should be panic ? */
+
+	/* get SP for this packet.
+	 * When we are called from ip_forward(), we call
+	 * ipsec6_getpolicybyaddr() with IP_FORWARDING flag.
+	 */
+	if (so == NULL)
+		sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_INBOUND, IP_FORWARDING, &error);
+	else
+		sp = ipsec6_getpolicybysock(m, IPSEC_DIR_INBOUND, so, &error);
+
+	if (sp == NULL)
+		return 0;	/* XXX should be panic ? */
+
+	result = ipsec_in_reject(sp, m);
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ipsec6_in_reject_so call free SP:%p\n", sp));
+	key_freesp(sp);
+
+	return result;
+}
+
+int
+ipsec6_in_reject(m, in6p)
+	struct mbuf *m;
+	struct in6pcb *in6p;
+{
+	if (in6p == NULL)
+		return ipsec6_in_reject_so(m, NULL);
+	if (in6p->in6p_socket)
+		return ipsec6_in_reject_so(m, in6p->in6p_socket);
+	else
+		panic("ipsec6_in_reject: invalid in6p/socket");
+        return 0;
+}
+#endif
+
+/*
+ * compute the byte size to be occupied by IPsec header.
+ * in case it is tunneled, it includes the size of outer IP header.
+ * NOTE: SP passed is free in this function.
+ */
+static size_t
+ipsec_hdrsiz(sp)
+	struct secpolicy *sp;
+{
+	struct ipsecrequest *isr;
+	size_t siz, clen;
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec_hdrsiz: using SP\n");
+		kdebug_secpolicy(sp));
+
+	/* check policy */
+	switch (sp->policy) {
+	case IPSEC_POLICY_DISCARD:
+	case IPSEC_POLICY_BYPASS:
+	case IPSEC_POLICY_NONE:
+		return 0;
+	
+	case IPSEC_POLICY_IPSEC:
+		break;
+
+	case IPSEC_POLICY_ENTRUST:
+	default:
+		panic("ipsec_hdrsiz: Invalid policy found. %d\n", sp->policy);
+	}
+
+	siz = 0;
+
+	for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+		clen = 0;
+
+		switch (isr->saidx.proto) {
+		case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+			clen = esp_hdrsiz(isr);
+#else
+			clen = 0;	/* XXX */
+#endif
+			break;
+		case IPPROTO_AH:
+			clen = ah_hdrsiz(isr);
+			break;
+		case IPPROTO_IPCOMP:
+			clen = sizeof(struct ipcomp);
+			break;
+		}
+
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+			switch (((struct sockaddr *)&isr->saidx.dst)->sa_family) {
+			case AF_INET:
+				clen += sizeof(struct ip);
+				break;
+#ifdef INET6
+			case AF_INET6:
+				clen += sizeof(struct ip6_hdr);
+				break;
+#endif
+			default:
+				ipseclog((LOG_ERR, "ipsec_hdrsiz: "
+				    "unknown AF %d in IPsec tunnel SA\n",
+				    ((struct sockaddr *)&isr->saidx.dst)->sa_family));
+				break;
+			}
+		}
+		siz += clen;
+	}
+
+	return siz;
+}
+
+/* This function is called from ip_forward() and ipsec4_hdrsize_tcp(). */
+size_t
+ipsec4_hdrsiz(m, dir, inp)
+	struct mbuf *m;
+	u_int dir;
+	struct inpcb *inp;
+{
+	struct secpolicy *sp = NULL;
+	int error;
+	size_t size;
+
+	/* sanity check */
+	if (m == NULL)
+		return 0;	/* XXX should be panic ? */
+	if (inp != NULL && inp->inp_socket == NULL)
+		panic("ipsec4_hdrsize: why is socket NULL but there is PCB.");
+
+	/* get SP for this packet.
+	 * When we are called from ip_forward(), we call
+	 * ipsec4_getpolicybyaddr() with IP_FORWARDING flag.
+	 */
+	if (inp == NULL)
+		sp = ipsec4_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
+	else
+		sp = ipsec4_getpolicybysock(m, dir, inp->inp_socket, &error);
+
+	if (sp == NULL)
+		return 0;	/* XXX should be panic ? */
+
+	size = ipsec_hdrsiz(sp);
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ipsec4_hdrsiz call free SP:%p\n", sp));
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec4_hdrsiz: size:%lu.\n", (unsigned long)size));
+	key_freesp(sp);
+
+	return size;
+}
+
+#ifdef INET6
+/* This function is called from ipsec6_hdrsize_tcp(),
+ * and maybe from ip6_forward.()
+ */
+size_t
+ipsec6_hdrsiz(m, dir, in6p)
+	struct mbuf *m;
+	u_int dir;
+	struct in6pcb *in6p;
+{
+	struct secpolicy *sp = NULL;
+	int error;
+	size_t size;
+
+	/* sanity check */
+	if (m == NULL)
+		return 0;	/* XXX shoud be panic ? */
+	if (in6p != NULL && in6p->in6p_socket == NULL)
+		panic("ipsec6_hdrsize: why is socket NULL but there is PCB.");
+
+	/* get SP for this packet */
+	/* XXX Is it right to call with IP_FORWARDING. */
+	if (in6p == NULL)
+		sp = ipsec6_getpolicybyaddr(m, dir, IP_FORWARDING, &error);
+	else
+		sp = ipsec6_getpolicybysock(m, dir, in6p->in6p_socket, &error);
+
+	if (sp == NULL)
+		return 0;
+	size = ipsec_hdrsiz(sp);
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP ipsec6_hdrsiz call free SP:%p\n", sp));
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec6_hdrsiz: size:%lu.\n", (unsigned long)size));
+	key_freesp(sp);
+
+	return size;
+}
+#endif /* INET6 */
+
+#ifdef INET
+/*
+ * encapsulate for ipsec tunnel.
+ * ip->ip_src must be fixed later on.
+ */
+static int
+ipsec4_encapsulate(m, sav)
+	struct mbuf *m;
+	struct secasvar *sav;
+{
+	struct ip *oip;
+	struct ip *ip;
+	size_t hlen;
+	size_t plen;
+
+	/* can't tunnel between different AFs */
+	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+		!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
+	 || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET) {
+		m_freem(m);
+		return EINVAL;
+	}
+#if 0
+	/* XXX if the dst is myself, perform nothing. */
+	if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
+		m_freem(m);
+		return EINVAL;
+	}
+#endif
+
+	if (m->m_len < sizeof(*ip))
+		panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
+
+	ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+	hlen = _IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	hlen = ip->ip_hl << 2;
+#endif
+
+	if (m->m_len != hlen)
+		panic("ipsec4_encapsulate: assumption failed (first mbuf length)");
+
+	/* generate header checksum */
+	ip->ip_sum = 0;
+#ifdef _IP_VHL
+	if (ip->ip_vhl == IP_VHL_BORING)
+		ip->ip_sum = in_cksum_hdr(ip);
+	else
+		ip->ip_sum = in_cksum(m, hlen);
+#else
+	ip->ip_sum = in_cksum(m, hlen);
+#endif
+
+	plen = m->m_pkthdr.len;
+
+	/*
+	 * grow the mbuf to accomodate the new IPv4 header.
+	 * NOTE: IPv4 options will never be copied.
+	 */
+	if (M_LEADINGSPACE(m->m_next) < hlen) {
+		struct mbuf *n;
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (!n) {
+			m_freem(m);
+			return ENOBUFS;
+		}
+		n->m_len = hlen;
+		n->m_next = m->m_next;
+		m->m_next = n;
+		m->m_pkthdr.len += hlen;
+		oip = mtod(n, struct ip *);
+	} else {
+		m->m_next->m_len += hlen;
+		m->m_next->m_data -= hlen;
+		m->m_pkthdr.len += hlen;
+		oip = mtod(m->m_next, struct ip *);
+	}
+	ip = mtod(m, struct ip *);
+	ovbcopy((caddr_t)ip, (caddr_t)oip, hlen);
+	m->m_len = sizeof(struct ip);
+	m->m_pkthdr.len -= (hlen - sizeof(struct ip));
+
+	/* construct new IPv4 header. see RFC 2401 5.1.2.1 */
+	/* ECN consideration. */
+	ip_ecn_ingress(ip4_ipsec_ecn, &ip->ip_tos, &oip->ip_tos);
+#ifdef _IP_VHL
+	ip->ip_vhl = IP_MAKE_VHL(IPVERSION, sizeof(struct ip) >> 2);
+#else
+	ip->ip_hl = sizeof(struct ip) >> 2;
+#endif
+	ip->ip_off &= htons(~IP_OFFMASK);
+	ip->ip_off &= htons(~IP_MF);
+	switch (ip4_ipsec_dfbit) {
+	case 0:	/* clear DF bit */
+		ip->ip_off &= htons(~IP_DF);
+		break;
+	case 1:	/* set DF bit */
+		ip->ip_off |= htons(IP_DF);
+		break;
+	default:	/* copy DF bit */
+		break;
+	}
+	ip->ip_p = IPPROTO_IPIP;
+	if (plen + sizeof(struct ip) < IP_MAXPACKET)
+		ip->ip_len = htons(plen + sizeof(struct ip));
+	else {
+		ipseclog((LOG_ERR, "IPv4 ipsec: size exceeds limit: "
+			"leave ip_len as is (invalid packet)\n"));
+	}
+	ip->ip_id = htons(ip_id++);
+	bcopy(&((struct sockaddr_in *)&sav->sah->saidx.src)->sin_addr,
+		&ip->ip_src, sizeof(ip->ip_src));
+	bcopy(&((struct sockaddr_in *)&sav->sah->saidx.dst)->sin_addr,
+		&ip->ip_dst, sizeof(ip->ip_dst));
+	ip->ip_ttl = IPDEFTTL;
+
+	/* XXX Should ip_src be updated later ? */
+
+	return 0;
+}
+#endif /* INET */
+
+#ifdef INET6
+static int
+ipsec6_encapsulate(m, sav)
+	struct mbuf *m;
+	struct secasvar *sav;
+{
+	struct ip6_hdr *oip6;
+	struct ip6_hdr *ip6;
+	size_t plen;
+
+	/* can't tunnel between different AFs */
+	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+		!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family
+	 || ((struct sockaddr *)&sav->sah->saidx.src)->sa_family != AF_INET6) {
+		m_freem(m);
+		return EINVAL;
+	}
+#if 0
+	/* XXX if the dst is myself, perform nothing. */
+	if (key_ismyaddr((struct sockaddr *)&sav->sah->saidx.dst)) {
+		m_freem(m);
+		return EINVAL;
+	}
+#endif
+
+	plen = m->m_pkthdr.len;
+
+	/*
+	 * grow the mbuf to accomodate the new IPv6 header.
+	 */
+	if (m->m_len != sizeof(struct ip6_hdr))
+		panic("ipsec6_encapsulate: assumption failed (first mbuf length)");
+	if (M_LEADINGSPACE(m->m_next) < sizeof(struct ip6_hdr)) {
+		struct mbuf *n;
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (!n) {
+			m_freem(m);
+			return ENOBUFS;
+		}
+		n->m_len = sizeof(struct ip6_hdr);
+		n->m_next = m->m_next;
+		m->m_next = n;
+		m->m_pkthdr.len += sizeof(struct ip6_hdr);
+		oip6 = mtod(n, struct ip6_hdr *);
+	} else {
+		m->m_next->m_len += sizeof(struct ip6_hdr);
+		m->m_next->m_data -= sizeof(struct ip6_hdr);
+		m->m_pkthdr.len += sizeof(struct ip6_hdr);
+		oip6 = mtod(m->m_next, struct ip6_hdr *);
+	}
+	ip6 = mtod(m, struct ip6_hdr *);
+	ovbcopy((caddr_t)ip6, (caddr_t)oip6, sizeof(struct ip6_hdr));
+
+	/* Fake link-local scope-class addresses */
+	if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_src))
+		oip6->ip6_src.s6_addr16[1] = 0;
+	if (IN6_IS_SCOPE_LINKLOCAL(&oip6->ip6_dst))
+		oip6->ip6_dst.s6_addr16[1] = 0;
+
+	/* construct new IPv6 header. see RFC 2401 5.1.2.2 */
+	/* ECN consideration. */
+	ip6_ecn_ingress(ip6_ipsec_ecn, &ip6->ip6_flow, &oip6->ip6_flow);
+	if (plen < IPV6_MAXPACKET - sizeof(struct ip6_hdr))
+		ip6->ip6_plen = htons(plen);
+	else {
+		/* ip6->ip6_plen will be updated in ip6_output() */
+	}
+	ip6->ip6_nxt = IPPROTO_IPV6;
+	bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.src)->sin6_addr,
+		&ip6->ip6_src, sizeof(ip6->ip6_src));
+	bcopy(&((struct sockaddr_in6 *)&sav->sah->saidx.dst)->sin6_addr,
+		&ip6->ip6_dst, sizeof(ip6->ip6_dst));
+	ip6->ip6_hlim = IPV6_DEFHLIM;
+
+	/* XXX Should ip6_src be updated later ? */
+
+	return 0;
+}
+#endif /* INET6 */
+
+/*
+ * Check the variable replay window.
+ * ipsec_chkreplay() performs replay check before ICV verification.
+ * ipsec_updatereplay() updates replay bitmap.  This must be called after
+ * ICV verification (it also performs replay check, which is usually done
+ * beforehand).
+ * 0 (zero) is returned if packet disallowed, 1 if packet permitted.
+ *
+ * based on RFC 2401.
+ */
+int
+ipsec_chkreplay(seq, sav)
+	u_int32_t seq;
+	struct secasvar *sav;
+{
+	const struct secreplay *replay;
+	u_int32_t diff;
+	int fr;
+	u_int32_t wsizeb;	/* constant: bits of window size */
+	int frlast;		/* constant: last frame */
+
+	/* sanity check */
+	if (sav == NULL)
+		panic("ipsec_chkreplay: NULL pointer was passed.\n");
+
+	replay = sav->replay;
+
+	if (replay->wsize == 0)
+		return 1;	/* no need to check replay. */
+
+	/* constant */
+	frlast = replay->wsize - 1;
+	wsizeb = replay->wsize << 3;
+
+	/* sequence number of 0 is invalid */
+	if (seq == 0)
+		return 0;
+
+	/* first time is always okay */
+	if (replay->count == 0)
+		return 1;
+
+	if (seq > replay->lastseq) {
+		/* larger sequences are okay */
+		return 1;
+	} else {
+		/* seq is equal or less than lastseq. */
+		diff = replay->lastseq - seq;
+
+		/* over range to check, i.e. too old or wrapped */
+		if (diff >= wsizeb)
+			return 0;
+
+		fr = frlast - diff / 8;
+
+		/* this packet already seen ? */
+		if ((replay->bitmap)[fr] & (1 << (diff % 8)))
+			return 0;
+
+		/* out of order but good */
+		return 1;
+	}
+}
+
+/*
+ * check replay counter whether to update or not.
+ * OUT:	0:	OK
+ *	1:	NG
+ */
+int
+ipsec_updatereplay(seq, sav)
+	u_int32_t seq;
+	struct secasvar *sav;
+{
+	struct secreplay *replay;
+	u_int32_t diff;
+	int fr;
+	u_int32_t wsizeb;	/* constant: bits of window size */
+	int frlast;		/* constant: last frame */
+
+	/* sanity check */
+	if (sav == NULL)
+		panic("ipsec_chkreplay: NULL pointer was passed.\n");
+
+	replay = sav->replay;
+
+	if (replay->wsize == 0)
+		goto ok;	/* no need to check replay. */
+
+	/* constant */
+	frlast = replay->wsize - 1;
+	wsizeb = replay->wsize << 3;
+
+	/* sequence number of 0 is invalid */
+	if (seq == 0)
+		return 1;
+
+	/* first time */
+	if (replay->count == 0) {
+		replay->lastseq = seq;
+		bzero(replay->bitmap, replay->wsize);
+		(replay->bitmap)[frlast] = 1;
+		goto ok;
+	}
+
+	if (seq > replay->lastseq) {
+		/* seq is larger than lastseq. */
+		diff = seq - replay->lastseq;
+
+		/* new larger sequence number */
+		if (diff < wsizeb) {
+			/* In window */
+			/* set bit for this packet */
+			vshiftl(replay->bitmap, diff, replay->wsize);
+			(replay->bitmap)[frlast] |= 1;
+		} else {
+			/* this packet has a "way larger" */
+			bzero(replay->bitmap, replay->wsize);
+			(replay->bitmap)[frlast] = 1;
+		}
+		replay->lastseq = seq;
+
+		/* larger is good */
+	} else {
+		/* seq is equal or less than lastseq. */
+		diff = replay->lastseq - seq;
+
+		/* over range to check, i.e. too old or wrapped */
+		if (diff >= wsizeb)
+			return 1;
+
+		fr = frlast - diff / 8;
+
+		/* this packet already seen ? */
+		if ((replay->bitmap)[fr] & (1 << (diff % 8)))
+			return 1;
+
+		/* mark as seen */
+		(replay->bitmap)[fr] |= (1 << (diff % 8));
+
+		/* out of order but good */
+	}
+
+ok:
+	if (replay->count == ~0) {
+
+		/* set overflow flag */
+		replay->overflow++;
+
+		/* don't increment, no more packets accepted */
+		if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0)
+			return 1;
+
+		ipseclog((LOG_WARNING, "replay counter made %d cycle. %s\n",
+		    replay->overflow, ipsec_logsastr(sav)));
+	}
+
+	replay->count++;
+
+	return 0;
+}
+
+/*
+ * shift variable length buffer to left.
+ * IN:	bitmap: pointer to the buffer
+ * 	nbit:	the number of to shift.
+ *	wsize:	buffer size (bytes).
+ */
+static void
+vshiftl(bitmap, nbit, wsize)
+	unsigned char *bitmap;
+	int nbit, wsize;
+{
+	int s, j, i;
+	unsigned char over;
+
+	for (j = 0; j < nbit; j += 8) {
+		s = (nbit - j < 8) ? (nbit - j): 8;
+		bitmap[0] <<= s;
+		for (i = 1; i < wsize; i++) {
+			over = (bitmap[i] >> (8 - s));
+			bitmap[i] <<= s;
+			bitmap[i-1] |= over;
+		}
+	}
+
+	return;
+}
+
+const char *
+ipsec4_logpacketstr(ip, spi)
+	struct ip *ip;
+	u_int32_t spi;
+{
+	static char buf[256];
+	char *p;
+	u_int8_t *s, *d;
+
+	s = (u_int8_t *)(&ip->ip_src);
+	d = (u_int8_t *)(&ip->ip_dst);
+
+	p = buf;
+	snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi));
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), "src=%u.%u.%u.%u",
+		s[0], s[1], s[2], s[3]);
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), " dst=%u.%u.%u.%u",
+		d[0], d[1], d[2], d[3]);
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), ")");
+
+	return buf;
+}
+
+#ifdef INET6
+const char *
+ipsec6_logpacketstr(ip6, spi)
+	struct ip6_hdr *ip6;
+	u_int32_t spi;
+{
+	static char buf[256];
+	char *p;
+
+	p = buf;
+	snprintf(buf, sizeof(buf), "packet(SPI=%u ", (u_int32_t)ntohl(spi));
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), "src=%s",
+		ip6_sprintf(&ip6->ip6_src));
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), " dst=%s",
+		ip6_sprintf(&ip6->ip6_dst));
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), ")");
+
+	return buf;
+}
+#endif /* INET6 */
+
+const char *
+ipsec_logsastr(sav)
+	struct secasvar *sav;
+{
+	static char buf[256];
+	char *p;
+	struct secasindex *saidx = &sav->sah->saidx;
+
+	/* validity check */
+	if (((struct sockaddr *)&sav->sah->saidx.src)->sa_family
+			!= ((struct sockaddr *)&sav->sah->saidx.dst)->sa_family)
+		panic("ipsec_logsastr: family mismatched.\n");
+
+	p = buf;
+	snprintf(buf, sizeof(buf), "SA(SPI=%u ", (u_int32_t)ntohl(sav->spi));
+	while (p && *p)
+		p++;
+	if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET) {
+		u_int8_t *s, *d;
+		s = (u_int8_t *)&((struct sockaddr_in *)&saidx->src)->sin_addr;
+		d = (u_int8_t *)&((struct sockaddr_in *)&saidx->dst)->sin_addr;
+		snprintf(p, sizeof(buf) - (p - buf),
+			"src=%d.%d.%d.%d dst=%d.%d.%d.%d",
+			s[0], s[1], s[2], s[3], d[0], d[1], d[2], d[3]);
+	}
+#ifdef INET6
+	else if (((struct sockaddr *)&saidx->src)->sa_family == AF_INET6) {
+		snprintf(p, sizeof(buf) - (p - buf),
+			"src=%s",
+			ip6_sprintf(&((struct sockaddr_in6 *)&saidx->src)->sin6_addr));
+		while (p && *p)
+			p++;
+		snprintf(p, sizeof(buf) - (p - buf),
+			" dst=%s",
+			ip6_sprintf(&((struct sockaddr_in6 *)&saidx->dst)->sin6_addr));
+	}
+#endif
+	while (p && *p)
+		p++;
+	snprintf(p, sizeof(buf) - (p - buf), ")");
+
+	return buf;
+}
+
+void
+ipsec_dumpmbuf(m)
+	struct mbuf *m;
+{
+	int totlen;
+	int i;
+	u_char *p;
+
+	totlen = 0;
+	printf("---\n");
+	while (m) {
+		p = mtod(m, u_char *);
+		for (i = 0; i < m->m_len; i++) {
+			printf("%02x ", p[i]);
+			totlen++;
+			if (totlen % 16 == 0)
+				printf("\n");
+		}
+		m = m->m_next;
+	}
+	if (totlen % 16 != 0)
+		printf("\n");
+	printf("---\n");
+}
+
+#ifdef INET
+/*
+ * IPsec output logic for IPv4.
+ */
+int
+ipsec4_output(state, sp, flags)
+	struct ipsec_output_state *state;
+	struct secpolicy *sp;
+	int flags;
+{
+	struct ip *ip = NULL;
+	struct ipsecrequest *isr = NULL;
+	struct secasindex saidx;
+	int s;
+	int error;
+	struct sockaddr_in *dst4;
+	struct sockaddr_in *sin;
+
+	if (!state)
+		panic("state == NULL in ipsec4_output");
+	if (!state->m)
+		panic("state->m == NULL in ipsec4_output");
+	if (!state->ro)
+		panic("state->ro == NULL in ipsec4_output");
+	if (!state->dst)
+		panic("state->dst == NULL in ipsec4_output");
+	state->encap = 0;
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec4_output: applyed SP\n");
+		kdebug_secpolicy(sp));
+
+	for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+#if 0	/* give up to check restriction of transport mode */
+	/* XXX but should be checked somewhere */
+		/*
+		 * some of the IPsec operation must be performed only in
+		 * originating case.
+		 */
+		if (isr->saidx.mode == IPSEC_MODE_TRANSPORT
+		 && (flags & IP_FORWARDING))
+			continue;
+#endif
+
+		/* make SA index for search proper SA */
+		ip = mtod(state->m, struct ip *);
+		bcopy(&isr->saidx, &saidx, sizeof(saidx));
+		saidx.mode = isr->saidx.mode;
+		saidx.reqid = isr->saidx.reqid;
+		sin = (struct sockaddr_in *)&saidx.src;
+		if (sin->sin_len == 0) {
+			sin->sin_len = sizeof(*sin);
+			sin->sin_family = AF_INET;
+			sin->sin_port = IPSEC_PORT_ANY;
+			bcopy(&ip->ip_src, &sin->sin_addr,
+			    sizeof(sin->sin_addr));
+		}
+		sin = (struct sockaddr_in *)&saidx.dst;
+		if (sin->sin_len == 0) {
+			sin->sin_len = sizeof(*sin);
+			sin->sin_family = AF_INET;
+			sin->sin_port = IPSEC_PORT_ANY;
+			bcopy(&ip->ip_dst, &sin->sin_addr,
+			    sizeof(sin->sin_addr));
+		}
+
+		if ((error = key_checkrequest(isr, &saidx)) != 0) {
+			/*
+			 * IPsec processing is required, but no SA found.
+			 * I assume that key_acquire() had been called
+			 * to get/establish the SA. Here I discard
+			 * this packet because it is responsibility for
+			 * upper layer to retransmit the packet.
+			 */
+			ipsecstat.out_nosa++;
+			goto bad;
+		}
+
+		/* validity check */
+		if (isr->sav == NULL) {
+			switch (ipsec_get_reqlevel(isr)) {
+			case IPSEC_LEVEL_USE:
+				continue;
+			case IPSEC_LEVEL_REQUIRE:
+				/* must be not reached here. */
+				panic("ipsec4_output: no SA found, but required.");
+			}
+		}
+
+		/*
+		 * If there is no valid SA, we give up to process any
+		 * more.  In such a case, the SA's status is changed
+		 * from DYING to DEAD after allocating.  If a packet
+		 * send to the receiver by dead SA, the receiver can
+		 * not decode a packet because SA has been dead.
+		 */
+		if (isr->sav->state != SADB_SASTATE_MATURE
+		 && isr->sav->state != SADB_SASTATE_DYING) {
+			ipsecstat.out_nosa++;
+			error = EINVAL;
+			goto bad;
+		}
+
+		/*
+		 * There may be the case that SA status will be changed when
+		 * we are refering to one. So calling splsoftnet().
+		 */
+#ifdef __NetBSD__
+		s = splsoftnet();
+#else
+		s = splnet();
+#endif
+
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+			/*
+			 * build IPsec tunnel.
+			 */
+			/* XXX should be processed with other familiy */
+			if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET) {
+				ipseclog((LOG_ERR, "ipsec4_output: "
+				    "family mismatched between inner and outer spi=%u\n",
+				    (u_int32_t)ntohl(isr->sav->spi)));
+				splx(s);
+				error = EAFNOSUPPORT;
+				goto bad;
+			}
+
+			state->m = ipsec4_splithdr(state->m);
+			if (!state->m) {
+				splx(s);
+				error = ENOMEM;
+				goto bad;
+			}
+			error = ipsec4_encapsulate(state->m, isr->sav);
+			splx(s);
+			if (error) {
+				state->m = NULL;
+				goto bad;
+			}
+			ip = mtod(state->m, struct ip *);
+
+			state->ro = &isr->sav->sah->sa_route;
+			state->dst = (struct sockaddr *)&state->ro->ro_dst;
+			dst4 = (struct sockaddr_in *)state->dst;
+			if (state->ro->ro_rt
+			 && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0
+			  || dst4->sin_addr.s_addr != ip->ip_dst.s_addr)) {
+				RTFREE(state->ro->ro_rt);
+				state->ro->ro_rt = NULL;
+			}
+			if (state->ro->ro_rt == 0) {
+				dst4->sin_family = AF_INET;
+				dst4->sin_len = sizeof(*dst4);
+				dst4->sin_addr = ip->ip_dst;
+				rtalloc(state->ro);
+			}
+			if (state->ro->ro_rt == 0) {
+				ipstat.ips_noroute++;
+				error = EHOSTUNREACH;
+				goto bad;
+			}
+
+			/* adjust state->dst if tunnel endpoint is offlink */
+			if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) {
+				state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
+				dst4 = (struct sockaddr_in *)state->dst;
+			}
+
+			state->encap++;
+		} else
+			splx(s);
+
+		state->m = ipsec4_splithdr(state->m);
+		if (!state->m) {
+			error = ENOMEM;
+			goto bad;
+		}
+		switch (isr->saidx.proto) {
+		case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+			if ((error = esp4_output(state->m, isr)) != 0) {
+				state->m = NULL;
+				goto bad;
+			}
+			break;
+#else
+			m_freem(state->m);
+			state->m = NULL;
+			error = EINVAL;
+			goto bad;
+#endif
+		case IPPROTO_AH:
+			if ((error = ah4_output(state->m, isr)) != 0) {
+				state->m = NULL;
+				goto bad;
+			}
+			break;
+		case IPPROTO_IPCOMP:
+			if ((error = ipcomp4_output(state->m, isr)) != 0) {
+				state->m = NULL;
+				goto bad;
+			}
+			break;
+		default:
+			ipseclog((LOG_ERR,
+			    "ipsec4_output: unknown ipsec protocol %d\n",
+			    isr->saidx.proto));
+			m_freem(state->m);
+			state->m = NULL;
+			error = EINVAL;
+			goto bad;
+		}
+
+		if (state->m == 0) {
+			error = ENOMEM;
+			goto bad;
+		}
+		ip = mtod(state->m, struct ip *);
+	}
+
+	return 0;
+
+bad:
+	m_freem(state->m);
+	state->m = NULL;
+	return error;
+}
+#endif
+
+#ifdef INET6
+/*
+ * IPsec output logic for IPv6, transport mode.
+ */
+int
+ipsec6_output_trans(state, nexthdrp, mprev, sp, flags, tun)
+	struct ipsec_output_state *state;
+	u_char *nexthdrp;
+	struct mbuf *mprev;
+	struct secpolicy *sp;
+	int flags;
+	int *tun;
+{
+	struct ip6_hdr *ip6;
+	struct ipsecrequest *isr = NULL;
+	struct secasindex saidx;
+	int error = 0;
+	int plen;
+	struct sockaddr_in6 *sin6;
+
+	if (!state)
+		panic("state == NULL in ipsec6_output_trans");
+	if (!state->m)
+		panic("state->m == NULL in ipsec6_output_trans");
+	if (!nexthdrp)
+		panic("nexthdrp == NULL in ipsec6_output_trans");
+	if (!mprev)
+		panic("mprev == NULL in ipsec6_output_trans");
+	if (!sp)
+		panic("sp == NULL in ipsec6_output_trans");
+	if (!tun)
+		panic("tun == NULL in ipsec6_output_trans");
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec6_output_trans: applyed SP\n");
+		kdebug_secpolicy(sp));
+
+	*tun = 0;
+	for (isr = sp->req; isr; isr = isr->next) {
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+			/* the rest will be handled by ipsec6_output_tunnel() */
+			break;
+		}
+
+		/* make SA index for search proper SA */
+		ip6 = mtod(state->m, struct ip6_hdr *);
+		bcopy(&isr->saidx, &saidx, sizeof(saidx));
+		saidx.mode = isr->saidx.mode;
+		saidx.reqid = isr->saidx.reqid;
+		sin6 = (struct sockaddr_in6 *)&saidx.src;
+		if (sin6->sin6_len == 0) {
+			sin6->sin6_len = sizeof(*sin6);
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_port = IPSEC_PORT_ANY;
+			bcopy(&ip6->ip6_src, &sin6->sin6_addr,
+			    sizeof(ip6->ip6_src));
+			if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+				/* fix scope id for comparing SPD */
+				sin6->sin6_addr.s6_addr16[1] = 0;
+				sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
+			}
+		}
+		sin6 = (struct sockaddr_in6 *)&saidx.dst;
+		if (sin6->sin6_len == 0) {
+			sin6->sin6_len = sizeof(*sin6);
+			sin6->sin6_family = AF_INET6;
+			sin6->sin6_port = IPSEC_PORT_ANY;
+			bcopy(&ip6->ip6_dst, &sin6->sin6_addr,
+			    sizeof(ip6->ip6_dst));
+			if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+				/* fix scope id for comparing SPD */
+				sin6->sin6_addr.s6_addr16[1] = 0;
+				sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
+			}
+		}
+
+		if (key_checkrequest(isr, &saidx) == ENOENT) {
+			/*
+			 * IPsec processing is required, but no SA found.
+			 * I assume that key_acquire() had been called
+			 * to get/establish the SA. Here I discard
+			 * this packet because it is responsibility for
+			 * upper layer to retransmit the packet.
+			 */
+			ipsec6stat.out_nosa++;
+			error = ENOENT;
+
+			/*
+			 * Notify the fact that the packet is discarded
+			 * to ourselves. I believe this is better than
+			 * just silently discarding. (jinmei@kame.net)
+			 * XXX: should we restrict the error to TCP packets?
+			 * XXX: should we directly notify sockets via
+			 *      pfctlinputs?
+			 *
+			 * Noone have initialized rcvif until this point,
+			 * so clear it.
+			 */
+			if ((state->m->m_flags & M_PKTHDR) != 0)
+				state->m->m_pkthdr.rcvif = NULL;
+			icmp6_error(state->m, ICMP6_DST_UNREACH,
+				    ICMP6_DST_UNREACH_ADMIN, 0);
+			state->m = NULL; /* icmp6_error freed the mbuf */
+			goto bad;
+		}
+
+		/* validity check */
+		if (isr->sav == NULL) {
+			switch (ipsec_get_reqlevel(isr)) {
+			case IPSEC_LEVEL_USE:
+				continue;
+			case IPSEC_LEVEL_REQUIRE:
+				/* must be not reached here. */
+				panic("ipsec6_output_trans: no SA found, but required.");
+			}
+		}
+
+		/*
+		 * If there is no valid SA, we give up to process.
+		 * see same place at ipsec4_output().
+		 */
+		if (isr->sav->state != SADB_SASTATE_MATURE
+		 && isr->sav->state != SADB_SASTATE_DYING) {
+			ipsec6stat.out_nosa++;
+			error = EINVAL;
+			goto bad;
+		}
+
+		switch (isr->saidx.proto) {
+		case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+			error = esp6_output(state->m, nexthdrp, mprev->m_next, isr);
+#else
+			m_freem(state->m);
+			error = EINVAL;
+#endif
+			break;
+		case IPPROTO_AH:
+			error = ah6_output(state->m, nexthdrp, mprev->m_next, isr);
+			break;
+		case IPPROTO_IPCOMP:
+			error = ipcomp6_output(state->m, nexthdrp, mprev->m_next, isr);
+			break;
+		default:
+			ipseclog((LOG_ERR, "ipsec6_output_trans: "
+			    "unknown ipsec protocol %d\n", isr->saidx.proto));
+			m_freem(state->m);
+			ipsec6stat.out_inval++;
+			error = EINVAL;
+			break;
+		}
+		if (error) {
+			state->m = NULL;
+			goto bad;
+		}
+		plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr);
+		if (plen > IPV6_MAXPACKET) {
+			ipseclog((LOG_ERR, "ipsec6_output_trans: "
+			    "IPsec with IPv6 jumbogram is not supported\n"));
+			ipsec6stat.out_inval++;
+			error = EINVAL;	/* XXX */
+			goto bad;
+		}
+		ip6 = mtod(state->m, struct ip6_hdr *);
+		ip6->ip6_plen = htons(plen);
+	}
+
+	/* if we have more to go, we need a tunnel mode processing */
+	if (isr != NULL)
+		*tun = 1;
+
+	return 0;
+
+bad:
+	m_freem(state->m);
+	state->m = NULL;
+	return error;
+}
+
+/*
+ * IPsec output logic for IPv6, tunnel mode.
+ */
+int
+ipsec6_output_tunnel(state, sp, flags)
+	struct ipsec_output_state *state;
+	struct secpolicy *sp;
+	int flags;
+{
+	struct ip6_hdr *ip6;
+	struct ipsecrequest *isr = NULL;
+	struct secasindex saidx;
+	int error = 0;
+	int plen;
+	struct sockaddr_in6* dst6;
+	int s;
+
+	if (!state)
+		panic("state == NULL in ipsec6_output_tunnel");
+	if (!state->m)
+		panic("state->m == NULL in ipsec6_output_tunnel");
+	if (!sp)
+		panic("sp == NULL in ipsec6_output_tunnel");
+
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("ipsec6_output_tunnel: applyed SP\n");
+		kdebug_secpolicy(sp));
+
+	/*
+	 * transport mode ipsec (before the 1st tunnel mode) is already
+	 * processed by ipsec6_output_trans().
+	 */
+	for (isr = sp->req; isr; isr = isr->next) {
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL)
+			break;
+	}
+
+	for (/* already initialized */; isr; isr = isr->next) {
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+			/* When tunnel mode, SA peers must be specified. */
+			bcopy(&isr->saidx, &saidx, sizeof(saidx));
+		} else {
+			/* make SA index to look for a proper SA */
+			struct sockaddr_in6 *sin6;
+
+			bzero(&saidx, sizeof(saidx));
+			saidx.proto = isr->saidx.proto;
+			saidx.mode = isr->saidx.mode;
+			saidx.reqid = isr->saidx.reqid;
+
+			ip6 = mtod(state->m, struct ip6_hdr *);
+			sin6 = (struct sockaddr_in6 *)&saidx.src;
+			if (sin6->sin6_len == 0) {
+				sin6->sin6_len = sizeof(*sin6);
+				sin6->sin6_family = AF_INET6;
+				sin6->sin6_port = IPSEC_PORT_ANY;
+				bcopy(&ip6->ip6_src, &sin6->sin6_addr,
+				    sizeof(ip6->ip6_src));
+				if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
+					/* fix scope id for comparing SPD */
+					sin6->sin6_addr.s6_addr16[1] = 0;
+					sin6->sin6_scope_id = ntohs(ip6->ip6_src.s6_addr16[1]);
+				}
+			}
+			sin6 = (struct sockaddr_in6 *)&saidx.dst;
+			if (sin6->sin6_len == 0) {
+				sin6->sin6_len = sizeof(*sin6);
+				sin6->sin6_family = AF_INET6;
+				sin6->sin6_port = IPSEC_PORT_ANY;
+				bcopy(&ip6->ip6_dst, &sin6->sin6_addr,
+				    sizeof(ip6->ip6_dst));
+				if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
+					/* fix scope id for comparing SPD */
+					sin6->sin6_addr.s6_addr16[1] = 0;
+					sin6->sin6_scope_id = ntohs(ip6->ip6_dst.s6_addr16[1]);
+				}
+			}
+		}
+
+		if (key_checkrequest(isr, &saidx) == ENOENT) {
+			/*
+			 * IPsec processing is required, but no SA found.
+			 * I assume that key_acquire() had been called
+			 * to get/establish the SA. Here I discard
+			 * this packet because it is responsibility for
+			 * upper layer to retransmit the packet.
+			 */
+			ipsec6stat.out_nosa++;
+			error = ENOENT;
+			goto bad;
+		}
+
+		/* validity check */
+		if (isr->sav == NULL) {
+			switch (ipsec_get_reqlevel(isr)) {
+			case IPSEC_LEVEL_USE:
+				continue;
+			case IPSEC_LEVEL_REQUIRE:
+				/* must be not reached here. */
+				panic("ipsec6_output_tunnel: no SA found, but required.");
+			}
+		}
+
+		/*
+		 * If there is no valid SA, we give up to process.
+		 * see same place at ipsec4_output().
+		 */
+		if (isr->sav->state != SADB_SASTATE_MATURE
+		 && isr->sav->state != SADB_SASTATE_DYING) {
+			ipsec6stat.out_nosa++;
+			error = EINVAL;
+			goto bad;
+		}
+
+		/*
+		 * There may be the case that SA status will be changed when
+		 * we are refering to one. So calling splsoftnet().
+		 */
+#ifdef __NetBSD__
+		s = splsoftnet();
+#else
+		s = splnet();
+#endif
+
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL) {
+			/*
+			 * build IPsec tunnel.
+			 */
+			/* XXX should be processed with other familiy */
+			if (((struct sockaddr *)&isr->sav->sah->saidx.src)->sa_family != AF_INET6) {
+				ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
+				    "family mismatched between inner and outer, spi=%u\n",
+				    (u_int32_t)ntohl(isr->sav->spi)));
+				splx(s);
+				ipsec6stat.out_inval++;
+				error = EAFNOSUPPORT;
+				goto bad;
+			}
+
+			state->m = ipsec6_splithdr(state->m);
+			if (!state->m) {
+				splx(s);
+				ipsec6stat.out_nomem++;
+				error = ENOMEM;
+				goto bad;
+			}
+			error = ipsec6_encapsulate(state->m, isr->sav);
+			splx(s);
+			if (error) {
+				state->m = 0;
+				goto bad;
+			}
+			ip6 = mtod(state->m, struct ip6_hdr *);
+
+			state->ro = &isr->sav->sah->sa_route;
+			state->dst = (struct sockaddr *)&state->ro->ro_dst;
+			dst6 = (struct sockaddr_in6 *)state->dst;
+			if (state->ro->ro_rt
+			 && ((state->ro->ro_rt->rt_flags & RTF_UP) == 0
+			  || !IN6_ARE_ADDR_EQUAL(&dst6->sin6_addr, &ip6->ip6_dst))) {
+				RTFREE(state->ro->ro_rt);
+				state->ro->ro_rt = NULL;
+			}
+			if (state->ro->ro_rt == 0) {
+				bzero(dst6, sizeof(*dst6));
+				dst6->sin6_family = AF_INET6;
+				dst6->sin6_len = sizeof(*dst6);
+				dst6->sin6_addr = ip6->ip6_dst;
+				rtalloc(state->ro);
+			}
+			if (state->ro->ro_rt == 0) {
+				ip6stat.ip6s_noroute++;
+				ipsec6stat.out_noroute++;
+				error = EHOSTUNREACH;
+				goto bad;
+			}
+
+			/* adjust state->dst if tunnel endpoint is offlink */
+			if (state->ro->ro_rt->rt_flags & RTF_GATEWAY) {
+				state->dst = (struct sockaddr *)state->ro->ro_rt->rt_gateway;
+				dst6 = (struct sockaddr_in6 *)state->dst;
+			}
+		} else
+			splx(s);
+
+		state->m = ipsec6_splithdr(state->m);
+		if (!state->m) {
+			ipsec6stat.out_nomem++;
+			error = ENOMEM;
+			goto bad;
+		}
+		ip6 = mtod(state->m, struct ip6_hdr *);
+		switch (isr->saidx.proto) {
+		case IPPROTO_ESP:
+#ifdef IPSEC_ESP
+			error = esp6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr);
+#else
+			m_freem(state->m);
+			error = EINVAL;
+#endif
+			break;
+		case IPPROTO_AH:
+			error = ah6_output(state->m, &ip6->ip6_nxt, state->m->m_next, isr);
+			break;
+		case IPPROTO_IPCOMP:
+			/* XXX code should be here */
+			/* FALLTHROUGH */
+		default:
+			ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
+			    "unknown ipsec protocol %d\n", isr->saidx.proto));
+			m_freem(state->m);
+			ipsec6stat.out_inval++;
+			error = EINVAL;
+			break;
+		}
+		if (error) {
+			state->m = NULL;
+			goto bad;
+		}
+		plen = state->m->m_pkthdr.len - sizeof(struct ip6_hdr);
+		if (plen > IPV6_MAXPACKET) {
+			ipseclog((LOG_ERR, "ipsec6_output_tunnel: "
+			    "IPsec with IPv6 jumbogram is not supported\n"));
+			ipsec6stat.out_inval++;
+			error = EINVAL;	/* XXX */
+			goto bad;
+		}
+		ip6 = mtod(state->m, struct ip6_hdr *);
+		ip6->ip6_plen = htons(plen);
+	}
+
+	return 0;
+
+bad:
+	m_freem(state->m);
+	state->m = NULL;
+	return error;
+}
+#endif /* INET6 */
+
+#ifdef INET
+/*
+ * Chop IP header and option off from the payload.
+ */
+static struct mbuf *
+ipsec4_splithdr(m)
+	struct mbuf *m;
+{
+	struct mbuf *mh;
+	struct ip *ip;
+	int hlen;
+
+	if (m->m_len < sizeof(struct ip))
+		panic("ipsec4_splithdr: first mbuf too short");
+	ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+	hlen = _IP_VHL_HL(ip->ip_vhl) << 2;
+#else
+	hlen = ip->ip_hl << 2;
+#endif
+	if (m->m_len > hlen) {
+		MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+		if (!mh) {
+			m_freem(m);
+			return NULL;
+		}
+		M_COPY_PKTHDR(mh, m);
+		MH_ALIGN(mh, hlen);
+		m->m_flags &= ~M_PKTHDR;
+		m->m_len -= hlen;
+		m->m_data += hlen;
+		mh->m_next = m;
+		m = mh;
+		m->m_len = hlen;
+		bcopy((caddr_t)ip, mtod(m, caddr_t), hlen);
+	} else if (m->m_len < hlen) {
+		m = m_pullup(m, hlen);
+		if (!m)
+			return NULL;
+	}
+	return m;
+}
+#endif
+
+#ifdef INET6
+static struct mbuf *
+ipsec6_splithdr(m)
+	struct mbuf *m;
+{
+	struct mbuf *mh;
+	struct ip6_hdr *ip6;
+	int hlen;
+
+	if (m->m_len < sizeof(struct ip6_hdr))
+		panic("ipsec6_splithdr: first mbuf too short");
+	ip6 = mtod(m, struct ip6_hdr *);
+	hlen = sizeof(struct ip6_hdr);
+	if (m->m_len > hlen) {
+		MGETHDR(mh, M_DONTWAIT, MT_HEADER);
+		if (!mh) {
+			m_freem(m);
+			return NULL;
+		}
+		M_COPY_PKTHDR(mh, m);
+		MH_ALIGN(mh, hlen);
+		m->m_flags &= ~M_PKTHDR;
+		m->m_len -= hlen;
+		m->m_data += hlen;
+		mh->m_next = m;
+		m = mh;
+		m->m_len = hlen;
+		bcopy((caddr_t)ip6, mtod(m, caddr_t), hlen);
+	} else if (m->m_len < hlen) {
+		m = m_pullup(m, hlen);
+		if (!m)
+			return NULL;
+	}
+	return m;
+}
+#endif
+
+/* validate inbound IPsec tunnel packet. */
+int
+ipsec4_tunnel_validate(m, off, nxt0, sav)
+	struct mbuf *m;		/* no pullup permitted, m->m_len >= ip */
+	int off;
+	u_int nxt0;
+	struct secasvar *sav;
+{
+	u_int8_t nxt = nxt0 & 0xff;
+	struct sockaddr_in *sin;
+	struct sockaddr_in osrc, odst, isrc, idst;
+	int hlen;
+	struct secpolicy *sp;
+	struct ip *oip;
+
+#ifdef DIAGNOSTIC
+	if (m->m_len < sizeof(struct ip))
+		panic("too short mbuf on ipsec4_tunnel_validate");
+#endif
+	if (nxt != IPPROTO_IPV4)
+		return 0;
+	if (m->m_pkthdr.len < off + sizeof(struct ip))
+		return 0;
+	/* do not decapsulate if the SA is for transport mode only */
+	if (sav->sah->saidx.mode == IPSEC_MODE_TRANSPORT)
+		return 0;
+
+	oip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+	hlen = _IP_VHL_HL(oip->ip_vhl) << 2;
+#else
+	hlen = oip->ip_hl << 2;
+#endif
+	if (hlen != sizeof(struct ip))
+		return 0;
+
+	/* AF_INET6 should be supported, but at this moment we don't. */
+	sin = (struct sockaddr_in *)&sav->sah->saidx.dst;
+	if (sin->sin_family != AF_INET)
+		return 0;
+	if (bcmp(&oip->ip_dst, &sin->sin_addr, sizeof(oip->ip_dst)) != 0)
+		return 0;
+
+	/* XXX slow */
+	bzero(&osrc, sizeof(osrc));
+	bzero(&odst, sizeof(odst));
+	bzero(&isrc, sizeof(isrc));
+	bzero(&idst, sizeof(idst));
+	osrc.sin_family = odst.sin_family = isrc.sin_family = idst.sin_family = 
+	    AF_INET;
+	osrc.sin_len = odst.sin_len = isrc.sin_len = idst.sin_len = 
+	    sizeof(struct sockaddr_in);
+	osrc.sin_addr = oip->ip_src;
+	odst.sin_addr = oip->ip_dst;
+	m_copydata(m, off + offsetof(struct ip, ip_src), sizeof(isrc.sin_addr),
+	    (caddr_t)&isrc.sin_addr);
+	m_copydata(m, off + offsetof(struct ip, ip_dst), sizeof(idst.sin_addr),
+	    (caddr_t)&idst.sin_addr);
+
+	/*
+	 * RFC2401 5.2.1 (b): (assume that we are using tunnel mode)
+	 * - if the inner destination is multicast address, there can be
+	 *   multiple permissible inner source address.  implementation
+	 *   may want to skip verification of inner source address against
+	 *   SPD selector.
+	 * - if the inner protocol is ICMP, the packet may be an error report
+	 *   from routers on the other side of the VPN cloud (R in the
+	 *   following diagram).  in this case, we cannot verify inner source
+	 *   address against SPD selector.
+	 *	me -- gw === gw -- R -- you
+	 *
+	 * we consider the first bullet to be users responsibility on SPD entry
+	 * configuration (if you need to encrypt multicast traffic, set
+	 * the source range of SPD selector to 0.0.0.0/0, or have explicit
+	 * address ranges for possible senders).
+	 * the second bullet is not taken care of (yet).
+	 *
+	 * therefore, we do not do anything special about inner source.
+	 */
+
+	sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
+	    (struct sockaddr *)&isrc, (struct sockaddr *)&idst);
+	/*
+	 * when there is no suitable inbound policy for the packet of the ipsec
+	 * tunnel mode, the kernel never decapsulate the tunneled packet
+	 * as the ipsec tunnel mode even when the system wide policy is "none".
+	 * then the kernel leaves the generic tunnel module to process this
+	 * packet.  if there is no rule of the generic tunnel, the packet
+	 * is rejected and the statistics will be counted up.
+	 */
+	if (!sp)
+		return 0;
+	key_freesp(sp);
+
+	return 1;
+}
+
+#ifdef INET6
+/* validate inbound IPsec tunnel packet. */
+int
+ipsec6_tunnel_validate(m, off, nxt0, sav)
+	struct mbuf *m;		/* no pullup permitted, m->m_len >= ip */
+	int off;
+	u_int nxt0;
+	struct secasvar *sav;
+{
+	u_int8_t nxt = nxt0 & 0xff;
+	struct sockaddr_in6 *sin6;
+	struct sockaddr_in6 osrc, odst, isrc, idst;
+	struct secpolicy *sp;
+	struct ip6_hdr *oip6;
+
+#ifdef DIAGNOSTIC
+	if (m->m_len < sizeof(struct ip6_hdr))
+		panic("too short mbuf on ipsec6_tunnel_validate");
+#endif
+	if (nxt != IPPROTO_IPV6)
+		return 0;
+	if (m->m_pkthdr.len < off + sizeof(struct ip6_hdr))
+		return 0;
+	/* do not decapsulate if the SA is for transport mode only */
+	if (sav->sah->saidx.mode == IPSEC_MODE_TRANSPORT)
+		return 0;
+
+	oip6 = mtod(m, struct ip6_hdr *);
+	/* AF_INET should be supported, but at this moment we don't. */
+	sin6 = (struct sockaddr_in6 *)&sav->sah->saidx.dst;
+	if (sin6->sin6_family != AF_INET6)
+		return 0;
+	if (!IN6_ARE_ADDR_EQUAL(&oip6->ip6_dst, &sin6->sin6_addr))
+		return 0;
+
+	/* XXX slow */
+	bzero(&osrc, sizeof(osrc));
+	bzero(&odst, sizeof(odst));
+	bzero(&isrc, sizeof(isrc));
+	bzero(&idst, sizeof(idst));
+	osrc.sin6_family = odst.sin6_family = isrc.sin6_family =
+	    idst.sin6_family = AF_INET6;
+	osrc.sin6_len = odst.sin6_len = isrc.sin6_len = idst.sin6_len = 
+	    sizeof(struct sockaddr_in6);
+	osrc.sin6_addr = oip6->ip6_src;
+	odst.sin6_addr = oip6->ip6_dst;
+	m_copydata(m, off + offsetof(struct ip6_hdr, ip6_src),
+	    sizeof(isrc.sin6_addr), (caddr_t)&isrc.sin6_addr);
+	m_copydata(m, off + offsetof(struct ip6_hdr, ip6_dst),
+	    sizeof(idst.sin6_addr), (caddr_t)&idst.sin6_addr);
+
+	/*
+	 * regarding to inner source address validation, see a long comment
+	 * in ipsec4_tunnel_validate.
+	 */
+
+	sp = key_gettunnel((struct sockaddr *)&osrc, (struct sockaddr *)&odst,
+	    (struct sockaddr *)&isrc, (struct sockaddr *)&idst);
+	if (!sp)
+		return 0;
+	key_freesp(sp);
+
+	return 1;
+}
+#endif
+
+/*
+ * Make a mbuf chain for encryption.
+ * If the original mbuf chain contains a mbuf with a cluster,
+ * allocate a new cluster and copy the data to the new cluster.
+ * XXX: this hack is inefficient, but is necessary to handle cases
+ * of TCP retransmission...
+ */
+struct mbuf *
+ipsec_copypkt(m)
+	struct mbuf *m;
+{
+	struct mbuf *n, **mpp, *mnew;
+
+	for (n = m, mpp = &m; n; n = n->m_next) {
+		if (n->m_flags & M_EXT) {
+			/*
+			 * Make a copy only if there is more than one
+			 * references to the cluster.
+			 * XXX: is this approach effective?
+			 */
+			if (
+#ifdef __bsdi__
+				n->m_ext.ext_func ||
+#else
+				n->m_ext.ext_free ||
+#endif
+#ifdef __NetBSD__
+				MCLISREFERENCED(n)
+#else
+				mclrefcnt[mtocl(n->m_ext.ext_buf)] > 1
+#endif
+			    )
+			{
+				int remain, copied;
+				struct mbuf *mm;
+
+				if (n->m_flags & M_PKTHDR) {
+					MGETHDR(mnew, M_DONTWAIT, MT_HEADER);
+					if (mnew == NULL)
+						goto fail;
+					mnew->m_pkthdr = n->m_pkthdr;
+#if 0
+					if (n->m_pkthdr.aux) {
+						mnew->m_pkthdr.aux =
+						    m_copym(n->m_pkthdr.aux,
+						    0, M_COPYALL, M_DONTWAIT);
+					}
+#endif
+					M_COPY_PKTHDR(mnew, n);
+					mnew->m_flags = n->m_flags & M_COPYFLAGS;
+				}
+				else {
+					MGET(mnew, M_DONTWAIT, MT_DATA);
+					if (mnew == NULL)
+						goto fail;
+				}
+				mnew->m_len = 0;
+				mm = mnew;
+
+				/*
+				 * Copy data. If we don't have enough space to
+				 * store the whole data, allocate a cluster
+				 * or additional mbufs.
+				 * XXX: we don't use m_copyback(), since the
+				 * function does not use clusters and thus is
+				 * inefficient.
+				 */
+				remain = n->m_len;
+				copied = 0;
+				while (1) {
+					int len;
+					struct mbuf *mn;
+
+					if (remain <= (mm->m_flags & M_PKTHDR ? MHLEN : MLEN))
+						len = remain;
+					else { /* allocate a cluster */
+						MCLGET(mm, M_DONTWAIT);
+						if (!(mm->m_flags & M_EXT)) {
+							m_free(mm);
+							goto fail;
+						}
+						len = remain < MCLBYTES ?
+							remain : MCLBYTES;
+					}
+
+					bcopy(n->m_data + copied, mm->m_data,
+					      len);
+
+					copied += len;
+					remain -= len;
+					mm->m_len = len;
+
+					if (remain <= 0) /* completed? */
+						break;
+
+					/* need another mbuf */
+					MGETHDR(mn, M_DONTWAIT, MT_HEADER);
+					if (mn == NULL)
+						goto fail;
+					mn->m_pkthdr.rcvif = NULL;
+					mm->m_next = mn;
+					mm = mn;
+				}
+
+				/* adjust chain */
+				mm->m_next = m_free(n);
+				n = mm;
+				*mpp = mnew;
+				mpp = &n->m_next;
+
+				continue;
+			}
+		}
+		*mpp = n;
+		mpp = &n->m_next;
+	}
+
+	return(m);
+  fail:
+	m_freem(m);
+	return(NULL);
+}
+
+static struct mbuf *
+ipsec_addaux(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
+	if (!n)
+		n = m_aux_add(m, AF_INET, IPPROTO_ESP);
+	if (!n)
+		return n;	/* ENOBUFS */
+	n->m_len = sizeof(struct ipsecaux);
+	bzero(mtod(n, void *), n->m_len);
+	return n;
+}
+
+static struct mbuf *
+ipsec_findaux(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
+#ifdef DIAGNOSTIC
+	if (n && n->m_len < sizeof(struct socket *))
+		panic("invalid ipsec m_aux");
+#endif
+	return n;
+}
+
+void
+ipsec_delaux(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = m_aux_find(m, AF_INET, IPPROTO_ESP);
+	if (n)
+		m_aux_delete(m, n);
+}
+
+/* if the aux buffer is unnecessary, nuke it. */
+static void
+ipsec_optaux(m, n)
+	struct mbuf *m;
+	struct mbuf *n;
+{
+	struct ipsecaux *aux;
+
+	if (!n)
+		return;
+	if (sizeof(*aux) > n->m_len) {
+		ipsec_delaux(m);
+		return;
+	}
+	aux = mtod(n, struct ipsecaux *);
+	if (!aux->so && !aux->sp)
+		ipsec_delaux(m);
+}
+
+int
+ipsec_setsocket(m, so)
+	struct mbuf *m;
+	struct socket *so;
+{
+	struct mbuf *n;
+	struct ipsecaux *aux;
+
+	/* if so == NULL, don't insist on getting the aux mbuf */
+	if (so) {
+		n = ipsec_addaux(m);
+		if (!n)
+			return ENOBUFS;
+	} else
+		n = ipsec_findaux(m);
+	if (n && n->m_len >= sizeof(*aux)) {
+		aux = mtod(n, struct ipsecaux *);
+		aux->so = so;
+	}
+	ipsec_optaux(m, n);
+	return 0;
+}
+
+struct socket *
+ipsec_getsocket(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+	struct ipsecaux *aux;
+
+	n = ipsec_findaux(m);
+	if (n && n->m_len >= sizeof(*aux)) {
+		aux = mtod(n, struct ipsecaux *);
+		return aux->so;
+	} else
+		return NULL;
+}
+
+int
+ipsec_addhist(m, proto, spi)
+	struct mbuf *m;
+	int proto;
+	u_int32_t spi;
+{
+	struct mbuf *n;
+	struct ipsecaux *aux;
+
+	n = ipsec_addaux(m);
+	if (!n)
+		return ENOBUFS;
+	if (sizeof(*aux) > n->m_len)
+		return ENOSPC;	/* XXX */
+	aux = mtod(n, struct ipsecaux *);
+	aux->hdrs++;
+	return 0;
+}
+
+int
+ipsec_getnhist(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+	struct ipsecaux *aux;
+
+	n = ipsec_findaux(m);
+	if (!n)
+		return 0;
+	if (sizeof(*aux) > n->m_len)
+		return 0;
+	aux = mtod(n, struct ipsecaux *);
+	return aux->hdrs;
+}
+
+struct ipsec_history *
+ipsec_gethist(m, lenp)
+	struct mbuf *m;
+	int *lenp;
+{
+
+	panic("ipsec_gethist: obsolete API");
+        return NULL;
+}
+
+void
+ipsec_clearhist(m)
+	struct mbuf *m;
+{
+	struct mbuf *n;
+
+	n = ipsec_findaux(m);
+	if ((n) && n->m_len > sizeof(struct ipsecaux))
+		n->m_len = sizeof(struct ipsecaux); 
+	ipsec_optaux(m, n);
+}
+
+#ifdef __bsdi__
+/*
+ * System control for IP
+ */
+u_char	ipsecctlerrmap[PRC_NCMDS] = {
+	0,		0,		0,		0,
+	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
+	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
+	EMSGSIZE,	EHOSTUNREACH,	0,		0,
+	0,		0,		0,		0,
+	ENOPROTOOPT
+};
+
+int *ipsec_sysvars[] = IPSECCTL_VARS;
+
+int
+ipsec_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int	*name;
+	u_int	namelen;
+	void	*oldp;
+	size_t	*oldlenp;
+	void	*newp;
+	size_t	newlen;
+{
+	if (name[0] >= IPSECCTL_MAXID)
+		return (EOPNOTSUPP);
+
+	switch (name[0]) {
+	case IPSECCTL_STATS:
+		return sysctl_rdtrunc(oldp, oldlenp, newp, &ipsecstat,
+		    sizeof(ipsecstat));
+	case IPSECCTL_DEF_POLICY:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_POLICY_DISCARD:
+			case IPSEC_POLICY_NONE:
+				break;
+			default:
+				return EINVAL;
+			}
+			ipsec_invalpcbcacheall();
+		}
+		return (sysctl_int_arr(ipsec_sysvars, name, namelen,
+		    oldp, oldlenp, newp, newlen));
+	case IPSECCTL_DEF_ESP_TRANSLEV:
+	case IPSECCTL_DEF_ESP_NETLEV:
+	case IPSECCTL_DEF_AH_TRANSLEV:	
+	case IPSECCTL_DEF_AH_NETLEV:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_LEVEL_USE:
+			case IPSEC_LEVEL_REQUIRE:
+				break;
+			default:
+				return EINVAL;
+			}
+			ipsec_invalpcbcacheall();
+		}
+		return (sysctl_int_arr(ipsec_sysvars, name, namelen,
+		    oldp, oldlenp, newp, newlen));
+	default:
+		return (sysctl_int_arr(ipsec_sysvars, name, namelen,
+		    oldp, oldlenp, newp, newlen));
+	}
+}
+
+#ifdef INET6
+/*
+ * System control for IP6
+ */
+u_char	ipsec6ctlerrmap[PRC_NCMDS] = {
+	0,		0,		0,		0,
+	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
+	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
+	EMSGSIZE,	EHOSTUNREACH,	0,		0,
+	0,		0,		0,		0,
+	ENOPROTOOPT
+};
+
+int *ipsec6_sysvars[] = IPSEC6CTL_VARS;
+
+int
+ipsec6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int	*name;
+	u_int	namelen;
+	void	*oldp;
+	size_t	*oldlenp;
+	void	*newp;
+	size_t	newlen;
+{
+	if (name[0] >= IPSECCTL_MAXID)	/* xxx no 6 in this definition */
+		return (EOPNOTSUPP);
+
+	switch (name[0]) {
+	case IPSECCTL_STATS:	/* xxx no 6 in this definition */
+		return sysctl_rdtrunc(oldp, oldlenp, newp, &ipsec6stat,
+		    sizeof(ipsec6stat));
+	case IPSECCTL_DEF_POLICY:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_POLICY_DISCARD:
+			case IPSEC_POLICY_NONE:
+				break;
+			default:
+				return EINVAL;
+			}
+			ipsec_invalpcbcacheall();
+		}
+		return (sysctl_int_arr(ipsec6_sysvars, name, namelen,
+		    oldp, oldlenp, newp, newlen));
+	case IPSECCTL_DEF_ESP_TRANSLEV:
+	case IPSECCTL_DEF_ESP_NETLEV:
+	case IPSECCTL_DEF_AH_TRANSLEV:	
+	case IPSECCTL_DEF_AH_NETLEV:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_LEVEL_USE:
+			case IPSEC_LEVEL_REQUIRE:
+				break;
+			default:
+				return EINVAL;
+			}
+			ipsec_invalpcbcacheall();
+		}
+		return (sysctl_int_arr(ipsec6_sysvars, name, namelen,
+		    oldp, oldlenp, newp, newlen));
+	default:
+		return (sysctl_int_arr(ipsec6_sysvars, name, namelen,
+		    oldp, oldlenp, newp, newlen));
+	}
+}
+#endif /* INET6 */
+#endif /* __bsdi__ */
+
+#ifdef __NetBSD__
+/*
+ * System control for IPSEC
+ */
+u_char	ipsecctlermap[PRC_NCMDS] = {
+	0,		0,		0,		0,
+	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
+	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
+	EMSGSIZE,	EHOSTUNREACH,	0,		0,
+	0,		0,		0,		0,
+	ENOPROTOOPT
+};
+
+int
+ipsec_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int *name;
+	u_int namelen;
+	void *oldp;
+	size_t *oldlenp;
+	void *newp;
+	size_t newlen;
+{
+	/* All sysctl names at this level are terminal. */
+	if (namelen != 1)
+		return ENOTDIR;
+
+	/* common sanity checks */
+	switch (name[0]) {
+	case IPSECCTL_DEF_ESP_TRANSLEV:
+	case IPSECCTL_DEF_ESP_NETLEV:
+	case IPSECCTL_DEF_AH_TRANSLEV:
+	case IPSECCTL_DEF_AH_NETLEV:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_LEVEL_USE:
+			case IPSEC_LEVEL_REQUIRE:
+				break;
+			default:
+				return EINVAL;
+			}
+		}
+	}
+
+	switch (name[0]) {
+
+	case IPSECCTL_STATS:
+		return sysctl_struct(oldp, oldlenp, newp, newlen,
+				     &ipsecstat, sizeof(ipsecstat));
+	case IPSECCTL_DEF_POLICY:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_POLICY_DISCARD:
+			case IPSEC_POLICY_NONE:
+				break;
+			default:
+				return EINVAL;
+			}
+			ipsec_invalpcbcacheall();
+		}
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_def_policy.policy);
+	case IPSECCTL_DEF_ESP_TRANSLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_esp_trans_deflev);
+	case IPSECCTL_DEF_ESP_NETLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_esp_net_deflev);
+	case IPSECCTL_DEF_AH_TRANSLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_ah_trans_deflev);
+	case IPSECCTL_DEF_AH_NETLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_ah_net_deflev);
+	case IPSECCTL_AH_CLEARTOS:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_ah_cleartos);
+	case IPSECCTL_AH_OFFSETMASK:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_ah_offsetmask);
+	case IPSECCTL_DFBIT:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip4_ipsec_dfbit);
+	case IPSECCTL_ECN:
+		return sysctl_int(oldp, oldlenp, newp, newlen, &ip4_ipsec_ecn);
+	case IPSECCTL_DEBUG:
+		return sysctl_int(oldp, oldlenp, newp, newlen, &ipsec_debug);
+	case IPSECCTL_ESP_RANDPAD:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &ip4_esp_randpad);
+	default:
+		return EOPNOTSUPP;
+	}
+	/* NOTREACHED */
+}
+
+#ifdef INET6
+/*
+ * System control for IPSEC6
+ */
+u_char	ipsec6ctlermap[PRC_NCMDS] = {
+	0,		0,		0,		0,
+	0,		EMSGSIZE,	EHOSTDOWN,	EHOSTUNREACH,
+	EHOSTUNREACH,	EHOSTUNREACH,	ECONNREFUSED,	ECONNREFUSED,
+	EMSGSIZE,	EHOSTUNREACH,	0,		0,
+	0,		0,		0,		0,
+	ENOPROTOOPT
+};
+
+int
+ipsec6_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int *name;
+	u_int namelen;
+	void *oldp;
+	size_t *oldlenp;
+	void *newp;
+	size_t newlen;
+{
+	/* All sysctl names at this level are terminal. */
+	if (namelen != 1)
+		return ENOTDIR;
+
+	/* common sanity checks */
+	switch (name[0]) {
+	case IPSECCTL_DEF_ESP_TRANSLEV:
+	case IPSECCTL_DEF_ESP_NETLEV:
+	case IPSECCTL_DEF_AH_TRANSLEV:
+	case IPSECCTL_DEF_AH_NETLEV:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_LEVEL_USE:
+			case IPSEC_LEVEL_REQUIRE:
+				break;
+			default:
+				return EINVAL;
+			}
+		}
+	}
+
+	switch (name[0]) {
+
+	case IPSECCTL_STATS:
+		return sysctl_struct(oldp, oldlenp, newp, newlen,
+				     &ipsec6stat, sizeof(ipsec6stat));
+	case IPSECCTL_DEF_POLICY:
+		if (newp != NULL && newlen == sizeof(int)) {
+			switch (*(int *)newp) {
+			case IPSEC_POLICY_DISCARD:
+			case IPSEC_POLICY_NONE:
+				break;
+			default:
+				return EINVAL;
+			}
+			ipsec_invalpcbcacheall();
+		}
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip6_def_policy.policy);
+	case IPSECCTL_DEF_ESP_TRANSLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip6_esp_trans_deflev);
+	case IPSECCTL_DEF_ESP_NETLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip6_esp_net_deflev);
+	case IPSECCTL_DEF_AH_TRANSLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip6_ah_trans_deflev);
+	case IPSECCTL_DEF_AH_NETLEV:
+		if (newp != NULL)
+			ipsec_invalpcbcacheall();
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+				  &ip6_ah_net_deflev);
+	case IPSECCTL_ECN:
+		return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_ipsec_ecn);
+	case IPSECCTL_DEBUG:
+		return sysctl_int(oldp, oldlenp, newp, newlen, &ipsec_debug);
+	case IPSECCTL_ESP_RANDPAD:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &ip6_esp_randpad);
+	default:
+		return EOPNOTSUPP;
+	}
+	/* NOTREACHED */
+}
+#endif /* INET6 */
+
+#endif /* __NetBSD__ */
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netkey/key.c
@@ -0,0 +1,7776 @@
+//==========================================================================
+//
+//      src/sys/netkey/key.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: key.c,v 1.221 2001/11/06 05:14:30 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This code is referd to RFC 2367
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef __NetBSD__
+#include <sys/callout.h>
+#endif
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/malloc.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+#include <sys/errno.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/raw_cb.h>
+#include <net/if_sec.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_var.h>
+
+#ifdef INET6
+#include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6_var.h>
+#endif /* INET6 */
+
+#ifdef INET
+#include <netinet/in_pcb.h>
+#endif
+#ifdef INET6
+#if !(defined(__bsdi__) && _BSDI_VERSION >= 199802)
+#include <netinet6/in6_pcb.h>
+#endif
+#endif /* INET6 */
+
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key.h>
+#include <netkey/keysock.h>
+#include <netkey/key_debug.h>
+
+#include <netinet6/ipsec.h>
+#include <netinet6/ah.h>
+#ifdef IPSEC_ESP
+#include <netinet6/esp.h>
+#endif
+#include <netinet6/ipcomp.h>
+
+#ifndef offsetof
+#define offsetof(type, member)	((size_t)(&((type *)0)->member))
+#endif
+#ifndef satosin
+#define satosin(s) ((struct sockaddr_in *)s)
+#endif
+
+#define FULLMASK	0xff
+
+/*
+ * Note on SA reference counting:
+ * - SAs that are not in DEAD state will have (total external reference + 1)
+ *   following value in reference count field.  they cannot be freed and are
+ *   referenced from SA header.
+ * - SAs that are in DEAD state will have (total external reference)
+ *   in reference count field.  they are ready to be freed.  reference from
+ *   SA header will be removed in key_delsav(), when the reference count
+ *   field hits 0 (= no external reference other than from SA header.
+ */
+
+u_int32_t key_debug_level = 0;
+static u_int key_spi_trycnt = 1000;
+static u_int32_t key_spi_minval = 0x100;
+static u_int32_t key_spi_maxval = 0x0fffffff;	/* XXX */
+static u_int32_t policy_id = 0;
+static u_int key_int_random = 60;	/*interval to initialize randseed,1(m)*/
+static u_int key_larval_lifetime = 30;	/* interval to expire acquiring, 30(s)*/
+static int key_blockacq_count = 10;	/* counter for blocking SADB_ACQUIRE.*/
+static int key_blockacq_lifetime = 20;	/* lifetime for blocking SADB_ACQUIRE.*/
+static int key_preferred_oldsa = 1;	/*preferred old sa rather than new sa.*/
+
+static u_int32_t acq_seq = 0;
+static int key_tick_init_random = 0;
+
+static LIST_HEAD(_sptree, secpolicy) sptree[IPSEC_DIR_MAX];	/* SPD */
+static LIST_HEAD(_sahtree, secashead) sahtree;			/* SAD */
+static LIST_HEAD(_regtree, secreg) regtree[SADB_SATYPE_MAX + 1];
+							/* registed list */
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+static LIST_HEAD(_acqtree, secacq) acqtree;		/* acquiring list */
+#endif
+static LIST_HEAD(_spacqtree, secspacq) spacqtree;	/* SP acquiring list */
+
+struct key_cb key_cb;
+
+/* search order for SAs */
+static u_int saorder_state_valid[] = {
+	SADB_SASTATE_DYING, SADB_SASTATE_MATURE,
+	/*
+	 * This order is important because we must select a oldest SA
+	 * for outbound processing.  For inbound, This is not important.
+	 */
+};
+static u_int saorder_state_alive[] = {
+	/* except DEAD */
+	SADB_SASTATE_MATURE, SADB_SASTATE_DYING, SADB_SASTATE_LARVAL
+};
+static u_int saorder_state_any[] = {
+	SADB_SASTATE_MATURE, SADB_SASTATE_DYING,
+	SADB_SASTATE_LARVAL, SADB_SASTATE_DEAD
+};
+
+static const int minsize[] = {
+	sizeof(struct sadb_msg),	/* SADB_EXT_RESERVED */
+	sizeof(struct sadb_sa),		/* SADB_EXT_SA */
+	sizeof(struct sadb_lifetime),	/* SADB_EXT_LIFETIME_CURRENT */
+	sizeof(struct sadb_lifetime),	/* SADB_EXT_LIFETIME_HARD */
+	sizeof(struct sadb_lifetime),	/* SADB_EXT_LIFETIME_SOFT */
+	sizeof(struct sadb_address),	/* SADB_EXT_ADDRESS_SRC */
+	sizeof(struct sadb_address),	/* SADB_EXT_ADDRESS_DST */
+	sizeof(struct sadb_address),	/* SADB_EXT_ADDRESS_PROXY */
+	sizeof(struct sadb_key),	/* SADB_EXT_KEY_AUTH */
+	sizeof(struct sadb_key),	/* SADB_EXT_KEY_ENCRYPT */
+	sizeof(struct sadb_ident),	/* SADB_EXT_IDENTITY_SRC */
+	sizeof(struct sadb_ident),	/* SADB_EXT_IDENTITY_DST */
+	sizeof(struct sadb_sens),	/* SADB_EXT_SENSITIVITY */
+	sizeof(struct sadb_prop),	/* SADB_EXT_PROPOSAL */
+	sizeof(struct sadb_supported),	/* SADB_EXT_SUPPORTED_AUTH */
+	sizeof(struct sadb_supported),	/* SADB_EXT_SUPPORTED_ENCRYPT */
+	sizeof(struct sadb_spirange),	/* SADB_EXT_SPIRANGE */
+	0,				/* SADB_X_EXT_KMPRIVATE */
+	sizeof(struct sadb_x_policy),	/* SADB_X_EXT_POLICY */
+	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+};
+static const int maxsize[] = {
+	sizeof(struct sadb_msg),	/* SADB_EXT_RESERVED */
+	sizeof(struct sadb_sa),		/* SADB_EXT_SA */
+	sizeof(struct sadb_lifetime),	/* SADB_EXT_LIFETIME_CURRENT */
+	sizeof(struct sadb_lifetime),	/* SADB_EXT_LIFETIME_HARD */
+	sizeof(struct sadb_lifetime),	/* SADB_EXT_LIFETIME_SOFT */
+	0,				/* SADB_EXT_ADDRESS_SRC */
+	0,				/* SADB_EXT_ADDRESS_DST */
+	0,				/* SADB_EXT_ADDRESS_PROXY */
+	0,				/* SADB_EXT_KEY_AUTH */
+	0,				/* SADB_EXT_KEY_ENCRYPT */
+	0,				/* SADB_EXT_IDENTITY_SRC */
+	0,				/* SADB_EXT_IDENTITY_DST */
+	0,				/* SADB_EXT_SENSITIVITY */
+	0,				/* SADB_EXT_PROPOSAL */
+	0,				/* SADB_EXT_SUPPORTED_AUTH */
+	0,				/* SADB_EXT_SUPPORTED_ENCRYPT */
+	sizeof(struct sadb_spirange),	/* SADB_EXT_SPIRANGE */
+	0,				/* SADB_X_EXT_KMPRIVATE */
+	0,				/* SADB_X_EXT_POLICY */
+	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+};
+
+static int ipsec_esp_keymin = 256;
+static int ipsec_esp_auth = 0;
+static int ipsec_ah_keymin = 128;
+
+#ifdef __FreeBSD__
+#ifdef SYSCTL_DECL
+SYSCTL_DECL(_net_key);
+#endif
+
+SYSCTL_INT(_net_key, KEYCTL_DEBUG_LEVEL,	debug,	CTLFLAG_RW, \
+	&key_debug_level,	0,	"");
+
+/* max count of trial for the decision of spi value */
+SYSCTL_INT(_net_key, KEYCTL_SPI_TRY,		spi_trycnt,	CTLFLAG_RW, \
+	&key_spi_trycnt,	0,	"");
+
+/* minimum spi value to allocate automatically. */
+SYSCTL_INT(_net_key, KEYCTL_SPI_MIN_VALUE,	spi_minval,	CTLFLAG_RW, \
+	&key_spi_minval,	0,	"");
+
+/* maximun spi value to allocate automatically. */
+SYSCTL_INT(_net_key, KEYCTL_SPI_MAX_VALUE,	spi_maxval,	CTLFLAG_RW, \
+	&key_spi_maxval,	0,	"");
+
+/* interval to initialize randseed */
+SYSCTL_INT(_net_key, KEYCTL_RANDOM_INT,	int_random,	CTLFLAG_RW, \
+	&key_int_random,	0,	"");
+
+/* lifetime for larval SA */
+SYSCTL_INT(_net_key, KEYCTL_LARVAL_LIFETIME,	larval_lifetime, CTLFLAG_RW, \
+	&key_larval_lifetime,	0,	"");
+
+/* counter for blocking to send SADB_ACQUIRE to IKEd */
+SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_COUNT,	blockacq_count,	CTLFLAG_RW, \
+	&key_blockacq_count,	0,	"");
+
+/* lifetime for blocking to send SADB_ACQUIRE to IKEd */
+SYSCTL_INT(_net_key, KEYCTL_BLOCKACQ_LIFETIME,	blockacq_lifetime, CTLFLAG_RW, \
+	&key_blockacq_lifetime,	0,	"");
+
+/* minimum ESP key length */
+SYSCTL_INT(_net_key, KEYCTL_ESP_KEYMIN,	esp_keymin, CTLFLAG_RW, \
+	&ipsec_esp_keymin,	0,	"");
+
+/* minimum AH key length */
+SYSCTL_INT(_net_key, KEYCTL_AH_KEYMIN,	ah_keymin, CTLFLAG_RW, \
+	&ipsec_ah_keymin,	0,	"");
+
+/* perfered old SA rather than new SA */
+SYSCTL_INT(_net_key, KEYCTL_PREFERED_OLDSA,	preferred_oldsa, CTLFLAG_RW,\
+	&key_preferred_oldsa,	0,	"");
+#endif /* __FreeBSD__ */
+
+#ifndef LIST_FOREACH
+#define LIST_FOREACH(elm, head, field)                                     \
+	for (elm = LIST_FIRST(head); elm; elm = LIST_NEXT(elm, field))
+#endif
+#define __LIST_CHAINED(elm) \
+	(!((elm)->chain.le_next == NULL && (elm)->chain.le_prev == NULL))
+#define LIST_INSERT_TAIL(head, elm, type, field) \
+do {\
+	struct type *curelm = LIST_FIRST(head); \
+	if (curelm == NULL) {\
+		LIST_INSERT_HEAD(head, elm, field); \
+	} else { \
+		while (LIST_NEXT(curelm, field)) \
+			curelm = LIST_NEXT(curelm, field);\
+		LIST_INSERT_AFTER(curelm, elm, field);\
+	}\
+} while (0)
+
+#define KEY_CHKSASTATE(head, sav, name) \
+do { \
+	if ((head) != (sav)) {						\
+		ipseclog((LOG_DEBUG, "%s: state mismatched (TREE=%d SA=%d)\n", \
+			(name), (head), (sav)));			\
+		continue;						\
+	}								\
+} while (0)
+
+#define KEY_CHKSPDIR(head, sp, name) \
+do { \
+	if ((head) != (sp)) {						\
+		ipseclog((LOG_DEBUG, "%s: direction mismatched (TREE=%d SP=%d), " \
+			"anyway continue.\n",				\
+			(name), (head), (sp)));				\
+	}								\
+} while (0)
+
+#if 1
+#define KMALLOC(p, t, n)                                                     \
+	((p) = (t) malloc((unsigned long)(n), M_SECA, M_NOWAIT))
+#define KFREE(p)                                                             \
+	free((caddr_t)(p), M_SECA)
+#else
+#define KMALLOC(p, t, n) \
+do { \
+	((p) = (t)malloc((unsigned long)(n), M_SECA, M_NOWAIT));             \
+	printf("%s %d: %p <- KMALLOC(%s, %d)\n",                             \
+		__FILE__, __LINE__, (p), #t, n);                             \
+} while (0)
+
+#define KFREE(p)                                                             \
+	do {                                                                 \
+		printf("%s %d: %p -> KFREE()\n", __FILE__, __LINE__, (p));   \
+		free((caddr_t)(p), M_SECA);                                  \
+	} while (0)
+#endif
+
+/*
+ * set parameters into secpolicyindex buffer.
+ * Must allocate secpolicyindex buffer passed to this function.
+ */
+#define KEY_SETSECSPIDX(_dir, s, d, ps, pd, ulp, idx) \
+do { \
+	bzero((idx), sizeof(struct secpolicyindex));                             \
+	(idx)->dir = (_dir);                                                 \
+	(idx)->prefs = (ps);                                                 \
+	(idx)->prefd = (pd);                                                 \
+	(idx)->ul_proto = (ulp);                                             \
+	bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len);           \
+	bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len);           \
+} while (0)
+
+/*
+ * set parameters into secasindex buffer.
+ * Must allocate secasindex buffer before calling this function.
+ */
+#define KEY_SETSECASIDX(p, m, r, s, d, idx) \
+do { \
+	bzero((idx), sizeof(struct secasindex));                             \
+	(idx)->proto = (p);                                                  \
+	(idx)->mode = (m);                                                   \
+	(idx)->reqid = (r);                                                  \
+	bcopy((s), &(idx)->src, ((struct sockaddr *)(s))->sa_len);           \
+	bcopy((d), &(idx)->dst, ((struct sockaddr *)(d))->sa_len);           \
+} while (0)
+
+/* key statistics */
+struct _keystat {
+	u_long getspi_count; /* the avarage of count to try to get new SPI */
+} keystat;
+
+struct sadb_msghdr {
+	struct sadb_msg *msg;
+	struct sadb_ext *ext[SADB_EXT_MAX + 1];
+	int extoff[SADB_EXT_MAX + 1];
+	int extlen[SADB_EXT_MAX + 1];
+};
+
+static struct secasvar *key_allocsa_policy __P((struct secasindex *));
+static void key_freesp_so __P((struct secpolicy **));
+static struct secasvar *key_do_allocsa_policy __P((struct secashead *, u_int));
+static void key_delsp __P((struct secpolicy *));
+static struct secpolicy *key_getsp __P((struct secpolicyindex *));
+static struct secpolicy *key_getspbyid __P((u_int32_t));
+static u_int32_t key_newreqid __P((void));
+static struct mbuf *key_gather_mbuf __P((struct mbuf *,
+	const struct sadb_msghdr *, int, int, ...));
+static int key_spdadd __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static u_int32_t key_getnewspid __P((void));
+static int key_spddelete __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_spddelete2 __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_spdget __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_spdflush __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_spddump __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static struct mbuf *key_setdumpsp __P((struct secpolicy *,
+	u_int8_t, u_int32_t, u_int32_t));
+static u_int key_getspreqmsglen __P((struct secpolicy *));
+static int key_spdexpire __P((struct secpolicy *));
+static struct secashead *key_newsah __P((struct secasindex *));
+static void key_delsah __P((struct secashead *));
+static struct secasvar *key_newsav __P((struct mbuf *,
+	const struct sadb_msghdr *, struct secashead *, int *));
+static void key_delsav __P((struct secasvar *));
+static struct secashead *key_getsah __P((struct secasindex *));
+static struct secasvar *key_checkspidup __P((struct secasindex *, u_int32_t));
+static struct secasvar *key_getsavbyspi __P((struct secashead *, u_int32_t));
+static int key_setsaval __P((struct secasvar *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_mature __P((struct secasvar *));
+static struct mbuf *key_setdumpsa __P((struct secasvar *, u_int8_t,
+	u_int8_t, u_int32_t, u_int32_t));
+static struct mbuf *key_setsadbmsg __P((u_int8_t, u_int16_t, u_int8_t,
+	u_int32_t, pid_t, u_int16_t));
+static struct mbuf *key_setsadbsa __P((struct secasvar *));
+static struct mbuf *key_setsadbaddr __P((u_int16_t,
+	struct sockaddr *, u_int8_t, u_int16_t));
+#if 0
+static struct mbuf *key_setsadbident __P((u_int16_t, u_int16_t, caddr_t,
+	int, u_int64_t));
+#endif
+static struct mbuf *key_setsadbxsa2 __P((u_int8_t, u_int32_t, u_int32_t));
+static struct mbuf *key_setsadblifetime __P((u_int16_t, u_int32_t,
+	u_int64_t, u_int64_t, u_int64_t));
+static struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t,
+	u_int32_t));
+static void *key_newbuf __P((const void *, u_int));
+#ifdef INET6
+static int key_ismyaddr6 __P((struct sockaddr_in6 *));
+#endif
+
+/* flags for key_cmpsaidx() */
+#define CMP_HEAD	1	/* protocol, addresses. */
+#define CMP_MODE_REQID	2	/* additionally HEAD, reqid, mode. */
+#define CMP_REQID	3	/* additionally HEAD, reaid. */
+#define CMP_EXACTLY	4	/* all elements. */
+static int key_cmpsaidx
+	__P((struct secasindex *, struct secasindex *, int));
+
+static int key_sockaddrcmp __P((struct sockaddr *, struct sockaddr *, int));
+static int key_bbcmp __P((caddr_t, caddr_t, u_int));
+static void key_srandom __P((void));
+static u_int16_t key_satype2proto __P((u_int8_t));
+static u_int8_t key_proto2satype __P((u_int16_t));
+
+static int key_getspi __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static u_int32_t key_do_getnewspi __P((struct sadb_spirange *,
+					struct secasindex *));
+static int key_update __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+#ifdef IPSEC_DOSEQCHECK
+static struct secasvar *key_getsavbyseq __P((struct secashead *, u_int32_t));
+#endif
+static int key_add __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_setident __P((struct secashead *, struct mbuf *,
+	const struct sadb_msghdr *));
+static struct mbuf *key_getmsgbuf_x1 __P((struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_delete __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_get __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+
+static void key_getcomb_setlifetime __P((struct sadb_comb *));
+#ifdef IPSEC_ESP
+static struct mbuf *key_getcomb_esp __P((void));
+#endif
+static struct mbuf *key_getcomb_ah __P((void));
+static struct mbuf *key_getcomb_ipcomp __P((void));
+static struct mbuf *key_getprop __P((const struct secasindex *));
+
+static int key_acquire __P((struct secasindex *, struct secpolicy *));
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+static struct secacq *key_newacq __P((struct secasindex *));
+static struct secacq *key_getacq __P((struct secasindex *));
+static struct secacq *key_getacqbyseq __P((u_int32_t));
+#endif
+static struct secspacq *key_newspacq __P((struct secpolicyindex *));
+static struct secspacq *key_getspacq __P((struct secpolicyindex *));
+static int key_acquire2 __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_register __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_expire __P((struct secasvar *));
+static int key_flush __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_dump __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_promisc __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *));
+static int key_senderror __P((struct socket *, struct mbuf *, int));
+static int key_validate_ext __P((const struct sadb_ext *, int));
+static int key_align __P((struct mbuf *, struct sadb_msghdr *));
+#if 0
+static const char *key_getfqdn __P((void));
+static const char *key_getuserfqdn __P((void));
+#endif
+static void key_sa_chgstate __P((struct secasvar *, u_int8_t));
+static struct mbuf *key_alloc_mbuf __P((int));
+#ifdef __NetBSD__
+struct callout key_timehandler_ch;
+#endif
+
+/* %%% IPsec policy management */
+/*
+ * allocating a SP for OUTBOUND or INBOUND packet.
+ * Must call key_freesp() later.
+ * OUT:	NULL:	not found
+ *	others:	found and return the pointer.
+ */
+struct secpolicy *
+key_allocsp(spidx, dir)
+	struct secpolicyindex *spidx;
+	u_int dir;
+{
+	struct secpolicy *sp;
+	int s;
+
+	/* sanity check */
+	if (spidx == NULL)
+		panic("key_allocsp: NULL pointer is passed.\n");
+
+	/* check direction */
+	switch (dir) {
+	case IPSEC_DIR_INBOUND:
+	case IPSEC_DIR_OUTBOUND:
+		break;
+	default:
+		panic("key_allocsp: Invalid direction is passed.\n");
+	}
+
+	/* get a SP entry */
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+	KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+		printf("*** objects\n");
+		kdebug_secpolicyindex(spidx));
+
+	LIST_FOREACH(sp, &sptree[dir], chain) {
+		KEYDEBUG(KEYDEBUG_IPSEC_DATA,
+			printf("*** in SPD\n");
+			kdebug_secpolicyindex(&sp->spidx));
+
+		if (sp->state == IPSEC_SPSTATE_DEAD)
+			continue;
+		if (key_cmpspidx_withmask(&sp->spidx, spidx))
+			goto found;
+	}
+
+	splx(s);
+	return NULL;
+
+found:
+	/* sanity check */
+	KEY_CHKSPDIR(sp->spidx.dir, dir, "key_allocsp");
+
+	/* found a SPD entry */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	sp->lastused = time_second;
+#else
+	sp->lastused = time.tv_sec;
+#endif
+	sp->refcnt++;
+	splx(s);
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP key_allocsp cause refcnt++:%d SP:%p\n",
+			sp->refcnt, sp));
+
+	return sp;
+}
+
+/*
+ * return a policy that matches this particular inbound packet.
+ * XXX slow
+ */
+struct secpolicy *
+key_gettunnel(osrc, odst, isrc, idst)
+	struct sockaddr *osrc, *odst, *isrc, *idst;
+{
+	struct secpolicy *sp;
+	const int dir = IPSEC_DIR_INBOUND;
+	int s;
+	struct ipsecrequest *r1, *r2, *p;
+	struct sockaddr *os, *od, *is, *id;
+	struct secpolicyindex spidx;
+
+	if (isrc->sa_family != idst->sa_family) {
+		ipseclog((LOG_ERR, "protocol family mismatched %d != %d\n.",
+			isrc->sa_family, idst->sa_family));
+		return NULL;
+	}
+
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+	LIST_FOREACH(sp, &sptree[dir], chain) {
+		if (sp->state == IPSEC_SPSTATE_DEAD)
+			continue;
+
+		r1 = r2 = NULL;
+		for (p = sp->req; p; p = p->next) {
+			if (p->saidx.mode != IPSEC_MODE_TUNNEL)
+				continue;
+
+			r1 = r2;
+			r2 = p;
+
+			if (!r1) {
+				/* here we look at address matches only */
+				spidx = sp->spidx;
+				if (isrc->sa_len > sizeof(spidx.src) ||
+				    idst->sa_len > sizeof(spidx.dst))
+					continue;
+				bcopy(isrc, &spidx.src, isrc->sa_len);
+				bcopy(idst, &spidx.dst, idst->sa_len);
+				if (!key_cmpspidx_withmask(&sp->spidx, &spidx))
+					continue;
+			} else {
+				is = (struct sockaddr *)&r1->saidx.src;
+				id = (struct sockaddr *)&r1->saidx.dst;
+				if (key_sockaddrcmp(is, isrc, 0) ||
+				    key_sockaddrcmp(id, idst, 0))
+					continue;
+			}
+
+			os = (struct sockaddr *)&r2->saidx.src;
+			od = (struct sockaddr *)&r2->saidx.dst;
+			if (key_sockaddrcmp(os, osrc, 0) ||
+			    key_sockaddrcmp(od, odst, 0))
+				continue;
+
+			goto found;
+		}
+	}
+	splx(s);
+	return NULL;
+
+found:
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	sp->lastused = time_second;
+#else
+	sp->lastused = time.tv_sec;
+#endif
+	sp->refcnt++;
+	splx(s);
+	return sp;
+}
+
+/*
+ * allocating an SA entry for an *OUTBOUND* packet.
+ * checking each request entries in SP, and acquire an SA if need.
+ * OUT:	0: there are valid requests.
+ *	ENOENT: policy may be valid, but SA with REQUIRE is on acquiring.
+ */
+int
+key_checkrequest(isr, saidx)
+	struct ipsecrequest *isr;
+	struct secasindex *saidx;
+{
+	u_int level;
+	int error;
+
+	/* sanity check */
+	if (isr == NULL || saidx == NULL)
+		panic("key_checkrequest: NULL pointer is passed.\n");
+
+	/* check mode */
+	switch (saidx->mode) {
+	case IPSEC_MODE_TRANSPORT:
+	case IPSEC_MODE_TUNNEL:
+		break;
+	case IPSEC_MODE_ANY:
+	default:
+		panic("key_checkrequest: Invalid policy defined.\n");
+	}
+
+	/* get current level */
+	level = ipsec_get_reqlevel(isr);
+
+#if 0
+	/*
+	 * We do allocate new SA only if the state of SA in the holder is
+	 * SADB_SASTATE_DEAD.  The SA for outbound must be the oldest.
+	 */
+	if (isr->sav != NULL) {
+		if (isr->sav->sah == NULL)
+			panic("key_checkrequest: sah is null.\n");
+		if (isr->sav == (struct secasvar *)LIST_FIRST(
+			    &isr->sav->sah->savtree[SADB_SASTATE_DEAD])) {
+			KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+				printf("DP checkrequest calls free SA:%p\n",
+					isr->sav));
+			key_freesav(isr->sav);
+			isr->sav = NULL;
+		}
+	}
+#else
+	/*
+	 * we free any SA stashed in the IPsec request because a different
+	 * SA may be involved each time this request is checked, either
+	 * because new SAs are being configured, or this request is
+	 * associated with an unconnected datagram socket, or this request
+	 * is associated with a system default policy.
+	 *
+	 * The operation may have negative impact to performance.  We may
+	 * want to check cached SA carefully, rather than picking new SA
+	 * every time.
+	 */
+	if (isr->sav != NULL) {
+		key_freesav(isr->sav);
+		isr->sav = NULL;
+	}
+#endif
+
+	/*
+	 * new SA allocation if no SA found.
+	 * key_allocsa_policy should allocate the oldest SA available.
+	 * See key_do_allocsa_policy(), and draft-jenkins-ipsec-rekeying-03.txt.
+	 */
+	if (isr->sav == NULL)
+		isr->sav = key_allocsa_policy(saidx);
+
+	/* When there is SA. */
+	if (isr->sav != NULL)
+		return 0;
+
+	/* there is no SA */
+	if ((error = key_acquire(saidx, isr->sp)) != 0) {
+		/* XXX What should I do ? */
+		ipseclog((LOG_DEBUG, "key_checkrequest: error %d returned "
+			"from key_acquire.\n", error));
+		return error;
+	}
+
+	return level == IPSEC_LEVEL_REQUIRE ? ENOENT : 0;
+}
+
+/*
+ * allocating a SA for policy entry from SAD.
+ * NOTE: searching SAD of aliving state.
+ * OUT:	NULL:	not found.
+ *	others:	found and return the pointer.
+ */
+static struct secasvar *
+key_allocsa_policy(saidx)
+	struct secasindex *saidx;
+{
+	struct secashead *sah;
+	struct secasvar *sav;
+	u_int stateidx, state;
+
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (sah->state == SADB_SASTATE_DEAD)
+			continue;
+		if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID))
+			goto found;
+	}
+
+	return NULL;
+
+    found:
+
+	/* search valid state */
+	for (stateidx = 0;
+	     stateidx < _ARRAYLEN(saorder_state_valid);
+	     stateidx++) {
+
+		state = saorder_state_valid[stateidx];
+
+		sav = key_do_allocsa_policy(sah, state);
+		if (sav != NULL)
+			return sav;
+	}
+
+	return NULL;
+}
+
+/*
+ * searching SAD with direction, protocol, mode and state.
+ * called by key_allocsa_policy().
+ * OUT:
+ *	NULL	: not found
+ *	others	: found, pointer to a SA.
+ */
+static struct secasvar *
+key_do_allocsa_policy(sah, state)
+	struct secashead *sah;
+	u_int state;
+{
+	struct secasvar *sav, *nextsav, *candidate, *d;
+
+	/* initilize */
+	candidate = NULL;
+
+	for (sav = LIST_FIRST(&sah->savtree[state]);
+	     sav != NULL;
+	     sav = nextsav) {
+
+		nextsav = LIST_NEXT(sav, chain);
+
+		/* sanity check */
+		KEY_CHKSASTATE(sav->state, state, "key_do_allocsa_policy");
+
+		/* initialize */
+		if (candidate == NULL) {
+			candidate = sav;
+			continue;
+		}
+
+		/* Which SA is the better ? */
+
+		/* sanity check 2 */
+		if (candidate->lft_c == NULL || sav->lft_c == NULL)
+			panic("key_do_allocsa_policy: "
+				"lifetime_current is NULL.\n");
+
+		/* What the best method is to compare ? */
+		if (key_preferred_oldsa) {
+			if (candidate->lft_c->sadb_lifetime_addtime >
+					sav->lft_c->sadb_lifetime_addtime) {
+				candidate = sav;
+			}
+			continue;
+			/*NOTREACHED*/
+		}
+
+		/* preferred new sa rather than old sa */
+		if (candidate->lft_c->sadb_lifetime_addtime <
+				sav->lft_c->sadb_lifetime_addtime) {
+			d = candidate;
+			candidate = sav;
+		} else
+			d = sav;
+
+		/*
+		 * prepared to delete the SA when there is more
+		 * suitable candidate and the lifetime of the SA is not
+		 * permanent.
+		 */
+		if (d->lft_c->sadb_lifetime_addtime != 0) {
+
+			struct mbuf *m, *result;
+
+			key_sa_chgstate(d, SADB_SASTATE_DEAD);
+			key_freesav(d);
+
+			m = key_setsadbmsg(SADB_DELETE, 0,
+					sav->sah->saidx.proto, 0, 0, d->refcnt);
+			if (!m)
+				return NULL;
+			result = m;
+
+			/* set sadb_address for saidx's. */
+			m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
+				(struct sockaddr *)&d->sah->saidx.src,
+				FULLMASK, IPSEC_ULPROTO_ANY);
+			if (!m)
+				return NULL;
+			m_cat(result, m);
+
+			/* set sadb_address for saidx's. */
+			m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
+				(struct sockaddr *)&d->sah->saidx.dst,
+				FULLMASK, IPSEC_ULPROTO_ANY);
+			if (!m)
+				return NULL;
+			m_cat(result, m);
+
+			/* create SA extension */
+			m = key_setsadbsa(d);
+			if (!m)
+				return NULL;
+			m_cat(result, m);
+
+			if (result->m_len < sizeof(struct sadb_msg)) {
+				result = m_pullup(result,
+						sizeof(struct sadb_msg));
+				if (result == NULL)
+					return NULL;
+			}
+
+			result->m_pkthdr.len = 0;
+			for (m = result; m; m = m->m_next)
+				result->m_pkthdr.len += m->m_len;
+			mtod(result, struct sadb_msg *)->sadb_msg_len =
+				PFKEY_UNIT64(result->m_pkthdr.len);
+
+			if (key_sendup_mbuf(NULL, result,
+					KEY_SENDUP_REGISTERED))
+				return NULL;
+		}
+	}
+
+	if (candidate) {
+		candidate->refcnt++;
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP allocsa_policy cause "
+				"refcnt++:%d SA:%p\n",
+				candidate->refcnt, candidate));
+	}
+	return candidate;
+}
+
+/*
+ * allocating a SA entry for a *INBOUND* packet.
+ * Must call key_freesav() later.
+ * OUT: positive:	pointer to a sav.
+ *	NULL:		not found, or error occured.
+ *
+ * In the comparison, source address will be ignored for RFC2401 conformance.
+ * To quote, from section 4.1:
+ *	A security association is uniquely identified by a triple consisting
+ *	of a Security Parameter Index (SPI), an IP Destination Address, and a
+ *	security protocol (AH or ESP) identifier.
+ * Note that, however, we do need to keep source address in IPsec SA.
+ * IKE specification and PF_KEY specification do assume that we
+ * keep source address in IPsec SA.  We see a tricky situation here.
+ */
+struct secasvar *
+key_allocsa(family, src, dst, proto, spi)
+	u_int family, proto;
+	caddr_t src, dst;
+	u_int32_t spi;
+{
+	struct secashead *sah;
+	struct secasvar *sav;
+	u_int stateidx, state;
+	struct sockaddr_in sin;
+	struct sockaddr_in6 sin6;
+	int s;
+
+	/* sanity check */
+	if (src == NULL || dst == NULL)
+		panic("key_allocsa: NULL pointer is passed.\n");
+
+	/*
+	 * searching SAD.
+	 * XXX: to be checked internal IP header somewhere.  Also when
+	 * IPsec tunnel packet is received.  But ESP tunnel mode is
+	 * encrypted so we can't check internal IP header.
+	 */
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+	LIST_FOREACH(sah, &sahtree, chain) {
+		/* search valid state */
+		for (stateidx = 0;
+		     stateidx < _ARRAYLEN(saorder_state_valid);
+		     stateidx++) {
+			state = saorder_state_valid[stateidx];
+			LIST_FOREACH(sav, &sah->savtree[state], chain) {
+				/* sanity check */
+				KEY_CHKSASTATE(sav->state, state, "key_allocsav");
+				if (proto != sav->sah->saidx.proto)
+					continue;
+				if (spi != sav->spi)
+					continue;
+				if (family != sav->sah->saidx.src.ss_family ||
+				    family != sav->sah->saidx.dst.ss_family)
+					continue;
+
+#if 0	/* don't check src */
+				/* check src address */
+				switch (family) {
+				case AF_INET:
+					bzero(&sin, sizeof(sin));
+					sin.sin_family = AF_INET;
+					sin.sin_len = sizeof(sin);
+					bcopy(src, &sin.sin_addr,
+					    sizeof(sin.sin_addr));
+					if (key_sockaddrcmp((struct sockaddr*)&sin,
+					    (struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
+						continue;
+
+					break;
+				case AF_INET6:
+					bzero(&sin6, sizeof(sin6));
+					sin6.sin6_family = AF_INET6;
+					sin6.sin6_len = sizeof(sin6);
+					bcopy(src, &sin6.sin6_addr,
+					    sizeof(sin6.sin6_addr));
+					if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) {
+						/* kame fake scopeid */
+						sin6.sin6_scope_id =
+						    ntohs(sin6.sin6_addr.s6_addr16[1]);
+						sin6.sin6_addr.s6_addr16[1] = 0;
+					}
+					if (key_sockaddrcmp((struct sockaddr*)&sin6,
+					    (struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
+						continue;
+					break;
+				default:
+					ipseclog((LOG_DEBUG, "key_allocsa: "
+					    "unknown address family=%d.\n",
+					    family));
+					continue;
+				}
+
+#endif
+				/* check dst address */
+				switch (family) {
+				case AF_INET:
+					bzero(&sin, sizeof(sin));
+					sin.sin_family = AF_INET;
+					sin.sin_len = sizeof(sin);
+					bcopy(dst, &sin.sin_addr,
+					    sizeof(sin.sin_addr));
+					if (key_sockaddrcmp((struct sockaddr*)&sin,
+					    (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
+						continue;
+
+					break;
+				case AF_INET6:
+					bzero(&sin6, sizeof(sin6));
+					sin6.sin6_family = AF_INET6;
+					sin6.sin6_len = sizeof(sin6);
+					bcopy(dst, &sin6.sin6_addr,
+					    sizeof(sin6.sin6_addr));
+					if (IN6_IS_SCOPE_LINKLOCAL(&sin6.sin6_addr)) {
+						/* kame fake scopeid */
+						sin6.sin6_scope_id =
+						    ntohs(sin6.sin6_addr.s6_addr16[1]);
+						sin6.sin6_addr.s6_addr16[1] = 0;
+					}
+					if (key_sockaddrcmp((struct sockaddr*)&sin6,
+					    (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
+						continue;
+					break;
+				default:
+					ipseclog((LOG_DEBUG, "key_allocsa: "
+					    "unknown address family=%d.\n",
+					    family));
+					continue;
+				}
+
+				goto found;
+			}
+		}
+	}
+
+	/* not found */
+	splx(s);
+	return NULL;
+
+found:
+	sav->refcnt++;
+	splx(s);
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP allocsa cause refcnt++:%d SA:%p\n",
+			sav->refcnt, sav));
+	return sav;
+}
+
+/*
+ * Must be called after calling key_allocsp().
+ * For both the packet without socket and key_freeso().
+ */
+void
+key_freesp(sp)
+	struct secpolicy *sp;
+{
+	/* sanity check */
+	if (sp == NULL)
+		panic("key_freesp: NULL pointer is passed.\n");
+
+	sp->refcnt--;
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP freesp cause refcnt--:%d SP:%p\n",
+			sp->refcnt, sp));
+
+	if (sp->refcnt == 0)
+		key_delsp(sp);
+
+	return;
+}
+
+/*
+ * Must be called after calling key_allocsp().
+ * For the packet with socket.
+ */
+void
+key_freeso(so)
+	struct socket *so;
+{
+	/* sanity check */
+	if (so == NULL)
+		panic("key_freeso: NULL pointer is passed.\n");
+
+	switch (so->so_proto->pr_domain->dom_family) {
+#ifdef INET
+	case PF_INET:
+	    {
+		struct inpcb *pcb = sotoinpcb(so);
+
+		/* Does it have a PCB ? */
+		if (pcb == NULL)
+			return;
+		key_freesp_so(&pcb->inp_sp->sp_in);
+		key_freesp_so(&pcb->inp_sp->sp_out);
+	    }
+		break;
+#endif
+#ifdef INET6
+	case PF_INET6:
+	    {
+#ifdef HAVE_NRL_INPCB
+		struct inpcb *pcb  = sotoinpcb(so);
+
+		/* Does it have a PCB ? */
+		if (pcb == NULL)
+			return;
+		key_freesp_so(&pcb->inp_sp->sp_in);
+		key_freesp_so(&pcb->inp_sp->sp_out);
+#else
+		struct in6pcb *pcb  = sotoin6pcb(so);
+
+		/* Does it have a PCB ? */
+		if (pcb == NULL)
+			return;
+		key_freesp_so(&pcb->in6p_sp->sp_in);
+		key_freesp_so(&pcb->in6p_sp->sp_out);
+#endif
+	    }
+		break;
+#endif /* INET6 */
+	default:
+		ipseclog((LOG_DEBUG, "key_freeso: unknown address family=%d.\n",
+		    so->so_proto->pr_domain->dom_family));
+		return;
+	}
+
+	return;
+}
+
+static void
+key_freesp_so(sp)
+	struct secpolicy **sp;
+{
+	/* sanity check */
+	if (sp == NULL || *sp == NULL)
+		panic("key_freesp_so: sp == NULL\n");
+
+	switch ((*sp)->policy) {
+	case IPSEC_POLICY_IPSEC:
+		KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+			printf("DP freeso calls free SP:%p\n", *sp));
+		key_freesp(*sp);
+		*sp = NULL;
+		break;
+	case IPSEC_POLICY_ENTRUST:
+	case IPSEC_POLICY_BYPASS:
+		return;
+	default:
+		panic("key_freesp_so: Invalid policy found %d", (*sp)->policy);
+	}
+
+	return;
+}
+
+/*
+ * Must be called after calling key_allocsa().
+ * This function is called by key_freesp() to free some SA allocated
+ * for a policy.
+ */
+void
+key_freesav(sav)
+	struct secasvar *sav;
+{
+	/* sanity check */
+	if (sav == NULL)
+		panic("key_freesav: NULL pointer is passed.\n");
+
+	sav->refcnt--;
+	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+		printf("DP freesav cause refcnt--:%d SA:%p SPI %u\n",
+			sav->refcnt, sav, (u_int32_t)ntohl(sav->spi)));
+
+	if (sav->refcnt == 0)
+		key_delsav(sav);
+
+	return;
+}
+
+/* %%% SPD management */
+/*
+ * free security policy entry.
+ */
+static void
+key_delsp(sp)
+	struct secpolicy *sp;
+{
+	int s;
+
+	/* sanity check */
+	if (sp == NULL)
+		panic("key_delsp: NULL pointer is passed.\n");
+
+	sp->state = IPSEC_SPSTATE_DEAD;
+
+	if (sp->refcnt > 0)
+		return; /* can't free */
+
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+	/* remove from SP index */
+	if (__LIST_CHAINED(sp))
+		LIST_REMOVE(sp, chain);
+
+    {
+	struct ipsecrequest *isr = sp->req, *nextisr;
+
+	while (isr != NULL) {
+		if (isr->sav != NULL) {
+			KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+				printf("DP delsp calls free SA:%p\n",
+					isr->sav));
+			key_freesav(isr->sav);
+			isr->sav = NULL;
+		}
+
+#if defined(NSEC) && NSEC > 0
+		if (isr->tunifp) {
+			int s;
+
+			s = splimp();
+			sec_demolish(isr->tunifp);
+			splx(s);
+
+			/* XXX more garbage-collection */
+			isr->tunifp = NULL;
+		}
+#endif
+
+		nextisr = isr->next;
+		KFREE(isr);
+		isr = nextisr;
+	}
+    }
+
+	keydb_delsecpolicy(sp);
+
+	splx(s);
+
+	return;
+}
+
+/*
+ * search SPD
+ * OUT:	NULL	: not found
+ *	others	: found, pointer to a SP.
+ */
+static struct secpolicy *
+key_getsp(spidx)
+	struct secpolicyindex *spidx;
+{
+	struct secpolicy *sp;
+
+	/* sanity check */
+	if (spidx == NULL)
+		panic("key_getsp: NULL pointer is passed.\n");
+
+	LIST_FOREACH(sp, &sptree[spidx->dir], chain) {
+		if (sp->state == IPSEC_SPSTATE_DEAD)
+			continue;
+		if (key_cmpspidx_exactly(spidx, &sp->spidx)) {
+			sp->refcnt++;
+			return sp;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * get SP by index.
+ * OUT:	NULL	: not found
+ *	others	: found, pointer to a SP.
+ */
+static struct secpolicy *
+key_getspbyid(id)
+	u_int32_t id;
+{
+	struct secpolicy *sp;
+
+	LIST_FOREACH(sp, &sptree[IPSEC_DIR_INBOUND], chain) {
+		if (sp->state == IPSEC_SPSTATE_DEAD)
+			continue;
+		if (sp->id == id) {
+			sp->refcnt++;
+			return sp;
+		}
+	}
+
+	LIST_FOREACH(sp, &sptree[IPSEC_DIR_OUTBOUND], chain) {
+		if (sp->state == IPSEC_SPSTATE_DEAD)
+			continue;
+		if (sp->id == id) {
+			sp->refcnt++;
+			return sp;
+		}
+	}
+
+	return NULL;
+}
+
+struct secpolicy *
+key_newsp()
+{
+	struct secpolicy *newsp = NULL;
+
+	newsp = keydb_newsecpolicy();
+	if (!newsp)
+		return newsp;
+
+	newsp->refcnt = 1;
+	newsp->req = NULL;
+
+	return newsp;
+}
+
+/*
+ * create secpolicy structure from sadb_x_policy structure.
+ * NOTE: `state', `secpolicyindex' in secpolicy structure are not set,
+ * so must be set properly later.
+ */
+struct secpolicy *
+key_msg2sp(xpl0, len, error)
+	struct sadb_x_policy *xpl0;
+	size_t len;
+	int *error;
+{
+	struct secpolicy *newsp;
+
+	/* sanity check */
+	if (xpl0 == NULL)
+		panic("key_msg2sp: NULL pointer was passed.\n");
+	if (len < sizeof(*xpl0))
+		panic("key_msg2sp: invalid length.\n");
+	if (len != PFKEY_EXTLEN(xpl0)) {
+		ipseclog((LOG_DEBUG, "key_msg2sp: Invalid msg length.\n"));
+		*error = EINVAL;
+		return NULL;
+	}
+
+	if ((newsp = key_newsp()) == NULL) {
+		*error = ENOBUFS;
+		return NULL;
+	}
+
+	newsp->spidx.dir = xpl0->sadb_x_policy_dir;
+	newsp->policy = xpl0->sadb_x_policy_type;
+
+	/* check policy */
+	switch (xpl0->sadb_x_policy_type) {
+	case IPSEC_POLICY_DISCARD:
+	case IPSEC_POLICY_NONE:
+	case IPSEC_POLICY_ENTRUST:
+	case IPSEC_POLICY_BYPASS:
+		newsp->req = NULL;
+		break;
+
+	case IPSEC_POLICY_IPSEC:
+	    {
+		int tlen;
+		struct sadb_x_ipsecrequest *xisr;
+		struct ipsecrequest **p_isr = &newsp->req;
+
+		/* validity check */
+		if (PFKEY_EXTLEN(xpl0) < sizeof(*xpl0)) {
+			ipseclog((LOG_DEBUG,
+			    "key_msg2sp: Invalid msg length.\n"));
+			key_freesp(newsp);
+			*error = EINVAL;
+			return NULL;
+		}
+
+		tlen = PFKEY_EXTLEN(xpl0) - sizeof(*xpl0);
+		xisr = (struct sadb_x_ipsecrequest *)(xpl0 + 1);
+
+		while (tlen > 0) {
+
+			/* length check */
+			if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) {
+				ipseclog((LOG_DEBUG, "key_msg2sp: "
+					"invalid ipsecrequest length.\n"));
+				key_freesp(newsp);
+				*error = EINVAL;
+				return NULL;
+			}
+
+			/* allocate request buffer */
+			KMALLOC(*p_isr, struct ipsecrequest *, sizeof(**p_isr));
+			if ((*p_isr) == NULL) {
+				ipseclog((LOG_DEBUG,
+				    "key_msg2sp: No more memory.\n"));
+				key_freesp(newsp);
+				*error = ENOBUFS;
+				return NULL;
+			}
+			bzero(*p_isr, sizeof(**p_isr));
+
+			/* set values */
+			(*p_isr)->next = NULL;
+
+			switch (xisr->sadb_x_ipsecrequest_proto) {
+			case IPPROTO_ESP:
+			case IPPROTO_AH:
+			case IPPROTO_IPCOMP:
+				break;
+			default:
+				ipseclog((LOG_DEBUG,
+				    "key_msg2sp: invalid proto type=%u\n",
+				    xisr->sadb_x_ipsecrequest_proto));
+				key_freesp(newsp);
+				*error = EPROTONOSUPPORT;
+				return NULL;
+			}
+			(*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto;
+
+			switch (xisr->sadb_x_ipsecrequest_mode) {
+			case IPSEC_MODE_TRANSPORT:
+			case IPSEC_MODE_TUNNEL:
+				break;
+			case IPSEC_MODE_ANY:
+			default:
+				ipseclog((LOG_DEBUG,
+				    "key_msg2sp: invalid mode=%u\n",
+				    xisr->sadb_x_ipsecrequest_mode));
+				key_freesp(newsp);
+				*error = EINVAL;
+				return NULL;
+			}
+			(*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode;
+
+			switch (xisr->sadb_x_ipsecrequest_level) {
+			case IPSEC_LEVEL_DEFAULT:
+			case IPSEC_LEVEL_USE:
+			case IPSEC_LEVEL_REQUIRE:
+				break;
+			case IPSEC_LEVEL_UNIQUE:
+				/* validity check */
+				/*
+				 * If range violation of reqid, kernel will
+				 * update it, don't refuse it.
+				 */
+				if (xisr->sadb_x_ipsecrequest_reqid
+						> IPSEC_MANUAL_REQID_MAX) {
+					ipseclog((LOG_DEBUG,
+					    "key_msg2sp: reqid=%d range "
+					    "violation, updated by kernel.\n",
+					    xisr->sadb_x_ipsecrequest_reqid));
+					xisr->sadb_x_ipsecrequest_reqid = 0;
+				}
+
+				/* allocate new reqid id if reqid is zero. */
+				if (xisr->sadb_x_ipsecrequest_reqid == 0) {
+					u_int32_t reqid;
+					if ((reqid = key_newreqid()) == 0) {
+						key_freesp(newsp);
+						*error = ENOBUFS;
+						return NULL;
+					}
+					(*p_isr)->saidx.reqid = reqid;
+					xisr->sadb_x_ipsecrequest_reqid = reqid;
+				} else {
+				/* set it for manual keying. */
+					(*p_isr)->saidx.reqid =
+						xisr->sadb_x_ipsecrequest_reqid;
+				}
+				break;
+
+			default:
+				ipseclog((LOG_DEBUG, "key_msg2sp: invalid level=%u\n",
+					xisr->sadb_x_ipsecrequest_level));
+				key_freesp(newsp);
+				*error = EINVAL;
+				return NULL;
+			}
+			(*p_isr)->level = xisr->sadb_x_ipsecrequest_level;
+
+			/* set IP addresses if there */
+			if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
+				struct sockaddr *paddr;
+
+				paddr = (struct sockaddr *)(xisr + 1);
+
+				/* validity check */
+				if (paddr->sa_len
+				    > sizeof((*p_isr)->saidx.src)) {
+					ipseclog((LOG_DEBUG, "key_msg2sp: invalid request "
+						"address length.\n"));
+					key_freesp(newsp);
+					*error = EINVAL;
+					return NULL;
+				}
+				bcopy(paddr, &(*p_isr)->saidx.src,
+					paddr->sa_len);
+
+				paddr = (struct sockaddr *)((caddr_t)paddr
+							+ paddr->sa_len);
+
+				/* validity check */
+				if (paddr->sa_len
+				    > sizeof((*p_isr)->saidx.dst)) {
+					ipseclog((LOG_DEBUG, "key_msg2sp: invalid request "
+						"address length.\n"));
+					key_freesp(newsp);
+					*error = EINVAL;
+					return NULL;
+				}
+				bcopy(paddr, &(*p_isr)->saidx.dst,
+					paddr->sa_len);
+			}
+
+			(*p_isr)->sav = NULL;
+			(*p_isr)->sp = newsp;
+
+			/* initialization for the next. */
+			p_isr = &(*p_isr)->next;
+			tlen -= xisr->sadb_x_ipsecrequest_len;
+
+			/* validity check */
+			if (tlen < 0) {
+				ipseclog((LOG_DEBUG, "key_msg2sp: becoming tlen < 0.\n"));
+				key_freesp(newsp);
+				*error = EINVAL;
+				return NULL;
+			}
+
+			xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr
+			                 + xisr->sadb_x_ipsecrequest_len);
+		}
+	    }
+		break;
+	default:
+		ipseclog((LOG_DEBUG, "key_msg2sp: invalid policy type.\n"));
+		key_freesp(newsp);
+		*error = EINVAL;
+		return NULL;
+	}
+
+	*error = 0;
+	return newsp;
+}
+
+static u_int32_t
+key_newreqid()
+{
+	static u_int32_t auto_reqid = IPSEC_MANUAL_REQID_MAX + 1;
+
+	auto_reqid = (auto_reqid == ~0
+			? IPSEC_MANUAL_REQID_MAX + 1 : auto_reqid + 1);
+
+	/* XXX should be unique check */
+
+	return auto_reqid;
+}
+
+/*
+ * copy secpolicy struct to sadb_x_policy structure indicated.
+ */
+struct mbuf *
+key_sp2msg(sp)
+	struct secpolicy *sp;
+{
+	struct sadb_x_policy *xpl;
+	int tlen;
+	caddr_t p;
+	struct mbuf *m;
+
+	/* sanity check. */
+	if (sp == NULL)
+		panic("key_sp2msg: NULL pointer was passed.\n");
+
+	tlen = key_getspreqmsglen(sp);
+
+	m = key_alloc_mbuf(tlen);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	m->m_len = tlen;
+	m->m_next = NULL;
+	xpl = mtod(m, struct sadb_x_policy *);
+	bzero(xpl, tlen);
+
+	xpl->sadb_x_policy_len = PFKEY_UNIT64(tlen);
+	xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+	xpl->sadb_x_policy_type = sp->policy;
+	xpl->sadb_x_policy_dir = sp->spidx.dir;
+	xpl->sadb_x_policy_id = sp->id;
+	p = (caddr_t)xpl + sizeof(*xpl);
+
+	/* if is the policy for ipsec ? */
+	if (sp->policy == IPSEC_POLICY_IPSEC) {
+		struct sadb_x_ipsecrequest *xisr;
+		struct ipsecrequest *isr;
+
+		for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+			xisr = (struct sadb_x_ipsecrequest *)p;
+
+			xisr->sadb_x_ipsecrequest_proto = isr->saidx.proto;
+			xisr->sadb_x_ipsecrequest_mode = isr->saidx.mode;
+			xisr->sadb_x_ipsecrequest_level = isr->level;
+			xisr->sadb_x_ipsecrequest_reqid = isr->saidx.reqid;
+
+			p += sizeof(*xisr);
+			bcopy(&isr->saidx.src, p, isr->saidx.src.ss_len);
+			p += isr->saidx.src.ss_len;
+			bcopy(&isr->saidx.dst, p, isr->saidx.dst.ss_len);
+			p += isr->saidx.src.ss_len;
+
+			xisr->sadb_x_ipsecrequest_len =
+				PFKEY_ALIGN8(sizeof(*xisr)
+					+ isr->saidx.src.ss_len
+					+ isr->saidx.dst.ss_len);
+		}
+	}
+
+	return m;
+}
+
+/* m will not be freed nor modified */
+static struct mbuf *
+#ifdef __STDC__
+key_gather_mbuf(struct mbuf *m, const struct sadb_msghdr *mhp,
+	int ndeep, int nitem, ...)
+#else
+key_gather_mbuf(m, mhp, ndeep, nitem, va_alist)
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+	int ndeep;
+	int nitem;
+	va_dcl
+#endif
+{
+	va_list ap;
+	int idx;
+	int i;
+	struct mbuf *result = NULL, *n;
+	int len;
+
+	if (m == NULL || mhp == NULL)
+		panic("null pointer passed to key_gather");
+
+	va_start(ap, nitem);
+	for (i = 0; i < nitem; i++) {
+		idx = va_arg(ap, int);
+		if (idx < 0 || idx > SADB_EXT_MAX)
+			goto fail;
+		/* don't attempt to pull empty extension */
+		if (idx == SADB_EXT_RESERVED && mhp->msg == NULL)
+			continue;
+		if (idx != SADB_EXT_RESERVED  &&
+		    (mhp->ext[idx] == NULL || mhp->extlen[idx] == 0))
+			continue;
+
+		if (idx == SADB_EXT_RESERVED) {
+			len = PFKEY_ALIGN8(sizeof(struct sadb_msg));
+#ifdef DIAGNOSTIC
+			if (len > MHLEN)
+				panic("assumption failed");
+#endif
+			MGETHDR(n, M_DONTWAIT, MT_DATA);
+			if (!n)
+				goto fail;
+			n->m_len = len;
+			n->m_next = NULL;
+			m_copydata(m, 0, sizeof(struct sadb_msg),
+			    mtod(n, caddr_t));
+		} else if (i < ndeep) {
+			len = mhp->extlen[idx];
+			n = key_alloc_mbuf(len);
+			if (!n || n->m_next) {	/*XXX*/
+				if (n)
+					m_freem(n);
+				goto fail;
+			}
+			m_copydata(m, mhp->extoff[idx], mhp->extlen[idx],
+			    mtod(n, caddr_t));
+		} else {
+			n = m_copym(m, mhp->extoff[idx], mhp->extlen[idx],
+			    M_DONTWAIT);
+		}
+		if (n == NULL)
+			goto fail;
+
+		if (result)
+			m_cat(result, n);
+		else
+			result = n;
+	}
+	va_end(ap);
+
+	if ((result->m_flags & M_PKTHDR) != 0) {
+		result->m_pkthdr.len = 0;
+		for (n = result; n; n = n->m_next)
+			result->m_pkthdr.len += n->m_len;
+	}
+
+	return result;
+
+fail:
+	m_freem(result);
+	return NULL;
+}
+
+/*
+ * SADB_X_SPDADD, SADB_X_SPDSETIDX or SADB_X_SPDUPDATE processing
+ * add a entry to SP database, when received
+ *   <base, address(SD), (lifetime(H),) policy>
+ * from the user(?).
+ * Adding to SP database,
+ * and send
+ *   <base, address(SD), (lifetime(H),) policy>
+ * to the socket which was send.
+ *
+ * SPDADD set a unique policy entry.
+ * SPDSETIDX like SPDADD without a part of policy requests.
+ * SPDUPDATE replace a unique policy entry.
+ *
+ * m will always be freed.
+ */
+static int
+key_spdadd(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_address *src0, *dst0;
+	struct sadb_x_policy *xpl0, *xpl;
+	struct sadb_lifetime *lft = NULL;
+	struct secpolicyindex spidx;
+	struct secpolicy *newsp;
+	struct ipsecrequest *req;
+	int error;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_spdadd: NULL pointer is passed.\n");
+
+	if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
+	    mhp->ext[SADB_X_EXT_POLICY] == NULL) {
+		ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
+		ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL) {
+		if (mhp->extlen[SADB_EXT_LIFETIME_HARD]
+			< sizeof(struct sadb_lifetime)) {
+			ipseclog((LOG_DEBUG, "key_spdadd: invalid message is passed.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+		lft = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD];
+	}
+
+	src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
+	dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+	xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY];
+
+	/* make secindex */
+	/* XXX boundary check against sa_len */
+	KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir,
+	                src0 + 1,
+	                dst0 + 1,
+	                src0->sadb_address_prefixlen,
+	                dst0->sadb_address_prefixlen,
+	                src0->sadb_address_proto,
+	                &spidx);
+
+	/* checking the direciton. */
+	switch (xpl0->sadb_x_policy_dir) {
+	case IPSEC_DIR_INBOUND:
+	case IPSEC_DIR_OUTBOUND:
+		break;
+	default:
+		ipseclog((LOG_DEBUG, "key_spdadd: Invalid SP direction.\n"));
+		mhp->msg->sadb_msg_errno = EINVAL;
+		return 0;
+	}
+
+	/* check policy */
+	/* key_spdadd() accepts DISCARD, NONE and IPSEC. */
+	if (xpl0->sadb_x_policy_type == IPSEC_POLICY_ENTRUST
+	 || xpl0->sadb_x_policy_type == IPSEC_POLICY_BYPASS) {
+		ipseclog((LOG_DEBUG, "key_spdadd: Invalid policy type.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* policy requests are mandatory when action is ipsec. */
+        if (mhp->msg->sadb_msg_type != SADB_X_SPDSETIDX
+	 && xpl0->sadb_x_policy_type == IPSEC_POLICY_IPSEC
+	 && mhp->extlen[SADB_X_EXT_POLICY] <= sizeof(*xpl0)) {
+		ipseclog((LOG_DEBUG, "key_spdadd: some policy requests part required.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/*
+	 * checking there is SP already or not.
+	 * SPDUPDATE doesn't depend on whether there is a SP or not.
+	 * If the type is either SPDADD or SPDSETIDX AND a SP is found,
+	 * then error.
+	 */
+	newsp = key_getsp(&spidx);
+	if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
+		if (newsp) {
+			newsp->state = IPSEC_SPSTATE_DEAD;
+			key_freesp(newsp);
+		}
+	} else {
+		if (newsp != NULL) {
+			key_freesp(newsp);
+			ipseclog((LOG_DEBUG, "key_spdadd: a SP entry exists already.\n"));
+			return key_senderror(so, m, EEXIST);
+		}
+	}
+
+	/* allocation new SP entry */
+	if ((newsp = key_msg2sp(xpl0, PFKEY_EXTLEN(xpl0), &error)) == NULL) {
+		return key_senderror(so, m, error);
+	}
+
+	if ((newsp->id = key_getnewspid()) == 0) {
+		keydb_delsecpolicy(newsp);
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir,
+	                src0 + 1,
+	                dst0 + 1,
+	                src0->sadb_address_prefixlen,
+	                dst0->sadb_address_prefixlen,
+	                src0->sadb_address_proto,
+	                &newsp->spidx);
+
+	/* sanity check on addr pair */
+	if (((struct sockaddr *)(src0 + 1))->sa_family !=
+			((struct sockaddr *)(dst0 + 1))->sa_family) {
+		keydb_delsecpolicy(newsp);
+		return key_senderror(so, m, EINVAL);
+	}
+	if (((struct sockaddr *)(src0 + 1))->sa_len !=
+			((struct sockaddr *)(dst0 + 1))->sa_len) {
+		keydb_delsecpolicy(newsp);
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/*
+	 * bark if we have different address family on tunnel address
+	 * specification.  applies only if we decapsulate in RFC2401 IPsec
+	 * (implementation limitation).
+	 */
+	if (!ipsec_tunnel_device) {
+		for (req = newsp->req; req; req = req->next) {
+			if (req->saidx.src.ss_family) {
+				struct sockaddr *sa;
+				sa = (struct sockaddr *)(src0 + 1);
+				if (sa->sa_family != req->saidx.src.ss_family) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+			}
+			if (req->saidx.dst.ss_family) {
+				struct sockaddr *sa;
+				sa = (struct sockaddr *)(dst0 + 1);
+				if (sa->sa_family != req->saidx.dst.ss_family) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+			}
+		}
+	}
+
+#if defined(NSEC) && NSEC > 0
+	/*
+	 * based on newsp->req, create/configure tunnels.
+	 * TODO: reuse devices
+	 * TODO: conflicting devices
+	 */
+	if (ipsec_tunnel_device) {
+		for (req = newsp->req; req; req = req->next) {
+			struct ifnet *ifp;
+			int s;
+			struct sockaddr *me, *you;
+
+			if (req->saidx.mode != IPSEC_MODE_TUNNEL)
+				continue;
+
+			if (newsp->spidx.dir == IPSEC_DIR_OUTBOUND) {
+				me = (struct sockaddr *)&req->saidx.src;
+				you = (struct sockaddr *)&req->saidx.dst;
+			} else {
+				me = (struct sockaddr *)&req->saidx.dst;
+				you = (struct sockaddr *)&req->saidx.src;
+			}
+
+			s = splimp();
+			ifp = sec_establish(me, you);
+			/* bring the interface up, we are willing to receive */
+			if (ifp)
+				if_up(ifp);
+			splx(s);
+			if (!ifp) {
+				keydb_delsecpolicy(newsp);
+				return key_senderror(so, m, EINVAL);
+			}
+
+			req->tunifp = ifp;
+		}
+	}
+#endif
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	newsp->created = time_second;
+	newsp->lastused = time_second;
+#else
+	newsp->created = time.tv_sec;
+	newsp->lastused = time.tv_sec;
+#endif
+	newsp->lifetime = lft ? lft->sadb_lifetime_addtime : 0;
+	newsp->validtime = lft ? lft->sadb_lifetime_usetime : 0;
+
+	newsp->refcnt = 1;	/* do not reclaim until I say I do */
+	newsp->state = IPSEC_SPSTATE_ALIVE;
+	LIST_INSERT_TAIL(&sptree[newsp->spidx.dir], newsp, secpolicy, chain);
+
+	/* delete the entry in spacqtree */
+	if (mhp->msg->sadb_msg_type == SADB_X_SPDUPDATE) {
+		struct secspacq *spacq;
+		if ((spacq = key_getspacq(&spidx)) != NULL) {
+			/* reset counter in order to deletion by timehandler. */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+			spacq->created = time_second;
+#else
+			spacq->created = time.tv_sec;
+#endif
+			spacq->count = 0;
+		}
+    	}
+
+	/* invalidate all cached SPD pointers on pcb */
+	ipsec_invalpcbcacheall();
+
+    {
+	struct mbuf *n, *mpolicy;
+	struct sadb_msg *newmsg;
+	int off;
+
+	/* create new sadb_msg to reply. */
+	if (lft) {
+		n = key_gather_mbuf(m, mhp, 2, 5, SADB_EXT_RESERVED,
+		    SADB_X_EXT_POLICY, SADB_EXT_LIFETIME_HARD,
+		    SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
+	} else {
+		n = key_gather_mbuf(m, mhp, 2, 4, SADB_EXT_RESERVED,
+		    SADB_X_EXT_POLICY,
+		    SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
+	}
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	if (n->m_len < sizeof(*newmsg)) {
+		n = m_pullup(n, sizeof(*newmsg));
+		if (!n)
+			return key_senderror(so, m, ENOBUFS);
+	}
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+	off = 0;
+	mpolicy = m_pulldown(n, PFKEY_ALIGN8(sizeof(struct sadb_msg)),
+	    sizeof(*xpl), &off);
+	if (mpolicy == NULL) {
+		/* n is already freed */
+		return key_senderror(so, m, ENOBUFS);
+	}
+	xpl = (struct sadb_x_policy *)(mtod(mpolicy, caddr_t) + off);
+	if (xpl->sadb_x_policy_exttype != SADB_X_EXT_POLICY) {
+		m_freem(n);
+		return key_senderror(so, m, EINVAL);
+	}
+	xpl->sadb_x_policy_id = newsp->id;
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/*
+ * get new policy id.
+ * OUT:
+ *	0:	failure.
+ *	others: success.
+ */
+static u_int32_t
+key_getnewspid()
+{
+	u_int32_t newid = 0;
+	int count = key_spi_trycnt;	/* XXX */
+	struct secpolicy *sp;
+
+	/* when requesting to allocate spi ranged */
+	while (count--) {
+		newid = (policy_id = (policy_id == ~0 ? 1 : policy_id + 1));
+
+		if ((sp = key_getspbyid(newid)) == NULL)
+			break;
+
+		key_freesp(sp);
+	}
+
+	if (count == 0 || newid == 0) {
+		ipseclog((LOG_DEBUG, "key_getnewspid: to allocate policy id is failed.\n"));
+		return 0;
+	}
+
+	return newid;
+}
+
+/*
+ * SADB_SPDDELETE processing
+ * receive
+ *   <base, address(SD), policy(*)>
+ * from the user(?), and set SADB_SASTATE_DEAD,
+ * and send,
+ *   <base, address(SD), policy(*)>
+ * to the ikmpd.
+ * policy(*) including direction of policy.
+ *
+ * m will always be freed.
+ */
+static int
+key_spddelete(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_address *src0, *dst0;
+	struct sadb_x_policy *xpl0;
+	struct secpolicyindex spidx;
+	struct secpolicy *sp;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_spddelete: NULL pointer is passed.\n");
+
+	if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
+	    mhp->ext[SADB_X_EXT_POLICY] == NULL) {
+		ipseclog((LOG_DEBUG, "key_spddelete: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
+		ipseclog((LOG_DEBUG, "key_spddelete: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
+	dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+	xpl0 = (struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY];
+
+	/* make secindex */
+	/* XXX boundary check against sa_len */
+	KEY_SETSECSPIDX(xpl0->sadb_x_policy_dir,
+	                src0 + 1,
+	                dst0 + 1,
+	                src0->sadb_address_prefixlen,
+	                dst0->sadb_address_prefixlen,
+	                src0->sadb_address_proto,
+	                &spidx);
+
+	/* checking the direciton. */
+	switch (xpl0->sadb_x_policy_dir) {
+	case IPSEC_DIR_INBOUND:
+	case IPSEC_DIR_OUTBOUND:
+		break;
+	default:
+		ipseclog((LOG_DEBUG, "key_spddelete: Invalid SP direction.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* Is there SP in SPD ? */
+	if ((sp = key_getsp(&spidx)) == NULL) {
+		ipseclog((LOG_DEBUG, "key_spddelete: no SP found.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* save policy id to buffer to be returned. */
+	xpl0->sadb_x_policy_id = sp->id;
+
+	sp->state = IPSEC_SPSTATE_DEAD;
+	key_freesp(sp);
+
+	/* invalidate all cached SPD pointers on pcb */
+	ipsec_invalpcbcacheall();
+
+    {
+	struct mbuf *n;
+	struct sadb_msg *newmsg;
+
+	/* create new sadb_msg to reply. */
+	n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED,
+	    SADB_X_EXT_POLICY, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/*
+ * SADB_SPDDELETE2 processing
+ * receive
+ *   <base, policy(*)>
+ * from the user(?), and set SADB_SASTATE_DEAD,
+ * and send,
+ *   <base, policy(*)>
+ * to the ikmpd.
+ * policy(*) including direction of policy.
+ *
+ * m will always be freed.
+ */
+static int
+key_spddelete2(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	u_int32_t id;
+	struct secpolicy *sp;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_spddelete2: NULL pointer is passed.\n");
+
+	if (mhp->ext[SADB_X_EXT_POLICY] == NULL ||
+	    mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
+		ipseclog((LOG_DEBUG, "key_spddelete2: invalid message is passed.\n"));
+		key_senderror(so, m, EINVAL);
+		return 0;
+	}
+
+	id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
+
+	/* Is there SP in SPD ? */
+	if ((sp = key_getspbyid(id)) == NULL) {
+		ipseclog((LOG_DEBUG, "key_spddelete2: no SP found id:%u.\n", id));
+		key_senderror(so, m, EINVAL);
+	}
+
+	sp->state = IPSEC_SPSTATE_DEAD;
+	key_freesp(sp);
+
+	/* invalidate all cached SPD pointers on pcb */
+	ipsec_invalpcbcacheall();
+
+    {
+	struct mbuf *n, *nn;
+	struct sadb_msg *newmsg;
+	int off, len;
+
+	/* create new sadb_msg to reply. */
+	len = PFKEY_ALIGN8(sizeof(struct sadb_msg));
+
+	if (len > MCLBYTES)
+		return key_senderror(so, m, ENOBUFS);
+	MGETHDR(n, M_DONTWAIT, MT_DATA);
+	if (n && len > MHLEN) {
+		MCLGET(n, M_DONTWAIT);
+		if ((n->m_flags & M_EXT) == 0) {
+			m_freem(n);
+			n = NULL;
+		}
+	}
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	n->m_len = len;
+	n->m_next = NULL;
+	off = 0;
+
+	m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off);
+	off += PFKEY_ALIGN8(sizeof(struct sadb_msg));
+
+#ifdef DIAGNOSTIC
+	if (off != len)
+		panic("length inconsistency in key_spddelete2");
+#endif
+
+	n->m_next = m_copym(m, mhp->extoff[SADB_X_EXT_POLICY],
+	    mhp->extlen[SADB_X_EXT_POLICY], M_DONTWAIT);
+	if (!n->m_next) {
+		m_freem(n);
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	n->m_pkthdr.len = 0;
+	for (nn = n; nn; nn = nn->m_next)
+		n->m_pkthdr.len += nn->m_len;
+
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/*
+ * SADB_X_GET processing
+ * receive
+ *   <base, policy(*)>
+ * from the user(?),
+ * and send,
+ *   <base, address(SD), policy>
+ * to the ikmpd.
+ * policy(*) including direction of policy.
+ *
+ * m will always be freed.
+ */
+static int
+key_spdget(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	u_int32_t id;
+	struct secpolicy *sp;
+	struct mbuf *n;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_spdget: NULL pointer is passed.\n");
+
+	if (mhp->ext[SADB_X_EXT_POLICY] == NULL ||
+	    mhp->extlen[SADB_X_EXT_POLICY] < sizeof(struct sadb_x_policy)) {
+		ipseclog((LOG_DEBUG, "key_spdget: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	id = ((struct sadb_x_policy *)mhp->ext[SADB_X_EXT_POLICY])->sadb_x_policy_id;
+
+	/* Is there SP in SPD ? */
+	if ((sp = key_getspbyid(id)) == NULL) {
+		ipseclog((LOG_DEBUG, "key_spdget: no SP found id:%u.\n", id));
+		return key_senderror(so, m, ENOENT);
+	}
+
+	n = key_setdumpsp(sp, SADB_X_SPDGET, 0, mhp->msg->sadb_msg_pid);
+	if (n != NULL) {
+		m_freem(m);
+		return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+	} else
+		return key_senderror(so, m, ENOBUFS);
+}
+
+/*
+ * SADB_X_SPDACQUIRE processing.
+ * Acquire policy and SA(s) for a *OUTBOUND* packet.
+ * send
+ *   <base, policy(*)>
+ * to KMD, and expect to receive
+ *   <base> with SADB_X_SPDACQUIRE if error occured,
+ * or
+ *   <base, policy>
+ * with SADB_X_SPDUPDATE from KMD by PF_KEY.
+ * policy(*) is without policy requests.
+ *
+ *    0     : succeed
+ *    others: error number
+ */
+int
+key_spdacquire(sp)
+	struct secpolicy *sp;
+{
+	struct mbuf *result = NULL, *m;
+	struct secspacq *newspacq;
+	int error;
+
+	/* sanity check */
+	if (sp == NULL)
+		panic("key_spdacquire: NULL pointer is passed.\n");
+	if (sp->req != NULL)
+		panic("key_spdacquire: called but there is request.\n");
+	if (sp->policy != IPSEC_POLICY_IPSEC)
+		panic("key_spdacquire: policy mismathed. IPsec is expected.\n");
+
+	/* get a entry to check whether sent message or not. */
+	if ((newspacq = key_getspacq(&sp->spidx)) != NULL) {
+		if (key_blockacq_count < newspacq->count) {
+			/* reset counter and do send message. */
+			newspacq->count = 0;
+		} else {
+			/* increment counter and do nothing. */
+			newspacq->count++;
+			return 0;
+		}
+	} else {
+		/* make new entry for blocking to send SADB_ACQUIRE. */
+		if ((newspacq = key_newspacq(&sp->spidx)) == NULL)
+			return ENOBUFS;
+
+		/* add to acqtree */
+		LIST_INSERT_HEAD(&spacqtree, newspacq, chain);
+	}
+
+	/* create new sadb_msg to reply. */
+	m = key_setsadbmsg(SADB_X_SPDACQUIRE, 0, 0, 0, 0, 0);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	result = m;
+
+	result->m_pkthdr.len = 0;
+	for (m = result; m; m = m->m_next)
+		result->m_pkthdr.len += m->m_len;
+
+	mtod(result, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(result->m_pkthdr.len);
+
+	return key_sendup_mbuf(NULL, m, KEY_SENDUP_REGISTERED);
+
+fail:
+	if (result)
+		m_freem(result);
+	return error;
+}
+
+/*
+ * SADB_SPDFLUSH processing
+ * receive
+ *   <base>
+ * from the user, and free all entries in secpctree.
+ * and send,
+ *   <base>
+ * to the user.
+ * NOTE: what to do is only marking SADB_SASTATE_DEAD.
+ *
+ * m will always be freed.
+ */
+static int
+key_spdflush(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_msg *newmsg;
+	struct secpolicy *sp;
+	u_int dir;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_spdflush: NULL pointer is passed.\n");
+
+	if (m->m_len != PFKEY_ALIGN8(sizeof(struct sadb_msg)))
+		return key_senderror(so, m, EINVAL);
+
+	for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
+		LIST_FOREACH(sp, &sptree[dir], chain) {
+			sp->state = IPSEC_SPSTATE_DEAD;
+		}
+	}
+
+	/* invalidate all cached SPD pointers on pcb */
+	ipsec_invalpcbcacheall();
+
+	if (sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
+		ipseclog((LOG_DEBUG, "key_spdflush: No more memory.\n"));
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	if (m->m_next)
+		m_freem(m->m_next);
+	m->m_next = NULL;
+	m->m_pkthdr.len = m->m_len = PFKEY_ALIGN8(sizeof(struct sadb_msg));
+	newmsg = mtod(m, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len);
+
+	return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
+}
+
+/*
+ * SADB_SPDDUMP processing
+ * receive
+ *   <base>
+ * from the user, and dump all SP leaves
+ * and send,
+ *   <base> .....
+ * to the ikmpd.
+ *
+ * m will always be freed.
+ */
+static int
+key_spddump(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct secpolicy *sp;
+	int cnt;
+	u_int dir;
+	struct mbuf *n;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_spddump: NULL pointer is passed.\n");
+
+	/* search SPD entry and get buffer size. */
+	cnt = 0;
+	for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
+		LIST_FOREACH(sp, &sptree[dir], chain) {
+			cnt++;
+		}
+	}
+
+	if (cnt == 0)
+		return key_senderror(so, m, ENOENT);
+
+	for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
+		LIST_FOREACH(sp, &sptree[dir], chain) {
+			--cnt;
+			n = key_setdumpsp(sp, SADB_X_SPDDUMP, cnt,
+			    mhp->msg->sadb_msg_pid);
+
+			if (n)
+				key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+		}
+	}
+
+	m_freem(m);
+	return 0;
+}
+
+static struct mbuf *
+key_setdumpsp(sp, type, seq, pid)
+	struct secpolicy *sp;
+	u_int8_t type;
+	u_int32_t seq, pid;
+{
+	struct mbuf *result = NULL, *m;
+
+	m = key_setsadbmsg(type, 0, SADB_SATYPE_UNSPEC, seq, pid, sp->refcnt);
+	if (!m)
+		goto fail;
+	result = m;
+
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
+	    (struct sockaddr *)&sp->spidx.src, sp->spidx.prefs,
+	    sp->spidx.ul_proto);
+	if (!m)
+		goto fail;
+	m_cat(result, m);
+
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
+	    (struct sockaddr *)&sp->spidx.dst, sp->spidx.prefd,
+	    sp->spidx.ul_proto);
+	if (!m)
+		goto fail;
+	m_cat(result, m);
+
+	m = key_sp2msg(sp);
+	if (!m)
+		goto fail;
+	m_cat(result, m);
+
+	m = key_setsadblifetime(SADB_EXT_LIFETIME_CURRENT,
+		0, 0, (u_int64_t)sp->created, (u_int64_t)sp->lastused);
+	if (!m)
+		goto fail;
+	m_cat(result, m);
+
+	m = key_setsadblifetime(SADB_EXT_LIFETIME_HARD,
+		0, 0, (u_int64_t)sp->lifetime, (u_int64_t)sp->validtime);
+	if (!m)
+		goto fail;
+	m_cat(result, m);
+
+	if ((result->m_flags & M_PKTHDR) == 0)
+		goto fail;
+
+	if (result->m_len < sizeof(struct sadb_msg)) {
+		result = m_pullup(result, sizeof(struct sadb_msg));
+		if (result == NULL)
+			goto fail;
+	}
+
+	result->m_pkthdr.len = 0;
+	for (m = result; m; m = m->m_next)
+		result->m_pkthdr.len += m->m_len;
+
+	mtod(result, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(result->m_pkthdr.len);
+
+	return result;
+
+fail:
+	m_freem(result);
+	return NULL;
+}
+
+/*
+ * get PFKEY message length for security policy and request.
+ */
+static u_int
+key_getspreqmsglen(sp)
+	struct secpolicy *sp;
+{
+	u_int tlen;
+
+	tlen = sizeof(struct sadb_x_policy);
+
+	/* if is the policy for ipsec ? */
+	if (sp->policy != IPSEC_POLICY_IPSEC)
+		return tlen;
+
+	/* get length of ipsec requests */
+    {
+	struct ipsecrequest *isr;
+	int len;
+
+	for (isr = sp->req; isr != NULL; isr = isr->next) {
+		len = sizeof(struct sadb_x_ipsecrequest)
+			+ isr->saidx.src.ss_len
+			+ isr->saidx.dst.ss_len;
+
+		tlen += PFKEY_ALIGN8(len);
+	}
+    }
+
+	return tlen;
+}
+
+/*
+ * SADB_SPDEXPIRE processing
+ * send
+ *   <base, address(SD), lifetime(CH), policy>
+ * to KMD by PF_KEY.
+ *
+ * OUT:	0	: succeed
+ *	others	: error number
+ */
+static int
+key_spdexpire(sp)
+	struct secpolicy *sp;
+{
+	int s;
+	struct mbuf *result = NULL, *m;
+	int len;
+	int error = -1;
+	struct sadb_lifetime *lt;
+
+	/* XXX: Why do we lock ? */
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+
+	/* sanity check */
+	if (sp == NULL)
+		panic("key_spdexpire: NULL pointer is passed.\n");
+
+	/* set msg header */
+	m = key_setsadbmsg(SADB_X_SPDEXPIRE, 0, 0, 0, 0, 0);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	result = m;
+
+	/* create lifetime extension (current and hard) */
+	len = PFKEY_ALIGN8(sizeof(*lt)) * 2;
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		error = ENOBUFS;
+		goto fail;
+	}
+	bzero(mtod(m, caddr_t), len);
+	lt = mtod(m, struct sadb_lifetime *);
+	lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime));
+	lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
+	lt->sadb_lifetime_allocations = 0;
+	lt->sadb_lifetime_bytes = 0;
+	lt->sadb_lifetime_addtime = sp->created;
+	lt->sadb_lifetime_usetime = sp->lastused;
+	lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2);
+	lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime));
+	lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+	lt->sadb_lifetime_allocations = 0;
+	lt->sadb_lifetime_bytes = 0;
+	lt->sadb_lifetime_addtime = sp->lifetime;
+	lt->sadb_lifetime_usetime = sp->validtime;
+	m_cat(result, m);
+
+	/* set sadb_address for source */
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
+	    (struct sockaddr *)&sp->spidx.src,
+	    sp->spidx.prefs, sp->spidx.ul_proto);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	/* set sadb_address for destination */
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
+	    (struct sockaddr *)&sp->spidx.dst,
+	    sp->spidx.prefd, sp->spidx.ul_proto);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	/* set secpolicy */
+	m = key_sp2msg(sp);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	if ((result->m_flags & M_PKTHDR) == 0) {
+		error = EINVAL;
+		goto fail;
+	}
+
+	if (result->m_len < sizeof(struct sadb_msg)) {
+		result = m_pullup(result, sizeof(struct sadb_msg));
+		if (result == NULL) {
+			error = ENOBUFS;
+			goto fail;
+		}
+	}
+
+	result->m_pkthdr.len = 0;
+	for (m = result; m; m = m->m_next)
+		result->m_pkthdr.len += m->m_len;
+
+	mtod(result, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(result->m_pkthdr.len);
+
+	return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED);
+
+ fail:
+	if (result)
+		m_freem(result);
+	splx(s);
+	return error;
+}
+
+/* %%% SAD management */
+/*
+ * allocating a memory for new SA head, and copy from the values of mhp.
+ * OUT:	NULL	: failure due to the lack of memory.
+ *	others	: pointer to new SA head.
+ */
+static struct secashead *
+key_newsah(saidx)
+	struct secasindex *saidx;
+{
+	struct secashead *newsah;
+
+	/* sanity check */
+	if (saidx == NULL)
+		panic("key_newsaidx: NULL pointer is passed.\n");
+
+	newsah = keydb_newsecashead();
+	if (newsah == NULL)
+		return NULL;
+
+	bcopy(saidx, &newsah->saidx, sizeof(newsah->saidx));
+
+	/* add to saidxtree */
+	newsah->state = SADB_SASTATE_MATURE;
+	LIST_INSERT_HEAD(&sahtree, newsah, chain);
+
+	return(newsah);
+}
+
+/*
+ * delete SA index and all SA registerd.
+ */
+static void
+key_delsah(sah)
+	struct secashead *sah;
+{
+	struct secasvar *sav, *nextsav;
+	u_int stateidx, state;
+	int s;
+	int zombie = 0;
+
+	/* sanity check */
+	if (sah == NULL)
+		panic("key_delsah: NULL pointer is passed.\n");
+
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+
+	/* searching all SA registerd in the secindex. */
+	for (stateidx = 0;
+	     stateidx < _ARRAYLEN(saorder_state_any);
+	     stateidx++) {
+
+		state = saorder_state_any[stateidx];
+		for (sav = (struct secasvar *)LIST_FIRST(&sah->savtree[state]);
+		     sav != NULL;
+		     sav = nextsav) {
+
+			nextsav = LIST_NEXT(sav, chain);
+
+			if (sav->refcnt > 0) {
+				/* give up to delete this sa */
+				zombie++;
+				continue;
+			}
+
+			/* sanity check */
+			KEY_CHKSASTATE(state, sav->state, "key_delsah");
+
+			key_freesav(sav);
+
+			/* remove back pointer */
+			sav->sah = NULL;
+			sav = NULL;
+		}
+	}
+
+	/* don't delete sah only if there are savs. */
+	if (zombie) {
+		splx(s);
+		return;
+	}
+
+	if (sah->sa_route.ro_rt) {
+		RTFREE(sah->sa_route.ro_rt);
+		sah->sa_route.ro_rt = (struct rtentry *)NULL;
+	}
+
+	/* remove from tree of SA index */
+	if (__LIST_CHAINED(sah))
+		LIST_REMOVE(sah, chain);
+
+	KFREE(sah);
+
+	splx(s);
+	return;
+}
+
+/*
+ * allocating a new SA with LARVAL state.  key_add() and key_getspi() call,
+ * and copy the values of mhp into new buffer.
+ * When SAD message type is GETSPI:
+ *	to set sequence number from acq_seq++,
+ *	to set zero to SPI.
+ *	not to call key_setsava().
+ * OUT:	NULL	: fail
+ *	others	: pointer to new secasvar.
+ *
+ * does not modify mbuf.  does not free mbuf on error.
+ */
+static struct secasvar *
+key_newsav(m, mhp, sah, errp)
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+	struct secashead *sah;
+	int *errp;
+{
+	struct secasvar *newsav;
+	const struct sadb_sa *xsa;
+
+	/* sanity check */
+	if (m == NULL || mhp == NULL || mhp->msg == NULL || sah == NULL)
+		panic("key_newsa: NULL pointer is passed.\n");
+
+	KMALLOC(newsav, struct secasvar *, sizeof(struct secasvar));
+	if (newsav == NULL) {
+		ipseclog((LOG_DEBUG, "key_newsa: No more memory.\n"));
+		*errp = ENOBUFS;
+		return NULL;
+	}
+	bzero((caddr_t)newsav, sizeof(struct secasvar));
+
+	switch (mhp->msg->sadb_msg_type) {
+	case SADB_GETSPI:
+		newsav->spi = 0;
+
+#ifdef IPSEC_DOSEQCHECK
+		/* sync sequence number */
+		if (mhp->msg->sadb_msg_seq == 0)
+			newsav->seq =
+				(acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq));
+		else
+#endif
+			newsav->seq = mhp->msg->sadb_msg_seq;
+		break;
+
+	case SADB_ADD:
+		/* sanity check */
+		if (mhp->ext[SADB_EXT_SA] == NULL) {
+			KFREE(newsav);
+			ipseclog((LOG_DEBUG, "key_newsa: invalid message is passed.\n"));
+			*errp = EINVAL;
+			return NULL;
+		}
+		xsa = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+		newsav->spi = xsa->sadb_sa_spi;
+		newsav->seq = mhp->msg->sadb_msg_seq;
+		break;
+	default:
+		KFREE(newsav);
+		*errp = EINVAL;
+		return NULL;
+	}
+
+	/* copy sav values */
+	if (mhp->msg->sadb_msg_type != SADB_GETSPI) {
+		*errp = key_setsaval(newsav, m, mhp);
+		if (*errp) {
+			KFREE(newsav);
+			return NULL;
+		}
+	}
+
+	/* reset created */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	newsav->created = time_second;
+#else
+	newsav->created = time.tv_sec;
+#endif
+
+	newsav->pid = mhp->msg->sadb_msg_pid;
+
+	/* add to satree */
+	newsav->sah = sah;
+	newsav->refcnt = 1;
+	newsav->state = SADB_SASTATE_LARVAL;
+	LIST_INSERT_TAIL(&sah->savtree[SADB_SASTATE_LARVAL], newsav,
+			secasvar, chain);
+
+	return newsav;
+}
+
+/*
+ * free() SA variable entry.
+ */
+static void
+key_delsav(sav)
+	struct secasvar *sav;
+{
+	/* sanity check */
+	if (sav == NULL)
+		panic("key_delsav: NULL pointer is passed.\n");
+
+	if (sav->refcnt > 0)
+		return;		/* can't free */
+
+	/* remove from SA header */
+	if (__LIST_CHAINED(sav))
+		LIST_REMOVE(sav, chain);
+
+	if (sav->key_auth != NULL) {
+		bzero(_KEYBUF(sav->key_auth), _KEYLEN(sav->key_auth));
+		KFREE(sav->key_auth);
+		sav->key_auth = NULL;
+	}
+	if (sav->key_enc != NULL) {
+		bzero(_KEYBUF(sav->key_enc), _KEYLEN(sav->key_enc));
+		KFREE(sav->key_enc);
+		sav->key_enc = NULL;
+	}
+	if (sav->sched) {
+		bzero(sav->sched, sav->schedlen);
+		KFREE(sav->sched);
+		sav->sched = NULL;
+	}
+	if (sav->replay != NULL) {
+		keydb_delsecreplay(sav->replay);
+		sav->replay = NULL;
+	}
+	if (sav->lft_c != NULL) {
+		KFREE(sav->lft_c);
+		sav->lft_c = NULL;
+	}
+	if (sav->lft_h != NULL) {
+		KFREE(sav->lft_h);
+		sav->lft_h = NULL;
+	}
+	if (sav->lft_s != NULL) {
+		KFREE(sav->lft_s);
+		sav->lft_s = NULL;
+	}
+	if (sav->iv != NULL) {
+		KFREE(sav->iv);
+		sav->iv = NULL;
+	}
+
+	KFREE(sav);
+
+	return;
+}
+
+/*
+ * search SAD.
+ * OUT:
+ *	NULL	: not found
+ *	others	: found, pointer to a SA.
+ */
+static struct secashead *
+key_getsah(saidx)
+	struct secasindex *saidx;
+{
+	struct secashead *sah;
+
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (sah->state == SADB_SASTATE_DEAD)
+			continue;
+		if (key_cmpsaidx(&sah->saidx, saidx, CMP_REQID))
+			return sah;
+	}
+
+	return NULL;
+}
+
+/*
+ * check not to be duplicated SPI.
+ * NOTE: this function is too slow due to searching all SAD.
+ * OUT:
+ *	NULL	: not found
+ *	others	: found, pointer to a SA.
+ */
+static struct secasvar *
+key_checkspidup(saidx, spi)
+	struct secasindex *saidx;
+	u_int32_t spi;
+{
+	struct secashead *sah;
+	struct secasvar *sav;
+
+	/* check address family */
+	if (saidx->src.ss_family != saidx->dst.ss_family) {
+		ipseclog((LOG_DEBUG, "key_checkspidup: address family mismatched.\n"));
+		return NULL;
+	}
+
+	/* check all SAD */
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (!key_ismyaddr((struct sockaddr *)&sah->saidx.dst))
+			continue;
+		sav = key_getsavbyspi(sah, spi);
+		if (sav != NULL)
+			return sav;
+	}
+
+	return NULL;
+}
+
+/*
+ * search SAD litmited alive SA, protocol, SPI.
+ * OUT:
+ *	NULL	: not found
+ *	others	: found, pointer to a SA.
+ */
+static struct secasvar *
+key_getsavbyspi(sah, spi)
+	struct secashead *sah;
+	u_int32_t spi;
+{
+	struct secasvar *sav;
+	u_int stateidx, state;
+
+	/* search all status */
+	for (stateidx = 0;
+	     stateidx < _ARRAYLEN(saorder_state_alive);
+	     stateidx++) {
+
+		state = saorder_state_alive[stateidx];
+		LIST_FOREACH(sav, &sah->savtree[state], chain) {
+
+			/* sanity check */
+			if (sav->state != state) {
+				ipseclog((LOG_DEBUG, "key_getsavbyspi: "
+				    "invalid sav->state (queue: %d SA: %d)\n",
+				    state, sav->state));
+				continue;
+			}
+
+			if (sav->spi == spi)
+				return sav;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * copy SA values from PF_KEY message except *SPI, SEQ, PID, STATE and TYPE*.
+ * You must update these if need.
+ * OUT:	0:	success.
+ *	!0:	failure.
+ *
+ * does not modify mbuf.  does not free mbuf on error.
+ */
+static int
+key_setsaval(sav, m, mhp)
+	struct secasvar *sav;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+#ifdef IPSEC_ESP
+	const struct esp_algorithm *algo;
+#endif
+	int error = 0;
+
+	/* sanity check */
+	if (m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_setsaval: NULL pointer is passed.\n");
+
+	/* initialization */
+	sav->replay = NULL;
+	sav->key_auth = NULL;
+	sav->key_enc = NULL;
+	sav->sched = NULL;
+	sav->schedlen = 0;
+	sav->iv = NULL;
+	sav->lft_c = NULL;
+	sav->lft_h = NULL;
+	sav->lft_s = NULL;
+
+	/* SA */
+	if (mhp->ext[SADB_EXT_SA] != NULL) {
+		const struct sadb_sa *sa0;
+
+		sa0 = (const struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+		if (mhp->extlen[SADB_EXT_SA] < sizeof(*sa0)) {
+			error = EINVAL;
+			goto fail;
+		}
+
+		sav->alg_auth = sa0->sadb_sa_auth;
+		sav->alg_enc = sa0->sadb_sa_encrypt;
+		sav->flags = sa0->sadb_sa_flags;
+
+		/* replay window */
+		if ((sa0->sadb_sa_flags & SADB_X_EXT_OLD) == 0) {
+			sav->replay = keydb_newsecreplay(sa0->sadb_sa_replay);
+			if (sav->replay == NULL) {
+				ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+				error = ENOBUFS;
+				goto fail;
+			}
+		}
+	}
+
+	/* Authentication keys */
+	if (mhp->ext[SADB_EXT_KEY_AUTH] != NULL) {
+		const struct sadb_key *key0;
+		int len;
+
+		key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_AUTH];
+		len = mhp->extlen[SADB_EXT_KEY_AUTH];
+
+		error = 0;
+		if (len < sizeof(*key0)) {
+			error = EINVAL;
+			goto fail;
+		}
+		switch (mhp->msg->sadb_msg_satype) {
+		case SADB_SATYPE_AH:
+		case SADB_SATYPE_ESP:
+			if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) &&
+			    sav->alg_auth != SADB_X_AALG_NULL)
+				error = EINVAL;
+			break;
+		case SADB_X_SATYPE_IPCOMP:
+		default:
+			error = EINVAL;
+			break;
+		}
+		if (error) {
+			ipseclog((LOG_DEBUG, "key_setsaval: invalid key_auth values.\n"));
+			goto fail;
+		}
+
+		sav->key_auth = (struct sadb_key *)key_newbuf(key0, len);
+		if (sav->key_auth == NULL) {
+			ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+			error = ENOBUFS;
+			goto fail;
+		}
+	}
+
+	/* Encryption key */
+	if (mhp->ext[SADB_EXT_KEY_ENCRYPT] != NULL) {
+		const struct sadb_key *key0;
+		int len;
+
+		key0 = (const struct sadb_key *)mhp->ext[SADB_EXT_KEY_ENCRYPT];
+		len = mhp->extlen[SADB_EXT_KEY_ENCRYPT];
+
+		error = 0;
+		if (len < sizeof(*key0)) {
+			error = EINVAL;
+			goto fail;
+		}
+		switch (mhp->msg->sadb_msg_satype) {
+		case SADB_SATYPE_ESP:
+			if (len == PFKEY_ALIGN8(sizeof(struct sadb_key)) &&
+			    sav->alg_enc != SADB_EALG_NULL) {
+				error = EINVAL;
+				break;
+			}
+			sav->key_enc = (struct sadb_key *)key_newbuf(key0, len);
+			if (sav->key_enc == NULL) {
+				ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+				error = ENOBUFS;
+				goto fail;
+			}
+			break;
+		case SADB_X_SATYPE_IPCOMP:
+			if (len != PFKEY_ALIGN8(sizeof(struct sadb_key)))
+				error = EINVAL;
+			sav->key_enc = NULL;	/*just in case*/
+			break;
+		case SADB_SATYPE_AH:
+		default:
+			error = EINVAL;
+			break;
+		}
+		if (error) {
+			ipseclog((LOG_DEBUG, "key_setsatval: invalid key_enc value.\n"));
+			goto fail;
+		}
+	}
+
+	/* set iv */
+	sav->ivlen = 0;
+
+	switch (mhp->msg->sadb_msg_satype) {
+	case SADB_SATYPE_ESP:
+#ifdef IPSEC_ESP
+		algo = esp_algorithm_lookup(sav->alg_enc);
+		if (algo && algo->ivlen)
+			sav->ivlen = (*algo->ivlen)(algo, sav);
+		if (sav->ivlen == 0)
+			break;
+		KMALLOC(sav->iv, caddr_t, sav->ivlen);
+		if (sav->iv == 0) {
+			ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+			error = ENOBUFS;
+			goto fail;
+		}
+
+		/* initialize */
+		key_randomfill(sav->iv, sav->ivlen);
+#endif
+		break;
+	case SADB_SATYPE_AH:
+	case SADB_X_SATYPE_IPCOMP:
+		break;
+	default:
+		ipseclog((LOG_DEBUG, "key_setsaval: invalid SA type.\n"));
+		error = EINVAL;
+		goto fail;
+	}
+
+	/* reset created */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	sav->created = time_second;
+#else
+	sav->created = time.tv_sec;
+#endif
+
+	/* make lifetime for CURRENT */
+	KMALLOC(sav->lft_c, struct sadb_lifetime *,
+	    sizeof(struct sadb_lifetime));
+	if (sav->lft_c == NULL) {
+		ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+		error = ENOBUFS;
+		goto fail;
+	}
+
+	sav->lft_c->sadb_lifetime_len =
+	    PFKEY_UNIT64(sizeof(struct sadb_lifetime));
+	sav->lft_c->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
+	sav->lft_c->sadb_lifetime_allocations = 0;
+	sav->lft_c->sadb_lifetime_bytes = 0;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	sav->lft_c->sadb_lifetime_addtime = time_second;
+#else
+	sav->lft_c->sadb_lifetime_addtime = time.tv_sec;
+#endif
+	sav->lft_c->sadb_lifetime_usetime = 0;
+
+	/* lifetimes for HARD and SOFT */
+    {
+	const struct sadb_lifetime *lft0;
+
+	lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_HARD];
+	if (lft0 != NULL) {
+		if (mhp->extlen[SADB_EXT_LIFETIME_HARD] < sizeof(*lft0)) {
+			error = EINVAL;
+			goto fail;
+		}
+		sav->lft_h = (struct sadb_lifetime *)key_newbuf(lft0,
+		    sizeof(*lft0));
+		if (sav->lft_h == NULL) {
+			ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+			error = ENOBUFS;
+			goto fail;
+		}
+		/* we no longer support byte lifetime */
+		if (sav->lft_h->sadb_lifetime_bytes) {
+			error = EINVAL;
+			goto fail;
+		}
+		/* initialize? */
+	}
+
+	lft0 = (struct sadb_lifetime *)mhp->ext[SADB_EXT_LIFETIME_SOFT];
+	if (lft0 != NULL) {
+		if (mhp->extlen[SADB_EXT_LIFETIME_SOFT] < sizeof(*lft0)) {
+			error = EINVAL;
+			goto fail;
+		}
+		sav->lft_s = (struct sadb_lifetime *)key_newbuf(lft0,
+		    sizeof(*lft0));
+		if (sav->lft_s == NULL) {
+			ipseclog((LOG_DEBUG, "key_setsaval: No more memory.\n"));
+			error = ENOBUFS;
+			goto fail;
+		}
+		/* we no longer support byte lifetime */
+		if (sav->lft_s->sadb_lifetime_bytes) {
+			error = EINVAL;
+			goto fail;
+		}
+		/* initialize? */
+	}
+    }
+
+	return 0;
+
+ fail:
+	/* initialization */
+	if (sav->replay != NULL) {
+		keydb_delsecreplay(sav->replay);
+		sav->replay = NULL;
+	}
+	if (sav->key_auth != NULL) {
+		KFREE(sav->key_auth);
+		sav->key_auth = NULL;
+	}
+	if (sav->key_enc != NULL) {
+		KFREE(sav->key_enc);
+		sav->key_enc = NULL;
+	}
+	if (sav->sched) {
+		KFREE(sav->sched);
+		sav->sched = NULL;
+	}
+	if (sav->iv != NULL) {
+		KFREE(sav->iv);
+		sav->iv = NULL;
+	}
+	if (sav->lft_c != NULL) {
+		KFREE(sav->lft_c);
+		sav->lft_c = NULL;
+	}
+	if (sav->lft_h != NULL) {
+		KFREE(sav->lft_h);
+		sav->lft_h = NULL;
+	}
+	if (sav->lft_s != NULL) {
+		KFREE(sav->lft_s);
+		sav->lft_s = NULL;
+	}
+
+	return error;
+}
+
+/*
+ * validation with a secasvar entry, and set SADB_SATYPE_MATURE.
+ * OUT:	0:	valid
+ *	other:	errno
+ */
+static int
+key_mature(sav)
+	struct secasvar *sav;
+{
+	int mature;
+	int checkmask = 0;	/* 2^0: ealg  2^1: aalg  2^2: calg */
+	int mustmask = 0;	/* 2^0: ealg  2^1: aalg  2^2: calg */
+
+	mature = 0;
+
+	/* check SPI value */
+	switch (sav->sah->saidx.proto) {
+	case IPPROTO_ESP:
+	case IPPROTO_AH:
+		if (ntohl(sav->spi) >= 0 && ntohl(sav->spi) <= 255) {
+			ipseclog((LOG_DEBUG,
+			    "key_mature: illegal range of SPI %u.\n",
+			    (u_int32_t)ntohl(sav->spi)));
+			return EINVAL;
+		}
+		break;
+	}
+
+	/* check satype */
+	switch (sav->sah->saidx.proto) {
+	case IPPROTO_ESP:
+		/* check flags */
+		if ((sav->flags & SADB_X_EXT_OLD)
+		 && (sav->flags & SADB_X_EXT_DERIV)) {
+			ipseclog((LOG_DEBUG, "key_mature: "
+			    "invalid flag (derived) given to old-esp.\n"));
+			return EINVAL;
+		}
+		if (sav->alg_auth == SADB_AALG_NONE)
+			checkmask = 1;
+		else
+			checkmask = 3;
+		mustmask = 1;
+		break;
+	case IPPROTO_AH:
+		/* check flags */
+		if (sav->flags & SADB_X_EXT_DERIV) {
+			ipseclog((LOG_DEBUG, "key_mature: "
+			    "invalid flag (derived) given to AH SA.\n"));
+			return EINVAL;
+		}
+		if (sav->alg_enc != SADB_EALG_NONE) {
+			ipseclog((LOG_DEBUG, "key_mature: "
+			    "protocol and algorithm mismated.\n"));
+			return(EINVAL);
+		}
+		checkmask = 2;
+		mustmask = 2;
+		break;
+	case IPPROTO_IPCOMP:
+		if (sav->alg_auth != SADB_AALG_NONE) {
+			ipseclog((LOG_DEBUG, "key_mature: "
+				"protocol and algorithm mismated.\n"));
+			return(EINVAL);
+		}
+		if ((sav->flags & SADB_X_EXT_RAWCPI) == 0
+		 && ntohl(sav->spi) >= 0x10000) {
+			ipseclog((LOG_DEBUG, "key_mature: invalid cpi for IPComp.\n"));
+			return(EINVAL);
+		}
+		checkmask = 4;
+		mustmask = 4;
+		break;
+	default:
+		ipseclog((LOG_DEBUG, "key_mature: Invalid satype.\n"));
+		return EPROTONOSUPPORT;
+	}
+
+	/* check authentication algorithm */
+	if ((checkmask & 2) != 0) {
+		const struct ah_algorithm *algo;
+		int keylen;
+
+		algo = ah_algorithm_lookup(sav->alg_auth);
+		if (!algo) {
+			ipseclog((LOG_DEBUG,"key_mature: "
+			    "unknown authentication algorithm.\n"));
+			return EINVAL;
+		}
+
+		/* algorithm-dependent check */
+		if (sav->key_auth)
+			keylen = sav->key_auth->sadb_key_bits;
+		else
+			keylen = 0;
+		if (keylen < algo->keymin || algo->keymax < keylen) {
+			ipseclog((LOG_DEBUG,
+			    "key_mature: invalid AH key length %d "
+			    "(%d-%d allowed)\n",
+			    keylen, algo->keymin, algo->keymax));
+			return EINVAL;
+		}
+
+		if (algo->mature) {
+			if ((*algo->mature)(sav)) {
+				/* message generated in per-algorithm function*/
+				return EINVAL;
+			} else
+				mature = SADB_SATYPE_AH;
+		}
+
+		if ((mustmask & 2) != 0 &&  mature != SADB_SATYPE_AH) {
+			ipseclog((LOG_DEBUG, "key_mature: no satisfy algorithm for AH\n"));
+			return EINVAL;
+		}
+	}
+
+	/* check encryption algorithm */
+	if ((checkmask & 1) != 0) {
+#ifdef IPSEC_ESP
+		const struct esp_algorithm *algo;
+		int keylen;
+
+		algo = esp_algorithm_lookup(sav->alg_enc);
+		if (!algo) {
+			ipseclog((LOG_DEBUG, "key_mature: unknown encryption algorithm.\n"));
+			return EINVAL;
+		}
+
+		/* algorithm-dependent check */
+		if (sav->key_enc)
+			keylen = sav->key_enc->sadb_key_bits;
+		else
+			keylen = 0;
+		if (keylen < algo->keymin || algo->keymax < keylen) {
+			ipseclog((LOG_DEBUG,
+			    "key_mature: invalid ESP key length %d "
+			    "(%d-%d allowed)\n",
+			    keylen, algo->keymin, algo->keymax));
+			return EINVAL;
+		}
+
+		if (algo->mature) {
+			if ((*algo->mature)(sav)) {
+				/* message generated in per-algorithm function*/
+				return EINVAL;
+			} else
+				mature = SADB_SATYPE_ESP;
+		}
+
+		if ((mustmask & 1) != 0 &&  mature != SADB_SATYPE_ESP) {
+			ipseclog((LOG_DEBUG, "key_mature: no satisfy algorithm for ESP\n"));
+			return EINVAL;
+		}
+#else /*IPSEC_ESP*/
+		ipseclog((LOG_DEBUG, "key_mature: ESP not supported in this configuration\n"));
+		return EINVAL;
+#endif
+	}
+
+	/* check compression algorithm */
+	if ((checkmask & 4) != 0) {
+		const struct ipcomp_algorithm *algo;
+
+		/* algorithm-dependent check */
+		algo = ipcomp_algorithm_lookup(sav->alg_enc);
+		if (!algo) {
+			ipseclog((LOG_DEBUG, "key_mature: unknown compression algorithm.\n"));
+			return EINVAL;
+		}
+	}
+
+	key_sa_chgstate(sav, SADB_SASTATE_MATURE);
+
+	return 0;
+}
+
+/*
+ * subroutine for SADB_GET and SADB_DUMP.
+ */
+static struct mbuf *
+key_setdumpsa(sav, type, satype, seq, pid)
+	struct secasvar *sav;
+	u_int8_t type, satype;
+	u_int32_t seq, pid;
+{
+	struct mbuf *result = NULL, *tres = NULL, *m;
+	int l = 0;
+	int i;
+	void *p;
+	int dumporder[] = {
+		SADB_EXT_SA, SADB_X_EXT_SA2,
+		SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT,
+		SADB_EXT_LIFETIME_CURRENT, SADB_EXT_ADDRESS_SRC,
+		SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH,
+		SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC,
+		SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY,
+	};
+
+	m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt);
+	if (m == NULL)
+		goto fail;
+	result = m;
+
+	for (i = sizeof(dumporder)/sizeof(dumporder[0]) - 1; i >= 0; i--) {
+		m = NULL;
+		p = NULL;
+		switch (dumporder[i]) {
+		case SADB_EXT_SA:
+			m = key_setsadbsa(sav);
+			if (!m)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_SA2:
+			m = key_setsadbxsa2(sav->sah->saidx.mode,
+					sav->replay ? sav->replay->count : 0,
+					sav->sah->saidx.reqid);
+			if (!m)
+				goto fail;
+			break;
+
+		case SADB_EXT_ADDRESS_SRC:
+			m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
+			    (struct sockaddr *)&sav->sah->saidx.src,
+			    FULLMASK, IPSEC_ULPROTO_ANY);
+			if (!m)
+				goto fail;
+			break;
+
+		case SADB_EXT_ADDRESS_DST:
+			m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
+			    (struct sockaddr *)&sav->sah->saidx.dst,
+			    FULLMASK, IPSEC_ULPROTO_ANY);
+			if (!m)
+				goto fail;
+			break;
+
+		case SADB_EXT_KEY_AUTH:
+			if (!sav->key_auth)
+				continue;
+			l = PFKEY_UNUNIT64(sav->key_auth->sadb_key_len);
+			p = sav->key_auth;
+			break;
+
+		case SADB_EXT_KEY_ENCRYPT:
+			if (!sav->key_enc)
+				continue;
+			l = PFKEY_UNUNIT64(sav->key_enc->sadb_key_len);
+			p = sav->key_enc;
+			break;
+
+		case SADB_EXT_LIFETIME_CURRENT:
+			if (!sav->lft_c)
+				continue;
+			l = PFKEY_UNUNIT64(((struct sadb_ext *)sav->lft_c)->sadb_ext_len);
+			p = sav->lft_c;
+			break;
+
+		case SADB_EXT_LIFETIME_HARD:
+			if (!sav->lft_h)
+				continue;
+			l = PFKEY_UNUNIT64(((struct sadb_ext *)sav->lft_h)->sadb_ext_len);
+			p = sav->lft_h;
+			break;
+
+		case SADB_EXT_LIFETIME_SOFT:
+			if (!sav->lft_s)
+				continue;
+			l = PFKEY_UNUNIT64(((struct sadb_ext *)sav->lft_s)->sadb_ext_len);
+			p = sav->lft_s;
+			break;
+
+		case SADB_EXT_ADDRESS_PROXY:
+		case SADB_EXT_IDENTITY_SRC:
+		case SADB_EXT_IDENTITY_DST:
+			/* XXX: should we brought from SPD ? */
+		case SADB_EXT_SENSITIVITY:
+		default:
+			continue;
+		}
+
+		if ((!m && !p) || (m && p))
+			goto fail;
+		if (p && tres) {
+			M_PREPEND(tres, l, M_DONTWAIT);
+			if (!tres)
+				goto fail;
+			bcopy(p, mtod(tres, caddr_t), l);
+			continue;
+		}
+		if (p) {
+			m = key_alloc_mbuf(l);
+			if (!m)
+				goto fail;
+			m_copyback(m, 0, l, p);
+		}
+
+		if (tres)
+			m_cat(m, tres);
+		tres = m;
+	}
+
+	m_cat(result, tres);
+
+	if (result->m_len < sizeof(struct sadb_msg)) {
+		result = m_pullup(result, sizeof(struct sadb_msg));
+		if (result == NULL)
+			goto fail;
+	}
+
+	result->m_pkthdr.len = 0;
+	for (m = result; m; m = m->m_next)
+		result->m_pkthdr.len += m->m_len;
+
+	mtod(result, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(result->m_pkthdr.len);
+
+	return result;
+
+fail:
+	m_freem(result);
+	m_freem(tres);
+	return NULL;
+}
+
+/*
+ * set data into sadb_msg.
+ */
+static struct mbuf *
+key_setsadbmsg(type, tlen, satype, seq, pid, reserved)
+	u_int8_t type, satype;
+	u_int16_t tlen;
+	u_int32_t seq;
+	pid_t pid;
+	u_int16_t reserved;
+{
+	struct mbuf *m;
+	struct sadb_msg *p;
+	int len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_msg));
+	if (len > MCLBYTES)
+		return NULL;
+	MGETHDR(m, M_DONTWAIT, MT_DATA);
+	if (m && len > MHLEN) {
+		MCLGET(m, M_DONTWAIT);
+		if ((m->m_flags & M_EXT) == 0) {
+			m_freem(m);
+			m = NULL;
+		}
+	}
+	if (!m)
+		return NULL;
+	m->m_pkthdr.len = m->m_len = len;
+	m->m_next = NULL;
+
+	p = mtod(m, struct sadb_msg *);
+
+	bzero(p, len);
+	p->sadb_msg_version = PF_KEY_V2;
+	p->sadb_msg_type = type;
+	p->sadb_msg_errno = 0;
+	p->sadb_msg_satype = satype;
+	p->sadb_msg_len = PFKEY_UNIT64(tlen);
+	p->sadb_msg_reserved = reserved;
+	p->sadb_msg_seq = seq;
+	p->sadb_msg_pid = (u_int32_t)pid;
+
+	return m;
+}
+
+/*
+ * copy secasvar data into sadb_address.
+ */
+static struct mbuf *
+key_setsadbsa(sav)
+	struct secasvar *sav;
+{
+	struct mbuf *m;
+	struct sadb_sa *p;
+	int len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_sa));
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_sa *);
+
+	bzero(p, len);
+	p->sadb_sa_len = PFKEY_UNIT64(len);
+	p->sadb_sa_exttype = SADB_EXT_SA;
+	p->sadb_sa_spi = sav->spi;
+	p->sadb_sa_replay = (sav->replay != NULL ? sav->replay->wsize : 0);
+	p->sadb_sa_state = sav->state;
+	p->sadb_sa_auth = sav->alg_auth;
+	p->sadb_sa_encrypt = sav->alg_enc;
+	p->sadb_sa_flags = sav->flags;
+
+	return m;
+}
+
+/*
+ * set data into sadb_address.
+ */
+static struct mbuf *
+key_setsadbaddr(exttype, saddr, prefixlen, ul_proto)
+	u_int16_t exttype;
+	struct sockaddr *saddr;
+	u_int8_t prefixlen;
+	u_int16_t ul_proto;
+{
+	struct mbuf *m;
+	struct sadb_address *p;
+	size_t len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_address)) +
+	    PFKEY_ALIGN8(saddr->sa_len);
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_address *);
+
+	bzero(p, len);
+	p->sadb_address_len = PFKEY_UNIT64(len);
+	p->sadb_address_exttype = exttype;
+	p->sadb_address_proto = ul_proto;
+	if (prefixlen == FULLMASK) {
+		switch (saddr->sa_family) {
+		case AF_INET:
+			prefixlen = sizeof(struct in_addr) << 3;
+			break;
+		case AF_INET6:
+			prefixlen = sizeof(struct in6_addr) << 3;
+			break;
+		default:
+			; /*XXX*/
+		}
+	}
+	p->sadb_address_prefixlen = prefixlen;
+	p->sadb_address_reserved = 0;
+
+	bcopy(saddr,
+	    mtod(m, caddr_t) + PFKEY_ALIGN8(sizeof(struct sadb_address)),
+	    saddr->sa_len);
+
+	return m;
+}
+
+#if 0
+/*
+ * set data into sadb_ident.
+ */
+static struct mbuf *
+key_setsadbident(exttype, idtype, string, stringlen, id)
+	u_int16_t exttype, idtype;
+	caddr_t string;
+	int stringlen;
+	u_int64_t id;
+{
+	struct mbuf *m;
+	struct sadb_ident *p;
+	size_t len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_ident)) + PFKEY_ALIGN8(stringlen);
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_ident *);
+
+	bzero(p, len);
+	p->sadb_ident_len = PFKEY_UNIT64(len);
+	p->sadb_ident_exttype = exttype;
+	p->sadb_ident_type = idtype;
+	p->sadb_ident_reserved = 0;
+	p->sadb_ident_id = id;
+
+	bcopy(string,
+	    mtod(m, caddr_t) + PFKEY_ALIGN8(sizeof(struct sadb_ident)),
+	    stringlen);
+
+	return m;
+}
+#endif
+
+/*
+ * set data into sadb_x_sa2.
+ */
+static struct mbuf *
+key_setsadbxsa2(mode, seq, reqid)
+	u_int8_t mode;
+	u_int32_t seq, reqid;
+{
+	struct mbuf *m;
+	struct sadb_x_sa2 *p;
+	size_t len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_sa2));
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_sa2 *);
+
+	bzero(p, len);
+	p->sadb_x_sa2_len = PFKEY_UNIT64(len);
+	p->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+	p->sadb_x_sa2_mode = mode;
+	p->sadb_x_sa2_reserved1 = 0;
+	p->sadb_x_sa2_reserved2 = 0;
+	p->sadb_x_sa2_sequence = seq;
+	p->sadb_x_sa2_reqid = reqid;
+
+	return m;
+}
+
+/*
+ * set data into sadb_lifetime
+ */
+static struct mbuf *
+key_setsadblifetime(type, alloc, bytes, addtime, usetime)
+	u_int16_t type;
+	u_int32_t alloc;
+	u_int64_t bytes, addtime, usetime;
+{
+	struct mbuf *m;
+	struct sadb_lifetime *p;
+	size_t len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_lifetime));
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_lifetime *);
+
+	bzero(p, len);
+	p->sadb_lifetime_len = PFKEY_UNIT64(len);
+	p->sadb_lifetime_exttype = type;
+	p->sadb_lifetime_allocations = alloc;
+	p->sadb_lifetime_bytes = bytes;
+	p->sadb_lifetime_addtime = addtime;
+	p->sadb_lifetime_usetime = usetime;
+
+	return m;
+}
+
+/*
+ * set data into sadb_x_policy
+ */
+static struct mbuf *
+key_setsadbxpolicy(type, dir, id)
+	u_int16_t type;
+	u_int8_t dir;
+	u_int32_t id;
+{
+	struct mbuf *m;
+	struct sadb_x_policy *p;
+	size_t len;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_policy));
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_policy *);
+
+	bzero(p, len);
+	p->sadb_x_policy_len = PFKEY_UNIT64(len);
+	p->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+	p->sadb_x_policy_type = type;
+	p->sadb_x_policy_dir = dir;
+	p->sadb_x_policy_id = id;
+
+	return m;
+}
+
+/* %%% utilities */
+/*
+ * copy a buffer into the new buffer allocated.
+ */
+static void *
+key_newbuf(src, len)
+	const void *src;
+	u_int len;
+{
+	caddr_t new;
+
+	KMALLOC(new, caddr_t, len);
+	if (new == NULL) {
+		ipseclog((LOG_DEBUG, "key_newbuf: No more memory.\n"));
+		return NULL;
+	}
+	bcopy(src, new, len);
+
+	return new;
+}
+
+/* compare my own address
+ * OUT:	1: true, i.e. my address.
+ *	0: false
+ */
+int
+key_ismyaddr(sa)
+	struct sockaddr *sa;
+{
+#ifdef INET
+	struct sockaddr_in *sin;
+	struct in_ifaddr *ia;
+#endif
+
+	/* sanity check */
+	if (sa == NULL)
+		panic("key_ismyaddr: NULL pointer is passed.\n");
+
+	switch (sa->sa_family) {
+#ifdef INET
+	case AF_INET:
+		sin = (struct sockaddr_in *)sa;
+#ifdef __NetBSD__
+		for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next)
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+		for (ia = in_ifaddrhead.tqh_first; ia;
+		     ia = ia->ia_link.tqe_next)
+#else
+		for (ia = in_ifaddr; ia; ia = ia->ia_next)
+#endif
+		{
+			if (sin->sin_family == ia->ia_addr.sin_family &&
+			    sin->sin_len == ia->ia_addr.sin_len &&
+			    sin->sin_addr.s_addr == ia->ia_addr.sin_addr.s_addr)
+			{
+				return 1;
+			}
+		}
+		break;
+#endif
+#ifdef INET6
+	case AF_INET6:
+		return key_ismyaddr6((struct sockaddr_in6 *)sa);
+#endif
+	}
+
+	return 0;
+}
+
+#ifdef INET6
+/*
+ * compare my own address for IPv6.
+ * 1: ours
+ * 0: other
+ * NOTE: derived ip6_input() in KAME. This is necessary to modify more.
+ */
+#include <netinet6/in6_var.h>
+
+static int
+key_ismyaddr6(sin6)
+	struct sockaddr_in6 *sin6;
+{
+	struct in6_ifaddr *ia;
+	struct in6_multi *in6m;
+
+	for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
+		if (key_sockaddrcmp((struct sockaddr *)&sin6,
+		    (struct sockaddr *)&ia->ia_addr, 0) == 0)
+			return 1;
+
+		/*
+		 * XXX Multicast
+		 * XXX why do we care about multlicast here while we don't care
+		 * about IPv4 multicast??
+		 * XXX scope
+		 */
+		in6m = NULL;
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+		IN6_LOOKUP_MULTI(sin6->sin6_addr, ia->ia_ifp, in6m);
+#else
+		for ((in6m) = ia->ia6_multiaddrs.lh_first;
+		     (in6m) != NULL &&
+		     !IN6_ARE_ADDR_EQUAL(&(in6m)->in6m_addr, &sin6->sin6_addr);
+		     (in6m) = in6m->in6m_entry.le_next)
+			continue;
+#endif
+		if (in6m)
+			return 1;
+	}
+
+	/* loopback, just for safety */
+	if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+		return 1;
+
+	return 0;
+}
+#endif /*INET6*/
+
+/*
+ * compare two secasindex structure.
+ * flag can specify to compare 2 saidxes.
+ * compare two secasindex structure without both mode and reqid.
+ * don't compare port.
+ * IN:  
+ *      saidx0: source, it can be in SAD.
+ *      saidx1: object.
+ * OUT: 
+ *      1 : equal
+ *      0 : not equal
+ */
+static int
+key_cmpsaidx(saidx0, saidx1, flag)
+	struct secasindex *saidx0, *saidx1;
+	int flag;
+{
+	/* sanity */
+	if (saidx0 == NULL && saidx1 == NULL)
+		return 1;
+
+	if (saidx0 == NULL || saidx1 == NULL)
+		return 0;
+
+	if (saidx0->proto != saidx1->proto)
+		return 0;
+
+	if (flag == CMP_EXACTLY) {
+		if (saidx0->mode != saidx1->mode)
+			return 0;
+		if (saidx0->reqid != saidx1->reqid)
+			return 0;
+		if (bcmp(&saidx0->src, &saidx1->src, saidx0->src.ss_len) != 0 ||
+		    bcmp(&saidx0->dst, &saidx1->dst, saidx0->dst.ss_len) != 0)
+			return 0;
+	} else {
+
+		/* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */
+		if (flag == CMP_MODE_REQID
+		  ||flag == CMP_REQID) {
+			/*
+			 * If reqid of SPD is non-zero, unique SA is required.
+			 * The result must be of same reqid in this case.
+			 */
+			if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid)
+				return 0;
+		}
+
+		if (flag == CMP_MODE_REQID) {
+			if (saidx0->mode != IPSEC_MODE_ANY
+			 && saidx0->mode != saidx1->mode)
+				return 0;
+		}
+
+		if (key_sockaddrcmp((struct sockaddr *)&saidx0->src,
+				(struct sockaddr *)&saidx1->src, 0) != 0) {
+			return 0;
+		}
+		if (key_sockaddrcmp((struct sockaddr *)&saidx0->dst,
+				(struct sockaddr *)&saidx1->dst, 0) != 0) {
+			return 0;
+		}
+	}
+
+	return 1;
+}
+
+/*
+ * compare two secindex structure exactly.
+ * IN:
+ *	spidx0: source, it is often in SPD.
+ *	spidx1: object, it is often from PFKEY message.
+ * OUT:
+ *	1 : equal
+ *	0 : not equal
+ */
+int
+key_cmpspidx_exactly(spidx0, spidx1)
+	struct secpolicyindex *spidx0, *spidx1;
+{
+	/* sanity */
+	if (spidx0 == NULL && spidx1 == NULL)
+		return 1;
+
+	if (spidx0 == NULL || spidx1 == NULL)
+		return 0;
+
+	if (spidx0->prefs != spidx1->prefs
+	 || spidx0->prefd != spidx1->prefd
+	 || spidx0->ul_proto != spidx1->ul_proto)
+		return 0;
+
+	if (key_sockaddrcmp((struct sockaddr *)&spidx0->src,
+	    (struct sockaddr *)&spidx1->src, 1) != 0) {
+		return 0;
+	}
+	if (key_sockaddrcmp((struct sockaddr *)&spidx0->dst,
+	    (struct sockaddr *)&spidx1->dst, 1) != 0) {
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * compare two secindex structure with mask.
+ * IN:
+ *	spidx0: source, it is often in SPD.
+ *	spidx1: object, it is often from IP header.
+ * OUT:
+ *	1 : equal
+ *	0 : not equal
+ */
+int
+key_cmpspidx_withmask(spidx0, spidx1)
+	struct secpolicyindex *spidx0, *spidx1;
+{
+	/* sanity */
+	if (spidx0 == NULL && spidx1 == NULL)
+		return 1;
+
+	if (spidx0 == NULL || spidx1 == NULL)
+		return 0;
+
+	if (spidx0->src.ss_family != spidx1->src.ss_family ||
+	    spidx0->dst.ss_family != spidx1->dst.ss_family ||
+	    spidx0->src.ss_len != spidx1->src.ss_len ||
+	    spidx0->dst.ss_len != spidx1->dst.ss_len)
+		return 0;
+
+	/* if spidx.ul_proto == IPSEC_ULPROTO_ANY, ignore. */
+	if (spidx0->ul_proto != (u_int16_t)IPSEC_ULPROTO_ANY
+	 && spidx0->ul_proto != spidx1->ul_proto)
+		return 0;
+
+	switch (spidx0->src.ss_family) {
+	case AF_INET:
+		if (satosin(&spidx0->src)->sin_port != IPSEC_PORT_ANY
+		 && satosin(&spidx0->src)->sin_port !=
+		    satosin(&spidx1->src)->sin_port)
+			return 0;
+		if (!key_bbcmp((caddr_t)&satosin(&spidx0->src)->sin_addr,
+		    (caddr_t)&satosin(&spidx1->src)->sin_addr, spidx0->prefs))
+			return 0;
+		break;
+	case AF_INET6:
+		if (satosin6(&spidx0->src)->sin6_port != IPSEC_PORT_ANY
+		 && satosin6(&spidx0->src)->sin6_port !=
+		    satosin6(&spidx1->src)->sin6_port)
+			return 0;
+		/*
+		 * scope_id check. if sin6_scope_id is 0, we regard it
+		 * as a wildcard scope, which matches any scope zone ID. 
+		 */
+		if (satosin6(&spidx0->src)->sin6_scope_id &&
+		    satosin6(&spidx1->src)->sin6_scope_id &&
+		    satosin6(&spidx0->src)->sin6_scope_id !=
+		    satosin6(&spidx1->src)->sin6_scope_id)
+			return 0;
+		if (!key_bbcmp((caddr_t)&satosin6(&spidx0->src)->sin6_addr,
+		    (caddr_t)&satosin6(&spidx1->src)->sin6_addr, spidx0->prefs))
+			return 0;
+		break;
+	default:
+		/* XXX */
+		if (bcmp(&spidx0->src, &spidx1->src, spidx0->src.ss_len) != 0)
+			return 0;
+		break;
+	}
+
+	switch (spidx0->dst.ss_family) {
+	case AF_INET:
+		if (satosin(&spidx0->dst)->sin_port != IPSEC_PORT_ANY
+		 && satosin(&spidx0->dst)->sin_port !=
+		    satosin(&spidx1->dst)->sin_port)
+			return 0;
+		if (!key_bbcmp((caddr_t)&satosin(&spidx0->dst)->sin_addr,
+		    (caddr_t)&satosin(&spidx1->dst)->sin_addr, spidx0->prefd))
+			return 0;
+		break;
+	case AF_INET6:
+		if (satosin6(&spidx0->dst)->sin6_port != IPSEC_PORT_ANY
+		 && satosin6(&spidx0->dst)->sin6_port !=
+		    satosin6(&spidx1->dst)->sin6_port)
+			return 0;
+		/*
+		 * scope_id check. if sin6_scope_id is 0, we regard it
+		 * as a wildcard scope, which matches any scope zone ID. 
+		 */
+		if (satosin6(&spidx0->src)->sin6_scope_id &&
+		    satosin6(&spidx1->src)->sin6_scope_id &&
+		    satosin6(&spidx0->dst)->sin6_scope_id !=
+		    satosin6(&spidx1->dst)->sin6_scope_id)
+			return 0;
+		if (!key_bbcmp((caddr_t)&satosin6(&spidx0->dst)->sin6_addr,
+		    (caddr_t)&satosin6(&spidx1->dst)->sin6_addr, spidx0->prefd))
+			return 0;
+		break;
+	default:
+		/* XXX */
+		if (bcmp(&spidx0->dst, &spidx1->dst, spidx0->dst.ss_len) != 0)
+			return 0;
+		break;
+	}
+
+	/* XXX Do we check other field ?  e.g. flowinfo */
+
+	return 1;
+}
+
+/* returns 0 on match */
+static int
+key_sockaddrcmp(sa1, sa2, port)
+	struct sockaddr *sa1;
+	struct sockaddr *sa2;
+	int port;
+{
+	if (sa1->sa_family != sa2->sa_family || sa1->sa_len != sa2->sa_len)
+		return 1;
+
+	switch (sa1->sa_family) {
+	case AF_INET:
+		if (sa1->sa_len != sizeof(struct sockaddr_in))
+			return 1;
+		if (satosin(sa1)->sin_addr.s_addr !=
+		    satosin(sa2)->sin_addr.s_addr) {
+			return 1;
+		}
+		if (port && satosin(sa1)->sin_port != satosin(sa2)->sin_port)
+			return 1;
+		break;
+	case AF_INET6:
+		if (sa1->sa_len != sizeof(struct sockaddr_in6))
+			return 1;	/*EINVAL*/
+		if (satosin6(sa1)->sin6_scope_id !=
+		    satosin6(sa2)->sin6_scope_id) {
+			return 1;
+		}
+		if (!IN6_ARE_ADDR_EQUAL(&satosin6(sa1)->sin6_addr,
+		    &satosin6(sa2)->sin6_addr)) {
+			return 1;
+		}
+		if (port &&
+		    satosin6(sa1)->sin6_port != satosin6(sa2)->sin6_port) {
+			return 1;
+		}
+	default:
+		if (bcmp(sa1, sa2, sa1->sa_len) != 0)
+			return 1;
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * compare two buffers with mask.
+ * IN:
+ *	addr1: source
+ *	addr2: object
+ *	bits:  Number of bits to compare
+ * OUT:
+ *	1 : equal
+ *	0 : not equal
+ */
+static int
+key_bbcmp(p1, p2, bits)
+	caddr_t p1, p2;
+	u_int bits;
+{
+	u_int8_t mask;
+
+	/* XXX: This could be considerably faster if we compare a word
+	 * at a time, but it is complicated on LSB Endian machines */
+
+	/* Handle null pointers */
+	if (p1 == NULL || p2 == NULL)
+		return (p1 == p2);
+
+	while (bits >= 8) {
+		if (*p1++ != *p2++)
+			return 0;
+		bits -= 8;
+	}
+
+	if (bits > 0) {
+		mask = ~((1<<(8-bits))-1);
+		if ((*p1 & mask) != (*p2 & mask))
+			return 0;
+	}
+	return 1;	/* Match! */
+}
+
+/*
+ * time handler.
+ * scanning SPD and SAD to check status for each entries,
+ * and do to remove or to expire.
+ * XXX: year 2038 problem may remain.
+ */
+void
+key_timehandler(void)
+{
+	u_int dir;
+	int s;
+	struct timeval tv;
+
+#ifdef __FreeBSD__
+	microtime(&tv);
+#else
+	tv = time;
+#endif
+
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+
+	/* SPD */
+    {
+	struct secpolicy *sp, *nextsp;
+
+	for (dir = 0; dir < IPSEC_DIR_MAX; dir++) {
+		for (sp = LIST_FIRST(&sptree[dir]);
+		     sp != NULL;
+		     sp = nextsp) {
+
+			nextsp = LIST_NEXT(sp, chain);
+
+			if (sp->state == IPSEC_SPSTATE_DEAD) {
+				key_freesp(sp);
+				continue;
+			}
+
+			if (sp->lifetime == 0 && sp->validtime == 0)
+				continue;
+
+			/* the deletion will occur next time */
+			if ((sp->lifetime
+			  && tv.tv_sec - sp->created > sp->lifetime)
+			 || (sp->validtime
+			  && tv.tv_sec - sp->lastused > sp->validtime)) {
+				sp->state = IPSEC_SPSTATE_DEAD;
+				key_spdexpire(sp);
+				continue;
+			}
+		}
+	}
+    }
+
+	/* SAD */
+    {
+	struct secashead *sah, *nextsah;
+	struct secasvar *sav, *nextsav;
+
+	for (sah = LIST_FIRST(&sahtree);
+	     sah != NULL;
+	     sah = nextsah) {
+
+		nextsah = LIST_NEXT(sah, chain);
+
+		/* if sah has been dead, then delete it and process next sah. */
+		if (sah->state == SADB_SASTATE_DEAD) {
+			key_delsah(sah);
+			continue;
+		}
+
+		/* if LARVAL entry doesn't become MATURE, delete it. */
+		for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_LARVAL]);
+		     sav != NULL;
+		     sav = nextsav) {
+
+			nextsav = LIST_NEXT(sav, chain);
+
+			if (tv.tv_sec - sav->created > key_larval_lifetime) {
+				key_freesav(sav);
+			}
+		}
+
+		/*
+		 * check MATURE entry to start to send expire message
+		 * whether or not.
+		 */
+		for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_MATURE]);
+		     sav != NULL;
+		     sav = nextsav) {
+
+			nextsav = LIST_NEXT(sav, chain);
+
+			/* we don't need to check. */
+			if (sav->lft_s == NULL)
+				continue;
+
+			/* sanity check */
+			if (sav->lft_c == NULL) {
+				ipseclog((LOG_DEBUG, "key_timehandler: "
+					"There is no CURRENT time, why?\n"));
+				continue;
+			}
+
+			/* check SOFT lifetime */
+			if (sav->lft_s->sadb_lifetime_addtime != 0
+			 && tv.tv_sec - sav->created > sav->lft_s->sadb_lifetime_addtime) {
+				/*
+				 * check SA to be used whether or not.
+				 * when SA hasn't been used, delete it.
+				 */
+				if (sav->lft_c->sadb_lifetime_usetime == 0) {
+					key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+					key_freesav(sav);
+					sav = NULL;
+				} else {
+					key_sa_chgstate(sav, SADB_SASTATE_DYING);
+					/*
+					 * XXX If we keep to send expire
+					 * message in the status of
+					 * DYING. Do remove below code.
+					 */
+					key_expire(sav);
+				}
+			}
+			/* check SOFT lifetime by bytes */
+			/*
+			 * XXX I don't know the way to delete this SA
+			 * when new SA is installed.  Caution when it's
+			 * installed too big lifetime by time.
+			 */
+			else if (sav->lft_s->sadb_lifetime_bytes != 0
+			      && sav->lft_s->sadb_lifetime_bytes < sav->lft_c->sadb_lifetime_bytes) {
+
+				key_sa_chgstate(sav, SADB_SASTATE_DYING);
+				/*
+				 * XXX If we keep to send expire
+				 * message in the status of
+				 * DYING. Do remove below code.
+				 */
+				key_expire(sav);
+			}
+		}
+
+		/* check DYING entry to change status to DEAD. */
+		for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DYING]);
+		     sav != NULL;
+		     sav = nextsav) {
+
+			nextsav = LIST_NEXT(sav, chain);
+
+			/* we don't need to check. */
+			if (sav->lft_h == NULL)
+				continue;
+
+			/* sanity check */
+			if (sav->lft_c == NULL) {
+				ipseclog((LOG_DEBUG,"key_timehandler: "
+					"There is no CURRENT time, why?\n"));
+				continue;
+			}
+
+			if (sav->lft_h->sadb_lifetime_addtime != 0
+			 && tv.tv_sec - sav->created > sav->lft_h->sadb_lifetime_addtime) {
+				key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+				key_freesav(sav);
+				sav = NULL;
+			}
+#if 0	/* XXX Should we keep to send expire message until HARD lifetime ? */
+			else if (sav->lft_s != NULL
+			      && sav->lft_s->sadb_lifetime_addtime != 0
+			      && tv.tv_sec - sav->created > sav->lft_s->sadb_lifetime_addtime) {
+				/*
+				 * XXX: should be checked to be
+				 * installed the valid SA.
+				 */
+
+				/*
+				 * If there is no SA then sending
+				 * expire message.
+				 */
+				key_expire(sav);
+			}
+#endif
+			/* check HARD lifetime by bytes */
+			else if (sav->lft_h->sadb_lifetime_bytes != 0
+			      && sav->lft_h->sadb_lifetime_bytes < sav->lft_c->sadb_lifetime_bytes) {
+				key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+				key_freesav(sav);
+				sav = NULL;
+			}
+		}
+
+		/* delete entry in DEAD */
+		for (sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_DEAD]);
+		     sav != NULL;
+		     sav = nextsav) {
+
+			nextsav = LIST_NEXT(sav, chain);
+
+			/* sanity check */
+			if (sav->state != SADB_SASTATE_DEAD) {
+				ipseclog((LOG_DEBUG, "key_timehandler: "
+					"invalid sav->state "
+					"(queue: %d SA: %d): "
+					"kill it anyway\n",
+					SADB_SASTATE_DEAD, sav->state));
+			}
+
+			/*
+			 * do not call key_freesav() here.
+			 * sav should already be freed, and sav->refcnt
+			 * shows other references to sav
+			 * (such as from SPD).
+			 */
+		}
+	}
+    }
+
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+	/* ACQ tree */
+    {
+	struct secacq *acq, *nextacq;
+
+	for (acq = LIST_FIRST(&acqtree);
+	     acq != NULL;
+	     acq = nextacq) {
+
+		nextacq = LIST_NEXT(acq, chain);
+
+		if (tv.tv_sec - acq->created > key_blockacq_lifetime
+		 && __LIST_CHAINED(acq)) {
+			LIST_REMOVE(acq, chain);
+			KFREE(acq);
+		}
+	}
+    }
+#endif
+
+	/* SP ACQ tree */
+    {
+	struct secspacq *acq, *nextacq;
+
+	for (acq = LIST_FIRST(&spacqtree);
+	     acq != NULL;
+	     acq = nextacq) {
+
+		nextacq = LIST_NEXT(acq, chain);
+
+		if (tv.tv_sec - acq->created > key_blockacq_lifetime
+		 && __LIST_CHAINED(acq)) {
+			LIST_REMOVE(acq, chain);
+			KFREE(acq);
+		}
+	}
+    }
+
+	/* initialize random seed */
+	if (key_tick_init_random++ > key_int_random) {
+		key_tick_init_random = 0;
+		key_srandom();
+	}
+
+	/*
+	 * should set timeout based on the most closest timer expiration.
+	 * we don't bother to do that yet.
+	 */
+#ifdef __NetBSD__
+	callout_reset(&key_timehandler_ch, hz,
+	    (void *)key_timehandler, (void *)0);
+#else
+	(void)timeout((void *)key_timehandler, (void *)0, hz);
+#endif
+
+	splx(s);
+	return;
+}
+
+/*
+ * to initialize a seed for random()
+ */
+static void
+key_srandom()
+{
+	struct timeval tv;
+        int i;
+        
+	microtime(&tv);
+
+	for (i = (int)((tv.tv_sec ^ tv.tv_usec) & 0x3ff); i > 0; i--)
+		(void)random();
+	return;
+}
+
+u_long
+key_random()
+{
+	u_long value;
+
+	key_randomfill(&value, sizeof(value));
+	return value;
+}
+
+void
+key_randomfill(p, l)
+	void *p;
+	size_t l;
+{
+	size_t n;
+	u_long v;
+	static int warn = 1;
+
+	n = 0;
+#if defined(__NetBSD__) && NRND > 0
+	n = rnd_extract_data(p, l, RND_EXTRACT_ANY);
+#elif defined(__OpenBSD__)
+	get_random_bytes(p, l);
+	n = l;
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 4
+	n = (size_t)read_random_unlimited(p, (u_int)l);
+#endif
+	/* last resort */
+	while (n < l) {
+		v = random();
+		bcopy(&v, (u_int8_t *)p + n,
+		    l - n < sizeof(v) ? l - n : sizeof(v));
+		n += sizeof(v);
+
+		if (warn) {
+			printf("WARNING: pseudo-random number generator "
+			    "used for IPsec processing\n");
+			warn = 0;
+		}
+	}
+}
+
+/*
+ * map SADB_SATYPE_* to IPPROTO_*.
+ * if satype == SADB_SATYPE then satype is mapped to ~0.
+ * OUT:
+ *	0: invalid satype.
+ */
+static u_int16_t
+key_satype2proto(satype)
+	u_int8_t satype;
+{
+	switch (satype) {
+	case SADB_SATYPE_UNSPEC:
+		return IPSEC_PROTO_ANY;
+	case SADB_SATYPE_AH:
+		return IPPROTO_AH;
+	case SADB_SATYPE_ESP:
+		return IPPROTO_ESP;
+	case SADB_X_SATYPE_IPCOMP:
+		return IPPROTO_IPCOMP;
+		break;
+	default:
+		return 0;
+	}
+	/* NOTREACHED */
+}
+
+/*
+ * map IPPROTO_* to SADB_SATYPE_*
+ * OUT:
+ *	0: invalid protocol type.
+ */
+static u_int8_t
+key_proto2satype(proto)
+	u_int16_t proto;
+{
+	switch (proto) {
+	case IPPROTO_AH:
+		return SADB_SATYPE_AH;
+	case IPPROTO_ESP:
+		return SADB_SATYPE_ESP;
+	case IPPROTO_IPCOMP:
+		return SADB_X_SATYPE_IPCOMP;
+		break;
+	default:
+		return 0;
+	}
+	/* NOTREACHED */
+}
+
+/* %%% PF_KEY */
+/*
+ * SADB_GETSPI processing is to receive
+ *	<base, (SA2), src address, dst address, (SPI range)>
+ * from the IKMPd, to assign a unique spi value, to hang on the INBOUND
+ * tree with the status of LARVAL, and send
+ *	<base, SA(*), address(SD)>
+ * to the IKMPd.
+ *
+ * IN:	mhp: pointer to the pointer to each header.
+ * OUT:	NULL if fail.
+ *	other if success, return pointer to the message to send.
+ */
+static int
+key_getspi(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *newsah;
+	struct secasvar *newsav;
+	u_int8_t proto;
+	u_int32_t spi;
+	u_int8_t mode;
+	u_int32_t reqid;
+	int error;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_getspi: NULL pointer is passed.\n");
+
+	if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) {
+		ipseclog((LOG_DEBUG, "key_getspi: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
+		ipseclog((LOG_DEBUG, "key_getspi: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
+		mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+		reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
+	} else {
+		mode = IPSEC_MODE_ANY;
+		reqid = 0;
+	}
+
+	src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
+	dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_getspi: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* make sure if port number is zero. */
+	switch (((struct sockaddr *)(src0 + 1))->sa_family) {
+	case AF_INET:
+		if (((struct sockaddr *)(src0 + 1))->sa_len !=
+		    sizeof(struct sockaddr_in))
+			return key_senderror(so, m, EINVAL);
+		((struct sockaddr_in *)(src0 + 1))->sin_port = 0;
+		break;
+	case AF_INET6:
+		if (((struct sockaddr *)(src0 + 1))->sa_len !=
+		    sizeof(struct sockaddr_in6))
+			return key_senderror(so, m, EINVAL);
+		((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0;
+		break;
+	default:
+		; /*???*/
+	}
+	switch (((struct sockaddr *)(dst0 + 1))->sa_family) {
+	case AF_INET:
+		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
+		    sizeof(struct sockaddr_in))
+			return key_senderror(so, m, EINVAL);
+		((struct sockaddr_in *)(dst0 + 1))->sin_port = 0;
+		break;
+	case AF_INET6:
+		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
+		    sizeof(struct sockaddr_in6))
+			return key_senderror(so, m, EINVAL);
+		((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0;
+		break;
+	default:
+		; /*???*/
+	}
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+
+	/* SPI allocation */
+	spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
+	                       &saidx);
+	if (spi == 0)
+		return key_senderror(so, m, EINVAL);
+
+	/* get a SA index */
+	if ((newsah = key_getsah(&saidx)) == NULL) {
+		/* create a new SA index */
+		if ((newsah = key_newsah(&saidx)) == NULL) {
+			ipseclog((LOG_DEBUG, "key_getspi: No more memory.\n"));
+			return key_senderror(so, m, ENOBUFS);
+		}
+	}
+
+	/* get a new SA */
+	/* XXX rewrite */
+	newsav = key_newsav(m, mhp, newsah, &error);
+	if (newsav == NULL) {
+		/* XXX don't free new SA index allocated in above. */
+		return key_senderror(so, m, error);
+	}
+
+	/* set spi */
+	newsav->spi = htonl(spi);
+
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+	/* delete the entry in acqtree */
+	if (mhp->msg->sadb_msg_seq != 0) {
+		struct secacq *acq;
+		if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) != NULL) {
+			/* reset counter in order to deletion by timehandler. */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+			acq->created = time_second;
+#else
+			acq->created = time.tv_sec;
+#endif
+			acq->count = 0;
+		}
+    	}
+#endif
+
+    {
+	struct mbuf *n, *nn;
+	struct sadb_sa *m_sa;
+	struct sadb_msg *newmsg;
+	int off, len;
+
+	/* create new sadb_msg to reply. */
+	len = PFKEY_ALIGN8(sizeof(struct sadb_msg)) +
+	    PFKEY_ALIGN8(sizeof(struct sadb_sa));
+	if (len > MCLBYTES)
+		return key_senderror(so, m, ENOBUFS);
+
+	MGETHDR(n, M_DONTWAIT, MT_DATA);
+	if (len > MHLEN) {
+		MCLGET(n, M_DONTWAIT);
+		if ((n->m_flags & M_EXT) == 0) {
+			m_freem(n);
+			n = NULL;
+		}
+	}
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	n->m_len = len;
+	n->m_next = NULL;
+	off = 0;
+
+	m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off);
+	off += PFKEY_ALIGN8(sizeof(struct sadb_msg));
+
+	m_sa = (struct sadb_sa *)(mtod(n, caddr_t) + off);
+	m_sa->sadb_sa_len = PFKEY_UNIT64(sizeof(struct sadb_sa));
+	m_sa->sadb_sa_exttype = SADB_EXT_SA;
+	m_sa->sadb_sa_spi = htonl(spi);
+	off += PFKEY_ALIGN8(sizeof(struct sadb_sa));
+
+#ifdef DIAGNOSTIC
+	if (off != len)
+		panic("length inconsistency in key_getspi");
+#endif
+
+	n->m_next = key_gather_mbuf(m, mhp, 0, 2, SADB_EXT_ADDRESS_SRC,
+	    SADB_EXT_ADDRESS_DST);
+	if (!n->m_next) {
+		m_freem(n);
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	if (n->m_len < sizeof(struct sadb_msg)) {
+		n = m_pullup(n, sizeof(struct sadb_msg));
+		if (n == NULL)
+			return key_sendup_mbuf(so, m, KEY_SENDUP_ONE);
+	}
+
+	n->m_pkthdr.len = 0;
+	for (nn = n; nn; nn = nn->m_next)
+		n->m_pkthdr.len += nn->m_len;
+
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_seq = newsav->seq;
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+    }
+}
+
+/*
+ * allocating new SPI
+ * called by key_getspi().
+ * OUT:
+ *	0:	failure.
+ *	others: success.
+ */
+static u_int32_t
+key_do_getnewspi(spirange, saidx)
+	struct sadb_spirange *spirange;
+	struct secasindex *saidx;
+{
+	u_int32_t newspi;
+	u_int32_t min, max;
+	int count = key_spi_trycnt;
+
+	/* set spi range to allocate */
+	if (spirange != NULL) {
+		min = spirange->sadb_spirange_min;
+		max = spirange->sadb_spirange_max;
+	} else {
+		min = key_spi_minval;
+		max = key_spi_maxval;
+	}
+	/* IPCOMP needs 2-byte SPI */
+	if (saidx->proto == IPPROTO_IPCOMP) {
+		u_int32_t t;
+		if (min >= 0x10000)
+			min = 0xffff;
+		if (max >= 0x10000)
+			max = 0xffff;
+		if (min > max) {
+			t = min; min = max; max = t;
+		}
+	}
+
+	if (min == max) {
+		if (key_checkspidup(saidx, min) != NULL) {
+			ipseclog((LOG_DEBUG, "key_do_getnewspi: SPI %u exists already.\n", min));
+			return 0;
+		}
+
+		count--; /* taking one cost. */
+		newspi = min;
+
+	} else {
+
+		/* init SPI */
+		newspi = 0;
+
+		/* when requesting to allocate spi ranged */
+		while (count--) {
+			/* generate pseudo-random SPI value ranged. */
+			newspi = min + (key_random() % (max - min + 1));
+
+			if (key_checkspidup(saidx, newspi) == NULL)
+				break;
+		}
+
+		if (count == 0 || newspi == 0) {
+			ipseclog((LOG_DEBUG, "key_do_getnewspi: to allocate spi is failed.\n"));
+			return 0;
+		}
+	}
+
+	/* statistics */
+	keystat.getspi_count =
+		(keystat.getspi_count + key_spi_trycnt - count) / 2;
+
+	return newspi;
+}
+
+/*
+ * SADB_UPDATE processing
+ * receive
+ *   <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
+ *       key(AE), (identity(SD),) (sensitivity)>
+ * from the ikmpd, and update a secasvar entry whose status is SADB_SASTATE_LARVAL.
+ * and send
+ *   <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
+ *       (identity(SD),) (sensitivity)>
+ * to the ikmpd.
+ *
+ * m will always be freed.
+ */
+static int
+key_update(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_sa *sa0;
+	struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *sah;
+	struct secasvar *sav;
+	u_int16_t proto;
+	u_int8_t mode;
+	u_int32_t reqid;
+	int error;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_update: NULL pointer is passed.\n");
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_update: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->ext[SADB_EXT_SA] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
+	    (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP &&
+	     mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) ||
+	    (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH &&
+	     mhp->ext[SADB_EXT_KEY_AUTH] == NULL) ||
+	    (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL &&
+	     mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) ||
+	    (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL &&
+	     mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) {
+		ipseclog((LOG_DEBUG, "key_update: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
+		ipseclog((LOG_DEBUG, "key_update: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
+		mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+		reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
+	} else {
+		mode = IPSEC_MODE_ANY;
+		reqid = 0;
+	}
+	/* XXX boundary checking for other extensions */
+
+	sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+	src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
+	dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+
+	/* get a SA header */
+	if ((sah = key_getsah(&saidx)) == NULL) {
+		ipseclog((LOG_DEBUG, "key_update: no SA index found.\n"));
+		return key_senderror(so, m, ENOENT);
+	}
+
+	/* set spidx if there */
+	/* XXX rewrite */
+	error = key_setident(sah, m, mhp);
+	if (error)
+		return key_senderror(so, m, error);
+
+	/* find a SA with sequence number. */
+#ifdef IPSEC_DOSEQCHECK
+	if (mhp->msg->sadb_msg_seq != 0
+	 && (sav = key_getsavbyseq(sah, mhp->msg->sadb_msg_seq)) == NULL) {
+		ipseclog((LOG_DEBUG,
+		    "key_update: no larval SA with sequence %u exists.\n",
+		    mhp->msg->sadb_msg_seq));
+		return key_senderror(so, m, ENOENT);
+	}
+#else
+	if ((sav = key_getsavbyspi(sah, sa0->sadb_sa_spi)) == NULL) {
+		ipseclog((LOG_DEBUG,
+		    "key_update: no such a SA found (spi:%u)\n",
+		    (u_int32_t)ntohl(sa0->sadb_sa_spi)));
+		return key_senderror(so, m, EINVAL);
+	}
+#endif
+
+	/* validity check */
+	if (sav->sah->saidx.proto != proto) {
+		ipseclog((LOG_DEBUG,
+		    "key_update: protocol mismatched (DB=%u param=%u)\n",
+		    sav->sah->saidx.proto, proto));
+		return key_senderror(so, m, EINVAL);
+	}
+#ifdef IPSEC_DOSEQCHECK
+	if (sav->spi != sa0->sadb_sa_spi) {
+		ipseclog((LOG_DEBUG,
+		    "key_update: SPI mismatched (DB:%u param:%u)\n",
+		    (u_int32_t)ntohl(sav->spi),
+		    (u_int32_t)ntohl(sa0->sadb_sa_spi)));
+		return key_senderror(so, m, EINVAL);
+	}
+#endif
+	if (sav->pid != mhp->msg->sadb_msg_pid) {
+		ipseclog((LOG_DEBUG,
+		    "key_update: pid mismatched (DB:%u param:%u)\n",
+		    sav->pid, mhp->msg->sadb_msg_pid));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* copy sav values */
+	error = key_setsaval(sav, m, mhp);
+	if (error) {
+		key_freesav(sav);
+		return key_senderror(so, m, error);
+	}
+
+	/* check SA values to be mature. */
+	if ((mhp->msg->sadb_msg_errno = key_mature(sav)) != 0) {
+		key_freesav(sav);
+		return key_senderror(so, m, 0);
+	}
+
+    {
+	struct mbuf *n;
+
+	/* set msg buf from mhp */
+	n = key_getmsgbuf_x1(m, mhp);
+	if (n == NULL) {
+		ipseclog((LOG_DEBUG, "key_update: No more memory.\n"));
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/*
+ * search SAD with sequence for a SA which state is SADB_SASTATE_LARVAL.
+ * only called by key_update().
+ * OUT:
+ *	NULL	: not found
+ *	others	: found, pointer to a SA.
+ */
+#ifdef IPSEC_DOSEQCHECK
+static struct secasvar *
+key_getsavbyseq(sah, seq)
+	struct secashead *sah;
+	u_int32_t seq;
+{
+	struct secasvar *sav;
+	u_int state;
+
+	state = SADB_SASTATE_LARVAL;
+
+	/* search SAD with sequence number ? */
+	LIST_FOREACH(sav, &sah->savtree[state], chain) {
+
+		KEY_CHKSASTATE(state, sav->state, "key_getsabyseq");
+
+		if (sav->seq == seq) {
+			sav->refcnt++;
+			KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
+				printf("DP key_getsavbyseq cause "
+					"refcnt++:%d SA:%p\n",
+					sav->refcnt, sav));
+			return sav;
+		}
+	}
+
+	return NULL;
+}
+#endif
+
+/*
+ * SADB_ADD processing
+ * add a entry to SA database, when received
+ *   <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
+ *       key(AE), (identity(SD),) (sensitivity)>
+ * from the ikmpd,
+ * and send
+ *   <base, SA, (SA2), (lifetime(HSC),) address(SD), (address(P),)
+ *       (identity(SD),) (sensitivity)>
+ * to the ikmpd.
+ *
+ * IGNORE identity and sensitivity messages.
+ *
+ * m will always be freed.
+ */
+static int
+key_add(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_sa *sa0;
+	struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *newsah;
+	struct secasvar *newsav;
+	u_int16_t proto;
+	u_int8_t mode;
+	u_int32_t reqid;
+	int error;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_add: NULL pointer is passed.\n");
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_add: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->ext[SADB_EXT_SA] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
+	    (mhp->msg->sadb_msg_satype == SADB_SATYPE_ESP &&
+	     mhp->ext[SADB_EXT_KEY_ENCRYPT] == NULL) ||
+	    (mhp->msg->sadb_msg_satype == SADB_SATYPE_AH &&
+	     mhp->ext[SADB_EXT_KEY_AUTH] == NULL) ||
+	    (mhp->ext[SADB_EXT_LIFETIME_HARD] != NULL &&
+	     mhp->ext[SADB_EXT_LIFETIME_SOFT] == NULL) ||
+	    (mhp->ext[SADB_EXT_LIFETIME_HARD] == NULL &&
+	     mhp->ext[SADB_EXT_LIFETIME_SOFT] != NULL)) {
+		ipseclog((LOG_DEBUG, "key_add: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
+		/* XXX need more */
+		ipseclog((LOG_DEBUG, "key_add: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
+		mode = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
+		reqid = ((struct sadb_x_sa2 *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
+	} else {
+		mode = IPSEC_MODE_ANY;
+		reqid = 0;
+	}
+
+	sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+	src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
+	dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+
+	/* get a SA header */
+	if ((newsah = key_getsah(&saidx)) == NULL) {
+		/* create a new SA header */
+		if ((newsah = key_newsah(&saidx)) == NULL) {
+			ipseclog((LOG_DEBUG, "key_add: No more memory.\n"));
+			return key_senderror(so, m, ENOBUFS);
+		}
+	}
+
+	/* set spidx if there */
+	/* XXX rewrite */
+	error = key_setident(newsah, m, mhp);
+	if (error) {
+		return key_senderror(so, m, error);
+	}
+
+	/* create new SA entry. */
+	/* We can create new SA only if SPI is differenct. */
+	if (key_getsavbyspi(newsah, sa0->sadb_sa_spi)) {
+		ipseclog((LOG_DEBUG, "key_add: SA already exists.\n"));
+		return key_senderror(so, m, EEXIST);
+	}
+	newsav = key_newsav(m, mhp, newsah, &error);
+	if (newsav == NULL) {
+		return key_senderror(so, m, error);
+	}
+
+	/* check SA values to be mature. */
+	if ((error = key_mature(newsav)) != 0) {
+		key_freesav(newsav);
+		return key_senderror(so, m, error);
+	}
+
+	/*
+	 * don't call key_freesav() here, as we would like to keep the SA
+	 * in the database on success.
+	 */
+
+    {
+	struct mbuf *n;
+
+	/* set msg buf from mhp */
+	n = key_getmsgbuf_x1(m, mhp);
+	if (n == NULL) {
+		ipseclog((LOG_DEBUG, "key_update: No more memory.\n"));
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/* m is retained */
+static int
+key_setident(sah, m, mhp)
+	struct secashead *sah;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	const struct sadb_ident *idsrc, *iddst;
+	int idsrclen, iddstlen;
+
+	/* sanity check */
+	if (sah == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_setident: NULL pointer is passed.\n");
+
+	/* don't make buffer if not there */
+	if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL &&
+	    mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) {
+		sah->idents = NULL;
+		sah->identd = NULL;
+		return 0;
+	}
+	
+	if (mhp->ext[SADB_EXT_IDENTITY_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_IDENTITY_DST] == NULL) {
+		ipseclog((LOG_DEBUG, "key_setident: invalid identity.\n"));
+		return EINVAL;
+	}
+
+	idsrc = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_SRC];
+	iddst = (const struct sadb_ident *)mhp->ext[SADB_EXT_IDENTITY_DST];
+	idsrclen = mhp->extlen[SADB_EXT_IDENTITY_SRC];
+	iddstlen = mhp->extlen[SADB_EXT_IDENTITY_DST];
+
+	/* validity check */
+	if (idsrc->sadb_ident_type != iddst->sadb_ident_type) {
+		ipseclog((LOG_DEBUG, "key_setident: ident type mismatch.\n"));
+		return EINVAL;
+	}
+
+	switch (idsrc->sadb_ident_type) {
+	case SADB_IDENTTYPE_PREFIX:
+	case SADB_IDENTTYPE_FQDN:
+	case SADB_IDENTTYPE_USERFQDN:
+	default:
+		/* XXX do nothing */
+		sah->idents = NULL;
+		sah->identd = NULL;
+	 	return 0;
+	}
+
+	/* make structure */
+	KMALLOC(sah->idents, struct sadb_ident *, idsrclen);
+	if (sah->idents == NULL) {
+		ipseclog((LOG_DEBUG, "key_setident: No more memory.\n"));
+		return ENOBUFS;
+	}
+	KMALLOC(sah->identd, struct sadb_ident *, iddstlen);
+	if (sah->identd == NULL) {
+		KFREE(sah->idents);
+		sah->idents = NULL;
+		ipseclog((LOG_DEBUG, "key_setident: No more memory.\n"));
+		return ENOBUFS;
+	}
+	bcopy(idsrc, sah->idents, idsrclen);
+	bcopy(iddst, sah->identd, iddstlen);
+
+	return 0;
+}
+
+/*
+ * m will not be freed on return.
+ * it is caller's responsibility to free the result. 
+ */
+static struct mbuf *
+key_getmsgbuf_x1(m, mhp)
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct mbuf *n;
+
+	/* sanity check */
+	if (m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_getmsgbuf_x1: NULL pointer is passed.\n");
+
+	/* create new sadb_msg to reply. */
+	n = key_gather_mbuf(m, mhp, 1, 9, SADB_EXT_RESERVED,
+	    SADB_EXT_SA, SADB_X_EXT_SA2,
+	    SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST,
+	    SADB_EXT_LIFETIME_HARD, SADB_EXT_LIFETIME_SOFT,
+	    SADB_EXT_IDENTITY_SRC, SADB_EXT_IDENTITY_DST);
+	if (!n)
+		return NULL;
+
+	if (n->m_len < sizeof(struct sadb_msg)) {
+		n = m_pullup(n, sizeof(struct sadb_msg));
+		if (n == NULL)
+			return NULL;
+	}
+	mtod(n, struct sadb_msg *)->sadb_msg_errno = 0;
+	mtod(n, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(n->m_pkthdr.len);
+
+	return n;
+}
+
+static int key_delete_all __P((struct socket *, struct mbuf *,
+	const struct sadb_msghdr *, u_int16_t));
+
+/*
+ * SADB_DELETE processing
+ * receive
+ *   <base, SA(*), address(SD)>
+ * from the ikmpd, and set SADB_SASTATE_DEAD,
+ * and send,
+ *   <base, SA(*), address(SD)>
+ * to the ikmpd.
+ *
+ * m will always be freed.
+ */
+static int
+key_delete(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_sa *sa0;
+	struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *sah;
+	struct secasvar *sav = NULL;
+	u_int16_t proto;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_delete: NULL pointer is passed.\n");
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_delete: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) {
+		ipseclog((LOG_DEBUG, "key_delete: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
+		ipseclog((LOG_DEBUG, "key_delete: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->ext[SADB_EXT_SA] == NULL) {
+		/*
+		 * Caller wants us to delete all non-LARVAL SAs
+		 * that match the src/dst.  This is used during
+		 * IKE INITIAL-CONTACT.
+		 */
+		ipseclog((LOG_DEBUG, "key_delete: doing delete all.\n"));
+		return key_delete_all(so, m, mhp, proto);
+	} else if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa)) {
+		ipseclog((LOG_DEBUG, "key_delete: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+	src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
+	dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+
+	/* get a SA header */
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (sah->state == SADB_SASTATE_DEAD)
+			continue;
+		if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0)
+			continue;
+
+		/* get a SA with SPI. */
+		sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
+		if (sav)
+			break;
+	}
+	if (sah == NULL) {
+		ipseclog((LOG_DEBUG, "key_delete: no SA found.\n"));
+		return key_senderror(so, m, ENOENT);
+	}
+
+	key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+	key_freesav(sav);
+	sav = NULL;
+
+    {
+	struct mbuf *n;
+	struct sadb_msg *newmsg;
+
+	/* create new sadb_msg to reply. */
+	n = key_gather_mbuf(m, mhp, 1, 4, SADB_EXT_RESERVED,
+	    SADB_EXT_SA, SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	if (n->m_len < sizeof(struct sadb_msg)) {
+		n = m_pullup(n, sizeof(struct sadb_msg));
+		if (n == NULL)
+			return key_senderror(so, m, ENOBUFS);
+	}
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/*
+ * delete all SAs for src/dst.  Called from key_delete().
+ */
+static int
+key_delete_all(so, m, mhp, proto)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+	u_int16_t proto;
+{
+	struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *sah;
+	struct secasvar *sav, *nextsav;
+	u_int stateidx, state;
+
+	src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
+	dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (sah->state == SADB_SASTATE_DEAD)
+			continue;
+		if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0)
+			continue;
+
+		/* Delete all non-LARVAL SAs. */
+		for (stateidx = 0;
+		     stateidx < _ARRAYLEN(saorder_state_alive);
+		     stateidx++) {
+			state = saorder_state_alive[stateidx];
+			if (state == SADB_SASTATE_LARVAL)
+				continue;
+			for (sav = LIST_FIRST(&sah->savtree[state]);
+			     sav != NULL; sav = nextsav) {
+				nextsav = LIST_NEXT(sav, chain);
+				/* sanity check */
+				if (sav->state != state) {
+					ipseclog((LOG_DEBUG, "key_delete_all: "
+					       "invalid sav->state "
+					       "(queue: %d SA: %d)\n",
+					       state, sav->state));
+					continue;
+				}
+				
+				key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+				key_freesav(sav);
+			}
+		}
+	}
+    {
+	struct mbuf *n;
+	struct sadb_msg *newmsg;
+
+	/* create new sadb_msg to reply. */
+	n = key_gather_mbuf(m, mhp, 1, 3, SADB_EXT_RESERVED,
+	    SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST);
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	if (n->m_len < sizeof(struct sadb_msg)) {
+		n = m_pullup(n, sizeof(struct sadb_msg));
+		if (n == NULL)
+			return key_senderror(so, m, ENOBUFS);
+	}
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(n->m_pkthdr.len);
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ALL);
+    }
+}
+
+/*
+ * SADB_GET processing
+ * receive
+ *   <base, SA(*), address(SD)>
+ * from the ikmpd, and get a SP and a SA to respond,
+ * and send,
+ *   <base, SA, (lifetime(HSC),) address(SD), (address(P),) key(AE),
+ *       (identity(SD),) (sensitivity)>
+ * to the ikmpd.
+ *
+ * m will always be freed.
+ */
+static int
+key_get(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_sa *sa0;
+	struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *sah;
+	struct secasvar *sav = NULL;
+	u_int16_t proto;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_get: NULL pointer is passed.\n");
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_get: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->ext[SADB_EXT_SA] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL) {
+		ipseclog((LOG_DEBUG, "key_get: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_SA] < sizeof(struct sadb_sa) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address)) {
+		ipseclog((LOG_DEBUG, "key_get: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	sa0 = (struct sadb_sa *)mhp->ext[SADB_EXT_SA];
+	src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
+	dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+
+	/* get a SA header */
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (sah->state == SADB_SASTATE_DEAD)
+			continue;
+		if (key_cmpsaidx(&sah->saidx, &saidx, CMP_HEAD) == 0)
+			continue;
+
+		/* get a SA with SPI. */
+		sav = key_getsavbyspi(sah, sa0->sadb_sa_spi);
+		if (sav)
+			break;
+	}
+	if (sah == NULL) {
+		ipseclog((LOG_DEBUG, "key_get: no SA found.\n"));
+		return key_senderror(so, m, ENOENT);
+	}
+
+    {
+	struct mbuf *n;
+	u_int8_t satype;
+
+	/* map proto to satype */
+	if ((satype = key_proto2satype(sah->saidx.proto)) == 0) {
+		ipseclog((LOG_DEBUG, "key_get: there was invalid proto in SAD.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* create new sadb_msg to reply. */
+	n = key_setdumpsa(sav, SADB_GET, satype, mhp->msg->sadb_msg_seq,
+	    mhp->msg->sadb_msg_pid);
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+    }
+}
+
+/* XXX make it sysctl-configurable? */
+static void
+key_getcomb_setlifetime(comb)
+	struct sadb_comb *comb;
+{
+
+	comb->sadb_comb_soft_allocations = 1;
+	comb->sadb_comb_hard_allocations = 1;
+	comb->sadb_comb_soft_bytes = 0;
+	comb->sadb_comb_hard_bytes = 0;
+	comb->sadb_comb_hard_addtime = 86400;	/* 1 day */
+	comb->sadb_comb_soft_addtime = comb->sadb_comb_soft_addtime * 80 / 100;
+	comb->sadb_comb_soft_usetime = 28800;	/* 8 hours */
+	comb->sadb_comb_hard_usetime = comb->sadb_comb_hard_usetime * 80 / 100;
+}
+
+#ifdef IPSEC_ESP
+/*
+ * XXX reorder combinations by preference
+ * XXX no idea if the user wants ESP authentication or not
+ */
+static struct mbuf *
+key_getcomb_esp()
+{
+	struct sadb_comb *comb;
+	const struct esp_algorithm *algo;
+	struct mbuf *result = NULL, *m, *n;
+	int encmin;
+	int i, off, o;
+	int totlen;
+	const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb));
+
+	m = NULL;
+	for (i = 1; i <= SADB_EALG_MAX; i++) {
+		algo = esp_algorithm_lookup(i);
+		if (!algo)
+			continue;
+
+		if (algo->keymax < ipsec_esp_keymin)
+			continue;
+		if (algo->keymin < ipsec_esp_keymin)
+			encmin = ipsec_esp_keymin;
+		else
+			encmin = algo->keymin;
+
+		if (ipsec_esp_auth)
+			m = key_getcomb_ah();
+		else {
+#ifdef DIAGNOSTIC
+			if (l > MLEN)
+				panic("assumption failed in key_getcomb_esp");
+#endif
+			MGET(m, M_DONTWAIT, MT_DATA);
+			if (m) {
+				M_ALIGN(m, l);
+				m->m_len = l;
+				m->m_next = NULL;
+				bzero(mtod(m, caddr_t), m->m_len);
+			}
+		}
+		if (!m)
+			goto fail;
+
+		totlen = 0;
+		for (n = m; n; n = n->m_next)
+			totlen += n->m_len;
+#ifdef DIAGNOSTIC
+		if (totlen % l)
+			panic("assumption failed in key_getcomb_esp");
+#endif
+
+		for (off = 0; off < totlen; off += l) {
+			n = m_pulldown(m, off, l, &o);
+			if (!n) {
+				/* m is already freed */
+				goto fail;
+			}
+			comb = (struct sadb_comb *)(mtod(n, caddr_t) + o);
+			bzero(comb, sizeof(*comb));
+			key_getcomb_setlifetime(comb);
+			comb->sadb_comb_encrypt = i;
+			comb->sadb_comb_encrypt_minbits = encmin;
+			comb->sadb_comb_encrypt_maxbits = algo->keymax;
+		}
+
+		if (!result)
+			result = m;
+		else
+			m_cat(result, m);
+	}
+
+	return result;
+
+ fail:
+	if (result)
+		m_freem(result);
+	return NULL;
+}
+#endif
+
+/*
+ * XXX reorder combinations by preference
+ */
+static struct mbuf *
+key_getcomb_ah()
+{
+	struct sadb_comb *comb;
+	const struct ah_algorithm *algo;
+	struct mbuf *m;
+	int min;
+	int i;
+	const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb));
+
+	m = NULL;
+	for (i = 1; i <= SADB_AALG_MAX; i++) {
+#if 1
+		/* we prefer HMAC algorithms, not old algorithms */
+		if (i != SADB_AALG_SHA1HMAC && i != SADB_AALG_MD5HMAC)
+			continue;
+#endif
+		algo = ah_algorithm_lookup(i);
+		if (!algo)
+			continue;
+
+		if (algo->keymax < ipsec_ah_keymin)
+			continue;
+		if (algo->keymin < ipsec_ah_keymin)
+			min = ipsec_ah_keymin;
+		else
+			min = algo->keymin;
+
+		if (!m) {
+#ifdef DIAGNOSTIC
+			if (l > MLEN)
+				panic("assumption failed in key_getcomb_ah");
+#endif
+			MGET(m, M_DONTWAIT, MT_DATA);
+			if (m) {
+				M_ALIGN(m, l);
+				m->m_len = l;
+				m->m_next = NULL;
+			}
+		} else
+			M_PREPEND(m, l, M_DONTWAIT);
+		if (!m)
+			return NULL;
+
+		comb = mtod(m, struct sadb_comb *);
+		bzero(comb, sizeof(*comb));
+		key_getcomb_setlifetime(comb);
+		comb->sadb_comb_auth = i;
+		comb->sadb_comb_auth_minbits = min;
+		comb->sadb_comb_auth_maxbits = algo->keymax;
+	}
+
+	return m;
+}
+
+/*
+ * not really an official behavior.  discussed in pf_key@inner.net in Sep2000.
+ * XXX reorder combinations by preference
+ */
+static struct mbuf *
+key_getcomb_ipcomp()
+{
+	struct sadb_comb *comb;
+	const struct ipcomp_algorithm *algo;
+	struct mbuf *m;
+	int i;
+	const int l = PFKEY_ALIGN8(sizeof(struct sadb_comb));
+
+	m = NULL;
+	for (i = 1; i <= SADB_X_CALG_MAX; i++) {
+		algo = ipcomp_algorithm_lookup(i);
+		if (!algo)
+			continue;
+
+		if (!m) {
+#ifdef DIAGNOSTIC
+			if (l > MLEN)
+				panic("assumption failed in key_getcomb_ipcomp");
+#endif
+			MGET(m, M_DONTWAIT, MT_DATA);
+			if (m) {
+				M_ALIGN(m, l);
+				m->m_len = l;
+				m->m_next = NULL;
+			}
+		} else
+			M_PREPEND(m, l, M_DONTWAIT);
+		if (!m)
+			return NULL;
+
+		comb = mtod(m, struct sadb_comb *);
+		bzero(comb, sizeof(*comb));
+		key_getcomb_setlifetime(comb);
+		comb->sadb_comb_encrypt = i;
+		/* what should we set into sadb_comb_*_{min,max}bits? */
+	}
+
+	return m;
+}
+
+/*
+ * XXX no way to pass mode (transport/tunnel) to userland
+ * XXX replay checking?
+ * XXX sysctl interface to ipsec_{ah,esp}_keymin
+ */
+static struct mbuf *
+key_getprop(saidx)
+	const struct secasindex *saidx;
+{
+	struct sadb_prop *prop;
+	struct mbuf *m, *n;
+	const int l = PFKEY_ALIGN8(sizeof(struct sadb_prop));
+	int totlen;
+
+	switch (saidx->proto)  {
+#ifdef IPSEC_ESP
+	case IPPROTO_ESP:
+		m = key_getcomb_esp();
+		break;
+#endif
+	case IPPROTO_AH:
+		m = key_getcomb_ah();
+		break;
+	case IPPROTO_IPCOMP:
+		m = key_getcomb_ipcomp();
+		break;
+	default:
+		return NULL;
+	}
+
+	if (!m)
+		return NULL;
+	M_PREPEND(m, l, M_DONTWAIT);
+	if (!m)
+		return NULL;
+
+	totlen = 0;
+	for (n = m; n; n = n->m_next)
+		totlen += n->m_len;
+
+	prop = mtod(m, struct sadb_prop *);
+	bzero(prop, sizeof(*prop));
+	prop->sadb_prop_len = PFKEY_UNIT64(totlen);
+	prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
+	prop->sadb_prop_replay = 32;	/* XXX */
+
+	return m;
+}
+
+/*
+ * SADB_ACQUIRE processing called by key_checkrequest() and key_acquire2().
+ * send
+ *   <base, SA, address(SD), (address(P)), x_policy,
+ *       (identity(SD),) (sensitivity,) proposal>
+ * to KMD, and expect to receive
+ *   <base> with SADB_ACQUIRE if error occured,
+ * or
+ *   <base, src address, dst address, (SPI range)> with SADB_GETSPI
+ * from KMD by PF_KEY.
+ *
+ * XXX x_policy is outside of RFC2367 (KAME extension).
+ * XXX sensitivity is not supported.
+ * XXX for ipcomp, RFC2367 does not define how to fill in proposal.
+ * see comment for key_getcomb_ipcomp().
+ *
+ * OUT:
+ *    0     : succeed
+ *    others: error number
+ */
+static int
+key_acquire(saidx, sp)
+	struct secasindex *saidx;
+	struct secpolicy *sp;
+{
+	struct mbuf *result = NULL, *m;
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+	struct secacq *newacq;
+#endif
+	u_int8_t satype;
+	int error = -1;
+	u_int32_t seq;
+
+	/* sanity check */
+	if (saidx == NULL)
+		panic("key_acquire: NULL pointer is passed.\n");
+	if ((satype = key_proto2satype(saidx->proto)) == 0)
+		panic("key_acquire: invalid proto is passed.\n");
+
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+	/*
+	 * We never do anything about acquirng SA.  There is anather
+	 * solution that kernel blocks to send SADB_ACQUIRE message until
+	 * getting something message from IKEd.  In later case, to be
+	 * managed with ACQUIRING list.
+	 */
+	/* get a entry to check whether sending message or not. */
+	if ((newacq = key_getacq(saidx)) != NULL) {
+		if (key_blockacq_count < newacq->count) {
+			/* reset counter and do send message. */
+			newacq->count = 0;
+		} else {
+			/* increment counter and do nothing. */
+			newacq->count++;
+			return 0;
+		}
+	} else {
+		/* make new entry for blocking to send SADB_ACQUIRE. */
+		if ((newacq = key_newacq(saidx)) == NULL)
+			return ENOBUFS;
+
+		/* add to acqtree */
+		LIST_INSERT_HEAD(&acqtree, newacq, chain);
+	}
+#endif
+
+
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+	seq = newacq->seq;
+#else
+	seq = (acq_seq = (acq_seq == ~0 ? 1 : ++acq_seq));
+#endif
+	m = key_setsadbmsg(SADB_ACQUIRE, 0, satype, seq, 0, 0);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	result = m;
+
+	/* set sadb_address for saidx's. */
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
+	    (struct sockaddr *)&saidx->src, FULLMASK, IPSEC_ULPROTO_ANY);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
+	    (struct sockaddr *)&saidx->dst, FULLMASK, IPSEC_ULPROTO_ANY);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	/* XXX proxy address (optional) */
+
+	/* set sadb_x_policy */
+	if (sp) {
+		m = key_setsadbxpolicy(sp->policy, sp->spidx.dir, sp->id);
+		if (!m) {
+			error = ENOBUFS;
+			goto fail;
+		}
+		m_cat(result, m);
+	}
+
+	/* XXX identity (optional) */
+#if 0
+	if (idexttype && fqdn) {
+		/* create identity extension (FQDN) */
+		struct sadb_ident *id;
+		int fqdnlen;
+
+		fqdnlen = strlen(fqdn) + 1;	/* +1 for terminating-NUL */
+		id = (struct sadb_ident *)p;
+		bzero(id, sizeof(*id) + PFKEY_ALIGN8(fqdnlen));
+		id->sadb_ident_len = PFKEY_UNIT64(sizeof(*id) + PFKEY_ALIGN8(fqdnlen));
+		id->sadb_ident_exttype = idexttype;
+		id->sadb_ident_type = SADB_IDENTTYPE_FQDN;
+		bcopy(fqdn, id + 1, fqdnlen);
+		p += sizeof(struct sadb_ident) + PFKEY_ALIGN8(fqdnlen);
+	}
+
+	if (idexttype) {
+		/* create identity extension (USERFQDN) */
+		struct sadb_ident *id;
+		int userfqdnlen;
+
+		if (userfqdn) {
+			/* +1 for terminating-NUL */
+			userfqdnlen = strlen(userfqdn) + 1;
+		} else
+			userfqdnlen = 0;
+		id = (struct sadb_ident *)p;
+		bzero(id, sizeof(*id) + PFKEY_ALIGN8(userfqdnlen));
+		id->sadb_ident_len = PFKEY_UNIT64(sizeof(*id) + PFKEY_ALIGN8(userfqdnlen));
+		id->sadb_ident_exttype = idexttype;
+		id->sadb_ident_type = SADB_IDENTTYPE_USERFQDN;
+		/* XXX is it correct? */
+		if (curproc && curproc->p_cred)
+			id->sadb_ident_id = curproc->p_cred->p_ruid;
+		if (userfqdn && userfqdnlen)
+			bcopy(userfqdn, id + 1, userfqdnlen);
+		p += sizeof(struct sadb_ident) + PFKEY_ALIGN8(userfqdnlen);
+	}
+#endif
+
+	/* XXX sensitivity (optional) */
+
+	/* create proposal/combination extension */
+	m = key_getprop(saidx);
+#if 0
+	/*
+	 * spec conformant: always attach proposal/combination extension,
+	 * the problem is that we have no way to attach it for ipcomp,
+	 * due to the way sadb_comb is declared in RFC2367.
+	 */
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+#else
+	/*
+	 * outside of spec; make proposal/combination extension optional.
+	 */
+	if (m)
+		m_cat(result, m);
+#endif
+
+	if ((result->m_flags & M_PKTHDR) == 0) {
+		error = EINVAL;
+		goto fail;
+	}
+
+	if (result->m_len < sizeof(struct sadb_msg)) {
+		result = m_pullup(result, sizeof(struct sadb_msg));
+		if (result == NULL) {
+			error = ENOBUFS;
+			goto fail;
+		}
+	}
+
+	result->m_pkthdr.len = 0;
+	for (m = result; m; m = m->m_next)
+		result->m_pkthdr.len += m->m_len;
+
+	mtod(result, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(result->m_pkthdr.len);
+
+	return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED);
+
+ fail:
+	if (result)
+		m_freem(result);
+	return error;
+}
+
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+static struct secacq *
+key_newacq(saidx)
+	struct secasindex *saidx;
+{
+	struct secacq *newacq;
+
+	/* get new entry */
+	KMALLOC(newacq, struct secacq *, sizeof(struct secacq));
+	if (newacq == NULL) {
+		ipseclog((LOG_DEBUG, "key_newacq: No more memory.\n"));
+		return NULL;
+	}
+	bzero(newacq, sizeof(*newacq));
+
+	/* copy secindex */
+	bcopy(saidx, &newacq->saidx, sizeof(newacq->saidx));
+	newacq->seq = (acq_seq == ~0 ? 1 : ++acq_seq);
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	newacq->created = time_second;
+#else
+	newacq->created = time.tv_sec;
+#endif
+	newacq->count = 0;
+
+	return newacq;
+}
+
+static struct secacq *
+key_getacq(saidx)
+	struct secasindex *saidx;
+{
+	struct secacq *acq;
+
+	LIST_FOREACH(acq, &acqtree, chain) {
+		if (key_cmpsaidx(saidx, &acq->saidx, CMP_EXACTLY))
+			return acq;
+	}
+
+	return NULL;
+}
+
+static struct secacq *
+key_getacqbyseq(seq)
+	u_int32_t seq;
+{
+	struct secacq *acq;
+
+	LIST_FOREACH(acq, &acqtree, chain) {
+		if (acq->seq == seq)
+			return acq;
+	}
+
+	return NULL;
+}
+#endif
+
+static struct secspacq *
+key_newspacq(spidx)
+	struct secpolicyindex *spidx;
+{
+	struct secspacq *acq;
+
+	/* get new entry */
+	KMALLOC(acq, struct secspacq *, sizeof(struct secspacq));
+	if (acq == NULL) {
+		ipseclog((LOG_DEBUG, "key_newspacq: No more memory.\n"));
+		return NULL;
+	}
+	bzero(acq, sizeof(*acq));
+
+	/* copy secindex */
+	bcopy(spidx, &acq->spidx, sizeof(acq->spidx));
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	acq->created = time_second;
+#else
+	acq->created = time.tv_sec;
+#endif
+	acq->count = 1;
+
+	return acq;
+}
+
+static struct secspacq *
+key_getspacq(spidx)
+	struct secpolicyindex *spidx;
+{
+	struct secspacq *acq;
+
+	LIST_FOREACH(acq, &spacqtree, chain) {
+		if (key_cmpspidx_exactly(spidx, &acq->spidx))
+			return acq;
+	}
+
+	return NULL;
+}
+
+/*
+ * SADB_ACQUIRE processing,
+ * in first situation, is receiving
+ *   <base>
+ * from the ikmpd, and clear sequence of its secasvar entry.
+ *
+ * In second situation, is receiving
+ *   <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal>
+ * from a user land process, and return
+ *   <base, address(SD), (address(P),) (identity(SD),) (sensitivity,) proposal>
+ * to the socket.
+ *
+ * m will always be freed.
+ */
+static int
+key_acquire2(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	const struct sadb_address *src0, *dst0;
+	struct secasindex saidx;
+	struct secashead *sah;
+	u_int16_t proto;
+	int error;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_acquire2: NULL pointer is passed.\n");
+
+	/*
+	 * Error message from KMd.
+	 * We assume that if error was occured in IKEd, the length of PFKEY
+	 * message is equal to the size of sadb_msg structure.
+	 * We do not raise error even if error occured in this function.
+	 */
+	if (mhp->msg->sadb_msg_len == PFKEY_UNIT64(sizeof(struct sadb_msg))) {
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+		struct secacq *acq;
+
+		/* check sequence number */
+		if (mhp->msg->sadb_msg_seq == 0) {
+			ipseclog((LOG_DEBUG, "key_acquire2: must specify sequence number.\n"));
+			m_freem(m);
+			return 0;
+		}
+
+		if ((acq = key_getacqbyseq(mhp->msg->sadb_msg_seq)) == NULL) {
+			/*
+			 * the specified larval SA is already gone, or we got
+			 * a bogus sequence number.  we can silently ignore it.
+			 */
+			m_freem(m);
+			return 0;
+		}
+
+		/* reset acq counter in order to deletion by timehander. */
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+		acq->created = time_second;
+#else
+		acq->created = time.tv_sec;
+#endif
+		acq->count = 0;
+#endif
+		m_freem(m);
+		return 0;
+	}
+
+	/*
+	 * This message is from user land.
+	 */
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_acquire2: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if (mhp->ext[SADB_EXT_ADDRESS_SRC] == NULL ||
+	    mhp->ext[SADB_EXT_ADDRESS_DST] == NULL ||
+	    mhp->ext[SADB_EXT_PROPOSAL] == NULL) {
+		/* error */
+		ipseclog((LOG_DEBUG, "key_acquire2: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if (mhp->extlen[SADB_EXT_ADDRESS_SRC] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_ADDRESS_DST] < sizeof(struct sadb_address) ||
+	    mhp->extlen[SADB_EXT_PROPOSAL] < sizeof(struct sadb_prop)) {
+		/* error */
+		ipseclog((LOG_DEBUG, "key_acquire2: invalid message is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
+	dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+
+	/* XXX boundary check against sa_len */
+	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+
+	/* get a SA index */
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (sah->state == SADB_SASTATE_DEAD)
+			continue;
+		if (key_cmpsaidx(&sah->saidx, &saidx, CMP_MODE_REQID))
+			break;
+	}
+	if (sah != NULL) {
+		ipseclog((LOG_DEBUG, "key_acquire2: a SA exists already.\n"));
+		return key_senderror(so, m, EEXIST);
+	}
+
+	error = key_acquire(&saidx, NULL);
+	if (error != 0) {
+		ipseclog((LOG_DEBUG, "key_acquire2: error %d returned "
+			"from key_acquire.\n", mhp->msg->sadb_msg_errno));
+		return key_senderror(so, m, error);
+	}
+
+	return key_sendup_mbuf(so, m, KEY_SENDUP_REGISTERED);
+}
+
+/*
+ * SADB_REGISTER processing.
+ * If SATYPE_UNSPEC has been passed as satype, only return sabd_supported.
+ * receive
+ *   <base>
+ * from the ikmpd, and register a socket to send PF_KEY messages,
+ * and send
+ *   <base, supported>
+ * to KMD by PF_KEY.
+ * If socket is detached, must free from regnode.
+ *
+ * m will always be freed.
+ */
+static int
+key_register(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct secreg *reg, *newreg = 0;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_register: NULL pointer is passed.\n");
+
+	/* check for invalid register message */
+	if (mhp->msg->sadb_msg_satype >= sizeof(regtree)/sizeof(regtree[0]))
+		return key_senderror(so, m, EINVAL);
+
+	/* When SATYPE_UNSPEC is specified, only return sabd_supported. */
+	if (mhp->msg->sadb_msg_satype == SADB_SATYPE_UNSPEC)
+		goto setmsg;
+
+	/* check whether existing or not */
+	LIST_FOREACH(reg, &regtree[mhp->msg->sadb_msg_satype], chain) {
+		if (reg->so == so) {
+			ipseclog((LOG_DEBUG, "key_register: socket exists already.\n"));
+			return key_senderror(so, m, EEXIST);
+		}
+	}
+
+	/* create regnode */
+	KMALLOC(newreg, struct secreg *, sizeof(*newreg));
+	if (newreg == NULL) {
+		ipseclog((LOG_DEBUG, "key_register: No more memory.\n"));
+		return key_senderror(so, m, ENOBUFS);
+	}
+	bzero((caddr_t)newreg, sizeof(*newreg));
+
+	newreg->so = so;
+	((struct keycb *)sotorawcb(so))->kp_registered++;
+
+	/* add regnode to regtree. */
+	LIST_INSERT_HEAD(&regtree[mhp->msg->sadb_msg_satype], newreg, chain);
+
+  setmsg:
+    {
+	struct mbuf *n;
+	struct sadb_msg *newmsg;
+	struct sadb_supported *sup;
+	u_int len, alen, elen;
+	int off;
+	int i;
+	struct sadb_alg *alg;
+
+	/* create new sadb_msg to reply. */
+	alen = 0;
+	for (i = 1; i <= SADB_AALG_MAX; i++) {
+		if (ah_algorithm_lookup(i))
+			alen += sizeof(struct sadb_alg);
+	}
+	if (alen)
+		alen += sizeof(struct sadb_supported);
+	elen = 0;
+#ifdef IPSEC_ESP
+	for (i = 1; i <= SADB_EALG_MAX; i++) {
+		if (esp_algorithm_lookup(i))
+			elen += sizeof(struct sadb_alg);
+	}
+	if (elen)
+		elen += sizeof(struct sadb_supported);
+#endif
+
+	len = sizeof(struct sadb_msg) + alen + elen;
+
+	if (len > MCLBYTES)
+		return key_senderror(so, m, ENOBUFS);
+
+	MGETHDR(n, M_DONTWAIT, MT_DATA);
+	if (len > MHLEN) {
+		MCLGET(n, M_DONTWAIT);
+		if ((n->m_flags & M_EXT) == 0) {
+			m_freem(n);
+			n = NULL;
+		}
+	}
+	if (!n)
+		return key_senderror(so, m, ENOBUFS);
+
+	n->m_pkthdr.len = n->m_len = len;
+	n->m_next = NULL;
+	off = 0;
+
+	m_copydata(m, 0, sizeof(struct sadb_msg), mtod(n, caddr_t) + off);
+	newmsg = mtod(n, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(len);
+	off += PFKEY_ALIGN8(sizeof(struct sadb_msg));
+
+	/* for authentication algorithm */
+	if (alen) {
+		sup = (struct sadb_supported *)(mtod(n, caddr_t) + off);
+		sup->sadb_supported_len = PFKEY_UNIT64(alen);
+		sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_AUTH;
+		off += PFKEY_ALIGN8(sizeof(*sup));
+
+		for (i = 1; i <= SADB_AALG_MAX; i++) {
+			const struct ah_algorithm *aalgo;
+
+			aalgo = ah_algorithm_lookup(i);
+			if (!aalgo)
+				continue;
+			alg = (struct sadb_alg *)(mtod(n, caddr_t) + off);
+			alg->sadb_alg_id = i;
+			alg->sadb_alg_ivlen = 0;
+			alg->sadb_alg_minbits = aalgo->keymin;
+			alg->sadb_alg_maxbits = aalgo->keymax;
+			off += PFKEY_ALIGN8(sizeof(*alg));
+		}
+	}
+
+#ifdef IPSEC_ESP
+	/* for encryption algorithm */
+	if (elen) {
+		sup = (struct sadb_supported *)(mtod(n, caddr_t) + off);
+		sup->sadb_supported_len = PFKEY_UNIT64(elen);
+		sup->sadb_supported_exttype = SADB_EXT_SUPPORTED_ENCRYPT;
+		off += PFKEY_ALIGN8(sizeof(*sup));
+
+		for (i = 1; i <= SADB_EALG_MAX; i++) {
+			const struct esp_algorithm *ealgo;
+
+			ealgo = esp_algorithm_lookup(i);
+			if (!ealgo)
+				continue;
+			alg = (struct sadb_alg *)(mtod(n, caddr_t) + off);
+			alg->sadb_alg_id = i;
+			if (ealgo && ealgo->ivlen) {
+				/*
+				 * give NULL to get the value preferred by
+				 * algorithm XXX SADB_X_EXT_DERIV ?
+				 */
+				alg->sadb_alg_ivlen =
+				    (*ealgo->ivlen)(ealgo, NULL);
+			} else
+				alg->sadb_alg_ivlen = 0;
+			alg->sadb_alg_minbits = ealgo->keymin;
+			alg->sadb_alg_maxbits = ealgo->keymax;
+			off += PFKEY_ALIGN8(sizeof(struct sadb_alg));
+		}
+	}
+#endif
+
+#ifdef DIGAGNOSTIC
+	if (off != len)
+		panic("length assumption failed in key_register");
+#endif
+
+	m_freem(m);
+	return key_sendup_mbuf(so, n, KEY_SENDUP_REGISTERED);
+    }
+}
+
+/*
+ * free secreg entry registered.
+ * XXX: I want to do free a socket marked done SADB_RESIGER to socket.
+ */
+void
+key_freereg(so)
+	struct socket *so;
+{
+	struct secreg *reg;
+	int i;
+
+	/* sanity check */
+	if (so == NULL)
+		panic("key_freereg: NULL pointer is passed.\n");
+
+	/*
+	 * check whether existing or not.
+	 * check all type of SA, because there is a potential that
+	 * one socket is registered to multiple type of SA.
+	 */
+	for (i = 0; i <= SADB_SATYPE_MAX; i++) {
+		LIST_FOREACH(reg, &regtree[i], chain) {
+			if (reg->so == so
+			 && __LIST_CHAINED(reg)) {
+				LIST_REMOVE(reg, chain);
+				KFREE(reg);
+				break;
+			}
+		}
+	}
+	
+	return;
+}
+
+/*
+ * SADB_EXPIRE processing
+ * send
+ *   <base, SA, SA2, lifetime(C and one of HS), address(SD)>
+ * to KMD by PF_KEY.
+ * NOTE: We send only soft lifetime extension.
+ *
+ * OUT:	0	: succeed
+ *	others	: error number
+ */
+static int
+key_expire(sav)
+	struct secasvar *sav;
+{
+	int s;
+	int satype;
+	struct mbuf *result = NULL, *m;
+	int len;
+	int error = -1;
+	struct sadb_lifetime *lt;
+
+	/* XXX: Why do we lock ? */
+#ifdef __NetBSD__
+	s = splsoftnet();	/*called from softclock()*/
+#else
+	s = splnet();	/*called from softclock()*/
+#endif
+
+	/* sanity check */
+	if (sav == NULL)
+		panic("key_expire: NULL pointer is passed.\n");
+	if (sav->sah == NULL)
+		panic("key_expire: Why was SA index in SA NULL.\n");
+	if ((satype = key_proto2satype(sav->sah->saidx.proto)) == 0)
+		panic("key_expire: invalid proto is passed.\n");
+
+	/* set msg header */
+	m = key_setsadbmsg(SADB_EXPIRE, 0, satype, sav->seq, 0, sav->refcnt);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	result = m;
+
+	/* create SA extension */
+	m = key_setsadbsa(sav);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	/* create SA extension */
+	m = key_setsadbxsa2(sav->sah->saidx.mode,
+			sav->replay ? sav->replay->count : 0,
+			sav->sah->saidx.reqid);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	/* create lifetime extension (current and soft) */
+	len = PFKEY_ALIGN8(sizeof(*lt)) * 2;
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		error = ENOBUFS;
+		goto fail;
+	}
+	bzero(mtod(m, caddr_t), len);
+	lt = mtod(m, struct sadb_lifetime *);
+	lt->sadb_lifetime_len = PFKEY_UNIT64(sizeof(struct sadb_lifetime));
+	lt->sadb_lifetime_exttype = SADB_EXT_LIFETIME_CURRENT;
+	lt->sadb_lifetime_allocations = sav->lft_c->sadb_lifetime_allocations;
+	lt->sadb_lifetime_bytes = sav->lft_c->sadb_lifetime_bytes;
+	lt->sadb_lifetime_addtime = sav->lft_c->sadb_lifetime_addtime;
+	lt->sadb_lifetime_usetime = sav->lft_c->sadb_lifetime_usetime;
+	lt = (struct sadb_lifetime *)(mtod(m, caddr_t) + len / 2);
+	bcopy(sav->lft_s, lt, sizeof(*lt));
+	m_cat(result, m);
+
+	/* set sadb_address for source */
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_SRC,
+	    (struct sockaddr *)&sav->sah->saidx.src,
+	    FULLMASK, IPSEC_ULPROTO_ANY);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	/* set sadb_address for destination */
+	m = key_setsadbaddr(SADB_EXT_ADDRESS_DST,
+	    (struct sockaddr *)&sav->sah->saidx.dst,
+	    FULLMASK, IPSEC_ULPROTO_ANY);
+	if (!m) {
+		error = ENOBUFS;
+		goto fail;
+	}
+	m_cat(result, m);
+
+	if ((result->m_flags & M_PKTHDR) == 0) {
+		error = EINVAL;
+		goto fail;
+	}
+
+	if (result->m_len < sizeof(struct sadb_msg)) {
+		result = m_pullup(result, sizeof(struct sadb_msg));
+		if (result == NULL) {
+			error = ENOBUFS;
+			goto fail;
+		}
+	}
+
+	result->m_pkthdr.len = 0;
+	for (m = result; m; m = m->m_next)
+		result->m_pkthdr.len += m->m_len;
+
+	mtod(result, struct sadb_msg *)->sadb_msg_len =
+	    PFKEY_UNIT64(result->m_pkthdr.len);
+
+	return key_sendup_mbuf(NULL, result, KEY_SENDUP_REGISTERED);
+
+ fail:
+	if (result)
+		m_freem(result);
+	splx(s);
+	return error;
+}
+
+/*
+ * SADB_FLUSH processing
+ * receive
+ *   <base>
+ * from the ikmpd, and free all entries in secastree.
+ * and send,
+ *   <base>
+ * to the ikmpd.
+ * NOTE: to do is only marking SADB_SASTATE_DEAD.
+ *
+ * m will always be freed.
+ */
+static int
+key_flush(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_msg *newmsg;
+	struct secashead *sah, *nextsah;
+	struct secasvar *sav, *nextsav;
+	u_int16_t proto;
+	u_int8_t state;
+	u_int stateidx;
+
+	/* sanity check */
+	if (so == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_flush: NULL pointer is passed.\n");
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_flush: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* no SATYPE specified, i.e. flushing all SA. */
+	for (sah = LIST_FIRST(&sahtree);
+	     sah != NULL;
+	     sah = nextsah) {
+		nextsah = LIST_NEXT(sah, chain);
+
+		if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC
+		 && proto != sah->saidx.proto)
+			continue;
+
+		for (stateidx = 0;
+		     stateidx < _ARRAYLEN(saorder_state_alive);
+		     stateidx++) {
+			state = saorder_state_any[stateidx];
+			for (sav = LIST_FIRST(&sah->savtree[state]);
+			     sav != NULL;
+			     sav = nextsav) {
+
+				nextsav = LIST_NEXT(sav, chain);
+
+				key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+				key_freesav(sav);
+			}
+		}
+
+		sah->state = SADB_SASTATE_DEAD;
+	}
+
+	if (m->m_len < sizeof(struct sadb_msg) ||
+	    sizeof(struct sadb_msg) > m->m_len + M_TRAILINGSPACE(m)) {
+		ipseclog((LOG_DEBUG, "key_flush: No more memory.\n"));
+		return key_senderror(so, m, ENOBUFS);
+	}
+
+	if (m->m_next)
+		m_freem(m->m_next);
+	m->m_next = NULL;
+	m->m_pkthdr.len = m->m_len = sizeof(struct sadb_msg);
+	newmsg = mtod(m, struct sadb_msg *);
+	newmsg->sadb_msg_errno = 0;
+	newmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len);
+
+	return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
+}
+
+/*
+ * SADB_DUMP processing
+ * dump all entries including status of DEAD in SAD.
+ * receive
+ *   <base>
+ * from the ikmpd, and dump all secasvar leaves
+ * and send,
+ *   <base> .....
+ * to the ikmpd.
+ *
+ * m will always be freed.
+ */
+static int
+key_dump(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct secashead *sah;
+	struct secasvar *sav;
+	u_int16_t proto;
+	u_int stateidx;
+	u_int8_t satype;
+	u_int8_t state;
+	int cnt;
+	struct sadb_msg *newmsg;
+	struct mbuf *n;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_dump: NULL pointer is passed.\n");
+
+	/* map satype to proto */
+	if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
+		ipseclog((LOG_DEBUG, "key_dump: invalid satype is passed.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	/* count sav entries to be sent to the userland. */
+	cnt = 0;
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC
+		 && proto != sah->saidx.proto)
+			continue;
+
+		for (stateidx = 0;
+		     stateidx < _ARRAYLEN(saorder_state_any);
+		     stateidx++) {
+			state = saorder_state_any[stateidx];
+			LIST_FOREACH(sav, &sah->savtree[state], chain) {
+				cnt++;
+			}
+		}
+	}
+
+	if (cnt == 0)
+		return key_senderror(so, m, ENOENT);
+
+	/* send this to the userland, one at a time. */
+	newmsg = NULL;
+	LIST_FOREACH(sah, &sahtree, chain) {
+		if (mhp->msg->sadb_msg_satype != SADB_SATYPE_UNSPEC
+		 && proto != sah->saidx.proto)
+			continue;
+
+		/* map proto to satype */
+		if ((satype = key_proto2satype(sah->saidx.proto)) == 0) {
+			ipseclog((LOG_DEBUG, "key_dump: there was invalid proto in SAD.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		for (stateidx = 0;
+		     stateidx < _ARRAYLEN(saorder_state_any);
+		     stateidx++) {
+			state = saorder_state_any[stateidx];
+			LIST_FOREACH(sav, &sah->savtree[state], chain) {
+				n = key_setdumpsa(sav, SADB_DUMP, satype,
+				    --cnt, mhp->msg->sadb_msg_pid);
+				if (!n)
+					return key_senderror(so, m, ENOBUFS);
+
+				key_sendup_mbuf(so, n, KEY_SENDUP_ONE);
+			}
+		}
+	}
+
+	m_freem(m);
+	return 0;
+}
+
+/*
+ * SADB_X_PROMISC processing
+ *
+ * m will always be freed.
+ */
+static int
+key_promisc(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	int olen;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_promisc: NULL pointer is passed.\n");
+
+	olen = PFKEY_UNUNIT64(mhp->msg->sadb_msg_len);
+
+	if (olen < sizeof(struct sadb_msg)) {
+#if 1
+		return key_senderror(so, m, EINVAL);
+#else
+		m_freem(m);
+		return 0;
+#endif
+	} else if (olen == sizeof(struct sadb_msg)) {
+		/* enable/disable promisc mode */
+		struct keycb *kp;
+
+		if ((kp = (struct keycb *)sotorawcb(so)) == NULL)
+			return key_senderror(so, m, EINVAL);
+		mhp->msg->sadb_msg_errno = 0;
+		switch (mhp->msg->sadb_msg_satype) {
+		case 0:
+		case 1:
+			kp->kp_promisc = mhp->msg->sadb_msg_satype;
+			break;
+		default:
+			return key_senderror(so, m, EINVAL);
+		}
+
+		/* send the original message back to everyone */
+		mhp->msg->sadb_msg_errno = 0;
+		return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
+	} else {
+		/* send packet as is */
+
+		m_adj(m, PFKEY_ALIGN8(sizeof(struct sadb_msg)));
+
+		/* TODO: if sadb_msg_seq is specified, send to specific pid */
+		return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
+	}
+}
+
+static int (*key_typesw[]) __P((struct socket *, struct mbuf *,
+		const struct sadb_msghdr *)) = {
+	NULL,		/* SADB_RESERVED */
+	key_getspi,	/* SADB_GETSPI */
+	key_update,	/* SADB_UPDATE */
+	key_add,	/* SADB_ADD */
+	key_delete,	/* SADB_DELETE */
+	key_get,	/* SADB_GET */
+	key_acquire2,	/* SADB_ACQUIRE */
+	key_register,	/* SADB_REGISTER */
+	NULL,		/* SADB_EXPIRE */
+	key_flush,	/* SADB_FLUSH */
+	key_dump,	/* SADB_DUMP */
+	key_promisc,	/* SADB_X_PROMISC */
+	NULL,		/* SADB_X_PCHANGE */
+	key_spdadd,	/* SADB_X_SPDUPDATE */
+	key_spdadd,	/* SADB_X_SPDADD */
+	key_spddelete,	/* SADB_X_SPDDELETE */
+	key_spdget,	/* SADB_X_SPDGET */
+	NULL,		/* SADB_X_SPDACQUIRE */
+	key_spddump,	/* SADB_X_SPDDUMP */
+	key_spdflush,	/* SADB_X_SPDFLUSH */
+	key_spdadd,	/* SADB_X_SPDSETIDX */
+	NULL,		/* SADB_X_SPDEXPIRE */
+	key_spddelete2,	/* SADB_X_SPDDELETE2 */
+};
+
+/*
+ * parse sadb_msg buffer to process PFKEYv2,
+ * and create a data to response if needed.
+ * I think to be dealed with mbuf directly.
+ * IN:
+ *     msgp  : pointer to pointer to a received buffer pulluped.
+ *             This is rewrited to response.
+ *     so    : pointer to socket.
+ * OUT:
+ *    length for buffer to send to user process.
+ */
+int
+key_parse(m, so)
+	struct mbuf *m;
+	struct socket *so;
+{
+	struct sadb_msg *msg;
+	struct sadb_msghdr mh;
+	u_int orglen;
+	int error;
+	int target;
+
+	/* sanity check */
+	if (m == NULL || so == NULL)
+		panic("key_parse: NULL pointer is passed.\n");
+
+#if 0	/*kdebug_sadb assumes msg in linear buffer*/
+	KEYDEBUG(KEYDEBUG_KEY_DUMP,
+		ipseclog((LOG_DEBUG, "key_parse: passed sadb_msg\n"));
+		kdebug_sadb(msg));
+#endif
+
+	if (m->m_len < sizeof(struct sadb_msg)) {
+		m = m_pullup(m, sizeof(struct sadb_msg));
+		if (!m)
+			return ENOBUFS;
+	}
+	msg = mtod(m, struct sadb_msg *);
+	orglen = PFKEY_UNUNIT64(msg->sadb_msg_len);
+	target = KEY_SENDUP_ONE;
+
+	if ((m->m_flags & M_PKTHDR) == 0 ||
+	    m->m_pkthdr.len != m->m_pkthdr.len) {
+		ipseclog((LOG_DEBUG, "key_parse: invalid message length.\n"));
+		pfkeystat.out_invlen++;
+		error = EINVAL;
+		goto senderror;
+	}
+
+	if (msg->sadb_msg_version != PF_KEY_V2) {
+		ipseclog((LOG_DEBUG,
+		    "key_parse: PF_KEY version %u is mismatched.\n",
+		    msg->sadb_msg_version));
+		pfkeystat.out_invver++;
+		error = EINVAL;
+		goto senderror;
+	}
+
+	if (msg->sadb_msg_type > SADB_MAX) {
+		ipseclog((LOG_DEBUG, "key_parse: invalid type %u is passed.\n",
+		    msg->sadb_msg_type));
+		pfkeystat.out_invmsgtype++;
+		error = EINVAL;
+		goto senderror;
+	}
+
+	/* for old-fashioned code - should be nuked */
+	if (m->m_pkthdr.len > MCLBYTES) {
+		m_freem(m);
+		return ENOBUFS;
+	}
+	if (m->m_next) {
+		struct mbuf *n;
+
+		MGETHDR(n, M_DONTWAIT, MT_DATA);
+		if (n && m->m_pkthdr.len > MHLEN) {
+			MCLGET(n, M_DONTWAIT);
+			if ((n->m_flags & M_EXT) == 0) {
+				m_free(n);
+				n = NULL;
+			}
+		}
+		if (!n) {
+			m_freem(m);
+			return ENOBUFS;
+		}
+		m_copydata(m, 0, m->m_pkthdr.len, mtod(n, caddr_t));
+		n->m_pkthdr.len = n->m_len = m->m_pkthdr.len;
+		n->m_next = NULL;
+		m_freem(m);
+		m = n;
+	}
+
+	/* align the mbuf chain so that extensions are in contiguous region. */
+	error = key_align(m, &mh);
+	if (error)
+		return error;
+
+	if (m->m_next) {	/*XXX*/
+		m_freem(m);
+		return ENOBUFS;
+	}
+
+	msg = mh.msg;
+
+	/* check SA type */
+	switch (msg->sadb_msg_satype) {
+	case SADB_SATYPE_UNSPEC:
+		switch (msg->sadb_msg_type) {
+		case SADB_GETSPI:
+		case SADB_UPDATE:
+		case SADB_ADD:
+		case SADB_DELETE:
+		case SADB_GET:
+		case SADB_ACQUIRE:
+		case SADB_EXPIRE:
+			ipseclog((LOG_DEBUG, "key_parse: must specify satype "
+			    "when msg type=%u.\n", msg->sadb_msg_type));
+			pfkeystat.out_invsatype++;
+			error = EINVAL;
+			goto senderror;
+		}
+		break;
+	case SADB_SATYPE_AH:
+	case SADB_SATYPE_ESP:
+	case SADB_X_SATYPE_IPCOMP:
+		switch (msg->sadb_msg_type) {
+		case SADB_X_SPDADD:
+		case SADB_X_SPDDELETE:
+		case SADB_X_SPDGET:
+		case SADB_X_SPDDUMP:
+		case SADB_X_SPDFLUSH:
+		case SADB_X_SPDSETIDX:
+		case SADB_X_SPDUPDATE:
+		case SADB_X_SPDDELETE2:
+			ipseclog((LOG_DEBUG, "key_parse: illegal satype=%u\n",
+			    msg->sadb_msg_type));
+			pfkeystat.out_invsatype++;
+			error = EINVAL;
+			goto senderror;
+		}
+		break;
+	case SADB_SATYPE_RSVP:
+	case SADB_SATYPE_OSPFV2:
+	case SADB_SATYPE_RIPV2:
+	case SADB_SATYPE_MIP:
+		ipseclog((LOG_DEBUG, "key_parse: type %u isn't supported.\n",
+		    msg->sadb_msg_satype));
+		pfkeystat.out_invsatype++;
+		error = EOPNOTSUPP;
+		goto senderror;
+	case 1:	/* XXX: What does it do? */
+		if (msg->sadb_msg_type == SADB_X_PROMISC)
+			break;
+		/*FALLTHROUGH*/
+	default:
+		ipseclog((LOG_DEBUG, "key_parse: invalid type %u is passed.\n",
+		    msg->sadb_msg_satype));
+		pfkeystat.out_invsatype++;
+		error = EINVAL;
+		goto senderror;
+	}
+
+	/* check field of upper layer protocol and address family */
+	if (mh.ext[SADB_EXT_ADDRESS_SRC] != NULL
+	 && mh.ext[SADB_EXT_ADDRESS_DST] != NULL) {
+		struct sadb_address *src0, *dst0;
+		u_int plen;
+
+		src0 = (struct sadb_address *)(mh.ext[SADB_EXT_ADDRESS_SRC]);
+		dst0 = (struct sadb_address *)(mh.ext[SADB_EXT_ADDRESS_DST]);
+
+		/* check upper layer protocol */
+		if (src0->sadb_address_proto != dst0->sadb_address_proto) {
+			ipseclog((LOG_DEBUG, "key_parse: upper layer protocol mismatched.\n"));
+			pfkeystat.out_invaddr++;
+			error = EINVAL;
+			goto senderror;
+		}
+
+		/* check family */
+		if (PFKEY_ADDR_SADDR(src0)->sa_family !=
+		    PFKEY_ADDR_SADDR(dst0)->sa_family) {
+			ipseclog((LOG_DEBUG, "key_parse: address family mismatched.\n"));
+			pfkeystat.out_invaddr++;
+			error = EINVAL;
+			goto senderror;
+		}
+		if (PFKEY_ADDR_SADDR(src0)->sa_len !=
+		    PFKEY_ADDR_SADDR(dst0)->sa_len) {
+			ipseclog((LOG_DEBUG,
+			    "key_parse: address struct size mismatched.\n"));
+			pfkeystat.out_invaddr++;
+			error = EINVAL;
+			goto senderror;
+		}
+
+		switch (PFKEY_ADDR_SADDR(src0)->sa_family) {
+		case AF_INET:
+			if (PFKEY_ADDR_SADDR(src0)->sa_len !=
+			    sizeof(struct sockaddr_in)) {
+				pfkeystat.out_invaddr++;
+				error = EINVAL;
+				goto senderror;
+			}
+			break;
+		case AF_INET6:
+			if (PFKEY_ADDR_SADDR(src0)->sa_len !=
+			    sizeof(struct sockaddr_in6)) {
+				pfkeystat.out_invaddr++;
+				error = EINVAL;
+				goto senderror;
+			}
+			break;
+		default:
+			ipseclog((LOG_DEBUG,
+			    "key_parse: unsupported address family.\n"));
+			pfkeystat.out_invaddr++;
+			error = EAFNOSUPPORT;
+			goto senderror;
+		}
+
+		switch (PFKEY_ADDR_SADDR(src0)->sa_family) {
+		case AF_INET:
+			plen = sizeof(struct in_addr) << 3;
+			break;
+		case AF_INET6:
+			plen = sizeof(struct in6_addr) << 3;
+			break;
+		default:
+			plen = 0;	/*fool gcc*/
+			break;
+		}
+
+		/* check max prefix length */
+		if (src0->sadb_address_prefixlen > plen ||
+		    dst0->sadb_address_prefixlen > plen) {
+			ipseclog((LOG_DEBUG,
+			    "key_parse: illegal prefixlen.\n"));
+			pfkeystat.out_invaddr++;
+			error = EINVAL;
+			goto senderror;
+		}
+
+		/*
+		 * prefixlen == 0 is valid because there can be a case when
+		 * all addresses are matched.
+		 */
+	}
+
+	if (msg->sadb_msg_type >= sizeof(key_typesw)/sizeof(key_typesw[0]) ||
+	    key_typesw[msg->sadb_msg_type] == NULL) {
+		pfkeystat.out_invmsgtype++;
+		error = EINVAL;
+		goto senderror;
+	}
+
+	return (*key_typesw[msg->sadb_msg_type])(so, m, &mh);
+
+senderror:
+	msg->sadb_msg_errno = error;
+	return key_sendup_mbuf(so, m, target);
+}
+
+static int
+key_senderror(so, m, code)
+	struct socket *so;
+	struct mbuf *m;
+	int code;
+{
+	struct sadb_msg *msg;
+
+	if (m->m_len < sizeof(struct sadb_msg))
+		panic("invalid mbuf passed to key_senderror");
+
+	msg = mtod(m, struct sadb_msg *);
+	msg->sadb_msg_errno = code;
+	return key_sendup_mbuf(so, m, KEY_SENDUP_ONE);
+}
+
+/*
+ * set the pointer to each header into message buffer.
+ * m will be freed on error.
+ * XXX larger-than-MCLBYTES extension?
+ */
+static int
+key_align(m, mhp)
+	struct mbuf *m;
+	struct sadb_msghdr *mhp;
+{
+	struct mbuf *n;
+	struct sadb_ext *ext;
+	size_t off, end;
+	int extlen;
+	int toff;
+
+	/* sanity check */
+	if (m == NULL || mhp == NULL)
+		panic("key_align: NULL pointer is passed.\n");
+	if (m->m_len < sizeof(struct sadb_msg))
+		panic("invalid mbuf passed to key_align");
+
+	/* initialize */
+	bzero(mhp, sizeof(*mhp));
+
+	mhp->msg = mtod(m, struct sadb_msg *);
+	mhp->ext[0] = (struct sadb_ext *)mhp->msg;	/*XXX backward compat */
+
+	end = PFKEY_UNUNIT64(mhp->msg->sadb_msg_len);
+	extlen = end;	/*just in case extlen is not updated*/
+	for (off = sizeof(struct sadb_msg); off < end; off += extlen) {
+		n = m_pulldown(m, off, sizeof(struct sadb_ext), &toff);
+		if (!n) {
+			/* m is already freed */
+			return ENOBUFS;
+		}
+		ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff);
+
+		/* set pointer */
+		switch (ext->sadb_ext_type) {
+		case SADB_EXT_SA:
+		case SADB_EXT_ADDRESS_SRC:
+		case SADB_EXT_ADDRESS_DST:
+		case SADB_EXT_ADDRESS_PROXY:
+		case SADB_EXT_LIFETIME_CURRENT:
+		case SADB_EXT_LIFETIME_HARD:
+		case SADB_EXT_LIFETIME_SOFT:
+		case SADB_EXT_KEY_AUTH:
+		case SADB_EXT_KEY_ENCRYPT:
+		case SADB_EXT_IDENTITY_SRC:
+		case SADB_EXT_IDENTITY_DST:
+		case SADB_EXT_SENSITIVITY:
+		case SADB_EXT_PROPOSAL:
+		case SADB_EXT_SUPPORTED_AUTH:
+		case SADB_EXT_SUPPORTED_ENCRYPT:
+		case SADB_EXT_SPIRANGE:
+		case SADB_X_EXT_POLICY:
+		case SADB_X_EXT_SA2:
+			/* duplicate check */
+			/*
+			 * XXX Are there duplication payloads of either
+			 * KEY_AUTH or KEY_ENCRYPT ?
+			 */
+			if (mhp->ext[ext->sadb_ext_type] != NULL) {
+				ipseclog((LOG_DEBUG,
+				    "key_align: duplicate ext_type %u "
+				    "is passed.\n", ext->sadb_ext_type));
+				m_freem(m);
+				pfkeystat.out_dupext++;
+				return EINVAL;
+			}
+			break;
+		default:
+			ipseclog((LOG_DEBUG,
+			    "key_align: invalid ext_type %u is passed.\n",
+			    ext->sadb_ext_type));
+			m_freem(m);
+			pfkeystat.out_invexttype++;
+			return EINVAL;
+		}
+
+		extlen = PFKEY_UNUNIT64(ext->sadb_ext_len);
+
+		if (key_validate_ext(ext, extlen)) {
+			m_freem(m);
+			pfkeystat.out_invlen++;
+			return EINVAL;
+		}
+
+		n = m_pulldown(m, off, extlen, &toff);
+		if (!n) {
+			/* m is already freed */
+			return ENOBUFS;
+		}
+		ext = (struct sadb_ext *)(mtod(n, caddr_t) + toff);
+
+		mhp->ext[ext->sadb_ext_type] = ext;
+		mhp->extoff[ext->sadb_ext_type] = off;
+		mhp->extlen[ext->sadb_ext_type] = extlen;
+	}
+
+	if (off != end) {
+		m_freem(m);
+		pfkeystat.out_invlen++;
+		return EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+key_validate_ext(ext, len)
+	const struct sadb_ext *ext;
+	int len;
+{
+	struct sockaddr *sa;
+	enum { NONE, ADDR } checktype = NONE;
+	int baselen = 0;
+	const int sal = offsetof(struct sockaddr, sa_len) + sizeof(sa->sa_len);
+
+	if (len != PFKEY_UNUNIT64(ext->sadb_ext_len))
+		return EINVAL;
+
+	/* if it does not match minimum/maximum length, bail */
+	if (ext->sadb_ext_type >= sizeof(minsize) / sizeof(minsize[0]) ||
+	    ext->sadb_ext_type >= sizeof(maxsize) / sizeof(maxsize[0]))
+		return EINVAL;
+	if (!minsize[ext->sadb_ext_type] || len < minsize[ext->sadb_ext_type])
+		return EINVAL;
+	if (maxsize[ext->sadb_ext_type] && len > maxsize[ext->sadb_ext_type])
+		return EINVAL;
+
+	/* more checks based on sadb_ext_type XXX need more */
+	switch (ext->sadb_ext_type) {
+	case SADB_EXT_ADDRESS_SRC:
+	case SADB_EXT_ADDRESS_DST:
+	case SADB_EXT_ADDRESS_PROXY:
+		baselen = PFKEY_ALIGN8(sizeof(struct sadb_address));
+		checktype = ADDR;
+		break;
+	case SADB_EXT_IDENTITY_SRC:
+	case SADB_EXT_IDENTITY_DST:
+		if (((struct sadb_ident *)ext)->sadb_ident_type ==
+		    SADB_X_IDENTTYPE_ADDR) {
+			baselen = PFKEY_ALIGN8(sizeof(struct sadb_ident));
+			checktype = ADDR;
+		} else
+			checktype = NONE;
+		break;
+	default:
+		checktype = NONE;
+		break;
+	}
+
+	switch (checktype) {
+	case NONE:
+		break;
+	case ADDR:
+		sa = (struct sockaddr *)((caddr_t)ext + baselen);
+		if (len < baselen + sal)
+			return EINVAL;
+		if (baselen + PFKEY_ALIGN8(sa->sa_len) != len)
+			return EINVAL;
+		break;
+	}
+
+	return 0;
+}
+
+void
+key_init()
+{
+	int i;
+
+	bzero((caddr_t)&key_cb, sizeof(key_cb));
+
+#ifdef __NetBSD__
+	callout_init(&key_timehandler_ch);
+#endif
+
+	for (i = 0; i < IPSEC_DIR_MAX; i++) {
+		LIST_INIT(&sptree[i]);
+	}
+
+	LIST_INIT(&sahtree);
+
+	for (i = 0; i <= SADB_SATYPE_MAX; i++) {
+		LIST_INIT(&regtree[i]);
+	}
+
+#ifndef IPSEC_NONBLOCK_ACQUIRE
+	LIST_INIT(&acqtree);
+#endif
+	LIST_INIT(&spacqtree);
+
+	/* system default */
+#ifdef INET
+	ip4_def_policy.policy = IPSEC_POLICY_NONE;
+	ip4_def_policy.refcnt++;	/*never reclaim this*/
+#endif
+#ifdef INET6
+	ip6_def_policy.policy = IPSEC_POLICY_NONE;
+	ip6_def_policy.refcnt++;	/*never reclaim this*/
+#endif
+
+#ifdef __NetBSD__
+	callout_reset(&key_timehandler_ch, hz,
+	    (void *)key_timehandler, (void *)0);
+#else
+	timeout((void *)key_timehandler, (void *)0, hz);
+#endif
+
+	/* initialize key statistics */
+	keystat.getspi_count = 1;
+
+	printf("IPsec: Initialized Security Association Processing.\n");
+
+	return;
+}
+
+/*
+ * XXX: maybe This function is called after INBOUND IPsec processing.
+ *
+ * Special check for tunnel-mode packets.
+ * We must make some checks for consistency between inner and outer IP header.
+ *
+ * xxx more checks to be provided
+ */
+int
+key_checktunnelsanity(sav, family, src, dst)
+	struct secasvar *sav;
+	u_int family;
+	caddr_t src;
+	caddr_t dst;
+{
+	/* sanity check */
+	if (sav->sah == NULL)
+		panic("sav->sah == NULL at key_checktunnelsanity");
+
+	/* XXX: check inner IP header */
+
+	return 1;
+}
+
+#if 0
+#ifdef __FreeBSD__
+#define hostnamelen	strlen(hostname)
+#endif
+
+/*
+ * Get FQDN for the host.
+ * If the administrator configured hostname (by hostname(1)) without
+ * domain name, returns nothing.
+ */
+static const char *
+key_getfqdn()
+{
+	int i;
+	int hasdot;
+	static char fqdn[MAXHOSTNAMELEN + 1];
+
+	if (!hostnamelen)
+		return NULL;
+
+	/* check if it comes with domain name. */
+	hasdot = 0;
+	for (i = 0; i < hostnamelen; i++) {
+		if (hostname[i] == '.')
+			hasdot++;
+	}
+	if (!hasdot)
+		return NULL;
+
+	/* NOTE: hostname may not be NUL-terminated. */
+	bzero(fqdn, sizeof(fqdn));
+	bcopy(hostname, fqdn, hostnamelen);
+	fqdn[hostnamelen] = '\0';
+	return fqdn;
+}
+
+/*
+ * get username@FQDN for the host/user.
+ */
+static const char *
+key_getuserfqdn()
+{
+	const char *host;
+	static char userfqdn[MAXHOSTNAMELEN + MAXLOGNAME + 2];
+	struct proc *p = curproc;
+	char *q;
+
+	if (!p || !p->p_pgrp || !p->p_pgrp->pg_session)
+		return NULL;
+	if (!(host = key_getfqdn()))
+		return NULL;
+
+	/* NOTE: s_login may not be-NUL terminated. */
+	bzero(userfqdn, sizeof(userfqdn));
+	bcopy(p->p_pgrp->pg_session->s_login, userfqdn, MAXLOGNAME);
+	userfqdn[MAXLOGNAME] = '\0';	/* safeguard */
+	q = userfqdn + strlen(userfqdn);
+	*q++ = '@';
+	bcopy(host, q, strlen(host));
+	q += strlen(host);
+	*q++ = '\0';
+
+	return userfqdn;
+}
+#endif
+
+/* record data transfer on SA, and update timestamps */
+void
+key_sa_recordxfer(sav, m)
+	struct secasvar *sav;
+	struct mbuf *m;
+{
+	if (!sav)
+		panic("key_sa_recordxfer called with sav == NULL");
+	if (!m)
+		panic("key_sa_recordxfer called with m == NULL");
+	if (!sav->lft_c)
+		return;
+
+	/*
+	 * XXX Currently, there is a difference of bytes size
+	 * between inbound and outbound processing.
+	 */
+	sav->lft_c->sadb_lifetime_bytes += m->m_pkthdr.len;
+	/* to check bytes lifetime is done in key_timehandler(). */
+
+	/*
+	 * We use the number of packets as the unit of
+	 * sadb_lifetime_allocations.  We increment the variable
+	 * whenever {esp,ah}_{in,out}put is called.
+	 */
+	sav->lft_c->sadb_lifetime_allocations++;
+	/* XXX check for expires? */
+
+	/*
+	 * NOTE: We record CURRENT sadb_lifetime_usetime by using wall clock,
+	 * in seconds.  HARD and SOFT lifetime are measured by the time
+	 * difference (again in seconds) from sadb_lifetime_usetime.
+	 *
+	 *	usetime
+	 *	v     expire   expire
+	 * -----+-----+--------+---> t
+	 *	<--------------> HARD
+	 *	<-----> SOFT
+	 */
+    {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+	sav->lft_c->sadb_lifetime_usetime = time_second;
+#else
+	sav->lft_c->sadb_lifetime_usetime = time.tv_sec;
+#endif
+	/* XXX check for expires? */
+    }
+
+	return;
+}
+
+/* dumb version */
+void
+key_sa_routechange(dst)
+	struct sockaddr *dst;
+{
+	struct secashead *sah;
+	struct route *ro;
+
+	LIST_FOREACH(sah, &sahtree, chain) {
+		ro = &sah->sa_route;
+		if (ro->ro_rt && dst->sa_len == ro->ro_dst.sa_len
+		 && bcmp(dst, &ro->ro_dst, dst->sa_len) == 0) {
+			RTFREE(ro->ro_rt);
+			ro->ro_rt = (struct rtentry *)NULL;
+		}
+	}
+
+	return;
+}
+
+static void
+key_sa_chgstate(sav, state)
+	struct secasvar *sav;
+	u_int8_t state;
+{
+	if (sav == NULL)
+		panic("key_sa_chgstate called with sav == NULL");
+
+	if (sav->state == state)
+		return;
+
+	if (__LIST_CHAINED(sav))
+		LIST_REMOVE(sav, chain);
+
+	sav->state = state;
+	LIST_INSERT_HEAD(&sav->sah->savtree[state], sav, chain);
+}
+
+void
+key_sa_stir_iv(sav)
+	struct secasvar *sav;
+{
+
+	if (!sav->iv)
+		panic("key_sa_stir_iv called with sav == NULL");
+	key_randomfill(sav->iv, sav->ivlen);
+}
+
+/* XXX too much? */
+static struct mbuf *
+key_alloc_mbuf(l)
+	int l;
+{
+	struct mbuf *m = NULL, *n;
+	int len, t;
+
+	len = l;
+	while (len > 0) {
+		MGET(n, M_DONTWAIT, MT_DATA);
+		if (n && len > MLEN)
+			MCLGET(n, M_DONTWAIT);
+		if (!n) {
+			m_freem(m);
+			return NULL;
+		}
+
+		n->m_next = NULL;
+		n->m_len = 0;
+		n->m_len = M_TRAILINGSPACE(n);
+		/* use the bottom of mbuf, hoping we can prepend afterwards */
+		if (n->m_len > len) {
+			t = (n->m_len - len) & ~(sizeof(long) - 1);
+			n->m_data += t;
+			n->m_len = len;
+		}
+
+		len -= n->m_len;
+
+		if (m)
+			m_cat(m, n);
+		else
+			m = n;
+	}
+
+	return m;
+}
+
+#ifdef __bsdi__
+#include <sys/user.h>
+#include <sys/sysctl.h>
+
+int *key_sysvars[] = KEYCTL_VARS;
+
+int
+key_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int *name;
+	u_int namelen;
+	void *oldp;
+	size_t *oldlenp;
+	void *newp;
+	size_t newlen;
+{
+	if (name[0] >= KEYCTL_MAXID)
+		return EOPNOTSUPP;
+	switch (name[0]) {
+	case KEYCTL_DEBUG_LEVEL:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_debug_level);
+	default:
+		return sysctl_int_arr(key_sysvars, name, namelen,
+			oldp, oldlenp, newp, newlen);
+	}
+}
+#endif /*__bsdi__*/
+
+#ifdef __NetBSD__
+#include <vm/vm.h>
+#include <sys/sysctl.h>
+
+int
+key_sysctl(name, namelen, oldp, oldlenp, newp, newlen)
+	int *name;
+	u_int namelen;
+	void *oldp;
+	size_t *oldlenp;
+	void *newp;
+	size_t newlen;
+{
+	if (name[0] >= KEYCTL_MAXID)
+		return EOPNOTSUPP;
+	switch (name[0]) {
+	case KEYCTL_DEBUG_LEVEL:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_debug_level);
+	case KEYCTL_SPI_TRY:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_spi_trycnt);
+	case KEYCTL_SPI_MIN_VALUE:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_spi_minval);
+	case KEYCTL_SPI_MAX_VALUE:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_spi_maxval);
+	case KEYCTL_RANDOM_INT:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_int_random);
+	case KEYCTL_LARVAL_LIFETIME:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_larval_lifetime);
+	case KEYCTL_BLOCKACQ_COUNT:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_blockacq_count);
+	case KEYCTL_BLOCKACQ_LIFETIME:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_blockacq_lifetime);
+	case KEYCTL_ESP_KEYMIN:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &ipsec_esp_keymin);
+	case KEYCTL_ESP_AUTH:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &ipsec_esp_auth);
+	case KEYCTL_AH_KEYMIN:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &ipsec_ah_keymin);
+	case KEYCTL_PREFERED_OLDSA:
+		return sysctl_int(oldp, oldlenp, newp, newlen,
+		    &key_preferred_oldsa);
+	default:
+		return EOPNOTSUPP;
+	}
+	/* NOTREACHED */
+}
+#endif /*__NetBSD__*/
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netkey/key_debug.c
@@ -0,0 +1,762 @@
+//==========================================================================
+//
+//      src/sys/netkey/key_debug.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: key_debug.c,v 1.29 2001/08/16 14:25:41 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef _KERNEL
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#endif
+#include <sys/socket.h>
+
+#include <net/route.h>
+
+#include <netkey/key_var.h>
+#include <netkey/key_debug.h>
+
+#include <netinet/in.h>
+#include <netinet6/ipsec.h>
+
+#ifndef _KERNEL
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#endif /* !_KERNEL */
+
+static void kdebug_sadb_prop __P((struct sadb_ext *));
+static void kdebug_sadb_identity __P((struct sadb_ext *));
+static void kdebug_sadb_supported __P((struct sadb_ext *));
+static void kdebug_sadb_lifetime __P((struct sadb_ext *));
+static void kdebug_sadb_sa __P((struct sadb_ext *));
+static void kdebug_sadb_address __P((struct sadb_ext *));
+static void kdebug_sadb_key __P((struct sadb_ext *));
+static void kdebug_sadb_x_sa2 __P((struct sadb_ext *));
+
+#ifdef _KERNEL
+static void kdebug_secreplay __P((struct secreplay *));
+#endif
+
+#ifndef _KERNEL
+#define panic(param)	{ printf(param); exit(1); }
+#endif
+
+/* NOTE: host byte order */
+
+/* %%%: about struct sadb_msg */
+void
+kdebug_sadb(base)
+	struct sadb_msg *base;
+{
+	struct sadb_ext *ext;
+	int tlen, extlen;
+
+	/* sanity check */
+	if (base == NULL)
+		panic("kdebug_sadb: NULL pointer was passed.\n");
+
+	printf("sadb_msg{ version=%u type=%u errno=%u satype=%u\n",
+	    base->sadb_msg_version, base->sadb_msg_type,
+	    base->sadb_msg_errno, base->sadb_msg_satype);
+	printf("  len=%u reserved=%u seq=%u pid=%u\n",
+	    base->sadb_msg_len, base->sadb_msg_reserved,
+	    base->sadb_msg_seq, base->sadb_msg_pid);
+
+	tlen = PFKEY_UNUNIT64(base->sadb_msg_len) - sizeof(struct sadb_msg);
+	ext = (struct sadb_ext *)((caddr_t)base + sizeof(struct sadb_msg));
+
+	while (tlen > 0) {
+		printf("sadb_ext{ len=%u type=%u }\n",
+		    ext->sadb_ext_len, ext->sadb_ext_type);
+
+		if (ext->sadb_ext_len == 0) {
+			printf("kdebug_sadb: invalid ext_len=0 was passed.\n");
+			return;
+		}
+		if (ext->sadb_ext_len > tlen) {
+			printf("kdebug_sadb: ext_len exceeds end of buffer.\n");
+			return;
+		}
+
+		switch (ext->sadb_ext_type) {
+		case SADB_EXT_SA:
+			kdebug_sadb_sa(ext);
+			break;
+		case SADB_EXT_LIFETIME_CURRENT:
+		case SADB_EXT_LIFETIME_HARD:
+		case SADB_EXT_LIFETIME_SOFT:
+			kdebug_sadb_lifetime(ext);
+			break;
+		case SADB_EXT_ADDRESS_SRC:
+		case SADB_EXT_ADDRESS_DST:
+		case SADB_EXT_ADDRESS_PROXY:
+			kdebug_sadb_address(ext);
+			break;
+		case SADB_EXT_KEY_AUTH:
+		case SADB_EXT_KEY_ENCRYPT:
+			kdebug_sadb_key(ext);
+			break;
+		case SADB_EXT_IDENTITY_SRC:
+		case SADB_EXT_IDENTITY_DST:
+			kdebug_sadb_identity(ext);
+			break;
+		case SADB_EXT_SENSITIVITY:
+			break;
+		case SADB_EXT_PROPOSAL:
+			kdebug_sadb_prop(ext);
+			break;
+		case SADB_EXT_SUPPORTED_AUTH:
+		case SADB_EXT_SUPPORTED_ENCRYPT:
+			kdebug_sadb_supported(ext);
+			break;
+		case SADB_EXT_SPIRANGE:
+		case SADB_X_EXT_KMPRIVATE:
+			break;
+		case SADB_X_EXT_POLICY:
+			kdebug_sadb_x_policy(ext);
+			break;
+		case SADB_X_EXT_SA2:
+			kdebug_sadb_x_sa2(ext);
+			break;
+		default:
+			printf("kdebug_sadb: invalid ext_type %u was passed.\n",
+			    ext->sadb_ext_type);
+			return;
+		}
+
+		extlen = PFKEY_UNUNIT64(ext->sadb_ext_len);
+		tlen -= extlen;
+		ext = (struct sadb_ext *)((caddr_t)ext + extlen);
+	}
+
+	return;
+}
+
+static void
+kdebug_sadb_prop(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_prop *prop = (struct sadb_prop *)ext;
+	struct sadb_comb *comb;
+	int len;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_prop: NULL pointer was passed.\n");
+
+	len = (PFKEY_UNUNIT64(prop->sadb_prop_len) - sizeof(*prop))
+		/ sizeof(*comb);
+	comb = (struct sadb_comb *)(prop + 1);
+	printf("sadb_prop{ replay=%u\n", prop->sadb_prop_replay);
+
+	while (len--) {
+		printf("sadb_comb{ auth=%u encrypt=%u "
+			"flags=0x%04x reserved=0x%08x\n",
+			comb->sadb_comb_auth, comb->sadb_comb_encrypt,
+			comb->sadb_comb_flags, comb->sadb_comb_reserved);
+
+		printf("  auth_minbits=%u auth_maxbits=%u "
+			"encrypt_minbits=%u encrypt_maxbits=%u\n",
+			comb->sadb_comb_auth_minbits,
+			comb->sadb_comb_auth_maxbits,
+			comb->sadb_comb_encrypt_minbits,
+			comb->sadb_comb_encrypt_maxbits);
+
+		printf("  soft_alloc=%u hard_alloc=%u "
+			"soft_bytes=%lu hard_bytes=%lu\n",
+			comb->sadb_comb_soft_allocations,
+			comb->sadb_comb_hard_allocations,
+			(unsigned long)comb->sadb_comb_soft_bytes,
+			(unsigned long)comb->sadb_comb_hard_bytes);
+
+		printf("  soft_alloc=%lu hard_alloc=%lu "
+			"soft_bytes=%lu hard_bytes=%lu }\n",
+			(unsigned long)comb->sadb_comb_soft_addtime,
+			(unsigned long)comb->sadb_comb_hard_addtime,
+			(unsigned long)comb->sadb_comb_soft_usetime,
+			(unsigned long)comb->sadb_comb_hard_usetime);
+		comb++;
+	}
+	printf("}\n");
+
+	return;
+}
+
+static void
+kdebug_sadb_identity(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_ident *id = (struct sadb_ident *)ext;
+	int len;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_identity: NULL pointer was passed.\n");
+
+	len = PFKEY_UNUNIT64(id->sadb_ident_len) - sizeof(*id);
+	printf("sadb_ident_%s{",
+	    id->sadb_ident_exttype == SADB_EXT_IDENTITY_SRC ? "src" : "dst");
+	switch (id->sadb_ident_type) {
+	default:
+		printf(" type=%d id=%lu",
+			id->sadb_ident_type, (u_long)id->sadb_ident_id);
+		if (len) {
+#ifdef _KERNEL
+			ipsec_hexdump((caddr_t)(id + 1), len); /*XXX cast ?*/
+#else
+			char *p, *ep;
+			printf("\n  str=\"");
+			p = (char *)(id + 1);
+			ep = p + len;
+			for (/*nothing*/; *p && p < ep; p++) {
+				if (isprint(*p))
+					printf("%c", *p & 0xff);
+				else
+					printf("\\%03o", *p & 0xff);
+			}
+#endif
+			printf("\"");
+		}
+		break;
+	}
+
+	printf(" }\n");
+
+	return;
+}
+
+static void
+kdebug_sadb_supported(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_supported *sup = (struct sadb_supported *)ext;
+	struct sadb_alg *alg;
+	int len;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_supported: NULL pointer was passed.\n");
+
+	len = (PFKEY_UNUNIT64(sup->sadb_supported_len) - sizeof(*sup))
+		/ sizeof(*alg);
+	alg = (struct sadb_alg *)(sup + 1);
+	printf("sadb_sup{\n");
+	while (len--) {
+		printf("  { id=%d ivlen=%d min=%d max=%d }\n",
+			alg->sadb_alg_id, alg->sadb_alg_ivlen,
+			alg->sadb_alg_minbits, alg->sadb_alg_maxbits);
+		alg++;
+	}
+	printf("}\n");
+
+	return;
+}
+
+static void
+kdebug_sadb_lifetime(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_lifetime *lft = (struct sadb_lifetime *)ext;
+
+	/* sanity check */
+	if (ext == NULL)
+		printf("kdebug_sadb_lifetime: NULL pointer was passed.\n");
+
+	printf("sadb_lifetime{ alloc=%u, bytes=%u\n",
+		lft->sadb_lifetime_allocations,
+		(u_int32_t)lft->sadb_lifetime_bytes);
+	printf("  addtime=%u, usetime=%u }\n",
+		(u_int32_t)lft->sadb_lifetime_addtime,
+		(u_int32_t)lft->sadb_lifetime_usetime);
+
+	return;
+}
+
+static void
+kdebug_sadb_sa(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_sa *sa = (struct sadb_sa *)ext;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_sa: NULL pointer was passed.\n");
+
+	printf("sadb_sa{ spi=%u replay=%u state=%u\n",
+	    (u_int32_t)ntohl(sa->sadb_sa_spi), sa->sadb_sa_replay,
+	    sa->sadb_sa_state);
+	printf("  auth=%u encrypt=%u flags=0x%08x }\n",
+	    sa->sadb_sa_auth, sa->sadb_sa_encrypt, sa->sadb_sa_flags);
+
+	return;
+}
+
+static void
+kdebug_sadb_address(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_address *addr = (struct sadb_address *)ext;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_address: NULL pointer was passed.\n");
+
+	printf("sadb_address{ proto=%u prefixlen=%u reserved=0x%02x%02x }\n",
+	    addr->sadb_address_proto, addr->sadb_address_prefixlen,
+	    ((u_char *)&addr->sadb_address_reserved)[0],
+	    ((u_char *)&addr->sadb_address_reserved)[1]);
+
+	kdebug_sockaddr((struct sockaddr *)((caddr_t)ext + sizeof(*addr)));
+
+	return;
+}
+
+static void
+kdebug_sadb_key(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_key *key = (struct sadb_key *)ext;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_key: NULL pointer was passed.\n");
+
+	printf("sadb_key{ bits=%u reserved=%u\n",
+	    key->sadb_key_bits, key->sadb_key_reserved);
+	printf("  key=");
+
+	/* sanity check 2 */
+	if ((key->sadb_key_bits >> 3) >
+		(PFKEY_UNUNIT64(key->sadb_key_len) - sizeof(struct sadb_key))) {
+		printf("kdebug_sadb_key: key length mismatch, bit:%d len:%ld.\n",
+			key->sadb_key_bits >> 3,
+			(long)PFKEY_UNUNIT64(key->sadb_key_len) - sizeof(struct sadb_key));
+	}
+
+	ipsec_hexdump((caddr_t)key + sizeof(struct sadb_key),
+	              key->sadb_key_bits >> 3);
+	printf(" }\n");
+	return;
+}
+
+static void
+kdebug_sadb_x_sa2(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_x_sa2 *sa2 = (struct sadb_x_sa2 *)ext;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_x_sa2: NULL pointer was passed.\n");
+
+	printf("sadb_x_sa2{ mode=%u reqid=%u\n",
+	    sa2->sadb_x_sa2_mode, sa2->sadb_x_sa2_reqid);
+	printf("  reserved1=%u reserved2=%u sequence=%u }\n",
+	    sa2->sadb_x_sa2_reserved1, sa2->sadb_x_sa2_reserved2,
+	    sa2->sadb_x_sa2_sequence);
+
+	return;
+}
+
+void
+kdebug_sadb_x_policy(ext)
+	struct sadb_ext *ext;
+{
+	struct sadb_x_policy *xpl = (struct sadb_x_policy *)ext;
+	struct sockaddr *addr;
+
+	/* sanity check */
+	if (ext == NULL)
+		panic("kdebug_sadb_x_policy: NULL pointer was passed.\n");
+
+	printf("sadb_x_policy{ type=%u dir=%u id=%x }\n",
+		xpl->sadb_x_policy_type, xpl->sadb_x_policy_dir,
+		xpl->sadb_x_policy_id);
+
+	if (xpl->sadb_x_policy_type == IPSEC_POLICY_IPSEC) {
+		int tlen;
+		struct sadb_x_ipsecrequest *xisr;
+
+		tlen = PFKEY_UNUNIT64(xpl->sadb_x_policy_len) - sizeof(*xpl);
+		xisr = (struct sadb_x_ipsecrequest *)(xpl + 1);
+
+		while (tlen > 0) {
+			printf(" { len=%u proto=%u mode=%u level=%u reqid=%u\n",
+				xisr->sadb_x_ipsecrequest_len,
+				xisr->sadb_x_ipsecrequest_proto,
+				xisr->sadb_x_ipsecrequest_mode,
+				xisr->sadb_x_ipsecrequest_level,
+				xisr->sadb_x_ipsecrequest_reqid);
+
+			if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
+				addr = (struct sockaddr *)(xisr + 1);
+				kdebug_sockaddr(addr);
+				addr = (struct sockaddr *)((caddr_t)addr
+							+ addr->sa_len);
+				kdebug_sockaddr(addr);
+			}
+
+			printf(" }\n");
+
+			/* prevent infinite loop */
+			if (xisr->sadb_x_ipsecrequest_len <= 0) {
+				printf("kdebug_sadb_x_policy: wrong policy struct.\n");
+				return;
+			}
+			/* prevent overflow */
+			if (xisr->sadb_x_ipsecrequest_len > tlen) {
+				printf("invalid ipsec policy length\n");
+				return;
+			}
+
+			tlen -= xisr->sadb_x_ipsecrequest_len;
+
+			xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr
+			                + xisr->sadb_x_ipsecrequest_len);
+		}
+
+		if (tlen != 0)
+			panic("kdebug_sadb_x_policy: wrong policy struct.\n");
+	}
+
+	return;
+}
+
+#ifdef _KERNEL
+/* %%%: about SPD and SAD */
+void
+kdebug_secpolicy(sp)
+	struct secpolicy *sp;
+{
+	/* sanity check */
+	if (sp == NULL)
+		panic("kdebug_secpolicy: NULL pointer was passed.\n");
+
+	printf("secpolicy{ refcnt=%u state=%u policy=%u\n",
+		sp->refcnt, sp->state, sp->policy);
+
+	kdebug_secpolicyindex(&sp->spidx);
+
+	switch (sp->policy) {
+	case IPSEC_POLICY_DISCARD:
+		printf("  type=discard }\n");
+		break;
+	case IPSEC_POLICY_NONE:
+		printf("  type=none }\n");
+		break;
+	case IPSEC_POLICY_IPSEC:
+	    {
+		struct ipsecrequest *isr;
+		for (isr = sp->req; isr != NULL; isr = isr->next) {
+
+			printf("  level=%u\n", isr->level);
+			kdebug_secasindex(&isr->saidx);
+
+			if (isr->sav != NULL)
+				kdebug_secasv(isr->sav);
+		}
+		printf("  }\n");
+	    }
+		break;
+	case IPSEC_POLICY_BYPASS:
+		printf("  type=bypass }\n");
+		break;
+	case IPSEC_POLICY_ENTRUST:
+		printf("  type=entrust }\n");
+		break;
+	default:
+		printf("kdebug_secpolicy: Invalid policy found. %d\n",
+			sp->policy);
+		break;
+	}
+
+	return;
+}
+
+void
+kdebug_secpolicyindex(spidx)
+	struct secpolicyindex *spidx;
+{
+	/* sanity check */
+	if (spidx == NULL)
+		panic("kdebug_secpolicyindex: NULL pointer was passed.\n");
+
+	printf("secpolicyindex{ dir=%u prefs=%u prefd=%u ul_proto=%u\n",
+		spidx->dir, spidx->prefs, spidx->prefd, spidx->ul_proto);
+
+	ipsec_hexdump((caddr_t)&spidx->src,
+		((struct sockaddr *)&spidx->src)->sa_len);
+	printf("\n");
+	ipsec_hexdump((caddr_t)&spidx->dst,
+		((struct sockaddr *)&spidx->dst)->sa_len);
+	printf("}\n");
+
+	return;
+}
+
+void
+kdebug_secasindex(saidx)
+	struct secasindex *saidx;
+{
+	/* sanity check */
+	if (saidx == NULL)
+		panic("kdebug_secpolicyindex: NULL pointer was passed.\n");
+
+	printf("secasindex{ mode=%u proto=%u\n",
+		saidx->mode, saidx->proto);
+
+	ipsec_hexdump((caddr_t)&saidx->src,
+		((struct sockaddr *)&saidx->src)->sa_len);
+	printf("\n");
+	ipsec_hexdump((caddr_t)&saidx->dst,
+		((struct sockaddr *)&saidx->dst)->sa_len);
+	printf("\n");
+
+	return;
+}
+
+void
+kdebug_secasv(sav)
+	struct secasvar *sav;
+{
+	/* sanity check */
+	if (sav == NULL)
+		panic("kdebug_secasv: NULL pointer was passed.\n");
+
+	printf("secas{");
+	kdebug_secasindex(&sav->sah->saidx);
+
+	printf("  refcnt=%u state=%u auth=%u enc=%u\n",
+	    sav->refcnt, sav->state, sav->alg_auth, sav->alg_enc);
+	printf("  spi=%u flags=%u\n",
+	    (u_int32_t)ntohl(sav->spi), sav->flags);
+
+	if (sav->key_auth != NULL)
+		kdebug_sadb_key((struct sadb_ext *)sav->key_auth);
+	if (sav->key_enc != NULL)
+		kdebug_sadb_key((struct sadb_ext *)sav->key_enc);
+	if (sav->iv != NULL) {
+		printf("  iv=");
+		ipsec_hexdump(sav->iv, sav->ivlen ? sav->ivlen : 8);
+		printf("\n");
+	}
+
+	if (sav->replay != NULL)
+		kdebug_secreplay(sav->replay);
+	if (sav->lft_c != NULL)
+		kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_c);
+	if (sav->lft_h != NULL)
+		kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_h);
+	if (sav->lft_s != NULL)
+		kdebug_sadb_lifetime((struct sadb_ext *)sav->lft_s);
+
+#if defined (notyet)
+	/* XXX: misc[123] ? */
+#endif
+
+	return;
+}
+
+static void
+kdebug_secreplay(rpl)
+	struct secreplay *rpl;
+{
+	int len, l;
+
+	/* sanity check */
+	if (rpl == NULL)
+		panic("kdebug_secreplay: NULL pointer was passed.\n");
+
+	printf(" secreplay{ count=%u wsize=%u seq=%u lastseq=%u",
+	    rpl->count, rpl->wsize, rpl->seq, rpl->lastseq);
+
+	if (rpl->bitmap == NULL) {
+		printf(" }\n");
+		return;
+	}
+
+	printf("\n   bitmap { ");
+
+	for (len = 0; len < rpl->wsize; len++) {
+		for (l = 7; l >= 0; l--)
+			printf("%u", (((rpl->bitmap)[len] >> l) & 1) ? 1 : 0);
+	}
+	printf(" }\n");
+
+	return;
+}
+
+void
+kdebug_mbufhdr(m)
+	struct mbuf *m;
+{
+	/* sanity check */
+	if (m == NULL)
+		return;
+
+	printf("mbuf(%p){ m_next:%p m_nextpkt:%p m_data:%p "
+	       "m_len:%d m_type:0x%02x m_flags:0x%02x }\n",
+		m, m->m_next, m->m_nextpkt, m->m_data,
+		m->m_len, m->m_type, m->m_flags);
+
+	if (m->m_flags & M_PKTHDR) {
+		printf("  m_pkthdr{ len:%d rcvif:%p }\n",
+		    m->m_pkthdr.len, m->m_pkthdr.rcvif);
+	}
+
+#ifdef __FreeBSD__
+	if (m->m_flags & M_EXT) {
+		printf("  m_ext{ ext_buf:%p ext_free:%p "
+		       "ext_size:%u ext_ref:%p }\n",
+			m->m_ext.ext_buf, m->m_ext.ext_free,
+			m->m_ext.ext_size, m->m_ext.ext_ref);
+	}
+#endif
+
+	return;
+}
+
+void
+kdebug_mbuf(m0)
+	struct mbuf *m0;
+{
+	struct mbuf *m = m0;
+	int i, j;
+
+	for (j = 0; m; m = m->m_next) {
+		kdebug_mbufhdr(m);
+		printf("  m_data:\n");
+		for (i = 0; i < m->m_len; i++) {
+			if (i && i % 32 == 0)
+				printf("\n");
+			if (i % 4 == 0)
+				printf(" ");
+			printf("%02x", mtod(m, u_char *)[i]);
+			j++;
+		}
+		printf("\n");
+	}
+
+	return;
+}
+#endif /* _KERNEL */
+
+void
+kdebug_sockaddr(addr)
+	struct sockaddr *addr;
+{
+	struct sockaddr_in *sin4;
+#ifdef INET6
+	struct sockaddr_in6 *sin6;
+#endif
+
+	/* sanity check */
+	if (addr == NULL)
+		panic("kdebug_sockaddr: NULL pointer was passed.\n");
+
+	/* NOTE: We deal with port number as host byte order. */
+	printf("sockaddr{ len=%u family=%u", addr->sa_len, addr->sa_family);
+
+	switch (addr->sa_family) {
+	case AF_INET:
+		sin4 = (struct sockaddr_in *)addr;
+		printf(" port=%u\n", ntohs(sin4->sin_port));
+		ipsec_hexdump((caddr_t)&sin4->sin_addr, sizeof(sin4->sin_addr));
+		break;
+#ifdef INET6
+	case AF_INET6:
+		sin6 = (struct sockaddr_in6 *)addr;
+		printf(" port=%u\n", ntohs(sin6->sin6_port));
+		printf("  flowinfo=0x%08x, scope_id=0x%08x\n",
+		    sin6->sin6_flowinfo, sin6->sin6_scope_id);
+		ipsec_hexdump((caddr_t)&sin6->sin6_addr,
+		    sizeof(sin6->sin6_addr));
+		break;
+#endif
+	}
+
+	printf("  }\n");
+
+	return;
+}
+
+void
+ipsec_bindump(buf, len)
+	caddr_t buf;
+	int len;
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		printf("%c", (unsigned char)buf[i]);
+
+	return;
+}
+
+
+void
+ipsec_hexdump(buf, len)
+	caddr_t buf;
+	int len;
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (i != 0 && i % 32 == 0) printf("\n");
+		if (i % 4 == 0) printf(" ");
+		printf("%02x", (unsigned char)buf[i]);
+	}
+#if 0
+	if (i % 32 != 0) printf("\n");
+#endif
+
+	return;
+}
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netkey/keydb.c
@@ -0,0 +1,236 @@
+//==========================================================================
+//
+//      src/sys/netkey/keydb.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: keydb.c,v 1.64 2000/05/11 17:02:30 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netinet6/ipsec.h>
+
+static void keydb_delsecasvar __P((struct secasvar *));
+
+/*
+ * secpolicy management
+ */
+struct secpolicy *
+keydb_newsecpolicy()
+{
+	struct secpolicy *p;
+
+	p = (struct secpolicy *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+	if (!p)
+		return p;
+	bzero(p, sizeof(*p));
+	return p;
+}
+
+void
+keydb_delsecpolicy(p)
+	struct secpolicy *p;
+{
+
+	free(p, M_SECA);
+}
+
+/*
+ * secashead management
+ */
+struct secashead *
+keydb_newsecashead()
+{
+	struct secashead *p;
+	int i;
+
+	p = (struct secashead *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+	if (!p)
+		return p;
+	bzero(p, sizeof(*p));
+	for (i = 0; i < sizeof(p->savtree)/sizeof(p->savtree[0]); i++)
+		LIST_INIT(&p->savtree[i]);
+	return p;
+}
+
+void
+keydb_delsecashead(p)
+	struct secashead *p;
+{
+
+	free(p, M_SECA);
+}
+
+/*
+ * secasvar management (reference counted)
+ */
+struct secasvar *
+keydb_newsecasvar()
+{
+	struct secasvar *p;
+
+	p = (struct secasvar *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+	if (!p)
+		return p;
+	bzero(p, sizeof(*p));
+	p->refcnt = 1;
+	return p;
+}
+
+void
+keydb_refsecasvar(p)
+	struct secasvar *p;
+{
+	int s;
+
+#ifdef __NetBSD__
+	s = splsoftnet();
+#else
+	s = splnet();
+#endif
+	p->refcnt++;
+	splx(s);
+}
+
+void
+keydb_freesecasvar(p)
+	struct secasvar *p;
+{
+	int s;
+
+#ifdef __NetBSD__
+	s = splsoftnet();
+#else
+	s = splnet();
+#endif
+	p->refcnt--;
+	/* negative refcnt will cause panic intentionally */
+	if (p->refcnt <= 0)
+		keydb_delsecasvar(p);
+	splx(s);
+}
+
+static void
+keydb_delsecasvar(p)
+	struct secasvar *p;
+{
+
+	if (p->refcnt)
+		panic("keydb_delsecasvar called with refcnt != 0");
+
+	free(p, M_SECA);
+}
+
+/*
+ * secreplay management
+ */
+struct secreplay *
+keydb_newsecreplay(wsize)
+	size_t wsize;
+{
+	struct secreplay *p;
+
+	p = (struct secreplay *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+	if (!p)
+		return p;
+
+	bzero(p, sizeof(*p));
+	if (wsize != 0) {
+		p->bitmap = (caddr_t)malloc(wsize, M_SECA, M_NOWAIT);
+		if (!p->bitmap) {
+			free(p, M_SECA);
+			return NULL;
+		}
+		bzero(p->bitmap, wsize);
+	}
+	p->wsize = wsize;
+	return p;
+}
+
+void
+keydb_delsecreplay(p)
+	struct secreplay *p;
+{
+
+	if (p->bitmap)
+		free(p->bitmap, M_SECA);
+	free(p, M_SECA);
+}
+
+/*
+ * secreg management
+ */
+struct secreg *
+keydb_newsecreg()
+{
+	struct secreg *p;
+
+	p = (struct secreg *)malloc(sizeof(*p), M_SECA, M_NOWAIT);
+	if (p)
+		bzero(p, sizeof(*p));
+	return p;
+}
+
+void
+keydb_delsecreg(p)
+	struct secreg *p;
+{
+
+	free(p, M_SECA);
+}
new file mode 100644
--- /dev/null
+++ b/packages/net/bsd_tcpip/current/src/sys/netkey/keysock.c
@@ -0,0 +1,752 @@
+//==========================================================================
+//
+//      src/sys/netkey/keysock.c
+//
+//==========================================================================
+//####BSDCOPYRIGHTBEGIN####
+//
+// -------------------------------------------
+//
+// Portions of this software may have been derived from OpenBSD, 
+// FreeBSD or other sources, and are covered by the appropriate
+// copyright disclaimers included herein.
+//
+// Portions created by Red Hat are
+// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
+//
+// -------------------------------------------
+//
+//####BSDCOPYRIGHTEND####
+//==========================================================================
+
+/*	$KAME: keysock.c,v 1.25 2001/08/13 20:07:41 itojun Exp $	*/
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include "opt_ipsec.h"
+#endif
+
+/* This code has derived from sys/net/rtsock.c on FreeBSD2.2.5 */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif
+#include <sys/mbuf.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <sys/malloc.h>
+#endif
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/errno.h>
+#ifdef __NetBSD__
+#include <sys/proc.h>
+#include <sys/queue.h>
+#endif
+
+#include <net/raw_cb.h>
+#include <net/route.h>
+
+#include <net/pfkeyv2.h>
+#include <netkey/keydb.h>
+#include <netkey/key.h>
+#include <netkey/keysock.h>
+#include <netkey/key_debug.h>
+
+struct sockaddr key_dst = { 2, PF_KEY, };
+struct sockaddr key_src = { 2, PF_KEY, };
+
+static int key_sendup0 __P((struct rawcb *, struct mbuf *, int));
+
+struct pfkeystat pfkeystat;
+
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+/*
+ * key_usrreq()
+ * derived from net/rtsock.c:route_usrreq()
+ */
+#ifndef __NetBSD__
+int
+key_usrreq(so, req, m, nam, control)
+	struct socket *so;
+	int req;
+	struct mbuf *m, *nam, *control;
+#else
+int
+key_usrreq(so, req, m, nam, control, p)
+	struct socket *so;
+	int req;
+	struct mbuf *m, *nam, *control;
+	struct proc *p;
+#endif /*__NetBSD__*/
+{
+	int error = 0;
+	struct keycb *kp = (struct keycb *)sotorawcb(so);
+	int s;
+
+#ifdef __NetBSD__
+	s = splsoftnet();
+#else
+	s = splnet();
+#endif
+	if (req == PRU_ATTACH) {
+		kp = (struct keycb *)malloc(sizeof(*kp), M_PCB, M_WAITOK);
+		so->so_pcb = (caddr_t)kp;
+		if (so->so_pcb)
+			bzero(so->so_pcb, sizeof(*kp));
+	}
+	if (req == PRU_DETACH && kp) {
+		int af = kp->kp_raw.rcb_proto.sp_protocol;
+		if (af == PF_KEY) /* XXX: AF_KEY */
+			key_cb.key_count--;
+		key_cb.any_count--;
+
+		key_freereg(so);
+	}
+
+#ifndef __NetBSD__
+	error = raw_usrreq(so, req, m, nam, control);
+#else
+	error = raw_usrreq(so, req, m, nam, control, p);
+#endif
+	m = control = NULL;	/* reclaimed in raw_usrreq */
+	kp = (struct keycb *)sotorawcb(so);
+	if (req == PRU_ATTACH && kp) {
+		int af = kp->kp_raw.rcb_proto.sp_protocol;
+		if (error) {
+			pfkeystat.sockerr++;
+			free((caddr_t)kp, M_PCB);
+			so->so_pcb = (caddr_t) 0;
+			splx(s);
+			return(error);
+		}
+
+		kp->kp_promisc = kp->kp_registered = 0;
+
+		if (af == PF_KEY) /* XXX: AF_KEY */
+			key_cb.key_count++;
+		key_cb.any_count++;
+#ifndef __bsdi__
+		kp->kp_raw.rcb_laddr = &key_src;
+		kp->kp_raw.rcb_faddr = &key_dst;
+#else
+		/*
+		 * XXX rcb_faddr must be dynamically allocated, otherwise
+		 * raw_disconnect() will get angry.
+		 */
+	    {
+		struct mbuf *m, *n;
+		MGET(m, M_WAITOK, MT_DATA);
+		if (!m) {
+			error = ENOBUFS;
+			pfkeystat.in_nomem++;
+			free((caddr_t)kp, M_PCB);
+			so->so_pcb = (caddr_t) 0;
+			splx(s);
+			return(error);
+		}
+		MGET(n, M_WAITOK, MT_DATA);
+		if (!n) {
+			error = ENOBUFS;
+			m_freem(m);
+			pfkeystat.in_nomem++;
+			free((caddr_t)kp, M_PCB);
+			so->so_pcb = (caddr_t) 0;
+			splx(s);
+			return(error);
+		}
+		m->m_len = sizeof(key_src);
+		kp->kp_raw.rcb_laddr = mtod(m, struct sockaddr *);
+		bcopy(&key_src, kp->kp_raw.rcb_laddr, sizeof(key_src));
+		n->m_len = sizeof(key_dst);
+		kp->kp_raw.rcb_faddr = mtod(n, struct sockaddr *);
+		bcopy(&key_dst, kp->kp_raw.rcb_faddr, sizeof(key_dst));
+	    }
+#endif
+		soisconnected(so);
+		so->so_options |= SO_USELOOPBACK;
+	}
+	splx(s);
+	return(error);
+}
+#endif /* other than FreeBSD >= 3 */
+
+/*
+ * key_output()
+ */
+int
+#if __STDC__
+key_output(struct mbuf *m, ...)
+#else
+key_output(m, va_alist)
+	struct mbuf *m;
+	va_dcl
+#endif
+{
+	struct sadb_msg *msg;
+	int len, error = 0;
+	int s;
+	struct socket *so;
+	va_list ap;
+
+	va_start(ap, m);
+	so = va_arg(ap, struct socket *);
+	va_end(ap);
+
+	if (m == 0)
+		panic("key_output: NULL pointer was passed.\n");
+
+	pfkeystat.out_total++;
+	pfkeystat.out_bytes += m->m_pkthdr.len;
+
+	len = m->m_pkthdr.len;
+	if (len < sizeof(struct sadb_msg)) {
+		pfkeystat.out_tooshort++;
+		error = EINVAL;
+		goto end;
+	}
+
+	if (m->m_len < sizeof(struct sadb_msg)) {
+		if ((m = m_pullup(m, sizeof(struct sadb_msg))) == 0) {
+			pfkeystat.out_nomem++;
+			error = ENOBUFS;
+			goto end;
+		}
+	}
+
+	if ((m->m_flags & M_PKTHDR) == 0)
+		panic("key_output: not M_PKTHDR ??");
+
+	KEYDEBUG(KEYDEBUG_KEY_DUMP, kdebug_mbuf(m));
+
+	msg = mtod(m, struct sadb_msg *);
+	pfkeystat.out_msgtype[msg->sadb_msg_type]++;
+	if (len != PFKEY_UNUNIT64(msg->sadb_msg_len)) {
+		pfkeystat.out_invlen++;
+		error = EINVAL;
+		goto end;
+	}
+
+	/*XXX giant lock*/
+#ifdef __NetBSD__
+	s = splsoftnet();
+#else
+	s = splnet();
+#endif
+	error = key_parse(m, so);
+	m = NULL;
+	splx(s);
+end:
+	if (m)
+		m_freem(m);
+	return error;
+}
+
+/*
+ * send message to the socket.
+ */
+static int
+key_sendup0(rp, m, promisc)
+	struct rawcb *rp;
+	struct mbuf *m;
+	int promisc;
+{
+	int error;
+
+	if (promisc) {
+		struct sadb_msg *pmsg;
+
+		M_PREPEND(m, sizeof(struct sadb_msg), M_NOWAIT);
+		if (m && m->m_len < sizeof(struct sadb_msg))
+			m = m_pullup(m, sizeof(struct sadb_msg));
+		if (!m) {
+			pfkeystat.in_nomem++;
+			m_freem(m);
+			return ENOBUFS;
+		}
+		m->m_pkthdr.len += sizeof(*pmsg);
+
+		pmsg = mtod(m, struct sadb_msg *);
+		bzero(pmsg, sizeof(*pmsg));
+		pmsg->sadb_msg_version = PF_KEY_V2;
+		pmsg->sadb_msg_type = SADB_X_PROMISC;
+		pmsg->sadb_msg_len = PFKEY_UNIT64(m->m_pkthdr.len);
+		/* pid and seq? */
+
+		pfkeystat.in_msgtype[pmsg->sadb_msg_type]++;
+	}
+
+	if (!sbappendaddr(&rp->rcb_socket->so_rcv, (struct sockaddr *)&key_src,
+	    m, NULL)) {
+		pfkeystat.in_nomem++;
+		m_freem(m);
+		error = ENOBUFS;
+	} else
+		error = 0;
+	sorwakeup(rp->rcb_socket);
+	return error;
+}
+
+/* XXX this interface should be obsoleted. */
+int
+key_sendup(so, msg, len, target)
+	struct socket *so;
+	struct sadb_msg *msg;
+	u_int len;
+	int target;	/*target of the resulting message*/
+{
+	struct mbuf *m, *n, *mprev;
+	int tlen;
+
+	/* sanity check */
+	if (so == 0 || msg == 0)
+		panic("key_sendup: NULL pointer was passed.\n");
+
+	KEYDEBUG(KEYDEBUG_KEY_DUMP,
+		printf("key_sendup: \n");
+		kdebug_sadb(msg));
+
+	/*
+	 * we increment statistics here, just in case we have ENOBUFS
+	 * in this function.
+	 */
+	pfkeystat.in_total++;
+	pfkeystat.in_bytes += len;
+	pfkeystat.in_msgtype[msg->sadb_msg_type]++;
+
+	/*
+	 * Get mbuf chain whenever possible (not clusters),
+	 * to save socket buffer.  We'll be generating many SADB_ACQUIRE
+	 * messages to listening key sockets.  If we simply allocate clusters,
+	 * sbappendaddr() will raise ENOBUFS due to too little sbspace().
+	 * sbspace() computes # of actual data bytes AND mbuf region.
+	 *
+	 * TODO: SADB_ACQUIRE filters should be implemented.
+	 */
+	tlen = len;
+	m = mprev = NULL;
+	while (tlen > 0) {
+		if (tlen == len) {
+			MGETHDR(n, M_DONTWAIT, MT_DATA);
+			n->m_len = MHLEN;
+		} else {
+			MGET(n, M_DONTWAIT, MT_DATA);
+			n->m_len = MLEN;
+		}
+		if (!n) {
+			pfkeystat.in_nomem++;
+			return ENOBUFS;
+		}
+		if (tlen >= MCLBYTES) {	/*XXX better threshold? */
+			MCLGET(n, M_DONTWAIT);
+			if ((n->m_flags & M_EXT) == 0) {
+				m_free(n);
+				m_freem(m);
+				pfkeystat.in_nomem++;
+				return ENOBUFS;
+			}
+			n->m_len = MCLBYTES;
+		}
+
+		if (tlen < n->m_len)
+			n->m_len = tlen;
+		n->m_next = NULL;
+		if (m == NULL)
+			m = mprev = n;
+		else {
+			mprev->m_next = n;
+			mprev = n;
+		}
+		tlen -= n->m_len;
+		n = NULL;
+	}
+	m->m_pkthdr.len = len;
+	m->m_pkthdr.rcvif = NULL;
+	m_copyback(m, 0, len, (caddr_t)msg);
+
+	/* avoid duplicated statistics */
+	pfkeystat.in_total--;
+	pfkeystat.in_bytes -= len;
+	pfkeystat.in_msgtype[msg->sadb_msg_type]--;
+
+	return key_sendup_mbuf(so, m, target);
+}
+
+/* so can be NULL if target != KEY_SENDUP_ONE */
+int
+key_sendup_mbuf(so, m, target)
+	struct socket *so;
+	struct mbuf *m;
+	int target;
+{
+	struct mbuf *n;
+	struct keycb *kp;
+	int sendup;
+	struct rawcb *rp;
+	int error = 0;
+
+	if (m == NULL)
+		panic("key_sendup_mbuf: NULL pointer was passed.\n");
+	if (so == NULL && target == KEY_SENDUP_ONE)
+		panic("key_sendup_mbuf: NULL pointer was passed.\n");
+
+	pfkeystat.in_total++;
+	pfkeystat.in_bytes += m->m_pkthdr.len;
+	if (m->m_len < sizeof(struct sadb_msg)) {
+#if 1
+		m = m_pullup(m, sizeof(struct sadb_msg));
+		if (m == NULL) {
+			pfkeystat.in_nomem++;
+			return ENOBUFS;
+		}
+#else
+		/* don't bother pulling it up just for stats */
+#endif
+	}
+	if (m->m_len >= sizeof(struct sadb_msg)) {
+		struct sadb_msg *msg;
+		msg = mtod(m, struct sadb_msg *);
+		pfkeystat.in_msgtype[msg->sadb_msg_type]++;
+	}
+
+#ifdef __NetBSD__
+	for (rp = rawcb.lh_first; rp; rp = rp->rcb_list.le_next)
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+	LIST_FOREACH(rp, &rawcb_list, list)
+#else
+	for (rp = rawcb.rcb_next; rp != &rawcb; rp = rp->rcb_next)
+#endif
+	{
+		if (rp->rcb_proto.sp_family != PF_KEY)
+			continue;
+		if (rp->rcb_proto.sp_protocol
+		 && rp->rcb_proto.sp_protocol != PF_KEY_V2) {
+			continue;
+		}
+
+		kp = (struct keycb *)rp;
+
+		/*
+		 * If you are in promiscuous mode, and when you get broadcasted
+		 * reply, you'll get two PF_KEY messages.
+		 * (based on pf_key@inner.net message on 14 Oct 1998)
+		 */
+		if (((struct keycb *)rp)->kp_promisc) {
+			if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) {
+				(void)key_sendup0(rp, n, 1);
+				n = NULL;
+			}
+		}
+
+		/* the exact target will be processed later */
+		if (so && sotorawcb(so) == rp)
+			continue;
+
+		sendup = 0;
+		switch (target) {
+		case KEY_SENDUP_ONE:
+			/* the statement has no effect */
+			if (so && sotorawcb(so) == rp)
+				sendup++;
+			break;
+		case KEY_SENDUP_ALL:
+			sendup++;
+			break;
+		case KEY_SENDUP_REGISTERED:
+			if (kp->kp_registered)
+				sendup++;
+			break;
+		}
+		pfkeystat.in_msgtarget[target]++;
+
+		if (!sendup)
+			continue;
+
+		if ((n = m_copy(m, 0, (int)M_COPYALL)) == NULL) {
+			m_freem(m);
+			pfkeystat.in_nomem++;
+			return ENOBUFS;
+		}
+
+		if ((error = key_sendup0(rp, n, 0)) != 0) {
+			m_freem(m);
+			return error;
+		}
+
+		n = NULL;
+	}
+
+	if (so) {
+		error = key_sendup0(sotorawcb(so), m, 0);
+		m = NULL;
+	} else {
+		error = 0;
+		m_freem(m);
+	}
+	return error;
+}
+
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+/*
+ * key_abort()
+ * derived from net/rtsock.c:rts_abort()
+ */
+static int
+key_abort(struct socket *so)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_abort(so);
+	splx(s);
+	return error;
+}
+
+/*
+ * key_attach()
+ * derived from net/rtsock.c:rts_attach()
+ */
+static int
+key_attach(struct socket *so, int proto, struct proc *p)
+{
+	struct keycb *kp;
+	int s, error;
+
+	if (sotorawcb(so) != 0)
+		return EISCONN;	/* XXX panic? */
+	kp = (struct keycb *)malloc(sizeof *kp, M_PCB, M_WAITOK); /* XXX */
+	if (kp == 0)
+		return ENOBUFS;
+	bzero(kp, sizeof *kp);
+
+	/*
+	 * The splnet() is necessary to block protocols from sending
+	 * error notifications (like RTM_REDIRECT or RTM_LOSING) while
+	 * this PCB is extant but incompletely initialized.
+	 * Probably we should try to do more of this work beforehand and
+	 * eliminate the spl.
+	 */
+	s = splnet();
+	so->so_pcb = (caddr_t)kp;
+	error = raw_usrreqs.pru_attach(so, proto, p);
+	kp = (struct keycb *)sotorawcb(so);
+	if (error) {
+		free(kp, M_PCB);
+		so->so_pcb = (caddr_t) 0;
+		splx(s);
+		return error;
+	}
+
+	kp->kp_promisc = kp->kp_registered = 0;
+
+	if (kp->kp_raw.rcb_proto.sp_protocol == PF_KEY) /* XXX: AF_KEY */
+		key_cb.key_count++;
+	key_cb.any_count++;
+	kp->kp_raw.rcb_laddr = &key_src;
+	kp->kp_raw.rcb_faddr = &key_dst;
+	soisconnected(so);
+	so->so_options |= SO_USELOOPBACK;
+
+	splx(s);
+	return 0;
+}
+
+/*
+ * key_bind()
+ * derived from net/rtsock.c:rts_bind()
+ */
+static int
+key_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_bind(so, nam, p); /* xxx just EINVAL */
+	splx(s);
+	return error;
+}
+
+/*
+ * key_connect()
+ * derived from net/rtsock.c:rts_connect()
+ */
+static int
+key_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_connect(so, nam, p); /* XXX just EINVAL */
+	splx(s);
+	return error;
+}
+
+/*
+ * key_detach()
+ * derived from net/rtsock.c:rts_detach()
+ */
+static int
+key_detach(struct socket *so)
+{
+	struct keycb *kp = (struct keycb *)sotorawcb(so);
+	int s, error;
+
+	s = splnet();
+	if (kp != 0) {
+		if (kp->kp_raw.rcb_proto.sp_protocol
+		    == PF_KEY) /* XXX: AF_KEY */
+			key_cb.key_count--;
+		key_cb.any_count--;
+
+		key_freereg(so);
+	}
+	error = raw_usrreqs.pru_detach(so);
+	splx(s);
+	return error;
+}
+
+/*
+ * key_disconnect()
+ * derived from net/rtsock.c:key_disconnect()
+ */
+static int
+key_disconnect(struct socket *so)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_disconnect(so);
+	splx(s);
+	return error;
+}
+
+/*
+ * key_peeraddr()
+ * derived from net/rtsock.c:rts_peeraddr()
+ */
+static int
+key_peeraddr(struct socket *so, struct sockaddr **nam)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_peeraddr(so, nam);
+	splx(s);
+	return error;
+}
+
+/*
+ * key_send()
+ * derived from net/rtsock.c:rts_send()
+ */
+static int
+key_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
+	 struct mbuf *control, struct proc *p)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_send(so, flags, m, nam, control, p);
+	splx(s);
+	return error;
+}
+
+/*
+ * key_shutdown()
+ * derived from net/rtsock.c:rts_shutdown()
+ */
+static int
+key_shutdown(struct socket *so)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_shutdown(so);
+	splx(s);
+	return error;
+}
+
+/*
+ * key_sockaddr()
+ * derived from net/rtsock.c:rts_sockaddr()
+ */
+static int
+key_sockaddr(struct socket *so, struct sockaddr **nam)
+{
+	int s, error;
+	s = splnet();
+	error = raw_usrreqs.pru_sockaddr(so, nam);
+	splx(s);
+	return error;
+}
+
+struct pr_usrreqs key_usrreqs = {
+	key_abort, pru_accept_notsupp, key_attach, key_bind,
+	key_connect,
+	pru_connect2_notsupp, pru_control_notsupp, key_detach,
+	key_disconnect, pru_listen_notsupp, key_peeraddr,
+	pru_rcvd_notsupp,
+	pru_rcvoob_notsupp, key_send, pru_sense_null, key_shutdown,
+	key_sockaddr, sosend, soreceive, sopoll
+};
+#endif /* __FreeBSD__ >= 3 */
+
+#ifdef __FreeBSD__
+/* sysctl */
+SYSCTL_NODE(_net, PF_KEY, key, CTLFLAG_RW, 0, "Key Family");
+#endif
+
+/*
+ * Definitions of protocols supported in the KEY domain.
+ */
+
+extern struct domain keydomain;
+
+struct protosw keysw[] = {
+{ SOCK_RAW,	&keydomain,	PF_KEY_V2,	PR_ATOMIC|PR_ADDR,
+  0,		key_output,	raw_ctlinput,	0,
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+  0,
+#else
+  key_usrreq,
+#endif
+  raw_init,	0,		0,		0,
+#if defined(__bsdi__) || defined(__NetBSD__)
+  key_sysctl,
+#elif defined(__FreeBSD__) && __FreeBSD__ >= 3
+  &key_usrreqs
+#endif
+}
+};
+
+struct domain keydomain =
+    { PF_KEY, "key", key_init, 0, 0,
+      keysw, &keysw[sizeof(keysw)/sizeof(keysw[0])] };
+
+#ifdef __FreeBSD__
+DOMAIN_SET(key);
+#endif