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 #include #include +#include #include +#ifdef CONFIG_IP_IPSEC +#include +#include +#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 / USAGI Project + * Mitsuru KANDA / USAGI Project + * YOSHIFUJI Hideaki / USAGI Project + */ +#ifndef _LINUX_IPSEC6_H +#define _LINUX_IPSEC6_H + +#include +#include +#include + +#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 . + * + * 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_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 + +#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</neigh/ */ 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 +#include /* sockaddr_storage */ +#include /* list_entry */ +#include /* rw_lock_t */ +#include +#include + +/* 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 +#include +#include +#include + +#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 #include #include +#include /* * 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 #include #include +#include /* * 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 / USAGI + * Mitsuru KANDA / USAGI + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sa proto type */ +#include + +/* 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 / USAGI + * Mitsuru KANDA / USAGI + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sa proto type */ +#include + +/* + 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 +#ifdef CONFIG_IPV6_IPSEC +#include +#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 #include +#ifdef CONFIG_IPV6_IPSEC +#include +#include +#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 #include +#ifdef CONFIG_IPV6_IPSEC +#include +#include +#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 / USAGI + * Mitsuru KANDA / USAGI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sa proto type */ +#include +#ifdef CONFIG_IPV6_IPCOMP +#include +#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 / USAGI + * Mitsuru KANDA / USAGI + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sa proto type */ +#include + +/* 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 +#ifdef CONFIG_IPV6_IPSEC +#include +#include +#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<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 #include +#ifdef CONFIG_IPV6_IPSEC +#include +#include +#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 +#ifdef CONFIG_IPV6_IPSEC +#include +#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 / USAGI + * Kazunori Miyazawa / 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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* struct socket */ +#include +#include +#include +#include +#include +#include +#include /* struct sock */ +#include +#include +#ifdef CONFIG_PROC_FS +#include +#endif /* CONFIG_PROC_FS */ + +#include + +#include +#include + +#include +#include +#include + +#include + +#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 + +#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 (posoffset+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 (posoffset+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 . + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#include +#endif +#include +#include +#include +#include +#include +#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 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 . + * + * 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 /* for printk */ +#include /* kmalloc() */ +#include /* error codes */ +#include /* size_t */ +#include /* mark_bh */ +#include /* struct device, and other headers */ +#include /* eth_type_trans */ +#include /* struct iphdr */ +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +#include +#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +#include +#include + +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< / USAGI + * Mitsuru KANDA / USAGI + */ +/* + * pfkey_v2_msg.c is a program for PF_KEY version2 parsing routine, and utilities. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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; iipsec_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 / USAGI + * Mitsuru KANDA / USAGI + * + */ + +#ifndef __PFKEY_V2_MSG_H +#define __PFKEY_V2_MSG_H + +#include +#include + +#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 / USAGI + * Mitsuru KANDA / USAGI + */ +/* + * This is a parse routine for a message of SADB_ADD. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 / USAGI + * Mitsuru KANDA / USAGI + */ +/* + * This is a parse routine for a message of SADB_DELETE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 / USAGI + * Kazunori MIYAZAWA / 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 / USAGI + * Kazunori MIYAZAWA / USAGI + */ +/* + * This is a parse routine for a message of SADB_GET. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 / USAGI + * Mitsuru KANDA / USAGI + */ +/* + * This is a parse routine for a message of SADB_GETSPI. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 / USAGI + * Mitsuru KANDA / USAGI + */ +/* + * This is a parse routine for a message of SADB_UPDATE. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 / USAGI + * Mitsuru KANDA / 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 / USAGI + * Mitsuru KANDA / USAGI + * + * Acknowledgements: + * Joy Latten + * + */ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pfkey_v2_msg.h" +#include "sockaddr_utils.h" + +#ifdef CONFIG_PROC_FS +#include +#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; iauth_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; iesp_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 (posoffset+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 + * Mitsuru KANDA + */ +/* + * sadb_utils.c include utility functions for SADB handling. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include + +#include +#include + +/* 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 / USAGI + * Mitsuru KANDA / USAGI + * + * Acknowledgements: + * Joy Latten + */ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sockaddr_utils.h" + +#ifdef CONFIG_PROC_FS +#include +#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 (posoffset+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 / USAGI + * Kazunori MIYAZAWA / USAGI + */ + +#include +#include + +/* 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 +#include +#include +#endif + extern int netdev_finish_unregister(struct net_device *dev); #include @@ -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) .