diff -uNr -x CVS linux-2.5.43/include/linux/ip.h linux25.43-ipsec/include/linux/ip.h
--- linux-2.5.43/include/linux/ip.h 2002-10-16 12:27:49.000000000 +0900
+++ linux25.43-ipsec/include/linux/ip.h 2002-10-16 15:27:44.000000000 +0900
@@ -113,6 +113,23 @@
unsigned char __data[0];
};
+#ifdef CONFIG_IP_IPSEC
+struct ip_auth_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen; /* This one is measured in 32 bit units! */
+ __u16 reserved;
+ __u32 spi;
+ __u32 seq_no; /* Sequence number */
+ __u8 auth_data[4]; /* Length variable but >=4. Mind the 64 bit alignment! */
+};
+
+struct ip_esp_hdr {
+ __u32 spi;
+ __u32 seq_no; /* Sequence number */
+ __u8 enc_data[8]; /* Length variable but >=8. Mind the 64 bit alignment! */
+};
+#endif /* CONFIG_IP_IPSEC */
+
#define optlength(opt) (sizeof(struct ip_options) + opt->optlen)
struct inet_opt {
diff -uNr -x CVS linux-2.5.43/include/linux/ipsec.h linux25.43-ipsec/include/linux/ipsec.h
--- linux-2.5.43/include/linux/ipsec.h 2002-10-16 12:27:16.000000000 +0900
+++ linux25.43-ipsec/include/linux/ipsec.h 2002-10-16 15:27:44.000000000 +0900
@@ -16,7 +16,12 @@
#include <linux/config.h>
#include <linux/socket.h>
#include <net/sock.h>
+#include <net/route.h>
#include <linux/skbuff.h>
+#ifdef CONFIG_IP_IPSEC
+#include <net/sadb.h>
+#include <net/spd.h>
+#endif
/* Values for the set/getsockopt calls */
@@ -65,5 +70,57 @@
}
#endif /* CONFIG */
+/* return value for ipsec[46]_output_check() -mk */
+#define IPSEC_ACTION_BYPASS 0x0
+#define IPSEC_ACTION_AUTH 0x1
+#define IPSEC_ACTION_ESP 0x2
+#define IPSEC_ACTION_COMP 0x4
+#define IPSEC_ACTION_DROP 0x8
+
+#ifdef CONFIG_SYSCTL
+extern int sysctl_ipsec_replay_window;
+#ifdef CONFIG_IPSEC_DEBUG
+extern int sysctl_ipsec_debug_ipv4;
+extern int sysctl_ipsec_debug_ipv6;
+extern int sysctl_ipsec_debug_pfkey;
+extern int sysctl_ipsec_debug_sadb;
+extern int sysctl_ipsec_debug_spd;
+#endif /* CONFIG_IPSEC_DEBUG */
+#endif /* CONFIG_SYSCTL */
+
+#ifdef CONFIG_IP_IPSEC
+int ipsec4_out_ah_calc(struct iphdr *iph, struct ip_auth_hdr *authhdr, struct ipsec_sp *policy);
+
+int ipsec4_out_get_ahsize(struct ipsec_sp *policy);
+int ipsec4_out_get_espsize(struct ipsec_sp *policy);
+static inline int ipsec4_out_get_hdrsize(struct ipsec_sp *policy)
+{
+ return ipsec4_out_get_ahsize(policy) + ipsec4_out_get_espsize(policy);
+}
+
+void ipsec4_out_enc(const void *data, unsigned length, u8 proto,
+ void **newdata, unsigned *newlength, struct ipsec_sp *policy);
+void ipsec4_out_finish(struct ipsec_sp *policy_ptr);
+
+int ipsec4_input_check(struct sk_buff **skb);
+int ipsec4_output_check(struct sock *sk, struct rtable *rt, struct ipsec_sp **policy_ptr);
+
+#ifdef CONFIG_IPSEC_DEBUG
+# ifdef CONFIG_SYSCTL
+# define IPSEC4_DEBUG(fmt, args...) \
+do { \
+ if (sysctl_ipsec_debug_ipv4) { \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args); \
+ } \
+} while(0)
+# else
+# define IPSEC4_DEBUG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+# endif /* CONFIG_SYSCTL */
+#else
+# define IPSEC4_DEBUG(fmt, args...)
+#endif
+#endif /* CONFIG_IP_IPSEC */
+
+
#endif /* __KERNEL__ */
#endif /* _LINUX_IPSEC_H */
diff -uNr -x CVS linux-2.5.43/include/linux/ipsec6.h linux25.43-ipsec/include/linux/ipsec6.h
--- linux-2.5.43/include/linux/ipsec6.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/include/linux/ipsec6.h 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,153 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI Project
+ * Mitsuru KANDA <
[email protected]> / USAGI Project
+ * YOSHIFUJI Hideaki <
[email protected]> / USAGI Project
+ */
+#ifndef _LINUX_IPSEC6_H
+#define _LINUX_IPSEC6_H
+
+#include <linux/skbuff.h>
+#include <net/ipv6.h>
+#include <net/spd.h>
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_IPSEC_DEBUG
+# ifdef CONFIG_SYSCTL
+# define IPSEC6_DEBUG(fmt, args...) \
+do { \
+ if (sysctl_ipsec_debug_ipv6) { \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args); \
+ } \
+} while(0)
+# else
+# define IPSEC6_DEBUG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+# endif /* CONFIG_SYSCTL */
+#else
+# define IPSEC6_DEBUG(fmt, args...)
+#endif
+
+/* Set all mutable/unpredictable fields to zero. */
+static int inline zero_out_mutable_opts(struct ipv6_opt_hdr *opthdr)
+{
+ u8 *opt = (u8*)opthdr;
+ int len = ipv6_optlen(opthdr);
+ int off = 0;
+ int optlen;
+
+ off += 2;
+ len -= 2;
+
+ while(len > 0) {
+ switch(opt[off]) {
+ case IPV6_TLV_PAD0:
+ optlen = 1;
+ break;
+ default:
+ if (len < 2)
+ goto bad;
+ optlen = opt[off+1]+2;
+ if (len < optlen)
+ goto bad;
+ if (opt[off] & 0x20) /* mutable check */
+ memset(&opt[off+2], 0, opt[off+1]);
+ break;
+ }
+ off += optlen;
+ len -= optlen;
+ }
+ if (len == 0)
+ return 1;
+bad:
+ return 0;
+}
+
+/* Set all mutable/predictable fields to the destination state, and all
+ mutable/unpredictable fields to zero. */
+static void inline zero_out_for_ah(struct inet6_skb_parm *parm, char* packet)
+{
+ struct ipv6hdr *hdr = (struct ipv6hdr*)packet;
+
+ /* Main header */
+ hdr->priority=0;
+ hdr->flow_lbl[0]=0;
+ hdr->flow_lbl[1]=0;
+ hdr->flow_lbl[2]=0;
+ hdr->hop_limit=0;
+ /* Mutable/unpredictable Option headers */
+ /* AH header */
+
+ if (parm->auth) {
+ struct ipv6_auth_hdr* authhdr =
+ (struct ipv6_auth_hdr*)(packet + parm->auth);
+ int len = ((authhdr->hdrlen - 1) << 2);
+ memset(authhdr->auth_data,0,len);
+ }
+
+ if (parm->hop) {
+ struct ipv6_hopopt_hdr* hopopthdr =
+ (struct ipv6_hopopt_hdr*)(packet + parm->hop);
+ if (!zero_out_mutable_opts(hopopthdr))
+ printk(KERN_WARNING
+ "overrun when muting hopopts\n");
+ }
+
+ if (parm->dst0) {
+ struct ipv6_destopt_hdr* destopthdr0 =
+ (struct ipv6_destopt_hdr*)(packet + parm->dst0);
+ if (!zero_out_mutable_opts(destopthdr0))
+ printk(KERN_WARNING
+ "overrun when muting destopt\n");
+ }
+
+ if (parm->dst1) {
+ struct ipv6_destopt_hdr* destopthdr1 =
+ (struct ipv6_destopt_hdr*)(packet + parm->dst1);
+ if (!zero_out_mutable_opts(destopthdr1))
+ printk(KERN_WARNING
+ "overrun when muting destopt\n");
+ }
+}
+
+int ipsec6_out_get_ahsize(struct ipsec_sp *policy);
+int ipsec6_out_get_espsize(struct ipsec_sp *policy);
+static inline int ipsec6_out_get_hdrsize(struct ipsec_sp *policy)
+{
+ return ipsec6_out_get_ahsize(policy) + ipsec6_out_get_espsize(policy);
+}
+
+struct ipv6_txoptions *ipsec6_out_get_newopt(struct ipv6_txoptions *opt, struct ipsec_sp *policy);
+int ipsec6_out_ah_calc(const void *data, unsigned length,
+ inet_getfrag_t getfrag, struct sk_buff *skb,
+ struct ipv6_auth_hdr *authhdr, struct ipsec_sp *policy);
+void ipsec6_out_enc(const void *data, unsigned length, u8 proto, struct ipv6_txoptions *opt,
+ void **newdata, unsigned *newlength, struct ipsec_sp *policy);
+void ipsec6_out_finish(struct ipv6_txoptions *opt, struct ipsec_sp *policy_ptr);
+int ipsec6_input_check_ah(struct sk_buff **skb, struct ipv6_auth_hdr* authhdr);
+int ipsec6_input_check_esp(struct sk_buff **skb, struct ipv6_esp_hdr *esphdr, u8 *nexthdr);
+
+int ipsec6_input_check(struct sk_buff **skb, u8 *nexthdr);
+int ipsec6_output_check(struct sock *sk, struct flowi *fl, const u8* data, struct ipsec_sp **policy_ptr);
+int ipsec6_ndisc_check(struct in6_addr *saddr, struct in6_addr *daddr, struct ipsec_sp **policy_ptr);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_IPSEC6_H */
diff -uNr -x CVS linux-2.5.43/include/linux/ipv6.h linux25.43-ipsec/include/linux/ipv6.h
--- linux-2.5.43/include/linux/ipv6.h 2002-10-16 12:28:30.000000000 +0900
+++ linux25.43-ipsec/include/linux/ipv6.h 2002-10-16 15:27:44.000000000 +0900
@@ -73,6 +73,22 @@
#define rt0_type rt_hdr.type;
};
+/* IPsec6 header */
+struct ipv6_auth_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen; /* This one is measured in 32 bit units! */
+ __u16 reserved;
+ __u32 spi;
+ __u32 seq_no; /* Sequence number */
+ __u8 auth_data[4]; /* Length variable but >=4. Mind the 64 bit alignment! */
+};
+
+struct ipv6_esp_hdr {
+ __u32 spi;
+ __u32 seq_no; /* Sequence number */
+ __u8 enc_data[8]; /* Length variable but >=8. Mind the 64 bit alignment! */
+};
+
/*
* IPv6 fixed header
*
@@ -120,6 +136,7 @@
__u16 dst0;
__u16 srcrt;
__u16 dst1;
+ __u32 espspi; /* not offset */
};
struct ipv6_pinfo {
diff -uNr -x CVS linux-2.5.43/include/linux/pfkey.h linux25.43-ipsec/include/linux/pfkey.h
--- linux-2.5.43/include/linux/pfkey.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/include/linux/pfkey.h 2002-10-16 15:27:46.000000000 +0900
@@ -0,0 +1,326 @@
+/*
+ * FreeS/WAN specific PF_KEY headers
+ * Copyright (C) 1999 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <
http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: pfkey.h,v 1.1.1.1 2001/05/22 06:13:35 miyazawa Exp $
+ */
+
+#ifndef __NET_IPSEC_PF_KEY_H
+#define __NET_IPSEC_PF_KEY_H
+#ifdef __KERNEL__
+extern void pfkey_proto_init(struct net_proto *pro);
+extern struct proto_ops pfkey_proto_ops;
+typedef struct sock pfkey_sock;
+extern int debug_pfkey;
+
+extern /* void */ int pfkey_init(void);
+extern /* void */ int pfkey_cleanup(void);
+
+extern struct sock *pfkey_sock_list;
+struct socket_list
+{
+ struct socket *socketp;
+ struct socket_list *next;
+};
+
+extern int pfkey_list_insert_socket(struct socket*, struct socket_list**);
+extern int pfkey_list_remove_socket(struct socket*, struct socket_list**);
+extern struct socket_list *pfkey_open_sockets;
+extern struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];
+extern rwlock_t pfkey_sk_lock;
+
+struct sockaddr_key
+{
+ uint16_t key_family; /* PF_KEY */
+ uint16_t key_pad; /* not used */
+ uint32_t key_pid; /* process ID */
+};
+
+struct pfkey_extracted_data
+{
+ struct tdb* tdb;
+ struct tdb* tdb2;
+ struct eroute *eroute;
+};
+
+extern int pfkey_upmsg(struct socket *, struct sadb_msg *);
+extern int pfkey_expire(struct tdb *, int);
+extern int pfkey_acquire(struct tdb *);
+#endif /* __KERNEL__ */
+
+extern uint8_t satype2proto(uint8_t satype);
+extern uint8_t proto2satype(uint8_t proto);
+extern char* satype2name(uint8_t satype);
+extern char* proto2name(uint8_t proto);
+
+struct key_opt
+{
+ uint32_t key_pid; /* process ID */
+ struct sock *sk;
+};
+
+#define key_pid(sk) ((struct key_opt*)&((sk)->protinfo))->key_pid
+
+#define IPSEC_PFKEYv2_ALIGN (sizeof(uint64_t)/sizeof(uint8_t))
+#define BITS_PER_OCTET 8
+#define OCTETBITS 8
+#define PFKEYBITS 64
+#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */
+#define ALIGN_N(x,y) (DIVUP(x,y) * y) /* align on y boundary */
+
+#define PFKEYv2_MAX_MSGSIZE 4096
+
+/*
+ * PF_KEYv2 permitted and required extensions in and out bitmaps
+ */
+
+extern unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/];
+#define EXT_BITS_IN 0
+#define EXT_BITS_OUT 1
+#define EXT_BITS_PERM 0
+#define EXT_BITS_REQ 1
+
+extern void pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1]);
+extern void pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1]);
+extern void pfkey_msg_free(struct sadb_msg **pfkey_msg);
+
+extern int pfkey_msg_parse(struct sadb_msg *pfkey_msg,
+ int (*ext_parsers[])(struct sadb_ext* ),
+ struct sadb_ext **extensions,
+ int dir);
+
+/*
+ * PF_KEYv2 build function prototypes
+ */
+
+int
+pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext,
+ uint8_t msg_type,
+ uint8_t satype,
+ uint8_t msg_errno,
+ uint32_t seq,
+ uint32_t pid);
+
+int
+pfkey_sa_build(struct sadb_ext ** pfkey_ext,
+ uint16_t exttype,
+ uint32_t spi, /* in network order */
+ uint8_t replay_window,
+ uint8_t sa_state,
+ uint8_t auth,
+ uint8_t encrypt,
+ uint32_t flags);
+
+int
+pfkey_lifetime_build(struct sadb_ext ** pfkey_ext,
+ uint16_t exttype,
+ uint32_t allocations,
+ uint64_t bytes,
+ uint64_t addtime,
+ uint64_t usetime);
+
+int
+pfkey_address_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint8_t proto,
+ uint8_t prefixlen,
+ struct sockaddr* address);
+
+int
+pfkey_key_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint16_t key_bits,
+ char* key);
+
+int
+pfkey_ident_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint16_t ident_type,
+ uint64_t ident_id,
+ char* ident_string);
+
+int
+pfkey_sens_build(struct sadb_ext** pfkey_ext,
+ uint32_t dpd,
+ uint8_t sens_level,
+ uint8_t sens_len,
+ uint64_t* sens_bitmap,
+ uint8_t integ_level,
+ uint8_t integ_len,
+ uint64_t* integ_bitmap);
+
+int
+pfkey_prop_build(struct sadb_ext** pfkey_ext,
+ uint8_t replay,
+ unsigned int comb_num,
+ struct sadb_comb* comb);
+
+int
+pfkey_supported_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ unsigned int alg_num,
+ struct sadb_alg* alg);
+
+int
+pfkey_spirange_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint32_t min,
+ uint32_t max);
+
+int
+pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext);
+
+int
+pfkey_x_satype_build(struct sadb_ext** pfkey_ext,
+ uint8_t satype);
+
+int
+pfkey_x_debug_build(struct sadb_ext** pfkey_ext,
+ uint32_t tunnel,
+ uint32_t netlink,
+ uint32_t xform,
+ uint32_t eroute,
+ uint32_t spi,
+ uint32_t radij,
+ uint32_t esp,
+ uint32_t ah,
+ uint32_t rcv,
+ uint32_t pfkey,
+ uint32_t ipcomp,
+ uint32_t verbose);
+
+int
+pfkey_msg_build(struct sadb_msg** pfkey_msg,
+ struct sadb_ext* extensions[],
+ int dir);
+
+#endif /* __NET_IPSEC_PF_KEY_H */
+
+/*
+ * $Log: pfkey.h,v $
+ * Revision 1.1.1.1 2001/05/22 06:13:35 miyazawa
+ * kernel for ipsec without FS
+ *
+ * Revision 1.30 2001/02/27 07:04:52 rgb
+ * Added satype2name prototype.
+ *
+ * Revision 1.29 2001/02/26 19:59:33 rgb
+ * Ditch unused sadb_satype2proto[], replaced by satype2proto().
+ *
+ * Revision 1.28 2000/10/10 20:10:19 rgb
+ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
+ *
+ * Revision 1.27 2000/09/21 04:20:45 rgb
+ * Fixed array size off-by-one error. (Thanks Svenning!)
+ *
+ * Revision 1.26 2000/09/12 03:26:05 rgb
+ * Added pfkey_acquire prototype.
+ *
+ * Revision 1.25 2000/09/08 19:21:28 rgb
+ * Fix pfkey_prop_build() parameter to be only single indirection.
+ *
+ * Revision 1.24 2000/09/01 18:46:42 rgb
+ * Added a supported algorithms array lists, one per satype and registered
+ * existing algorithms.
+ * Fixed pfkey_list_{insert,remove}_{socket,support}() to allow change to
+ * list.
+ *
+ * Revision 1.23 2000/08/27 01:55:26 rgb
+ * Define OCTETBITS and PFKEYBITS to avoid using 'magic' numbers in code.
+ *
+ * Revision 1.22 2000/08/20 21:39:23 rgb
+ * Added kernel prototypes for kernel funcitions pfkey_upmsg() and
+ * pfkey_expire().
+ *
+ * Revision 1.21 2000/08/15 17:29:23 rgb
+ * Fixes from SZI to untested pfkey_prop_build().
+ *
+ * Revision 1.20 2000/05/10 20:14:19 rgb
+ * Fleshed out sensitivity, proposal and supported extensions.
+ *
+ * Revision 1.19 2000/03/16 14:07:23 rgb
+ * Renamed ALIGN macro to avoid fighting with others in kernel.
+ *
+ * Revision 1.18 2000/01/22 23:24:06 rgb
+ * Added prototypes for proto2satype(), satype2proto() and proto2name().
+ *
+ * Revision 1.17 2000/01/21 06:26:59 rgb
+ * Converted from double tdb arguments to one structure (extr)
+ * containing pointers to all temporary information structures.
+ * Added klipsdebug switching capability.
+ * Dropped unused argument to pfkey_x_satype_build().
+ *
+ * Revision 1.16 1999/12/29 21:17:41 rgb
+ * Changed pfkey_msg_build() I/F to include a struct sadb_msg**
+ * parameter for cleaner manipulation of extensions[] and to guard
+ * against potential memory leaks.
+ * Changed the I/F to pfkey_msg_free() for the same reason.
+ *
+ * Revision 1.15 1999/12/09 23:12:54 rgb
+ * Added macro for BITS_PER_OCTET.
+ * Added argument to pfkey_sa_build() to do eroutes.
+ *
+ * Revision 1.14 1999/12/08 20:33:25 rgb
+ * Changed sa_family_t to uint16_t for 2.0.xx compatibility.
+ *
+ * Revision 1.13 1999/12/07 19:53:40 rgb
+ * Removed unused first argument from extension parsers.
+ * Changed __u* types to uint* to avoid use of asm/types.h and
+ * sys/types.h in userspace code.
+ * Added function prototypes for pfkey message and extensions
+ * initialisation and cleanup.
+ *
+ * Revision 1.12 1999/12/01 22:19:38 rgb
+ * Change pfkey_sa_build to accept an SPI in network byte order.
+ *
+ * Revision 1.11 1999/11/27 11:55:26 rgb
+ * Added extern sadb_satype2proto to enable moving protocol lookup table
+ * to lib/pfkey_v2_parse.c.
+ * Delete unused, moved typedefs.
+ * Add argument to pfkey_msg_parse() for direction.
+ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
+ *
+ * Revision 1.10 1999/11/23 22:29:21 rgb
+ * This file has been moved in the distribution from klips/net/ipsec to
+ * lib.
+ * Add macros for dealing with alignment and rounding up more opaquely.
+ * The uint<n>_t type defines have been moved to freeswan.h to avoid
+ * chicken-and-egg problems.
+ * Add macros for dealing with alignment and rounding up more opaque.
+ * Added prototypes for using extention header bitmaps.
+ * Added prototypes of all the build functions.
+ *
+ * Revision 1.9 1999/11/20 21:59:48 rgb
+ * Moved socketlist type declarations and prototypes for shared use.
+ * Slightly modified scope of sockaddr_key declaration.
+ *
+ * Revision 1.8 1999/11/17 14:34:25 rgb
+ * Protect sa_family_t from being used in userspace with GLIBC<2.
+ *
+ * Revision 1.7 1999/10/27 19:40:35 rgb
+ * Add a maximum PFKEY packet size macro.
+ *
+ * Revision 1.6 1999/10/26 16:58:58 rgb
+ * Created a sockaddr_key and key_opt socket extension structures.
+ *
+ * Revision 1.5 1999/06/10 05:24:41 rgb
+ * Renamed variables to reduce confusion.
+ *
+ * Revision 1.4 1999/04/29 15:21:11 rgb
+ * Add pfkey support to debugging.
+ * Add return values to init and cleanup functions.
+ *
+ * Revision 1.3 1999/04/15 17:58:07 rgb
+ * Add RCSID labels.
+ *
+ */
diff -uNr -x CVS linux-2.5.43/include/linux/pfkeyv2.h linux25.43-ipsec/include/linux/pfkeyv2.h
--- linux-2.5.43/include/linux/pfkeyv2.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/include/linux/pfkeyv2.h 2002-10-16 15:27:46.000000000 +0900
@@ -0,0 +1,368 @@
+/*
+ * RCSID $Id: pfkeyv2.h,v 1.2 2001/05/26 10:10:17 mk Exp $
+ */
+
+/*
+RFC 2367 PF_KEY Key Management API July 1998
+
+
+Appendix D: Sample Header File
+
+This file defines structures and symbols for the PF_KEY Version 2
+key management interface. It was written at the U.S. Naval Research
+Laboratory. This file is in the public domain. The authors ask that
+you leave this credit intact on any copies of this file.
+*/
+#ifndef __PFKEY_V2_H
+#define __PFKEY_V2_H 1
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_IPSEC_DEBUG
+# ifdef CONFIG_SYSCTL
+# define PFKEY_DEBUG(fmt, args...) \
+do { \
+ if (sysctl_ipsec_debug_pfkey) { \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args); \
+ } \
+} while(0)
+# else
+# define PFKEY_DEBUG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#endif /* CONFIG_SYSCTL */
+#else
+# define PFKEY_DEBUG(fmt, args...)
+#endif
+
+#endif /* __KERNEL__ */
+
+#define PF_KEY_V2 2
+#define PFKEYV2_REVISION 199806L
+
+#define SADB_RESERVED 0
+#define SADB_GETSPI 1
+#define SADB_UPDATE 2
+#define SADB_ADD 3
+#define SADB_DELETE 4
+#define SADB_GET 5
+#define SADB_ACQUIRE 6
+#define SADB_REGISTER 7
+#define SADB_EXPIRE 8
+#define SADB_FLUSH 9
+#define SADB_DUMP 10
+#define SADB_X_PROMISC 11
+#define SADB_X_PCHANGE 12
+#define SADB_X_GRPSA 13
+#define SADB_X_ADDFLOW 14
+#define SADB_X_DELFLOW 15
+#define SADB_X_DEBUG 16
+#define SADB_X_FLUSH_SP 17
+#define SADB_MAX 17
+
+struct sadb_msg {
+ uint8_t sadb_msg_version;
+ uint8_t sadb_msg_type;
+ uint8_t sadb_msg_errno;
+ uint8_t sadb_msg_satype;
+ uint16_t sadb_msg_len;
+ uint16_t sadb_msg_reserved;
+ uint32_t sadb_msg_seq;
+ uint32_t sadb_msg_pid;
+};
+
+struct sadb_ext {
+ uint16_t sadb_ext_len;
+ uint16_t sadb_ext_type;
+};
+
+struct sadb_sa {
+ uint16_t sadb_sa_len;
+ uint16_t sadb_sa_exttype;
+ uint32_t sadb_sa_spi;
+ uint8_t sadb_sa_replay;
+ uint8_t sadb_sa_state;
+ uint8_t sadb_sa_auth;
+ uint8_t sadb_sa_encrypt;
+ uint32_t sadb_sa_flags;
+};
+
+struct sadb_lifetime {
+ uint16_t sadb_lifetime_len;
+ uint16_t sadb_lifetime_exttype;
+ uint32_t sadb_lifetime_allocations;
+ uint64_t sadb_lifetime_bytes;
+ uint64_t sadb_lifetime_addtime;
+ uint64_t sadb_lifetime_usetime;
+};
+
+struct sadb_address {
+ uint16_t sadb_address_len;
+ uint16_t sadb_address_exttype;
+ uint8_t sadb_address_proto;
+ uint8_t sadb_address_prefixlen;
+ uint16_t sadb_address_reserved;
+};
+
+struct sadb_key {
+ uint16_t sadb_key_len;
+ uint16_t sadb_key_exttype;
+ uint16_t sadb_key_bits;
+ uint16_t sadb_key_reserved;
+};
+
+struct sadb_ident {
+ uint16_t sadb_ident_len;
+ uint16_t sadb_ident_exttype;
+ uint16_t sadb_ident_type;
+ uint16_t sadb_ident_reserved;
+ uint64_t sadb_ident_id;
+};
+
+struct sadb_sens {
+ uint16_t sadb_sens_len;
+ uint16_t sadb_sens_exttype;
+ uint32_t sadb_sens_dpd;
+ uint8_t sadb_sens_sens_level;
+ uint8_t sadb_sens_sens_len;
+ uint8_t sadb_sens_integ_level;
+ uint8_t sadb_sens_integ_len;
+ uint32_t sadb_sens_reserved;
+};
+
+struct sadb_prop {
+ uint16_t sadb_prop_len;
+ uint16_t sadb_prop_exttype;
+ uint8_t sadb_prop_replay;
+ uint8_t sadb_prop_reserved[3];
+};
+
+struct sadb_comb {
+ uint8_t sadb_comb_auth;
+ uint8_t sadb_comb_encrypt;
+ uint16_t sadb_comb_flags;
+ uint16_t sadb_comb_auth_minbits;
+ uint16_t sadb_comb_auth_maxbits;
+ uint16_t sadb_comb_encrypt_minbits;
+ uint16_t sadb_comb_encrypt_maxbits;
+ uint32_t sadb_comb_reserved;
+ uint32_t sadb_comb_soft_allocations;
+ uint32_t sadb_comb_hard_allocations;
+ uint64_t sadb_comb_soft_bytes;
+ uint64_t sadb_comb_hard_bytes;
+ uint64_t sadb_comb_soft_addtime;
+ uint64_t sadb_comb_hard_addtime;
+ uint64_t sadb_comb_soft_usetime;
+ uint64_t sadb_comb_hard_usetime;
+};
+
+struct sadb_supported {
+ uint16_t sadb_supported_len;
+ uint16_t sadb_supported_exttype;
+ uint32_t sadb_supported_reserved;
+};
+
+struct sadb_alg {
+ uint8_t sadb_alg_id;
+ uint8_t sadb_alg_ivlen;
+ uint16_t sadb_alg_minbits;
+ uint16_t sadb_alg_maxbits;
+ uint16_t sadb_alg_reserved;
+};
+
+struct sadb_spirange {
+ uint16_t sadb_spirange_len;
+ uint16_t sadb_spirange_exttype;
+ uint32_t sadb_spirange_min;
+ uint32_t sadb_spirange_max;
+ uint32_t sadb_spirange_reserved;
+};
+
+struct sadb_x_kmprivate {
+ uint16_t sadb_x_kmprivate_len;
+ uint16_t sadb_x_kmprivate_exttype;
+ uint32_t sadb_x_kmprivate_reserved;
+};
+
+struct sadb_x_satype {
+ uint16_t sadb_x_satype_len;
+ uint16_t sadb_x_satype_exttype;
+ uint8_t sadb_x_satype_satype;
+ uint8_t sadb_x_satype_reserved[3];
+};
+
+struct sadb_x_debug {
+ uint16_t sadb_x_debug_len;
+ uint16_t sadb_x_debug_exttype;
+ uint32_t sadb_x_debug_tunnel;
+ uint32_t sadb_x_debug_netlink;
+ uint32_t sadb_x_debug_xform;
+ uint32_t sadb_x_debug_eroute;
+ uint32_t sadb_x_debug_spi;
+ uint32_t sadb_x_debug_radij;
+ uint32_t sadb_x_debug_esp;
+ uint32_t sadb_x_debug_ah;
+ uint32_t sadb_x_debug_rcv;
+ uint32_t sadb_x_debug_pfkey;
+ uint32_t sadb_x_debug_ipcomp;
+ uint32_t sadb_x_debug_verbose;
+ uint8_t sadb_x_debug_reserved[4];
+};
+
+#define SADB_EXT_RESERVED 0
+#define SADB_EXT_SA 1
+#define SADB_EXT_LIFETIME_CURRENT 2
+#define SADB_EXT_LIFETIME_HARD 3
+#define SADB_EXT_LIFETIME_SOFT 4
+#define SADB_EXT_ADDRESS_SRC 5
+#define SADB_EXT_ADDRESS_DST 6
+#define SADB_EXT_ADDRESS_PROXY 7
+#define SADB_EXT_KEY_AUTH 8
+#define SADB_EXT_KEY_ENCRYPT 9
+#define SADB_EXT_IDENTITY_SRC 10
+#define SADB_EXT_IDENTITY_DST 11
+#define SADB_EXT_SENSITIVITY 12
+#define SADB_EXT_PROPOSAL 13
+#define SADB_EXT_SUPPORTED_AUTH 14
+#define SADB_EXT_SUPPORTED_ENCRYPT 15
+#define SADB_EXT_SPIRANGE 16
+#define SADB_X_EXT_KMPRIVATE 17
+#define SADB_X_EXT_SATYPE2 18
+#define SADB_X_EXT_SA2 19
+#define SADB_X_EXT_ADDRESS_DST2 20
+#define SADB_X_EXT_ADDRESS_SRC_FLOW 21
+#define SADB_X_EXT_ADDRESS_DST_FLOW 22
+#define SADB_X_EXT_ADDRESS_SRC_MASK 23
+#define SADB_X_EXT_ADDRESS_DST_MASK 24
+#define SADB_X_EXT_DEBUG 25
+#define SADB_EXT_MAX 25
+
+/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */
+#define SADB_X_EXT_ADDRESS_DELFLOW \
+ ( (1<<SADB_X_EXT_ADDRESS_SRC_FLOW) \
+ | (1<<SADB_X_EXT_ADDRESS_DST_FLOW) \
+ | (1<<SADB_X_EXT_ADDRESS_SRC_MASK) \
+ | (1<<SADB_X_EXT_ADDRESS_DST_MASK))
+
+#define SADB_SATYPE_UNSPEC 0
+#define SADB_SATYPE_AH 2
+#define SADB_SATYPE_ESP 3
+#define SADB_SATYPE_RSVP 5
+#define SADB_SATYPE_OSPFV2 6
+#define SADB_SATYPE_RIPV2 7
+#define SADB_SATYPE_MIP 8
+#define SADB_X_SATYPE_IPIP 9
+#define SADB_X_SATYPE_COMP 10
+#define SADB_X_SATYPE_INT 11
+#define SADB_SATYPE_MAX 11
+
+#define SADB_SASTATE_LARVAL 0
+#define SADB_SASTATE_MATURE 1
+#define SADB_SASTATE_DYING 2
+#define SADB_SASTATE_DEAD 3
+#define SADB_SASTATE_MAX 3
+
+#define SADB_SAFLAGS_PFS 1
+#define SADB_X_SAFLAGS_REPLACEFLOW 2
+#define SADB_X_SAFLAGS_CLEARFLOW 4
+#define SADB_X_SAFLAGS_TUNNEL 8
+
+#define SADB_AALG_NONE 0
+#define SADB_AALG_MD5HMAC 2
+#define SADB_AALG_SHA1HMAC 3
+#define SADB_AALG_MAX 3
+
+#define SADB_EALG_NONE 0
+#define SADB_EALG_DES_IV64 1 /* N/A */
+#define SADB_EALG_DESCBC 2
+#define SADB_EALG_3DESCBC 3
+#define SADB_EALG_RC5 4 /* N/A */
+#define SADB_EALG_IDEA 5 /* N/A */
+#define SADB_EALG_CAST 6 /* N/A */
+#define SADB_EALG_BLOWFISH 7 /* N/A */
+#define SADB_EALG_3IDEA 8 /* N/A */
+#define SADB_EALG_DES_IV32 9 /* N/A */
+#define SADB_EALG_ESP_RC4 10 /* N/A */
+#define SADB_EALG_NULL 11
+#define SADB_EALG_AES 12 /* N/A */
+#define SADB_EALG_MAX 12
+
+#define SADB_X_CALG_NONE 0
+#define SADB_X_CALG_OUI 1
+#define SADB_X_CALG_DEFLATE 2
+#define SADB_X_CALG_LZS 3
+#define SADB_X_CALG_V42BIS 4
+#define SADB_X_CALG_LZJH 4
+#define SADB_X_CALG_MAX 4
+
+#define SADB_X_TALG_NONE 0
+#define SADB_X_TALG_IPv4_in_IPv4 1
+#define SADB_X_TALG_IPv6_in_IPv4 2
+#define SADB_X_TALG_IPv4_in_IPv6 3
+#define SADB_X_TALG_IPv6_in_IPv6 4
+#define SADB_X_TALG_MAX 4
+
+
+#define SADB_IDENTTYPE_RESERVED 0
+#define SADB_IDENTTYPE_PREFIX 1
+#define SADB_IDENTTYPE_FQDN 2
+#define SADB_IDENTTYPE_USERFQDN 3
+#define SADB_IDENTTYPE_MAX 3
+
+#define SADB_KEY_FLAGS_MAX 0
+#endif /* __PFKEY_V2_H */
+
+/*
+ * $Log: pfkeyv2.h,v $
+ * Revision 1.2 2001/05/26 10:10:17 mk
+ * rename ah_utils.h to ipsec6_utils.h
+ *
+ * Revision 1.1.1.1 2001/05/22 06:13:33 miyazawa
+ * kernel for ipsec without FS
+ *
+ * Revision 1.15 2001/02/26 20:00:43 rgb
+ * Added internal IP protocol 61 for magic SAs.
+ *
+ * Revision 1.14 2001/02/08 18:51:05 rgb
+ * Include RFC document title and appendix subsection title.
+ *
+ * Revision 1.13 2000/10/10 20:10:20 rgb
+ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
+ *
+ * Revision 1.12 2000/09/15 06:41:50 rgb
+ * Added V42BIS constant.
+ *
+ * Revision 1.11 2000/09/12 22:35:37 rgb
+ * Restructured to remove unused extensions from CLEARFLOW messages.
+ *
+ * Revision 1.10 2000/09/12 18:50:09 rgb
+ * Added IPIP tunnel types as algo support.
+ *
+ * Revision 1.9 2000/08/21 16:47:19 rgb
+ * Added SADB_X_CALG_* macros for IPCOMP.
+ *
+ * Revision 1.8 2000/08/09 20:43:34 rgb
+ * Fixed bitmask value for SADB_X_SAFLAGS_CLEAREROUTE.
+ *
+ * Revision 1.7 2000/01/21 06:28:37 rgb
+ * Added flow add/delete message type macros.
+ * Added flow address extension type macros.
+ * Tidied up spacing.
+ * Added klipsdebug switching capability.
+ *
+ * Revision 1.6 1999/11/27 11:56:08 rgb
+ * Add SADB_X_SATYPE_COMP for compression, eventually.
+ *
+ * Revision 1.5 1999/11/23 22:23:16 rgb
+ * This file has been moved in the distribution from klips/net/ipsec to
+ * lib.
+ *
+ * Revision 1.4 1999/04/29 15:23:29 rgb
+ * Add GRPSA support.
+ * Add support for a second SATYPE, SA and DST_ADDRESS.
+ * Add IPPROTO_IPIP support.
+ *
+ * Revision 1.3 1999/04/15 17:58:08 rgb
+ * Add RCSID labels.
+ *
+ */
diff -uNr -x CVS linux-2.5.43/include/linux/socket.h linux25.43-ipsec/include/linux/socket.h
--- linux-2.5.43/include/linux/socket.h 2002-10-16 12:28:33.000000000 +0900
+++ linux25.43-ipsec/include/linux/socket.h 2002-10-16 15:27:46.000000000 +0900
@@ -25,6 +25,24 @@
};
/*
+ * Desired design of maximum size and alignment
+ */
+#define _SS_MAXSIZE 128 /* Implementation specific max size */
+#define _SS_ALIGNSIZE (sizeof (int64_t)) /* Implementation specific desired alignment */
+/*
+ * Definitions used for sockaddr_storage structure paddings design.
+ */
+#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof (sa_family_t))
+#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (sa_family_t)+ _SS_PAD1SIZE + _SS_ALIGNSIZE))
+
+struct sockaddr_storage {
+ sa_family_t ss_family;
+ char __ss_pad1[_SS_PAD1SIZE];
+ int64_t __ss_align;
+ char __ss_pad2[_SS_PAD2SIZE];
+};
+
+/*
* As we do 4.4BSD message passing we use a 4.4BSD message passing
* system, not 4.3. Thus msg_accrights(len) are now missing. They
* belong in an obscure libc emulation or the bin.
diff -uNr -x CVS linux-2.5.43/include/linux/sysctl.h linux25.43-ipsec/include/linux/sysctl.h
--- linux-2.5.43/include/linux/sysctl.h 2002-10-16 12:27:08.000000000 +0900
+++ linux25.43-ipsec/include/linux/sysctl.h 2002-10-16 15:27:46.000000000 +0900
@@ -177,6 +177,7 @@
NET_DECNET=15,
NET_ECONET=16,
NET_SCTP=17,
+ NET_IPSEC=18
};
/* /proc/sys/kernel/random */
@@ -385,6 +386,16 @@
NET_IPV6_RTR_SOLICIT_DELAY=10
};
+/* /proc/net/ipsec */
+enum {
+ NET_IPSEC_REPLAY_WINDOW=1,
+ NET_IPSEC_DEBUG_IPV4=4,
+ NET_IPSEC_DEBUG_IPV6=5,
+ NET_IPSEC_DEBUG_PFKEY=6,
+ NET_IPSEC_DEBUG_SADB=7,
+ NET_IPSEC_DEBUG_SPD=8,
+};
+
/* /proc/sys/net/<protocol>/neigh/<dev> */
enum {
NET_NEIGH_MCAST_SOLICIT=1,
diff -uNr -x CVS linux-2.5.43/include/net/ipv6.h linux25.43-ipsec/include/net/ipv6.h
--- linux-2.5.43/include/net/ipv6.h 2002-10-16 12:28:34.000000000 +0900
+++ linux25.43-ipsec/include/net/ipv6.h 2002-10-16 15:27:46.000000000 +0900
@@ -185,7 +185,7 @@
extern int ipv6_parse_hopopts(struct sk_buff *skb, int);
-extern int ipv6_parse_exthdrs(struct sk_buff **skb, int);
+extern int ipv6_parse_exthdrs(struct sk_buff **skb, int, u8* nexthdr);
extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
diff -uNr -x CVS linux-2.5.43/include/net/sadb.h linux25.43-ipsec/include/net/sadb.h
--- linux-2.5.43/include/net/sadb.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/include/net/sadb.h 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,230 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/* IPsec Security Association Implementation */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SADB_H
+#define __SADB_H
+
+#include <linux/types.h>
+#include <linux/socket.h> /* sockaddr_storage */
+#include <linux/list.h> /* list_entry */
+#include <linux/spinlock.h> /* rw_lock_t */
+#include <linux/crypto.h>
+#include <asm/atomic.h>
+
+/* internally reserved SPI values */
+#define IPSEC_SPI_BYPASS 256
+#define IPSEC_SPI_DROP 257
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_IPSEC_DEBUG
+# ifdef CONFIG_SYSCTL
+# define SADB_DEBUG(fmt, args...) \
+do { \
+ if (sysctl_ipsec_debug_sadb) { \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args); \
+ } \
+} while(0)
+# else
+# define SADB_DEBUG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+# endif /* CONFIG_SYSCTL */
+#else
+# define SADB_DEBUG(fmt, args...)
+#endif
+
+
+#define KEY_LEN_MAX 24
+#define AUTH_KEY_LEN_MAX 20 /* enough for HMAC SHA1 key length */
+#define ESP_KEY_LEN_MAX 384 /* enough for 3DES key length */
+#define ESP_IV_LEN_MAX 16
+#define IPSEC_SPI_ANY 0xFFFFFFFF
+
+extern struct list_head sadb_list;
+extern rwlock_t sadb_lock;
+
+/* The sa_replay_window holds information for a replay window.*/
+struct sa_replay_window{
+ __u8 overflow;
+ __u8 size;
+ __u32 seq_num;
+ __u32 last_seq;
+ __u32 bitmap;
+};
+
+/* The sa_lifetime holds lifetime for a specific SA. */
+struct sa_lifetime{
+ __u64 bytes;
+ __u64 addtime;
+ __u64 usetime;
+ __u32 allocations;
+};
+
+/* We use kerneli transformation. */
+struct auth_algo_info{
+ __u8 algo;
+ __u8 *key;
+ __u16 key_len; /* key length in byte */
+ __u16 digest_len;
+ struct digest_context *dx;
+
+};
+
+struct esp_algo_info{
+ __u8 algo;
+ __u8 *key;
+ __u16 key_len; /* key length in byte */
+ __u8 *iv;
+ struct cipher_context *cx;
+};
+
+/* Security Association (SA)
+ *
+ * RFC2401 ch4.4 says
+ *
+ * Each interface for which IPsec is enabled requires nominally separate
+ * inbound vs. outbound databases (SAD and SPD), because of the
+ * directionality of many of the fields that are used as selectors.
+ *
+ * But we don't care whether SA is inbound or outbound.
+ * Because it is enough to check src/dst address pair of SA. */
+struct ipsec_sa {
+
+ /* Never touch entry without lock sadb_lock */
+ struct list_head entry;
+
+ /* list entry for temporary work */
+ struct list_head tmp_entry;
+
+ /* reference count describes the number of sa_index_t */
+ atomic_t refcnt;
+
+ /* This lock only affect elements except for entry */
+ rwlock_t lock;
+
+ /* The sadb_address_proto field is normally zero,
+ which MUST be filled with the transport protocol's number. */
+ __u8 proto;
+
+ /* SA status */
+ __u8 state;
+
+ /* tunnel mode */
+ __u8 tunnel_mode;
+
+ /* ipsec protocol like AH, ESP, IPCOMP */
+ __u8 ipsec_proto;
+
+ /* SPI: Security Parameter Index (network byte order)
+ * or
+ * CPI: Compression Prameter Index
+ * IPcomp algorithm(CPI): deflate=2, (lhz=3, lzjh) (network byte order)
+ * in case of CPI, we use lower u16 as CPI. */
+ __u32 spi;
+
+ /* SA destination address */
+ struct sockaddr_storage dst;
+
+ /* SA source address */
+ struct sockaddr_storage src;
+
+ /* SA proxy address (RFC specified but we don't use) */
+ /* struct sockaddr_storage pxy; */
+
+ /* SA source address prefix len */
+ __u8 prefixlen_s;
+
+ /* SA dst address prefix len */
+ __u8 prefixlen_d;
+
+ /* SA proxy address prefix len (not use) */
+ /* __u8 prefixlen_p; */
+
+ /* replay window */
+ struct sa_replay_window replay_window;
+
+ /* liftetime for this SA, giving an upper limit
+ for the sequence number */
+ struct sa_lifetime lifetime_s; /* soft lifetime */
+ struct sa_lifetime lifetime_h; /* hard lifetime */
+ struct sa_lifetime lifetime_c; /* current lifetime */
+ /* internal use, because uint64 and jiffies are different unit */
+ unsigned long init_time; /* initial time (unit: jiffies) */
+ unsigned long fuse_time; /* first use time (unit: jiffies) */
+
+ struct timer_list timer;
+
+ /* algo is the number of the algorithm in use */
+ struct auth_algo_info auth_algo;
+ struct esp_algo_info esp_algo;
+};
+
+struct sa_index{
+
+ struct list_head entry;
+
+ /* SA doesn't always exist.
+ * If SA is null, it means suitable SA doesn't exist
+ * in SA list(it consists of struct ipsec_sa_t). */
+ struct ipsec_sa *sa;
+
+ struct sockaddr_storage dst;
+ __u32 spi;
+ __u8 prefixlen_d;
+ __u8 ipsec_proto;
+};
+
+int sadb_init(void);
+int sadb_cleanup(void);
+
+/* The function sadb_append copies entry to own list. */
+/* It is able to free the parameter entry. */
+struct ipsec_sa* ipsec_sa_kmalloc(void);
+int ipsec_sa_init(struct ipsec_sa *sa);
+void ipsec_sa_kfree(struct ipsec_sa *sa);
+int ipsec_sa_copy(struct ipsec_sa *dst, struct ipsec_sa *src);
+void ipsec_sa_lifetime_check(unsigned long data);
+void ipsec_sa_put(struct ipsec_sa *sa);
+static inline void ipsec_sa_hold(struct ipsec_sa *sa)
+{
+ if(sa) atomic_inc(&sa->refcnt);
+};
+
+int sadb_append(struct ipsec_sa *sa);
+void sadb_remove(struct ipsec_sa *sa);
+void sadb_clear_db(void);
+int sadb_flush_sa(int satype);
+
+/* These functions expect the parameter SA has been allocated. */
+struct ipsec_sa* sadb_find_by_sa_index(struct sa_index *sa_index);
+int sadb_find_by_address_proto_spi(struct sockaddr *src, __u8 prefixlen_s,
+ struct sockaddr *dst, __u8 prefixlen_d,
+ __u8 ipsec_proto, __u32 spi, struct ipsec_sa **sa);
+
+/* The struct sa_index is a glue between SA and Policy */
+struct sa_index* sa_index_kmalloc(void);
+int sa_index_init(struct sa_index *sa);
+void sa_index_kfree(struct sa_index *sa);
+int sa_index_compare(struct sa_index *sa_idx1, struct sa_index *sa_idx2);
+int sa_index_copy(struct sa_index *dst, struct sa_index *src);
+void ipsec_sa_mod_timer(struct ipsec_sa *sa);
+
+#endif /* __KERNEL__ */
+#endif /* __SADB_H */
+
diff -uNr -x CVS linux-2.5.43/include/net/spd.h linux25.43-ipsec/include/net/spd.h
--- linux-2.5.43/include/net/spd.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/include/net/spd.h 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,117 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/* IPsec Security Policy Implementation */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SPD_H
+#define __SPD_H
+
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/spinlock.h>
+#include <net/sadb.h>
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_IPSEC_DEBUG
+# ifdef CONFIG_SYSCTL
+# define SPD_DEBUG(fmt, args...) \
+do { \
+ if (sysctl_ipsec_debug_spd) { \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args); \
+ } \
+} while(0)
+# else
+# define SPD_DEBUG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+# endif /* CONFIG_SYSCTL */
+#else
+# define SPD_DEBUG(fmt, args...)
+#endif
+
+extern struct list_head spd_list;
+extern rwlock_t spd_lock;
+
+/* Selector for SPD
+ *
+ * Now Selector realy consist of a pair of source and destination address. */
+struct selector{
+ /* TODO: It should be better to hold elements as reference(pointer) */
+ struct sockaddr_storage src;
+ struct sockaddr_storage dst;
+ __u8 prefixlen_s;
+ __u8 prefixlen_d;
+ __u8 proto;
+#ifdef CONFIG_IPSEC_TUNNEL
+ __u8 mode;
+#endif
+};
+
+/* IPsec Policy */
+struct ipsec_sp{
+ /* Never touch entry without locking spd_lock */
+ struct list_head entry;
+
+ /* This lock only affects elements except for entry. */
+ rwlock_t lock;
+ atomic_t refcnt;
+ struct selector selector;
+ struct sa_index* auth_sa_idx;
+ struct sa_index* esp_sa_idx;
+ struct sa_index* comp_sa_idx;
+ __u8 policy_action;
+};
+
+struct ipsec_sp* ipsec_sp_kmalloc(void);
+int ipsec_sp_init(struct ipsec_sp *policy);
+void ipsec_sp_kfree(struct ipsec_sp *policy);
+int ipsec_sp_copy(struct ipsec_sp *dst, struct ipsec_sp *src);
+int ipsec_sp_put(struct ipsec_sp *policy);
+void ipsec_sp_release_invalid_sa(struct ipsec_sp *policy, struct ipsec_sa *sa);
+
+static inline void ipsec_sp_hold(struct ipsec_sp *policy)
+{
+ if (policy) atomic_inc(&policy->refcnt);
+}
+
+int spd_append(struct ipsec_sp *entry);
+int spd_remove(struct selector *selector);
+int spd_find_by_selector(struct selector *selector, struct ipsec_sp **policy);
+
+static inline struct ipsec_sp* spd_get(struct selector *selector)
+{
+ struct ipsec_sp *policy = NULL;
+ spd_find_by_selector(selector, &policy);
+ return policy;
+}
+
+void spd_clear_db(void);
+
+int spd_init(void);
+int spd_cleanup(void);
+
+#define IPSEC_POLICY_APPLY 0
+#define IPSEC_POLICY_BYPASS 1
+#define IPSEC_POLICY_DROP 2
+
+#ifdef CONFIG_IPSEC_TUNNEL
+#define IPSEC_MODE_TRANSPORT 0
+#define IPSEC_MODE_TUNNEL 1
+#endif
+
+#endif /* __KERNEL__ */
+#endif /* __SPD_H */
diff -uNr -x CVS linux-2.5.43/net/Config.in linux25.43-ipsec/net/Config.in
--- linux-2.5.43/net/Config.in 2002-10-16 12:27:22.000000000 +0900
+++ linux25.43-ipsec/net/Config.in 2002-10-16 15:27:47.000000000 +0900
@@ -18,6 +18,14 @@
tristate 'Unix domain sockets' CONFIG_UNIX
bool 'TCP/IP networking' CONFIG_INET
if [ "$CONFIG_INET" = "y" ]; then
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_CRYPTO" = "y" ]; then
+ bool ' The IPsec protocol (EXPERIMENTAL)' CONFIG_IPSEC
+ if [ "$CONFIG_IPSEC" != "n" ]; then
+ source net/key/Config.in
+ fi
+ fi
+ fi
source net/ipv4/Config.in
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
# IPv6 as module will cause a CRASH if you try to unload it
diff -uNr -x CVS linux-2.5.43/net/Makefile linux25.43-ipsec/net/Makefile
--- linux-2.5.43/net/Makefile 2002-10-16 12:27:55.000000000 +0900
+++ linux25.43-ipsec/net/Makefile 2002-10-16 15:27:47.000000000 +0900
@@ -35,6 +35,7 @@
obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_VLAN_8021Q) += 8021q/
obj-$(CONFIG_IP_SCTP) += sctp/
+obj-$(CONFIG_IPSEC) += key/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_MODULES) += netsyms.o
diff -uNr -x CVS linux-2.5.43/net/ipv4/Config.help linux25.43-ipsec/net/ipv4/Config.help
--- linux-2.5.43/net/ipv4/Config.help 2002-10-16 12:27:54.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/Config.help 2002-10-16 15:27:48.000000000 +0900
@@ -277,3 +277,7 @@
gated-5). This routing protocol is not used widely, so say N unless
you want to play with it.
+CONFIG_IP_IPSEC
+
+ IPsec for IPv4 support. You also need to say Y to "The IPsec Protocol."
+ If unsure, say N.
diff -uNr -x CVS linux-2.5.43/net/ipv4/Config.in linux25.43-ipsec/net/ipv4/Config.in
--- linux-2.5.43/net/ipv4/Config.in 2002-10-16 12:27:14.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/Config.in 2002-10-16 15:27:48.000000000 +0900
@@ -41,6 +41,12 @@
fi
bool ' IP: TCP Explicit Congestion Notification support' CONFIG_INET_ECN
bool ' IP: TCP syncookie support (disabled per default)' CONFIG_SYN_COOKIES
+# --- IPsec ---
+if [ "$CONFIG_EXPERIMENTAL" = "y" ] ; then
+ if [ "$CONFIG_IPSEC" != "n" ] ; then
+ bool ' IP: IP Security Support (EXPERIMENTAL) ' CONFIG_IP_IPSEC
+ fi
+fi
if [ "$CONFIG_NETFILTER" != "n" ]; then
source net/ipv4/netfilter/Config.in
fi
diff -uNr -x CVS linux-2.5.43/net/ipv4/Makefile linux25.43-ipsec/net/ipv4/Makefile
--- linux-2.5.43/net/ipv4/Makefile 2002-10-16 12:27:23.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/Makefile 2002-10-16 15:27:48.000000000 +0900
@@ -16,6 +16,7 @@
obj-$(CONFIG_NET_IPGRE) += ip_gre.o
obj-$(CONFIG_SYN_COOKIES) += syncookies.o
obj-$(CONFIG_IP_PNP) += ipconfig.o
+obj-$(CONFIG_IP_IPSEC) += ipsec4_output.o ipsec4_input.o
obj-$(CONFIG_NETFILTER) += netfilter/
include $(TOPDIR)/Rules.make
diff -uNr -x CVS linux-2.5.43/net/ipv4/ip_input.c linux25.43-ipsec/net/ipv4/ip_input.c
--- linux-2.5.43/net/ipv4/ip_input.c 2002-10-16 12:27:12.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/ip_input.c 2002-10-16 15:27:48.000000000 +0900
@@ -143,6 +143,7 @@
#include <linux/netfilter_ipv4.h>
#include <linux/mroute.h>
#include <linux/netlink.h>
+#include <linux/ipsec.h>
/*
* SNMP management statistics
@@ -197,6 +198,9 @@
static inline int ip_local_deliver_finish(struct sk_buff *skb)
{
int ihl = skb->nh.iph->ihl*4;
+#ifdef CONFIG_IP_IPSEC
+ int result;
+#endif
#ifdef CONFIG_NETFILTER_DEBUG
nf_debug_ip_local_deliver(skb);
@@ -214,6 +218,13 @@
/* Point into the IP datagram, just past the header. */
skb->h.raw = skb->data;
+#ifdef CONFIG_IP_IPSEC
+ result = ipsec4_input_check(&skb);
+ if (result < 0) {
+ kfree_skb(skb);
+ return result;
+ }
+#endif
{
/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
int protocol = skb->nh.iph->protocol;
diff -uNr -x CVS linux-2.5.43/net/ipv4/ip_output.c linux25.43-ipsec/net/ipv4/ip_output.c
--- linux-2.5.43/net/ipv4/ip_output.c 2002-10-16 12:29:05.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/ip_output.c 2002-10-16 15:27:48.000000000 +0900
@@ -77,6 +77,7 @@
#include <linux/netfilter_ipv4.h>
#include <linux/mroute.h>
#include <linux/netlink.h>
+#include <linux/ipsec.h>
/*
* Shall we try to damage output packets if routing dev changes?
@@ -125,6 +126,38 @@
struct inet_opt *inet = inet_sk(sk);
struct rtable *rt = (struct rtable *)skb->dst;
struct iphdr *iph;
+#ifdef CONFIG_IP_IPSEC
+ struct ip_auth_hdr *authhdr = NULL;
+ int ah_len;
+ struct ipsec_sp *policy_ptr = NULL;
+ int ipsec_action = 0;
+ u8 protocol = sk->protocol;
+
+ ipsec_action = ipsec4_output_check(sk, rt, &policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *encdata = NULL;
+ unsigned int enclength = 0;
+ unsigned int encaddsize = 0;
+ struct ip_esp_hdr *esphdr = NULL;
+
+ ipsec4_out_enc(skb->data, skb->len, protocol, &encdata, &enclength, policy_ptr);
+ encaddsize = enclength - skb->len;
+ esphdr = (struct ip_esp_hdr*) skb_push(skb, encaddsize);
+ memcpy(esphdr, encdata, enclength);
+ protocol = IPPROTO_ESP;
+ if (encdata) kfree(encdata);
+ }
+
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ ah_len = ipsec4_out_get_ahsize(policy_ptr);
+ authhdr = (struct ip_auth_hdr*) skb_push(skb, ah_len);
+ authhdr->nexthdr = protocol;
+ authhdr->hdrlen = (ah_len >> 2) -2;
+ authhdr->reserved = 0;
+ protocol = IPPROTO_AH;
+ }
+#endif
/* Build the IP header. */
if (opt)
@@ -142,7 +175,11 @@
iph->ttl = inet->ttl;
iph->daddr = rt->rt_dst;
iph->saddr = rt->rt_src;
+#ifdef CONFIG_IP_IPSEC
+ iph->protocol = protocol;
+#else
iph->protocol = sk->protocol;
+#endif
iph->tot_len = htons(skb->len);
ip_select_ident(iph, &rt->u.dst, sk);
skb->nh.iph = iph;
@@ -151,6 +188,10 @@
iph->ihl += opt->optlen>>2;
ip_options_build(skb, opt, daddr, rt, 0);
}
+#ifdef CONFIG_IP_IPSEC
+ ipsec4_out_ah_calc(skb->nh.iph, authhdr, policy_ptr);
+ ipsec4_out_finish(policy_ptr);
+#endif
ip_send_check(iph);
/* Send it out. */
@@ -319,8 +360,9 @@
skb_shinfo(skb)->tso_size - 1;
}
+#ifndef CONFIG_IP_IPSEC
ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);
-
+#endif
/* Add an IP checksum. */
ip_send_check(iph);
@@ -354,7 +396,13 @@
struct ip_options *opt = inet->opt;
struct rtable *rt;
struct iphdr *iph;
-
+#ifdef CONFIG_IP_IPSEC
+ struct ipsec_sp *policy_ptr = NULL;
+ struct ip_auth_hdr *authhdr = NULL;
+ int ah_len = 0;
+ int ipsec_action = 0;
+ u8 protocol = sk->protocol;
+#endif
/* Skip all of this if the packet is already routed,
* f.e. by something like SCTP.
*/
@@ -392,9 +440,38 @@
skb->dst = dst_clone(&rt->u.dst);
packet_routed:
+
if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
goto no_route;
+#ifdef CONFIG_IP_IPSEC
+ ipsec_action = ipsec4_output_check(sk, rt, &policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *encdata = NULL;
+ unsigned int enclength = 0;
+ unsigned int encaddsize = 0;
+ struct ip_esp_hdr *esphdr = NULL;
+
+ ipsec4_out_enc(skb->data, skb->len, protocol, &encdata, &enclength, policy_ptr);
+ encaddsize = enclength - skb->len;
+ esphdr = (struct ip_esp_hdr*) skb_push(skb, encaddsize);
+ memcpy(esphdr, encdata, enclength);
+ protocol = IPPROTO_ESP;
+ if (encdata) kfree(encdata);
+ }
+
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ ah_len = ipsec4_out_get_ahsize(policy_ptr);
+ authhdr = (struct ip_auth_hdr*)skb_push(skb, ah_len);
+ authhdr->nexthdr = protocol;
+ authhdr->hdrlen = (ah_len >> 2) - 2;
+ authhdr->reserved = 0;
+ protocol = IPPROTO_AH;
+ }
+#endif
+
+
/* OK, we know where to send it, allocate and build IP header. */
iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
*((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
@@ -404,7 +481,12 @@
else
iph->frag_off = 0;
iph->ttl = inet->ttl;
+#ifdef CONFIG_IP_IPSEC
+ iph->protocol = protocol;
+ ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);
+#else
iph->protocol = sk->protocol;
+#endif
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst;
skb->nh.iph = iph;
@@ -414,16 +496,30 @@
iph->ihl += opt->optlen >> 2;
ip_options_build(skb, opt, inet->daddr, rt, 0);
}
-
+#ifdef CONFIG_IP_IPSEC
+ ipsec4_out_ah_calc(skb->nh.iph, authhdr, policy_ptr);
+#endif
return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
ip_queue_xmit2);
+#ifdef CONFIG_IP_IPSEC
+ ipsec4_out_finish(policy_ptr);
+#endif
+
no_route:
IP_INC_STATS(IpOutNoRoutes);
kfree_skb(skb);
return -EHOSTUNREACH;
}
+#ifdef CONFIG_IP_IPSEC
+static int ipsec4_getfrag(const void *data, char *buff, unsigned int offset, unsigned int len)
+{
+ memcpy(buff, ((char*)data)+offset, len);
+ return 0;
+}
+#endif
+
/*
* Build and send a packet, with as little as one copy
*
@@ -453,7 +549,13 @@
unsigned length,
struct ipcm_cookie *ipc,
struct rtable *rt,
- int flags)
+ int flags
+#ifdef CONFIG_IP_IPSEC
+ ,struct ipsec_sp *policy_ptr
+ ,int ipsec_action
+ ,int protocol
+#endif
+)
{
struct inet_opt *inet = inet_sk(sk);
unsigned int fraglen, maxfraglen, fragheaderlen;
@@ -467,12 +569,63 @@
struct ip_options *opt = ipc->opt;
int df = 0;
+#if CONFIG_IP_IPSEC
+ struct ip_auth_hdr *authhdr = NULL;
+ char* tmpdata = NULL;
+#endif
+
mtu = rt->u.dst.pmtu;
if (ip_dont_fragment(sk, &rt->u.dst))
df = htons(IP_DF);
length -= sizeof(struct iphdr);
+#if CONFIG_IP_IPSEC
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ struct inet_opt *inet = inet_sk(sk);
+ struct iphdr *iph = NULL;
+ int ah_len = ipsec4_out_get_ahsize(policy_ptr);
+ int tot_len = sizeof(struct iphdr) + ah_len + length;
+ if (opt)
+ tot_len += opt->optlen;
+
+ tmpdata = kmalloc(tot_len, GFP_ATOMIC);
+ if (!tmpdata) {
+ return -ENOMEM;
+ }
+
+ iph = (struct iphdr*)tmpdata;
+ iph->version=4;
+ iph->ihl=5 + (opt ? opt->optlen : 0);
+ iph->tos=inet->tos;
+ iph->tot_len = htons(tot_len);
+ iph->frag_off = htons(IP_MF) | df;
+ if (rt->rt_type != RTN_MULTICAST)
+ iph->ttl=inet->ttl;
+ iph->protocol=protocol;
+ iph->saddr=rt->rt_src;
+ iph->daddr=rt->rt_dst;
+ iph->check=0;
+ iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+ iph->ttl=inet->mc_ttl;
+ authhdr = (struct ip_auth_hdr*)(tmpdata + iph->ihl*4);
+ authhdr->nexthdr = protocol;
+ authhdr->hdrlen = (ah_len >> 2) - 2;
+ authhdr->reserved = 0;
+ iph->protocol = IPPROTO_AH;
+
+ err = getfrag(frag, tmpdata+iph->ihl*4+ah_len, 0, length);
+ if (err) {
+ goto error;
+ }
+
+ getfrag = ipsec4_getfrag;
+ frag = authhdr;
+ length += ah_len;
+ protocol = IPPROTO_AH;
+ }
+#endif
+
if (opt) {
fragheaderlen = sizeof(struct iphdr) + opt->optlen;
maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
@@ -601,12 +754,22 @@
* Any further fragments will have MF set.
*/
mf = htons(IP_MF);
+#ifdef CONFIG_IP_IPSEC
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ ((struct iphdr *)tmpdata)->id = id;
+ ipsec4_out_ah_calc((struct iphdr*)tmpdata, authhdr, policy_ptr);
+ }
+#endif
}
if (rt->rt_type == RTN_MULTICAST)
iph->ttl = inet->mc_ttl;
else
iph->ttl = inet->ttl;
+#ifdef CONFIG_IP_IPSEC
+ iph->protocol = protocol;
+#else
iph->protocol = sk->protocol;
+#endif
iph->check = 0;
iph->saddr = rt->rt_src;
iph->daddr = rt->rt_dst;
@@ -639,12 +802,21 @@
}
} while (offset >= 0);
+#if CONFIG_IP_IPSEC
+ if (tmpdata)
+ kfree(tmpdata);
+#endif
+
if (nfrags>1)
ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;
out:
return 0;
error:
+#if CONFIG_IP_IPSEC
+ if (tmpdata)
+ kfree(tmpdata);
+#endif
IP_INC_STATS(IpOutDiscards);
if (nfrags>1)
ip_statistics[smp_processor_id()*2 + !in_softirq()].IpFragCreates += nfrags;
@@ -670,7 +842,83 @@
struct sk_buff *skb;
int df;
struct iphdr *iph;
+ u16 ah_len = 0;
+#if CONFIG_IP_IPSEC
+ u8 protocol = sk->protocol;
+ void *newdata = NULL;
+ char *dummyhdr = NULL;
+ unsigned int newlength = 0;
+ int ipsec_action = 0;
+ struct ipsec_sp *policy_ptr = NULL;
+ struct ip_auth_hdr *authhdr = NULL;
+
+ ipsec_action = ipsec4_output_check(sk, rt, &policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *olddata = NULL;
+ olddata = kmalloc(length, GFP_ATOMIC);
+ if (!olddata) {
+ err = -ENOMEM;
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Could not get memory for ESP (olddata)\n");
+ goto out;
+ }
+
+ err = getfrag(frag, olddata, 0, length);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Could nog get data for ESP (olddata)\n");
+ kfree(olddata);
+ goto out;
+ }
+
+ if(!inet->hdrincl) {
+ ipsec4_out_enc(olddata, length, protocol, &newdata, &newlength, policy_ptr);
+ if (newdata) {
+ frag = newdata;
+ length = newlength;
+ getfrag = ipsec4_getfrag; /* needs this function for Ipv4 */
+ protocol = IPPROTO_ESP;
+ } else {
+ kfree(olddata);
+ goto out;
+ }
+ } else {
+ int hdrlen = ((struct iphdr*)olddata)->ihl * 4;
+ protocol = ((struct iphdr*)olddata)->protocol;
+ ipsec4_out_enc(olddata + hdrlen, length - hdrlen, protocol, &newdata, &newlength, policy_ptr);
+ if (newdata) {
+ char* tmpdata = kmalloc(hdrlen + newlength, GFP_ATOMIC);
+ if (!tmpdata) {
+ kfree(olddata);
+ kfree(newdata);
+ goto out;
+ }
+ memcpy(tmpdata, olddata, hdrlen);
+ memcpy(tmpdata + hdrlen, newdata, newlength);
+ ((struct iphdr*)tmpdata)->protocol = IPPROTO_ESP;
+ ((struct iphdr*)tmpdata)->tot_len = htons(hdrlen + newlength);
+ ip_send_check((struct iphdr*)tmpdata);
+
+ frag = tmpdata;
+ length = hdrlen + newlength;
+ getfrag = ipsec4_getfrag; /* needs this function for Ipv4 */
+ kfree(newdata);
+ newdata = tmpdata;
+ } else {
+ kfree(olddata);
+ goto out;
+ }
+ }
+
+ kfree(olddata);
+ }
+
+ if (ipsec_action & IPSEC_ACTION_AUTH)
+ ah_len = ipsec4_out_get_ahsize(policy_ptr);
+
+#endif
/*
* Try the simple case first. This leaves fragmented frames, and by
* choice RAW frames within 20 bytes of maximum size(rare) to the long path
@@ -683,7 +931,12 @@
* Check for slow path.
*/
if (length > rt->u.dst.pmtu || ipc->opt != NULL)
+#ifdef CONFIG_IP_IPSEC
+ return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags,
+ policy_ptr, ipsec_action, protocol);
+#else
return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags);
+#endif
} else {
if (length > rt->u.dst.dev->mtu) {
ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->dport,
@@ -707,7 +960,7 @@
{
int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
- skb = sock_alloc_send_skb(sk, length+hh_len+15,
+ skb = sock_alloc_send_skb(sk, length+hh_len+15+ah_len,
flags&MSG_DONTWAIT, &err);
if(skb==NULL)
goto error;
@@ -717,6 +970,10 @@
skb->priority = sk->priority;
skb->dst = dst_clone(&rt->u.dst);
+#ifdef CONFIG_IP_IPSEC
+ if (ipsec_action & IPSEC_ACTION_AUTH)
+ dummyhdr = skb_put(skb, ah_len);
+#endif
skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
if (!inet->hdrincl) {
@@ -729,16 +986,43 @@
ip_select_ident(iph, &rt->u.dst, sk);
if (rt->rt_type != RTN_MULTICAST)
iph->ttl = inet->ttl;
+#ifdef CONFIG_IP_IPSEC
+ iph->protocol=protocol;
+#else
iph->protocol=sk->protocol;
+#endif
iph->saddr=rt->rt_src;
iph->daddr=rt->rt_dst;
iph->check=0;
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
}
- else
+ else {
err = getfrag(frag, (void *)iph, 0, length);
+ if (err)
+ goto error_fault;
+#ifdef CONFIG_IP_IPSEC
+ protocol = iph->protocol;
+#endif
+ }
+
+#ifdef CONFIG_IP_IPSEC
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ protocol = iph->protocol;
+ iph->protocol = IPPROTO_AH;
+ memcpy(dummyhdr, iph, iph->ihl * 4);
+ authhdr = (struct ip_auth_hdr*)(((char*)dummyhdr) + iph->ihl * 4);
+ skb->nh.iph = iph = (struct iphdr*)dummyhdr;
+ authhdr->nexthdr = protocol;
+ authhdr->hdrlen = (ah_len >> 2) - 2;
+ authhdr->reserved = 0;
+ iph->tot_len = htons(ntohs(iph->tot_len) + ah_len);
+ ipsec4_out_ah_calc(skb->nh.iph, authhdr, policy_ptr);
+ ip_send_check(iph);
+ }
+#endif
+
if (err)
goto error_fault;
@@ -749,6 +1033,10 @@
if (err)
goto error;
out:
+#ifdef CONFIG_IP_IPSEC
+ ipsec4_out_finish(policy_ptr);
+ if (newdata) kfree(newdata);
+#endif
return 0;
error_fault:
diff -uNr -x CVS linux-2.5.43/net/ipv4/ipsec4_input.c linux25.43-ipsec/net/ipv4/ipsec4_input.c
--- linux-2.5.43/net/ipv4/ipsec4_input.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/ipsec4_input.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,622 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysctl.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/smp.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+#include <net/addrconf.h>
+#include <net/snmp.h>
+#include <linux/in.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h> /* sa proto type */
+#include <linux/pfkey.h>
+
+/* XXX must insert window size sa_replay_window member */
+static int check_replay_window(struct sa_replay_window *rw, __u32 hdrseq)
+{
+ __u32 diff;
+ __u32 seq = ntohl(hdrseq);
+
+ if (!sysctl_ipsec_replay_window) {
+ IPSEC4_DEBUG("disable replay window check, skip!\n");
+ return 1;
+ }
+
+ IPSEC4_DEBUG("overflow: %u\n"
+ " size: %u\n"
+ " seq_num: %x\n"
+ "last_seq: %x\n"
+ " bitmap: %08x\n"
+ " curr seq: %x\n",
+ rw->overflow, rw->size, rw->seq_num, rw->last_seq, rw->bitmap, seq);
+ if (seq == 0) {
+ return 0; /* first == 0 or wrapped */
+ }
+
+ if (seq > rw->last_seq) return 1; /* larger is good */
+
+ diff = rw->last_seq - seq;
+
+ if (diff >= rw->size) {
+ return 0; /* too old or wrapped */
+ }
+
+ if ( rw->bitmap & ((u_long)1 << diff) ) {
+ return 0; /* already seen */
+ }
+
+ return 1; /* out of order but good */
+}
+
+static void update_replay_window(struct sa_replay_window *rw, __u32 hdrseq)
+{
+ __u32 diff;
+ __u32 seq = ntohl(hdrseq);
+
+ if (!sysctl_ipsec_replay_window) {
+ IPSEC4_DEBUG("disable replay window check, skip!\n");
+ return;
+ }
+
+ if (seq == 0) return;
+
+ if (seq > rw->last_seq) { /* new larger sequence number */
+ diff = seq - rw->last_seq;
+ if (diff < rw->size) { /* In window */
+ rw->bitmap <<= diff;
+ rw->bitmap |= 1; /* set bit for this packet */
+ } else {
+ rw->bitmap = 1; /* This packet has a "way larger" */
+ }
+
+ rw->last_seq = seq;
+ }
+
+ diff = rw->last_seq - seq;
+ rw->bitmap |= ((u_long)1 << diff); /* mark as seen */
+}
+
+static int ipsec4_input_check_ah(struct sk_buff **skb, struct ip_auth_hdr *authhdr, struct sa_index *sa_idx, u8 *nexthdr)
+{
+ int rtn = 0;
+ __u8* authdata;
+ size_t authsize;
+ char *packet;
+ int offset;
+ struct ip_auth_hdr *pseudo_authhdr = NULL;
+
+ IPSEC4_DEBUG("start auth header processing\n");
+
+ if (!((*skb)&&authhdr)) {
+ IPSEC4_DEBUG("parameters is invalid\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+
+ /* Check SPI */
+ IPSEC4_DEBUG("authhdr->spi is 0x%x\n", ntohl(authhdr->spi));
+
+ sa_index_init(sa_idx);
+ ((struct sockaddr_in *)&sa_idx->dst)->sin_addr.s_addr = (*skb)->nh.iph->daddr;
+ ((struct sockaddr_in *)&sa_idx->dst)->sin_family = AF_INET;
+ sa_idx->prefixlen_d = 32;
+ sa_idx->ipsec_proto = SADB_SATYPE_AH;
+ sa_idx->spi = authhdr->spi;
+
+ sa_idx->sa = sadb_find_by_sa_index(sa_idx);
+
+ if (!sa_idx->sa) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: SA(%d.%d.%d.%d AH %d): Can't find SA\n",
+ NIPQUAD(((struct sockaddr_in *)&sa_idx->dst)->sin_addr),
+ ntohs(sa_idx->spi));
+ }
+ rtn = -EINVAL;
+ goto finish;
+ }
+
+ write_lock_bh(&sa_idx->sa->lock);
+
+ if (sa_idx->sa->auth_algo.algo == SADB_AALG_NONE ) {
+ if (net_ratelimit())
+ IPSEC4_DEBUG("not found auth algo.\n");
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ if (!check_replay_window(&sa_idx->sa->replay_window, authhdr->seq_no)) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: SA(%d.%d.%d.%d AH %d): Replay check error\n",
+ NIPQUAD(((struct sockaddr_in *)&sa_idx->dst)->sin_addr),
+ ntohs(sa_idx->spi));
+ }
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ authsize = ntohs((*skb)->nh.iph->tot_len);
+
+ if (authsize > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.iph)) {
+ if (net_ratelimit())
+ IPSEC4_DEBUG("the packet length is wrong\n");
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ packet = kmalloc(((authsize + 3) & ~3) +
+ sa_idx->sa->auth_algo.dx->di->blocksize, GFP_ATOMIC);
+ if (!packet) {
+ if (net_ratelimit())
+ IPSEC4_DEBUG("can't get memory for pakcet\n");
+ rtn = -ENOMEM;
+ goto unlock_finish;
+ }
+ authdata = packet + ((authsize + 3) & ~3);
+
+ offset = (char *)((*skb)->nh.iph) - (char *)((*skb)->data);
+
+ if (skb_copy_bits(*skb, offset, packet, authsize)) {
+ if (net_ratelimit())
+ IPSEC4_DEBUG("packet copy failed\n");
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ ((struct iphdr*)packet)->tos = 0;
+ ((struct iphdr*)packet)->frag_off = 0;
+ ((struct iphdr*)packet)->ttl = 0;
+ ((struct iphdr*)packet)->check = 0;
+
+ pseudo_authhdr = (struct ip_auth_hdr*)(packet + (((char*)authhdr) - ((char*)(*skb)->nh.iph)));
+ memset(&pseudo_authhdr->auth_data[0], 0, ((authhdr->hdrlen - 1) << 2));
+
+ sa_idx->sa->auth_algo.dx->di->hmac_atomic(sa_idx->sa->auth_algo.dx,
+ sa_idx->sa->auth_algo.key,
+ sa_idx->sa->auth_algo.key_len,
+ packet, authsize, authdata);
+
+ /* Originally, IABG uses "for" loop for matching authentication data. */
+ /* I change it into memcmp routine. */
+ if (memcmp(authdata, authhdr->auth_data, sa_idx->sa->auth_algo.digest_len)) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: SA(%d.%d.%d.%d AH %d): Invalid checksum\n",
+ NIPQUAD(((struct sockaddr_in *)&sa_idx->dst)->sin_addr),
+ ntohs(sa_idx->spi));
+ }
+ kfree(packet);
+ rtn = IPSEC_ACTION_DROP;
+ goto unlock_finish;
+ }
+ kfree(packet);
+
+ rtn = IPSEC_ACTION_AUTH;
+ *nexthdr = authhdr->nexthdr;
+ update_replay_window(&sa_idx->sa->replay_window, authhdr->seq_no);
+
+ (*skb)->security |= RCV_AUTH; /* ? we must rewrite linux/ipsec.h */
+ {
+ int authhdrlen = (authhdr->hdrlen + 2) << 2;
+ int payloadlen = (*skb)->len - (*skb)->data_len - authhdrlen;
+ IPSEC4_DEBUG("authhdrlen=%d, payloadlen=%d\n", authhdrlen, payloadlen);
+ if (payloadlen > 0) {
+ memmove(authhdr, ((char*)authhdr) + authhdrlen, payloadlen);
+ (*skb)->len -= authhdrlen;
+ (*skb)->nh.iph->protocol = *nexthdr;
+ (*skb)->nh.iph->tot_len = htons(ntohs((*skb)->nh.iph->tot_len) - authhdrlen);
+ (*skb)->nh.iph->check = ip_fast_csum((unsigned char*)((*skb)->nh.iph), (*skb)->nh.iph->ihl);
+ } else {
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+ }
+
+
+ if (!sa_idx->sa->fuse_time) {
+ sa_idx->sa->fuse_time = jiffies;
+ sa_idx->sa->lifetime_c.usetime = (sa_idx->sa->fuse_time) / HZ;
+ ipsec_sa_mod_timer(sa_idx->sa);
+ IPSEC4_DEBUG("set fuse_time = %lu\n", sa_idx->sa->fuse_time);
+ }
+ sa_idx->sa->lifetime_c.bytes += (*skb)->tail - (*skb)->head;
+ IPSEC4_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa_idx->sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx->sa->lifetime_c.bytes));
+ if (sa_idx->sa->lifetime_c.bytes >= sa_idx->sa->lifetime_s.bytes && sa_idx->sa->lifetime_s.bytes) {
+ sa_idx->sa->state = SADB_SASTATE_DYING;
+ IPSEC4_DEBUG("change sa state DYING\n");
+ }
+ if (sa_idx->sa->lifetime_c.bytes >= sa_idx->sa->lifetime_h.bytes && sa_idx->sa->lifetime_h.bytes) {
+ sa_idx->sa->state = SADB_SASTATE_DEAD;
+ IPSEC4_DEBUG("change sa state DEAD\n");
+ }
+
+unlock_finish:
+ write_unlock_bh(&sa_idx->sa->lock); /* unlock SA */
+ ipsec_sa_put(sa_idx->sa);
+finish:
+ return rtn;
+}
+
+static int ipsec4_input_check_esp(struct sk_buff **skb, struct ip_esp_hdr* esphdr, struct sa_index *sa_idx, u8 *nexthdr)
+{
+ int len = 0;
+ int rtn = IPSEC_ACTION_DROP;
+ u8 *authdata = NULL;
+ u8 *srcdata = NULL;
+ int srcsize = 0, totalsize = 0, hashsize = 0, encsize = 0;
+
+ IPSEC4_DEBUG("start esp processing\n");
+ if (!(*skb&&esphdr)) {
+ printk(KERN_ERR "ipsec4_input_check_esp: parameters are invalid\n");
+ goto finish;
+ }
+
+ if (skb_is_nonlinear(*skb)) {
+ u16 offset = ((char*)esphdr) - (char*)((*skb)->nh.raw);
+ if (!skb_linearize(*skb, GFP_ATOMIC)) {
+ esphdr = (struct ip_esp_hdr*)((*skb)->nh.raw + offset);
+ } else {
+ printk(KERN_ERR "ipsec4_input_check_esp: counld not linearize skb\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+ }
+
+ /* Check SPI */
+ IPSEC4_DEBUG("esphdr->spi is 0x%x\n", ntohl(esphdr->spi));
+
+ sa_index_init(sa_idx);
+ ((struct sockaddr_in *)&sa_idx->dst)->sin_addr.s_addr = (*skb)->nh.iph->daddr;
+ ((struct sockaddr_in *)&sa_idx->dst)->sin_family = AF_INET;
+ sa_idx->prefixlen_d = 32;
+ sa_idx->ipsec_proto = SADB_SATYPE_ESP;
+ sa_idx->spi = esphdr->spi;
+
+ sa_idx->sa = sadb_find_by_sa_index(sa_idx);
+
+ if (!sa_idx->sa) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: (%d.%d.%d.%d ESP %d) Can't find SA\n",
+ NIPQUAD(((struct sockaddr_in *)&sa_idx->dst)->sin_addr),
+ sa_idx->spi);
+ }
+ rtn = -EINVAL;
+ goto finish;
+ }
+
+ write_lock_bh(&sa_idx->sa->lock);
+
+ if ( sa_idx->sa->esp_algo.algo == SADB_EALG_NONE ) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec4_input_check_esp: not found encryption algorithm in SA!\n");
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ len = ntohs((*skb)->nh.iph->tot_len);
+
+ if (len > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.iph)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec4_input_check_esp: received packet length is wrong\n");
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ totalsize = len - (((char*)esphdr) - ((char*)(*skb)->nh.iph));
+
+ if (!(sa_idx->sa->esp_algo.cx->ci)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec4_input_check_esp: not found esp algo\n");
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ if ( !check_replay_window(&sa_idx->sa->replay_window, esphdr->seq_no) ) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: SA(%d.%d.%d.%d ESP %d): Replay check error\n",
+ NIPQUAD(((struct sockaddr_in *)&sa_idx->dst)->sin_addr),
+ sa_idx->spi);
+ }
+ kfree(srcdata);
+ rtn = -EINVAL;
+ goto unlock_finish;
+ }
+
+ encsize = totalsize - sa_idx->sa->esp_algo.cx->ci->ivsize - 8;
+ /* 8 = SPI + Sequence Number */
+
+ if ( sa_idx->sa->auth_algo.algo != SADB_AALG_NONE ) {
+ /* Calculate size */
+ /* The tail of payload does not have to be aligned */
+ /* with a multiple number of 64 bit. */
+ /* 64 bit alignment is adapted to the position of top of header.*/
+ hashsize = sa_idx->sa->auth_algo.digest_len;
+ encsize -= hashsize;
+ authdata=kmalloc(sa_idx->sa->auth_algo.dx->di->blocksize, GFP_ATOMIC);
+ sa_idx->sa->auth_algo.dx->di->hmac_atomic(sa_idx->sa->auth_algo.dx,
+ sa_idx->sa->auth_algo.key,
+ sa_idx->sa->auth_algo.key_len,
+ (char*)esphdr, totalsize - hashsize, authdata);
+ /* Originally, IABG uses "for" loop for matching authentication data. */
+ /* I change it into memcmp routine. */
+
+ if (memcmp(authdata, &((char*)esphdr)[totalsize - hashsize],
+ sa_idx->sa->auth_algo.digest_len )) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: SA(%d.%d.%d.%d ESP %d): Invalid checksum\n",
+ NIPQUAD(((struct sockaddr_in*)&sa_idx->dst)->sin_addr), sa_idx->spi);
+ }
+ kfree(authdata);
+ rtn = IPSEC_ACTION_DROP;
+ goto unlock_finish;
+ }
+ kfree(authdata);
+ authdata = NULL;
+ }
+
+ /* Decrypt data */
+ srcdata = kmalloc(encsize, GFP_ATOMIC);
+ if (!srcdata) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec4_input_check_esp: can't allocate memory for decrypt\n");
+ rtn = -ENOMEM;
+ goto unlock_finish;
+ }
+
+ IPSEC4_DEBUG("len=%d, totalsize=%d, encsize=%d\n",
+ len, totalsize, encsize);
+
+ if (!(sa_idx->sa->esp_algo.iv)) { /* first packet */
+ sa_idx->sa->esp_algo.iv = kmalloc(sa_idx->sa->esp_algo.cx->ci->ivsize, GFP_ATOMIC);
+ }
+
+ memcpy(sa_idx->sa->esp_algo.iv, esphdr->enc_data, sa_idx->sa->esp_algo.cx->ci->ivsize);
+ sa_idx->sa->esp_algo.cx->ci->decrypt_atomic_iv(sa_idx->sa->esp_algo.cx,
+ ((u8 *)(esphdr->enc_data)) + sa_idx->sa->esp_algo.cx->ci->ivsize,
+ srcdata, encsize, sa_idx->sa->esp_algo.iv);
+
+ /* encsize - (pad_len + next_hdr) - pad_len */
+ srcsize = encsize - 2 - srcdata[encsize-2];
+ IPSEC4_DEBUG("Original data is srcsize=%d, padlength=%d\n", srcsize, srcdata[encsize-2]);
+ if (srcsize <= 0) {
+ if (net_ratelimit()) {
+ printk(KERN_ERR "IPSEC: SA(%d.%d.%d.%d ESP %d):Decrypted packet contains garbage\n",
+ NIPQUAD(((struct sockaddr_in *)&sa_idx->dst)->sin_addr), sa_idx->spi);
+ }
+ kfree(srcdata);
+ rtn = IPSEC_ACTION_DROP;
+ goto unlock_finish;
+ }
+
+ update_replay_window(&sa_idx->sa->replay_window, esphdr->seq_no);
+
+ memcpy(esphdr, srcdata, srcsize);
+
+ (*skb)->nh.iph->protocol = *nexthdr = srcdata[encsize-1];
+ (*skb)->nh.iph->tot_len = htons(ntohs((*skb)->nh.iph->tot_len) - 10 - srcdata[encsize]);
+ /* 10 = sizeof(struct ip_esp_hdr) - 8 + 2 */
+ (*skb)->nh.iph->check = ip_fast_csum((unsigned char*)((*skb)->nh.iph), (*skb)->nh.iph->ihl);
+
+ skb_trim(*skb, (*skb)->len + srcsize - totalsize);
+ (*skb)->nh.iph->tot_len = htons(((char *)esphdr - (char *)((*skb)->nh.iph)) + srcsize);
+
+ kfree(srcdata);
+ srcdata = NULL;
+
+ rtn = IPSEC_ACTION_ESP;
+
+ /* Otherwise checksum of fragmented udp packets fails (udp.c, csum_fold) */
+ (*skb)->ip_summed = CHECKSUM_UNNECESSARY;
+ (*skb)->security |= RCV_CRYPT;
+
+ if (!sa_idx->sa->fuse_time) {
+ sa_idx->sa->fuse_time = jiffies;
+ sa_idx->sa->lifetime_c.usetime = (sa_idx->sa->fuse_time) / HZ;
+ ipsec_sa_mod_timer(sa_idx->sa);
+ IPSEC4_DEBUG("set fuse_time = %lu\n", (sa_idx->sa->fuse_time));
+ }
+ sa_idx->sa->lifetime_c.bytes += totalsize;
+ IPSEC4_DEBUG("sa->bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa_idx->sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx->sa->lifetime_c.bytes));
+ if (sa_idx->sa->lifetime_c.bytes >= sa_idx->sa->lifetime_s.bytes && sa_idx->sa->lifetime_s.bytes) {
+ sa_idx->sa->state = SADB_SASTATE_DYING;
+ IPSEC4_DEBUG("change sa state DYING\n");
+ }
+ if (sa_idx->sa->lifetime_c.bytes >= sa_idx->sa->lifetime_h.bytes && sa_idx->sa->lifetime_h.bytes) {
+ sa_idx->sa->state = SADB_SASTATE_DEAD;
+ IPSEC4_DEBUG("change sa state DEAD\n");
+ }
+
+
+unlock_finish:
+ write_unlock_bh(&sa_idx->sa->lock); /* unlock SA */
+ ipsec_sa_put(sa_idx->sa);
+
+finish:
+ return rtn;
+}
+
+int ipsec4_input_check(struct sk_buff **skb)
+{
+ int rtn = 0;
+ int result = IPSEC_ACTION_BYPASS;
+ u8 nexthdr = (*skb)->nh.iph->protocol;
+ struct sa_index auth_sa_idx;
+ struct sa_index esp_sa_idx;
+ struct selector selector;
+ struct ipsec_sp *policy = NULL;
+ struct iphdr *hdr = (*skb)->nh.iph;
+
+ IPSEC4_DEBUG("called\n");
+#ifdef CONFIG_IPSEC_DEBUG
+ IPSEC4_DEBUG("src addr: %d.%d.%d.%d\n", NIPQUAD(hdr->saddr));
+ IPSEC4_DEBUG("dst addr: %d.%d.%d.%d\n", NIPQUAD(hdr->daddr));
+ IPSEC4_DEBUG("hdr->tot_len is %d\n", ntohs(hdr->tot_len));
+#endif /* CONFIG_IPSEC_DEBUG */
+
+ if ( hdr->protocol == IPPROTO_UDP ) { /* IKE */
+ if (pskb_may_pull(*skb, (*skb)->h.raw - (*skb)->data + sizeof(struct udphdr))) {
+ if ((*skb)->h.uh->source == __constant_htons(500)
+ && (*skb)->h.uh->dest == __constant_htons(500)) {
+ IPSEC4_DEBUG("received IKE packet. skip!\n");
+ goto finish;
+ }
+ }
+ }
+
+
+ if (nexthdr == IPPROTO_AH) {
+ result |= ipsec4_input_check_ah(skb, (struct ip_auth_hdr*)((*skb)->h.raw), &auth_sa_idx, &nexthdr);
+ }
+
+ if (nexthdr == IPPROTO_ESP) {
+ result |= ipsec4_input_check_esp(skb, (struct ip_esp_hdr*)((*skb)->h.raw), &esp_sa_idx, &nexthdr);
+ }
+
+ if (result&IPSEC_ACTION_DROP) {
+ IPSEC4_DEBUG("result is drop.\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+
+ /* copy selector XXX port */
+ memset(&selector, 0, sizeof(struct selector));
+
+#if 0 /* ipsec4 tunnel mode: not yet */
+ if (nexthdr == IPPROTO_IPIP)
+ selector.mode = IPSEC_MODE_TUNNEL;
+#endif
+ selector.proto = nexthdr;
+
+ IPSEC4_DEBUG("nexthdr = %u\n", nexthdr);
+
+ ((struct sockaddr_in *)&selector.src)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&selector.src)->sin_addr.s_addr = (*skb)->nh.iph->saddr;
+ ((struct sockaddr_in *)&selector.dst)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&selector.dst)->sin_addr.s_addr = (*skb)->nh.iph->daddr;
+ selector.prefixlen_d = 32;
+ selector.prefixlen_s = 32;
+
+ /* beggining of matching check selector and policy */
+ IPSEC4_DEBUG("start match check SA and policy.\n");
+
+#ifdef CONFIG_IPSEC_DEBUG
+ IPSEC4_DEBUG("selector dst addr: %d.%d.%d.%d\n",
+ NIPQUAD(((struct sockaddr_in *)&selector.dst)->sin_addr));
+ IPSEC4_DEBUG("selector src addr: %d.%d.%d.%d\n",
+ NIPQUAD(((struct sockaddr_in *)&selector.src)->sin_addr));
+#endif /* CONFIG_IPSEC_DEBUG */
+ policy = spd_get(&selector);
+
+ if (policy) {
+
+ read_lock_bh(&policy->lock);
+
+ /* non-ipsec packet processing: If this packet doesn't
+ * have any IPSEC headers, then consult policy to see
+ * what to do with packet. If policy says to apply IPSEC,
+ * and there is an SA, then pass packet to netxt layer,
+ * if ther isn't an SA, then drop the packet.
+ */
+ if (policy->policy_action == IPSEC_POLICY_DROP) {
+ rtn = -EINVAL;
+ read_unlock_bh(&policy->lock);
+ IPSEC4_DEBUG("a policy is drop, drop packet!\n");
+ goto finish;
+ }
+
+ if (policy->policy_action == IPSEC_POLICY_BYPASS) {
+ rtn = 0;
+ read_unlock_bh(&policy->lock);
+ IPSEC4_DEBUG("a policy is bypass, through!\n");
+ goto finish;
+ }
+
+ if (policy->policy_action == IPSEC_POLICY_APPLY) {
+ if (result&IPSEC_ACTION_AUTH) {
+ if (policy->auth_sa_idx) {
+ if (sa_index_compare(&auth_sa_idx, policy->auth_sa_idx)) {
+ rtn = -EINVAL;
+ }
+ } else {
+ rtn = -EINVAL;
+ }
+ } else {
+ if (policy->auth_sa_idx) {
+ rtn = -EINVAL;
+ }
+ }
+
+ if (result&IPSEC_ACTION_ESP) {
+ if (policy->esp_sa_idx) {
+ if (sa_index_compare(&esp_sa_idx, policy->esp_sa_idx)) {
+ rtn = -EINVAL;
+ }
+ } else {
+ rtn = -EINVAL;
+ }
+ } else {
+ if (policy->esp_sa_idx) {
+ rtn = -EINVAL;
+ }
+ }
+ IPSEC4_DEBUG("matching piar of SA and policy, through=%d\n", rtn);
+ }
+
+ read_unlock_bh(&policy->lock);
+ ipsec_sp_put(policy);
+ } else {
+ if (!result) {
+ rtn = 0;
+ } else {
+ IPSEC4_DEBUG("matching pair of SA and policy not found, through!\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+ }
+
+ IPSEC4_DEBUG("end match check SA and policy.\n");
+ /* end of matching check selector and policy */
+
+
+finish:
+
+ return rtn;
+}
+
diff -uNr -x CVS linux-2.5.43/net/ipv4/ipsec4_output.c linux25.43-ipsec/net/ipv4/ipsec4_output.c
--- linux-2.5.43/net/ipv4/ipsec4_output.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/ipsec4_output.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,673 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ *
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysctl.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/smp.h>
+#include <linux/list.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+#include <net/ipv6.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+#include <net/addrconf.h>
+#include <net/snmp.h>
+#include <linux/in.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h> /* sa proto type */
+#include <linux/pfkey.h>
+
+/*
+ We assume iph and authder is on continuous buffer.
+*/
+int ipsec4_out_ah_calc(struct iphdr *iph, struct ip_auth_hdr *authhdr, struct ipsec_sp *policy)
+{
+ struct ipsec_sa *sa = NULL;
+ struct ip_auth_hdr *pseudo_authhdr = NULL;
+ char* pseudo_packet = NULL;
+ int packetlen = 0;
+ __u8* authdata = NULL;
+
+ IPSEC4_DEBUG("called.\n");
+ if(!policy){
+ return -EINVAL;
+ }
+
+ if (!iph) {
+ IPSEC4_DEBUG("iph is NULL!\n");
+ return -EINVAL;
+ }
+
+ if (!authhdr) {
+ authhdr = (struct ip_auth_hdr*)(((char*)iph) + iph->ihl*4);
+ }
+
+ read_lock_bh(&policy->lock);
+
+ if (!policy->auth_sa_idx || !policy->auth_sa_idx->sa) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(ah) SA missing.\n", __FUNCTION__);
+ read_unlock_bh(&policy->lock);
+ return -EINVAL;
+ }
+
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ sa = policy->auth_sa_idx->sa;
+
+ read_unlock_bh(&policy->lock);
+
+ packetlen = ntohs(iph->tot_len);
+
+ pseudo_packet = kmalloc(packetlen,GFP_ATOMIC);
+ if (!pseudo_packet) {
+ ipsec_sa_put(sa);
+ return -ENOMEM;
+ }
+
+ pseudo_authhdr = (struct ip_auth_hdr*)(pseudo_packet + (((char*)authhdr) - ((char*)iph)));
+
+ authdata=kmalloc((sa->auth_algo.dx)->di->blocksize, GFP_ATOMIC);
+ if (!authdata) {
+ kfree(pseudo_packet);
+ ipsec_sa_put(sa);
+ return -ENOMEM;
+ }
+
+ write_lock_bh(&sa->lock);
+
+ /* authhdr->spi = htonl(sa->spi); */
+ IPSEC4_DEBUG("spi is 0x%x\n", ntohl(sa->spi));
+ authhdr->spi = sa->spi; /* -mk */
+ authhdr->seq_no = htonl(++sa->replay_window.seq_num);
+
+ memcpy(pseudo_packet,iph,packetlen);
+ memset(&pseudo_authhdr->auth_data[0], 0, (authhdr->hdrlen -1) << 2);
+
+ pseudo_authhdr->spi = authhdr->spi;
+ pseudo_authhdr->seq_no = authhdr->seq_no;
+
+ ((struct iphdr*)pseudo_packet)->tos = 0;
+ ((struct iphdr*)pseudo_packet)->frag_off = 0;
+ ((struct iphdr*)pseudo_packet)->ttl = 0;
+ ((struct iphdr*)pseudo_packet)->check = 0;
+
+ sa->auth_algo.dx->di->hmac_atomic(sa->auth_algo.dx,
+ sa->auth_algo.key,
+ sa->auth_algo.key_len,
+ pseudo_packet, packetlen, authdata);
+
+ memcpy(authhdr->auth_data, authdata, sa->auth_algo.digest_len);
+
+ if (!sa->fuse_time) {
+ sa->fuse_time = jiffies;
+ sa->lifetime_c.usetime = (sa->fuse_time)/HZ;
+ ipsec_sa_mod_timer(sa);
+ IPSEC4_DEBUG("set fuse_time = %lu\n", sa->fuse_time);
+ }
+
+ sa->lifetime_c.bytes += packetlen;
+ IPSEC4_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa->lifetime_c.bytes) >> 32), (__u32)(sa->lifetime_c.bytes));
+
+ if (sa->lifetime_c.bytes >= sa->lifetime_s.bytes && sa->lifetime_s.bytes) {
+ IPSEC4_DEBUG("change sa state DYING\n");
+ sa->state = SADB_SASTATE_DYING;
+ }
+
+ if (sa->lifetime_c.bytes >= sa->lifetime_h.bytes && sa->lifetime_h.bytes) {
+ sa->state = SADB_SASTATE_DEAD;
+ IPSEC4_DEBUG("change sa state DEAD\n");
+ }
+
+ write_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+
+ kfree(authdata);
+ kfree(pseudo_packet);
+ return 0;
+}
+
+int ipsec4_out_get_ahsize(struct ipsec_sp *policy)
+{
+ int result = 0;
+ struct ipsec_sa *sa_ah = NULL;
+
+ IPSEC4_DEBUG("called.\n");
+
+ if (!policy) return 0;
+
+ write_lock_bh(&policy->lock);
+ if (policy->auth_sa_idx && policy->auth_sa_idx->sa) {
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ sa_ah = policy->auth_sa_idx->sa;
+ }
+
+ write_unlock_bh(&policy->lock);
+
+ if (sa_ah) {
+ read_lock_bh(&sa_ah->lock);
+ if ( sa_ah->auth_algo.algo != SADB_AALG_NONE) {
+ result += (offsetof(struct ip_auth_hdr, auth_data) +
+ sa_ah->auth_algo.digest_len + 7) & ~7; /* 64 bit alignment */
+ }
+ read_unlock_bh(&sa_ah->lock);
+ ipsec_sa_put(sa_ah);
+ }
+
+ IPSEC4_DEBUG("Calculated size is %d.\n", result);
+ return result;
+}
+
+int ipsec4_out_get_espsize(struct ipsec_sp *policy)
+{
+ int result = 0;
+ struct ipsec_sa *sa_esp = NULL;
+
+ IPSEC4_DEBUG("called.\n");
+
+ if (!policy) return 0;
+
+ write_lock_bh(&policy->lock);
+
+ if (policy->esp_sa_idx && policy->esp_sa_idx->sa) {
+ ipsec_sa_hold(policy->esp_sa_idx->sa);
+ sa_esp = policy->esp_sa_idx->sa;
+ }
+ write_unlock_bh(&policy->lock);
+
+ if (sa_esp) {
+ read_lock_bh(&sa_esp->lock);
+ if ( sa_esp->esp_algo.algo != SADB_EALG_NONE){
+ result += sizeof(struct ip_esp_hdr) - 8;
+ result += sa_esp->esp_algo.cx->ci->ivsize;
+ result += (sa_esp->esp_algo.cx->ci->blocksize + 3) & ~3;
+ result += 4; /* included pad_len and next_hdr 32 bit align */
+ }else{
+ read_unlock_bh(&sa_esp->lock);
+ ipsec_sa_put(sa_esp);
+ return 0;
+ }
+ if ( sa_esp->auth_algo.algo != SADB_AALG_NONE) {
+ result += (sa_esp->auth_algo.digest_len + 3) & ~3; /* 32 bit alignment */
+ }
+ read_unlock_bh(&sa_esp->lock);
+ ipsec_sa_put(sa_esp);
+ }
+ IPSEC4_DEBUG("Calculated size is %d.\n", result);
+ return result;
+}
+
+/*** ESP ***/
+
+void ipsec4_out_enc(const void *data, unsigned length, u8 proto,
+ void **newdata, unsigned *newlength, struct ipsec_sp *policy)
+{
+ struct ipsec_sa *sa = NULL;
+ struct ip_esp_hdr *esphdr = NULL;
+ u8* srcdata = NULL;
+ u8* authdata = NULL;
+ int encblocksize = 0;
+ int encsize = 0, hashsize = 0, totalsize = 0;
+ int i;
+
+ IPSEC4_DEBUG("called.\nData ptr is %p, data length is %d.\n",data,length);
+
+ if (!policy) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s; ipsec policy is NULL\n", __FUNCTION__);
+ return;
+ }
+
+ read_lock_bh(&policy->lock);
+ if (!policy->esp_sa_idx || !policy->esp_sa_idx->sa) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(esp) SA missing.\n", __FUNCTION__);
+ read_unlock_bh(&policy->lock);
+ return;
+ }
+ ipsec_sa_hold(policy->esp_sa_idx->sa);
+ sa = policy->esp_sa_idx->sa;
+ read_unlock_bh(&policy->lock);
+
+ write_lock_bh(&sa->lock);
+ /* Get algorithms */
+ if (sa->esp_algo.algo == SADB_EALG_NONE) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(esp) encryption algorithm not present.\n", __FUNCTION__);
+ goto unlock_finish;
+ return;
+ }
+
+
+ if (!(sa->esp_algo.cx->ci)){
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(esp) cipher_implementation not present.\n", __FUNCTION__);
+ goto unlock_finish;
+ return;
+ }
+
+ /* Calculate size */
+
+
+ encblocksize = (sa->esp_algo.cx->ci->blocksize + 3) & ~3;
+ encsize = length + 2 + (encblocksize - 1);
+ encsize -= encsize % encblocksize;
+
+ /* The tail of payload does not have to be aligned with a multiple number of 64 bit. */
+ /* 64 bit alignment is adapted to the position of top of header. */
+
+ if (sa->auth_algo.algo != SADB_AALG_NONE)
+ hashsize = sa->auth_algo.digest_len;
+
+
+ totalsize = sizeof(struct ip_esp_hdr) - 8 + sa->esp_algo.cx->ci->ivsize + encsize + hashsize;
+ IPSEC4_DEBUG("IV size=%d, enc size=%d hash size=%d, total size=%d\n",
+ sa->esp_algo.cx->ci->ivsize, encsize, hashsize, totalsize);
+
+ /* Get memory */
+ esphdr = kmalloc(totalsize, GFP_ATOMIC);
+ srcdata = kmalloc(encsize, GFP_ATOMIC);
+ if (!esphdr || !srcdata) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ipsec6_enc: Out of memory.\n");
+ if (esphdr) kfree(esphdr);
+ if (srcdata) kfree(srcdata);
+ goto unlock_finish;
+ return;
+ }
+
+ memset(esphdr, 0, totalsize);
+ memset(srcdata, 0, encsize);
+ /* Handle sequence number and fill in header fields */
+ esphdr->spi = sa->spi;
+ esphdr->seq_no = htonl(++sa->replay_window.seq_num);
+
+ /* Get source data, fill in padding and trailing fields */
+
+ memcpy(srcdata, data, length);
+ for (i = length; i < encsize-2; i++)
+ srcdata[i] = (u8)(i-length+1);
+ srcdata[encsize-2] = (encsize-2)-length;
+ IPSEC4_DEBUG("length=%d, encsize=%d\n", length, encsize);
+ IPSEC4_DEBUG("encsize-2=%d\n", srcdata[encsize-2]);
+
+ srcdata[encsize-1] = proto;
+
+ /* Do encryption */
+
+ if (!(sa->esp_algo.iv)) { /* first packet */
+ sa->esp_algo.iv = kmalloc(sa->esp_algo.cx->ci->ivsize, GFP_ATOMIC); /* kfree at SA removed */
+ get_random_bytes(sa->esp_algo.iv, sa->esp_algo.cx->ci->ivsize);
+ IPSEC4_DEBUG("IV initilized.\n");
+ } /* else, had inserted a stored iv (last packet block) */
+
+#ifdef CONFIG_IPSEC_DEBUG
+ {
+ int i;
+ IPSEC4_DEBUG("IV is 0x");
+ if (sysctl_ipsec_debug_ipv6) {
+ for (i=0; i < sa->esp_algo.cx->ci->ivsize ; i++) {
+ printk(KERN_DEBUG "%x", (u8)(sa->esp_algo.iv[i]));
+ }
+ }
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+ sa->esp_algo.cx->ci->encrypt_atomic_iv(sa->esp_algo.cx, srcdata,
+ (u8 *)&esphdr->enc_data + sa->esp_algo.cx->ci->ivsize, encsize, sa->esp_algo.iv);
+ memcpy(esphdr->enc_data, sa->esp_algo.iv, sa->esp_algo.cx->ci->ivsize);
+ kfree(srcdata);
+ srcdata=NULL;
+ /* copy last block for next IV (src: enc_data + ivsize + encsize - ivsize) */
+ memcpy(sa->esp_algo.iv, esphdr->enc_data + encsize, sa->esp_algo.cx->ci->ivsize);
+ /* if CONFIG_IPSEC_DEBUG isn't defined here is finish of encryption process */
+
+ if(sa->auth_algo.algo){
+ authdata = kmalloc(sa->auth_algo.dx->di->blocksize, GFP_ATOMIC);
+ if (!authdata) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ipsec6_enc: Out of memory.\n");
+ kfree(esphdr);
+ goto unlock_finish;
+ return;
+ }
+ memset(authdata, 0, sa->auth_algo.dx->di->blocksize);
+ sa->auth_algo.dx->di->hmac_atomic(sa->auth_algo.dx,
+ sa->auth_algo.key,
+ sa->auth_algo.key_len,
+ (char*)esphdr, totalsize-hashsize, authdata);
+ memcpy(&((char*)esphdr)[8 + sa->esp_algo.cx->ci->ivsize + encsize],
+ authdata, sa->auth_algo.digest_len);
+
+ kfree(authdata);
+ }
+
+ if (!sa->fuse_time) {
+ sa->fuse_time = jiffies;
+ sa->lifetime_c.usetime = (sa->fuse_time)/HZ;
+ ipsec_sa_mod_timer(sa);
+ IPSEC4_DEBUG("set fuse_time = %lu\n", sa->fuse_time);
+ }
+ sa->lifetime_c.bytes += totalsize;
+ IPSEC4_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa->lifetime_c.bytes) >> 32), (__u32)(sa->lifetime_c.bytes));
+ if (sa->lifetime_c.bytes >= sa->lifetime_s.bytes && sa->lifetime_s.bytes) {
+ sa->state = SADB_SASTATE_DYING;
+ IPSEC4_DEBUG("change sa state DYING\n");
+ }
+ if (sa->lifetime_c.bytes >= sa->lifetime_h.bytes && sa->lifetime_h.bytes) {
+ sa->state = SADB_SASTATE_DEAD;
+ IPSEC4_DEBUG("change sa state DEAD\n");
+ }
+
+ write_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+
+ authdata = NULL;
+ /* Set return values */
+ *newdata = esphdr;
+ *newlength = totalsize;
+ return;
+
+unlock_finish:
+ write_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+ return;
+}
+
+void ipsec4_out_finish(struct ipsec_sp *policy)
+{
+ if (policy) {
+ ipsec_sp_put(policy);
+ }
+}
+
+static int ipsec4_output_check_core(struct selector *selector, struct ipsec_sp **policy_ptr)
+{
+ int error = 0;
+ struct ipsec_sp *policy = NULL;
+ int result = IPSEC_ACTION_BYPASS; /* default */
+
+ IPSEC4_DEBUG("called\n");
+
+ if (!selector) {
+ IPSEC4_DEBUG("selector is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ policy = spd_get(selector);
+ if (!policy) { /* not match ! */
+ IPSEC4_DEBUG("no policy exists.\n");
+ result = IPSEC_ACTION_BYPASS;
+ goto err;
+ }
+
+ read_lock_bh(&policy->lock);
+ if (policy->policy_action == IPSEC_POLICY_DROP) {
+ result = IPSEC_ACTION_DROP;
+ read_unlock_bh(&policy->lock);
+ goto err;
+ } else if (policy->policy_action == IPSEC_POLICY_BYPASS) {
+ result = IPSEC_ACTION_BYPASS;
+ read_unlock_bh(&policy->lock);
+ goto err;
+ }
+
+ /* policy must then be to apply ipsec */
+ if (policy->auth_sa_idx) {
+ if (policy->auth_sa_idx->sa) {
+ struct ipsec_sa *sa = NULL;
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ sa = policy->auth_sa_idx->sa;
+ read_unlock_bh(&policy->lock);
+
+ read_lock_bh(&sa->lock);
+ switch (sa->state) {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ result |= IPSEC_ACTION_AUTH;
+ break;
+ default:
+ result = IPSEC_ACTION_DROP;
+ }
+ read_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+ } else {
+ /* copy sa_idx in policy to avoid to lock SADB and SPD at the same time */
+ struct sa_index sa_idx;
+ sa_index_init(&sa_idx);
+ sa_index_copy(&sa_idx, policy->auth_sa_idx);
+ read_unlock_bh(&policy->lock);
+
+ sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
+ if (sa_idx.sa) {
+ write_lock_bh(&policy->lock);
+ policy->auth_sa_idx->sa = sa_idx.sa;
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ write_unlock_bh(&policy->lock);
+ ipsec_sa_put(sa_idx.sa);
+ result |= IPSEC_ACTION_AUTH;
+ } else {
+ /* SADB_ACQUIRE message should be thrown up to KMd */
+ result = IPSEC_ACTION_DROP;
+ }
+ }
+ } else {
+ read_unlock_bh(&policy->lock);
+ }
+
+ read_lock_bh(&policy->lock);
+ if (policy->esp_sa_idx) {
+ if (policy->esp_sa_idx->sa) {
+ struct ipsec_sa *sa = NULL;
+ ipsec_sa_hold(policy->esp_sa_idx->sa);
+ sa = policy->esp_sa_idx->sa;
+ read_unlock_bh(&policy->lock);
+
+ read_lock_bh(&sa->lock);
+ switch (sa->state) {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ result |= IPSEC_ACTION_ESP;
+ break;
+ default:
+ result = IPSEC_ACTION_DROP;
+ }
+ read_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+ } else {
+ /* copy sa_idx in policy to avoid to lock SADB and SPD at the same time */
+ struct sa_index sa_idx;
+ sa_index_init(&sa_idx);
+ sa_index_copy(&sa_idx, policy->esp_sa_idx);
+ read_unlock_bh(&policy->lock);
+
+ sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
+ if (sa_idx.sa) {
+ write_lock_bh(&policy->lock);
+ policy->esp_sa_idx->sa = sa_idx.sa;
+ ipsec_sa_hold(policy->esp_sa_idx->sa);
+ write_unlock_bh(&policy->lock);
+ ipsec_sa_put(sa_idx.sa);
+ result |= IPSEC_ACTION_ESP;
+ } else {
+ /* SADB_ACQUIRE message should be thrown up to KMd */
+ result = IPSEC_ACTION_DROP;
+ }
+ }
+ } else {
+ read_unlock_bh(&policy->lock);
+ }
+
+ read_lock_bh(&policy->lock);
+ if (policy->comp_sa_idx) {
+ if (policy->comp_sa_idx->sa) {
+ struct ipsec_sa *sa = NULL;
+ ipsec_sa_hold(policy->comp_sa_idx->sa);
+ sa = policy->comp_sa_idx->sa;
+ read_unlock_bh(&policy->lock);
+
+ read_lock_bh(&sa->lock);
+ switch (sa->state) {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ result |= IPSEC_ACTION_COMP;
+ break;
+ default:
+ result |= IPSEC_ACTION_DROP;
+ }
+ read_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+ } else {
+ /* copy sa_idx in policy to avoid to lock SADB and SPD at the same time */
+ struct sa_index sa_idx;
+ sa_index_init(&sa_idx);
+ sa_index_copy(&sa_idx, policy->comp_sa_idx);
+ read_unlock_bh(&policy->lock);
+
+ sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
+ if (sa_idx.sa) {
+ write_lock_bh(&policy->lock);
+ policy->comp_sa_idx->sa = sa_idx.sa;
+ ipsec_sa_hold(policy->comp_sa_idx->sa);
+ write_unlock_bh(&policy->lock);
+ ipsec_sa_put(sa_idx.sa);
+ result |= IPSEC_ACTION_COMP;
+ } else {
+ /* SADB_ACUIRE message should be thrown up to KMd */
+ result |= IPSEC_ACTION_DROP;
+ }
+ }
+ } else {
+ read_unlock_bh(&policy->lock);
+ }
+
+ *policy_ptr= policy;
+
+ IPSEC4_DEBUG("end\n");
+
+err:
+ return result;
+}
+
+int ipsec4_output_check(struct sock *sk, struct rtable *rt, struct ipsec_sp **policy_ptr)
+{
+ struct inet_opt *inet = inet_sk(sk);
+ struct in_addr saddr,daddr;
+ u16 sport,dport;
+ unsigned char proto;
+ struct selector selector;
+ int result = IPSEC_ACTION_BYPASS; /* default */
+
+ IPSEC4_DEBUG("called\n");
+ if (!sk) {
+ printk(KERN_ERR "flowi and sock are NULL\n");
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (rt) {
+ saddr.s_addr = rt->rt_src;
+ }else if (sk) {
+ saddr.s_addr = inet->saddr;
+ } else {
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (rt) {
+ daddr.s_addr = rt->rt_dst;
+ } else if (sk) {
+ daddr.s_addr = inet->daddr;
+ } else {
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (sk) {
+ sport=inet->sport;
+ dport=inet->dport;
+ proto=sk->protocol;
+ } else {
+ result = -EINVAL;
+ goto err;
+ }
+
+ /* for ISKAMP see RFC2408 */
+ if (proto == IPPROTO_UDP &&
+ sport == __constant_htons(500) && dport == __constant_htons(500)) {
+ result = IPSEC_ACTION_BYPASS; /* default */
+ goto err;
+ }
+
+ if (proto == IPPROTO_ICMP) {
+ sport = 0;
+ dport = 0;
+ }
+
+ memset(&selector, 0, sizeof(struct selector));
+
+ ((struct sockaddr_in *)&selector.src)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&selector.src)->sin_addr = saddr;
+ ((struct sockaddr_in *)&selector.dst)->sin_family = AF_INET;
+ ((struct sockaddr_in *)&selector.dst)->sin_addr = daddr;
+ selector.proto = proto;
+ selector.prefixlen_d = 32;
+ selector.prefixlen_s = 32;
+
+ ((struct sockaddr_in *)&selector.src)->sin_port = sport;
+ ((struct sockaddr_in *)&selector.dst)->sin_port = dport;
+
+#ifdef CONFIG_IPSEC_DEBUG
+ IPSEC4_DEBUG("original src addr: %d.%d.%d.%d\n", NIPQUAD(saddr));
+ IPSEC4_DEBUG("original src port: %u\n", ntohs(sport));
+ IPSEC4_DEBUG("original dst addr: %d.%d.%d.%d\n", NIPQUAD(daddr));
+ IPSEC4_DEBUG("original dst port: %u\n", ntohs(dport));
+
+ IPSEC4_DEBUG("selector src addr: %d.%d.%d.%d\n",
+ NIPQUAD(((struct sockaddr_in *)&selector.src)->sin_addr));
+ IPSEC4_DEBUG("selector src port: %u\n",
+ ntohs(((struct sockaddr_in *)&selector.src)->sin_port));
+ IPSEC4_DEBUG("selector dst addr: %d.%d.%d.%d\n",
+ NIPQUAD(((struct sockaddr_in *)&selector.dst)->sin_addr));
+ IPSEC4_DEBUG("selector dst port: %u\n",
+ ntohs(((struct sockaddr_in *)&selector.dst)->sin_port));
+ IPSEC4_DEBUG("selector proto: %u\n", selector.proto);
+#endif /* CONFIG_IPSEC_DEBUG */
+
+ result = ipsec4_output_check_core(&selector, policy_ptr);
+
+ err:
+ return result;
+}
+
diff -uNr -x CVS linux-2.5.43/net/ipv4/tcp_ipv4.c linux25.43-ipsec/net/ipv4/tcp_ipv4.c
--- linux-2.5.43/net/ipv4/tcp_ipv4.c 2002-10-16 12:27:50.000000000 +0900
+++ linux25.43-ipsec/net/ipv4/tcp_ipv4.c 2002-10-16 15:27:49.000000000 +0900
@@ -817,6 +817,17 @@
tp->ext_header_len = 0;
if (inet->opt)
tp->ext_header_len = inet->opt->optlen;
+#ifdef CONFIG_IP_IPSEC
+ {
+ struct ipsec_sp *policy_ptr = NULL;
+ int action = ipsec4_output_check(sk, NULL, &policy_ptr);
+
+ if (action != IPSEC_ACTION_DROP) {
+ tp->ext_header_len += ipsec4_out_get_hdrsize(policy_ptr);
+ }
+ ipsec4_out_finish(policy_ptr);
+ }
+#endif /* CONFIG_IP_IPSEC */
tp->mss_clamp = 536;
@@ -1577,6 +1588,17 @@
newtp->ext_header_len = 0;
if (newinet->opt)
newtp->ext_header_len = newinet->opt->optlen;
+#ifdef CONFIG_IP_IPSEC
+ {
+ struct ipsec_sp *policy_ptr = NULL;
+ int action = ipsec4_output_check(sk, NULL, &policy_ptr);
+
+ if (action != IPSEC_ACTION_DROP) {
+ newtp->ext_header_len += ipsec4_out_get_hdrsize(policy_ptr);
+ }
+ ipsec4_out_finish(policy_ptr);
+ }
+#endif /* CONFIG_IP_IPSEC */
newinet->id = newtp->write_seq ^ jiffies;
tcp_sync_mss(newsk, dst->pmtu);
diff -uNr -x CVS linux-2.5.43/net/ipv6/Config.help linux25.43-ipsec/net/ipv6/Config.help
--- linux-2.5.43/net/ipv6/Config.help 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/Config.help 2002-10-16 15:27:49.000000000 +0900
@@ -0,0 +1,5 @@
+CONFIG_IPV6_IPSEC
+
+ IPsec for IPv6 support. You also need to say Y to "The IPsec Protocol."
+ Since IPsec is mandatory feature of IPv6, you probably want to say
+ Y here. If unsure, say N.
diff -uNr -x CVS linux-2.5.43/net/ipv6/Config.in linux25.43-ipsec/net/ipv6/Config.in
--- linux-2.5.43/net/ipv6/Config.in 2002-10-16 12:28:22.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/Config.in 2002-10-16 15:27:49.000000000 +0900
@@ -5,3 +5,13 @@
if [ "$CONFIG_NETFILTER" != "n" ]; then
source net/ipv6/netfilter/Config.in
fi
+
+# -- IPsec --
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ if [ "$CONFIG_IPSEC" != "n" ] ; then
+ bool ' IPv6: IP Security Support (EXPERIMENTAL)' CONFIG_IPV6_IPSEC
+ fi
+ if [ "$CONFIG_IPSEC_TUNNEL" != "n" ]; then
+ define_bool CONFIG_IPV6_IPSEC_TUNNEL y
+ fi
+fi
diff -uNr -x CVS linux-2.5.43/net/ipv6/Makefile linux25.43-ipsec/net/ipv6/Makefile
--- linux-2.5.43/net/ipv6/Makefile 2002-10-16 12:28:33.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/Makefile 2002-10-16 15:43:04.000000000 +0900
@@ -12,6 +12,7 @@
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
ip6_flowlabel.o ipv6_syms.o
+obj-$(CONFIG_IPV6_IPSEC) += ipsec6_input.o ipsec6_output.o
obj-$(CONFIG_NETFILTER) += netfilter/
include $(TOPDIR)/Rules.make
diff -uNr -x CVS linux-2.5.43/net/ipv6/exthdrs.c linux25.43-ipsec/net/ipv6/exthdrs.c
--- linux-2.5.43/net/ipv6/exthdrs.c 2002-10-16 12:27:09.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/exthdrs.c 2002-10-16 15:27:50.000000000 +0900
@@ -43,6 +43,10 @@
#include <asm/uaccess.h>
+#ifdef CONFIG_IPV6_IPSEC
+#include <linux/ipsec6.h>
+#endif
+
/*
* Parsing inbound headers.
*
@@ -402,7 +406,7 @@
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8))
goto fail;
- len = (skb->h.raw[1]+1)<<2;
+ len = (skb->h.raw[1]+2)<<2;
if (len&7)
goto fail;
@@ -410,6 +414,12 @@
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+len))
goto fail;
+#ifdef CONFIG_IPV6_IPSEC
+ if (ipsec6_input_check_ah(skb_ptr,
+ (struct ipv6_auth_hdr*)((*skb_ptr)->h.raw)) <= 0)
+ goto fail;
+#endif /* CONFIG_IPV6_IPSEC */
+
opt->auth = skb->h.raw - skb->nh.raw;
skb->h.raw += len;
return opt->auth;
@@ -419,6 +429,22 @@
return -1;
}
+#ifdef CONFIG_IPV6_IPSEC
+static int ipv6_esp_hdr(struct sk_buff **skb_ptr, int nhoff, u8 *nexthdr)
+{
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)(*skb_ptr)->cb;
+
+ u32 espspi = ipsec6_input_check_esp(skb_ptr,
+ (struct ipv6_esp_hdr*)(*skb_ptr)->h.raw, nexthdr);
+ if (ntohl(espspi) >0 ) {
+ opt->espspi = espspi;
+ return nhoff;
+ } else {
+ return -1;
+ }
+}
+#endif
+
/* This list MUST NOT contain entry for NEXTHDR_HOP.
It is parsed immediately after packet received
and if it occurs somewhere in another place we must
@@ -437,20 +463,28 @@
{-1, NULL}
};
-int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff)
+int ipv6_parse_exthdrs(struct sk_buff **skb_in, int nhoff, u8 *nexthdr)
{
struct hdrtype_proc *hdrt;
- u8 nexthdr = (*skb_in)->nh.raw[nhoff];
restart:
for (hdrt=hdrproc_lst; hdrt->type >= 0; hdrt++) {
- if (hdrt->type == nexthdr) {
+ if (hdrt->type == *nexthdr) {
if ((nhoff = hdrt->func(skb_in, nhoff)) >= 0) {
- nexthdr = (*skb_in)->nh.raw[nhoff];
+ *nexthdr = (*skb_in)->nh.raw[nhoff];
goto restart;
}
return -1;
}
+#ifdef CONFIG_IPV6_IPSEC
+ if (*nexthdr == NEXTHDR_ESP) {
+ if ((nhoff = ipv6_esp_hdr(skb_in, nhoff, nexthdr)) >= 0) {
+ goto restart;
+ }
+ return -1;
+ }
+#endif
+
}
return nhoff;
}
@@ -539,6 +573,7 @@
static u8 *ipv6_build_rthdr(struct sk_buff *skb, u8 *prev_hdr,
struct ipv6_rt_hdr *opt, struct in6_addr *addr)
{
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm*)skb->cb;
struct rt0_hdr *phdr, *ihdr;
int hops;
@@ -557,26 +592,30 @@
phdr->rt_hdr.nexthdr = *prev_hdr;
*prev_hdr = NEXTHDR_ROUTING;
+ parm->srcrt = (unsigned char*)phdr - skb->nh.raw;
return &phdr->rt_hdr.nexthdr;
}
-static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt)
+static u8 *ipv6_build_exthdr(struct sk_buff *skb, u8 *prev_hdr, u8 type, struct ipv6_opt_hdr *opt, __u16 *optoff)
{
struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, ipv6_optlen(opt));
memcpy(h, opt, ipv6_optlen(opt));
h->nexthdr = *prev_hdr;
*prev_hdr = type;
+ *optoff = (unsigned char*)h - skb->nh.raw;
return &h->nexthdr;
}
static u8 *ipv6_build_authhdr(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_opt_hdr *opt)
{
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm*)skb->cb;
struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_put(skb, (opt->hdrlen+2)<<2);
memcpy(h, opt, (opt->hdrlen+2)<<2);
h->nexthdr = *prev_hdr;
*prev_hdr = NEXTHDR_AUTH;
+ parm->auth = (unsigned char*)h - skb->nh.raw;
return &h->nexthdr;
}
@@ -584,10 +623,11 @@
u8 *ipv6_build_nfrag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt,
struct in6_addr *daddr, u32 jumbolen)
{
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm*)skb->cb;
struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb->data;
if (opt && opt->hopopt)
- prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt);
+ prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_HOP, opt->hopopt, &parm->hop);
if (jumbolen) {
u8 *jumboopt = (u8 *)skb_put(skb, 8);
@@ -610,7 +650,7 @@
}
if (opt) {
if (opt->dst0opt)
- prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt);
+ prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt, &parm->dst0);
if (opt->srcrt)
prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr);
}
@@ -619,21 +659,23 @@
u8 *ipv6_build_frag_opts(struct sk_buff *skb, u8 *prev_hdr, struct ipv6_txoptions *opt)
{
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm*)skb->cb;
+
if (opt->auth)
prev_hdr = ipv6_build_authhdr(skb, prev_hdr, opt->auth);
if (opt->dst1opt)
- prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt);
+ prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst1opt, &parm->dst1);
return prev_hdr;
}
static void ipv6_push_rthdr(struct sk_buff *skb, u8 *proto,
- struct ipv6_rt_hdr *opt,
+ struct ipv6_rt_hdr **opt,
struct in6_addr **addr_p)
{
struct rt0_hdr *phdr, *ihdr;
int hops;
- ihdr = (struct rt0_hdr *) opt;
+ ihdr = (struct rt0_hdr *) *opt;
phdr = (struct rt0_hdr *) skb_push(skb, (ihdr->rt_hdr.hdrlen + 1) << 3);
memcpy(phdr, ihdr, sizeof(struct rt0_hdr));
@@ -649,24 +691,36 @@
phdr->rt_hdr.nexthdr = *proto;
*proto = NEXTHDR_ROUTING;
+
+#ifdef CONFIG_IPV6_IPSEC
+ *opt = (struct ipv6_rt_hdr*)phdr;
+#endif
}
-static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr *opt)
+static void ipv6_push_exthdr(struct sk_buff *skb, u8 *proto, u8 type, struct ipv6_opt_hdr **opt)
{
- struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(opt));
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ipv6_optlen(*opt));
- memcpy(h, opt, ipv6_optlen(opt));
+ memcpy(h, *opt, ipv6_optlen(*opt));
h->nexthdr = *proto;
*proto = type;
+
+#ifdef CONFIG_IPV6_IPSEC
+ *opt = h;
+#endif
}
-static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr *opt)
+static void ipv6_push_authhdr(struct sk_buff *skb, u8 *proto, struct ipv6_opt_hdr **opt)
{
- struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, (opt->hdrlen+2)<<2);
+ struct ipv6_opt_hdr *h = (struct ipv6_opt_hdr *)skb_push(skb, ((*opt)->hdrlen+2)<<2);
- memcpy(h, opt, (opt->hdrlen+2)<<2);
+ memcpy(h, *opt, ((*opt)->hdrlen+2)<<2);
h->nexthdr = *proto;
*proto = NEXTHDR_AUTH;
+
+#ifdef CONFIG_IPV6_IPSEC
+ *opt = h;
+#endif
}
void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt,
@@ -674,19 +728,19 @@
struct in6_addr **daddr)
{
if (opt->srcrt)
- ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
+ ipv6_push_rthdr(skb, proto, &opt->srcrt, daddr);
if (opt->dst0opt)
- ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
+ ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, &opt->dst0opt);
if (opt->hopopt)
- ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
+ ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, &opt->hopopt);
}
void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
{
if (opt->dst1opt)
- ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
+ ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, &opt->dst1opt);
if (opt->auth)
- ipv6_push_authhdr(skb, proto, opt->auth);
+ ipv6_push_authhdr(skb, proto, &opt->auth);
}
struct ipv6_txoptions *
@@ -726,6 +780,7 @@
(nexthdr == NEXTHDR_ROUTING) ||
(nexthdr == NEXTHDR_FRAGMENT) ||
(nexthdr == NEXTHDR_AUTH) ||
+ (nexthdr == NEXTHDR_ESP) ||
(nexthdr == NEXTHDR_NONE) ||
(nexthdr == NEXTHDR_DEST) );
}
diff -uNr -x CVS linux-2.5.43/net/ipv6/ip6_input.c linux25.43-ipsec/net/ipv6/ip6_input.c
--- linux-2.5.43/net/ipv6/ip6_input.c 2002-10-16 12:28:32.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/ip6_input.c 2002-10-16 15:27:50.000000000 +0900
@@ -40,6 +40,10 @@
#include <net/ip6_route.h>
#include <net/addrconf.h>
+#ifdef CONFIG_IPV6_IPSEC
+#include <linux/ipsec.h>
+#include <linux/ipsec6.h>
+#endif /* CONFIG_IPV6_IPSEC */
static inline int ip6_rcv_finish( struct sk_buff *skb)
@@ -125,7 +129,8 @@
struct inet6_protocol *ipprot;
struct sock *raw_sk;
int nhoff;
- int nexthdr;
+ u8 nexthdr;
+ int found = 0;
u8 hash;
skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
@@ -149,10 +154,10 @@
which are missing with probability of 200%
*/
if (nexthdr != IPPROTO_TCP && nexthdr != IPPROTO_UDP) {
- nhoff = ipv6_parse_exthdrs(&skb, nhoff);
+ nexthdr = skb->nh.raw[nhoff];
+ nhoff = ipv6_parse_exthdrs(&skb, nhoff, &nexthdr);
if (nhoff < 0)
return 0;
- nexthdr = skb->nh.raw[nhoff];
hdr = skb->nh.ipv6h;
}
@@ -163,6 +168,14 @@
skb->csum = csum_sub(skb->csum,
csum_partial(skb->nh.raw, skb->h.raw-skb->nh.raw, 0));
+#ifdef CONFIG_IPV6_IPSEC
+ IPSEC6_DEBUG("called\n");
+ if (ipsec6_input_check(&skb, &nexthdr)) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ip6_input_finish: (ipsec) dropping packet\n");
+ goto discard;
+ }
+#endif /* CONFIG_IPV6_IPSEC */
resubmit:
raw_sk = raw_v6_htable[nexthdr & (MAX_INET_PROTOS - 1)];
if (raw_sk)
diff -uNr -x CVS linux-2.5.43/net/ipv6/ip6_output.c linux25.43-ipsec/net/ipv6/ip6_output.c
--- linux-2.5.43/net/ipv6/ip6_output.c 2002-10-16 12:27:12.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/ip6_output.c 2002-10-16 15:27:50.000000000 +0900
@@ -51,6 +51,13 @@
#include <net/rawv6.h>
#include <net/icmp.h>
+#ifdef CONFIG_IPV6_IPSEC
+#include <linux/ipsec.h>
+#include <linux/ipsec6.h>
+#else
+struct ipsec_sp; /* to suppress warning */
+#endif /* CONFIG_IPV6_IPSEC */
+
static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
{
static u32 ipv6_fragmentation_id = 1;
@@ -191,14 +198,67 @@
u8 proto = fl->proto;
int seg_len = skb->len;
int hlimit;
+ int retval = 0;
+ void *encdata = NULL;
+#ifdef CONFIG_IPV6_IPSEC
+ unsigned enclength = 0;
+ struct ipsec_sp *policy_ptr = NULL;
+ int ipsec_action = 0;
+ struct ipv6_txoptions *newopt = NULL;
+ struct ipv6_txoptions *opt2 = NULL;
+#endif /* CONFIG_IPV6_IPSEC */
+
+#ifdef CONFIG_IPV6_IPSEC
+ IPSEC6_DEBUG("call ipsec6_output_check\n");
+ ipsec_action = ipsec6_output_check(sk, fl, NULL, &policy_ptr);
+ IPSEC6_DEBUG("ipsec_action is %d\n", ipsec_action);
+ if (ipsec_action == IPSEC_ACTION_DROP) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ip6_xmit: (ipsec) dropping packet.\n");
+ return -EFAULT;
+ }
- if (opt) {
- int head_room;
+ if (opt && opt->auth) {
+ ipsec_action &= ~IPSEC_ACTION_AUTH;
+ printk(KERN_DEBUG "ip6_xmit: AH header is duplicated!\n");
+ }
+
+ /* Get a copy of opt for IPsec */
+ if (ipsec_action & (IPSEC_ACTION_AUTH | IPSEC_ACTION_ESP)) {
+ newopt = ipsec6_out_get_newopt(opt, policy_ptr);
+ if (newopt)
+ opt = newopt;
+ }
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ ipsec6_out_enc(skb->data, skb->len, fl->proto, opt, &encdata, &enclength, policy_ptr);
+ if (!encdata) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ip6_xmit: encrypt failed\n");
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+#endif /* CONFIG_IPV6_IPSEC */
+
+ if (opt || encdata) {
+#ifdef CONFIG_IPV6_IPSEC
+ int encaddsize = 0;
+#endif
+ int head_room = 0;
/* First: exthdrs may take lots of space (~8K for now)
MAX_HEADER is not enough.
*/
- head_room = opt->opt_nflen + opt->opt_flen;
+ if (opt)
+ head_room += opt->opt_nflen + opt->opt_flen;
+#ifdef CONFIG_IPV6_IPSEC
+ if (encdata) {
+ encaddsize = enclength - skb->len;
+ head_room += encaddsize+4;
+ }
+#endif
+
seg_len += head_room;
head_room += sizeof(struct ipv6hdr) + ((dst->dev->hard_header_len + 15)&~15);
@@ -206,15 +266,40 @@
struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room);
kfree_skb(skb);
skb = skb2;
- if (skb == NULL)
- return -ENOBUFS;
+ if (skb == NULL) {
+ retval = -ENOBUFS;
+ goto out;
+ }
if (sk)
skb_set_owner_w(skb, sk);
}
- if (opt->opt_flen)
- ipv6_push_frag_opts(skb, opt, &proto);
- if (opt->opt_nflen)
- ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
+#ifdef CONFIG_IPV6_IPSEC
+ if (encdata) {
+ skb->h.raw = skb_push(skb,encaddsize+4);
+ memcpy(skb->h.raw,encdata,enclength);
+ seg_len -= 4;
+ skb_trim(skb,enclength);
+ fl->proto = NEXTHDR_ESP;
+ proto = fl->proto;
+ }
+ /* ipv6_push_[n}frag_opts overwrites opt. */
+ if (opt) {
+ opt2 = kmalloc(sizeof(struct ipv6_txoptions), GFP_ATOMIC);
+ memcpy(opt2, opt, sizeof(struct ipv6_txoptions));
+
+ if (opt2->opt_flen)
+ ipv6_push_frag_opts(skb, opt2, &proto);
+ if (opt2->opt_nflen)
+ ipv6_push_nfrag_opts(skb, opt2, &proto, &first_hop);
+ }
+#else
+ if (opt) {
+ if (opt->opt_flen)
+ ipv6_push_frag_opts(skb, opt, &proto);
+ if (opt->opt_nflen)
+ ipv6_push_nfrag_opts(skb, opt, &proto, &first_hop);
+ }
+#endif
}
hdr = skb->nh.ipv6h = (struct ipv6hdr*)skb_push(skb, sizeof(struct ipv6hdr));
@@ -237,16 +322,46 @@
ipv6_addr_copy(&hdr->saddr, fl->nl_u.ip6_u.saddr);
ipv6_addr_copy(&hdr->daddr, first_hop);
+#ifdef CONFIG_IPV6_IPSEC
+ if (opt && opt2 && opt2->auth) {
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm*)skb->cb;
+
+ parm->auth = (char*)(opt2->auth) - (char*)(skb->nh.raw);
+
+ if (opt2->hopopt)
+ parm->hop = (char*)(opt2->hopopt) - (char*)(skb->nh.raw);
+ if (opt2->dst0opt)
+ parm->dst0 = (char*)(opt2->dst0opt) - (char*)(skb->nh.raw);
+ if (opt2->dst1opt)
+ parm->dst1 = (char*)(opt2->dst1opt) - (char*)(skb->nh.raw);
+ if ( (ipsec_action & IPSEC_ACTION_AUTH) ) {
+ IPSEC6_DEBUG("call ipsec6_out_calc_ah\n");
+ ipsec6_out_ah_calc(NULL, 0, NULL, skb, NULL, policy_ptr);
+ }
+ kfree(opt2);
+ }
+#endif /* CONFIG_IPV6_IPSEC */
+
if (skb->len <= dst->pmtu) {
IP6_INC_STATS(Ip6OutRequests);
- return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
+ retval = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
+ goto out;
}
if (net_ratelimit())
printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev);
kfree_skb(skb);
- return -EMSGSIZE;
+ retval = -EMSGSIZE;
+
+out:
+#ifdef CONFIG_IPV6_IPSEC
+ if (newopt) ipsec6_out_finish(newopt, policy_ptr);
+ if (encdata) kfree(encdata);
+ policy_ptr=NULL;
+#endif /* CONFIG_IPV6_IPSEC */
+
+ return retval;
}
/*
@@ -323,7 +438,8 @@
const void *data, struct dst_entry *dst,
struct flowi *fl, struct ipv6_txoptions *opt,
struct in6_addr *final_dst,
- int hlimit, int flags, unsigned length, int mtu)
+ int hlimit, int flags, unsigned length, int mtu,
+ int ipsec_action, struct ipsec_sp *policy_ptr)
{
struct ipv6hdr *hdr;
struct sk_buff *last_skb;
@@ -425,6 +541,38 @@
if (opt && opt->opt_nflen)
prev_hdr = ipv6_build_nfrag_opts(last_skb, prev_hdr, opt, final_dst, 0);
+#ifdef CONFIG_IPV6_IPSEC
+ if ( (ipsec_action & IPSEC_ACTION_AUTH) && opt && opt->auth) {
+ struct sk_buff *skb = NULL;
+ u8 *skb_prev_hdr = NULL;
+ int prev_hdr_offset;
+
+ /* The nfrag headers and the ip header are in last_skb. Copy it, and add the
+ * fragmentable headers. So we can build an unfragmented version of the
+ * packet, which is necessary for AH calculation. */
+ skb = skb_copy(last_skb, sk->allocation);
+ if (skb) {
+ prev_hdr_offset = prev_hdr - (u8*)hdr;
+ skb_prev_hdr = ((u8*)skb->nh.ipv6h) + prev_hdr_offset;
+
+ if (opt && opt->opt_flen)
+ ipv6_build_frag_opts(skb, skb_prev_hdr, opt);
+
+ /* Calculate payload length */
+ if (opt) /* This should always be true :-) */
+ skb->nh.ipv6h->payload_len = htons(length + opt->opt_flen + opt->opt_nflen);
+ else
+ skb->nh.ipv6h->payload_len = htons(length);
+
+ /* Calculate AH and free memory */
+ ipsec6_out_ah_calc(data, length, getfrag, skb,
+ (struct ipv6_auth_hdr*)opt->auth, policy_ptr);
+ kfree_skb(skb);
+ } else
+ printk(KERN_WARNING "Could not allocate memory for AH calculation!\n");
+ }
+#endif /* CONFIG_IPV6_IPSEC */
+
prev_hdr = ipv6_build_fraghdr(last_skb, prev_hdr, frag_off);
fhdr_dist = prev_hdr - last_skb->data;
@@ -498,6 +646,15 @@
return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute);
}
+#ifdef CONFIG_IPV6_IPSEC
+static int ipsec6_getfrag(const void *data, struct in6_addr *saddr,
+ char *buff, unsigned int offset, unsigned int len)
+{
+ memcpy(buff, ((char*)data)+offset, len);
+ return 0;
+}
+#endif /* CONFIG_IPV6_IPSEC */
+
int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
struct flowi *fl, unsigned length,
struct ipv6_txoptions *opt, int hlimit, int flags)
@@ -506,6 +663,13 @@
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *final_dst = NULL;
struct dst_entry *dst;
+ struct ipsec_sp *policy_ptr = NULL;
+ int ipsec_action = 0;
+#ifdef CONFIG_IPV6_IPSEC
+ struct ipv6_txoptions *newopt = NULL;
+ void *newdata = NULL;
+ unsigned int newlength = 0;
+#endif /* CONFIG_IPV6_IPSEC */
int err = 0;
unsigned int pktlength, jumbolen, mtu;
struct in6_addr saddr;
@@ -572,6 +736,76 @@
}
fl->fl6_src = &saddr;
}
+
+#ifdef CONFIG_IPV6_IPSEC
+ IPSEC6_DEBUG("call ipsec6_output_check\n");
+ ipsec_action = ipsec6_output_check(sk, fl, data, &policy_ptr);
+ if (ipsec_action == IPSEC_ACTION_DROP) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ip6_build_xmit: (ipsec) dropping packet.\n");
+ return -EFAULT;
+ }
+ if (opt && opt->auth) { /* why ? */
+ ipsec_action &= ~IPSEC_ACTION_AUTH;
+ printk(KERN_DEBUG "ip6_build_xmit: AH is duplicated!\n");
+ }
+
+ /* Get a copy of opt for IPsec */
+ if (ipsec_action & (IPSEC_ACTION_AUTH | IPSEC_ACTION_ESP)) {
+ newopt = ipsec6_out_get_newopt(opt, policy_ptr);
+ if (newopt)
+ opt = newopt;
+ }
+
+ /* Check for encryption */
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *olddata;
+ struct in6_addr dummyaddr;
+
+ IPSEC6_DEBUG("IPsec6: action ESP selected.\n");
+ if (fl->fl6_src == NULL) {
+ dst = __sk_dst_check(sk, np->dst_cookie);
+ err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ip6_build_xmit: no availiable source address (olddata)\n");
+ goto out;
+ }
+ fl->fl6_src = &saddr;
+ }
+ ipv6_addr_copy(&dummyaddr, fl->nl_u.ip6_u.saddr);
+ olddata = kmalloc(length, GFP_ATOMIC);
+ if (!olddata) {
+ err = -ENOMEM;
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Could not get memory for ESP (olddata)\n");
+ goto out;
+ }
+ err = getfrag(data, &dummyaddr, (char*)olddata, 0, length);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "Could not get data for ESP (olddata)\n");
+ kfree(olddata);
+ goto out;
+ }
+
+ ipsec6_out_enc(olddata, length, fl->proto, opt, &newdata, &newlength, policy_ptr);
+
+ if (newdata) {
+ data = newdata;
+ length = newlength;
+ fl->proto = NEXTHDR_ESP;
+ getfrag = ipsec6_getfrag;
+ } else {
+ if (net_ratelimit())
+ printk(KERN_DEBUG "ip6_build_xmit: encrypt failed\n");
+ kfree(olddata);
+ err = -EFAULT;
+ goto out;
+ }
+ kfree(olddata);
+ }
+#endif /* CONFIG_IPV6_IPSEC */
pktlength = length;
if (hlimit < 0) {
@@ -663,6 +897,11 @@
0, length);
if (!err) {
+#ifdef CONFIG_IPV6_IPSEC
+ if ( (ipsec_action & IPSEC_ACTION_AUTH) && opt && opt->auth) {
+ ipsec6_out_ah_calc(NULL, 0, NULL, skb, NULL, policy_ptr);
+ }
+#endif /* CONFIG_IPV6_IPSEC */
IP6_INC_STATS(Ip6OutRequests);
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
} else {
@@ -677,8 +916,44 @@
goto out;
}
+#ifdef CONFIG_IPV6_IPSEC
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ if (policy_ptr && policy_ptr->selector.mode == IPSEC_MODE_TUNNEL) {
+ struct sk_buff *skb;
+ struct ipv6hdr *hdr;
+ struct net_device *dev = dst->dev;
+ unsigned int pmtu = mtu - sizeof(struct ipv6hdr) - ipsec6_out_get_hdrsize(policy_ptr);
+
+ skb = sock_alloc_send_skb(sk, pktlength + 15 +
+ dev->hard_header_len,
+ flags & MSG_DONTWAIT, &err);
+
+ if (skb == NULL) {
+ IP6_INC_STATS(Ip6OutDiscards);
+ goto out;
+ }
+
+ skb->dst = dst_clone(dst);
+
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
+
+ hdr = (struct ipv6hdr *) skb->tail;
+ skb->nh.ipv6h = hdr;
+
+ skb_put(skb, length);
+ err = getfrag(data, &hdr->saddr, (char *) hdr, 0, length);
+
+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0,
+ (pmtu > IPV6_MIN_MTU ? pmtu : IPV6_MIN_MTU), dev);
+ IP6_INC_STATS_BH(Ip6InTooBigErrors);
+
+ goto out;
+ }
+#endif /* CONFIG_IPV6_IPSEC_TUNNEL */
+#endif /* CONFIG_IPV6_IPSEC */
+
err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, final_dst, hlimit,
- flags, length, mtu);
+ flags, length, mtu, ipsec_action, policy_ptr);
}
/*
@@ -688,6 +963,13 @@
ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL);
if (err > 0)
err = np->recverr ? net_xmit_errno(err) : 0;
+
+#ifdef CONFIG_IPV6_IPSEC
+ if (newopt) ipsec6_out_finish(newopt, policy_ptr);
+ if (newdata) kfree(newdata);
+ policy_ptr=NULL;
+#endif /* CONFIG_IPV6_IPSEC */
+
return err;
}
diff -uNr -x CVS linux-2.5.43/net/ipv6/ipsec6_input.c linux25.43-ipsec/net/ipv6/ipsec6_input.c
--- linux-2.5.43/net/ipv6/ipsec6_input.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/ipsec6_input.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,699 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysctl.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/smp.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <net/ipv6.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/snmp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+#include <linux/ipsec.h>
+#include <linux/ipsec6.h>
+#include <linux/pfkeyv2.h> /* sa proto type */
+#include <linux/pfkey.h>
+#ifdef CONFIG_IPV6_IPCOMP
+#include <net/ipcomp.h>
+#endif /* CONFIG_IPV6_IPCOMP */
+
+/* XXX: should move this function to net/ipv6/utils.c */
+static char* in6_ntop(const struct in6_addr *in6, char *buf){
+ if (!buf)
+ return NULL;
+ sprintf(buf,
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ ntohs(in6->s6_addr16[0]), ntohs(in6->s6_addr16[1]),
+ ntohs(in6->s6_addr16[2]), ntohs(in6->s6_addr16[3]),
+ ntohs(in6->s6_addr16[4]), ntohs(in6->s6_addr16[5]),
+ ntohs(in6->s6_addr16[6]), ntohs(in6->s6_addr16[7]));
+ return buf;
+}
+
+static int check_replay_window(struct sa_replay_window *rw, __u32 hdrseq)
+{
+ __u32 diff;
+ __u32 seq = ntohl(hdrseq);
+
+ if (!sysctl_ipsec_replay_window) {
+ IPSEC6_DEBUG("disable replay window check, skip!\n");
+ return 1;
+ }
+
+ IPSEC6_DEBUG("overflow: %u\n"
+ " size: %u\n"
+ " seq_num: %x\n"
+ "last_seq: %x\n"
+ " bitmap: %08x\n"
+ " curr seq: %x\n",
+ rw->overflow, rw->size, rw->seq_num, rw->last_seq, rw->bitmap, seq);
+ if (seq == 0) {
+ return 0; /* first == 0 or wrapped */
+ }
+
+ if (seq > rw->last_seq) return 1; /* larger is good */
+
+ diff = rw->last_seq - seq;
+
+ if (diff >= rw->size) {
+ return 0; /* too old or wrapped */
+ }
+
+ if ( rw->bitmap & ((u_long)1 << diff) ) {
+ return 0; /* already seen */
+ }
+
+ return 1; /* out of order but good */
+}
+
+static void update_replay_window(struct sa_replay_window *rw, __u32 hdrseq)
+{
+ __u32 diff;
+ __u32 seq = ntohl(hdrseq);
+
+ if (!sysctl_ipsec_replay_window) {
+ IPSEC6_DEBUG("disable replay window check, skip!\n");
+ return;
+ }
+
+ if (seq == 0) return;
+
+ if (seq > rw->last_seq) { /* new larger sequence number */
+ diff = seq - rw->last_seq;
+ if (diff < rw->size) { /* In window */
+ rw->bitmap <<= diff;
+ rw->bitmap |= 1; /* set bit for this packet */
+ } else {
+ rw->bitmap = 1; /* This packet has a "way larger" */
+ }
+
+ rw->last_seq = seq;
+ }
+
+ diff = rw->last_seq - seq;
+ rw->bitmap |= ((u_long)1 << diff); /* mark as seen */
+}
+
+static void ipsec6_input_get_offset(u8 *packet, u32 packet_len,
+ struct inet6_skb_parm* opt)
+{
+ u16 offset = sizeof(struct ipv6hdr);
+ u8 nexthdr = ((struct ipv6hdr*)packet)->nexthdr;
+ struct ipv6_opt_hdr *exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+
+ while (offset + 1 < packet_len) {
+
+ switch (nexthdr) {
+
+ case NEXTHDR_HOP:
+ opt->hop = offset;
+ offset += ipv6_optlen(exthdr);
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ break;
+
+ case NEXTHDR_ROUTING:
+ if (opt->dst1) {
+ opt->dst0 = opt->dst1;
+ opt->dst1 = 0;
+ }
+ opt->srcrt = offset;
+ offset += ipv6_optlen(exthdr);
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ break;
+
+ case NEXTHDR_DEST:
+ opt->dst1 = offset;
+ offset += ipv6_optlen(exthdr);
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ break;
+
+ case NEXTHDR_AUTH:
+ opt->auth = offset;
+ offset += (exthdr->hdrlen + 2) <<2;
+ nexthdr = exthdr->nexthdr;
+ exthdr = (struct ipv6_opt_hdr*)(packet + offset);
+ break;
+
+ default :
+ return;
+ }
+ }
+
+ return;
+}
+
+
+int ipsec6_input_check_ah(struct sk_buff **skb, struct ipv6_auth_hdr *authhdr)
+{
+ int rtn = 0;
+ __u8* authdata;
+ size_t authsize;
+ char *packet;
+ int offset;
+ struct sa_index sa_idx;
+ struct inet6_skb_parm opt;
+
+ IPSEC6_DEBUG("start auth header processing\n");
+
+ if (!((*skb)&&authhdr)) {
+ IPSEC6_DEBUG("parameters is invalid\n");
+ goto finish;
+ }
+
+ /* Check SPI */
+ IPSEC6_DEBUG("authhdr->spi is 0x%x\n", ntohl(authhdr->spi));
+
+ sa_index_init(&sa_idx);
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&sa_idx.dst)->sin6_addr,
+ &(*skb)->nh.ipv6h->daddr);
+ ((struct sockaddr_in6 *)&sa_idx.dst)->sin6_family = AF_INET6;
+ sa_idx.prefixlen_d = 128;
+ sa_idx.ipsec_proto = SADB_SATYPE_AH;
+ sa_idx.spi = authhdr->spi;
+
+ sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
+
+ if (!sa_idx.sa) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_ah: not found SA for ah\n");
+ goto finish;
+ }
+
+ write_lock_bh(&sa_idx.sa->lock);
+
+ if (sa_idx.sa->auth_algo.algo == SADB_AALG_NONE ) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec_input_calc_ah: not found auth algo.\n");
+ goto unlock_finish;
+ }
+
+ if (!check_replay_window(&sa_idx.sa->replay_window, authhdr->seq_no)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_ah: replay check err!\n");
+ goto unlock_finish;
+ }
+
+ authsize = ntohs((*skb)->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
+
+ if (authsize > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.ipv6h)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_ah: the packet length is wrong\n");
+ goto unlock_finish;
+ }
+
+ packet = kmalloc(((authsize + 3) & ~3) +
+ sa_idx.sa->auth_algo.dx->di->blocksize, GFP_ATOMIC);
+
+ if (!packet) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_ah: can't get memory for pakcet\n");
+ goto unlock_finish;
+ }
+ authdata = packet + ((authsize + 3) & ~3);
+
+ offset = (char *)((*skb)->nh.ipv6h) - (char *)((*skb)->data);
+
+ if (skb_copy_bits(*skb, offset, packet, authsize)) {
+ IPSEC6_DEBUG("packet copy failed\n");
+ goto unlock_finish;
+ }
+
+ memset(&opt, 0, sizeof(struct inet6_skb_parm));
+ ipsec6_input_get_offset(packet, authsize, &opt);
+
+ zero_out_for_ah(&opt, packet);
+
+ sa_idx.sa->auth_algo.dx->di->hmac_atomic(sa_idx.sa->auth_algo.dx,
+ sa_idx.sa->auth_algo.key,
+ sa_idx.sa->auth_algo.key_len,
+ packet, authsize, authdata);
+
+ /* Originally, IABG uses "for" loop for matching authentication data. */
+ /* I change it into memcmp routine. */
+ if (memcmp(authdata, authhdr->auth_data, sa_idx.sa->auth_algo.digest_len)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_ah: invalid checksum in AH.\n");
+ kfree(packet);
+ goto unlock_finish;
+ }
+ kfree(packet);
+
+ rtn = 1;
+
+ (*skb)->security |= RCV_AUTH; /* ? we must rewrite linux/ipsec.h */
+
+ update_replay_window(&sa_idx.sa->replay_window, authhdr->seq_no);
+
+ if (!sa_idx.sa->fuse_time) {
+ sa_idx.sa->fuse_time = jiffies;
+ sa_idx.sa->lifetime_c.usetime = (sa_idx.sa->fuse_time) / HZ;
+ ipsec_sa_mod_timer(sa_idx.sa);
+ IPSEC6_DEBUG("set fuse_time = %lu\n", sa_idx.sa->fuse_time);
+ }
+ sa_idx.sa->lifetime_c.bytes += (*skb)->tail - (*skb)->head;
+ IPSEC6_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa_idx.sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx.sa->lifetime_c.bytes));
+ if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_s.bytes && sa_idx.sa->lifetime_s.bytes) {
+ sa_idx.sa->state = SADB_SASTATE_DYING;
+ IPSEC6_DEBUG("change sa state DYING\n");
+ }
+ if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_h.bytes && sa_idx.sa->lifetime_h.bytes) {
+ sa_idx.sa->state = SADB_SASTATE_DEAD;
+ IPSEC6_DEBUG("change sa state DEAD\n");
+ }
+
+unlock_finish:
+ write_unlock_bh(&sa_idx.sa->lock); /* unlock SA */
+ ipsec_sa_put(sa_idx.sa);
+finish:
+ return rtn;
+}
+
+int ipsec6_input_check_esp(struct sk_buff **skb, struct ipv6_esp_hdr* esphdr, u8 *nexthdr)
+{
+ int len = 0;
+ int rtn = 0;
+ struct sa_index sa_idx;
+ u8 *authdata = NULL;
+ u8 *srcdata = NULL;
+ int srcsize = 0, totalsize = 0, hashsize = 0, encsize = 0;
+
+ IPSEC6_DEBUG("start esp processing\n");
+ if (!(*skb&&esphdr)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: parameters are invalid\n");
+ goto finish;
+ }
+
+ if (skb_is_nonlinear(*skb)) {
+ u16 offset = ((char*)esphdr) - (char*)((*skb)->nh.raw);
+ if (!skb_linearize(*skb, GFP_ATOMIC)) {
+ esphdr = (struct ipv6_esp_hdr*)((*skb)->nh.raw + offset);
+ } else {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: counld not linearize skb\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+ }
+
+ /* Check SPI */
+ IPSEC6_DEBUG("esphdr->spi is 0x%x\n", ntohl(esphdr->spi));
+
+ sa_index_init(&sa_idx);
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&sa_idx.dst)->sin6_addr,
+ &(*skb)->nh.ipv6h->daddr);
+ ((struct sockaddr_in6 *)&sa_idx.dst)->sin6_family = AF_INET6;
+ sa_idx.prefixlen_d = 128;
+ sa_idx.ipsec_proto = SADB_SATYPE_ESP;
+ sa_idx.spi = esphdr->spi;
+
+ sa_idx.sa = sadb_find_by_sa_index(&sa_idx);
+
+ if (!sa_idx.sa) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: not found SA for esp\n");
+ goto finish;
+ }
+
+ write_lock_bh(&sa_idx.sa->lock);
+
+ if ( sa_idx.sa->esp_algo.algo == SADB_EALG_NONE ) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: not found encryption algorithm in SA!\n");
+ goto unlock_finish;
+ }
+
+ len = ntohs((*skb)->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
+
+ if (len > (*skb)->len + ((char*)(*skb)->data - (char*)(*skb)->nh.ipv6h)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: received packet length is wrong\n");
+ goto unlock_finish;
+ }
+
+ totalsize = len - ((((char*)esphdr) - ((char*)(*skb)->nh.ipv6h)));
+
+ if (!(sa_idx.sa->esp_algo.cx->ci)) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: not found esp algo\n");
+ goto unlock_finish;
+ }
+
+ if ( !check_replay_window(&sa_idx.sa->replay_window, esphdr->seq_no) ) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: replay check err!\n");
+ kfree(srcdata);
+ goto unlock_finish;
+ }
+
+ encsize = totalsize - sa_idx.sa->esp_algo.cx->ci->ivsize - 8;
+ /* 8 = SPI + Sequence Number */
+
+ if ( sa_idx.sa->auth_algo.algo != SADB_AALG_NONE ) {
+ /* Calculate size */
+ /* The tail of payload does not have to be aligned */
+ /* with a multiple number of 64 bit. */
+ /* 64 bit alignment is adapted to the position of top of header.*/
+ hashsize = sa_idx.sa->auth_algo.digest_len;
+ encsize -= hashsize;
+ authdata=kmalloc(sa_idx.sa->auth_algo.dx->di->blocksize, GFP_ATOMIC);
+ sa_idx.sa->auth_algo.dx->di->hmac_atomic(sa_idx.sa->auth_algo.dx,
+ sa_idx.sa->auth_algo.key,
+ sa_idx.sa->auth_algo.key_len,
+ (char*)esphdr, totalsize - hashsize, authdata);
+ /* Originally, IABG uses "for" loop for matching authentication data. */
+ /* I change it into memcmp routine. */
+
+ if (memcmp(authdata, &((char*)esphdr)[totalsize - hashsize],
+ sa_idx.sa->auth_algo.digest_len )) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: invalid checksum in ESP\n");
+ kfree(authdata);
+ goto unlock_finish;
+ }
+ kfree(authdata);
+ authdata = NULL;
+ }
+
+ /* Decrypt data */
+ srcdata = kmalloc(encsize, GFP_ATOMIC);
+ if (!srcdata) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: can't allocate memory for decrypt\n");
+ goto unlock_finish;
+ }
+
+ IPSEC6_DEBUG("len=%d, totalsize=%d, encsize=%d\n",
+ len, totalsize, encsize);
+
+ if (!(sa_idx.sa->esp_algo.iv)) { /* first packet */
+ sa_idx.sa->esp_algo.iv = kmalloc(sa_idx.sa->esp_algo.cx->ci->ivsize, GFP_ATOMIC);
+ }
+
+ memcpy(sa_idx.sa->esp_algo.iv, esphdr->enc_data, sa_idx.sa->esp_algo.cx->ci->ivsize);
+ sa_idx.sa->esp_algo.cx->ci->decrypt_atomic_iv(sa_idx.sa->esp_algo.cx,
+ ((u8 *)(esphdr->enc_data)) + sa_idx.sa->esp_algo.cx->ci->ivsize,
+ srcdata, encsize, sa_idx.sa->esp_algo.iv);
+
+ /* encsize - (pad_len + next_hdr) - pad_len */
+ srcsize = encsize - 2 - srcdata[encsize-2];
+ IPSEC6_DEBUG("Original data is srcsize=%d, padlength=%d\n", srcsize, srcdata[encsize-2]);
+ if (srcsize <= 0) {
+ if (net_ratelimit())
+ printk(KERN_ERR "ipsec6_input_check_esp: Encrypted packet contains garbage(Size of decrypted packet < 0).\n");
+ kfree(srcdata);
+ goto unlock_finish;
+ }
+
+ update_replay_window(&sa_idx.sa->replay_window, esphdr->seq_no);
+
+ *nexthdr = srcdata[encsize-1];
+ IPSEC6_DEBUG("nexthdr = %u\n", *nexthdr);
+ memcpy(esphdr, srcdata, srcsize);
+
+ skb_trim(*skb, (*skb)->len + srcsize - totalsize);
+ (*skb)->nh.ipv6h->payload_len = htons(((char *)esphdr - (char *)((*skb)->nh.ipv6h)) - sizeof(struct ipv6hdr) + srcsize);
+ /* ok ? -mk */
+
+ kfree(srcdata);
+ srcdata = NULL;
+
+ rtn = sa_idx.spi;
+
+ /* Otherwise checksum of fragmented udp packets fails (udp.c, csum_fold) */
+ (*skb)->ip_summed = CHECKSUM_UNNECESSARY;
+ (*skb)->security |= RCV_CRYPT;
+
+ if (!sa_idx.sa->fuse_time) {
+ sa_idx.sa->fuse_time = jiffies;
+ sa_idx.sa->lifetime_c.usetime = (sa_idx.sa->fuse_time) / HZ;
+ ipsec_sa_mod_timer(sa_idx.sa);
+ IPSEC6_DEBUG("set fuse_time = %lu\n", (sa_idx.sa->fuse_time));
+ }
+ sa_idx.sa->lifetime_c.bytes += totalsize;
+ IPSEC6_DEBUG("sa->bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa_idx.sa->lifetime_c.bytes) >> 32), (__u32)(sa_idx.sa->lifetime_c.bytes));
+ if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_s.bytes && sa_idx.sa->lifetime_s.bytes) {
+ sa_idx.sa->state = SADB_SASTATE_DYING;
+ IPSEC6_DEBUG("change sa state DYING\n");
+ }
+ if (sa_idx.sa->lifetime_c.bytes >= sa_idx.sa->lifetime_h.bytes && sa_idx.sa->lifetime_h.bytes) {
+ sa_idx.sa->state = SADB_SASTATE_DEAD;
+ IPSEC6_DEBUG("change sa state DEAD\n");
+ }
+
+
+unlock_finish:
+ write_unlock_bh(&sa_idx.sa->lock); /* unlock SA */
+ ipsec_sa_put(sa_idx.sa);
+
+finish:
+ return rtn;
+}
+
+int ipsec6_input_check(struct sk_buff **skb, __u8 *nexthdr)
+{
+ int rtn = 0;
+ struct inet6_skb_parm *opt = NULL;
+ int result = IPSEC_ACTION_BYPASS;
+ struct sa_index auth_sa_idx;
+ struct sa_index esp_sa_idx;
+#ifdef CONFIG_IPV6_IPCOMP
+#if 0
+ struct sa_index comp_sa_idx;
+#endif
+#endif /* CONFIG_IPV6_IPCOMP */
+ struct selector selector;
+ struct ipsec_sp *policy = NULL;
+ int addr_type = 0;
+ struct ipv6hdr *hdr = (*skb)->nh.ipv6h;
+
+ IPSEC6_DEBUG("called\n");
+#ifdef CONFIG_IPSEC_DEBUG
+ {
+ char buf[64];
+ IPSEC6_DEBUG("dst addr: %s\n",
+ in6_ntop( &hdr->daddr, buf));
+ IPSEC6_DEBUG("src addr: %s\n",
+ in6_ntop( &hdr->saddr, buf));
+ IPSEC6_DEBUG("hdr->payload_len is %d\n",
+ ntohs(hdr->payload_len));
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+ /* XXX */
+ addr_type = ipv6_addr_type(&hdr->daddr);
+ if (addr_type & IPV6_ADDR_MULTICAST) {
+ IPSEC6_DEBUG("address type multicast skip!\n");
+ goto finish;
+ }
+
+ if ( *nexthdr == NEXTHDR_UDP ) { /* IKE */
+ if (pskb_may_pull(*skb, (*skb)->h.raw - (*skb)->data + sizeof(struct udphdr))) {
+ if ((*skb)->h.uh->source == htons(500) &&
+ (*skb)->h.uh->dest == htons(500)) {
+ IPSEC6_DEBUG("received IKE packet. skip!\n");
+ goto finish;
+ }
+ }
+ }
+
+ opt = (struct inet6_skb_parm*)((*skb)->cb);
+
+ if (opt->auth) {
+ sa_index_init(&auth_sa_idx);
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&auth_sa_idx.dst)->sin6_addr,
+ &(*skb)->nh.ipv6h->daddr);
+ ((struct sockaddr_in6 *)&auth_sa_idx.dst)->sin6_family = AF_INET6;
+ auth_sa_idx.prefixlen_d = 128;
+ auth_sa_idx.ipsec_proto = SADB_SATYPE_AH;
+ auth_sa_idx.spi = ((struct ipv6_auth_hdr*)((*skb)->nh.raw + opt->auth))->spi;
+ result |= IPSEC_ACTION_AUTH;
+ opt->auth=0;
+ }
+
+ if (opt->espspi) {
+ sa_index_init(&esp_sa_idx);
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&esp_sa_idx.dst)->sin6_addr,
+ &(*skb)->nh.ipv6h->daddr);
+ ((struct sockaddr_in6 *)&esp_sa_idx.dst)->sin6_family = AF_INET6;
+ esp_sa_idx.prefixlen_d = 128;
+ esp_sa_idx.ipsec_proto = SADB_SATYPE_ESP;
+ esp_sa_idx.spi = opt->espspi;
+ result |= IPSEC_ACTION_ESP;
+ opt->espspi=0;
+ }
+
+ if (result&IPSEC_ACTION_DROP) {
+ IPSEC6_DEBUG("result is drop.\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+
+ /* copy selector XXX port */
+ memset(&selector, 0, sizeof(struct selector));
+
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ if (*nexthdr == NEXTHDR_IPV6) {
+ selector.mode = IPSEC_MODE_TUNNEL;
+ }
+#endif
+ selector.proto = *nexthdr;
+
+ IPSEC6_DEBUG("nexthdr = %u\n", *nexthdr);
+
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ if (selector.mode == IPSEC_MODE_TUNNEL) {
+ struct ipv6hdr *h = NULL;
+ if (pskb_may_pull(*skb, (*skb)->h.raw - (*skb)->data + sizeof(struct ipv6hdr))) {
+ h = (struct ipv6hdr*) (*skb)->h.raw;
+
+ ((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
+ &h->saddr);
+ ((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
+ &h->daddr);
+ } else {
+ rtn = -EINVAL;
+ goto finish;
+ }
+ } else {
+#endif
+ ((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
+ &(*skb)->nh.ipv6h->saddr);
+ ((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
+ &(*skb)->nh.ipv6h->daddr);
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ }
+#endif
+ selector.prefixlen_d = 128;
+ selector.prefixlen_s = 128;
+
+ /* beggining of matching check selector and policy */
+ IPSEC6_DEBUG("start match check SA and policy.\n");
+
+#ifdef CONFIG_IPSEC_DEBUG
+ {
+ char buf[64];
+ IPSEC6_DEBUG("selector dst addr: %s\n",
+ in6_ntop( &((struct sockaddr_in6 *)&selector.dst)->sin6_addr, buf));
+ IPSEC6_DEBUG("selector src addr: %s\n",
+ in6_ntop( &((struct sockaddr_in6 *)&selector.src)->sin6_addr, buf));
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+ policy = spd_get(&selector);
+
+ if (policy) {
+
+ read_lock_bh(&policy->lock);
+
+ /* non-ipsec packet processing: If this packet doesn't
+ * have any IPSEC headers, then consult policy to see
+ * what to do with packet. If policy says to apply IPSEC,
+ * and there is an SA, then pass packet to netxt layer,
+ * if ther isn't an SA, then drop the packet.
+ */
+ if (policy->policy_action == IPSEC_POLICY_DROP) {
+ rtn = -EINVAL;
+ read_unlock_bh(&policy->lock);
+ goto finish;
+ }
+
+ if (policy->policy_action == IPSEC_POLICY_BYPASS) {
+ rtn = 0;
+ read_unlock_bh(&policy->lock);
+ goto finish;
+ }
+
+ if (policy->policy_action == IPSEC_POLICY_APPLY) {
+ if (result&IPSEC_ACTION_AUTH) {
+ if (policy->auth_sa_idx) {
+ if (sa_index_compare(&auth_sa_idx,
+ policy->auth_sa_idx)) {
+ rtn = -EINVAL;
+ }
+ } else {
+ rtn = -EINVAL;
+ }
+ } else {
+ if (policy->auth_sa_idx) rtn = -EINVAL;
+ }
+
+ if (result&IPSEC_ACTION_ESP) {
+ if (policy->esp_sa_idx) {
+ if (sa_index_compare(&esp_sa_idx,
+ policy->esp_sa_idx)) {
+ rtn = -EINVAL;
+ }
+ } else {
+ rtn = -EINVAL;
+ }
+ } else {
+ if (policy->esp_sa_idx) rtn = -EINVAL;
+ }
+ }
+
+ read_unlock_bh(&policy->lock);
+ ipsec_sp_put(policy);
+ } else {
+ if (!result) {
+ rtn = 0;
+ } else {
+ IPSEC6_DEBUG("matching pair of SA and policy not found, through!\n");
+ rtn = -EINVAL;
+ goto finish;
+ }
+ }
+
+
+ IPSEC6_DEBUG("end match check SA and policy.\n");
+ /* end of matching check selector and policy */
+
+
+finish:
+
+ return rtn;
+}
+
diff -uNr -x CVS linux-2.5.43/net/ipv6/ipsec6_output.c linux25.43-ipsec/net/ipv6/ipsec6_output.c
--- linux-2.5.43/net/ipv6/ipsec6_output.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/ipsec6_output.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,851 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Authors:
+ * Kazunori Miyazawa <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/byteorder.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysctl.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/smp.h>
+#include <linux/list.h>
+#include <linux/random.h>
+#include <asm/uaccess.h>
+#include <net/ipv6.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+#include <net/snmp.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/icmpv6.h>
+#include <linux/ipsec.h>
+#include <linux/ipsec6.h>
+#include <linux/pfkeyv2.h> /* sa proto type */
+#include <linux/pfkey.h>
+
+/* XXX: should move this function to net/ipv6/utils.c */
+static char* in6_ntop(const struct in6_addr *in6, char *buf){
+ if (!buf)
+ return NULL;
+ sprintf(buf,
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ ntohs(in6->s6_addr16[0]), ntohs(in6->s6_addr16[1]),
+ ntohs(in6->s6_addr16[2]), ntohs(in6->s6_addr16[3]),
+ ntohs(in6->s6_addr16[4]), ntohs(in6->s6_addr16[5]),
+ ntohs(in6->s6_addr16[6]), ntohs(in6->s6_addr16[7]));
+ return buf;
+}
+
+int ipsec6_out_ah_calc(const void *data, unsigned length, inet_getfrag_t getfrag,
+ struct sk_buff *skb, struct ipv6_auth_hdr *authhdr, struct ipsec_sp *policy)
+{
+ struct ipsec_sa *sa = NULL;
+ char* pseudo_packet;
+ int packetlen;
+ struct inet6_skb_parm* parm;
+ struct ipv6_auth_hdr *pseudo_authhdr = NULL;
+ __u8* authdata = NULL;
+
+ IPSEC6_DEBUG("called.\n");
+ if(!policy){
+ return -EINVAL;
+ }
+
+ if (!data) length=0;
+ if (!skb) {
+ IPSEC6_DEBUG("skb is NULL!\n");
+ return -EINVAL;
+ }
+
+ parm = (struct inet6_skb_parm*)skb->cb;
+
+ if (!authhdr) {
+ if (parm->auth) {
+ authhdr = (struct ipv6_auth_hdr*)(skb->nh.raw + parm->auth);
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ read_lock_bh(&policy->lock);
+
+ if (!policy->auth_sa_idx || !policy->auth_sa_idx->sa) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(ah) SA missing.\n", __FUNCTION__);
+ read_unlock_bh(&policy->lock);
+ return -EINVAL;
+ }
+
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ sa = policy->auth_sa_idx->sa;
+
+ read_unlock_bh(&policy->lock);
+
+ packetlen = ntohs(skb->nh.ipv6h->payload_len) + sizeof(struct ipv6hdr);
+
+ pseudo_packet = kmalloc(packetlen,GFP_ATOMIC);
+ if (!pseudo_packet) {
+ ipsec_sa_put(sa);
+ return -ENOMEM;
+ }
+
+ if (length>0) {
+ struct in6_addr *addr;
+ addr=&skb->nh.ipv6h->saddr;
+ getfrag(data,addr,&pseudo_packet[skb->len],0,length);
+ }
+
+ pseudo_authhdr = (struct ipv6_auth_hdr*)(pseudo_packet + parm->auth);
+
+ authdata=kmalloc((sa->auth_algo.dx)->di->blocksize, GFP_ATOMIC);
+ if (!authdata) {
+ kfree(pseudo_packet);
+ ipsec_sa_put(sa);
+ return -ENOMEM;
+ }
+
+ write_lock_bh(&sa->lock);
+
+ /* authhdr->spi = htonl(sa->spi); */
+ IPSEC6_DEBUG("spi is 0x%x\n", ntohl(sa->spi));
+ authhdr->spi = sa->spi; /* -mk */
+ authhdr->seq_no = htonl(++sa->replay_window.seq_num);
+
+ memcpy(pseudo_packet,skb->nh.ipv6h,skb->len);
+
+ pseudo_authhdr->spi = authhdr->spi;
+ pseudo_authhdr->seq_no = authhdr->seq_no;
+
+ zero_out_for_ah(parm, pseudo_packet);
+
+ sa->auth_algo.dx->di->hmac_atomic(sa->auth_algo.dx,
+ sa->auth_algo.key,
+ sa->auth_algo.key_len,
+ pseudo_packet, packetlen, authdata);
+
+ memcpy(authhdr->auth_data, authdata, sa->auth_algo.digest_len);
+
+ if (!sa->fuse_time) {
+ sa->fuse_time = jiffies;
+ sa->lifetime_c.usetime = (sa->fuse_time)/HZ;
+ ipsec_sa_mod_timer(sa);
+ IPSEC6_DEBUG("set fuse_time = %lu\n", sa->fuse_time);
+ }
+
+ sa->lifetime_c.bytes += packetlen;
+ IPSEC6_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa->lifetime_c.bytes) >> 32),
+ (__u32)(sa->lifetime_c.bytes));
+
+ if (sa->lifetime_c.bytes >= sa->lifetime_s.bytes &&
+ sa->lifetime_s.bytes) {
+ IPSEC6_DEBUG("change sa state DYING\n");
+ sa->state = SADB_SASTATE_DYING;
+ }
+ if (sa->lifetime_c.bytes >= sa->lifetime_h.bytes &&
+ sa->lifetime_h.bytes) {
+ sa->state = SADB_SASTATE_DEAD;
+ IPSEC6_DEBUG("change sa state DEAD\n");
+ }
+
+ write_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+
+ kfree(authdata);
+ kfree(pseudo_packet);
+ return 0;
+
+}
+
+int ipsec6_out_get_ahsize(struct ipsec_sp *policy)
+{
+ int result = 0;
+ struct ipsec_sa *sa_ah = NULL;
+
+ IPSEC6_DEBUG("called.\n");
+
+ if (!policy) return 0;
+
+ write_lock_bh(&policy->lock);
+ if (policy->auth_sa_idx && policy->auth_sa_idx->sa) {
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ sa_ah = policy->auth_sa_idx->sa;
+ }
+
+ write_unlock_bh(&policy->lock);
+
+ if (sa_ah) {
+ read_lock_bh(&sa_ah->lock);
+ if ( sa_ah->auth_algo.algo != SADB_AALG_NONE) {
+ result += (offsetof(struct ipv6_auth_hdr, auth_data) +
+ sa_ah->auth_algo.digest_len + 7) & ~7; /* 64 bit alignment */
+ }
+ read_unlock_bh(&sa_ah->lock);
+ ipsec_sa_put(sa_ah);
+ }
+
+ IPSEC6_DEBUG("Calculated size is %d.\n", result);
+ return result;
+}
+
+int ipsec6_out_get_espsize(struct ipsec_sp *policy)
+{
+ int result = 0;
+ struct ipsec_sa *sa_esp = NULL;
+
+ IPSEC6_DEBUG("called.\n");
+
+ if (!policy) return 0;
+
+ write_lock_bh(&policy->lock);
+
+ if (policy->esp_sa_idx && policy->esp_sa_idx->sa) {
+ ipsec_sa_hold(policy->esp_sa_idx->sa);
+ sa_esp = policy->esp_sa_idx->sa;
+ }
+ write_unlock_bh(&policy->lock);
+
+ if (sa_esp) {
+ read_lock_bh(&sa_esp->lock);
+ if ( sa_esp->esp_algo.algo != SADB_EALG_NONE){
+ result += sizeof(struct ipv6_esp_hdr) - 8;
+ result += sa_esp->esp_algo.cx->ci->ivsize;
+ result += (sa_esp->esp_algo.cx->ci->blocksize + 3) & ~3;
+ result += 4; /* included pad_len and next_hdr 32 bit align */
+ }else{
+ read_unlock_bh(&sa_esp->lock);
+ ipsec_sa_put(sa_esp);
+ return 0;
+ }
+ if ( sa_esp->auth_algo.algo != SADB_AALG_NONE) {
+ result += (sa_esp->auth_algo.digest_len + 3) & ~3; /* 32 bit alignment */
+ }
+ read_unlock_bh(&sa_esp->lock);
+ ipsec_sa_put(sa_esp);
+ }
+ IPSEC6_DEBUG("Calculated size is %d.\n", result);
+ return result;
+}
+
+struct ipv6_txoptions *ipsec6_out_get_newopt(struct ipv6_txoptions *opt, struct ipsec_sp *policy)
+{
+ struct ipv6_txoptions *newopt = NULL;
+ struct ipsec_sa *sa = NULL;
+ int ah_len = 0;
+
+ IPSEC6_DEBUG("called.\n");
+
+
+ if (!policy) {
+ if (net_ratelimit())
+ printk(KERN_INFO "ipsec6_out_get_newopt: ipsec6_ptr/policy is NULL.\n");
+ return NULL;
+ }
+
+ read_lock_bh(&policy->lock);
+
+ if (policy->auth_sa_idx && policy->auth_sa_idx->sa) {
+
+ ipsec_sa_hold(policy->auth_sa_idx->sa);
+ sa = policy->auth_sa_idx->sa;
+ read_unlock_bh(&policy->lock);
+
+ read_lock_bh(&sa->lock);
+
+ if ( sa->auth_algo.algo == SADB_AALG_NONE ) {
+ if (net_ratelimit())
+ printk(KERN_INFO "ipsec6_out_get_newopt: Hash algorithm %d not present.\n",
+ sa->auth_algo.algo);
+ read_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+ return NULL;
+ }
+
+ ah_len = (offsetof(struct ipv6_auth_hdr, auth_data) +
+ sa->auth_algo.digest_len + 7) & ~7;
+
+ IPSEC6_DEBUG("ah_len=%d hash_size=%d\n", ah_len, sa->auth_algo.digest_len);
+ read_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+
+ } else {
+ read_unlock_bh(&policy->lock);
+ }
+
+ if (opt) {
+ IPSEC6_DEBUG("There have already been opt.\n");
+ newopt = (struct ipv6_txoptions*)kmalloc(opt->tot_len + ah_len, GFP_ATOMIC);
+ if (!newopt) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "Couldn't allocate newopt - out of memory.\n");
+ return NULL;
+ }
+ memset(newopt, 0, opt->tot_len + ah_len);
+ memcpy(newopt, opt, sizeof(struct ipv6_txoptions));
+
+ if (ah_len)
+ newopt->auth = (struct ipv6_opt_hdr*)((char*)newopt + opt->tot_len);
+
+
+ } else if (ah_len) {
+
+ IPSEC6_DEBUG("There is not opt.\n");
+ newopt = (struct ipv6_txoptions*) kmalloc(sizeof(struct ipv6_txoptions) + ah_len, GFP_ATOMIC);
+ if (!newopt) {
+ if (net_ratelimit()) {
+ printk(KERN_INFO "ipsec6_out_get_newopt: could not allocate newopt.\n");
+ return NULL;
+ }
+ }
+ memset(newopt, 0, sizeof(struct ipv6_txoptions) + ah_len);
+ newopt->auth = (struct ipv6_opt_hdr*)((char*)newopt + sizeof(struct ipv6_txoptions));
+ newopt->tot_len = sizeof(struct ipv6_txoptions);
+ }
+
+ if (ah_len) {
+ newopt->tot_len += ah_len;
+ newopt->opt_flen += ah_len;
+ newopt->auth->hdrlen = (ah_len >> 2) - 2;
+ }
+
+ return newopt;
+}
+
+/*** ESP ***/
+
+void ipsec6_out_enc(const void *data, unsigned length, u8 proto, struct ipv6_txoptions *opt,
+ void **newdata, unsigned *newlength, struct ipsec_sp *policy)
+{
+ struct ipsec_sa *sa = NULL;
+ struct ipv6_esp_hdr *esphdr = NULL;
+ u8* srcdata = NULL;
+ u8* authdata = NULL;
+ int encblocksize = 0;
+ int encsize = 0, hashsize = 0, totalsize = 0;
+ int dstoptlen = 0;
+ int i;
+
+ IPSEC6_DEBUG("called.\nData ptr is %p, data length is %d.\n",data,length);
+
+ if (!policy) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s; ipsec policy is NULL\n", __FUNCTION__);
+ return;
+ }
+
+ read_lock_bh(&policy->lock);
+ if (!policy->esp_sa_idx || !policy->esp_sa_idx->sa) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(esp) SA missing.\n", __FUNCTION__);
+ read_unlock_bh(&policy->lock);
+ return;
+ }
+ ipsec_sa_hold(policy->esp_sa_idx->sa);
+ sa = policy->esp_sa_idx->sa;
+ read_unlock_bh(&policy->lock);
+
+ write_lock_bh(&sa->lock);
+ /* Get algorithms */
+ if (sa->esp_algo.algo == SADB_EALG_NONE) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(esp) encryption algorithm not present.\n", __FUNCTION__);
+ goto unlock_finish;
+ return;
+ }
+
+
+ if (!(sa->esp_algo.cx->ci)){
+ if (net_ratelimit())
+ printk(KERN_WARNING "%s: ipsec(esp) cipher_implementation not present.\n", __FUNCTION__);
+ goto unlock_finish;
+ return;
+ }
+
+ /* Calculate size */
+
+ if (opt && opt->dst1opt)
+ dstoptlen = ipv6_optlen(opt->dst1opt);
+
+ encblocksize = (sa->esp_algo.cx->ci->blocksize + 3) & ~3;
+ encsize = dstoptlen + length + 2 + (encblocksize - 1);
+ encsize -= encsize % encblocksize;
+
+ /* The tail of payload does not have to be aligned with a multiple number of 64 bit. */
+ /* 64 bit alignment is adapted to the position of top of header. */
+
+ if (sa->auth_algo.algo != SADB_AALG_NONE)
+ hashsize = sa->auth_algo.digest_len;
+
+
+ totalsize = sizeof(struct ipv6_esp_hdr) - 8 + sa->esp_algo.cx->ci->ivsize + encsize + hashsize;
+ IPSEC6_DEBUG("IV size=%d, enc size=%d hash size=%d, total size=%d\n",
+ sa->esp_algo.cx->ci->ivsize, encsize, hashsize, totalsize);
+
+ /* Get memory */
+ esphdr = kmalloc(totalsize, GFP_ATOMIC);
+ srcdata = kmalloc(encsize, GFP_ATOMIC);
+ if (!esphdr || !srcdata) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ipsec6_out_enc: Out of memory.\n");
+ if (esphdr) kfree(esphdr);
+ if (srcdata) kfree(srcdata);
+ goto unlock_finish;
+ return;
+ }
+
+ memset(esphdr, 0, totalsize);
+ memset(srcdata, 0, encsize);
+ /* Handle sequence number and fill in header fields */
+ esphdr->spi = sa->spi;
+ esphdr->seq_no = htonl(++sa->replay_window.seq_num);
+
+ /* Get source data, fill in padding and trailing fields */
+ if (opt && opt->dst1opt)
+ memcpy(srcdata, opt->dst1opt, dstoptlen);
+
+ memcpy(srcdata + dstoptlen, data, length);
+ for (i = length + dstoptlen; i < encsize-2; i++)
+ srcdata[i] = (u8)(i-length+dstoptlen+1);
+ srcdata[encsize-2] = (encsize-2)-length-dstoptlen;
+ IPSEC6_DEBUG("length=%d, encsize=%d\n", length+dstoptlen, encsize);
+ IPSEC6_DEBUG("encsize-2=%d\n", srcdata[encsize-2]);
+
+ if (opt && opt->dst1opt) {
+ /* ((struct ipv6_opt_hdr*)srcdata)->nexthdr = proto; */
+ srcdata[0] = proto;
+ opt->dst1opt = NULL;
+ opt->opt_flen -= dstoptlen;
+ srcdata[encsize-1] = NEXTHDR_DEST;
+ } else {
+ srcdata[encsize-1] = proto;
+ }
+
+ /* Do encryption */
+
+ if (!(sa->esp_algo.iv)) { /* first packet */
+ sa->esp_algo.iv = kmalloc(sa->esp_algo.cx->ci->ivsize, GFP_ATOMIC); /* kfree at SA removed */
+ get_random_bytes(sa->esp_algo.iv, sa->esp_algo.cx->ci->ivsize);
+ IPSEC6_DEBUG("IV initilized.\n");
+ } /* else, had inserted a stored iv (last packet block) */
+
+#ifdef CONFIG_IPSEC_DEBUG
+ {
+ int i;
+ IPSEC6_DEBUG("IV is 0x");
+ if (sysctl_ipsec_debug_ipv6) {
+ for (i=0; i < sa->esp_algo.cx->ci->ivsize ; i++) {
+ printk(KERN_DEBUG "%x", (u8)(sa->esp_algo.iv[i]));
+ }
+ }
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+ sa->esp_algo.cx->ci->encrypt_atomic_iv(sa->esp_algo.cx, srcdata,
+ (u8 *)&esphdr->enc_data + sa->esp_algo.cx->ci->ivsize, encsize, sa->esp_algo.iv);
+ memcpy(esphdr->enc_data, sa->esp_algo.iv, sa->esp_algo.cx->ci->ivsize);
+ kfree(srcdata);
+ srcdata=NULL;
+ /* copy last block for next IV (src: enc_data + ivsize + encsize - ivsize) */
+ memcpy(sa->esp_algo.iv, esphdr->enc_data + encsize, sa->esp_algo.cx->ci->ivsize);
+ /* if CONFIG_IPSEC_DEBUG isn't defined here is finish of encryption process */
+
+ if(sa->auth_algo.algo){
+ authdata = kmalloc(sa->auth_algo.dx->di->blocksize, GFP_ATOMIC);
+ if (!authdata) {
+ if (net_ratelimit())
+ printk(KERN_WARNING "ipsec6_out_enc: Out of memory.\n");
+ kfree(esphdr);
+ goto unlock_finish;
+ return;
+ }
+ memset(authdata, 0, sa->auth_algo.dx->di->blocksize);
+ sa->auth_algo.dx->di->hmac_atomic(sa->auth_algo.dx,
+ sa->auth_algo.key,
+ sa->auth_algo.key_len,
+ (char*)esphdr, totalsize-hashsize, authdata);
+ memcpy(&((char*)esphdr)[8 + sa->esp_algo.cx->ci->ivsize + encsize],
+ authdata, sa->auth_algo.digest_len);
+
+ kfree(authdata);
+ }
+
+ if (!sa->fuse_time) {
+ sa->fuse_time = jiffies;
+ sa->lifetime_c.usetime = (sa->fuse_time)/HZ;
+ ipsec_sa_mod_timer(sa);
+ IPSEC6_DEBUG("set fuse_time = %lu\n", sa->fuse_time);
+ }
+ sa->lifetime_c.bytes += totalsize;
+ IPSEC6_DEBUG("sa->lifetime_c.bytes=%-9u %-9u\n", /* XXX: %-18Lu */
+ (__u32)((sa->lifetime_c.bytes) >> 32), (__u32)(sa->lifetime_c.bytes));
+ if (sa->lifetime_c.bytes >= sa->lifetime_s.bytes && sa->lifetime_s.bytes) {
+ sa->state = SADB_SASTATE_DYING;
+ IPSEC6_DEBUG("change sa state DYING\n");
+ }
+ if (sa->lifetime_c.bytes >= sa->lifetime_h.bytes && sa->lifetime_h.bytes) {
+ sa->state = SADB_SASTATE_DEAD;
+ IPSEC6_DEBUG("change sa state DEAD\n");
+ }
+
+ write_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+
+ authdata = NULL;
+ /* Set return values */
+ *newdata = esphdr;
+ *newlength = totalsize;
+ return;
+
+unlock_finish:
+ write_unlock_bh(&sa->lock);
+ ipsec_sa_put(sa);
+ return;
+}
+
+void ipsec6_out_finish(struct ipv6_txoptions *opt, struct ipsec_sp *policy)
+{
+
+ if (opt) {
+ kfree(opt);
+ }
+
+ if (policy) {
+ ipsec_sp_put(policy);
+ }
+}
+
+static int ipsec6_output_check_core(struct selector *selector, struct ipsec_sp **policy_ptr)
+{
+ int error = 0;
+ struct ipsec_sp *policy = NULL;
+ int result = IPSEC_ACTION_BYPASS; /* default */
+
+ IPSEC6_DEBUG("called\n");
+
+ if (!selector) {
+ IPSEC6_DEBUG("selector is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ policy = spd_get(selector);
+ if (!policy) { /* not match ! */
+ IPSEC6_DEBUG("no policy exists.\n");
+ result = IPSEC_ACTION_BYPASS;
+ goto err;
+ }
+
+ read_lock_bh(&policy->lock);
+ if (policy->policy_action == IPSEC_POLICY_DROP) {
+ result = IPSEC_ACTION_DROP;
+ read_unlock_bh(&policy->lock);
+ goto err;
+ } else if (policy->policy_action == IPSEC_POLICY_BYPASS) {
+ result = IPSEC_ACTION_BYPASS;
+ read_unlock_bh(&policy->lock);
+ goto err;
+ }
+
+ /* policy must then be to apply ipsec */
+ if (policy->auth_sa_idx) {
+ if (policy->auth_sa_idx->sa) {
+ read_lock_bh(&policy->auth_sa_idx->sa->lock);
+ switch (policy->auth_sa_idx->sa->state) {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ result |= IPSEC_ACTION_AUTH;
+ break;
+ default:
+ result = IPSEC_ACTION_DROP;
+ }
+ read_unlock_bh(&policy->auth_sa_idx->sa->lock);
+ } else {
+ read_unlock_bh(&policy->lock);
+ write_lock_bh(&policy->lock);
+ /* check and see if another process attached an sa to
+ * the policy while we were acquiring the write lock.
+ * Note: refcnt guarantees policy is still in memory.
+ */
+ if (!policy->auth_sa_idx->sa)
+ policy->auth_sa_idx->sa = sadb_find_by_sa_index(policy->auth_sa_idx);
+ write_unlock_bh(&policy->lock);
+ read_lock_bh(&policy->lock);
+ if (policy->auth_sa_idx->sa)
+ result |= IPSEC_ACTION_AUTH;
+ else
+ /* SADB_ACQUIRE message should be thrown up to KMd */
+ result = IPSEC_ACTION_DROP;
+ }
+ }
+
+ if (policy->esp_sa_idx) {
+ if (policy->esp_sa_idx->sa) {
+ read_lock_bh(&policy->esp_sa_idx->sa->lock);
+ switch (policy->esp_sa_idx->sa->state) {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ result |= IPSEC_ACTION_ESP;
+ break;
+ default:
+ result = IPSEC_ACTION_DROP;
+ }
+ read_unlock_bh(&policy->esp_sa_idx->sa->lock);
+ } else {
+ read_unlock_bh(&policy->lock);
+ write_lock_bh(&policy->lock);
+ /* check and see if another process attached an sa to
+ * the policy while we were acquiring the write lock.
+ * Note: refcnt guarantees policy is still in memory.
+ */
+ if (!policy->esp_sa_idx->sa)
+ policy->esp_sa_idx->sa = sadb_find_by_sa_index(policy->esp_sa_idx);
+ write_unlock_bh(&policy->lock);
+ read_lock_bh(&policy->lock);
+ if (policy->esp_sa_idx->sa)
+ result |= IPSEC_ACTION_ESP;
+ else
+ /* SADB_ACQUIRE message should be thrown up to KMd */
+ result = IPSEC_ACTION_DROP;
+ }
+ }
+
+ if (policy->comp_sa_idx) {
+ if (policy->comp_sa_idx->sa) {
+ read_lock_bh(&policy->comp_sa_idx->sa->lock);
+ switch (policy->comp_sa_idx->sa->state) {
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ result |= IPSEC_ACTION_COMP;
+ break;
+ default:
+ result |= IPSEC_ACTION_DROP;
+ }
+ read_unlock_bh(&policy->comp_sa_idx->sa->lock);
+ } else {
+ read_unlock_bh(&policy->lock);
+ write_lock_bh(&policy->lock);
+ /* check and see if another process attached an sa to
+ * the policy while we were acquiring the write lock.
+ * Note: refcnt guarantees policy is still in memory.
+ */
+ if (!policy->comp_sa_idx->sa)
+ policy->comp_sa_idx->sa = sadb_find_by_sa_index(policy->comp_sa_idx);
+ write_unlock_bh(&policy->lock);
+ read_lock_bh(&policy->lock);
+ if (policy->comp_sa_idx->sa)
+ result |= IPSEC_ACTION_COMP;
+ else
+ /* SADB_ACUIRE message should be thrown up to KMd */
+ result |= IPSEC_ACTION_DROP;
+ }
+ }
+
+ *policy_ptr= policy;
+ read_unlock_bh(&policy->lock);
+ IPSEC6_DEBUG("end\n");
+
+err:
+ return result;
+}
+
+int ipsec6_output_check(struct sock *sk, struct flowi *fl, const u8 *data, struct ipsec_sp **policy_ptr)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct inet_opt *inet = inet_sk(sk);
+ struct in6_addr *saddr,*daddr;
+ u16 sport,dport;
+ unsigned char proto;
+ struct selector selector;
+ int result = IPSEC_ACTION_BYPASS; /* default */
+
+ IPSEC6_DEBUG("called\n");
+ if (!sk && !fl) {
+ printk(KERN_ERR "flowi and sock are NULL\n");
+ result = -EINVAL;
+ goto err;
+ }
+
+ if (fl && fl->fl6_src) {
+ saddr = fl->fl6_src;
+ } else {
+ if (sk) {
+ saddr = &np->saddr;
+ } else {
+ result = -EINVAL;
+ printk(KERN_ERR "sock is null\n");
+ goto err;
+ }
+ }
+
+ if (fl && fl->fl6_dst) {
+ daddr = fl->fl6_dst;
+ } else {
+ if (sk) {
+ daddr = &np->daddr;
+ } else {
+ result = -EINVAL;
+ printk(KERN_ERR "flowi and sock are NULL\n");
+ goto err;
+ }
+ }
+
+ if (fl) {
+ sport=fl->uli_u.ports.sport;
+ dport=fl->uli_u.ports.dport;
+ proto=fl->proto;
+ } else if (sk) {
+ sport=inet->sport;
+ dport=inet->dport;
+ proto=sk->protocol;
+ } else {
+ result = -EINVAL;
+ printk(KERN_ERR "flowi and sock are NULL\n");
+ goto err;
+ }
+
+ /* for ISKAMP see RFC2408 */
+ if (proto == IPPROTO_UDP &&
+ sport == htons(500) && dport == htons(500)) {
+ result = IPSEC_ACTION_BYPASS; /* default */
+ goto err;
+ }
+
+ /* XXX have to decide to the policy of ICMP messages -mk*/
+ if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) {
+ sport = 0;
+ dport = 0;
+ }
+
+ /* XXX config port policy */
+ memset(&selector, 0, sizeof(struct selector));
+
+
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ if (proto == IPPROTO_IPV6) {
+ selector.mode = IPSEC_MODE_TUNNEL;
+ } else {
+#endif
+ ((struct sockaddr_in6 *)&selector.src)->sin6_port = sport;
+ ((struct sockaddr_in6 *)&selector.dst)->sin6_port = dport;
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ }
+ selector.proto = proto;
+
+ if (selector.mode == IPSEC_MODE_TUNNEL) {
+ struct ipv6hdr *h = (struct ipv6hdr*) data;
+ ((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
+ &h->saddr);
+ ((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
+ &h->daddr);
+ } else { /* IPSEC_MODE_TRANSPORT */
+#endif
+ ((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
+ saddr);
+ ((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
+ daddr);
+#ifdef CONFIG_IPV6_IPSEC_TUNNEL
+ }
+#endif
+
+ selector.prefixlen_d = 128;
+ selector.prefixlen_s = 128;
+
+
+#ifdef CONFIG_IPSEC_DEBUG
+ {
+ char buf[64];
+ IPSEC6_DEBUG("original src addr: %s\n", in6_ntop(saddr, buf));
+ IPSEC6_DEBUG("original src port: %u\n", ntohs(sport));
+ IPSEC6_DEBUG("original dst addr: %s\n", in6_ntop(daddr, buf));
+ IPSEC6_DEBUG("original dst port: %u\n", ntohs(dport));
+
+ IPSEC6_DEBUG("selector src addr: %s\n",
+ in6_ntop( &((struct sockaddr_in6 *)&selector.src)->sin6_addr, buf));
+ IPSEC6_DEBUG("selector src port: %u\n",
+ ntohs(((struct sockaddr_in6 *)&selector.src)->sin6_port));
+ IPSEC6_DEBUG("selector dst addr: %s\n",
+ in6_ntop( &((struct sockaddr_in6 *)&selector.dst)->sin6_addr, buf));
+ IPSEC6_DEBUG("selector dst port: %u\n",
+ ntohs(((struct sockaddr_in6 *)&selector.dst)->sin6_port));
+ IPSEC6_DEBUG("selector proto: %u\n", selector.proto);
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+
+ result = ipsec6_output_check_core(&selector, policy_ptr);
+
+ err:
+ return result;
+}
+
+
+int ipsec6_ndisc_check(struct in6_addr *saddr, struct in6_addr *daddr, struct ipsec_sp **policy_ptr)
+{
+ struct selector selector;
+ int result = IPSEC_ACTION_BYPASS; /* default */
+
+ IPSEC6_DEBUG("called\n");
+
+
+ /* XXX config port policy */
+ memset(&selector, 0, sizeof(struct selector));
+
+ ((struct sockaddr_in6 *)&selector.src)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.src)->sin6_addr,
+ saddr);
+ ((struct sockaddr_in6 *)&selector.dst)->sin6_family = AF_INET6;
+ ipv6_addr_copy(&((struct sockaddr_in6 *)&selector.dst)->sin6_addr,
+ daddr);
+ selector.proto = IPPROTO_ICMPV6;
+ selector.prefixlen_d = 128;
+ selector.prefixlen_s = 128;
+
+#ifdef CONFIG_IPSEC_DEBUG
+ {
+ char buf[64];
+ IPSEC6_DEBUG("original dst addr: %s\n", in6_ntop(daddr, buf));
+ IPSEC6_DEBUG("original src addr: %s\n", in6_ntop(saddr, buf));
+
+ IPSEC6_DEBUG("selector dst addr: %s\n",
+ in6_ntop( &((struct sockaddr_in6 *)&selector.dst)->sin6_addr, buf));
+ IPSEC6_DEBUG("selector src addr: %s\n",
+ in6_ntop( &((struct sockaddr_in6 *)&selector.src)->sin6_addr, buf));
+ IPSEC6_DEBUG("selector proto: %u\n", selector.proto);
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+
+ result = ipsec6_output_check_core(&selector, policy_ptr);
+
+ return result;
+}
diff -uNr -x CVS linux-2.5.43/net/ipv6/ipv6_sockglue.c linux25.43-ipsec/net/ipv6/ipv6_sockglue.c
--- linux-2.5.43/net/ipv6/ipv6_sockglue.c 2002-10-16 12:27:14.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/ipv6_sockglue.c 2002-10-16 15:27:50.000000000 +0900
@@ -51,6 +51,11 @@
#include <asm/uaccess.h>
+#ifdef CONFIG_IPV6_IPSEC
+#include <linux/ipsec.h>
+#include <linux/ipsec6.h>
+#endif /* CONFIG_IPV6_IPSEC */
+
struct ipv6_mib ipv6_statistics[NR_CPUS*2];
static struct packet_type ipv6_packet_type =
@@ -284,7 +289,22 @@
struct tcp_opt *tp = tcp_sk(sk);
if (!((1<<sk->state)&(TCPF_LISTEN|TCPF_CLOSE))
&& inet_sk(sk)->daddr != LOOPBACK4_IPV6) {
+#ifdef CONFIG_IPV6_IPSEC
+ struct ipsec_sp *policy_ptr = NULL;
+ int action = ipsec6_output_check(sk,NULL,NULL,&policy_ptr);
+#endif /* CONFIG_IPV6_IPSEC */
tp->ext_header_len = opt->opt_flen + opt->opt_nflen;
+#ifdef CONFIG_IPV6_IPSEC
+ if (action != IPSEC_ACTION_DROP) {
+ printk(KERN_DEBUG "ipv6_setsockopt: ipsec6_output_check said DROP.\n");
+ if (opt->auth) {
+ action &=! IPSEC_ACTION_AUTH;
+ } else {
+ tp->ext_header_len += ipsec6_out_get_hdrsize(policy_ptr);
+ }
+ }
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif /* CONFIG_IPV6_IPSEC */
tcp_sync_mss(sk, tp->pmtu_cookie);
}
}
diff -uNr -x CVS linux-2.5.43/net/ipv6/ndisc.c linux25.43-ipsec/net/ipv6/ndisc.c
--- linux-2.5.43/net/ipv6/ndisc.c 2002-10-16 12:28:24.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/ndisc.c 2002-10-16 15:37:23.000000000 +0900
@@ -75,6 +75,11 @@
#include <net/checksum.h>
#include <linux/proc_fs.h>
+#ifdef CONFIG_IPV6_IPSEC
+#include <linux/ipsec.h>
+#include <linux/ipsec6.h>
+#endif
+
static struct socket *ndisc_socket;
static u32 ndisc_hash(const void *pkey, const struct net_device *dev);
@@ -383,6 +388,37 @@
int len;
struct sk_buff *skb;
int err;
+ int ipsec_hdrlen = 0;
+ int ipsec_authhdrlen = 0;
+#ifdef CONFIG_IPV6_IPSEC
+ struct in6_addr *src_addr;
+ struct inet6_ifaddr *ifp;
+ u8 *prevhdr;
+ struct ipsec_sp *policy_ptr = NULL;
+ int ipsec_action = IPSEC_ACTION_DROP;
+ struct ipv6_auth_hdr *authhdr = NULL;
+#endif
+
+#ifdef CONFIG_IPV6_IPSEC
+ ifp = ipv6_get_ifaddr(solicited_addr, dev);
+ if (ifp) {
+ src_addr = solicited_addr;
+ in6_ifa_put(ifp);
+ } else {
+ return;
+ }
+
+ ipsec_action = ipsec6_ndisc_check(src_addr, daddr, &policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_DROP || ipsec_action == -EINVAL)
+ return;
+
+ if (ipsec_action & IPSEC_ACTION_AUTH)
+ ipsec_hdrlen += ipsec_authhdrlen = ipsec6_out_get_ahsize(policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_ESP)
+ ipsec_hdrlen += ipsec6_out_get_espsize(policy_ptr);
+#endif
len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr);
@@ -393,23 +429,53 @@
inc_opt = 0;
}
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15 + ipsec_hdrlen,
0, &err);
if (skb == NULL) {
ND_PRINTK1("send_na: alloc skb failed\n");
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
return;
}
- if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
+ if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len + ipsec_hdrlen) == 0) {
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
kfree_skb(skb);
return;
}
- ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len);
+ ip6_nd_hdr(sk, skb, dev, solicited_addr, daddr, IPPROTO_ICMPV6, len + ipsec_authhdrlen);
+#ifdef CONFIG_IPV6_IPSEC
+ prevhdr = &(skb->nh.ipv6h->nexthdr);
+ if (ipsec_authhdrlen) {
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb;
+ authhdr = (struct ipv6_auth_hdr *)skb_put(skb, ipsec_authhdrlen);
+ memset(authhdr, 0, ipsec_authhdrlen);
+ parm->auth = sizeof(struct ipv6hdr); /* offset of authentication header */
+ *prevhdr = IPPROTO_AH;
+ prevhdr = &(authhdr->nexthdr);
+ authhdr->hdrlen = (ipsec_authhdrlen>>2) - 2;
+ *prevhdr = IPPROTO_ICMPV6;
+ }
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ msg = (struct nd_msg *)kmalloc(len, GFP_ATOMIC);
+ if (!msg) {
+ ipsec6_out_finish(NULL, policy_ptr);
+ kfree_skb(skb);
+ return;
+ }
+ } else {
+ msg = (struct nd_msg *) skb_put(skb, len);
+ }
+#else
msg = (struct nd_msg *) skb_put(skb, len);
-
+#endif
msg->icmph.icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
msg->icmph.icmp6_code = 0;
msg->icmph.icmp6_cksum = 0;
@@ -431,6 +497,33 @@
csum_partial((__u8 *) msg,
len, 0));
+#ifdef CONFIG_IPV6_IPSEC
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *encdata = NULL;
+ int enclength = 0;
+ ipsec6_out_enc(msg, len, IPPROTO_ICMPV6, NULL, &encdata, &enclength, policy_ptr);
+ if (encdata) {
+ char *esphdr = skb_put(skb, enclength);
+ memcpy(esphdr, encdata, enclength);
+ *prevhdr = IPPROTO_ESP;
+ skb->nh.ipv6h->payload_len =
+ htons(ntohs(skb->nh.ipv6h->payload_len) - len + enclength);
+ } else {
+ ipsec6_out_finish(NULL, policy_ptr);
+ kfree(msg);
+ kfree_skb(skb);
+ return;
+ }
+ kfree(msg);
+ }
+
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ ipsec6_out_ah_calc(NULL, 0, NULL, skb, authhdr, policy_ptr);
+ }
+
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
+
dev_queue_xmit(skb);
ICMP6_INC_STATS(Icmp6OutNeighborAdvertisements);
@@ -448,6 +541,14 @@
int len;
int err;
int send_llinfo;
+ int ipsec_hdrlen = 0;
+ int ipsec_authhdrlen = 0;
+#ifdef CONFIG_IPV6_IPSEC
+ u8 *prevhdr;
+ struct ipsec_sp *policy_ptr = NULL;
+ struct ipv6_auth_hdr *authhdr = NULL;
+ int ipsec_action = IPSEC_ACTION_DROP;
+#endif
if (saddr == NULL) {
if (ipv6_get_lladdr(dev, &addr_buf))
@@ -460,21 +561,65 @@
if (send_llinfo)
len += NDISC_OPT_SPACE(dev->addr_len);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec_action = ipsec6_ndisc_check(saddr, daddr, &policy_ptr);
+
+ if (ipsec_action == IPSEC_ACTION_DROP || ipsec_action == -EINVAL)
+ return;
+
+ if (ipsec_action & IPSEC_ACTION_AUTH)
+ ipsec_hdrlen += ipsec_authhdrlen = ipsec6_out_get_ahsize(policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_ESP)
+ ipsec_hdrlen += ipsec6_out_get_espsize(policy_ptr);
+#endif
+
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15 + ipsec_hdrlen,
0, &err);
if (skb == NULL) {
ND_PRINTK1("send_ns: alloc skb failed\n");
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
return;
}
- if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len) == 0) {
+ if (ndisc_build_ll_hdr(skb, dev, daddr, neigh, len + ipsec_hdrlen) == 0) {
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
kfree_skb(skb);
return;
}
- ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len + ipsec_authhdrlen);
+#ifdef CONFIG_IPV6_IPSEC
+ prevhdr = &(skb->nh.ipv6h->nexthdr);
+ if (ipsec_authhdrlen) {
+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb;
+ authhdr = (struct ipv6_auth_hdr *)skb_put(skb, ipsec_authhdrlen);
+ memset(authhdr, 0, ipsec_authhdrlen);
+ parm->auth = sizeof(struct ipv6hdr); /* offset of authentication header */
+ *prevhdr = IPPROTO_AH;
+ prevhdr = &(authhdr->nexthdr);
+ authhdr->hdrlen = (ipsec_authhdrlen>>2) - 2;
+ *prevhdr = IPPROTO_ICMPV6;
+ }
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ msg = (struct nd_msg *)kmalloc(len, GFP_ATOMIC);
+ if (!msg) {
+ ipsec6_out_finish(NULL, policy_ptr);
+ kfree_skb(skb);
+ return;
+ }
+ } else {
+ msg = (struct nd_msg *)skb_put(skb, len);
+ }
+#else
msg = (struct nd_msg *)skb_put(skb, len);
+#endif
msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION;
msg->icmph.icmp6_code = 0;
msg->icmph.icmp6_cksum = 0;
@@ -492,6 +637,35 @@
IPPROTO_ICMPV6,
csum_partial((__u8 *) msg,
len, 0));
+
+#ifdef CONFIG_IPV6_IPSEC
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *encdata = NULL;
+ int enclength = 0;
+ ipsec6_out_enc(msg, len, IPPROTO_ICMPV6, NULL, &encdata, &enclength, policy_ptr);
+ if (encdata) {
+ char *esphdr = skb_put(skb, enclength);
+ memcpy(esphdr, encdata, enclength);
+ *prevhdr = IPPROTO_ESP;
+
+ skb->nh.ipv6h->payload_len =
+ htons(ntohs(skb->nh.ipv6h->payload_len) - len + enclength);
+ } else {
+ ipsec6_out_finish(NULL, policy_ptr);
+ kfree(msg);
+ kfree_skb(skb);
+ return;
+ }
+ kfree(msg);
+ }
+
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ ipsec6_out_ah_calc(NULL, 0, NULL, skb, authhdr, policy_ptr);
+ }
+
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
+
/* send it! */
dev_queue_xmit(skb);
@@ -508,26 +682,78 @@
__u8 * opt;
int len;
int err;
+ int ipsec_hdrlen = 0;
+ int ipsec_authhdrlen = 0;
+#ifdef CONFIG_IPV6_IPSEC
+ u8 *prevhdr;
+ struct ipsec_sp *policy_ptr = NULL;
+ struct ipv6_auth_hdr *authhdr = NULL;
+ int ipsec_action = ipsec6_ndisc_check(saddr, daddr, &policy_ptr);
+
+#endif
+
+#ifdef CONFIG_IPV6_IPSEC
+ if (ipsec_action == IPSEC_ACTION_DROP || ipsec_action == -EINVAL)
+ return;
+
+ if (ipsec_action & IPSEC_ACTION_AUTH)
+ ipsec_hdrlen += ipsec_authhdrlen = ipsec6_out_get_ahsize(policy_ptr);
+
+ if (ipsec_action & IPSEC_ACTION_ESP)
+ ipsec_hdrlen += ipsec6_out_get_espsize(policy_ptr);
+#endif
len = sizeof(struct icmp6hdr);
if (dev->addr_len)
len += NDISC_OPT_SPACE(dev->addr_len);
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15,
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + dev->hard_header_len + 15 + ipsec_hdrlen,
0, &err);
if (skb == NULL) {
ND_PRINTK1("send_ns: alloc skb failed\n");
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
return;
}
- if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len) == 0) {
+ if (ndisc_build_ll_hdr(skb, dev, daddr, NULL, len + ipsec_hdrlen) == 0) {
+#ifdef CONFIG_IPV6_IPSEC
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif
kfree_skb(skb);
return;
}
- ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len);
+ ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len + ipsec_authhdrlen);
+#ifdef CONFIG_IPV6_IPSEC
+ prevhdr = &(skb->nh.ipv6h->nexthdr);
+
+ if (ipsec_authhdrlen) {
+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb;
+ authhdr = (struct ipv6_auth_hdr*)skb_put(skb, ipsec_authhdrlen);
+ memset(authhdr, 0, ipsec_authhdrlen);
+ opt->auth = sizeof(struct ipv6hdr);
+ *prevhdr = IPPROTO_AH;
+ prevhdr = &(authhdr->nexthdr);
+ authhdr->hdrlen = (ipsec_authhdrlen>>2) - 2;
+ *prevhdr = IPPROTO_ICMPV6;
+ }
+
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ hdr = (struct icmp6hdr *)kmalloc(len, GFP_ATOMIC);
+ if (!hdr) {
+ ipsec6_out_finish(NULL, policy_ptr);
+ kfree_skb(skb);
+ return;
+ }
+ } else {
+ hdr = (struct icmp6hdr *)skb_put(skb, len);
+ }
+#else
hdr = (struct icmp6hdr *) skb_put(skb, len);
+#endif
hdr->icmp6_type = NDISC_ROUTER_SOLICITATION;
hdr->icmp6_code = 0;
hdr->icmp6_cksum = 0;
@@ -543,6 +769,33 @@
IPPROTO_ICMPV6,
csum_partial((__u8 *) hdr, len, 0));
+#ifdef CONFIG_IPV6_IPSEC
+ if (ipsec_action & IPSEC_ACTION_ESP) {
+ void *encdata = NULL;
+ int enclength = 0;
+ ipsec6_out_enc(hdr, len, IPPROTO_ICMPV6, NULL, &encdata, &enclength, policy_ptr);
+ if (encdata) {
+ char *esphdr = skb_put(skb, enclength);
+ memcpy(esphdr, encdata, enclength);
+ *prevhdr = IPPROTO_ESP;
+
+ skb->nh.ipv6h->payload_len =
+ htons(ntohs(skb->nh.ipv6h->payload_len) - len + enclength);
+ } else {
+ ipsec6_out_finish(NULL, policy_ptr);
+ kfree(hdr);
+ kfree_skb(skb);
+ return;
+ }
+ }
+
+ if (ipsec_action & IPSEC_ACTION_AUTH) {
+ ipsec6_out_ah_calc(NULL, 0, NULL, skb, authhdr, policy_ptr);
+ }
+
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif /* CONFIG_IPV6_IPSEC */
+
/* send it! */
dev_queue_xmit(skb);
diff -uNr -x CVS linux-2.5.43/net/ipv6/reassembly.c linux25.43-ipsec/net/ipv6/reassembly.c
--- linux-2.5.43/net/ipv6/reassembly.c 2002-10-16 12:27:49.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/reassembly.c 2002-10-16 15:27:50.000000000 +0900
@@ -534,12 +534,20 @@
nhoff = head->h.raw - head->nh.raw;
if (payload_len > 65535) {
+#ifdef CONFIG_IPV6_IPSEC
+ if (payload_len > 65535 + 8)
+#else
payload_len -= 8;
if (payload_len > 65535)
+#endif
goto out_oversize;
remove_fraghdr = 1;
}
+#ifdef CONFIG_IPV6_IPSEC
+ remove_fraghdr = 1;
+#endif /* CONFIG_IPV6_IPSEC */
+
/* Head of list must not be cloned. */
if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC))
goto out_oom;
@@ -576,6 +584,9 @@
memmove(head->head+8, head->head, (head->data-head->head)-8);
head->mac.raw += 8;
head->nh.raw += 8;
+#ifdef CONFIG_IPV6_IPSEC
+ payload_len -= 8;
+#endif
} else {
((struct frag_hdr*)head->h.raw)->frag_off = 0;
}
diff -uNr -x CVS linux-2.5.43/net/ipv6/tcp_ipv6.c linux25.43-ipsec/net/ipv6/tcp_ipv6.c
--- linux-2.5.43/net/ipv6/tcp_ipv6.c 2002-10-16 12:28:23.000000000 +0900
+++ linux25.43-ipsec/net/ipv6/tcp_ipv6.c 2002-10-16 15:27:50.000000000 +0900
@@ -51,6 +51,10 @@
#include <asm/uaccess.h>
+#ifdef CONFIG_IPV6_IPSEC
+#include <linux/ipsec6.h>
+#endif /* CONFIG_IPV6_IPSEC */
+
static void tcp_v6_send_reset(struct sk_buff *skb);
static void tcp_v6_or_send_ack(struct sk_buff *skb, struct open_request *req);
static void tcp_v6_send_check(struct sock *sk, struct tcphdr *th, int len,
@@ -675,8 +679,24 @@
inet->rcv_saddr = LOOPBACK4_IPV6;
tp->ext_header_len = 0;
- if (np->opt)
- tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+ {
+#ifdef CONFIG_IPV6_IPSEC
+ struct ipsec_sp *policy_ptr = NULL;
+ int action = ipsec6_output_check(sk, &fl, NULL, &policy_ptr);
+#endif /* CONFIG_IPV6_IPSEC */
+ if (np->opt)
+ tp->ext_header_len = np->opt->opt_flen + np->opt->opt_nflen;
+#ifdef CONFIG_IPV6_IPSEC
+ if (action != IPSEC_ACTION_DROP) {
+ if (np->opt && np->opt->auth) {
+ action &= ~IPSEC_ACTION_AUTH;
+ } else {
+ tp->ext_header_len += ipsec6_out_get_hdrsize(policy_ptr);
+ }
+ }
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif /* CONFIG_IPv6_IPSEC */
+ }
tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr);
inet->dport = usin->sin6_port;
@@ -1385,10 +1405,25 @@
}
newtp->ext_header_len = 0;
- if (newnp->opt)
- newtp->ext_header_len = newnp->opt->opt_nflen +
- newnp->opt->opt_flen;
-
+ {
+#ifdef CONFIG_IPV6_IPSEC
+ struct ipsec_sp *policy_ptr = NULL;
+ int action = ipsec6_output_check(sk, &fl, NULL, &policy_ptr);
+#endif /* CONFIG_IPV6_IPSEC */
+ if (newnp->opt)
+ newtp->ext_header_len = newnp->opt->opt_nflen +
+ newnp->opt->opt_flen;
+#ifdef CONFIG_IPV6_IPSEC
+ if (action != IPSEC_ACTION_DROP) {
+ if (np->opt && np->opt->auth) {
+ action &= ~IPSEC_ACTION_AUTH;
+ } else {
+ newtp->ext_header_len += ipsec6_out_get_hdrsize(policy_ptr);
+ }
+ }
+ ipsec6_out_finish(NULL, policy_ptr);
+#endif /* CONFIG_IPV6_IPSEC */
+ }
tcp_sync_mss(newsk, dst->pmtu);
newtp->advmss = dst->advmss;
tcp_initialize_rcv_mss(newsk);
diff -uNr -x CVS linux-2.5.43/net/key/Config.help linux25.43-ipsec/net/key/Config.help
--- linux-2.5.43/net/key/Config.help 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/Config.help 2002-10-16 15:27:50.000000000 +0900
@@ -0,0 +1,27 @@
+CONFIG_IPSEC
+
+ This option enables (extended) PF_KEY(v2) support, which manages
+ Security Association and Security Policy for IPsec (and IPComp).
+ Say Y here if you want to enable IPsec.
+ Note: Since IPsec is mandatory feature of IPv6, you probably want
+ to say Y here when you enable IPv6.
+
+ You also need to say Y to Cryptographic API support, ciphers,
+ digest and IPsec for each IP versions, you need.
+
+CONFIG_IPSEC_DEBUG
+
+ Say Y here if you want to get additional messages useful for
+ debugging the IPsec (IPsec6, PFKEY, SADB, SPD) code.
+
+ You can enable/disable these parameters via sysctl(/proc/net/ipsec/).
+
+CONFIG_IPSEC_DEBUG_DISABLE_DEFAULT
+
+ Normally IPsec debugging messages are activated by default,
+ if you set CONFIG_IPSEC_DEBUG.
+
+ IPsec debugging messages are activated by default and you will receive
+ tons of log messages if you enable "IPsec Debug messages" option
+ (CONFIG_IPSEC_DEBUG). This option turns these debug messages off
+ by default (at the boot time).
diff -uNr -x CVS linux-2.5.43/net/key/Config.in linux25.43-ipsec/net/key/Config.in
--- linux-2.5.43/net/key/Config.in 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/Config.in 2002-10-16 15:27:50.000000000 +0900
@@ -0,0 +1,13 @@
+#comment ' PF_KEY_V2 Configuration'
+
+if [ "$CONFIG_SYSCTL" = "y" ] ; then
+ if [ "$CONFIG_IPSEC" != "n" ]; then
+ bool ' IPsec: IPsec Debug messages' CONFIG_IPSEC_DEBUG
+ if [ "$CONFIG_IPSEC_DEBUG" = "y" ] ; then
+ bool ' IPsec: IPsec debugging off by default' CONFIG_IPSEC_DEBUG_DISABLE_DEFAULT
+ fi
+ fi
+fi
+define_bool CONFIG_IPSEC_TUNNEL y
+
+#endmenu
diff -uNr -x CVS linux-2.5.43/net/key/Makefile linux25.43-ipsec/net/key/Makefile
--- linux-2.5.43/net/key/Makefile 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/Makefile 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,17 @@
+#
+# Makefile for the IPsec basic part (PF_KEY_V2)
+#
+# $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $
+
+export-objs := key.o
+
+obj-y := af_key.o pfkey_v2_build.o pfkey_v2_ext_bits.o \
+ pfkey_v2_msg.o pfkey_v2_msg_add.o pfkey_v2_msg_delete.o pfkey_v2_msg_get.o \
+ pfkey_v2_msg_getspi.o pfkey_v2_msg_update.o pfkey_v2_msg_flow.o \
+ sa_index.o sadb.o spd.o sockaddr_utils.o
+
+
+obj-$(CONFIG_SYSCTL) += sysctl_net_ipsec.o
+
+EXTRA_CFLAGS+=-I./
+include $(TOPDIR)/Rules.make
diff -uNr -x CVS linux-2.5.43/net/key/af_key.c linux25.43-ipsec/net/key/af_key.c
--- linux-2.5.43/net/key/af_key.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/af_key.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,884 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * This file derived from FreeS/WAN-1.9 pfkey_v2.c.
+ * But changed a lot from the origin.
+ *
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ * Kazunori Miyazawa <
[email protected]> / USAGI
+ *
+ * Copyright (C)2001 USAGI/WIDE Project
+ */
+/*
+ * RFC2367 PF_KEYv2 Key management API domain socket I/F
+ * Copyright (C) 1999, 2000 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <
http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ */
+
+/*
+ * Template from /usr/src/linux-2.0.36/net/unix/af_unix.c.
+ * Hints from /usr/src/linux-2.0.36/net/ipv4/udp.c.
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/major.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/un.h>
+#include <linux/fcntl.h>
+#include <linux/termios.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/net.h> /* struct socket */
+#include <linux/in.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <asm/segment.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/sock.h> /* struct sock */
+#include <net/af_unix.h>
+#include <linux/spinlock.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif /* CONFIG_PROC_FS */
+
+#include <linux/types.h>
+
+#include <asm/uaccess.h>
+#include <linux/in6.h>
+
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/spd.h>
+
+#include <linux/ipsec.h>
+
+#include "pfkey_v2_msg.h"
+
+
+struct proto_ops SOCKOPS_WRAPPED(pfkey_ops);
+struct sock *pfkey_sock_list = NULL;
+
+struct socket_list *pfkey_open_sockets = NULL;
+struct socket_list *pfkey_registered_sockets[SADB_SATYPE_MAX+1];
+rwlock_t pfkey_sk_lock = RW_LOCK_UNLOCKED;
+
+#ifdef CONFIG_SYSCTL
+extern void ipsec_sysctl_register(void);
+extern void ipsec_sysctl_unregister(void);
+#endif /* CONFIG_SYSCTL */
+
+/* derived from FreeS/WAN-1.9 pfkey_v2_parse.c (mk) */
+int
+pfkey_msg_interp(struct sock *sk, struct sadb_msg *pfkey_msg)
+{
+ int error = 0;
+ struct sadb_msg *pfkey_reply = NULL;
+ struct sadb_ext *reply_ext_msgs[SADB_EXT_MAX+1];
+ struct socket_list *pfkey_socketsp = 0;
+
+ if (pfkey_msg->sadb_msg_satype > SADB_SATYPE_MAX) {
+ return -EINVAL;
+ }
+
+ memset(reply_ext_msgs, 0, SADB_EXT_MAX+1);
+
+ switch (pfkey_msg->sadb_msg_type) {
+ case SADB_GETSPI:
+ error = sadb_msg_add_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_GETSPI\n");
+ break;
+ case SADB_UPDATE:
+ error = -EINVAL;/* XXX */
+ PFKEY_DEBUG("PF_KEY:interp: SADB_UPDATE\n");
+ break;
+ case SADB_ADD:
+ error = sadb_msg_add_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_ADD\n");
+ break;
+ case SADB_DELETE:
+ error = sadb_msg_delete_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_DELETE\n");
+ break;
+ case SADB_FLUSH:
+ error = sadb_msg_flush_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_FLUSH\n");
+ break;
+ case SADB_REGISTER:
+ error = sadb_msg_register_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_REGISTER\n");
+ break;
+ case SADB_X_GRPSA:
+ error = -EINVAL;/* XXX */
+ PFKEY_DEBUG("PF_KEY:interp: SADB_X_GRPSA\n");
+ break;
+ case SADB_X_ADDFLOW:
+ error = sadb_msg_addflow_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_ADDFLOW\n");
+ break;
+ case SADB_X_DELFLOW:
+ error = sadb_msg_delflow_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PF_KEY:interp: SADB_DELFLOW\n");
+ break;
+
+ case SADB_X_FLUSH_SP:
+ error = sadb_msg_flush_sp_parse(sk, pfkey_msg, &pfkey_reply);
+ PFKEY_DEBUG("PFKEY:interp: SADB_X_FLUSH_SP\n");
+ break;
+ default:
+ error = -EINVAL;
+ break;
+ }
+
+ if (error) {
+ PFKEY_DEBUG("PFKEY:interp: parse routine return error=%d\n", error);
+ goto err;
+ }
+
+ switch (pfkey_msg->sadb_msg_type) {
+
+ case SADB_GETSPI:
+ case SADB_UPDATE:
+ case SADB_ADD:
+ case SADB_DELETE:
+ case SADB_FLUSH:
+ case SADB_X_ADDFLOW:
+ case SADB_X_DELFLOW:
+ case SADB_X_FLUSH_SP:
+ write_lock_bh(&pfkey_sk_lock);
+ for (pfkey_socketsp = pfkey_open_sockets;
+ pfkey_socketsp;
+ pfkey_socketsp = pfkey_socketsp->next)
+ {
+ pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply);
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+ break;
+ case SADB_GET:
+ case SADB_DUMP:
+ case SADB_REGISTER:
+ write_lock_bh(&pfkey_sk_lock);
+ pfkey_upmsg(sk->socket, pfkey_reply);
+ write_unlock_bh(&pfkey_sk_lock);
+ break;
+ case SADB_ACQUIRE:
+ write_lock_bh(&pfkey_sk_lock);
+ for (pfkey_socketsp = pfkey_registered_sockets[pfkey_msg->sadb_msg_satype];
+ pfkey_socketsp;
+ pfkey_socketsp = pfkey_socketsp->next)
+ {
+ pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply);
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+ break;
+ default:
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (pfkey_reply)
+ kfree(pfkey_reply);
+
+ return 0;
+err:
+ if (pfkey_reply)
+ kfree(pfkey_reply);
+
+ pfkey_reply = kmalloc(sizeof(struct sadb_msg), GFP_KERNEL);
+ if (!pfkey_reply){
+ return -ENOMEM;
+ }
+ memcpy(pfkey_reply, pfkey_msg, sizeof(pfkey_reply));
+ pfkey_reply->sadb_msg_errno = error;
+
+ write_lock_bh(&pfkey_sk_lock);
+ pfkey_upmsg(sk->socket, pfkey_reply);
+ for (pfkey_socketsp = pfkey_registered_sockets[pfkey_msg->sadb_msg_satype];
+ pfkey_socketsp;
+ pfkey_socketsp = pfkey_socketsp->next)
+ {
+ pfkey_upmsg(pfkey_socketsp->socketp, pfkey_reply);
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+
+ if (pfkey_reply) {
+ kfree(pfkey_reply);
+ }
+
+ return error;
+}
+
+int
+pfkey_list_remove_socket(struct socket *socketp, struct socket_list **sockets)
+{
+ struct socket_list *socket_listp,*prev;
+
+ PFKEY_DEBUG("called\n");
+ if (!socketp) {
+ return -EINVAL;
+ }
+
+ if (!sockets) {
+ return -EINVAL;
+ }
+
+ socket_listp = *sockets;
+ prev = NULL;
+
+ while (socket_listp != NULL) {
+ if (socket_listp->socketp == socketp) {
+ if (prev != NULL) {
+ prev->next = socket_listp->next;
+ } else {
+ *sockets = socket_listp->next;
+ }
+
+ kfree((void*)socket_listp);
+ PFKEY_DEBUG("removed sock=%p\n", socketp);
+
+ break;
+ }
+ prev = socket_listp;
+ socket_listp = socket_listp->next;
+ }
+ PFKEY_DEBUG("end\n");
+
+ return 0;
+}
+
+int
+pfkey_list_insert_socket(struct socket *socketp, struct socket_list **sockets)
+{
+ struct socket_list *socket_listp;
+
+ PFKEY_DEBUG("called\n");
+ if (!socketp) {
+ return -EINVAL;
+ }
+
+ if (!sockets) {
+ return -EINVAL;
+ }
+
+ if ((socket_listp = (struct socket_list *)kmalloc(sizeof(struct socket_list), GFP_KERNEL)) == NULL) {
+ return -ENOMEM;
+ }
+
+ socket_listp->socketp = socketp;
+ socket_listp->next = *sockets;
+ *sockets = socket_listp;
+ PFKEY_DEBUG("inserted sock=%p\n", socketp);
+ PFKEY_DEBUG("end\n");
+
+ return 0;
+}
+
+static void
+pfkey_insert_socket(struct sock *sk)
+{
+ PFKEY_DEBUG("called\n");
+ write_lock_bh(&pfkey_sk_lock);
+ sk->next=pfkey_sock_list;
+ pfkey_sock_list=sk;
+ write_unlock_bh(&pfkey_sk_lock);
+ PFKEY_DEBUG("end\n");
+}
+
+static void
+pfkey_remove_socket(struct sock *sk)
+{
+ struct sock **s;
+
+ PFKEY_DEBUG("called\n");
+ s=&pfkey_sock_list;
+
+ while (*s!=NULL) {
+ if (*s==sk) {
+ *s=sk->next;
+ sk->next=NULL;
+ goto final;
+ }
+ s=&((*s)->next);
+ }
+
+ PFKEY_DEBUG("end\n");
+final:
+ return;
+}
+
+static void
+pfkey_destroy_socket(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ PFKEY_DEBUG("called\n");
+ pfkey_remove_socket(sk);
+
+ while (sk && (skb=skb_dequeue(&sk->receive_queue)) !=NULL ) {
+#if defined(CONFIG_IPSEC_DEBUG) && defined(CONFIG_SYSCTL)
+ if (sysctl_ipsec_debug_pfkey) {
+ printk(KERN_DEBUG "pfkey_destroy_socket: pfkey_skb contents:");
+ printk(" next:%p", skb->next);
+ printk(" prev:%p", skb->prev);
+ printk(" list:%p", skb->list);
+ printk(" sk:%p", skb->sk);
+ printk(" stamp:%ld.%ld", skb->stamp.tv_sec, skb->stamp.tv_usec);
+ printk(" dev:%p", skb->dev);
+ if (skb->dev) {
+ if (skb->dev->name) {
+ printk(" dev->name:%s", skb->dev->name);
+ } else {
+ printk(" dev->name:NULL?");
+ }
+ } else {
+ printk(" dev:NULL");
+ }
+ printk(" h:%p", skb->h.raw);
+ printk(" nh:%p", skb->nh.raw);
+ printk(" mac:%p", skb->mac.raw);
+ printk(" dst:%p", skb->dst);
+ {
+ int i;
+
+ printk(" cb");
+ for (i=0; i<48; i++) {
+ printk(":%2x", skb->cb[i]);
+ }
+ }
+ printk(" len:%d", skb->len);
+ printk(" csum:%d", skb->csum);
+ printk(" cloned:%d", skb->cloned);
+ printk(" pkt_type:%d", skb->pkt_type);
+ printk(" ip_summed:%d", skb->ip_summed);
+ printk(" priority:%d", skb->priority);
+ printk(" protocol:%d", skb->protocol);
+ printk(" security:%d", skb->security);
+ printk(" truesize:%d", skb->truesize);
+ printk(" head:%p", skb->head);
+ printk(" data:%p", skb->data);
+ printk(" tail:%p", skb->tail);
+ printk(" end:%p", skb->end);
+ {
+ unsigned int i;
+ printk(" data");
+ for (i=(unsigned int)(skb->head); i<(unsigned int)(skb->end); i++) {
+ printk(":%2x", (unsigned char)(*(char*)(i)));
+ }
+ }
+ printk(" destructor:%p", skb->destructor);
+ printk("\n");
+ }
+#endif /* CONFIG_IPSEC_DEBUG and CONFIG_SYSCTL */
+ PFKEY_DEBUG("skb=%p freed.\n", skb);
+ kfree_skb(skb);
+ }
+
+ sk->dead = 1;
+ sk_free(sk);
+ PFKEY_DEBUG("end\n");
+}
+
+int
+pfkey_upmsg(struct socket *sock, struct sadb_msg *pfkey_msg)
+{
+ int error;
+ struct sk_buff * skb = NULL;
+ struct sock *sk;
+
+ PFKEY_DEBUG("called\n");
+ if (sock == NULL) {
+ return -EINVAL;
+ }
+
+ if (pfkey_msg == NULL) {
+ return -EINVAL;
+ }
+
+ sk = sock->sk;
+
+ if (sk == NULL) {
+ return -EINVAL;
+ }
+
+ if (!(skb = alloc_skb(pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC) )) {
+ return -ENOBUFS;
+ }
+
+ skb->dev = NULL;
+
+ if (skb_tailroom(skb) < pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
+ printk(KERN_WARNING "klips_error:pfkey_upmsg: "
+ "tried to skb_put %ld, %d available. This should never happen, please report.\n",
+ (unsigned long int)pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN,
+ skb_tailroom(skb));
+ kfree_skb(skb);
+
+ return -ENOBUFS;
+ }
+ skb->h.raw = skb_put(skb, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
+ memcpy(skb->h.raw, pfkey_msg, pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
+
+ if ((error = sock_queue_rcv_skb(sk, skb)) < 0) {
+ skb->sk=NULL;
+ kfree_skb(skb);
+ return error;
+ }
+ PFKEY_DEBUG("end\n");
+ return 0;
+}
+
+static int
+pfkey_create(struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ PFKEY_DEBUG("called\n");
+ if (sock == NULL) {
+ return -EINVAL;
+ }
+
+ if (sock->type != SOCK_RAW) {
+ return -ESOCKTNOSUPPORT;
+ }
+
+ if (protocol != PF_KEY_V2) {
+ return -EPROTONOSUPPORT;
+ }
+
+ if ((current->uid != 0)) {
+ return -EACCES;
+ }
+
+ sock->state = SS_UNCONNECTED;
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif /* MODULE */
+
+ if ((sk=(struct sock *)sk_alloc(PF_KEY, GFP_KERNEL, 1, NULL)) == NULL)
+ {
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif /* MODULE */
+ return -ENOMEM;
+ }
+
+ sock_init_data(sock, sk);
+
+ sk->destruct = NULL;
+ sk->reuse = 1;
+ sock->ops = &SOCKOPS_WRAPPED(pfkey_ops);
+
+ sk->zapped=0;
+ sk->family = PF_KEY;
+ sk->protocol = protocol;
+ key_pid(sk) = current->pid;
+
+ pfkey_insert_socket(sk);
+ pfkey_list_insert_socket(sock, &pfkey_open_sockets);
+
+ PFKEY_DEBUG("end\n");
+ return 0;
+}
+
+static int
+pfkey_release(struct socket *sock)
+{
+ struct sock *sk;
+ int i;
+
+ PFKEY_DEBUG("called\n");
+ if (sock==NULL) {
+ return 0; /* -EINVAL; */
+ }
+
+ sk=sock->sk;
+
+ /* May not have data attached */
+ if (sk==NULL) {
+ return 0; /* -EINVAL; */
+ }
+
+ if (!sk->dead)
+ if (sk->state_change) {
+ sk->state_change(sk);
+ }
+
+ sock->sk = NULL;
+
+ /* Try to flush out this socket. Throw out buffers at least */
+ write_lock_bh(&pfkey_sk_lock);
+ pfkey_destroy_socket(sk);
+ pfkey_list_remove_socket(sock, &pfkey_open_sockets);
+ for (i = SADB_SATYPE_UNSPEC; i <= SADB_SATYPE_MAX; i++) {
+ pfkey_list_remove_socket(sock, &pfkey_registered_sockets[i]);
+ PFKEY_DEBUG("socket=%p is released, SA type is %d\n", sock, i);
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif /* MODULE */
+
+ PFKEY_DEBUG("called\n");
+ return 0;
+}
+
+static int
+pfkey_shutdown(struct socket *sock, int mode)
+{
+ struct sock *sk;
+
+ PFKEY_DEBUG("called\n");
+ if (sock == NULL) {
+ return -EINVAL;
+ }
+
+ sk=sock->sk;
+
+ if (sk == NULL) {
+ return -EINVAL;
+ }
+
+ mode++;
+
+ if (mode&SEND_SHUTDOWN) {
+ sk->shutdown|=SEND_SHUTDOWN;
+ sk->state_change(sk);
+ }
+
+ if (mode&RCV_SHUTDOWN) {
+ sk->shutdown|=RCV_SHUTDOWN;
+ sk->state_change(sk);
+ }
+ PFKEY_DEBUG("end\n");
+ return 0;
+}
+
+/*
+ * Send PF_KEY data down.
+ */
+
+static int
+pfkey_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+{
+ struct sock *sk;
+ int error = 0;
+ struct sadb_msg *pfkey_msg = NULL;
+
+ PFKEY_DEBUG("called\n");
+ if (sock == NULL) return -EINVAL;
+
+ sk = sock->sk;
+
+ if (sk == NULL) return -EINVAL;
+
+ if (msg == NULL) return -EINVAL;
+
+ if (sk->err) return sock_error(sk);
+
+ if ((current->uid != 0)) return -EACCES;
+
+ if (msg->msg_control) return -EINVAL;
+
+ if (sk->shutdown & SEND_SHUTDOWN) {
+ send_sig(SIGPIPE, current, 0);
+ return -EPIPE;
+ }
+
+ if (len < sizeof(struct sadb_msg)) return -EMSGSIZE;
+
+ if ((pfkey_msg = (struct sadb_msg*)kmalloc(len, GFP_KERNEL)) == NULL)
+ return -ENOBUFS;
+
+ memcpy_fromiovec((void *)pfkey_msg, msg->msg_iov, len);
+
+ if (pfkey_msg->sadb_msg_version != PF_KEY_V2) {
+ kfree((void*)pfkey_msg);
+ return -EINVAL;
+ }
+
+ if (len != pfkey_msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) {
+ kfree((void *)pfkey_msg);
+ return -EMSGSIZE;
+ }
+
+ if (pfkey_msg->sadb_msg_reserved) {
+ kfree((void *)pfkey_msg);
+ return -EINVAL;
+ }
+
+ if ((pfkey_msg->sadb_msg_type > SADB_MAX) || (!pfkey_msg->sadb_msg_type)) {
+ kfree((void *)pfkey_msg);
+ return -EINVAL;
+ }
+
+ error = pfkey_msg_interp(sk, pfkey_msg);
+ if (error) {
+ kfree((void *)pfkey_msg);
+ return -error;
+ }
+ PFKEY_DEBUG("end\n");
+
+ return len;
+}
+
+/*
+ * Receive PF_KEY data up.
+ */
+
+static int
+pfkey_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
+{
+ struct sock *sk;
+ int noblock = flags & MSG_DONTWAIT;
+ struct sk_buff *skb;
+ int error;
+
+ PFKEY_DEBUG("called\n");
+ if (sock == NULL) return -EINVAL;
+
+ sk = sock->sk;
+
+ if (sk == NULL) return -EINVAL;
+
+ if (msg == NULL) return -EINVAL;
+
+ if (flags & ~MSG_PEEK) return -EOPNOTSUPP;
+
+ msg->msg_namelen = 0; /* sizeof(*ska); */
+
+ if (sk->err) return sock_error(sk);
+
+ if ((skb = skb_recv_datagram(sk, flags, noblock, &error) ) == NULL)
+ return error;
+
+ if (size > skb->len)
+ size = skb->len;
+
+ skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size);
+ sk->stamp=skb->stamp;
+
+ skb_free_datagram(sk, skb);
+ PFKEY_DEBUG("end\n");
+ return size;
+}
+
+struct net_proto_family pfkey_family_ops = {
+ PF_KEY,
+ pfkey_create
+};
+
+struct proto_ops SOCKOPS_WRAPPED(pfkey_ops) = {
+ family: PF_KEY,
+ release: pfkey_release,
+ bind: sock_no_bind,
+ connect: sock_no_connect,
+ socketpair: sock_no_socketpair,
+ accept: sock_no_accept,
+ getname: sock_no_getname,
+ poll: datagram_poll,
+ ioctl: sock_no_ioctl,
+ listen: sock_no_listen,
+ shutdown: pfkey_shutdown,
+ setsockopt: sock_no_setsockopt,
+ getsockopt: sock_no_getsockopt,
+ sendmsg: pfkey_sendmsg,
+ recvmsg: pfkey_recvmsg,
+ mmap: sock_no_mmap,
+};
+
+#include <linux/smp_lock.h>
+
+#ifdef CONFIG_PROC_FS
+int
+pfkey_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ off_t pos=0;
+ off_t begin=0;
+ int len=0;
+ struct sock *sk=pfkey_sock_list;
+
+ len+= sprintf(buffer,
+ " sock pid socket next prev e n p sndbf Flags Type St\n");
+
+ while (sk!=NULL) {
+ len+=sprintf(buffer+len,
+ "%8p %5d %8p %8p %8p %d %d %5d %08lX %8X %2X\n",
+ sk,
+ key_pid(sk),
+ sk->socket,
+ sk->next,
+ sk->prev,
+ sk->err,
+ sk->protocol,
+ sk->sndbuf,
+ sk->socket->flags,
+ sk->socket->type,
+ sk->socket->state);
+ pos=begin+len;
+ if (pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if (pos>offset+length)
+ break;
+ sk=sk->next;
+ }
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if (len>length)
+ len=length;
+ return len;
+}
+
+int
+pfkey_registered_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ off_t pos=0;
+ off_t begin=0;
+ int len=0;
+ int satype;
+ struct socket_list *pfkey_sockets;
+
+ len+= sprintf(buffer,
+ "satype socket pid sk\n");
+
+ for (satype = SADB_SATYPE_UNSPEC; satype <= SADB_SATYPE_MAX; satype++) {
+ pfkey_sockets = pfkey_registered_sockets[satype];
+ while (pfkey_sockets) {
+ len+=sprintf(buffer+len,
+ " %2d %8p %5d %8p\n",
+ satype,
+ pfkey_sockets->socketp,
+ key_pid(pfkey_sockets->socketp->sk),
+ pfkey_sockets->socketp->sk);
+
+ pos=begin+len;
+ if (pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if (pos>offset+length)
+ break;
+ pfkey_sockets = pfkey_sockets->next;
+ }
+ }
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if (len>length)
+ len=length;
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+int
+pfkey_init(void)
+{
+ int error = 0;
+
+ sock_register(&pfkey_family_ops);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_create ("pf_key", 0, pfkey_get_info);
+ proc_net_create ("pf_key_registered", 0, pfkey_registered_get_info);
+#endif /* CONFIG_PROC_FS */
+
+
+ error = sadb_init();
+ if (error) {
+ PFKEY_DEBUG("sadb_init failed\n");
+ goto err;
+ }
+ error = spd_init();
+ if (error) {
+ PFKEY_DEBUG("spd_init faild\n");
+ goto err;
+ }
+#ifdef CONFIG_SYSCTL
+ ipsec_sysctl_register();
+#endif /* CONFIG_SYSCTL */
+
+ printk(KERN_INFO "IPsec PF_KEY V2: initialized\n");
+
+err:
+ return error;
+}
+
+int
+pfkey_cleanup(void)
+{
+ int error = 0;
+
+ error = spd_cleanup();
+ if (error) {
+ PFKEY_DEBUG("spd_cleanup failed\n");
+ goto err;
+ }
+ error = sadb_cleanup();
+ if (error) {
+ PFKEY_DEBUG("sadb_cleanup failed\n");
+ goto err;
+ }
+
+ printk(KERN_INFO "pfkey_cleanup: shutting down PF_KEY domain sockets.\n");
+
+ sock_unregister(PF_KEY);
+
+
+#ifdef CONFIG_PROC_FS
+ proc_net_remove ("pf_key");
+ proc_net_remove ("pf_key_registered");
+#endif /* CONFIG_PROC_FS */
+
+#ifdef CONFIG_SYSCTL
+ ipsec_sysctl_unregister();
+#endif
+
+ /* other module unloading cleanup happens here */
+err:
+ return error;
+}
+
+/* XXX: currently not available as a module */
+#ifdef MODULE
+static int __init pfkey_module_init(void)
+{
+ int err = pfkey_init();
+ return err;
+}
+
+static void __exit pfkey_module_cleanup(void)
+{
+ pfkey_cleanup();
+}
+
+module_init(pfkey_module_init);
+module_exit(pfkey_module_cleanup);
+
+#else /* MODULE */
+void
+pfkey_proto_init(struct net_proto *pro)
+{
+ pfkey_init();
+}
+#endif /* MODULE */
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_build.c linux25.43-ipsec/net/key/pfkey_v2_build.c
--- linux-2.5.43/net/key/pfkey_v2_build.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_build.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,915 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/* this file was derived from FreeS/WAN-1.9. (changed a little) */
+/*
+ * RFC2367 PF_KEYv2 Key management API message parser
+ * Copyright (C) 1999 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <
http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ */
+
+/*
+ * Template from klips/net/ipsec/ipsec/ipsec_parser.c.
+ */
+
+
+/*
+ * Some ugly stuff to allow consistent debugging code for use in the
+ * kernel and in user space
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/sysctl.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <linux/ipv6.h>
+#endif
+#include <linux/inet.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+#include "sockaddr_utils.h"
+
+#define ADDRTOT_BUF 128
+
+#define SENDERR(_x) do { error = -(_x); goto errlab; } while (0)
+
+void
+pfkey_extensions_init(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
+{
+ int i;
+
+ for (i = 0; i != SADB_EXT_MAX + 1; i++) {
+ extensions[i] = NULL;
+ }
+}
+
+void
+pfkey_extensions_free(struct sadb_ext *extensions[SADB_EXT_MAX + 1])
+{
+ int i;
+
+ if (!extensions) {
+ return;
+ }
+
+ if (extensions[0]) {
+ memset(extensions[0], 0, sizeof(struct sadb_msg));
+ kfree(extensions[0]);
+ extensions[0] = NULL;
+ }
+
+ for (i = 1; i != SADB_EXT_MAX + 1; i++) {
+ if (extensions[i]) {
+ memset(extensions[i], 0, extensions[i]->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
+ kfree(extensions[i]);
+ extensions[i] = NULL;
+ }
+ }
+}
+
+void
+pfkey_msg_free(struct sadb_msg **pfkey_msg)
+{
+ if (*pfkey_msg) {
+ memset(*pfkey_msg, 0, (*pfkey_msg)->sadb_msg_len * IPSEC_PFKEYv2_ALIGN);
+ kfree(*pfkey_msg);
+ *pfkey_msg = NULL;
+ }
+}
+
+/* Default extension builders taken from the KLIPS code */
+
+int
+pfkey_msg_hdr_build(struct sadb_ext** pfkey_ext,
+ uint8_t msg_type,
+ uint8_t satype,
+ uint8_t msg_errno,
+ uint32_t seq,
+ uint32_t pid)
+{
+ int error = 0;
+ struct sadb_msg *pfkey_msg = (struct sadb_msg *)*pfkey_ext;
+
+ PFKEY_DEBUG("called\n");
+ PFKEY_DEBUG("on_entry &pfkey_ext=%p pfkey_ext=%p *pfkey_ext=%p.\n",
+ &pfkey_ext, pfkey_ext, *pfkey_ext);
+ /* sanity checks... */
+ if (pfkey_msg) {
+ PFKEY_DEBUG("why is pfkey_msg already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (!msg_type) {
+ PFKEY_DEBUG("msg type not set, must be non-zero.\n");
+ SENDERR(EINVAL);
+ }
+
+ if (msg_type > SADB_MAX) {
+ PFKEY_DEBUG("msg type too large:%d.\n", msg_type);
+ SENDERR(EINVAL);
+ }
+
+ if (satype > SADB_SATYPE_MAX) {
+ PFKEY_DEBUG("satype %d > max %d\n", satype, SADB_SATYPE_MAX);
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_msg = (struct sadb_msg*)
+ kmalloc(sizeof(struct sadb_msg), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_msg, 0, sizeof(struct sadb_msg));
+
+ pfkey_msg->sadb_msg_len = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
+
+ pfkey_msg->sadb_msg_type = msg_type;
+ pfkey_msg->sadb_msg_satype = satype;
+
+ pfkey_msg->sadb_msg_version = PF_KEY_V2;
+ pfkey_msg->sadb_msg_errno = msg_errno;
+ pfkey_msg->sadb_msg_reserved = 0;
+ pfkey_msg->sadb_msg_seq = seq;
+ pfkey_msg->sadb_msg_pid = pid;
+ PFKEY_DEBUG("on_exit &pfkey_ext=%p pfkey_ext=%p *pfkey_ext=%p.\n",
+ &pfkey_ext, pfkey_ext, *pfkey_ext);
+errlab:
+ return error;
+}
+
+int
+pfkey_sa_build(struct sadb_ext ** pfkey_ext,
+ uint16_t exttype,
+ uint32_t spi, /* in network order */
+ uint8_t replay_window,
+ uint8_t sa_state,
+ uint8_t auth,
+ uint8_t encrypt,
+ uint32_t flags)
+{
+ int error = 0;
+ struct sadb_sa *pfkey_sa = (struct sadb_sa *)*pfkey_ext;
+
+ PFKEY_DEBUG("spi=%08x replay=%d sa_state=%d auth=%d encrypt=%d flags=%d\n",
+ ntohl(spi), replay_window, sa_state, auth, encrypt, flags);
+ /* sanity checks... */
+ if (pfkey_sa) {
+ PFKEY_DEBUG("why is pfkey_sa already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (exttype != SADB_EXT_SA &&
+ exttype != SADB_X_EXT_SA2) {
+ PFKEY_DEBUG("invalid exttype=%d.\n", exttype);
+ SENDERR(EINVAL);
+ }
+
+ if (replay_window > 64) {
+ PFKEY_DEBUG("replay window size: %d -- must be 0 <= size <= 64\n",
+ replay_window);
+ SENDERR(EINVAL);
+ }
+
+ if (auth > SADB_AALG_MAX) {
+ PFKEY_DEBUG("auth=%d > SADB_AALG_MAX=%d.\n", auth, SADB_AALG_MAX);
+ SENDERR(EINVAL);
+ }
+
+ if (encrypt > SADB_EALG_MAX) {
+ PFKEY_DEBUG("encrypt=%d > SADB_EALG_MAX=%d.\n", encrypt, SADB_EALG_MAX);
+ SENDERR(EINVAL);
+ }
+
+ if (sa_state > SADB_SASTATE_MAX) {
+ PFKEY_DEBUG("sa_state=%d exceeds MAX=%d.\n", sa_state, SADB_SASTATE_MAX);
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_sa = (struct sadb_sa*)
+ kmalloc(sizeof(struct sadb_sa), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_sa, 0, sizeof(struct sadb_sa));
+
+ pfkey_sa->sadb_sa_len = sizeof(*pfkey_sa) / IPSEC_PFKEYv2_ALIGN;
+ pfkey_sa->sadb_sa_exttype = exttype;
+ pfkey_sa->sadb_sa_spi = spi;
+ pfkey_sa->sadb_sa_replay = replay_window;
+ pfkey_sa->sadb_sa_state = sa_state;
+ pfkey_sa->sadb_sa_auth = auth;
+ pfkey_sa->sadb_sa_encrypt = encrypt;
+ pfkey_sa->sadb_sa_flags = flags;
+
+errlab:
+ return error;
+}
+
+int
+pfkey_lifetime_build(struct sadb_ext ** pfkey_ext,
+ uint16_t exttype,
+ uint32_t allocations,
+ uint64_t bytes,
+ uint64_t addtime,
+ uint64_t usetime)
+{
+ int error = 0;
+ struct sadb_lifetime *pfkey_lifetime = (struct sadb_lifetime *)*pfkey_ext;
+
+ PFKEY_DEBUG(
+ "pfkey_lifetime_build:\n");
+ /* sanity checks... */
+ if (pfkey_lifetime) {
+ PFKEY_DEBUG("why is pfkey_lifetime already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (exttype != SADB_EXT_LIFETIME_CURRENT &&
+ exttype != SADB_EXT_LIFETIME_HARD &&
+ exttype != SADB_EXT_LIFETIME_SOFT) {
+ PFKEY_DEBUG("invalid exttype=%d.\n", exttype);
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_lifetime = (struct sadb_lifetime*)
+ kmalloc(sizeof(struct sadb_lifetime), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_lifetime, 0, sizeof(struct sadb_lifetime));
+
+ pfkey_lifetime->sadb_lifetime_len = sizeof(struct sadb_lifetime) / IPSEC_PFKEYv2_ALIGN;
+ pfkey_lifetime->sadb_lifetime_exttype = exttype;
+ pfkey_lifetime->sadb_lifetime_allocations = allocations;
+ pfkey_lifetime->sadb_lifetime_bytes = bytes;
+ pfkey_lifetime->sadb_lifetime_addtime = addtime;
+ pfkey_lifetime->sadb_lifetime_usetime = usetime;
+
+errlab:
+ return error;
+}
+
+int
+pfkey_address_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint8_t proto,
+ uint8_t prefixlen,
+ struct sockaddr* address)
+{
+ int error = 0;
+ int saddr_len = 0;
+ char ipaddr_txt[ADDRTOT_BUF];
+ struct sadb_address *pfkey_address = (struct sadb_address *)*pfkey_ext;
+
+ PFKEY_DEBUG(
+ "pfkey_address_build: exttype=%d proto=%d prefixlen=%d\n", exttype, proto, prefixlen);
+ /* sanity checks... */
+ if (pfkey_address) {
+ PFKEY_DEBUG("why is pfkey_address already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (!address) {
+ PFKEY_DEBUG("address is NULL\n");
+ SENDERR(EINVAL);
+ }
+
+ switch (exttype) {
+ case SADB_EXT_ADDRESS_SRC:
+ case SADB_EXT_ADDRESS_DST:
+ case SADB_EXT_ADDRESS_PROXY:
+ break;
+ default:
+ PFKEY_DEBUG("unrecognised ext_type=%d.\n",
+ exttype);
+ SENDERR(EINVAL);
+ }
+
+ switch (address->sa_family) {
+ case AF_INET:
+ PFKEY_DEBUG("found address family AF_INET.\n");
+ saddr_len = sizeof(struct sockaddr_in);
+ sockaddrtoa(address, ipaddr_txt, sizeof(ipaddr_txt));
+ break;
+ case AF_INET6:
+ PFKEY_DEBUG("found address family AF_INET6.\n");
+ saddr_len = sizeof(struct sockaddr_in6);
+ sockaddrtoa(address, ipaddr_txt, sizeof(ipaddr_txt));
+ break;
+ default:
+ PFKEY_DEBUG("address->sa_family=%d not supported.\n",
+ address->sa_family);
+ SENDERR(EPFNOSUPPORT);
+ }
+
+ PFKEY_DEBUG("found address=%s.\n", ipaddr_txt);
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_address = (struct sadb_address*)
+ kmalloc(ALIGN_N(sizeof(struct sadb_address) + saddr_len, IPSEC_PFKEYv2_ALIGN), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_address,
+ 0,
+ ALIGN_N(sizeof(struct sadb_address) + saddr_len,
+ IPSEC_PFKEYv2_ALIGN));
+
+ pfkey_address->sadb_address_len = DIVUP(sizeof(struct sadb_address) + saddr_len,
+ IPSEC_PFKEYv2_ALIGN);
+
+ pfkey_address->sadb_address_exttype = exttype;
+ pfkey_address->sadb_address_proto = proto;
+ pfkey_address->sadb_address_prefixlen = prefixlen;
+ pfkey_address->sadb_address_reserved = 0;
+
+ memcpy((char*)pfkey_address + sizeof(struct sadb_address),
+ address,
+ saddr_len);
+
+ PFKEY_DEBUG("successful.\n");
+
+ errlab:
+ return error;
+}
+
+int
+pfkey_key_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint16_t key_bits,
+ char* key)
+{
+ int error = 0;
+ struct sadb_key *pfkey_key = (struct sadb_key *)*pfkey_ext;
+
+ PFKEY_DEBUG(
+ "pfkey_key_build:\n");
+ /* sanity checks... */
+ if (pfkey_key) {
+ PFKEY_DEBUG("why is pfkey_key already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (!key_bits) {
+ PFKEY_DEBUG("key_bits is zero, it must be non-zero.\n");
+ SENDERR(EINVAL);
+ }
+
+ if ( !((exttype == SADB_EXT_KEY_AUTH) || (exttype == SADB_EXT_KEY_ENCRYPT))) {
+ PFKEY_DEBUG("unsupported extension type=%d.\n", exttype);
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_key = (struct sadb_key*)
+ kmalloc(sizeof(struct sadb_key) +
+ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_key,
+ 0,
+ sizeof(struct sadb_key) +
+ DIVUP(key_bits, 64) * IPSEC_PFKEYv2_ALIGN);
+
+ pfkey_key->sadb_key_len = DIVUP(sizeof(struct sadb_key) * IPSEC_PFKEYv2_ALIGN + key_bits,
+ 64);
+ pfkey_key->sadb_key_exttype = exttype;
+ pfkey_key->sadb_key_bits = key_bits;
+ pfkey_key->sadb_key_reserved = 0;
+ memcpy((char*)pfkey_key + sizeof(struct sadb_key),
+ key,
+ DIVUP(key_bits, 8));
+
+errlab:
+ return error;
+}
+
+int
+pfkey_ident_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint16_t ident_type,
+ uint64_t ident_id,
+ char* ident_string)
+{
+ int error = 0;
+ struct sadb_ident *pfkey_ident = (struct sadb_ident *)*pfkey_ext;
+
+ PFKEY_DEBUG(
+ "pfkey_ident_build:\n");
+ /* sanity checks... */
+ if (pfkey_ident) {
+ PFKEY_DEBUG("why is pfkey_ident already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if ( ! ((exttype == SADB_EXT_IDENTITY_SRC) ||
+ (exttype == SADB_EXT_IDENTITY_DST))) {
+ PFKEY_DEBUG("unsupported extension type=%d.\n", exttype);
+ SENDERR(EINVAL);
+ }
+
+ if ((ident_type == SADB_IDENTTYPE_RESERVED)) {
+ PFKEY_DEBUG("ident_type must be non-zero.\n");
+ SENDERR(EINVAL);
+ }
+
+ if (ident_type > SADB_IDENTTYPE_MAX) {
+ PFKEY_DEBUG("identtype=%d out of range.\n", ident_type);
+ SENDERR(EINVAL);
+ }
+
+ if (((ident_type == SADB_IDENTTYPE_PREFIX) ||
+ (ident_type == SADB_IDENTTYPE_FQDN)) &&
+ !ident_string) {
+ PFKEY_DEBUG("string required to allocate size of extension.\n");
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_ident = (struct sadb_ident*)
+ kmalloc(ALIGN_N(sizeof(struct sadb_key) + strlen(ident_string), IPSEC_PFKEYv2_ALIGN), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_ident,
+ 0,
+ ALIGN_N(sizeof(struct sadb_ident) + strlen(ident_string),
+ IPSEC_PFKEYv2_ALIGN));
+
+ pfkey_ident->sadb_ident_len = DIVUP(sizeof(struct sadb_ident) + strlen(ident_string),
+ IPSEC_PFKEYv2_ALIGN);
+
+ pfkey_ident->sadb_ident_exttype = exttype;
+ pfkey_ident->sadb_ident_type = ident_type;
+ pfkey_ident->sadb_ident_reserved = 0;
+ pfkey_ident->sadb_ident_id = ident_id;
+ memcpy((char*)pfkey_ident + sizeof(struct sadb_ident),
+ ident_string,
+ strlen(ident_string));
+
+ memset(((char*)pfkey_ident) + sizeof(struct sadb_ident) + strlen(ident_string),
+ 0,
+ ALIGN_N(sizeof(struct sadb_ident) + strlen(ident_string), IPSEC_PFKEYv2_ALIGN) -
+ sizeof(struct sadb_ident) + strlen(ident_string));
+
+errlab:
+ return error;
+}
+
+int
+pfkey_sens_build(struct sadb_ext** pfkey_ext,
+ uint32_t dpd,
+ uint8_t sens_level,
+ uint8_t sens_len,
+ uint64_t* sens_bitmap,
+ uint8_t integ_level,
+ uint8_t integ_len,
+ uint64_t* integ_bitmap)
+{
+ int error = 0;
+ struct sadb_sens *pfkey_sens = (struct sadb_sens *)*pfkey_ext;
+ int i;
+ uint64_t* bitmap;
+
+ PFKEY_DEBUG(
+ "pfkey_sens_build:\n");
+ /* sanity checks... */
+ if (pfkey_sens) {
+ PFKEY_DEBUG("why is pfkey_sens already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ PFKEY_DEBUG("Sorry, I can't build exttype=%d yet.\n", (*pfkey_ext)->sadb_ext_type);
+ SENDERR(EINVAL); /* don't process these yet */
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_sens = (struct sadb_sens*)
+ kmalloc(sizeof(struct sadb_sens) +
+ (sens_len + integ_len) * sizeof(uint64_t), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_sens,
+ 0,
+ sizeof(struct sadb_sens) +
+ (sens_len + integ_len) * sizeof(uint64_t));
+
+ pfkey_sens->sadb_sens_len = (sizeof(struct sadb_sens) +
+ (sens_len + integ_len) * sizeof(uint64_t)) / IPSEC_PFKEYv2_ALIGN;
+ pfkey_sens->sadb_sens_exttype = SADB_EXT_SENSITIVITY;
+ pfkey_sens->sadb_sens_dpd = dpd;
+ pfkey_sens->sadb_sens_sens_level = sens_level;
+ pfkey_sens->sadb_sens_sens_len = sens_len;
+ pfkey_sens->sadb_sens_integ_level = integ_level;
+ pfkey_sens->sadb_sens_integ_len = integ_len;
+ pfkey_sens->sadb_sens_reserved = 0;
+
+ bitmap = (uint64_t*)((char*)pfkey_ext + sizeof(struct sadb_sens));
+ for (i = 0; i < sens_len; i++) {
+ *bitmap = sens_bitmap[i];
+ bitmap++;
+ }
+ for (i = 0; i < integ_len; i++) {
+ *bitmap = integ_bitmap[i];
+ bitmap++;
+ }
+
+errlab:
+ return error;
+}
+
+int
+pfkey_prop_build(struct sadb_ext** pfkey_ext,
+ uint8_t replay,
+ unsigned int comb_num,
+ struct sadb_comb* comb)
+{
+ int error = 0;
+ int i;
+ struct sadb_prop *pfkey_prop = (struct sadb_prop *)*pfkey_ext;
+ struct sadb_comb *combp;
+
+ PFKEY_DEBUG(
+ "pfkey_prop_build:\n");
+ /* sanity checks... */
+ if (pfkey_prop) {
+ PFKEY_DEBUG("why is pfkey_prop already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_prop = (struct sadb_prop*)
+ kmalloc(sizeof(struct sadb_prop) +
+ comb_num * sizeof(struct sadb_comb), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_prop,
+ 0,
+ sizeof(struct sadb_prop) +
+ comb_num * sizeof(struct sadb_comb));
+
+ pfkey_prop->sadb_prop_len = (sizeof(struct sadb_prop) +
+ comb_num * sizeof(struct sadb_comb)) / IPSEC_PFKEYv2_ALIGN;
+
+ pfkey_prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
+ pfkey_prop->sadb_prop_replay = replay;
+
+ for (i=0; i<3; i++) {
+ pfkey_prop->sadb_prop_reserved[i] = 0;
+ }
+
+ combp = (struct sadb_comb*)((char*)*pfkey_ext + sizeof(struct sadb_prop));
+ for (i = 0; i < comb_num; i++) {
+ memcpy (combp, &comb[i], sizeof(struct sadb_comb));
+ combp++;
+ }
+
+errlab:
+ return error;
+}
+
+int
+pfkey_supported_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ unsigned int alg_num,
+ struct sadb_alg* alg)
+{
+ int error = 0;
+ unsigned int i;
+ struct sadb_supported *pfkey_supported = (struct sadb_supported *)*pfkey_ext;
+ struct sadb_alg *pfkey_alg;
+
+ /* sanity checks... */
+ if (pfkey_supported) {
+ PFKEY_DEBUG("why is pfkey_supported already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if ( !((exttype == SADB_EXT_SUPPORTED_AUTH) || (exttype == SADB_EXT_SUPPORTED_ENCRYPT))) {
+ PFKEY_DEBUG("unsupported extension type=%d.\n", exttype);
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_supported = (struct sadb_supported*)
+ kmalloc(sizeof(struct sadb_supported) +
+ alg_num *
+ sizeof(struct sadb_alg), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_supported,
+ 0,
+ sizeof(struct sadb_supported) +
+ alg_num *
+ sizeof(struct sadb_alg));
+
+ pfkey_supported->sadb_supported_len = (sizeof(struct sadb_supported) +
+ alg_num *
+ sizeof(struct sadb_alg)) /
+ IPSEC_PFKEYv2_ALIGN;
+ pfkey_supported->sadb_supported_exttype = exttype;
+ pfkey_supported->sadb_supported_reserved = 0;
+
+ pfkey_alg = (struct sadb_alg*)((char*)pfkey_supported + sizeof(struct sadb_supported));
+ for (i = 0; i < alg_num; i++) {
+ memcpy (pfkey_alg, &alg[i], sizeof(struct sadb_alg));
+ pfkey_alg->sadb_alg_reserved = 0;
+ pfkey_alg++;
+ }
+
+errlab:
+ return error;
+}
+
+int
+pfkey_spirange_build(struct sadb_ext** pfkey_ext,
+ uint16_t exttype,
+ uint32_t min, /* in network order */
+ uint32_t max) /* in network order */
+{
+ int error = 0;
+ struct sadb_spirange *pfkey_spirange = (struct sadb_spirange *)*pfkey_ext;
+
+ /* sanity checks... */
+ if (pfkey_spirange) {
+ PFKEY_DEBUG("why is pfkey_spirange already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (ntohl(max) < ntohl(min)) {
+ PFKEY_DEBUG("minspi=%08x must be < maxspi=%08x.\n", ntohl(min), ntohl(max));
+ SENDERR(EINVAL);
+ }
+
+ if (ntohl(min) <= 255) {
+ PFKEY_DEBUG("minspi=%08x must be > 255.\n", ntohl(min));
+ SENDERR(EEXIST);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_spirange = (struct sadb_spirange*)
+ kmalloc(sizeof(struct sadb_spirange), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_spirange,
+ 0,
+ sizeof(struct sadb_spirange));
+
+ pfkey_spirange->sadb_spirange_len = sizeof(struct sadb_spirange) / IPSEC_PFKEYv2_ALIGN;
+
+ pfkey_spirange->sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+ pfkey_spirange->sadb_spirange_min = min;
+ pfkey_spirange->sadb_spirange_max = max;
+ pfkey_spirange->sadb_spirange_reserved = 0;
+ errlab:
+ return error;
+}
+
+int
+pfkey_x_kmprivate_build(struct sadb_ext** pfkey_ext)
+{
+ int error = 0;
+ struct sadb_x_kmprivate *pfkey_x_kmprivate = (struct sadb_x_kmprivate *)*pfkey_ext;
+
+ /* sanity checks... */
+ if (pfkey_x_kmprivate) {
+ PFKEY_DEBUG("why is pfkey_x_kmprivate already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0;
+
+ PFKEY_DEBUG("Sorry, I can't build exttype=%d yet.\n", (*pfkey_ext)->sadb_ext_type);
+ SENDERR(EINVAL); /* don't process these yet */
+
+ if (!(*pfkey_ext = (struct sadb_ext*)
+ pfkey_x_kmprivate = (struct sadb_x_kmprivate*)
+ kmalloc(sizeof(struct sadb_x_kmprivate), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_x_kmprivate,
+ 0,
+ sizeof(struct sadb_x_kmprivate));
+
+ pfkey_x_kmprivate->sadb_x_kmprivate_len =
+ sizeof(struct sadb_x_kmprivate) / IPSEC_PFKEYv2_ALIGN;
+
+ pfkey_x_kmprivate->sadb_x_kmprivate_exttype = SADB_X_EXT_KMPRIVATE;
+ pfkey_x_kmprivate->sadb_x_kmprivate_reserved = 0;
+errlab:
+ return error;
+}
+
+int
+pfkey_x_satype_build(struct sadb_ext** pfkey_ext,
+ uint8_t satype)
+{
+ int error = 0;
+ int i;
+ struct sadb_x_satype *pfkey_x_satype = (struct sadb_x_satype *)*pfkey_ext;
+
+ PFKEY_DEBUG("called.\n");
+ /* sanity checks... */
+ if (pfkey_x_satype) {
+ PFKEY_DEBUG("why is pfkey_x_satype already pointing to something?\n");
+ SENDERR(EINVAL);
+ }
+
+ if (!satype) {
+ PFKEY_DEBUG("SA type not set, must be non-zero.\n");
+ SENDERR(EINVAL);
+ }
+
+ if (satype > SADB_SATYPE_MAX) {
+ PFKEY_DEBUG("satype %d > max %d\n",
+ satype, SADB_SATYPE_MAX);
+ SENDERR(EINVAL);
+ }
+
+ if (!(*pfkey_ext = (struct sadb_ext*)pfkey_x_satype = (struct sadb_x_satype*)
+ kmalloc(sizeof(struct sadb_x_satype), GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+ memset(pfkey_x_satype,
+ 0,
+ sizeof(struct sadb_x_satype));
+
+ pfkey_x_satype->sadb_x_satype_len = sizeof(struct sadb_x_satype) / IPSEC_PFKEYv2_ALIGN;
+
+ pfkey_x_satype->sadb_x_satype_exttype = SADB_X_EXT_SATYPE2;
+ pfkey_x_satype->sadb_x_satype_satype = satype;
+ for (i=0; i<3; i++) {
+ pfkey_x_satype->sadb_x_satype_reserved[i] = 0;
+ }
+
+errlab:
+ return error;
+}
+
+
+int
+pfkey_msg_build(struct sadb_msg **pfkey_msg, struct sadb_ext *extensions[], int dir)
+{
+ int error = 0;
+ int ext;
+ int total_size;
+ struct sadb_ext *pfkey_ext;
+ int extensions_seen = 0;
+
+ if (!extensions[0]) {
+ PFKEY_DEBUG("extensions[0] must be specified (struct sadb_msg).\n");
+ SENDERR(EINVAL);
+ }
+
+ total_size = sizeof(struct sadb_msg) / IPSEC_PFKEYv2_ALIGN;
+ for (ext = 1; ext <= SADB_EXT_MAX; ext++) {
+ if (extensions[ext]) {
+ total_size += (extensions[ext])->sadb_ext_len;
+ }
+ }
+
+ if (!(*pfkey_msg = (struct sadb_msg*)kmalloc(total_size * IPSEC_PFKEYv2_ALIGN, GFP_ATOMIC))) {
+ PFKEY_DEBUG("memory allocation failed\n");
+ SENDERR(ENOMEM);
+ }
+
+ PFKEY_DEBUG("pfkey_msg=%p allocated %d bytes, &extensions[0]=%p\n",
+ *pfkey_msg, total_size * IPSEC_PFKEYv2_ALIGN, &extensions[0]);
+ memcpy(*pfkey_msg,
+ extensions[0],
+ sizeof(struct sadb_msg));
+ (*pfkey_msg)->sadb_msg_len = total_size;
+ (*pfkey_msg)->sadb_msg_reserved = 0;
+ extensions_seen = 1 ;
+
+ pfkey_ext = (struct sadb_ext*)(((char*)(*pfkey_msg)) + sizeof(struct sadb_msg));
+
+ for (ext = 1; ext <= SADB_EXT_MAX; ext++) {
+ /* copy from extension[ext] to buffer */
+ if (extensions[ext]) {
+ memcpy(pfkey_ext,
+ extensions[ext],
+ (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN);
+ ((char*)pfkey_ext) += (extensions[ext])->sadb_ext_len * IPSEC_PFKEYv2_ALIGN;
+ /* Mark that we have seen this extension and remember the header location */
+ extensions_seen |= ( 1 << ext );
+ }
+ }
+
+errlab:
+
+ return error;
+}
+
+/*
+ * $Log: pfkey_v2_build.c,v $
+ * Revision 1.1.1.1 2001/05/22 06:14:05 miyazawa
+ * kernel for ipsec without FS
+ *
+ * Revision 1.21 2000/11/17 18:10:30 rgb
+ * Fixed bugs mostly relating to spirange, to treat all spi variables as
+ * network byte order since this is the way PF_KEYv2 stored spis.
+ *
+ * Revision 1.20 2000/10/12 00:02:39 rgb
+ * Removed 'format, ##' nonsense from debug macros for RH7.0.
+ *
+ * Revision 1.19 2000/10/10 20:10:20 rgb
+ * Added support for debug_ipcomp and debug_verbose to klipsdebug.
+ *
+ * Revision 1.18 2000/09/12 18:59:54 rgb
+ * Added Gerhard's IPv6 support to pfkey parts of libfreeswan.
+ *
+ * Revision 1.17 2000/09/12 03:27:00 rgb
+ * Moved DEBUGGING definition to compile kernel with debug off.
+ *
+ * Revision 1.16 2000/09/08 19:22:12 rgb
+ * Fixed pfkey_prop_build() parameter to be only single indirection.
+ * Fixed struct alg copy.
+ *
+ * Revision 1.15 2000/08/20 21:40:01 rgb
+ * Added an address parameter sanity check to pfkey_address_build().
+ *
+ * Revision 1.14 2000/08/15 17:29:23 rgb
+ * Fixes from SZI to untested pfkey_prop_build().
+ *
+ * Revision 1.13 2000/06/02 22:54:14 rgb
+ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
+ *
+ * Revision 1.12 2000/05/10 19:24:01 rgb
+ * Fleshed out sensitivity, proposal and supported extensions.
+ *
+ * Revision 1.11 2000/03/16 14:07:23 rgb
+ * Renamed ALIGN macro to avoid fighting with others in kernel.
+ *
+ * Revision 1.10 2000/01/24 21:14:35 rgb
+ * Added disabled pluto pfkey lib debug flag.
+ *
+ * Revision 1.9 2000/01/21 06:27:32 rgb
+ * Added address cases for eroute flows.
+ * Removed unused code.
+ * Dropped unused argument to pfkey_x_satype_build().
+ * Indented compiler directives for readability.
+ * Added klipsdebug switching capability.
+ * Fixed SADB_EXT_MAX bug not permitting last extension access.
+ *
+ * Revision 1.8 1999/12/29 21:17:41 rgb
+ * Changed pfkey_msg_build() I/F to include a struct sadb_msg**
+ * parameter for cleaner manipulation of extensions[] and to guard
+ * against potential memory leaks.
+ * Changed the I/F to pfkey_msg_free() for the same reason.
+ *
+ * Revision 1.7 1999/12/09 23:12:20 rgb
+ * Removed unused cruft.
+ * Added argument to pfkey_sa_build() to do eroutes.
+ * Fixed exttype check in as yet unused pfkey_lifetime_build().
+ *
+ * Revision 1.6 1999/12/07 19:54:29 rgb
+ * Removed static pluto debug flag.
+ * Added functions for pfkey message and extensions initialisation
+ * and cleanup.
+ *
+ * Revision 1.5 1999/12/01 22:20:06 rgb
+ * Changed pfkey_sa_build to accept an SPI in network byte order.
+ * Added <string.h> to quiet userspace compiler.
+ * Moved pfkey_lib_debug variable into the library.
+ * Removed SATYPE check from pfkey_msg_hdr_build so FLUSH will work.
+ * Added extension assembly debugging.
+ * Isolated assignment with brackets to be sure of scope.
+ *
+ * Revision 1.4 1999/11/27 11:57:35 rgb
+ * Added ipv6 headers.
+ * Remove over-zealous algorithm sanity checkers from pfkey_sa_build.
+ * Debugging error messages added.
+ * Fixed missing auth and encrypt assignment bug.
+ * Add argument to pfkey_msg_parse() for direction.
+ * Move parse-after-build check inside pfkey_msg_build().
+ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
+ * Add CVS log entry to bottom of file.
+ *
+ */
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_ext_bits.c linux25.43-ipsec/net/key/pfkey_v2_ext_bits.c
--- linux-2.5.43/net/key/pfkey_v2_ext_bits.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_ext_bits.c 2002-10-16 15:27:50.000000000 +0900
@@ -0,0 +1,703 @@
+/* this file was derived from FreeS/WAN-1.9. (changed a little) */
+/*
+ * RFC2367 PF_KEYv2 Key management API message parser
+ * Copyright (C) 1999 Richard Guy Briggs.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <
http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id: pfkey_v2_ext_bits.c,v 1.7 2000/09/12 22:35:37 rgb Exp $
+ */
+/*
+ * Template from klips/net/ipsec/ipsec/ipsec_parse.c.
+ */
+/*
+ * Some ugly stuff to allow consistent debugging code for use in the
+ * kernel and in user space
+*/
+
+#include <linux/kernel.h> /* for printk */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/interrupt.h> /* mark_bh */
+#include <linux/netdevice.h> /* struct device, and other headers */
+#include <linux/etherdevice.h> /* eth_type_trans */
+#include <linux/ip.h> /* struct iphdr */
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#include <linux/ipv6.h>
+#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+
+unsigned int extensions_bitmaps[2/*in/out*/][2/*perm/req*/][SADB_MAX + 1/*ext*/] = {
+
+/* INBOUND EXTENSIONS */
+{
+
+/* PERMITTED IN */
+{
+/* SADB_RESERVED */
+0
+,
+/* SADB_GETSPI PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_SPIRANGE
+,
+/* SADB_UPDATE PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+,
+/* SADB_ADD PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+,
+/* SADB_DELETE PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_GET PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_ACQUIRE PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+,
+/* SADB_REGISTER PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_EXPIRE PERMITTED IN */
+0
+,
+/* SADB_FLUSH PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_DUMP PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_X_PROMISC PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_PCHANGE PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_GRPSA PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_ADDFLOW PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DELFLOW PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DEBUG PERMITTED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_X_EXT_DEBUG
+},
+
+/* REQUIRED IN */
+{
+/* SADB_RESERVED REQUIRED IN */
+0
+,
+/* SADB_GETSPI REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_SPIRANGE
+,
+/* SADB_UPDATE REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+,
+/* SADB_ADD REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ /*| 1<<SADB_EXT_KEY_AUTH*/
+ /*| 1<<SADB_EXT_KEY_ENCRYPT*/
+,
+/* SADB_DELETE REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_GET REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_ACQUIRE REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_PROPOSAL
+,
+/* SADB_REGISTER REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_EXPIRE REQUIRED IN */
+0
+,
+/* SADB_FLUSH REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_DUMP REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_X_PROMISC REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_PCHANGE REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_GRPSA REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_DST
+ /*| 1<<SADB_X_EXT_SATYPE2*/
+ /*| 1<<SADB_X_EXT_SA2*/
+ /*| 1<<SADB_X_EXT_ADDRESS_DST2*/
+,
+/* SADB_X_ADDFLOW REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DELFLOW REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+/*| 1<<SADB_EXT_SA*/
+#if 0 /* SADB_X_CLREROUTE doesn't need all these... */
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+#endif
+,
+/* SADB_X_DEBUG REQUIRED IN */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_X_EXT_DEBUG
+}
+
+},
+
+/* OUTBOUND EXTENSIONS */
+{
+
+/* PERMITTED OUT */
+{
+/* SADB_RESERVED */
+0
+,
+/* SADB_GETSPI PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_UPDATE PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+,
+/* SADB_ADD PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+,
+/* SADB_DELETE PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_GET PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+#if 0
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+#endif
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+#if 0
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+#endif
+,
+/* SADB_ACQUIRE PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+,
+/* SADB_REGISTER PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+,
+/* SADB_EXPIRE PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_FLUSH PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_DUMP PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+,
+/* SADB_X_PROMISC PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_PCHANGE PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_GRPSA PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_ADDFLOW PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DELFLOW PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DEBUG PERMITTED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_X_EXT_DEBUG
+},
+
+/* REQUIRED OUT */
+{
+/* SADB_RESERVED REQUIRED OUT */
+0
+,
+/* SADB_GETSPI REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_UPDATE REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_ADD REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_DELETE REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_GET REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+,
+/* SADB_ACQUIRE REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_PROPOSAL
+,
+/* SADB_REGISTER REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ /* | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT */
+,
+/* SADB_EXPIRE REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ /* | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT */
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_FLUSH REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+,
+/* SADB_DUMP REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+,
+/* SADB_X_PROMISC REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_PCHANGE REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_LIFETIME_CURRENT
+ | 1<<SADB_EXT_LIFETIME_HARD
+ | 1<<SADB_EXT_LIFETIME_SOFT
+ | 1<<SADB_EXT_ADDRESS_SRC
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_EXT_ADDRESS_PROXY
+ | 1<<SADB_EXT_KEY_AUTH
+ | 1<<SADB_EXT_KEY_ENCRYPT
+ | 1<<SADB_EXT_IDENTITY_SRC
+ | 1<<SADB_EXT_IDENTITY_DST
+ | 1<<SADB_EXT_SENSITIVITY
+ | 1<<SADB_EXT_PROPOSAL
+ | 1<<SADB_EXT_SUPPORTED_AUTH
+ | 1<<SADB_EXT_SUPPORTED_ENCRYPT
+ | 1<<SADB_EXT_SPIRANGE
+ | 1<<SADB_X_EXT_KMPRIVATE
+ | 1<<SADB_X_EXT_SATYPE2
+ | 1<<SADB_X_EXT_SA2
+ | 1<<SADB_X_EXT_ADDRESS_DST2
+,
+/* SADB_X_GRPSA REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_DST
+,
+/* SADB_X_ADDFLOW REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_EXT_SA
+ | 1<<SADB_EXT_ADDRESS_DST
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DELFLOW REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ /*| 1<<SADB_EXT_SA*/
+ | 1<<SADB_X_EXT_ADDRESS_SRC_FLOW
+ | 1<<SADB_X_EXT_ADDRESS_DST_FLOW
+#if 0
+ | 1<<SADB_X_EXT_ADDRESS_SRC_MASK
+ | 1<<SADB_X_EXT_ADDRESS_DST_MASK
+#endif
+,
+/* SADB_X_DEBUG REQUIRED OUT */
+ 1<<SADB_EXT_RESERVED
+ | 1<<SADB_X_EXT_DEBUG
+}
+}
+};
+
+/*
+ * $Log: pfkey_v2_ext_bits.c,v $
+ * Revision 1.2 2001/07/09 00:40:50 miyazawa
+ * remove obsolete file
+ *
+ * Revision 1.7 2000/09/12 22:35:37 rgb
+ * Restructured to remove unused extensions from CLEARFLOW messages.
+ *
+ * Revision 1.6 2000/09/09 06:39:01 rgb
+ * Added comments for clarity.
+ *
+ * Revision 1.5 2000/06/02 22:54:14 rgb
+ * Added Gerhard Gessler's struct sockaddr_storage mods for IPv6 support.
+ *
+ * Revision 1.4 2000/01/21 06:27:56 rgb
+ * Added address cases for eroute flows.
+ * Added comments for each message type.
+ * Added klipsdebug switching capability.
+ * Fixed GRPSA bitfields.
+ *
+ * Revision 1.3 1999/12/01 22:20:27 rgb
+ * Remove requirement for a proxy address in an incoming getspi message.
+ *
+ * Revision 1.2 1999/11/27 11:57:06 rgb
+ * Consolidated the 4 1-d extension bitmap arrays into one 4-d array.
+ * Add CVS log entry to bottom of file.
+ * Cleaned out unused bits.
+ *
+ */
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg.c linux25.43-ipsec/net/key/pfkey_v2_msg.c
--- linux-2.5.43/net/key/pfkey_v2_msg.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,959 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+/*
+ * pfkey_v2_msg.c is a program for PF_KEY version2 parsing routine, and utilities.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/net.h>
+#include <linux/crypto.h>
+#include <linux/ipsec.h>
+
+#include <net/sock.h>
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+
+#include "pfkey_v2_msg.h"
+#include "sockaddr_utils.h"
+
+#define BUF_SIZE 64
+
+int sadb_msg_sanity_check(struct sadb_msg* msg)
+{
+ int error = 0;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (!(msg->sadb_msg_version == PF_KEY_V2 &&
+ msg->sadb_msg_type > 0 && msg->sadb_msg_type <= SADB_MAX &&
+ /* msg->sadb_msg_satype >= 0 && */ msg->sadb_msg_satype <= SADB_SATYPE_MAX &&
+ msg->sadb_msg_reserved == 0))
+ error = -EINVAL;
+
+ PFKEY_DEBUG("sadb_msg_version=%d\n", msg->sadb_msg_version);
+ PFKEY_DEBUG("sadb_msg_type=%d\n", msg->sadb_msg_type);
+ PFKEY_DEBUG("sadb_msg_satype=%d\n", msg->sadb_msg_satype);
+ PFKEY_DEBUG("sadb_msg_len=%d\n", msg->sadb_msg_len);
+ PFKEY_DEBUG("sadb_msg_reserved=%d\n", msg->sadb_msg_reserved);
+
+err:
+ PFKEY_DEBUG("error=%d\n", error);
+ return error;
+}
+
+int sadb_address_to_sockaddr(const struct sadb_address *ext_msg, struct sockaddr* addr)
+{
+ int error = 0, len = 0;
+ struct sockaddr* tmp_addr = NULL;
+#ifdef CONFIG_IPSEC_DEBUG
+ char buf[BUF_SIZE];
+#endif
+
+ if (!ext_msg || !addr) {
+ PFKEY_DEBUG("msg or addr is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ len = ext_msg->sadb_address_len - sizeof(struct sadb_address);
+ if (len < sizeof(struct sockaddr)) {
+ PFKEY_DEBUG("sadb_address_len is small len=%d\n", len);
+ error = -EINVAL;
+ goto err;
+ }
+
+ tmp_addr = (struct sockaddr*)((char*)ext_msg + sizeof(struct sadb_address));
+ if (!tmp_addr) {
+ PFKEY_DEBUG("address==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ switch (tmp_addr->sa_family) {
+
+ case AF_INET:
+#ifdef CONFIG_IPSEC_DEBUG
+ PFKEY_DEBUG("address family is AF_INET\n");
+ sockaddrtoa((struct sockaddr*)tmp_addr, buf, BUF_SIZE);
+ PFKEY_DEBUG("address=%s\n", buf);
+#endif
+ memcpy(addr, tmp_addr, sizeof(struct sockaddr_in));
+ break;
+
+ case AF_INET6:
+#ifdef CONFIG_IPSEC_DEBUG
+ PFKEY_DEBUG("address family is AF_INET6\n");
+ sockaddrtoa((struct sockaddr*)tmp_addr, buf, BUF_SIZE);
+ PFKEY_DEBUG("address=%s\n", buf);
+#endif
+ memcpy(addr, tmp_addr, sizeof(struct sockaddr_in6));
+ break;
+
+ default:
+ PFKEY_DEBUG("address family is unknown\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+err:
+#ifdef CONFIG_IPSEC_DEBUG
+ if (!error)
+ PFKEY_DEBUG("error=%d\n", error);
+#endif
+ return error;
+}
+
+int sadb_key_to_esp(const __u8 esp_algo, const struct sadb_key* ext_msg, struct ipsec_sa* sa_entry)
+{
+ int error = 0;
+ char *algoname = NULL;
+ struct cipher_implementation *ci=NULL;
+
+ if (!sa_entry) {
+ PFKEY_DEBUG("sa_entry is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (esp_algo != SADB_EALG_NULL && !ext_msg) {
+ PFKEY_DEBUG("ext_msg is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ switch (esp_algo) {
+ case SADB_EALG_DESCBC:
+ algoname = "des-cbc";
+ PFKEY_DEBUG("esp algorithm is DES-CBC\n");
+ if(ext_msg->sadb_key_bits != ESP_DES_KEY_BITS){
+ PFKEY_DEBUG("the key length is not match\n");
+ error = -EINVAL;
+ goto err;
+ }
+ break;
+ case SADB_EALG_3DESCBC:
+ algoname = "3des-cbc";
+ PFKEY_DEBUG("esp algorithm is 3DES-CBC\n");
+ if(ext_msg->sadb_key_bits != ESP_3DES_KEY_BITS){
+ PFKEY_DEBUG("the key length is not match\n");
+ error = -EINVAL;
+ goto err;
+ }
+ break;
+ case SADB_EALG_NULL:
+ algoname = "null-ecb";
+ PFKEY_DEBUG("esp algorithm is NULL\n");
+ break;
+ case SADB_EALG_AES:
+ algoname = "aes-cbc";
+ PFKEY_DEBUG("esp algorithm is AES(128-bit)\n");
+ if (ext_msg->sadb_key_bits != ESP_AES_KEY_BITS) {
+ PFKEY_DEBUG("the key length is no match\n");
+ error = -EINVAL;
+ goto err;
+ }
+ break;
+ case SADB_EALG_NONE: /* currently not enter */
+ default:
+ PFKEY_DEBUG("esp_algo is NONE or not supported one\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ ci = find_cipher_by_name(algoname, 1);
+ if (!ci) {
+ PFKEY_DEBUG("algorithm %u:%s is not supported\n", esp_algo, algoname);
+ error = -EINVAL;
+ goto err;
+ }
+ ci->lock();
+
+ sa_entry->esp_algo.algo = esp_algo;
+
+ if (esp_algo != SADB_EALG_NULL) {
+ sa_entry->esp_algo.key_len = (ext_msg->sadb_key_bits)/OCTETBITS;
+ } else {
+ sa_entry->esp_algo.key_len = 0;
+ }
+
+ sa_entry->esp_algo.cx = ci->realloc_context (NULL, ci, sa_entry->esp_algo.key_len);
+ if (!sa_entry->esp_algo.cx) {
+ ci->unlock();
+ error = -EINVAL;
+ goto err;
+ }
+ sa_entry->esp_algo.cx->ci = ci;
+
+ if (esp_algo != SADB_EALG_NULL) {
+ sa_entry->esp_algo.key = kmalloc(sa_entry->esp_algo.key_len, GFP_KERNEL);
+ if (!sa_entry->esp_algo.key) {
+ PFKEY_DEBUG("could not allocate memory for key\n");
+ ci->wipe_context(sa_entry->esp_algo.cx);
+ ci->free_context(sa_entry->esp_algo.cx);
+ ci->unlock();
+ error = -ENOMEM;
+ goto err;
+ }
+ memset(sa_entry->esp_algo.key, 0, sa_entry->esp_algo.key_len);
+ memcpy(sa_entry->esp_algo.key, ((char*)ext_msg)+sizeof(struct sadb_key), sa_entry->esp_algo.key_len);
+
+ memset(sa_entry->esp_algo.cx->keyinfo, 0, ci->key_schedule_size);
+
+ error = ci->set_key(sa_entry->esp_algo.cx, ((char*)ext_msg)+sizeof(struct sadb_key), sa_entry->esp_algo.key_len);
+ if (error < 0) {
+ printk(KERN_DEBUG "set_key failed, weak key?\n");
+ ci->wipe_context(sa_entry->esp_algo.cx);
+ ci->free_context(sa_entry->esp_algo.cx);
+ ci->unlock();
+ goto err;
+ }
+ }
+err:
+ return error;
+
+}
+
+int sadb_key_to_auth(const __u8 auth_algo, const struct sadb_key* ext_msg, struct ipsec_sa* sa_entry)
+{
+ int error = 0;
+ char *algoname = NULL;
+ __u16 digest_len = 0;
+ struct digest_implementation *di = NULL;
+
+ if (!(ext_msg&&sa_entry)) {
+ PFKEY_DEBUG("msg or sa_entry is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ switch (auth_algo) {
+
+ case SADB_AALG_MD5HMAC:
+ algoname = "md5";
+ digest_len = 12; /* 96 bit length */
+ PFKEY_DEBUG("auth algorithm is HMAC-MD5\n");
+
+ if(ext_msg->sadb_key_bits != AUTH_MD5HMAC_KEY_BITS){
+ PFKEY_DEBUG("the key length is %d\n", ext_msg->sadb_key_bits);
+ PFKEY_DEBUG("the key length is not match\n");
+ error = -EINVAL;
+ goto err;
+ }
+ break;
+
+ case SADB_AALG_SHA1HMAC:
+ algoname = "sha1";
+ digest_len = 12; /* 96 bit length */
+ PFKEY_DEBUG("auth algorithm is HMAC-SHA1\n");
+
+ if (ext_msg->sadb_key_bits != AUTH_SHA1HMAC_KEY_BITS) {
+ PFKEY_DEBUG("the key length is %d\n", ext_msg->sadb_key_bits);
+ PFKEY_DEBUG("the key length is not match\n");
+ error = -EINVAL;
+ goto err;
+ }
+ break;
+
+ case SADB_AALG_NONE:
+ default:
+ PFKEY_DEBUG("auth_algo is NONE or not supported one\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ di = find_digest_by_name(algoname, 0);
+ if (!di) {
+ PFKEY_DEBUG("algorithm %u:%s is not supported\n", auth_algo, algoname);
+ error = -EINVAL;
+ goto err;
+ }
+ di->lock();
+
+ sa_entry->auth_algo.algo = auth_algo;
+ sa_entry->auth_algo.key_len = (ext_msg->sadb_key_bits)/OCTETBITS;
+ sa_entry->auth_algo.digest_len = digest_len;
+ sa_entry->auth_algo.dx = di->realloc_context(NULL, di);
+ if (!sa_entry->auth_algo.dx) {
+ di->unlock();
+ error = -EINVAL;
+ goto err;
+ }
+ sa_entry->auth_algo.dx->di = di;
+
+ sa_entry->auth_algo.key = kmalloc(sa_entry->auth_algo.key_len, GFP_KERNEL);
+ if (!sa_entry->auth_algo.key) {
+ PFKEY_DEBUG("cannot allocate key\n");
+ di->free_context(sa_entry->auth_algo.dx);
+ di->unlock();
+ error = -ENOMEM;
+ goto err;
+ }
+
+ memcpy(sa_entry->auth_algo.key, (char *)ext_msg+sizeof(struct sadb_key),
+ sa_entry->auth_algo.key_len);
+err:
+ return error;
+}
+
+
+int sadb_lifetime_to_lifetime(const struct sadb_lifetime* ext_msg, struct sa_lifetime* lifetime)
+{
+ int error = 0;
+
+ if (!ext_msg || !lifetime) {
+ PFKEY_DEBUG("param is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ lifetime->allocations = ext_msg->sadb_lifetime_allocations;
+ lifetime->bytes = ext_msg->sadb_lifetime_bytes;
+ lifetime->addtime = ext_msg->sadb_lifetime_addtime;
+ lifetime->usetime = ext_msg->sadb_lifetime_usetime;
+
+err:
+ return error;
+}
+
+int lifetime_to_sadb_lifetime(struct sa_lifetime *lifetime, struct sadb_lifetime *ext_msg, int type)
+{
+ int error = 0;
+ if (!lifetime || !ext_msg) {
+ PFKEY_DEBUG("param is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ error = pfkey_lifetime_build((struct sadb_ext**)&ext_msg, type,
+ lifetime->allocations,
+ lifetime->bytes,
+ lifetime->addtime,
+ lifetime->usetime);
+err:
+ return error;
+}
+
+int sadb_msg_detect_ext(struct sadb_msg* msg, struct sadb_ext **ext_msgs)
+{
+ int error = 0, len = 0, msg_size = 0;
+ struct sadb_ext *ext_ptr = NULL;
+
+ if (!msg || !ext_msgs) {
+ PFKEY_DEBUG("msg or ext_msgs is NULL.\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ error = sadb_msg_sanity_check(msg);
+ if (error) {
+ error = -EINVAL;
+ goto err;
+ }
+
+ ext_ptr = (struct sadb_ext*)((u8*)msg + sizeof(struct sadb_msg));
+ len = (msg->sadb_msg_len * IPSEC_PFKEYv2_ALIGN) - sizeof(struct sadb_msg);
+
+ while (len >= sizeof(*ext_ptr)) {
+ PFKEY_DEBUG("ext type %d\n", ext_ptr->sadb_ext_type);
+ PFKEY_DEBUG("ext length %d\n", ext_ptr->sadb_ext_len);
+ msg_size = (ext_ptr->sadb_ext_len) * IPSEC_PFKEYv2_ALIGN;
+ if (len < msg_size)
+ break; /* error will be set later */
+ ext_msgs[ext_ptr->sadb_ext_type] = ext_ptr;
+ ext_ptr = (struct sadb_ext*)((u8*)ext_ptr + msg_size);
+ len -= msg_size;
+ }
+
+ if (len)
+ error = -EINVAL;
+
+err:
+ PFKEY_DEBUG("error=%d\n", error);
+ return error;
+}
+
+int sadb_msg_acquire_parse(struct sock *sk, struct sadb_msg *msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1];
+
+ if (!msg) {
+ PFKEY_DEBUG("msg==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+err:
+ return error;
+}
+
+int sadb_msg_register_parse(struct sock *sk, struct sadb_msg *msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ uint alg_num = 0;
+ struct sadb_alg algs[5];
+ struct digest_implementation *di = NULL;
+ struct cipher_implementation *ci = NULL;
+ struct sadb_ext *reply_ext_msgs[SADB_EXT_MAX+1];
+
+ if (!sk || !msg) {
+ PFKEY_DEBUG("msg or sk is NULL\n");
+ error = -EINVAL;
+ PFKEY_DEBUG("msg or *reply is NULL\n");
+ goto err;
+ }
+
+ error = sadb_msg_sanity_check(msg);
+ if (error)
+ goto err;
+
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ memset(algs, 0, sizeof(algs));
+
+ switch (msg->sadb_msg_satype) {
+
+ case SADB_SATYPE_AH:
+ case SADB_SATYPE_ESP:
+ case SADB_X_SATYPE_COMP:
+ break;
+ case SADB_SATYPE_RSVP:
+ case SADB_SATYPE_OSPFV2:
+ case SADB_SATYPE_RIPV2:
+ case SADB_SATYPE_MIP:
+ case SADB_X_SATYPE_IPIP:
+ case SADB_X_SATYPE_INT:
+ default:
+ error = -EINVAL;
+ goto err;
+ }
+
+ write_lock_bh(&pfkey_sk_lock);
+ /*
+ register a socket with the proper SA type's socket list
+ it will be release in pfkey_release process (file:pfkey_v2.c).
+ */
+ error = pfkey_list_insert_socket(sk->socket, &pfkey_registered_sockets[msg->sadb_msg_satype]);
+ if (error) {
+ goto err;
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+
+ PFKEY_DEBUG("socket=%p registered, SA type is %d\n", sk->socket, msg->sadb_msg_satype);
+
+ reply_ext_msgs[0] = (struct sadb_ext*)msg;
+
+ if (msg->sadb_msg_satype == SADB_SATYPE_AH || msg->sadb_msg_satype == SADB_SATYPE_ESP) {
+ di = find_digest_by_name("md5", 0 /* atomic */);
+ if (di) {
+ algs[alg_num].sadb_alg_id = SADB_AALG_MD5HMAC;
+ algs[alg_num].sadb_alg_ivlen = 0;
+ algs[alg_num].sadb_alg_minbits = AUTH_MD5HMAC_KEY_BITS;
+ algs[alg_num].sadb_alg_maxbits = AUTH_MD5HMAC_KEY_BITS;
+ algs[alg_num].sadb_alg_reserved = 0;
+ alg_num++;
+ di = NULL;
+ }
+
+ di = find_digest_by_name("sha1", 0 /* atomic */);
+ if (di) {
+ algs[alg_num].sadb_alg_id = SADB_AALG_SHA1HMAC;
+ algs[alg_num].sadb_alg_ivlen = 0;
+ algs[alg_num].sadb_alg_minbits = AUTH_SHA1HMAC_KEY_BITS;
+ algs[alg_num].sadb_alg_maxbits = AUTH_SHA1HMAC_KEY_BITS;
+ algs[alg_num].sadb_alg_reserved = 0;
+ alg_num++;
+ di = NULL;
+ }
+
+ if (msg->sadb_msg_satype == SADB_SATYPE_AH) {
+ error = pfkey_supported_build(&reply_ext_msgs[SADB_EXT_SUPPORTED_AUTH],
+ SADB_EXT_SUPPORTED_AUTH,
+ alg_num,
+ algs);
+ if (error) goto free_ext_finish;
+ }
+
+ }
+
+ if (msg->sadb_msg_satype == SADB_SATYPE_ESP) {
+ ci = find_cipher_by_name("des-cbc", 1 /* atomic */);
+ if (ci) {
+ ci->lock();
+ algs[alg_num].sadb_alg_id = SADB_EALG_DESCBC;
+ algs[alg_num].sadb_alg_ivlen = ci->ivsize;
+ algs[alg_num].sadb_alg_minbits = ESP_DES_KEY_BITS;
+ algs[alg_num].sadb_alg_maxbits = ESP_DES_KEY_BITS;
+ algs[alg_num].sadb_alg_reserved = 0;
+ alg_num++;
+ ci->unlock();
+ ci = NULL;
+ }
+
+ ci = find_cipher_by_name("3des-cbc", 1 /* atomic */);
+ if (ci) {
+ ci->lock();
+ algs[alg_num].sadb_alg_id = SADB_EALG_3DESCBC;
+ algs[alg_num].sadb_alg_ivlen = ci->ivsize;
+ algs[alg_num].sadb_alg_minbits = ESP_3DES_KEY_BITS;
+ algs[alg_num].sadb_alg_maxbits = ESP_3DES_KEY_BITS;
+ algs[alg_num].sadb_alg_reserved = 0;
+ alg_num++;
+ ci->unlock();
+ ci = NULL;
+ }
+ ci = find_cipher_by_name("aes-cbc", 1 /* atomic */);
+ if (ci) {
+ ci->lock();
+ algs[alg_num].sadb_alg_id = SADB_EALG_AES;
+ algs[alg_num].sadb_alg_ivlen = ci->ivsize;
+ algs[alg_num].sadb_alg_minbits = ESP_AES_KEY_BITS;
+ algs[alg_num].sadb_alg_maxbits = ESP_AES_KEY_BITS;
+ algs[alg_num].sadb_alg_reserved = 0;
+ alg_num++;
+ ci->unlock();
+ ci = NULL;
+ }
+
+ error = pfkey_supported_build(&reply_ext_msgs[SADB_EXT_SUPPORTED_ENCRYPT],
+ SADB_EXT_SUPPORTED_ENCRYPT,
+ alg_num,
+ algs);
+ if (error) goto free_ext_finish;
+
+ }
+
+ pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+
+free_ext_finish:
+
+ if (reply_ext_msgs[SADB_EXT_SUPPORTED_AUTH])
+ kfree(reply_ext_msgs[SADB_EXT_SUPPORTED_AUTH]);
+
+ if (reply_ext_msgs[SADB_EXT_SUPPORTED_ENCRYPT])
+ kfree(reply_ext_msgs[SADB_EXT_SUPPORTED_ENCRYPT]);
+
+
+err:
+
+ return error;
+}
+
+int sadb_msg_expire_parse(struct sock *sk, struct sadb_msg *msg, struct sadb_msg **reply)
+{
+ int error = -EINVAL;
+
+#if 0 /* kernel never receive SADB_EXPIRE message */
+
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1];
+
+ if (!msg) {
+ PFKEY_DEBUG("msg==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+err:
+#endif /* kernel never receive SADB_EXPIRE message */
+
+ return error;
+}
+
+int sadb_msg_flush_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ int error = 0;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ error = sadb_msg_sanity_check(msg);
+
+ if (error) {
+ PFKEY_DEBUG("message is invalid\n");
+ goto err;
+ }
+
+ switch(msg->sadb_msg_satype) {
+ case SADB_SATYPE_AH:
+ case SADB_SATYPE_ESP:
+ sadb_flush_sa(msg->sadb_msg_satype);
+ break;
+ default:
+ sadb_clear_db();
+ }
+
+ *reply = kmalloc(sizeof(struct sadb_msg), GFP_KERNEL);
+ memcpy(*reply, msg, sizeof(struct sadb_msg));
+err:
+ return error;
+}
+
+int sadb_msg_flush_sp_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ int error = 0;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ error = sadb_msg_sanity_check(msg);
+
+ if (error) {
+ PFKEY_DEBUG("message is invalid\n");
+ goto err;
+ }
+
+ spd_clear_db();
+
+ *reply = kmalloc(sizeof(struct sadb_msg), GFP_KERNEL);
+ memcpy(*reply, msg, sizeof(struct sadb_msg));
+err:
+ return error;
+}
+
+int sadb_msg_dump_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1];
+
+ if (!msg) {
+ PFKEY_DEBUG("msg==NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+err:
+ return error;
+}
+
+int sadb_msg_send_expire(struct ipsec_sa *sa)
+{
+ int i=0, error = 0;
+ struct sadb_msg *msg = NULL;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1];
+ struct socket_list *pfkey_socketsp = NULL;
+
+ if (!sa) {
+ PFKEY_DEBUG("sa is NULL\n");
+ return -EINVAL;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+
+ error = pfkey_msg_hdr_build(&ext_msgs[0],
+ SADB_EXPIRE,
+ sa->ipsec_proto,
+ 0,
+ 0,
+ 0);
+ if (error) {
+ PFKEY_DEBUG("pfkey_msg_hdr_build is failed\n");
+ goto free_ext_finish;
+ }
+
+ error = pfkey_sa_build(&ext_msgs[SADB_EXT_SA],
+ SADB_EXT_SA,
+ sa->spi,
+ 64,
+ sa->state,
+ sa->auth_algo.algo,
+ sa->esp_algo.algo,
+ 0); /* TODO: add pfs flag to struct ipsec_sa */
+ if (error) {
+ PFKEY_DEBUG("pfkey_sa_build is failed\n");
+ goto free_ext_finish;
+ }
+
+ error = pfkey_lifetime_build(&ext_msgs[SADB_EXT_LIFETIME_CURRENT],
+ SADB_EXT_LIFETIME_CURRENT,
+ sa->lifetime_c.allocations,
+ sa->lifetime_c.bytes,
+ sa->lifetime_c.addtime,
+ sa->lifetime_c.usetime);
+ if (error) {
+ PFKEY_DEBUG("pfkey_lifetime_build is failed\n");
+ goto free_ext_finish;
+ }
+
+ switch(sa->state) {
+ case SADB_SASTATE_DEAD:
+ error = pfkey_lifetime_build(&ext_msgs[SADB_EXT_LIFETIME_HARD],
+ SADB_EXT_LIFETIME_HARD,
+ sa->lifetime_h.allocations,
+ sa->lifetime_h.bytes,
+ sa->lifetime_h.addtime,
+ sa->lifetime_h.usetime);
+ if (error) {
+ PFKEY_DEBUG("pfkey_liftime_build(hard) is failed\n");
+ goto free_ext_finish;
+ }
+ break;
+
+ case SADB_SASTATE_DYING:
+ error = pfkey_lifetime_build(&ext_msgs[SADB_EXT_LIFETIME_SOFT],
+ SADB_EXT_LIFETIME_SOFT,
+ sa->lifetime_s.allocations,
+ sa->lifetime_s.bytes,
+ sa->lifetime_s.addtime,
+ sa->lifetime_s.usetime);
+ if (error) {
+ PFKEY_DEBUG("pfkey_lifetime_build(soft) is failed\n");
+ goto free_ext_finish;
+ }
+ break;
+
+ case SADB_SASTATE_LARVAL:
+ case SADB_SASTATE_MATURE:
+ default:
+ error = -EINVAL;
+ goto free_ext_finish;
+
+ }
+
+ error = pfkey_address_build(&ext_msgs[SADB_EXT_ADDRESS_SRC],
+ SADB_EXT_ADDRESS_SRC,
+ sa->proto,
+ sa->prefixlen_s,
+ (struct sockaddr*)&sa->src);
+ if (error) goto free_ext_finish;
+
+ error = pfkey_address_build(&ext_msgs[SADB_EXT_ADDRESS_DST],
+ SADB_EXT_ADDRESS_DST,
+ sa->proto,
+ sa->prefixlen_d,
+ (struct sockaddr*)&sa->dst);
+ if (error) goto free_ext_finish;
+
+ error = pfkey_msg_build(&msg, ext_msgs, EXT_BITS_OUT);
+
+ write_lock_bh(&pfkey_sk_lock);
+ for (pfkey_socketsp = pfkey_open_sockets;
+ pfkey_socketsp;
+ pfkey_socketsp = pfkey_socketsp->next)
+ {
+ pfkey_upmsg(pfkey_socketsp->socketp, msg);
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+
+ kfree(msg);
+
+free_ext_finish:
+ for (i=0; i<SADB_MAX+1; i++) {
+ if (ext_msgs[i]) {
+ kfree(ext_msgs[i]);
+ }
+ }
+
+ return 0;
+
+}
+
+int sadb_msg_send_acquire(struct ipsec_sa *sa)
+{
+ int i=0, error = 0;
+ uint comb_num = 0;
+ struct sadb_comb combs[5];
+ struct digest_implementation *di = NULL;
+ struct cipher_implementation *ci = NULL;
+ struct sadb_msg *msg = NULL;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1];
+ struct socket_list *pfkey_socketsp = NULL;
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(combs, 0, sizeof(combs));
+
+ if (sa->ipsec_proto == SADB_SATYPE_AH || sa->ipsec_proto == SADB_SATYPE_ESP) {
+ di = find_digest_by_name("md5", 0 /* atomic */);
+ if (di) {
+ combs[comb_num].sadb_comb_auth = SADB_AALG_MD5HMAC;
+ combs[comb_num].sadb_comb_auth_minbits = AUTH_MD5HMAC_KEY_BITS;
+ combs[comb_num].sadb_comb_auth_maxbits = AUTH_MD5HMAC_KEY_BITS;
+ combs[comb_num].sadb_comb_reserved = 0;
+ combs[comb_num].sadb_comb_soft_allocations = 0;
+ combs[comb_num].sadb_comb_hard_allocations = 0;
+ combs[comb_num].sadb_comb_soft_bytes = 0;
+ combs[comb_num].sadb_comb_hard_bytes = 0;
+ combs[comb_num].sadb_comb_soft_addtime = 57600;
+ combs[comb_num].sadb_comb_hard_addtime = 86400;
+ combs[comb_num].sadb_comb_soft_usetime = 57600;
+ combs[comb_num].sadb_comb_hard_usetime = 86400;
+ comb_num++;
+ di = NULL;
+ }
+
+ di = find_digest_by_name("sha1", 0 /* atomic */);
+ if (di) {
+ combs[comb_num].sadb_comb_auth = SADB_AALG_SHA1HMAC;
+ combs[comb_num].sadb_comb_auth_minbits = AUTH_SHA1HMAC_KEY_BITS;
+ combs[comb_num].sadb_comb_auth_maxbits = AUTH_SHA1HMAC_KEY_BITS;
+ combs[comb_num].sadb_comb_reserved = 0;
+ combs[comb_num].sadb_comb_soft_allocations = 0;
+ combs[comb_num].sadb_comb_hard_allocations = 0;
+ combs[comb_num].sadb_comb_soft_bytes = 0;
+ combs[comb_num].sadb_comb_hard_bytes = 0;
+ combs[comb_num].sadb_comb_soft_addtime = 57600;
+ combs[comb_num].sadb_comb_hard_addtime = 86400;
+ combs[comb_num].sadb_comb_soft_usetime = 57600;
+ combs[comb_num].sadb_comb_hard_usetime = 86400;
+ comb_num++;
+ di = NULL;
+ }
+ }
+
+ if (sa->ipsec_proto == SADB_SATYPE_ESP) {
+ ci = find_cipher_by_name("des-cbc", 1 /* atomic */);
+ if (ci) {
+ combs[comb_num].sadb_comb_encrypt = SADB_EALG_DESCBC;
+ combs[comb_num].sadb_comb_encrypt_minbits = ESP_DES_KEY_BITS;
+ combs[comb_num].sadb_comb_encrypt_maxbits = ESP_DES_KEY_BITS;
+ combs[comb_num].sadb_comb_reserved = 0;
+ combs[comb_num].sadb_comb_soft_allocations = 0;
+ combs[comb_num].sadb_comb_hard_allocations = 0;
+ combs[comb_num].sadb_comb_soft_bytes = 0;
+ combs[comb_num].sadb_comb_hard_bytes = 0;
+ combs[comb_num].sadb_comb_soft_addtime = 57600;
+ combs[comb_num].sadb_comb_hard_addtime = 86400;
+ combs[comb_num].sadb_comb_soft_usetime = 57600;
+ combs[comb_num].sadb_comb_hard_usetime = 86400;
+ comb_num++;
+ ci = NULL;
+ }
+
+ ci = find_cipher_by_name("3des-cbc", 1 /* atomic */);
+ if (ci) {
+ combs[comb_num].sadb_comb_encrypt = SADB_EALG_3DESCBC;
+ combs[comb_num].sadb_comb_encrypt_minbits = ESP_3DES_KEY_BITS;
+ combs[comb_num].sadb_comb_encrypt_maxbits = ESP_3DES_KEY_BITS;
+ combs[comb_num].sadb_comb_reserved = 0;
+ combs[comb_num].sadb_comb_soft_allocations = 0;
+ combs[comb_num].sadb_comb_hard_allocations = 0;
+ combs[comb_num].sadb_comb_soft_bytes = 0;
+ combs[comb_num].sadb_comb_hard_bytes = 0;
+ combs[comb_num].sadb_comb_soft_addtime = 57600;
+ combs[comb_num].sadb_comb_hard_addtime = 86400;
+ combs[comb_num].sadb_comb_soft_usetime = 57600;
+ combs[comb_num].sadb_comb_hard_usetime = 86400;
+ comb_num++;
+ ci = NULL;
+ }
+
+ ci = find_cipher_by_name("aes-cbc", 1 /* atomic */);
+ if (ci) {
+ combs[comb_num].sadb_comb_encrypt = SADB_EALG_AES;
+ combs[comb_num].sadb_comb_encrypt_minbits = ESP_AES_KEY_BITS;
+ combs[comb_num].sadb_comb_encrypt_maxbits = ESP_AES_KEY_BITS;
+ combs[comb_num].sadb_comb_reserved = 0;
+ combs[comb_num].sadb_comb_soft_allocations = 0;
+ combs[comb_num].sadb_comb_hard_allocations = 0;
+ combs[comb_num].sadb_comb_soft_bytes = 0;
+ combs[comb_num].sadb_comb_hard_bytes = 0;
+ combs[comb_num].sadb_comb_soft_addtime = 57600;
+ combs[comb_num].sadb_comb_hard_addtime = 86400;
+ combs[comb_num].sadb_comb_soft_usetime = 57600;
+ combs[comb_num].sadb_comb_hard_usetime = 86400;
+ comb_num++;
+ ci = NULL;
+ }
+ }
+
+ error = pfkey_msg_hdr_build(&ext_msgs[0],
+ SADB_ACQUIRE,
+ sa->ipsec_proto,
+ 0,
+ 0,
+ 0);
+ if (error) {
+ PFKEY_DEBUG("pfkey_msg_hdr_build is failed\n");
+ goto free_ext_finish;
+ }
+
+ error = pfkey_address_build(&ext_msgs[SADB_EXT_ADDRESS_SRC],
+ SADB_EXT_ADDRESS_SRC,
+ sa->proto,
+ sa->prefixlen_s,
+ (struct sockaddr*)&sa->src);
+ if (error) goto free_ext_finish;
+
+ error = pfkey_address_build(&ext_msgs[SADB_EXT_ADDRESS_DST],
+ SADB_EXT_ADDRESS_DST,
+ sa->proto,
+ sa->prefixlen_d,
+ (struct sockaddr*)&sa->dst);
+ if (error) goto free_ext_finish;
+
+ error = pfkey_prop_build(&ext_msgs[SADB_EXT_PROPOSAL],
+ 64,
+ comb_num,
+ combs);
+ if (error) goto free_ext_finish;
+
+
+ error = pfkey_msg_build(&msg, ext_msgs, EXT_BITS_OUT);
+
+ write_lock_bh(&pfkey_sk_lock);
+ for (pfkey_socketsp = pfkey_registered_sockets[sa->ipsec_proto];
+ pfkey_socketsp;
+ pfkey_socketsp = pfkey_socketsp->next)
+ {
+ pfkey_upmsg(pfkey_socketsp->socketp, msg);
+ }
+ write_unlock_bh(&pfkey_sk_lock);
+
+ kfree(msg);
+free_ext_finish:
+
+ for (i=0; i<SADB_MAX+1; i++) {
+ if (ext_msgs[i]) {
+ kfree(ext_msgs[i]);
+ }
+ }
+
+ return 0;
+}
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg.h linux25.43-ipsec/net/key/pfkey_v2_msg.h
--- linux-2.5.43/net/key/pfkey_v2_msg.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg.h 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,78 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ *
+ */
+
+#ifndef __PFKEY_V2_MSG_H
+#define __PFKEY_V2_MSG_H
+
+#include <linux/pfkeyv2.h>
+#include <net/sadb.h>
+
+#define ESP_DES_KEY_BITS 64
+#define ESP_3DES_KEY_BITS 192
+#define ESP_NULL_KEY_BITS 0
+#define ESP_AES_KEY_BITS 128
+
+#define AUTH_MD5HMAC_KEY_BITS 128
+#define AUTH_SHA1HMAC_KEY_BITS 160
+
+/* thease codes are derived from FreeS/WAN */
+#define IPSEC_PFKEYv2_ALIGN (sizeof(uint64_t)/sizeof(uint8_t))
+#define BITS_PER_OCTET 8
+#define OCTETBITS 8
+#define PFKEYBITS 64
+#define DIVUP(x,y) ((x + y -1) / y) /* divide, rounding upwards */
+#define ALIGN_N(x,y) (DIVUP(x,y) * y) /* align on y boundary */
+
+#define PFKEYv2_MAX_MSGSIZE 4096
+
+
+/* utils */
+int sadb_msg_sanity_check(struct sadb_msg* msg);
+int sadb_address_to_sockaddr(const struct sadb_address *ext_msg, struct sockaddr *addr);
+int sadb_key_to_esp(const __u8 esp_algo, const struct sadb_key* ext_msg, struct ipsec_sa *sa_entry);
+int sadb_key_to_auth(const __u8 auth_algo, const struct sadb_key *ext_msg, struct ipsec_sa *sa_entry);
+int sadb_lifetime_to_lifetime(const struct sadb_lifetime* ext_msg, struct sa_lifetime *lifetime);
+int sadb_msg_detect_ext(struct sadb_msg* msg, struct sadb_ext **ext_msgs);
+int lifetime_to_sadb_lifetime(struct sa_lifetime *lifetime, struct sadb_lifetime *ext_msg, int type);
+
+/* parsers */
+int sadb_msg_getspi_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_update_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_add_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_delete_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_get_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_acquire_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_register_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_expire_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_flush_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_flush_sp_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_dump_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_addflow_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+int sadb_msg_delflow_parse(struct sock* sk, struct sadb_msg* msg, struct sadb_msg **reply);
+
+/* send message */
+int sadb_msg_send_expire(struct ipsec_sa* sa);
+int sadb_msg_send_acquire(struct ipsec_sa* sa);
+#endif /* __PFKEY_V2_MSG_H */
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg_add.c linux25.43-ipsec/net/key/pfkey_v2_msg_add.c
--- linux-2.5.43/net/key/pfkey_v2_msg_add.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg_add.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,217 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+/*
+ * This is a parse routine for a message of SADB_ADD.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/ipsec.h>
+
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+
+#include "pfkey_v2_msg.h"
+
+#define BUFSIZE 64
+
+int sadb_msg_add_parse(struct sock *sk, struct sadb_msg *msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1], *reply_ext_msgs[SADB_EXT_MAX+1];
+ struct sadb_sa *sa;
+ struct sadb_address *src;
+ struct sadb_address *dst;
+ struct ipsec_sa *sa_entry = NULL;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+ error = -EINVAL;
+ goto rtn;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+ if (ext_msgs[SADB_EXT_SA] &&
+ ext_msgs[SADB_EXT_ADDRESS_SRC] &&
+ ext_msgs[SADB_EXT_ADDRESS_DST])
+ {
+ sa = (struct sadb_sa*)ext_msgs[SADB_EXT_SA];
+
+ if ( ((!sa->sadb_sa_auth && !sa->sadb_sa_encrypt) && msg->sadb_msg_satype != SADB_X_SATYPE_COMP) ||
+ sa->sadb_sa_auth > SADB_AALG_MAX ||
+ sa->sadb_sa_encrypt > SADB_EALG_MAX ) {
+ PFKEY_DEBUG("SA has no transform or invalid transform value\n");
+ error = -EINVAL;
+ goto rtn;
+ }
+
+ switch (msg->sadb_msg_satype) {
+ case SADB_SATYPE_AH:
+ case SADB_SATYPE_ESP:
+ case SADB_X_SATYPE_COMP:
+ break;
+ case SADB_SATYPE_RSVP:
+ case SADB_SATYPE_OSPFV2:
+ case SADB_SATYPE_RIPV2:
+ case SADB_SATYPE_MIP:
+ default:
+ PFKEY_DEBUG("invalid SA type\n");
+ error = -EINVAL;
+ goto rtn;
+ }
+
+ if (msg->sadb_msg_satype == SADB_SATYPE_ESP && !(sa->sadb_sa_encrypt)) {
+ PFKEY_DEBUG("SA type is esp but no algorithm\n");
+ error = -EINVAL;
+ goto rtn;
+ }
+
+ sa_entry = ipsec_sa_kmalloc();
+ if (!sa_entry) {
+ PFKEY_DEBUG("sa_entry: NULL\n");
+ error = -ENOMEM;
+ goto err;
+ }
+
+ src = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_SRC];
+ dst = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_DST];
+
+ sa_entry->ipsec_proto = msg->sadb_msg_satype;
+
+ /* SPI which is under 255 is reserved by IANA.
+ * Additionally, 256 and 257 reserved for our internal use. */
+ if (ntohl(sa->sadb_sa_spi) < 258 && sa_entry->ipsec_proto != SADB_X_SATYPE_COMP) {
+ PFKEY_DEBUG("SPI value is reserved.(SPI<258)\n");
+ goto err;
+ }
+
+ sa_entry->spi = sa->sadb_sa_spi;
+
+ error = sadb_address_to_sockaddr( src,
+ (struct sockaddr*)&sa_entry->src);
+ if (error) {
+ PFKEY_DEBUG("src translation failed\n");
+ goto err;
+ }
+ error = sadb_address_to_sockaddr( dst,
+ (struct sockaddr*)&sa_entry->dst);
+ if (error) {
+ PFKEY_DEBUG("dst translation failed\n");
+ goto err;
+ }
+
+ if (src->sadb_address_proto == dst->sadb_address_proto) {
+ sa_entry->proto = src->sadb_address_proto;
+ } else {
+ error = -EINVAL;
+ goto err;
+ }
+
+ sa_entry->prefixlen_s = src->sadb_address_prefixlen;
+ sa_entry->prefixlen_d = dst->sadb_address_prefixlen;
+
+ if (sa->sadb_sa_auth) {
+ if( (ext_msgs[SADB_EXT_KEY_AUTH]) ) {
+ error = sadb_key_to_auth(sa->sadb_sa_auth,
+ (struct sadb_key*) ext_msgs[SADB_EXT_KEY_AUTH], sa_entry);
+ if (error)
+ goto err;
+ } else {
+ PFKEY_DEBUG("SA has auth algo but there is no key for auth\n");
+ error = -EINVAL;
+ goto err;
+ }
+ }
+
+ if (sa->sadb_sa_encrypt) {
+ if (sa->sadb_sa_encrypt == SADB_EALG_NULL &&
+ sa->sadb_sa_auth == SADB_AALG_NONE) {
+ error = -EINVAL;
+ goto err;
+ }
+ if (msg->sadb_msg_satype != SADB_X_SATYPE_COMP) {
+ error = sadb_key_to_esp(sa->sadb_sa_encrypt,
+ (struct sadb_key*) ext_msgs[SADB_EXT_KEY_ENCRYPT], sa_entry);
+ if (error)
+ goto err;
+ }
+ }
+
+ if (ext_msgs[SADB_EXT_ADDRESS_PROXY]) {
+ printk(KERN_WARNING "PFKEY proxy translation is not supported.\n");
+ goto err;
+ }
+
+ if (ext_msgs[SADB_EXT_LIFETIME_HARD]) {
+ error = sadb_lifetime_to_lifetime((struct sadb_lifetime*)ext_msgs[SADB_EXT_LIFETIME_HARD],
+ &sa_entry->lifetime_h);
+ if (error) goto err;
+ }
+
+ if (ext_msgs[SADB_EXT_LIFETIME_SOFT]) {
+ error = sadb_lifetime_to_lifetime((struct sadb_lifetime*)ext_msgs[SADB_EXT_LIFETIME_SOFT],
+ &sa_entry->lifetime_s);
+ if (error) goto err;
+ }
+
+ sa_entry->state = SADB_SASTATE_MATURE;
+
+ error = sadb_append(sa_entry);
+ if (error) goto err;
+
+ reply_ext_msgs[0] = (struct sadb_ext*) msg;
+ reply_ext_msgs[SADB_EXT_SA] = ext_msgs[SADB_EXT_SA];
+ reply_ext_msgs[SADB_EXT_ADDRESS_SRC] = ext_msgs[SADB_EXT_ADDRESS_SRC];
+ reply_ext_msgs[SADB_EXT_ADDRESS_DST] = ext_msgs[SADB_EXT_ADDRESS_DST];
+
+ if (ext_msgs[SADB_EXT_LIFETIME_HARD])
+ reply_ext_msgs[SADB_EXT_LIFETIME_HARD] = ext_msgs[SADB_EXT_LIFETIME_HARD];
+
+ if (ext_msgs[SADB_EXT_LIFETIME_SOFT])
+ reply_ext_msgs[SADB_EXT_LIFETIME_SOFT] = ext_msgs[SADB_EXT_LIFETIME_SOFT];
+
+ error = pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+ goto rtn;
+ } else {
+ PFKEY_DEBUG("extensions not enough\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+err:
+ ipsec_sa_kfree(sa_entry);
+rtn:
+ return error;
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg_delete.c linux25.43-ipsec/net/key/pfkey_v2_msg_delete.c
--- linux-2.5.43/net/key/pfkey_v2_msg_delete.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg_delete.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,127 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+/*
+ * This is a parse routine for a message of SADB_DELETE.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/ipsec.h>
+
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+
+#include "pfkey_v2_msg.h"
+
+#define BUFSIZE 64
+
+int sadb_msg_delete_parse(struct sock *sk, struct sadb_msg *msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1], *reply_ext_msgs[SADB_EXT_MAX+1];
+ struct sadb_sa reply_sa;
+ struct sadb_sa *sa = NULL;
+ struct sadb_address *src = NULL;
+ struct sadb_address *dst = NULL;
+ struct sockaddr_storage saddr, daddr;
+ struct ipsec_sa *ipsec_sa = NULL;
+ uint8_t prefixlen_s, prefixlen_d;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+ if (error) {
+ PFKEY_DEBUG("error in sadb_msg_detect_ext\n");
+ goto err;
+ }
+
+ if (ext_msgs[SADB_EXT_SA] &&
+ ext_msgs[SADB_EXT_ADDRESS_SRC] &&
+ ext_msgs[SADB_EXT_ADDRESS_DST])
+ {
+ sa = (struct sadb_sa*)ext_msgs[SADB_EXT_SA];
+
+ src = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_SRC];
+ dst = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_DST];
+
+ memset(&saddr, 0, sizeof(struct sockaddr_storage));
+ memset(&daddr, 0, sizeof(struct sockaddr_storage));
+
+ error = sadb_address_to_sockaddr(src, (struct sockaddr*)&saddr);
+ if (error) {
+ PFKEY_DEBUG("src translate\n");
+ goto err;
+ }
+ error = sadb_address_to_sockaddr(dst, (struct sockaddr*)&daddr);
+ if (error) {
+ PFKEY_DEBUG("dst translate\n");
+ goto err;
+ }
+
+ prefixlen_s = src->sadb_address_prefixlen;
+ prefixlen_d = dst->sadb_address_prefixlen;
+
+ error = sadb_find_by_address_proto_spi( (struct sockaddr*)&saddr, prefixlen_s,
+ (struct sockaddr*)&daddr, prefixlen_d,
+ msg->sadb_msg_satype,
+ sa->sadb_sa_spi,
+ &ipsec_sa);
+
+ if (error == -EEXIST) {
+ ipsec_sa_put(ipsec_sa);
+ sadb_remove(ipsec_sa);
+ error = 0;
+ }else{
+ error = -ESRCH;
+ goto err;
+ }
+ }
+
+ memset(&reply_sa, 0, sizeof(struct sadb_sa));
+ reply_sa.sadb_sa_len = sizeof(struct sadb_sa) / 8;
+ reply_sa.sadb_sa_exttype = SADB_EXT_SA;
+ reply_sa.sadb_sa_spi = sa->sadb_sa_spi;
+ reply_ext_msgs[0] = (struct sadb_ext*) msg;
+ reply_ext_msgs[SADB_EXT_SA] = (struct sadb_ext*)&reply_sa;
+ reply_ext_msgs[SADB_EXT_ADDRESS_SRC] = ext_msgs[SADB_EXT_ADDRESS_SRC];
+ reply_ext_msgs[SADB_EXT_ADDRESS_DST] = ext_msgs[SADB_EXT_ADDRESS_DST];
+ error = pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+err:
+ return error;
+}
+
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg_flow.c linux25.43-ipsec/net/key/pfkey_v2_msg_flow.c
--- linux-2.5.43/net/key/pfkey_v2_msg_flow.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg_flow.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,474 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ */
+/*
+ * This is a parse routine for messages SADB_EXT_ADDFLOW and SADB_EXT_DELFLOW.
+ * We use FreeS/WAN's user land routine for manipulating SA and Policy.
+ * These are FreeS/WAN specific.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+
+#include "pfkey_v2_msg.h"
+#include "sockaddr_utils.h"
+
+#define BUFSIZE 64
+
+static int sadb_mask_to_prefixlen(const struct sadb_address *ext_msg)
+{
+ int rtn = 0;
+#ifdef CONFIG_IPSEC_DEBUG
+ int len;
+ char buf[BUFSIZE];
+#endif
+
+ struct sockaddr* tmp_addr = NULL;
+
+ if (!ext_msg) {
+ PFKEY_DEBUG("ext_msg is NULL\n");
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_IPSEC_DEBUG
+ len = ext_msg->sadb_address_len - sizeof(struct sadb_address);
+ if (len < sizeof(struct sockaddr)) {
+ PFKEY_DEBUG("sadb_address_len is small len=%d\n", len);
+ return -EINVAL;
+ }
+#endif
+
+ tmp_addr = (struct sockaddr*)((char*)ext_msg + sizeof(struct sadb_address));
+
+ switch (tmp_addr->sa_family) {
+
+ case AF_INET:
+ {
+ int i;
+ __u32 tmp;
+#ifdef CONFIG_IPSEC_DEBUG
+ PFKEY_DEBUG("address family is AF_INET\n");
+ sockaddrtoa(tmp_addr, buf, BUFSIZE);
+ PFKEY_DEBUG("address %s\n", buf);
+#endif
+ tmp = ntohl(((struct sockaddr_in*)tmp_addr)->sin_addr.s_addr);
+ for (i=0; i<32; i++) {
+ if ((tmp>>i)&0x01)
+ break;
+ }
+ rtn = 32 - i;
+ }
+ break;
+
+ case AF_INET6:
+ {
+ int i,j;
+ __u16 tmp;
+#ifdef CONFIG_IPSEC_DEBUG
+ PFKEY_DEBUG("address family is AF_INET6\n");
+ sockaddrtoa(tmp_addr, buf, BUFSIZE);
+ PFKEY_DEBUG("address %s\n", buf);
+#endif
+ for (i=0; i<8; i++ ) {
+ tmp = ntohs(((struct sockaddr_in6*)tmp_addr)->sin6_addr.s6_addr16[7-i]);
+ for (j=0; j<16; j++) {
+ if ((tmp>>j)&0x01) {
+ rtn = 128 - i*16 - j;
+ PFKEY_DEBUG("result i=%d, j=%d\n", i, j);
+ goto err;
+ }
+ }
+ }
+ }
+ break;
+ default:
+ PFKEY_DEBUG("address family is unknown\n");
+ rtn = -EINVAL;
+ goto err;
+ }
+
+err:
+#ifdef CONFIG_IPSEC_DEBUG
+ if (rtn)
+ PFKEY_DEBUG("prefixlen=%d\n", rtn);
+#endif
+ return rtn;
+}
+
+
+int sadb_msg_addflow_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ int tmp = 0;
+ char buf[BUFSIZE];
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1], *reply_ext_msgs[SADB_EXT_MAX+1];
+ struct sadb_sa *sa = NULL;
+ struct sadb_address *dst = NULL;
+ struct sadb_address *sflow = NULL;
+ struct sadb_address *dflow = NULL;
+ struct ipsec_sp *policy = NULL;
+ struct sa_index sa_idx;
+ struct selector selector;
+
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+ if (ext_msgs[SADB_EXT_SA] &&
+ ext_msgs[SADB_EXT_ADDRESS_DST] &&
+ ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW] &&
+ ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW])
+ {
+ sa = (struct sadb_sa*)ext_msgs[SADB_EXT_SA];
+ PFKEY_DEBUG("sa.spi=0x%x\n", ntohl(sa->sadb_sa_spi));
+ PFKEY_DEBUG("sa.auth=%u\n", ntohs(sa->sadb_sa_auth));
+ PFKEY_DEBUG("sa.encrypt=%u\n", ntohs(sa->sadb_sa_encrypt));
+#ifdef CONFIG_IPSEC_TUNNEL
+ PFKEY_DEBUG("sa mode=%d\n", sa->sadb_sa_flags & SADB_X_SAFLAGS_TUNNEL);
+#endif
+ dst = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_DST];
+ sflow = (struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW];
+ dflow = (struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW];
+
+#ifdef CONFIG_IPSEC_TUNNEL
+ selector.mode = sa->sadb_sa_flags & SADB_X_SAFLAGS_TUNNEL
+ ? IPSEC_MODE_TUNNEL
+ : IPSEC_MODE_TRANSPORT;
+
+ if (selector.mode == IPSEC_MODE_TRANSPORT) {
+#endif
+ if (sflow->sadb_address_proto == dflow->sadb_address_proto) {
+ selector.proto = sflow->sadb_address_proto;
+ } else {
+ error = -EINVAL;
+ goto err;
+ }
+#ifdef CONFIG_IPSEC_TUNNEL
+ }
+#endif
+ /* assuming an SA is not needed if policy is
+ * bypass or discard the packet. */
+ if ((ntohl(sa->sadb_sa_spi) != IPSEC_SPI_DROP)
+ && (ntohl (sa->sadb_sa_spi) != IPSEC_SPI_BYPASS)) {
+ sa_index_init(&sa_idx);
+
+ sa_idx.ipsec_proto = msg->sadb_msg_satype;
+ sa_idx.spi = sa->sadb_sa_spi;
+
+ sadb_address_to_sockaddr(dst, (struct sockaddr*)&sa_idx.dst);
+ sa_idx.prefixlen_d = dst->sadb_address_prefixlen;
+#ifdef CONFIG_IPSEC_DEBUG
+ memset(buf, 0, BUFSIZE);
+ sockaddrtoa((struct sockaddr*)&sa_idx.dst, buf, BUFSIZE);
+ PFKEY_DEBUG("dst=%s\n", buf);
+#endif /* CONFIG_IPSEC_DEBUG */
+ }
+
+ memset(buf, 0, BUFSIZE);
+ sadb_address_to_sockaddr( sflow, (struct sockaddr*)&selector.src);
+ sockaddrtoa((struct sockaddr*)&selector.src, buf, BUFSIZE);
+ PFKEY_DEBUG("sflow=%s\n", buf);
+
+ memset(buf, 0, BUFSIZE);
+ sadb_address_to_sockaddr( dflow, (struct sockaddr*)&selector.dst);
+ sockaddrtoa((struct sockaddr*)&selector.dst, buf, BUFSIZE);
+ PFKEY_DEBUG("dflow=%s\n", buf);
+
+ selector.prefixlen_s = sflow->sadb_address_prefixlen;
+ PFKEY_DEBUG("prefixlen_s=%d\n", selector.prefixlen_s);
+ selector.prefixlen_d = dflow->sadb_address_prefixlen;
+ PFKEY_DEBUG("prefixlen_d=%d\n", selector.prefixlen_d);
+
+ if (ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK]) {
+ tmp = sadb_mask_to_prefixlen((struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK]);
+ if (tmp<0) {
+ error = tmp;
+ goto err;
+ } else {
+ selector.prefixlen_s = tmp;
+ }
+ }
+
+ if (ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK]) {
+ tmp = sadb_mask_to_prefixlen((struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK]);
+ if (tmp<0) {
+ error = tmp;
+ goto err;
+ } else {
+ selector.prefixlen_d = tmp;
+ }
+ }
+
+ error = spd_find_by_selector(&selector, &policy);
+
+ PFKEY_DEBUG("spd_find error = %d\n", error);
+
+ if (error == -ESRCH) {
+
+ /* It's new one. I append it into spd_list */
+
+ policy = ipsec_sp_kmalloc();
+ if (!policy) {
+ PFKEY_DEBUG("ipsec_sp_kmalloc faild\n");
+ error = -ENOMEM;
+ goto err;
+ }
+ memcpy(&policy->selector, &selector, sizeof(struct selector));
+
+ if (ntohl(sa->sadb_sa_spi) == IPSEC_SPI_DROP) {
+ policy->policy_action = IPSEC_POLICY_DROP;
+ error = 0;
+ } else if (ntohl(sa->sadb_sa_spi) == IPSEC_SPI_BYPASS) {
+ policy->policy_action = IPSEC_POLICY_BYPASS;
+ error = 0;
+ } else {
+ policy->policy_action = IPSEC_POLICY_APPLY;
+ switch (sa_idx.ipsec_proto) {
+ case SADB_SATYPE_AH:
+ policy->auth_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->auth_sa_idx, &sa_idx);
+ break;
+ case SADB_SATYPE_ESP:
+ policy->esp_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->esp_sa_idx, &sa_idx);
+ break;
+ case SADB_X_SATYPE_COMP: /* Currently, just set as SA related with ESP which you specify by SPI. */
+ policy->comp_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->comp_sa_idx, &sa_idx);
+ printk(KERN_DEBUG "set IPComp policy.\n");
+ break;
+ default:
+ error = -EINVAL;
+ ipsec_sp_put(policy);
+ goto err;
+ }
+ }
+
+ if (!error) {
+ error = spd_append(policy);
+ }
+
+ ipsec_sp_put(policy);
+ error = 0;
+
+ } else if (error == -EEXIST) {
+
+ /* It has already been in spd_list, I append sa_index into it's sa_list */
+ write_lock_bh(&policy->lock);
+ PFKEY_DEBUG("policy=%p\n", policy);
+
+ switch(sa_idx.ipsec_proto) {
+
+ case SADB_SATYPE_AH:
+ if (!policy->auth_sa_idx) {
+ policy->auth_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->auth_sa_idx, &sa_idx);
+
+ } else if (sa->sadb_sa_flags & SADB_X_SAFLAGS_REPLACEFLOW) {
+ sa_index_kfree(policy->auth_sa_idx);
+ policy->auth_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->auth_sa_idx, &sa_idx);
+ }
+
+ break;
+
+ case SADB_SATYPE_ESP:
+ if (!policy->esp_sa_idx) {
+ policy->esp_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->esp_sa_idx, &sa_idx);
+
+ } else if (sa->sadb_sa_flags & SADB_X_SAFLAGS_REPLACEFLOW) {
+ sa_index_kfree(policy->esp_sa_idx);
+ policy->esp_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->esp_sa_idx, &sa_idx);
+ }
+
+ break;
+
+ case SADB_X_SATYPE_COMP:
+ if (!policy->comp_sa_idx) {
+ policy->comp_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->comp_sa_idx, &sa_idx);
+ } else if (sa->sadb_sa_flags & SADB_X_SAFLAGS_REPLACEFLOW) {
+ sa_index_kfree(policy->comp_sa_idx);
+ policy->comp_sa_idx = sa_index_kmalloc();
+ error = sa_index_copy(policy->comp_sa_idx, &sa_idx);
+ }
+
+ break;
+
+ default:
+ error = -EINVAL;
+ write_unlock_bh(&policy->lock);
+ ipsec_sp_put(policy);
+ goto err;
+ }
+
+ if (error) {
+ write_unlock_bh(&policy->lock);
+ ipsec_sp_put(policy);
+ goto err;
+ }
+
+ write_unlock_bh(&policy->lock);
+ ipsec_sp_put(policy);
+ error = 0;
+
+ } else {
+ /* Something is wrong */
+ PFKEY_DEBUG("spd_find_by_selector faild\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ }
+
+err:
+ reply_ext_msgs[0] = (struct sadb_ext*) msg;
+ reply_ext_msgs[SADB_EXT_SA] = ext_msgs[SADB_EXT_SA];
+ reply_ext_msgs[SADB_EXT_ADDRESS_SRC] = ext_msgs[SADB_EXT_ADDRESS_SRC];
+ reply_ext_msgs[SADB_EXT_ADDRESS_DST] = ext_msgs[SADB_EXT_ADDRESS_DST];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW] = ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW] = ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK] = ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK] = ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK];
+ error = pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+
+ return error;
+
+}
+
+int sadb_msg_delflow_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ int tmp = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1], *reply_ext_msgs[SADB_EXT_MAX+1];
+ struct sadb_sa *sa = NULL;
+ struct sadb_address *sflow = NULL;
+ struct sadb_address *dflow = NULL;
+ struct selector selector;
+
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+ if (ext_msgs[SADB_EXT_SA] &&
+ ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW] &&
+ ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW])
+ {
+ sa = (struct sadb_sa*)ext_msgs[SADB_EXT_SA];
+
+ sflow = (struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW];
+ dflow = (struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW];
+#ifdef CONFIG_IPSEC_TUNNEL
+ selector.mode = sa->sadb_sa_flags & SADB_X_SAFLAGS_TUNNEL
+ ? IPSEC_MODE_TUNNEL
+ : IPSEC_MODE_TRANSPORT;
+
+ if (selector.mode == IPSEC_MODE_TRANSPORT) {
+#endif
+ if (sflow->sadb_address_proto == dflow->sadb_address_proto) {
+ selector.proto = sflow->sadb_address_proto;
+ } else {
+ error = -EINVAL;
+ goto err;
+ }
+#ifdef CONFIG_IPSEC_TUNNEL
+ }
+#endif
+ sadb_address_to_sockaddr( sflow, (struct sockaddr*)&selector.src);
+ sadb_address_to_sockaddr( dflow, (struct sockaddr*)&selector.dst);
+ selector.prefixlen_s = sflow->sadb_address_prefixlen;
+ selector.prefixlen_d = dflow->sadb_address_prefixlen;
+
+ if (ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK]) {
+ tmp = sadb_mask_to_prefixlen(
+ (struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK]);
+ if (tmp<0) {
+ error = tmp;
+ goto err;
+ } else {
+ selector.prefixlen_s = tmp;
+ }
+ }
+
+ if (ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK]) {
+ tmp = sadb_mask_to_prefixlen(
+ (struct sadb_address*)ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK]);
+ if (tmp<0) {
+ error = tmp;
+ goto err;
+ } else {
+ selector.prefixlen_d = tmp;
+ }
+ }
+
+ error = spd_remove(&selector);
+
+ if (error) {
+ PFKEY_DEBUG("spd_remove failed\n");
+ goto err;
+ }
+
+ }
+
+ reply_ext_msgs[0] = (struct sadb_ext*) msg;
+ reply_ext_msgs[SADB_EXT_SA] = ext_msgs[SADB_EXT_SA];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW] = ext_msgs[SADB_X_EXT_ADDRESS_SRC_FLOW];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW] = ext_msgs[SADB_X_EXT_ADDRESS_DST_FLOW];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK] = ext_msgs[SADB_X_EXT_ADDRESS_SRC_MASK];
+ reply_ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK] = ext_msgs[SADB_X_EXT_ADDRESS_DST_MASK];
+ error = pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+
+err:
+ return error;
+
+}
+
+
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg_get.c linux25.43-ipsec/net/key/pfkey_v2_msg_get.c
--- linux-2.5.43/net/key/pfkey_v2_msg_get.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg_get.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,212 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ */
+/*
+ * This is a parse routine for a message of SADB_GET.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+
+#include "pfkey_v2_msg.h"
+
+#define BUFSIZE 64
+
+int sadb_msg_get_parse(struct sock *sk, struct sadb_msg *msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1], *reply_ext_msgs[SADB_EXT_MAX+1];
+ struct sadb_sa *sa_msg = NULL;
+ struct sadb_address *src = NULL;
+ struct sadb_address *dst = NULL;
+ struct ipsec_sa *sa_entry = NULL;
+ struct sockaddr_storage saddr, daddr;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+ if (error) {
+ PFKEY_DEBUG("sadb_msg_detect_ext faild\n");
+ goto err;
+ }
+
+ if (ext_msgs[SADB_EXT_SA] &&
+ ext_msgs[SADB_EXT_ADDRESS_SRC] &&
+ ext_msgs[SADB_EXT_ADDRESS_DST])
+ {
+ sa_msg = (struct sadb_sa*)ext_msgs[SADB_EXT_SA];
+
+ src = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_SRC];
+ dst = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_DST];
+
+ error = sadb_address_to_sockaddr( src, (struct sockaddr*)&saddr);
+ if (error) {
+ PFKEY_DEBUG("src translation failed\n");
+ goto err;
+ }
+ error = sadb_address_to_sockaddr( dst, (struct sockaddr*)&daddr);
+ if (error) {
+ PFKEY_DEBUG("dst translation failed\n");
+ goto err;
+ }
+
+ error = sadb_find_by_address_proto_spi((struct sockaddr*)&saddr, src->sadb_address_prefixlen,
+ (struct sockaddr*)&daddr, dst->sadb_address_prefixlen,
+ msg->sadb_msg_type,
+ sa_msg->sadb_sa_spi,
+ &sa_entry);
+
+
+ if (error == -EEXIST) {
+
+ read_lock_bh(&sa_entry->lock);
+
+ error=pfkey_sa_build(&reply_ext_msgs[SADB_EXT_SA], SADB_EXT_SA,
+ sa_entry->spi, sa_entry->replay_window.size,
+ sa_entry->state, sa_entry->auth_algo.algo,
+ sa_entry->esp_algo.algo, SADB_SAFLAGS_PFS );
+ if (error) {
+ PFKEY_DEBUG("pfkey_sa_build faild\n");
+ goto err;
+ }
+
+ error = lifetime_to_sadb_lifetime(&sa_entry->lifetime_c,
+ (struct sadb_lifetime*)reply_ext_msgs[SADB_EXT_LIFETIME_CURRENT],
+ SADB_EXT_LIFETIME_CURRENT);
+ if (error) {
+ PFKEY_DEBUG("lifetime_to_sadb_lifetime failed\n");
+ goto err;
+ }
+
+ error = lifetime_to_sadb_lifetime(&sa_entry->lifetime_h,
+ (struct sadb_lifetime*)reply_ext_msgs[SADB_EXT_LIFETIME_HARD],
+ SADB_EXT_LIFETIME_HARD);
+ if (error) {
+ PFKEY_DEBUG("lifetime_to_sadb_lifetime failed\n");
+ goto err;
+ }
+
+ error = lifetime_to_sadb_lifetime(&sa_entry->lifetime_s,
+ (struct sadb_lifetime*)reply_ext_msgs[SADB_EXT_LIFETIME_SOFT],
+ SADB_EXT_LIFETIME_SOFT);
+ if (error) {
+ PFKEY_DEBUG("lifetime_to_sadb_lifetime failed\n");
+ goto err;
+ }
+
+ error = pfkey_address_build((struct sadb_ext**)&reply_ext_msgs[SADB_EXT_ADDRESS_SRC],
+ SADB_EXT_ADDRESS_SRC,
+ sa_entry->proto,
+ sa_entry->prefixlen_s,
+ (struct sockaddr*)&sa_entry->src);
+ if (error) {
+ PFKEY_DEBUG("pfkey_address_build faild\n");
+ goto err;
+ }
+
+ error = pfkey_address_build((struct sadb_ext**)&reply_ext_msgs[SADB_EXT_ADDRESS_DST],
+ SADB_EXT_ADDRESS_DST,
+ sa_entry->proto,
+ sa_entry->prefixlen_d,
+ (struct sockaddr*)&sa_entry->dst);
+ if (error) {
+ PFKEY_DEBUG("pfkey_address_build faild\n");
+ goto err;
+ }
+
+ if (sa_entry->ipsec_proto == SADB_SATYPE_AH) {
+ error = pfkey_key_build((struct sadb_ext**)&reply_ext_msgs[SADB_EXT_KEY_AUTH],
+ SADB_EXT_KEY_AUTH,
+ sa_entry->auth_algo.key_len*sizeof(__u8),
+ sa_entry->auth_algo.key);
+ if (error) {
+ PFKEY_DEBUG("pfkey_key_build faild\n");
+ goto err;
+ }
+ }
+
+ if (sa_entry->ipsec_proto == SADB_SATYPE_ESP) {
+#ifndef CONFIG_IPSEC_DEBUG
+ error = pfkey_key_build((struct sadb_ext**)&reply_ext_msgs[SADB_EXT_KEY_ENCRYPT],
+ SADB_EXT_KEY_ENCRYPT,
+ sa_entry->esp_algo.key_len*sizeof(__u8),
+ (char*)sa_entry->esp_algo.cx->ci);
+#else /* CONFIG_IPSEC_DEBUG */
+ if (sa_entry->esp_algo.algo != SADB_EALG_NULL) {
+ error = pfkey_key_build((struct sadb_ext**)&reply_ext_msgs[SADB_EXT_KEY_ENCRYPT],
+ SADB_EXT_KEY_ENCRYPT,
+ sa_entry->esp_algo.key_len*sizeof(__u8),
+ (char*)sa_entry->esp_algo.cx->ci);
+ } else {
+ struct sadb_key *tmp_key_msg = kmalloc(sizeof(struct sadb_key), GFP_KERNEL);
+ if (tmp_key_msg) {
+ PFKEY_DEBUG("could not allocat tmp_key_msg");
+ error = -ENOMEM;
+ goto err;
+ }
+ tmp_key_msg->sadb_key_len = sizeof(struct sadb_key)/8;
+ tmp_key_msg->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+ tmp_key_msg->sadb_key_bits = 0;
+ tmp_key_msg->sadb_key_reserved = 0;
+ reply_ext_msgs[SADB_EXT_KEY_ENCRYPT] = (struct sadb_ext *)tmp_key_msg;
+ }
+#endif /* CONFIG_IPSEC_DEBUG */
+ if (error) {
+ PFKEY_DEBUG("pfkey_key_build faild\n");
+ goto err;
+ }
+ }
+
+ read_unlock_bh(&sa_entry->lock);
+ ipsec_sa_put(sa_entry);
+
+ } else {
+ PFKEY_DEBUG("sadb_find_by_address_spi faild\n");
+ goto err;
+ }
+ }
+
+ reply_ext_msgs[0] = (struct sadb_ext*) msg;
+ pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+ pfkey_extensions_free(reply_ext_msgs);
+err:
+ return error;
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg_getspi.c linux25.43-ipsec/net/key/pfkey_v2_msg_getspi.c
--- linux-2.5.43/net/key/pfkey_v2_msg_getspi.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg_getspi.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,191 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+/*
+ * This is a parse routine for a message of SADB_GETSPI.
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/ipsec.h>
+
+#include <linux/pfkeyv2.h>
+#include <linux/pfkey.h>
+#include <net/sadb.h>
+
+#include "pfkey_v2_msg.h"
+
+#define BUFSIZE 64
+
+int sadb_msg_getspi_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1], *reply_ext_msgs[SADB_EXT_MAX+1];
+ int error = 0, found_avail = 0;
+ __u32 newspi = 0;
+ __u32 spi_max = 0, spi_min = 0;
+ struct ipsec_sa sadb_entry;
+ struct sadb_address *src, *dst;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+ if (error) {
+ PFKEY_DEBUG("error in sadb_msg_detect_ext\n");
+ goto err;
+ }
+
+ memset(reply_ext_msgs, 0, sizeof(reply_ext_msgs));
+
+ if (ext_msgs[SADB_EXT_ADDRESS_SRC] &&
+ ext_msgs[SADB_EXT_ADDRESS_DST] &&
+ ext_msgs[SADB_EXT_SPIRANGE])
+ {
+ src = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_SRC];
+ dst = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_DST];
+
+ memset(&sadb_entry, 0, sizeof(struct ipsec_sa));
+ error = sadb_address_to_sockaddr(src, (struct sockaddr*)&sadb_entry.src);
+ if (error) {
+ PFKEY_DEBUG("error in translate src address\n");
+ goto err;
+ }
+ sadb_entry.prefixlen_s = src->sadb_address_prefixlen;
+
+ error = sadb_address_to_sockaddr(dst, (struct sockaddr*)&sadb_entry.dst);
+ if (error) {
+ PFKEY_DEBUG("error in translate dst address\n");
+ }
+ sadb_entry.prefixlen_d = dst->sadb_address_prefixlen;
+
+ spi_min = ((struct sadb_spirange*)ext_msgs[SADB_EXT_SPIRANGE])->sadb_spirange_min;
+ spi_max = ((struct sadb_spirange*)ext_msgs[SADB_EXT_SPIRANGE])->sadb_spirange_max;
+
+ /* SPI which is under 255 is reserved by IANA.
+ * Additionally, 256 and 257 reserved ofr internal use. */
+ if (spi_min < 258) {
+ PFKEY_DEBUG("SPI value is reserved.(SPI<258)\n");
+ goto err;
+ }
+
+ if (spi_min == spi_max) {
+ PFKEY_DEBUG("spi_min and spi_max are equal\n");
+
+ error = sadb_find_by_address_proto_spi((struct sockaddr*)&sadb_entry.src, sadb_entry.prefixlen_s,
+ (struct sockaddr*)&sadb_entry.dst, sadb_entry.prefixlen_d,
+ spi_min, msg->sadb_msg_type, NULL /*only check*/);
+
+ if (error == -ESRCH) {
+ newspi = spi_min;
+ found_avail = 1;
+ } else {
+ PFKEY_DEBUG("sadb_find_by_address_proto_spi return %d\n", error);
+ goto err;
+ }
+
+ } else if (ntohl(spi_min) < ntohl(spi_max)) {
+ /* This codes are derived from FreeS/WAN */
+ int i = 0;
+ __u32 rand_val;
+ __u32 spi_diff;
+
+ PFKEY_DEBUG("spi_min and spi_max are defference\n");
+
+ while ( ( i < (spi_diff = (ntohl(spi_max) - ntohl(spi_min)))) && !found_avail ) {
+ get_random_bytes((void*) &rand_val,
+ /* sizeof(extr->tdb->tdb_said.spi) */
+ ( (spi_diff < (2^8)) ? 1 :
+ ( (spi_diff < (2^16)) ? 2 :
+ ( (spi_diff < (2^24)) ? 3 :
+ 4 ) ) ) );
+ newspi = htonl(ntohl(spi_min) +
+ (rand_val % (spi_diff + 1)));
+ PFKEY_DEBUG("new spi is %d\n", ntohl(newspi));
+
+ i++;
+ error = sadb_find_by_address_proto_spi( (struct sockaddr*)&sadb_entry.src, sadb_entry.prefixlen_s,
+ (struct sockaddr*)&sadb_entry.dst, sadb_entry.prefixlen_d,
+ newspi, msg->sadb_msg_type, NULL /* only check */);
+ if (error == -ESRCH) {
+ found_avail = 1;
+ break;
+ } else {
+ PFKEY_DEBUG("sadb_find_by_address_proto_spi return %d\n", error);
+ goto err;
+ }
+ }
+
+ } else {
+ PFKEY_DEBUG("invalid spi range\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (found_avail) {
+ sadb_entry.spi = newspi;
+ sadb_entry.state = SADB_SASTATE_LARVAL;
+ error = sadb_append(&sadb_entry);
+ if (error) {
+ PFKEY_DEBUG("sadb_append return %d\n", error);
+ goto err;
+ }
+ } else {
+ PFKEY_DEBUG("could not find available spi\n");
+ goto err;
+ }
+
+ } else {
+ PFKEY_DEBUG("necessary ext messages are not available\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ error = pfkey_sa_build(&reply_ext_msgs[SADB_EXT_SA], SADB_EXT_SA,
+ newspi, 0, 0, 0, 0, SADB_SAFLAGS_PFS);
+ if (error) {
+ PFKEY_DEBUG("pfkey_address_build faild\n");
+ goto err;
+ }
+
+ reply_ext_msgs[0] = (struct sadb_ext*) msg;
+ reply_ext_msgs[SADB_EXT_ADDRESS_SRC] = ext_msgs[SADB_EXT_ADDRESS_SRC];
+ reply_ext_msgs[SADB_EXT_ADDRESS_DST] = ext_msgs[SADB_EXT_ADDRESS_DST];
+ error = pfkey_msg_build(reply, reply_ext_msgs, EXT_BITS_OUT);
+err:
+ return error;
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/pfkey_v2_msg_update.c linux25.43-ipsec/net/key/pfkey_v2_msg_update.c
--- linux-2.5.43/net/key/pfkey_v2_msg_update.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/pfkey_v2_msg_update.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,135 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+/*
+ * This is a parse routine for a message of SADB_UPDATE.
+ */
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/random.h>
+#include <linux/ipsec.h>
+
+#include <linux/pfkeyv2.h>
+#include <net/sadb.h>
+
+#include "pfkey_v2_msg.h"
+
+#define BUFSIZE 64
+
+int sadb_msg_update_parse(struct sock *sk, struct sadb_msg* msg, struct sadb_msg **reply)
+{
+ int error = 0;
+ __u32 spi = 0;
+ struct sadb_ext *ext_msgs[SADB_EXT_MAX+1];
+ struct sadb_sa *sa = NULL;
+ struct sadb_address *src = NULL;
+ struct sadb_address *dst = NULL;
+ struct ipsec_sa *sa_entry = NULL;
+ struct sockaddr_storage saddr, daddr, paddr;
+
+ if (!msg) {
+ PFKEY_DEBUG("msg is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(ext_msgs, 0, sizeof(ext_msgs));
+ error = sadb_msg_detect_ext(msg, ext_msgs);
+
+ if (ext_msgs[SADB_EXT_SA] &&
+ ext_msgs[SADB_EXT_ADDRESS_SRC] &&
+ ext_msgs[SADB_EXT_ADDRESS_DST] &&
+ (ext_msgs[SADB_EXT_KEY_AUTH] ||
+ ext_msgs[SADB_EXT_KEY_ENCRYPT]))
+ {
+
+ memset(&saddr, 0, sizeof(struct sockaddr_storage));
+ memset(&daddr, 0, sizeof(struct sockaddr_storage));
+ memset(&paddr, 0, sizeof(struct sockaddr_storage));
+
+ src = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_SRC];
+ dst = (struct sadb_address*)ext_msgs[SADB_EXT_ADDRESS_DST];
+
+ spi = sa->sadb_sa_spi;
+
+ error = sadb_address_to_sockaddr(src, (struct sockaddr*)&saddr);
+
+ if (error) {
+ PFKEY_DEBUG("src translation failed\n");
+ goto err;
+ }
+ error = sadb_address_to_sockaddr(dst, (struct sockaddr*)&daddr);
+ if (error) {
+ PFKEY_DEBUG("dst translation failed\n");
+ goto err;
+ }
+
+ if (ext_msgs[SADB_EXT_ADDRESS_PROXY]) {
+ printk(KERN_WARNING "PFKEY proxy translation is not supported.\n");
+ }
+
+ if (sa->sadb_sa_auth && !(ext_msgs[SADB_EXT_KEY_AUTH])) {
+ PFKEY_DEBUG("SA has auth algo but there is no key for auth\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (sa->sadb_sa_encrypt && !(ext_msgs[SADB_EXT_KEY_ENCRYPT])) {
+ PFKEY_DEBUG("SA has esp algo but there is no key for esp\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (ext_msgs[SADB_EXT_ADDRESS_PROXY]) {
+ PFKEY_DEBUG("PFKEY proxy translation not supported.\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ if (error) {
+ PFKEY_DEBUG("could not find SA\n");
+ goto err;
+ }
+
+ switch (sa_entry->state) {
+ case SADB_SASTATE_LARVAL:
+ case SADB_SASTATE_MATURE:
+ case SADB_SASTATE_DYING:
+ break;
+ case SADB_SASTATE_DEAD:
+ default:
+ }
+
+ }
+ /* mask auth key and esp key */
+err:
+ return error;
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/sa_index.c linux25.43-ipsec/net/key/sa_index.c
--- linux-2.5.43/net/key/sa_index.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/sa_index.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,143 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ */
+/*
+ * struct ipsec_sa is connected with struct ipsec_sp by struct sa_index
+ * The element sa of sa_index occasionlly NULL.
+ * Note that struct ipsec_sa has a reference count.
+ */
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/ipsec.h>
+#include <net/sadb.h>
+#include "sockaddr_utils.h"
+
+struct sa_index* sa_index_kmalloc()
+{
+ struct sa_index* sa_idx = NULL;
+
+ sa_idx = kmalloc(sizeof(struct sa_index), GFP_ATOMIC);
+
+ if (!sa_idx) {
+ SADB_DEBUG("kmalloc faild\n");
+ return NULL;
+ }
+
+ sa_index_init(sa_idx);
+
+ return sa_idx;
+}
+
+int sa_index_init(struct sa_index *sa)
+{
+ int error = 0;
+
+ if (!sa) {
+ SADB_DEBUG("SA is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(sa, 0, sizeof(struct sa_index));
+ INIT_LIST_HEAD(&sa->entry);
+
+err:
+ return error;
+}
+
+void sa_index_kfree(struct sa_index *sa_idx)
+{
+ if (!sa_idx) {
+ SADB_DEBUG("SA is NULL\n");
+ return;
+ }
+
+ if (sa_idx->sa) {
+ ipsec_sa_put((sa_idx)->sa);
+ SADB_DEBUG("ptr=%p,refcnt=%d\n",
+ sa_idx->sa, atomic_read(&sa_idx->sa->refcnt));
+ sa_idx->sa = NULL;
+ }
+
+ kfree(sa_idx);
+}
+
+int sa_index_copy(struct sa_index *dst, struct sa_index *src)
+{
+ int error = 0;
+
+ if (!dst || !src) {
+ SADB_DEBUG("dst or src is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ dst->dst = src->dst;
+ dst->prefixlen_d = src->prefixlen_d;
+ dst->ipsec_proto = src->ipsec_proto;
+ dst->spi = src->spi;
+ if (src->sa) {
+ dst->sa = src->sa;
+ atomic_inc(&dst->sa->refcnt);
+ SADB_DEBUG("ptr=%p,refcnt=%d\n",
+ dst->sa, atomic_read(&dst->sa->refcnt));
+ }
+
+err:
+ return error;
+}
+
+/* Currently sa_index_compare is used in ipsec6_input.c
+ * If ether sa_idx1 or sa_idx2 has SPI=0xFFFFFFFF, It ignores SPI.
+ * Please take care to use sa_index_compare in other codes.
+ *
+ * We use SPI=0xFFFFFFFF in policy as SPI is any.
+ */
+int sa_index_compare(struct sa_index *sa_idx1, struct sa_index *sa_idx2)
+{
+ if (!sa_idx1 || !sa_idx2) {
+ SADB_DEBUG("sa_idx1 or sa_idx2 is NULL\n");
+ return -EINVAL;
+ }
+
+ if (sa_idx1->spi != IPSEC_SPI_ANY && sa_idx2->spi != IPSEC_SPI_ANY) {
+ if (sa_idx1->spi != sa_idx2->spi)
+ return 1;
+ }
+
+ if (sa_idx1->ipsec_proto != sa_idx2->ipsec_proto)
+ return 1;
+
+ return sockaddr_prefix_compare((struct sockaddr*)&sa_idx1->dst, sa_idx1->prefixlen_d,
+ (struct sockaddr*)&sa_idx2->dst, sa_idx2->prefixlen_d);
+}
diff -uNr -x CVS linux-2.5.43/net/key/sadb.c linux25.43-ipsec/net/key/sadb.c
--- linux-2.5.43/net/key/sadb.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/sadb.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,853 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ *
+ * Acknowledgements:
+ * Joy Latten <
[email protected]>
+ *
+ */
+/*
+ * sadb.c is a program for IPsec SADB. It provide functions for SADB manipulation.
+ * struct ipsec_sa represents IPsec SA. It has a reference count and maneges itself.
+ * If you get a reference(pointer) of struct ipsec_sa by sadb_find_by_***, please
+ * call ipsec_sa_put when you abondan the reference.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <net/sadb.h>
+#include <net/spd.h>
+
+#include "pfkey_v2_msg.h"
+#include "sockaddr_utils.h"
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif /* CONFIG_PROC_FS */
+
+#define BUFSIZE 64
+
+/* sa_list : IPsec Security Association DataBase(SADB)
+ * sadb_lock : lock for SADB
+ */
+LIST_HEAD(sadb_list);
+rwlock_t sadb_lock = RW_LOCK_UNLOCKED;
+
+struct ipsec_sa *ipsec_sa_kmalloc()
+{
+ struct ipsec_sa *sa = NULL;
+
+ sa = (struct ipsec_sa *)kmalloc(sizeof(struct ipsec_sa), GFP_KERNEL);
+
+ if (!sa) {
+ SADB_DEBUG("SA couldn\'t be allocated\n");
+ return NULL;
+ }
+
+ SADB_DEBUG("kmalloc sa %p\n", sa);
+
+ ipsec_sa_init(sa);
+
+ return sa;
+}
+
+int ipsec_sa_init(struct ipsec_sa *sa)
+{
+ if (!sa) {
+ SADB_DEBUG("SA is NULL\n");
+ return -EINVAL;
+ }
+
+ memset(sa, 0, sizeof(struct ipsec_sa));
+ /* set default replay window size (32) -mk */
+ sa->replay_window.size = 32;
+ sa->init_time = jiffies;
+ sa->lifetime_c.addtime = (sa->init_time) / HZ;
+ sa->timer.expires = 0; /* 0 stands for timer is not added to timer list */
+ atomic_set(&sa->refcnt, 1);
+ init_timer(&sa->timer);
+ sa->lock = RW_LOCK_UNLOCKED;
+
+ return 0;
+}
+
+void ipsec_sa_kfree(struct ipsec_sa *sa)
+{
+ struct cipher_implementation *ci = NULL;
+ struct digest_implementation *di = NULL;
+
+ if (!sa) {
+ SADB_DEBUG("SA is NULL\n");
+ return;
+ }
+
+ if (atomic_read(&sa->refcnt)) {
+ SADB_DEBUG("SA has been referred.\n");
+ return;
+ }
+
+ if (sa->auth_algo.key)
+ kfree(sa->auth_algo.key);
+
+ if (sa->auth_algo.dx && sa->auth_algo.dx->di) {
+ di = sa->auth_algo.dx->di;
+ di->free_context(sa->auth_algo.dx);
+ di->unlock();
+ }
+
+ if (sa->esp_algo.key)
+ kfree(sa->esp_algo.key);
+
+ if (sa->esp_algo.cx && sa->esp_algo.cx->ci) {
+ ci = sa->esp_algo.cx->ci;
+ ci->wipe_context(sa->esp_algo.cx);
+ ci->free_context(sa->esp_algo.cx);
+ ci->unlock();
+ }
+
+ if (sa->esp_algo.iv)
+ kfree(sa->esp_algo.iv);
+
+ kfree(sa);
+
+}
+/* XXX dx/cx */
+int ipsec_sa_copy(struct ipsec_sa *dst, struct ipsec_sa *src)
+{
+ int error = 0;
+ if (!(src&&dst)) {
+ SADB_DEBUG("src or dst is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memcpy( dst, src, sizeof(struct ipsec_sa));
+
+ if (dst->auth_algo.algo != SADB_AALG_NONE) {
+ if (src->auth_algo.dx->digest_info) {
+ dst->auth_algo.dx->digest_info
+ = kmalloc(dst->auth_algo.dx->di->working_size, GFP_KERNEL);
+ if (!dst->auth_algo.dx->digest_info) {
+ SADB_DEBUG("cannot allocate digest_info\n");
+ error = -ENOMEM;
+ goto err;
+ }
+
+ memcpy(dst->auth_algo.dx->digest_info,
+ src->auth_algo.dx->digest_info,
+ dst->auth_algo.dx->di->working_size);
+ }
+
+ if (src->auth_algo.key) {
+ dst->auth_algo.key
+ = kmalloc(dst->auth_algo.key_len, GFP_KERNEL);
+ if (!dst->auth_algo.key) {
+ SADB_DEBUG("cannot allocate authkey\n");
+ error = -ENOMEM;
+ goto free_digest_info;
+ }
+
+ memcpy(dst->auth_algo.key,
+ src->auth_algo.key,
+ dst->auth_algo.key_len);
+ }
+ }
+
+ if (dst->esp_algo.algo != SADB_EALG_NULL && dst->esp_algo.algo != SADB_EALG_NONE) {
+ if (src->esp_algo.key) {
+ dst->esp_algo.key = kmalloc(dst->esp_algo.key_len, GFP_KERNEL);
+ if (!dst->esp_algo.key) {
+ SADB_DEBUG("cannot allocate espkey\n");
+ error = -ENOMEM;
+ goto free_auth_key;
+ }
+
+ memcpy(dst->esp_algo.key,
+ src->esp_algo.key,
+ dst->esp_algo.key_len);
+ }
+
+
+ if (src->esp_algo.cx->keyinfo && src->esp_algo.cx->ci) {
+ dst->esp_algo.cx->keyinfo
+ = kmalloc(dst->esp_algo.cx->ci->key_schedule_size, GFP_KERNEL);
+ if (!dst->esp_algo.cx->keyinfo) {
+ SADB_DEBUG("cannot allocate keyinfo\n");
+ error = -ENOMEM;
+ goto free_esp_key;
+ }
+
+ memcpy(dst->esp_algo.cx->keyinfo,
+ src->esp_algo.cx->keyinfo,
+ dst->esp_algo.cx->ci->key_schedule_size);
+ }
+
+ }
+
+ atomic_set(&(dst->refcnt),1);
+
+ return error;
+
+free_esp_key:
+ kfree(dst->esp_algo.key);
+free_auth_key:
+ kfree(dst->auth_algo.key);
+free_digest_info:
+ kfree(dst->auth_algo.dx->digest_info);
+err:
+ return error;
+}
+
+void ipsec_sa_put(struct ipsec_sa *sa)
+{
+ if (!sa) {
+ SADB_DEBUG("SA is NULL\n");
+ return;
+ }
+
+ write_lock_bh(&sa->lock);
+ SADB_DEBUG("prt=%p,refcnt=%d-1\n",
+ sa, atomic_read(&sa->refcnt));
+ if (atomic_dec_and_test(&sa->refcnt)) {
+
+ SADB_DEBUG("kfree prt=%p,refcnt=%d\n",
+ sa, atomic_read(&sa->refcnt));
+ write_unlock_bh(&sa->lock);
+
+ ipsec_sa_kfree(sa);
+
+ return;
+
+ }
+ write_unlock_bh(&sa->lock);
+}
+
+static __inline__ unsigned long __ipsec_sa_next_expire(struct ipsec_sa *sa)
+{
+ unsigned long schedule_time = 0;
+
+ switch(sa->state){
+
+ case SADB_SASTATE_LARVAL:
+ case SADB_SASTATE_MATURE:
+
+ if (sa->lifetime_s.addtime)
+ schedule_time = sa->init_time + sa->lifetime_s.addtime * HZ;
+
+ if (sa->lifetime_h.addtime) {
+ if (schedule_time) {
+ schedule_time = schedule_time < sa->init_time + sa->lifetime_h.addtime * HZ
+ ? schedule_time : sa->init_time + sa->lifetime_h.addtime * HZ;
+ } else {
+ schedule_time = sa->init_time + sa->lifetime_h.addtime * HZ;
+ }
+ }
+
+ if (sa->fuse_time) {
+ if (sa->lifetime_s.usetime) {
+ if (schedule_time) {
+ schedule_time = schedule_time < sa->fuse_time + sa->lifetime_s.usetime * HZ
+ ? schedule_time : sa->fuse_time + sa->lifetime_s.usetime * HZ;
+ } else {
+ schedule_time = sa->fuse_time + sa->lifetime_s.usetime * HZ;
+ }
+ }
+
+ if (sa->lifetime_h.usetime) {
+ if (schedule_time) {
+ schedule_time = schedule_time < sa->fuse_time + sa->lifetime_h.usetime * HZ
+ ? schedule_time : sa->fuse_time + sa->lifetime_h.usetime * HZ;
+ } else {
+ schedule_time = sa->fuse_time + sa->lifetime_h.usetime * HZ;
+ }
+ }
+ }
+
+ break;
+
+ case SADB_SASTATE_DYING:
+
+ if (sa->lifetime_h.addtime)
+ schedule_time = sa->init_time + sa->lifetime_h.addtime * HZ;
+
+ if (sa->fuse_time) {
+ if (sa->lifetime_h.usetime) {
+ if (schedule_time) {
+ schedule_time = schedule_time < sa->fuse_time + sa->lifetime_h.usetime * HZ
+ ? schedule_time : sa->fuse_time + sa->lifetime_h.usetime * HZ;
+ } else {
+ schedule_time = sa->fuse_time + sa->lifetime_h.usetime * HZ;
+ }
+ }
+ }
+
+ break;
+ }
+
+ return schedule_time;
+}
+
+void ipsec_sa_mod_timer(struct ipsec_sa *sa)
+{
+ unsigned long expire_time = 0;
+
+ if (!sa) {
+ SADB_DEBUG("SA is NULL");
+ return;
+ }
+
+ SADB_DEBUG("ipsec_sa_mod_timer called with %p\n", sa);
+
+ expire_time = __ipsec_sa_next_expire(sa);
+
+ if (!expire_time) return;
+
+ SADB_DEBUG("expire_time=%ld, jiffies=%ld\n", expire_time, jiffies);
+
+
+ /* if expire time is 0, it means no timer have added */
+ if (sa->timer.expires) {
+ mod_timer(&sa->timer, expire_time);
+ } else {
+ sa->timer.data = (unsigned long)sa;
+ sa->timer.function = ipsec_sa_lifetime_check;
+ sa->timer.expires = expire_time;
+ ipsec_sa_hold(sa); /* refernce count for timer */
+ add_timer(&sa->timer);
+ }
+}
+
+static __inline__ int __ipsec_sa_lifetime_check(struct ipsec_sa *sa)
+{
+ time_t ctime = jiffies; /* current time */
+
+ if (!sa) {
+ SADB_DEBUG("SA is NULL");
+ return -EINVAL;
+ }
+
+ if ( sa->lifetime_s.addtime && ctime >= (sa->init_time + sa->lifetime_s.addtime * HZ) ) {
+ printk(KERN_INFO "SA soft lifetime(addtime) over !\n");
+ sa->state = SADB_SASTATE_DYING;
+ /* XXX report the information to userland applications ? */
+ }
+
+ if ( sa->lifetime_s.usetime && sa->fuse_time && ctime >= (sa->fuse_time + sa->lifetime_s.usetime * HZ) ) {
+ printk(KERN_INFO "SA soft lifetime(usetime) over !\n");
+ sa->state = SADB_SASTATE_DYING;
+ /* XXX report the information to userland applications ? */
+ }
+
+ if ( sa->lifetime_h.addtime && ctime >= (sa->init_time + sa->lifetime_h.addtime * HZ) ) {
+ printk(KERN_INFO "SA hard lifetime(addtime) over! \n ");
+ sa->state = SADB_SASTATE_DEAD;
+ }
+
+ if ( sa->lifetime_h.usetime && sa->fuse_time && ctime >= (sa->fuse_time + sa->lifetime_h.usetime * HZ) ) {
+ printk(KERN_INFO "SA hard lifetime(usetime) over ! \n ");
+ sa->state = SADB_SASTATE_DEAD;
+ }
+
+
+ return sa->state;
+}
+
+void ipsec_sa_lifetime_check(unsigned long data)
+{
+ struct ipsec_sa *sa = (struct ipsec_sa*)data;
+ __u8 sa_state;
+
+ if(!sa)
+ return;
+
+ write_lock_bh(&sa->lock);
+ sa_state = __ipsec_sa_lifetime_check(sa);
+ SADB_DEBUG("SA state change to %d\n", sa_state);
+ switch(sa_state) {
+ case SADB_SASTATE_DYING:
+ ipsec_sa_mod_timer(sa);
+ sadb_msg_send_expire(sa);
+ sadb_msg_send_acquire(sa);
+ write_unlock_bh(&sa->lock);
+ break;
+ case SADB_SASTATE_DEAD:
+ sadb_msg_send_expire(sa);
+ write_unlock_bh(&sa->lock);
+ sadb_remove(sa);
+ break;
+ case SADB_SASTATE_LARVAL:
+ case SADB_SASTATE_MATURE:
+ default:
+ write_unlock_bh(&sa->lock);
+ break;
+ }
+}
+
+int sadb_append(struct ipsec_sa *sa)
+{
+ int error = 0;
+ struct sa_index tmp;
+
+ if (!sa) {
+ SADB_DEBUG("SA is NULL.\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ memcpy(&tmp.dst, &sa->dst, (sa->dst.ss_family == AF_INET) ? sizeof(struct sockaddr_in) :
+ (sa->dst.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) :
+ sizeof(struct sockaddr_storage));
+ tmp.prefixlen_d = sa->prefixlen_d;
+ tmp.ipsec_proto = sa->ipsec_proto;
+ tmp.spi = sa->spi;
+
+ tmp.sa = sadb_find_by_sa_index(&tmp);
+
+ if (tmp.sa) {
+ SADB_DEBUG("sa already exist\n");
+ error = -EEXIST;
+ goto err;
+ }
+
+ ipsec_sa_mod_timer(sa);
+
+ write_lock_bh(&sadb_lock);
+ list_add_tail(&sa->entry, &sadb_list);
+ write_unlock_bh(&sadb_lock);
+
+ error = 0;
+err:
+ return error;
+}
+
+static void __sadb_remove(struct ipsec_sa *sa)
+{
+ struct list_head *pos = NULL;
+ struct ipsec_sp *tmp_sp = NULL;
+
+ if(!sa)
+ return;
+
+ /* check sp, if any sp has the sa, make it release sa */
+ write_lock_bh(&spd_lock);
+ list_for_each(pos, &spd_list){
+ tmp_sp = list_entry(pos, struct ipsec_sp, entry);
+ write_lock_bh(&tmp_sp->lock);
+ ipsec_sp_release_invalid_sa(tmp_sp, sa);
+ write_unlock_bh(&tmp_sp->lock);
+ }
+ write_unlock_bh(&spd_lock);
+
+ if(sa->timer.expires){
+ del_timer(&sa->timer);
+ sa->timer.expires = 0;
+ ipsec_sa_put(sa);
+ }
+ ipsec_sa_put(sa);
+}
+
+void sadb_remove(struct ipsec_sa *sa)
+{
+ struct list_head *pos = NULL;
+ struct ipsec_sa *tmp_sa = NULL;
+
+ SADB_DEBUG("sa=%p\n", sa);
+
+ /* If SA has already chained to sadb_list, SA is removed from sadb */
+ write_lock_bh(&sadb_lock);
+ list_for_each(pos, &sadb_list) {
+ tmp_sa = list_entry(pos, struct ipsec_sa, entry);
+ write_lock_bh(&tmp_sa->lock);
+ if (tmp_sa == sa) {
+ SADB_DEBUG(" I found a SA to be removed.\n");
+ tmp_sa->state = SADB_SASTATE_DEAD;
+ list_del(&tmp_sa->entry);
+ write_unlock_bh(&tmp_sa->lock);
+ break;
+ }
+ write_unlock_bh(&tmp_sa->lock);
+ tmp_sa = NULL;
+ }
+ write_unlock_bh(&sadb_lock);
+
+ if (tmp_sa)
+ __sadb_remove(tmp_sa);
+}
+
+int sadb_update(struct ipsec_sa *entry)
+{
+ int error = 0;
+ if (entry) {
+ SADB_DEBUG("sadb_update entry == null\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ /* TODO: Current code removes old sa and append new sa,
+ but it is correct to update sa returnd from sa_get_by_said. */
+
+ sadb_remove(entry);
+
+ error = sadb_append(entry);
+ if (error) {
+ SADB_DEBUG("could not append\n");
+ goto err;
+ }
+
+err:
+ return error;
+}
+
+int sadb_find_by_address_proto_spi(struct sockaddr *src, __u8 prefixlen_s,
+ struct sockaddr *dst, __u8 prefixlen_d,
+ __u8 ipsec_proto,
+ __u32 spi,
+ struct ipsec_sa **sa)
+{
+ int error = -ESRCH;
+ struct list_head *pos = NULL;
+ struct ipsec_sa *tmp = NULL;
+
+ if (!(src&&dst)) {
+ SADB_DEBUG("src or dst is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+
+#ifdef CONFIG_IPSEC_DEBUG
+{
+ char buf[BUFSIZE];
+ sockaddrtoa(src, buf, sizeof(buf));
+ SADB_DEBUG("src=%s\n", buf);
+ sockaddrtoa(dst, buf, sizeof(buf));
+ SADB_DEBUG("dst=%s\n", buf);
+}
+#endif
+
+ read_lock_bh(&sadb_lock);
+ list_for_each(pos, &sadb_list){
+ tmp = list_entry(pos, struct ipsec_sa, entry);
+ read_lock_bh(&tmp->lock);
+ if (tmp->ipsec_proto == ipsec_proto && tmp->spi == spi &&
+ !sockaddr_prefix_compare(dst, prefixlen_d,
+ (struct sockaddr*)&tmp->dst, tmp->prefixlen_d) &&
+ !sockaddr_prefix_compare(src, prefixlen_s,
+ (struct sockaddr*)&tmp->src, tmp->prefixlen_s) &&
+ spi == tmp->spi) {
+ SADB_DEBUG("found sa matching params.\n");
+ if (sa) {
+ atomic_inc(&tmp->refcnt);
+ *sa = tmp;
+ SADB_DEBUG("ptr=%p,refcnt%d\n",
+ tmp, atomic_read(&tmp->refcnt));
+ }
+ read_unlock_bh(&tmp->lock);
+ error = -EEXIST;
+ break;
+ }
+ read_unlock_bh(&tmp->lock);
+ }
+ read_unlock_bh(&sadb_lock);
+err:
+
+#ifdef CONFIG_IPSEC_DEBUG
+ if (error && (error != -EEXIST))
+ SADB_DEBUG("I could not find any SA.\n");
+#endif
+
+ return error;
+}
+
+/*
+ * We use SPI=0xFFFFFFFF for a special purpose.
+ * It means sadb_find_by_sa_index() looking for proper SA
+ * whether SPI value is same or not.
+ *
+ * Please take care to use sadb_find_by_sa_index() to use other places.
+ *
+ */
+struct ipsec_sa* sadb_find_by_sa_index(struct sa_index *sa_idx)
+{
+ struct list_head *pos;
+ struct ipsec_sa *tmp_sa = NULL;
+
+ if (!sa_idx) {
+ SADB_DEBUG("sa_index is NULL\n");
+ return NULL;
+ }
+
+#ifdef CONFIG_IPSEC_DEBUG
+{
+ char buf[BUFSIZE];
+ sockaddrtoa((struct sockaddr*)&sa_idx->dst, buf, sizeof(buf));
+ SADB_DEBUG("sa_idx->dst=%s\n", buf);
+ SADB_DEBUG("sa_idx->ipsec_proto=%u\n", sa_idx->ipsec_proto);
+ SADB_DEBUG("sa_idx->spi=0x%x\n", ntohl(sa_idx->spi));
+}
+#endif /* CONFIG_IPSEC_DEBUG */
+
+ read_lock(&sadb_lock);
+ list_for_each(pos, &sadb_list){
+ tmp_sa = list_entry(pos, struct ipsec_sa, entry);
+ read_lock_bh(&tmp_sa->lock);
+ if ((sa_idx->spi == IPSEC_SPI_ANY || tmp_sa->spi == sa_idx->spi) &&
+ tmp_sa->ipsec_proto == sa_idx->ipsec_proto &&
+ !sockaddr_prefix_compare((struct sockaddr*)&sa_idx->dst, sa_idx->prefixlen_d,
+ (struct sockaddr*)&tmp_sa->dst, tmp_sa->prefixlen_d) &&
+ (tmp_sa->state == SADB_SASTATE_MATURE || tmp_sa->state == SADB_SASTATE_DYING)) {
+ SADB_DEBUG("found SA matching params.\n");
+
+ atomic_inc(&tmp_sa->refcnt);
+ read_unlock_bh(&tmp_sa->lock);
+
+ if (sa_idx->sa)
+ ipsec_sa_put(sa_idx->sa);
+ sa_idx->sa = tmp_sa;
+
+ SADB_DEBUG("ptr=%p,refcnt=%d\n",
+ tmp_sa, atomic_read(&tmp_sa->refcnt));
+
+ break;
+ }
+ read_unlock_bh(&tmp_sa->lock);
+ }
+ read_unlock(&sadb_lock);
+
+#ifdef CONFIG_IPSEC_DEBUG
+ if (!sa_idx->sa)
+ SADB_DEBUG("I could not find any SA.\n");
+#endif
+
+ return sa_idx->sa;
+}
+
+int sadb_flush_sa(int satype)
+{
+ struct list_head dead_sa_list;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+ struct ipsec_sa *tmp = NULL;
+
+ if (satype != SADB_SATYPE_AH && satype != SADB_SATYPE_ESP) {
+ SADB_DEBUG("satype is not supported\n");
+ return -EINVAL;
+ }
+
+ INIT_LIST_HEAD(&dead_sa_list);
+
+ write_lock_bh(&sadb_lock);
+ list_for_each_safe(pos, next, &sadb_list) {
+ tmp = list_entry(pos, struct ipsec_sa, entry);
+ write_lock_bh(&tmp->lock);
+ if (tmp->ipsec_proto == satype) {
+ list_del(&tmp->entry);
+ list_add_tail(&tmp->entry, &dead_sa_list);
+ }
+ write_unlock_bh(&tmp->lock);
+ }
+ write_unlock_bh(&sadb_lock);
+
+ list_for_each_safe(pos, next, &dead_sa_list) {
+ tmp = list_entry(pos, struct ipsec_sa, entry);
+ list_del(&tmp->entry);
+ __sadb_remove(tmp);
+ }
+
+ return 0;
+}
+
+void sadb_clear_db(void)
+{
+ struct list_head dead_sa_list;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+ struct ipsec_sa *tmp = NULL;
+
+ INIT_LIST_HEAD(&dead_sa_list);
+
+ write_lock_bh(&sadb_lock);
+ list_for_each_safe(pos, next, &sadb_list) {
+ tmp = list_entry(pos, struct ipsec_sa, entry);
+ write_lock_bh(&tmp->lock);
+ /*
+ A previous pointer of pos must be stored because
+ pos indicates tmp->entry and these operations
+ change tmp->entry. Therefore this loop is broken.
+ */
+ list_del(&tmp->entry);
+ list_add_tail(&tmp->entry, &dead_sa_list);
+ write_unlock_bh(&tmp->lock);
+ }
+ write_unlock_bh(&sadb_lock);
+
+ list_for_each_safe(pos, next, &dead_sa_list) {
+ tmp = list_entry(pos, struct ipsec_sa, entry);
+ list_del(&tmp->entry);
+ __sadb_remove(tmp);
+ }
+}
+
+#ifdef CONFIG_PROC_FS
+static int sadb_get_info(char *buffer, char **start, off_t offset, int length )
+{
+ int len = 0;
+ off_t pos=0;
+ off_t begin=0;
+ char buf[BUFSIZE];
+ struct list_head *list_pos = NULL;
+ struct ipsec_sa *tmp = NULL;
+ read_lock_bh(&sadb_lock);
+ list_for_each(list_pos, &sadb_list){
+ tmp = list_entry(list_pos, struct ipsec_sa, entry);
+ read_lock_bh(&tmp->lock);
+
+ len += sprintf(buffer + len, "sa:%p\n", tmp);
+
+ memset(buf, 0, sizeof(buf));
+ sockaddrtoa((struct sockaddr*)&tmp->src, buf, sizeof(buf));
+ len += sprintf(buffer + len, "%s/%d ", buf, tmp->prefixlen_s);
+
+ memset(buf, 0, sizeof(buf));
+ sockporttoa((struct sockaddr*)&tmp->src, buf, sizeof(buf));
+ len += sprintf(buffer + len, "%s ", buf);
+
+ memset(buf, 0, sizeof(buf));
+ sockaddrtoa((struct sockaddr*)&tmp->dst, buf, sizeof(buf));
+ len += sprintf(buffer + len, "%s/%d ", buf, tmp->prefixlen_d);
+
+ memset(buf, 0, sizeof(buf));
+ sockporttoa((struct sockaddr*)&tmp->dst, buf, sizeof(buf));
+ len += sprintf(buffer + len, "%s ", buf);
+ len += sprintf(buffer + len, "%u %u ", tmp->proto, tmp->ipsec_proto );
+
+ if (tmp->ipsec_proto == SADB_X_SATYPE_COMP) {
+ len += sprintf(buffer + len, "0x%x ", ntohl(tmp->spi));
+ } else {
+ len += sprintf(buffer + len, "0x%x ", ntohl(tmp->spi));
+ }
+ len += sprintf(buffer + len, "%d ", tmp->auth_algo.algo);
+ len += sprintf(buffer + len, "%d\n", tmp->esp_algo.algo);
+
+ if (tmp->auth_algo.algo == SADB_AALG_MD5HMAC ||
+ tmp->auth_algo.algo == SADB_AALG_SHA1HMAC) {
+ int i;
+ memset(buf, 0, sizeof(buf));
+ for (i=0; i<tmp->auth_algo.key_len; i++)
+ sprintf(&buf[i*2], "%02x", tmp->auth_algo.key[i]);
+ len += sprintf(buffer + len, "%s\n", buf);
+ } else {
+ len += sprintf(buffer + len, "0\n");
+ }
+
+ if (tmp->esp_algo.algo == SADB_EALG_DESCBC ||
+ tmp->esp_algo.algo == SADB_EALG_3DESCBC ||
+ tmp->esp_algo.algo == SADB_EALG_AES) {
+ int i;
+ memset(buf, 0, sizeof(buf));
+ for (i=0; i<tmp->esp_algo.key_len; i++)
+ sprintf(&buf[i*2], "%02x", tmp->esp_algo.key[i]);
+ len += sprintf(buffer + len, "%s\n", buf);
+ } else {
+ len += sprintf(buffer + len, "0\n");
+ }
+
+ len += sprintf(buffer + len, "%u %u %u %u ", tmp->lifetime_s.allocations,
+ (u32)tmp->lifetime_s.bytes,
+ (u32)(tmp->lifetime_s.addtime),
+ (u32)(tmp->lifetime_s.usetime));
+ len += sprintf(buffer + len, "%u %u %u %u ", tmp->lifetime_h.allocations,
+ (u32)tmp->lifetime_h.bytes,
+ (u32)(tmp->lifetime_h.addtime),
+ (u32)(tmp->lifetime_h.usetime));
+ len += sprintf(buffer + len, "%u %u %u %u ", tmp->lifetime_c.allocations,
+ (u32)tmp->lifetime_c.bytes,
+ (u32)(tmp->lifetime_c.addtime),
+ (u32)(tmp->lifetime_c.usetime));
+
+ len += sprintf(buffer + len, "%u ", tmp->state);
+ len += sprintf(buffer + len, "%u\n", atomic_read(&tmp->refcnt) );
+ len += sprintf(buffer + len, "\n");
+
+ read_unlock_bh(&tmp->lock);
+
+ pos=begin+len;
+ if (pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if (pos>offset+length) {
+ goto done;
+ }
+ }
+done:
+ read_unlock_bh(&sadb_lock);
+
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if (len>length)
+ len=length;
+ if (len<0)
+ len=0;
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+int sadb_init(void)
+{
+ int error = 0;
+
+#ifdef CONFIG_PROC_FS
+ proc_net_create("sadb", 0400, sadb_get_info);
+#endif /* CONFIG_PROC_FS */
+
+ pr_info("IPsec Security Association Database (SADB): initialized.\n");
+ return error;
+}
+
+int sadb_cleanup(void)
+{
+ int error = 0;
+
+ INIT_LIST_HEAD(&sadb_list);
+#ifdef CONFIG_PROC_FS
+ proc_net_remove("sadb");
+#endif /* CONFIG_PROC_FS */
+
+ sadb_clear_db();
+
+ pr_info("IPsec SADB: cleaned up\n");
+ return error;
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/sockaddr_utils.c linux25.43-ipsec/net/key/sockaddr_utils.c
--- linux-2.5.43/net/key/sockaddr_utils.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/sockaddr_utils.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,197 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]>
+ * Mitsuru KANDA <
[email protected]>
+ */
+/*
+ * sadb_utils.c include utility functions for SADB handling.
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <net/ipv6.h>
+#include <linux/inet.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <net/sadb.h>
+
+#define BUFSIZE 64
+
+/* XXX: should move these 3 functions to net/ipv6/utils.c */
+static char*
+in6_ntop(const struct in6_addr *in6, char *buf) {
+ if (!buf)
+ return NULL;
+ sprintf(buf,
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ ntohs(in6->s6_addr16[0]), ntohs(in6->s6_addr16[1]),
+ ntohs(in6->s6_addr16[2]), ntohs(in6->s6_addr16[3]),
+ ntohs(in6->s6_addr16[4]), ntohs(in6->s6_addr16[5]),
+ ntohs(in6->s6_addr16[6]), ntohs(in6->s6_addr16[7]));
+ return buf;
+}
+
+static inline int
+u32_prefix_cmp(const void *__a1, const void *__a2, int plen)
+{
+ const u32 *a1 = __a1;
+ const u32 *a2 = __a2;
+ int w, b;
+
+ if (plen <= 0)
+ return 0;
+
+ w = plen >> 5; /* num of whole u32 in prefix */
+ b = plen & 0x1f; /* num of bits in incomplete u32 in prefix */
+
+ if (w) {
+ if (memcmp(a1, a2, w << 2))
+ return !0;
+ }
+ if (b) {
+ u32 mask = htonl(~0 << (32 - b));
+ return (a1[w] ^ a2[w]) & mask;
+ }
+ return 0;
+}
+
+static inline int
+ipv6_prefix_cmp(const struct in6_addr *a1, const struct in6_addr *a2, int plen)
+{
+ if (plen > 128)
+ plen = 128;
+ return u32_prefix_cmp(a1->s6_addr, a2->s6_addr, plen);
+}
+
+int
+sockaddrtoa(struct sockaddr *addr, char *buf, size_t buflen)
+{
+ int ret = 0;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ sprintf(buf, "%d.%d.%d.%d", NIPQUAD((((struct sockaddr_in *)addr)->sin_addr)));
+ break;
+ case AF_INET6:
+ in6_ntop(&(((struct sockaddr_in6*)addr)->sin6_addr), buf);
+ break;
+ default:
+ ret = -EAFNOSUPPORT;
+ break;
+ }
+
+ return ret;
+}
+
+int
+sockporttoa(struct sockaddr *addr, char *buf, size_t buflen)
+{
+ int ret = 0;
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ sprintf(buf, "%hd", ntohs(((struct sockaddr_in *)addr)->sin_port));
+ break;
+ case AF_INET6:
+ sprintf(buf, "%hd", ntohs(((struct sockaddr_in6*)addr)->sin6_port));
+ break;
+ default:
+ printk(KERN_WARNING "sockporttoa: unrecognized socket family: %d\n", addr->sa_family);
+ ret = -EAFNOSUPPORT;
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * addr1, prefixlen1 : packet(must set 128 or 32 befor call this)
+ * addr2, prefixlen2 : sa/sp
+ */
+int
+sockaddr_prefix_compare(struct sockaddr *addr1, __u8 prefixlen1,
+ struct sockaddr *addr2, __u8 prefixlen2)
+{
+ __u8 prefixlen;
+
+ if (!addr1 || !addr2) {
+ SADB_DEBUG("addr1 or add2 is NULL\n");
+ return -EINVAL;
+ }
+
+ if (addr1->sa_family != addr2->sa_family) {
+ SADB_DEBUG("sa_family not match\n");
+ return 1;
+ }
+
+ if (prefixlen1 < prefixlen2)
+ prefixlen = prefixlen1;
+ else
+ prefixlen = prefixlen2;
+ SADB_DEBUG("prefixlen: %d, prefixlen1: %d, prefixlen2: %d\n", prefixlen, prefixlen1, prefixlen2);
+
+ switch (addr1->sa_family) {
+ case AF_INET:
+ if (prefixlen > 32 )
+ return 1;
+ return (((struct sockaddr_in *)addr1)->sin_addr.s_addr ^
+ ((struct sockaddr_in *)addr2)->sin_addr.s_addr) &
+ htonl((0xffffffff << (32 - prefixlen)));
+ case AF_INET6:
+ if (prefixlen > 128)
+ return 1;
+
+ return ipv6_prefix_cmp(&((struct sockaddr_in6 *)addr1)->sin6_addr,
+ &((struct sockaddr_in6 *)addr2)->sin6_addr,
+ prefixlen);
+ default:
+ SADB_DEBUG("unknown sa_family\n");
+ return 1;
+ }
+}
+
+int
+sockaddr_compare_ports(struct sockaddr *addr1, struct sockaddr *addr2)
+ {
+ if (addr1->sa_family != addr2->sa_family)
+ return -EINVAL;
+
+ switch (addr1->sa_family) {
+ case AF_INET:
+ if (((struct sockaddr_in *)addr1)->sin_port && ((struct sockaddr_in *)addr2)->sin_port)
+ return !( ((struct sockaddr_in *)addr1)->sin_port == ((struct sockaddr_in *)addr2)->sin_port);
+ break;
+ case AF_INET6:
+ if (((struct sockaddr_in6 *)addr1)->sin6_port && ((struct sockaddr_in6 *)addr2)->sin6_port)
+ return !( ((struct sockaddr_in6 *)addr1)->sin6_port == ((struct sockaddr_in6 *)addr2)->sin6_port);
+ break;
+ default:
+ SADB_DEBUG("%s:%d: compare_ports_if_set: unsupported address family: %d\n",
+ __FILE__, __LINE__, addr1->sa_family);
+ return -EINVAL;
+ break;
+ }
+
+ return 0; /* should never reach here */
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/sockaddr_utils.h linux25.43-ipsec/net/key/sockaddr_utils.h
--- linux-2.5.43/net/key/sockaddr_utils.h 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/sockaddr_utils.h 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,41 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __SOCKADDR_UTILS_H
+#define __SOCKADDR_UTILS_H
+
+#include <linux/types.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/errno.h>
+
+#include <net/sadb.h>
+#include <net/spd.h>
+
+/* Function addrtoa converts sockaddr to ascii */
+/* It returns the length, if it scceed. Otherwise it return 0.*/
+int sockaddrtoa(struct sockaddr *addr, char *buf, size_t buflen);
+/* Function sockporttoa converts port numbers to ascii */
+/* Returns 0 if successful, -EINVAL on error */
+int sockporttoa(struct sockaddr *addr, char *buf, size_t buflen);
+
+int sockaddr_prefix_compare(struct sockaddr *addr1, __u8 plen1,
+ struct sockaddr *addr2, __u8 plen2);
+int sockaddr_compare_ports(struct sockaddr *addr1, struct sockaddr *addr2);
+#endif /* __SOCKADDR_UTILS_H */
diff -uNr -x CVS linux-2.5.43/net/key/spd.c linux25.43-ipsec/net/key/spd.c
--- linux-2.5.43/net/key/spd.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/spd.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,487 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ *
+ * Acknowledgements:
+ * Joy Latten <
[email protected]>
+ */
+/*
+ * spd.c provide manipulatoin routines for IPsec SPD.
+ * struct ipsec_sp represent a policy in IPsec SPD.
+ * struct ipsec_sp refers IPsec SA by struct sa_index.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/net.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <net/spd.h>
+#include <net/sadb.h>
+#include "sockaddr_utils.h"
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif /* CONFIG_PROC_FS */
+
+#define BUFSIZE 64
+
+/* spd_list : IPsec Security Policy Database(SPD)
+ * spd_lock : lock for SPD
+ */
+LIST_HEAD(spd_list);
+rwlock_t spd_lock = RW_LOCK_UNLOCKED;
+
+
+static int ipsec_selector_compare(struct selector *selector1, struct selector *selector2)
+{
+ int tmp;
+
+ if (!(selector1&&selector2)) {
+ SPD_DEBUG("selector1 or selecotr2 is NULL\n");
+ return -EINVAL;
+ }
+
+ if (selector1->proto && selector2->proto) {
+ if (selector1->proto != selector2->proto)
+ return -EINVAL;
+ }
+
+#ifdef CONFIG_IPSEC_TUNNEL
+ tmp = !(selector1->mode == selector2->mode);
+ if (tmp)
+ return (tmp);
+#endif
+
+ tmp = sockaddr_prefix_compare((struct sockaddr*)&selector1->src, selector1->prefixlen_s,
+ (struct sockaddr*)&selector2->src, selector2->prefixlen_s) ||
+ sockaddr_prefix_compare((struct sockaddr*)&selector1->dst, selector1->prefixlen_d,
+ (struct sockaddr*)&selector2->dst, selector2->prefixlen_d);
+
+ /* tmp == 0 means successful match so far */
+ if (tmp)
+ return (tmp);
+
+ /* compare ports, if they are set */
+ tmp = sockaddr_compare_ports((struct sockaddr*)&selector1->src, (struct sockaddr*)&selector2->src);
+ if (tmp)
+ return (tmp);
+ tmp = sockaddr_compare_ports((struct sockaddr*)&selector1->dst, (struct sockaddr*)&selector2->dst);
+ if (tmp)
+ return (tmp);
+
+ return 0; /* everything matches */
+
+}
+struct ipsec_sp *ipsec_sp_kmalloc()
+{
+ struct ipsec_sp *sp = NULL;
+
+ sp = (struct ipsec_sp *)kmalloc(sizeof(struct ipsec_sp), GFP_KERNEL);
+
+ if (!sp) {
+ SPD_DEBUG("entry couldn\'t be allocated.\n");
+ return NULL;
+ }
+
+ ipsec_sp_init(sp);
+
+ return sp;
+}
+
+int ipsec_sp_init(struct ipsec_sp *policy)
+{
+ if (!policy) {
+ SPD_DEBUG("policy is NULL\n");
+ return -EINVAL;
+ }
+
+ memset(policy, 0, sizeof(struct ipsec_sp));
+ policy->auth_sa_idx = NULL;
+ policy->esp_sa_idx = NULL;
+ policy->comp_sa_idx = NULL;
+ atomic_set(&policy->refcnt,1);
+ policy->lock = RW_LOCK_UNLOCKED;
+
+ return 0;
+}
+
+void ipsec_sp_kfree(struct ipsec_sp *policy)
+{
+ if (!policy) {
+ SPD_DEBUG("entry is NULL\n");
+ return;
+ }
+
+ if (atomic_read(&policy->refcnt)) {
+ SPD_DEBUG("policy has been referenced\n");
+ return;
+ }
+
+ if (policy->auth_sa_idx) sa_index_kfree(policy->auth_sa_idx);
+ if (policy->esp_sa_idx) sa_index_kfree(policy->esp_sa_idx);
+ if (policy->comp_sa_idx) sa_index_kfree(policy->comp_sa_idx);
+
+ kfree(policy);
+}
+
+int ipsec_sp_copy(struct ipsec_sp *dst, struct ipsec_sp *src)
+{
+ int error = 0;
+
+ if (!dst || !src) {
+ SPD_DEBUG("dst or src is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ memcpy(&dst->selector, &src->selector, sizeof(struct selector));
+
+ if (dst->auth_sa_idx) sa_index_kfree(dst->auth_sa_idx);
+ if (dst->esp_sa_idx) sa_index_kfree(dst->esp_sa_idx);
+ if (dst->comp_sa_idx) sa_index_kfree(dst->comp_sa_idx);
+
+ if (src->auth_sa_idx) {
+ dst->auth_sa_idx = sa_index_kmalloc();
+ memcpy(dst->auth_sa_idx, src->auth_sa_idx, sizeof(struct sa_index));
+ }
+
+ if (src->esp_sa_idx) {
+ dst->esp_sa_idx = sa_index_kmalloc();
+ memcpy(dst->esp_sa_idx, src->esp_sa_idx, sizeof(struct sa_index));
+ }
+
+ if (src->comp_sa_idx) {
+ dst->comp_sa_idx = sa_index_kmalloc();
+ memcpy(dst->comp_sa_idx, src->comp_sa_idx, sizeof(struct sa_index));
+ }
+
+ dst->policy_action = src->policy_action;
+
+ atomic_set(&dst->refcnt, 1);
+err:
+ return error;
+}
+
+int ipsec_sp_put(struct ipsec_sp *policy)
+{
+ int error = 0;
+
+ if (!policy) {
+ SPD_DEBUG("policy is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ write_lock_bh(&policy->lock);
+ SPD_DEBUG("ptr=%p,refcnt=%d\n",
+ policy, atomic_read(&policy->refcnt));
+
+ if (atomic_dec_and_test(&policy->refcnt)) {
+
+ SPD_DEBUG("ptr=%p,refcnt=%d\n",
+ policy, atomic_read(&policy->refcnt));
+
+ write_unlock_bh(&policy->lock);
+
+ ipsec_sp_kfree(policy);
+
+ return 0;
+ }
+
+ write_unlock_bh(&policy->lock);
+
+err:
+ return error;
+}
+
+void ipsec_sp_release_invalid_sa(struct ipsec_sp *policy, struct ipsec_sa *sa)
+{
+ if (!policy) {
+ SPD_DEBUG("policy is NULL\n");
+ return;
+ }
+
+ if (policy->auth_sa_idx && policy->auth_sa_idx->sa == sa) {
+ ipsec_sa_put(policy->auth_sa_idx->sa);
+ policy->auth_sa_idx->sa = NULL;
+ }
+
+ if (policy->esp_sa_idx && policy->esp_sa_idx->sa == sa) {
+ ipsec_sa_put(policy->esp_sa_idx->sa);
+ policy->esp_sa_idx->sa = NULL;
+ }
+
+ if (policy->comp_sa_idx && policy->comp_sa_idx->sa == sa) {
+ ipsec_sa_put(policy->comp_sa_idx->sa);
+ policy->comp_sa_idx->sa = NULL;
+ }
+}
+
+int spd_append(struct ipsec_sp *policy)
+{
+ int error = 0;
+ struct ipsec_sp *new = NULL;
+
+ if (!policy) {
+ SPD_DEBUG("policy is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ new = ipsec_sp_kmalloc();
+ if (!new) {
+ SPD_DEBUG("ipsec_sp_kmalloc failed\n");
+ error = -ENOMEM;
+ goto err;
+ }
+
+ error = ipsec_sp_init(new);
+ if (error) {
+ SPD_DEBUG("ipsec_sp_init failed\n");
+ goto err;
+ }
+
+ error = ipsec_sp_copy(new, policy);
+ if (error) {
+ SPD_DEBUG("ipsec_sp_copy failed\n");
+ goto err;
+ }
+
+ write_lock_bh(&spd_lock);
+ list_add_tail(&new->entry, &spd_list);
+ write_unlock_bh(&spd_lock);
+err:
+ return error;
+}
+
+int spd_remove(struct selector *selector)
+{
+ int error = -ESRCH;
+ struct list_head *pos = NULL;
+ struct list_head *next = NULL;
+ struct ipsec_sp *tmp_sp = NULL;
+
+ if (!selector) {
+ SPD_DEBUG("selector is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ write_lock_bh(&spd_lock);
+ list_for_each_safe(pos, next, &spd_list){
+ tmp_sp = list_entry(pos, struct ipsec_sp, entry);
+ write_lock_bh(&tmp_sp->lock);
+ if (!ipsec_selector_compare(selector, &tmp_sp->selector)) {
+ SPD_DEBUG("found matched element\n");
+ error = 0;
+ list_del(&tmp_sp->entry);
+ write_unlock_bh(&tmp_sp->lock);
+ ipsec_sp_put(tmp_sp);
+ break;
+ }
+ write_unlock_bh(&tmp_sp->lock);
+ }
+ write_unlock_bh(&spd_lock);
+
+err:
+ SPD_DEBUG("error = %d\n", error);
+ return error;
+}
+
+
+int spd_find_by_selector(struct selector *selector, struct ipsec_sp **policy)
+{
+ int error = -ESRCH;
+ struct list_head *pos = NULL;
+ struct ipsec_sp *tmp_sp = NULL;
+
+ if (!selector) {
+ SPD_DEBUG("selector is NULL\n");
+ error = -EINVAL;
+ goto err;
+ }
+
+ read_lock(&spd_lock);
+ list_for_each(pos, &spd_list){
+ tmp_sp = list_entry(pos, struct ipsec_sp, entry);
+ read_lock_bh(&tmp_sp->lock);
+ if (!ipsec_selector_compare(selector, &tmp_sp->selector)) {
+ SPD_DEBUG("found matched element\n");
+ error = -EEXIST;
+ *policy = tmp_sp;
+ atomic_inc(&(*policy)->refcnt);
+ read_unlock_bh(&tmp_sp->lock);
+ break;
+ }
+ read_unlock_bh(&tmp_sp->lock);
+ }
+ read_unlock(&spd_lock);
+
+
+err:
+ return error;
+}
+
+void spd_clear_db()
+{
+ struct list_head *pos;
+ struct list_head *next;
+ struct ipsec_sp *policy;
+
+ write_lock_bh(&spd_lock);
+ list_for_each_safe(pos, next, &spd_list){
+ policy = list_entry(pos, struct ipsec_sp, entry);
+ list_del(&policy->entry);
+ ipsec_sp_kfree(policy);
+ }
+ write_unlock_bh(&spd_lock);
+}
+
+
+#ifdef CONFIG_PROC_FS
+static int spd_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ int error = 0;
+ int count = 0;
+ int len = 0;
+ off_t pos=0;
+ off_t begin=0;
+ char buf[BUFSIZE];
+ struct list_head *list_pos = NULL;
+ struct ipsec_sp *tmp_sp = NULL;
+
+ read_lock_bh(&spd_lock);
+ list_for_each(list_pos, &spd_list){
+ count = 0;
+ tmp_sp = list_entry(list_pos, struct ipsec_sp, entry);
+ read_lock_bh(&tmp_sp->lock);
+
+ len += sprintf(buffer + len, "spd:%p\n", tmp_sp);
+ memset(buf, 0, BUFSIZE);
+ sockaddrtoa((struct sockaddr*)&tmp_sp->selector.src, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s/%u ", buf, tmp_sp->selector.prefixlen_s);
+ sockporttoa((struct sockaddr *)&tmp_sp->selector.src, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s ", buf);
+ memset(buf, 0, BUFSIZE);
+ sockaddrtoa((struct sockaddr*)&tmp_sp->selector.dst, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s/%u ", buf, tmp_sp->selector.prefixlen_d);
+ sockporttoa((struct sockaddr *)&tmp_sp->selector.dst, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s ", buf);
+ len += sprintf(buffer + len, "%u ", tmp_sp->selector.proto);
+#ifdef CONFIG_IPSEC_TUNNEL
+ len += sprintf(buffer + len, "%u ", tmp_sp->selector.mode);
+#endif
+ len += sprintf(buffer + len, "%u\n", tmp_sp->policy_action);
+
+ if (tmp_sp->auth_sa_idx) {
+ len += sprintf(buffer + len, "sa(ah):%p ", tmp_sp->auth_sa_idx->sa);
+ sockaddrtoa((struct sockaddr*)&tmp_sp->auth_sa_idx->dst, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s/%d ", buf, tmp_sp->auth_sa_idx->prefixlen_d);
+ len += sprintf(buffer + len, "%u ", tmp_sp->auth_sa_idx->ipsec_proto);
+ len += sprintf(buffer + len, "0x%x\n", htonl(tmp_sp->auth_sa_idx->spi));
+ }
+
+ if (tmp_sp->esp_sa_idx) {
+ len += sprintf(buffer + len, "sa(esp):%p ", tmp_sp->esp_sa_idx->sa);
+ sockaddrtoa((struct sockaddr*)&tmp_sp->esp_sa_idx->dst, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s/%d ", buf, tmp_sp->esp_sa_idx->prefixlen_d);
+ len += sprintf(buffer + len, "%u ", tmp_sp->esp_sa_idx->ipsec_proto);
+ len += sprintf(buffer + len, "0x%x\n", htonl(tmp_sp->esp_sa_idx->spi));
+ }
+
+ if (tmp_sp->comp_sa_idx) {
+ len += sprintf(buffer + len, "sa(comp):%p ", tmp_sp->comp_sa_idx->sa);
+ sockaddrtoa((struct sockaddr*)&tmp_sp->comp_sa_idx->dst, buf, BUFSIZE);
+ len += sprintf(buffer + len, "%s/%d ", buf, tmp_sp->comp_sa_idx->prefixlen_d);
+ len += sprintf(buffer + len, "%u ", tmp_sp->comp_sa_idx->ipsec_proto);
+ len += sprintf(buffer + len, "0x%x\n", htonl(tmp_sp->comp_sa_idx->spi));
+ }
+
+ read_unlock_bh(&tmp_sp->lock);
+ len += sprintf(buffer + len, "\n");
+
+ pos=begin+len;
+ if (pos<offset) {
+ len=0;
+ begin=pos;
+ }
+ if (pos>offset+length) {
+ read_unlock_bh(&spd_lock);
+ goto done;
+ }
+ }
+ read_unlock_bh(&spd_lock);
+done:
+
+ *start=buffer+(offset-begin);
+ len-=(offset-begin);
+ if (len>length)
+ len=length;
+ if (len<0)
+ len=0;
+ return len;
+
+ goto err;
+err:
+ return error;
+}
+#endif /* CONFIG_PROC_FS */
+
+int spd_init(void)
+{
+ int error = 0;
+
+ INIT_LIST_HEAD(&spd_list);
+ SPD_DEBUG("spd_list.prev=%p\n", spd_list.prev);
+ SPD_DEBUG("spd_list.next=%p\n", spd_list.next);
+#ifdef CONFIG_PROC_FS
+ proc_net_create("spd", 0, spd_get_info);
+#endif /* CONFIG_PROC_FS */
+
+ pr_info("IPsec Security Policy Database (SPD): initialized.\n");
+ return error;
+}
+
+int spd_cleanup(void)
+{
+ int error = 0;
+
+#ifdef CONFIG_PROC_FS
+ proc_net_remove("spd");
+#endif /* CONFIG_PROC_FS */
+
+ spd_clear_db();
+
+ pr_info("IPsec SPD: cleaned up.\n");
+ return error;
+}
+
diff -uNr -x CVS linux-2.5.43/net/key/sysctl_net_ipsec.c linux25.43-ipsec/net/key/sysctl_net_ipsec.c
--- linux-2.5.43/net/key/sysctl_net_ipsec.c 1970-01-01 09:00:00.000000000 +0900
+++ linux25.43-ipsec/net/key/sysctl_net_ipsec.c 2002-10-16 15:43:38.000000000 +0900
@@ -0,0 +1,67 @@
+/* $USAGI: linux25-IPSEC_2_5_43.patch,v 1.1 2002/10/16 09:45:23 mk Exp $ */
+/*
+ * Copyright (C)2001 USAGI/WIDE Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Mitsuru KANDA <
[email protected]> / USAGI
+ * Kazunori MIYAZAWA <
[email protected]> / USAGI
+ */
+
+#include <linux/config.h>
+#include <linux/sysctl.h>
+
+/* extern */ int sysctl_ipsec_replay_window = 1;
+/* extern */ int sysctl_ipsec_debug_ipv4 = 0;
+/* extern */ int sysctl_ipsec_debug_ipv6 = 0;
+/* extern */ int sysctl_ipsec_debug_pfkey= 0;
+/* extern */ int sysctl_ipsec_debug_sadb = 0;
+/* extern */ int sysctl_ipsec_debug_spd = 0;
+
+ctl_table ipsec_table[] = {
+ {NET_IPSEC_REPLAY_WINDOW, "replay_window_check", &sysctl_ipsec_replay_window, sizeof(int), 0600, NULL, proc_dointvec},
+#ifdef CONFIG_IPSEC_DEBUG
+ {NET_IPSEC_DEBUG_IPV4, "debug_ipv4", &sysctl_ipsec_debug_ipv4, sizeof(int), 0600, NULL, proc_dointvec},
+ {NET_IPSEC_DEBUG_IPV6, "debug_ipv6", &sysctl_ipsec_debug_ipv6, sizeof(int), 0600, NULL, proc_dointvec},
+ {NET_IPSEC_DEBUG_PFKEY, "debug_pfkey", &sysctl_ipsec_debug_pfkey, sizeof(int), 0600, NULL, proc_dointvec},
+ {NET_IPSEC_DEBUG_SADB, "debug_sadb", &sysctl_ipsec_debug_sadb, sizeof(int), 0600, NULL, proc_dointvec},
+ {NET_IPSEC_DEBUG_SPD, "debug_spd", &sysctl_ipsec_debug_spd, sizeof(int), 0600, NULL, proc_dointvec},
+#endif /* CONFIG_IPSEC_DEBUG */
+ {0},
+};
+
+static ctl_table ipsec_net_table[] = {
+ {NET_IPSEC, "ipsec", NULL, 0, 0555, ipsec_table},
+ {0}
+};
+
+static ctl_table ipsec_root_table[] = {
+ {CTL_NET, "net", NULL, 0, 0555, ipsec_net_table},
+ {0}
+};
+
+static struct ctl_table_header *ipsec_sysctl_header;
+
+void ipsec_sysctl_register(void)
+{
+ ipsec_sysctl_header = register_sysctl_table(ipsec_root_table, 0);
+}
+
+void ipsec_sysctl_unregister(void)
+{
+ unregister_sysctl_table(ipsec_sysctl_header);
+}
+
diff -uNr -x CVS linux-2.5.43/net/netsyms.c linux25.43-ipsec/net/netsyms.c
--- linux-2.5.43/net/netsyms.c 2002-10-16 12:27:54.000000000 +0900
+++ linux25.43-ipsec/net/netsyms.c 2002-10-16 15:27:47.000000000 +0900
@@ -72,6 +72,12 @@
#endif
+#ifdef CONFIG_IPSEC
+#include <net/sadb.h>
+#include <net/spd.h>
+#include <linux/ipsec.h>
+#endif
+
extern int netdev_finish_unregister(struct net_device *dev);
#include <linux/rtnetlink.h>
@@ -287,6 +293,26 @@
EXPORT_SYMBOL(dlci_ioctl_hook);
#endif
+#ifdef CONFIG_IPSEC
+/* sa_index */
+EXPORT_SYMBOL(sa_index_init);
+EXPORT_SYMBOL(sa_index_copy);
+EXPORT_SYMBOL(sa_index_compare);
+/* sadb */
+EXPORT_SYMBOL(ipsec_sa_put);
+EXPORT_SYMBOL(sadb_find_by_sa_index);
+EXPORT_SYMBOL(ipsec_sa_mod_timer);
+/* spd */
+EXPORT_SYMBOL(ipsec_sp_put);
+EXPORT_SYMBOL(spd_find_by_selector);
+/* sysctl */
+#ifdef CONFIG_SYSCTL
+EXPORT_SYMBOL(sysctl_ipsec_replay_window);
+#ifdef CONFIG_IPSEC_DEBUG
+EXPORT_SYMBOL(sysctl_ipsec_debug_ipv6);
+#endif
+#endif
+#endif
#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
/* inet functions common to v4 and v6 */
diff -uNr -x CVS linux-2.5.43/net/socket.c linux25.43-ipsec/net/socket.c
--- linux-2.5.43/net/socket.c 2002-10-16 12:27:51.000000000 +0900
+++ linux25.43-ipsec/net/socket.c 2002-10-16 15:27:47.000000000 +0900
@@ -1721,6 +1721,10 @@
extern void wanrouter_init(void);
#endif
+#ifdef CONFIG_IPSEC
+extern void pfkey_init(void);
+#endif
+
void __init sock_init(void)
{
int i;
@@ -1781,6 +1785,10 @@
#ifdef CONFIG_NETFILTER
netfilter_init();
#endif
+
+#ifdef CONFIG_IPSEC
+ pfkey_init();
+#endif
}
int socket_get_info(char *buffer, char **start, off_t offset, int length)