diff -urN linux-2.6.10/Documentation/DocBook/hipl-kernelspace-api.tmpl linux-2.6.10-hip/Documentation/DocBook/hipl-kernelspace-api.tmpl
--- linux-2.6.10/Documentation/DocBook/hipl-kernelspace-api.tmpl 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/Documentation/DocBook/hipl-kernelspace-api.tmpl 2005-03-01 00:01:36.000000000 +0200
@@ -0,0 +1,407 @@
+
+
+
+ HIP for Linux Kernel Module Internal API Reference Manual
+
+
+
+ Miika
+ Komu
+
+
+ miika@iki.fi
+
+
+
+
+ Mika
+ Kousa
+
+
+ mika.kousa@hiit.fi
+
+
+
+
+ Kristian
+ Slavov
+
+
+ ksl@iki.fi
+
+
+
+
+
+
+ 2002
+ Helsinki Institute for Information Technology
+
+
+
+
+
+ This documentation 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
+
+
+
+ For more details see the file COPYING in the source
+ distribution of Linux.
+
+
+
+
+
+
+
+
+
+
+
+ Introduction
+
+
+
+ Host Identity Protocol for
+ Linux (HIPL) is implemented both in the Linux kernelspace
+ and userspace in C language. The kernelspace function interfaces
+ are described here. Most of the kernelspace functions are only to
+ be used internally by the implementation; using them outside the
+ implementation is not recommended.
+
+
+
+
+ All the file names are relative to the main HIP kernel directory
+ hipl/linux/net/ipv6/hip. Some of the files
+ are shared between kernelspace and userspace. In those cases, the
+ file is located in the kernelspace source directory,
+ hip/linux/net/ipv6/hip, to emphasize the
+ fact that the kernelspace has a very restricted set of utility
+ functions, and the shared code should survive without any
+ external libraries.
+
+
+
+
+
+ Database Functions
+
+
+ The database functions in db.c are
+ responsible of storing HIP related data. The functions store
+ information about ESP (SPD, SA, SPI), HITs and IPv6 addresses
+ (local and peer) and cookies sent to the peers. The data is
+ usually stored into local variables or in the case of ESP related
+ data, it is stored using the PF_KEY interface into elsewhere in
+ the kernel. The data is always accessed using accessor functions
+ which hide the locking issues from the caller.
+
+
+!Inet/ipv6/hip/db.c
+
+
+
+
+ Message Builder
+
+
+
+
+ The HIP daemon reuses the same TLV structure as the HIP kernel
+ module so that the userspace and kernelspace may share the same
+ library for building messages. The header of a daemon message is
+ used a bit differently; it is used for carrying errors and the
+ type of the operation to be performed on the daemon. The rest of
+ the TLV structures are the same as in HIP drafts but there are
+ some "extended" types that serve their purpose only in the
+ context of the HIP daemon (such as querying the public-private
+ DSA key pair of the host). The message builder is located in
+ linux/net/ipv6/hip/builder.c
+
+
+
+ The are one major benefits in using the builder interface. It
+ keeps the code simple because the details of building parameters
+ can be separated from "control" code. Doing otherwise results in
+ spaghetti code. The builder interface introduces an extra layer
+ of indirection which can be seen also as a slight performance
+ drawback. The benefits from the better readability of the code and
+ less bugs in the code are undisputable and they weight more than
+ slight than slight performance optimizations.
+
+
+
+ The extra layer of indirection is realized with builder accessors
+ functions that manipulate the HIP parameter data structures. This
+ means that the members in the structures should not be accesses
+ directly in the code; it is the responsibility of the accessor
+ functions to manipulate the members in structures and hide the
+ details of network byte ordering, padding issues and size
+ conversion.
+
+
+!Inet/ipv6/hip/builder.c
+
+
+
+
+ Packet Handling
+
+ Input
+
+
+ Packet handling in input.cconsists of
+ handling incoming packets or events triggered by the hooks in
+ IPv6 code. The incoming packets or events are checked for
+ validity and a HIP packet is output as a response when
+ needed. Base exchange, ESP protection and readdressing are all
+ handled here.
+
+
+!Inet/ipv6/hip/input.c
+
+
+
+ Output
+
+
+ Output functions in output.c are related to
+ the transmission of HIP related data from the localhost to a
+ peer. The functions are usually triggered by HIP input code, IPv6
+ packet output transmission or changes in the localhost
+ locators. Checksums are also implemented here.
+
+
+!Inet/ipv6/hip/output.c
+
+
+
+ Keying Material Handling
+
+
+ Keying material handling for ESP in keymat.c
+ is separated into its own set of functions. Keying material
+ handling consists of generation and drawing of the keys from the
+ data received from the peer.
+
+
+!Inet/ipv6/hip/keymat.c
+
+
+
+ HIP Initialization and Other General Functions
+
+
+ The initialization of the HIP module is handled in
+ hip.c. It also includes other more or less
+ general functions.
+
+
+!Inet/ipv6/hip/hip.c
+
+
+
+ Cookie Processing
+
+
+ File cookie.c includes all the functions
+ related to processing HIP cookies. It includes indexing and creation
+ of cookie at responder end, and solving of cookies at responder end.
+
+
+!Inet/ipv6/hip/cookie.c
+
+
+
+ Host Association Database (HADB)
+
+
+ File hadb.c contains the functions needed for
+ storing and retrieving HIP state. The databases are indexed by both
+ SPI and HIT.
+
+
+!Inet/ipv6/hip/hadb.c
+
+
+
+ Hashtable
+
+
+ File hashtable.c contains are generic implementation
+ of a hashtable.
+
+
+!Inet/ipv6/hip/hashtable.c
+
+
+
+ UPDATE
+
+
+ File update.c contains the code needed for UPDATE
+ packet handling.
+
+
+!Inet/ipv6/hip/update.c
+
+
+
+ Rendezvous Server
+
+
+ File rvs.c implements the rendezvous module
+ functionality.
+
+
+!Inet/ipv6/hip/rvs.c
+
+
+
+ Security Assocation Related Functions
+
+
+ File security.c includes the functionality needed
+ to set up proper IPsec security association using the standard kernel
+ interfaces.
+
+
+!Inet/ipv6/hip/security.c
+
+
+
+ Workqueue
+
+
+ File workqueue.c contains the HIP work queue
+ implementation. The main task of the work queues is to handle the
+ long lasting asymmetric cryptographic operations. Incoming HIP packets
+ are put into queue when other packets are being processed. This way,
+ incoming HIP packets do not need to be dropped while others are being
+ processed.
+
+
+
+
+ Miscellaneous Functions
+
+
+ File misc.c contains miscellaneous functions that
+ have not been categorized yet.
+
+
+!Inet/ipv6/hip/misc.c
+
+
+
+ Debugging utilities
+
+
+ Files debug.c and debug.h
+ contain the functions used for debugging HIP module. The messages
+ are sent to disk using the standard syslog facility.
+
+
+!Inet/ipv6/hip/debug.c
+
+
+
+
+
+
+
+ Unit Test Environment
+
+
+ Unit testing related functions are located in unit.c
+ and test.c.
+
+
+ Unit Testing Framework
+
+
+ The unit testing environment in
+ linux/net/ipv6/hip/unit.c is used for
+ triggering unit tests from the command line. The unit testing
+ environment separates test cases a special kind of hierarchy:
+ testspaces, testsuites and testcases. The testspace divides the
+ testcases to those that will be executed in the userspace and to
+ those that will be executed in the kernelspace. Testsuites can
+ refer to e.g. "crypto", "message" in the context of the
+ testspace. Testcases name the individual testcases in the
+ context of the testspace.
+
+
+!Inet/ipv6/hip/unit.c
+
+
+
+ Unit Testcases
+
+
+ File test.c contains the test cases for the
+ unit test environment.
+
+
+!Inet/ipv6/hip/test.c
+
+
+
+
+
+
+ Userspace Communication
+
+
+ Manual configuration of the HIP module from the userspace is handled
+ in daemon.c. The actual message transfer mechanism
+ is implemented in ioctl.c. HIP socket handler
+ is defined in socket.c.
+
+
+ Socket
+
+
+ File socket.c implements the kernel socket handler
+ interface. It handles the assignment of new EID numbers,
+ EID-to-HIT translation and socket options. All userspace sockets API
+ function calls, such as bind,
+ connect etc. translate eventually to this file if
+ the family of the socket is PF_HIP.
+
+
+
+ In effect, the HIP socket handler is a wrapper for the IP socket handlers.
+ The main purpose of the HIP socket handler is to catch the HIP socket
+ options and provide the EID-to-HIT translation service.
+
+
+!Inet/ipv6/hip/socket.c
+
+
+
+
+
+
diff -urN linux-2.6.10/Documentation/DocBook/Makefile linux-2.6.10-hip/Documentation/DocBook/Makefile
--- linux-2.6.10/Documentation/DocBook/Makefile 2004-12-24 23:34:45.000000000 +0200
+++ linux-2.6.10-hip/Documentation/DocBook/Makefile 2005-02-22 00:04:03.000000000 +0200
@@ -11,7 +11,8 @@
deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \
writing_usb_driver.sgml scsidrivers.sgml sis900.sgml \
kernel-api.sgml journal-api.sgml lsm.sgml usb.sgml \
- gadget.sgml libata.sgml mtdnand.sgml librs.sgml
+ gadget.sgml libata.sgml mtdnand.sgml librs.sgml \
+ hipl-kernelspace-api.sgml
###
# The build process is as follows (targets):
diff -urN linux-2.6.10/include/linux/in6.h linux-2.6.10-hip/include/linux/in6.h
--- linux-2.6.10/include/linux/in6.h 2004-12-24 23:35:00.000000000 +0200
+++ linux-2.6.10-hip/include/linux/in6.h 2005-02-01 17:16:37.000000000 +0200
@@ -132,6 +132,7 @@
#define IPPROTO_ICMPV6 58 /* ICMPv6 */
#define IPPROTO_NONE 59 /* IPv6 no next header */
#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */
+#define IPPTOTO_HIP 99 /* temporary */
/*
* IPv6 TLV options.
diff -urN linux-2.6.10/include/linux/in.h linux-2.6.10-hip/include/linux/in.h
--- linux-2.6.10/include/linux/in.h 2004-12-24 23:35:49.000000000 +0200
+++ linux-2.6.10-hip/include/linux/in.h 2005-02-01 17:16:37.000000000 +0200
@@ -39,6 +39,7 @@
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, /* Authentication Header protocol */
+ IPPROTO_HIP = 99, /* Temporary for HIP */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol */
diff -urN linux-2.6.10/include/linux/net.h linux-2.6.10-hip/include/linux/net.h
--- linux-2.6.10/include/linux/net.h 2004-12-24 23:34:32.000000000 +0200
+++ linux-2.6.10-hip/include/linux/net.h 2005-02-22 00:06:13.000000000 +0200
@@ -26,7 +26,9 @@
struct poll_table_struct;
struct inode;
-#define NPROTO 32 /* should be enough for now.. */
+/* BEGIN HIPL PATCH */
+#define NPROTO 33 /* should be enough for now.. */
+/* END HIPL PATCH */
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
diff -urN linux-2.6.10/include/linux/socket.h linux-2.6.10-hip/include/linux/socket.h
--- linux-2.6.10/include/linux/socket.h 2004-12-24 23:35:24.000000000 +0200
+++ linux-2.6.10-hip/include/linux/socket.h 2005-02-22 00:06:15.000000000 +0200
@@ -181,7 +181,8 @@
#define AF_WANPIPE 25 /* Wanpipe API Sockets */
#define AF_LLC 26 /* Linux LLC */
#define AF_BLUETOOTH 31 /* Bluetooth sockets */
-#define AF_MAX 32 /* For now.. */
+#define AF_HIP 32 /* Host Identity Protol */
+#define AF_MAX 33 /* For now.. */
/* Protocol families, same as address families. */
#define PF_UNSPEC AF_UNSPEC
@@ -213,6 +214,7 @@
#define PF_WANPIPE AF_WANPIPE
#define PF_LLC AF_LLC
#define PF_BLUETOOTH AF_BLUETOOTH
+#define PF_HIP AF_HIP
#define PF_MAX AF_MAX
/* Maximum queue length specifiable by listen. */
diff -urN linux-2.6.10/include/linux/sysctl.h linux-2.6.10-hip/include/linux/sysctl.h
--- linux-2.6.10/include/linux/sysctl.h 2004-12-24 23:34:58.000000000 +0200
+++ linux-2.6.10-hip/include/linux/sysctl.h 2005-02-22 00:06:15.000000000 +0200
@@ -190,7 +190,10 @@
NET_TR=14,
NET_DECNET=15,
NET_ECONET=16,
- NET_SCTP=17,
+ NET_SCTP=17,
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ NET_HIP=18,
+#endif
};
/* /proc/sys/kernel/random */
@@ -643,6 +646,13 @@
NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4,
};
+/* /proc/sys/net/hip */
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+enum {
+ NET_HIP_COOKIE_MAX_K_R1 = 1, /* max value of K to calculate received cookie */
+};
+#endif
+
/* CTL_PROC names: */
/* CTL_FS names: */
diff -urN linux-2.6.10/include/net/hip_glue.h linux-2.6.10-hip/include/net/hip_glue.h
--- linux-2.6.10/include/net/hip_glue.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/include/net/hip_glue.h 2005-02-01 17:16:37.000000000 +0200
@@ -0,0 +1,35 @@
+#ifndef _NET_HIP_GLUE_H
+#define _NET_HIP_GLUE_H
+
+#ifdef __KERNEL__
+
+#include
+#include
+#include
+#include
+#include
+
+#define HIP_CALLPROC(X) if(hip_functions.X) hip_functions.X
+#define HIP_CALLFUNC(X,Y) (!hip_functions.X)?(Y):hip_functions.X
+#define HIP_SETCALL(X) if(hip_functions.X) printk("hip: Warning, function assigned twice!\n"); \
+ hip_functions.X = X
+#define HIP_INVALIDATE(X) hip_functions.X = NULL
+
+struct hip_callable_functions {
+ /* int (*hip_exchange) (struct sockaddr *uaddr, int addr_len); */
+ void (*hip_handle_esp) (uint32_t spi, struct ipv6hdr *ipv6);
+ int (*hip_handle_output) (struct ipv6hdr *hdr, struct sk_buff *skb);
+ int (*hip_get_addr) (struct in6_addr *hit, struct in6_addr *addr);
+ int (*hip_get_saddr) (struct flowi *fl, struct in6_addr *hit_storage);
+ void (*hip_unknown_spi) (struct sk_buff *skb, uint32_t spi);
+ void (*hip_handle_ipv6_dad_completed)(int ifindex);
+ void (*hip_handle_inet6_addr_del)(int ifindex);
+ /* int (*hip_update_spi_waitlist_ispending)(uint32_t spi); */
+ uint32_t (*hip_get_default_spi_out) (struct in6_addr *hit, int *state_ok);
+};
+
+extern struct hip_callable_functions hip_functions;
+
+#endif /* __KERNEL__ */
+
+#endif /* _NET_HIP_GLUE_H */
diff -urN linux-2.6.10/include/net/hip.h linux-2.6.10-hip/include/net/hip.h
--- linux-2.6.10/include/net/hip.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/include/net/hip.h 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,919 @@
+#ifndef _NET_HIP
+#define _NET_HIP
+
+/*
+ * HIP header and parameter related constants and structures.
+ *
+ * Authors:
+ * - Janne Lundberg
+ * - Miika Komu
+ * - Mika Kousa
+ * - Kristian Slavov
+ *
+ * TODO:
+ * - split this file into net/hip.h (packet structs etc) and linux/hip.h
+ * (implementation specific stuff)
+ * - hip_local_hi and hip_lhid contain reduntantly the anonymous bit?
+ * - hip_tlv_common should be packed?
+ * - the packing of the structures could be hidden in the builder
+ * - replace all in6add6 with hip_hit
+ *
+ * BUGS:
+ * -
+ *
+ */
+
+#ifdef __KERNEL__
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+# include
+
+typedef uint16_t in_port_t;
+
+#else
+#include
+#include
+
+#endif /* __KERNEL__ */
+
+#define HIP_MAX_PACKET 2048
+
+#define HIP_HIT_KNOWN 1
+#define HIP_HIT_ANON 2
+
+#define HIP_HIT_TYPE_MASK_HAA 0x80
+#define HIP_HIT_TYPE_MASK_126 0x40
+
+#define HIP_HIT_TYPE_HASH126 1
+#define HIP_HIT_TYPE_HAA_HASH 2
+
+#define HIP_I1 1
+#define HIP_R1 2
+#define HIP_I2 3
+#define HIP_R2 4
+#define HIP_CER 5
+#define HIP_UPDATE 6
+#define HIP_NOTIFY 7
+#define HIP_CLOSE 8
+#define HIP_CLOSE_ACK 9
+//#define HIP_REA 10 /* removed from ietf-hip-mm-00 */
+#define HIP_BOS 11 /* removed from ietf-hip-base-01 */
+//#define HIP_AC 12 /* removed from ietf-hip-mm-00 */
+//#define HIP_ACR 13 /* removed from ietf-hip-mm-00 */
+#define HIP_PAYLOAD 64 /* xxx */
+
+#define SO_HIP_GLOBAL_OPT 1
+#define SO_HIP_SOCKET_OPT 2
+
+/* HIP socket options */
+#define SO_HIP_ADD_LOCAL_HI 1
+#define SO_HIP_DEL_LOCAL_HI 2
+#define SO_HIP_ADD_PEER_MAP_HIT_IP 3
+#define SO_HIP_DEL_PEER_MAP_HIT_IP 4
+#define SO_HIP_RUN_UNIT_TEST 5
+#define SO_HIP_RST 6
+#define SO_HIP_ADD_RVS 7
+#define SO_HIP_DEL_RVS 8
+#define SO_HIP_GET_MY_EID 9
+#define SO_HIP_SET_MY_EID 10
+#define SO_HIP_GET_PEER_EID 11
+#define SO_HIP_SET_PEER_EID 12
+#define SO_HIP_NULL_OP 13
+#define SO_HIP_UNIT_TEST 14
+#define SO_HIP_BOS 15
+#define SO_HIP_GET_PEER_LIST 16
+
+#define HIP_HOST_ID_HOSTNAME_LEN_MAX 64
+
+#define HIP_ENDPOINT_FLAG_HIT 1
+#define HIP_ENDPOINT_FLAG_ANON 2
+/* Other flags: keep them to the power of two! */
+
+#define HIP_HOST_ID_RR_DSA_MAX_T_VAL 8
+#define HIP_HOST_ID_RR_T_SIZE 1
+#define HIP_HOST_ID_RR_Q_SIZE 20
+#define HIP_HOST_ID_RR_P_BASE_SIZE 20
+#define HIP_HOST_ID_RR_G_BASE_SIZE 20
+#define HIP_HOST_ID_RR_Y_BASE_SIZE 20
+#define HIP_HOST_ID_RR_DSA_PRIV_KEY_SIZE 20
+
+//#define HIP_CONTROL_PIGGYBACK_ALLOW 0x4000 /* Host accepts piggybacked ESP in I2 and R2 */
+
+#define HIP_PSEUDO_CONTROL_REQ_RVS 0x8000
+//#define HIP_CONTROL_ESP_64 0x1000 /* Use 64-bit sequence number */
+#define HIP_CONTROL_RVS_CAPABLE 0x8000 /* not yet defined */
+#define HIP_CONTROL_CONCEAL_IP /* still undefined */
+#define HIP_CONTROL_CERTIFICATES 0x0002 /* Certificate packets follow */
+#define HIP_CONTROL_HIT_ANON 0x0001 /* Anonymous HI */
+#define HIP_CONTROL_NONE 0x0000
+
+#define HIP_CONTROL_SHT_SHIFT 13
+#define HIP_CONTROL_DHT_SHIFT 10
+#define HIP_CONTROL_SHT_MASK (0x8000|0x4000|0x2000) /* bits 16-14 */
+#define HIP_CONTROL_DHT_MASK (0x1000|0x800|0x400) /* bits 13-11 */
+#define HIP_CONTROL_SHT_TYPE1 1
+#define HIP_CONTROL_SHT_TYPE2 2
+#define HIP_CONTROL_DHT_TYPE1 HIP_CONTROL_SHT_TYPE1
+#define HIP_CONTROL_DHT_TYPE2 HIP_CONTROL_SHT_TYPE2
+#define HIP_CONTROL_SHT_ALL (HIP_CONTROL_SHT_MASK >> HIP_CONTROL_SHT_SHIFT)
+#define HIP_CONTROL_DHT_ALL (HIP_CONTROL_DHT_MASK >> HIP_CONTROL_DHT_SHIFT)
+
+#define HIP_VER_RES 0x10 /* Version 1, reserved 0 */
+#define HIP_VER_MASK 0xF0
+#define HIP_RES_MASK 0x0F
+
+#define HIP_STATE_NONE 0 /* No state, structure unused */
+#define HIP_STATE_UNASSOCIATED 1 /* ex-E0 */
+#define HIP_STATE_I1_SENT 2 /* ex-E1 */
+#define HIP_STATE_I2_SENT 3 /* ex-E2 */
+#define HIP_STATE_R2_SENT 4
+#define HIP_STATE_ESTABLISHED 5 /* ex-E3 */
+#define HIP_STATE_REKEYING 6 /* ex-E4 */
+/* when adding new states update debug.c hip_state_str */
+#define HIP_STATE_FAILED 7
+
+#define HIP_PARAM_MIN -1 /* exclusive */
+
+#define HIP_PARAM_SPI 1
+#define HIP_PARAM_R1_COUNTER 2
+#define HIP_PARAM_REA 3
+#define HIP_PARAM_PUZZLE 5
+#define HIP_PARAM_SOLUTION 7
+#define HIP_PARAM_NES 9
+#define HIP_PARAM_SEQ 11
+#define HIP_PARAM_ACK 13
+#define HIP_PARAM_DIFFIE_HELLMAN 15
+#define HIP_PARAM_HIP_TRANSFORM 17
+#define HIP_PARAM_ESP_TRANSFORM 19
+#define HIP_PARAM_ENCRYPTED 21
+#define HIP_PARAM_HOST_ID 35
+#define HIP_PARAM_CERT 64
+#define HIP_PARAM_RVA_REQUEST 100
+#define HIP_PARAM_RVA_REPLY 102
+
+//#define HIP_PARAM_REA_INFO 8 /* to be removed */
+//#define HIP_PARAM_AC_INFO 129 /* mm-01: to be removed */
+//#define HIP_PARAM_FA_INFO 130 /* mm-01: to be removed */
+
+#define HIP_PARAM_NOTIFY 256
+#define HIP_PARAM_ECHO_REQUEST_SIGN 1022
+#define HIP_PARAM_ECHO_RESPONSE_SIGN 1024
+
+/* Range 32768 - 49141 can be used for HIPL private parameters. */
+#define HIP_PARAM_HIT 32768
+#define HIP_PARAM_IPV6_ADDR 32769
+#define HIP_PARAM_DSA_SIGN_DATA 32770 /* XX TODO: change to digest */
+#define HIP_PARAM_HI 32771
+#define HIP_PARAM_DH_SHARED_KEY 32772
+#define HIP_PARAM_UNIT_TEST 32773
+#define HIP_PARAM_EID_SOCKADDR 32774
+#define HIP_PARAM_EID_ENDPOINT 32775 /* Pass endpoint_hip structures into kernel */
+#define HIP_PARAM_EID_IFACE 32776
+#define HIP_PARAM_EID_ADDR 32777
+#define HIP_PARAM_UINT 32778 /* Unsigned integer */
+/* End of HIPL private parameters. */
+
+#define HIP_PARAM_FROM_SIGN 65100
+#define HIP_PARAM_TO_SIGN 65102
+#define HIP_PARAM_HMAC 65245
+#define HIP_PARAM_HMAC2 65247
+#define HIP_PARAM_HIP_SIGNATURE2 65277
+#define HIP_PARAM_HIP_SIGNATURE 65279
+#define HIP_PARAM_ECHO_REQUEST 65281
+#define HIP_PARAM_ECHO_RESPONSE 65283
+#define HIP_PARAM_FROM 65300
+#define HIP_PARAM_TO 65302
+#define HIP_PARAM_RVA_HMAC 65320
+#define HIP_PARAM_VIA_RVS 65500
+#define HIP_PARAM_MAX 65536 /* exclusive */
+
+#define HIP_HIP_RESERVED 0
+#define HIP_HIP_AES_SHA1 1
+#define HIP_HIP_3DES_SHA1 2
+#define HIP_HIP_3DES_MD5 3
+#define HIP_HIP_NULL_SHA1 5
+
+#define HIP_TRANSFORM_HIP_MAX 6
+#define HIP_TRANSFORM_ESP_MAX 6
+
+#define HIP_ESP_RESERVED 0
+#define HIP_ESP_AES_SHA1 1
+#define HIP_ESP_3DES_SHA1 2
+#define HIP_ESP_3DES_MD5 3
+#define HIP_ESP_BLOWFISH_SHA1 4
+#define HIP_ESP_NULL_SHA1 5
+#define HIP_ESP_NULL_MD5 6
+
+#define ESP_AES_KEY_BITS 128
+#define ESP_3DES_KEY_BITS 192
+
+/* Only for testing!!! */
+#define HIP_ESP_NULL_NULL 0x0
+
+#define HIP_DH_384 1 /* 384-bit group */
+#define HIP_DH_OAKLEY_1 2 /* 768-bit OAKLEY well known group 1 */
+#define HIP_DH_OAKLEY_5 3 /* 1536-bit MODP group */
+#define HIP_DH_OAKLEY_15 4 /* 3072-bit MODP group */
+#define HIP_DH_OAKLEY_17 5 /* 6144-bit MODP group */
+#define HIP_DH_OAKLEY_18 6 /* 8192-bit MODP group */
+#define HIP_DEFAULT_DH_GROUP_ID HIP_DH_OAKLEY_5
+
+#define HIP_HI_DSA 3
+#define HIP_SIG_DSA 3
+#define HIP_HI_RSA 5
+#define HIP_SIG_RSA 5
+#define HIP_HI_DEFAULT_ALGO HIP_HI_RSA
+#define HIP_SIG_DEFAULT_ALGO HIP_SIG_RSA
+
+#define HIP_DIGEST_MD5 1
+#define HIP_DIGEST_SHA1 2
+#define HIP_DIGEST_SHA1_HMAC 3
+#define HIP_DIGEST_MD5_HMAC 4
+
+#define HIP_DIRECTION_ENCRYPT 1
+#define HIP_DIRECTION_DECRYPT 2
+
+#define HIP_KEYMAT_INDEX_NBR_SIZE 1
+
+#define HIP_VERIFY_PUZZLE 0
+#define HIP_SOLVE_PUZZLE 1
+#define HIP_PUZZLE_OPAQUE_LEN 2
+
+#define HIP_PARAM_ENCRYPTED_IV_LEN 8
+
+#define HIP_DSA_SIGNATURE_LEN 41
+/* Assume that RSA key is 1024 bits. RSA signature is as long as the key
+ (1024 bits -> 128 bytes) */
+#define HIP_RSA_SIGNATURE_LEN 128
+
+#define ENOTHIT 666
+
+/* Domain Identifiers (to be used in HOST_ID TLV) */
+#define HIP_DI_NONE 0
+#define HIP_DI_FQDN 1
+#define HIP_DI_NAI 2
+
+
+/* Rendezvous types */
+#define HIP_RVA_RELAY_I1 1
+#define HIP_RVA_RELAY_I1R1 2
+#define HIP_RVA_RELAY_I1R1I2 3
+#define HIP_RVA_RELAY_I1R1I2R2 4
+#define HIP_RVA_RELAY_ESP_I1 5
+#define HIP_RVA_REDIRECT_I1 6
+
+/* Returns length of TLV option (contents) with padding. */
+#define HIP_LEN_PAD(len) \
+ ((((len) & 0x07) == 0) ? (len) : ((((len) >> 3) << 3) + 8))
+
+#ifdef __KERNEL__
+ #ifndef hton64
+ #define hton64(n) __cpu_to_be64(n)
+ #endif
+ #ifndef ntoh64
+ #define ntoh64(n) __be64_to_cpu(n)
+ #endif
+#else
+ #if __BYTE_ORDER == __BIG_ENDIAN
+ #define hton64(i) (i)
+ #define ntoh64(i) (i)
+ #else
+ #define hton64(i) ( ((__u64)(htonl((i) & 0xffffffff)) << 32) | htonl(((i) >> 32) & 0xffffffff ) )
+ #define ntoh64 hton64
+ #endif
+
+#endif /* __KERNEL__ */
+
+
+#define HIP_AH_SHA_LEN 20
+
+typedef struct in6_addr hip_hit_t;
+typedef uint16_t se_family_t;
+typedef uint16_t se_length_t;
+typedef uint16_t se_hip_flags_t;
+typedef uint32_t sa_eid_t;
+typedef uint8_t hip_hdr_type_t;
+typedef uint8_t hip_hdr_len_t;
+typedef uint16_t hip_hdr_err_t;
+typedef uint16_t hip_tlv_type_t;
+typedef uint16_t hip_tlv_len_t;
+typedef struct hip_hadb_state hip_ha_t;
+/* todo: remove HIP_HASTATE_SPIOK */
+typedef enum { HIP_HASTATE_INVALID=0, HIP_HASTATE_SPIOK=1,
+ HIP_HASTATE_HITOK=2, HIP_HASTATE_VALID=3 } hip_hastate_t;
+/*
+ * Use accessor functions defined in hip_build.h, do not access members
+ * directly to avoid hassle with byte ordering and number conversion.
+ */
+struct hip_common {
+ uint8_t payload_proto;
+ uint8_t payload_len;
+ uint8_t type_hdr;
+ uint8_t ver_res;
+
+ uint16_t control;
+ uint16_t checksum;
+
+ struct in6_addr hits; /* Sender HIT */
+ struct in6_addr hitr; /* Receiver HIT */
+} __attribute__ ((packed));
+
+
+/*
+ * Localhost Host Identity. Used only internally in the implementation.
+ * Used for wrapping anonymous bit with the corresponding HIT.
+ */
+struct hip_lhi
+{
+ uint16_t anonymous; /* Is this an anonymous HI */
+ struct in6_addr hit;
+} __attribute__ ((packed));
+
+
+/*
+ * Use accessor functions defined in hip_build.h, do not access members
+ * directly to avoid hassle with byte ordering and length conversion.
+ */
+struct hip_tlv_common {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+} __attribute__ ((packed));
+
+#if 0
+struct hip_i1 {
+ uint8_t payload_proto;
+ hip_hdr_len_t payload_len;
+ hip_hdr_type_t type_hdr;
+ uint8_t ver_res;
+
+ uint16_t control;
+ uint16_t checksum;
+
+ struct in6_addr hits; /* Sender HIT */
+ struct in6_addr hitr; /* Receiver HIT */
+} __attribute__ ((packed));
+#endif
+
+struct hip_keymat_keymat
+{
+ size_t offset; /* Offset into the key material */
+ size_t keymatlen; /* Length of the key material */
+
+ void *keymatdst; /* Pointer to beginning of key material */
+};
+
+/*
+ * Used in executing a unit test case in a test suite in the kernel module.
+ */
+struct hip_unit_test {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint16_t suiteid;
+ uint16_t caseid;
+} __attribute__ ((packed));
+
+struct hip_spi {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t spi;
+} __attribute__ ((packed));
+
+
+struct hip_r1_counter {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t reserved;
+ uint64_t generation;
+} __attribute__ ((packed));
+
+
+struct hip_puzzle {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint8_t K;
+ uint8_t lifetime;
+ uint8_t opaque[HIP_PUZZLE_OPAQUE_LEN];
+ uint64_t I;
+} __attribute__ ((packed));
+
+struct hip_solution {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint8_t K;
+ uint8_t reserved;
+ uint8_t opaque[HIP_PUZZLE_OPAQUE_LEN];
+ uint64_t I;
+ uint64_t J;
+} __attribute__ ((packed));
+
+struct hip_diffie_hellman {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint8_t group_id;
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+typedef uint16_t hip_transform_suite_t;
+
+struct hip_hip_transform {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ hip_transform_suite_t suite_id[HIP_TRANSFORM_HIP_MAX];
+} __attribute__ ((packed));
+
+struct hip_esp_transform {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint16_t reserved;
+
+ hip_transform_suite_t suite_id[HIP_TRANSFORM_ESP_MAX];
+} __attribute__ ((packed));
+
+/*
+ * XX FIXME: HIP AND ESP TRANSFORM ARE NOT SYMMETRIC (RESERVED)
+ */
+struct hip_any_transform {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ /* XX TODO: replace with MAX(HIP, ESP) */
+ hip_transform_suite_t suite_id[HIP_TRANSFORM_HIP_MAX +
+ HIP_TRANSFORM_ESP_MAX];
+} __attribute__ ((packed));
+
+/* RFC2535 3.1 KEY RDATA format */
+struct hip_host_id_key_rdata {
+ uint16_t flags;
+ uint8_t protocol;
+ uint8_t algorithm;
+
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+struct hip_host_id {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint16_t hi_length;
+ uint16_t di_type_length;
+
+ struct hip_host_id_key_rdata rdata;
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+struct hip_encrypted_aes_sha1 {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t reserved;
+ uint8_t iv[16];
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+struct hip_encrypted_3des_sha1 {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t reserved;
+ uint8_t iv[8];
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+struct hip_encrypted_null_sha1 {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t reserved;
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+struct hip_sig {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint8_t algorithm;
+
+ /* fixed part end */
+} __attribute__ ((packed));
+
+struct hip_sig2 {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint8_t algorithm;
+
+ /* fixed part end */
+} __attribute__ ((packed));
+
+struct hip_nes {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint16_t reserved;
+ uint16_t keymat_index;
+ uint32_t old_spi;
+ uint32_t new_spi;
+} __attribute__ ((packed));
+
+
+struct hip_seq {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t update_id;
+} __attribute__ ((packed));
+
+struct hip_ack {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint32_t peer_update_id; /* n items */
+} __attribute__ ((packed));
+
+struct hip_notify {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint16_t reserved;
+ uint16_t msgtype;
+ /* end of fixed part */
+} __attribute__ ((packed));
+
+struct hip_rea_info_addr_item {
+ uint32_t lifetime;
+ uint32_t reserved;
+ struct in6_addr address;
+} __attribute__ ((packed));
+
+struct hip_rea {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint32_t spi;
+ /* fixed part ends */
+} __attribute__ ((packed));
+
+struct hip_hmac {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint8_t hmac_data[HIP_AH_SHA_LEN];
+} __attribute__ ((packed));
+
+struct hip_cert {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+
+ uint8_t cert_count;
+ uint8_t cert_id;
+ uint8_t cert_type;
+ /* end of fixed part */
+} __attribute__ ((packed));
+
+/************* RVS *******************/
+
+struct hip_rva_request {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint32_t lifetime;
+ /* RVA types */
+} __attribute__ ((packed));
+
+struct hip_rva_reply {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint32_t lifetime;
+ /* RVA types */
+} __attribute__ ((packed));
+
+struct hip_rva_hmac {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint8_t hmac_data[HIP_AH_SHA_LEN];
+} __attribute__ ((packed));
+
+struct hip_from {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint8_t address[16];
+} __attribute__ ((packed));
+
+struct hip_to {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint8_t address[16];
+} __attribute__ ((packed));
+
+struct hip_via_rvs {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ uint8_t address[16];
+ /* the rest of the addresses */
+} __attribute__ ((packed));
+
+struct hip_echo_request {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ /* opaque */
+} __attribute__ ((packed));
+
+struct hip_echo_response {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ /* opaque */
+} __attribute__ ((packed));
+
+/* Structure describing an endpoint. This structure is used by the resolver in
+ * the userspace, so it is not length-padded like HIP parameters. All of the
+ * members are in network byte order.
+ */
+struct endpoint {
+ se_family_t family; /* PF_HIP, PF_XX */
+ se_length_t length; /* length of the whole endpoint in octets */
+};
+
+/*
+ * Note: not padded
+ */
+struct endpoint_hip {
+ se_family_t family; /* PF_HIP */
+ se_length_t length; /* length of the whole endpoint in octets */
+ se_hip_flags_t flags; /* e.g. ANON or HIT */
+ union {
+ struct hip_host_id host_id;
+ struct in6_addr hit;
+ } id;
+};
+
+struct sockaddr_eid {
+ unsigned short int eid_family;
+ in_port_t eid_port;
+ sa_eid_t eid_val;
+} __attribute__ ((packed));
+
+/*
+ * This structure is by the native API to carry local and peer identities
+ * from libc (setmyeid and setpeereid calls) to the HIP socket handler
+ * (setsockopt). It is almost the same as endpoint_hip, but it is
+ * length-padded like HIP parameters to make it usable with the builder
+ * interface.
+ */
+struct hip_eid_endpoint {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ struct endpoint_hip endpoint;
+} __attribute__ ((packed));
+
+typedef uint16_t hip_eid_iface_type_t;
+
+struct hip_eid_iface {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ hip_eid_iface_type_t if_index;
+} __attribute__ ((packed));
+
+struct hip_eid_sockaddr {
+ hip_tlv_type_t type;
+ hip_tlv_len_t length;
+ struct sockaddr sockaddr;
+} __attribute__ ((packed));
+
+#ifdef __KERNEL__
+
+#define HIP_MAX_KEY_LEN 32 /* max. draw: 256 bits! */
+
+/* Both for storing peer host ids and localhost host ids */
+#define HIP_HOST_ID_MAX 16
+
+struct hip_crypto_key {
+ char key[HIP_MAX_KEY_LEN];
+};
+
+struct hip_packet_dh_sig
+{
+ struct hip_common *common;
+ struct hip_diffie_hellman *dh;
+ struct hip_host_id *host_id;
+ struct hip_sig2 *hsig2;
+};
+
+struct hip_context
+{
+ struct sk_buff *skb_in; /* received skbuff */
+ struct hip_common *input; /* received packet */
+ struct hip_common *output; /* packet to be built and sent */
+
+ struct hip_crypto_key hip_enc_out;
+ struct hip_crypto_key hip_hmac_out;
+ struct hip_crypto_key esp_out;
+ struct hip_crypto_key auth_out;
+
+ struct hip_crypto_key hip_enc_in;
+ struct hip_crypto_key hip_hmac_in;
+ struct hip_crypto_key esp_in;
+ struct hip_crypto_key auth_in;
+
+ char *dh_shared_key;
+ size_t dh_shared_key_len;
+
+ uint16_t current_keymat_index; /* the byte offset index in draft chapter HIP KEYMAT */
+ unsigned char current_keymat_K[HIP_AH_SHA_LEN];
+ uint8_t keymat_calc_index; /* the one byte index number used
+ * during the keymat calculation */
+ uint16_t keymat_index; /* KEYMAT offset */
+};
+
+struct hip_context_dh_sig
+{
+ struct hip_common *out_packet; /* kmalloced */
+ struct hip_packet_dh_sig hip_out; /* packet to be built and sent */
+};
+
+struct hip_peer_addr_list_item
+{
+ struct list_head list;
+
+ struct in6_addr address;
+ int address_state; /* current state of the
+ * address (PEER_ADDR_STATE_xx) */
+ int is_preferred; /* 1 if this address was set as
+ preferred address in the REA */
+ uint32_t lifetime;
+ struct timeval modified_time; /* time when this address was
+ added or updated */
+ uint32_t seq_update_id; /* the Update ID in SEQ parameter
+ this address is related to */
+ uint8_t echo_data[4]; /* data put into the ECHO_REQUEST parameter */
+};
+
+#define PEER_ADDR_STATE_UNVERIFIED 1
+#define PEER_ADDR_STATE_ACTIVE 2
+#define PEER_ADDR_STATE_DEPRECATED 3
+
+#define HIP_SPI_DIRECTION_OUT 1
+#define HIP_SPI_DIRECTION_IN 2
+
+/* for HIT-SPI hashtable only */
+struct hip_hit_spi {
+ struct list_head list;
+ spinlock_t lock;
+ atomic_t refcnt;
+ hip_hit_t hit;
+ uint32_t spi; /* this SPI spi belongs to the HIT hit */
+};
+
+struct hip_spi_in_item
+{
+ struct list_head list;
+ uint32_t spi;
+ uint32_t new_spi; /* SPI is changed to this when rekeying */
+ int ifindex; /* ifindex if the netdev to which this is related to */
+ unsigned long timestamp; /* when SA was created */
+ int updating; /* UPDATE is in progress */
+ uint32_t nes_spi_out; /* UPDATE, the stored outbound
+ * SPI related to the inbound
+ * SPI we sent in reply (useless ?) */
+ uint16_t keymat_index; /* advertized keymat index */
+ int update_state_flags; /* 0x1=received ack for
+ sent SEQ, 0x2=received
+ peer's NES,
+ both=0x3=can move back
+ to established */
+ uint32_t seq_update_id; /* the Update ID in SEQ parameter these SPI are related to */
+ struct hip_nes stored_received_nes; /* the corresponding NES of peer */
+};
+
+struct hip_spi_out_item
+{
+ struct list_head list;
+ uint32_t spi;
+ uint32_t new_spi; /* spi is changed to this when rekeying */
+ uint32_t seq_update_id; /* USELESS, IF SEQ ID WILL BE RELATED TO ADDRESS ITEMS,
+ * NOT OUTBOUND SPIS *//* the Update ID in SEQ parameter these SPI are related to */
+
+ struct list_head peer_addr_list; /* Peer's IPv6 addresses */
+ struct in6_addr preferred_address; /* check */
+};
+
+struct hip_hadb_state
+{
+ struct list_head next_hit;
+
+ spinlock_t lock;
+ atomic_t refcnt;
+ hip_hastate_t hastate;
+ int state;
+ uint16_t local_controls;
+ uint16_t peer_controls;
+ hip_hit_t hit_our; /* The HIT we use with this host */
+ hip_hit_t hit_peer; /* Peer's HIT */
+ struct list_head spis_in; /* SPIs for inbound SAs, hip_spi_in_item */
+ struct list_head spis_out; /* SPIs for outbound SAs, hip_spi_out_item */
+ uint32_t default_spi_out;
+ struct in6_addr preferred_address; /* preferred dst address to use when
+ * sending data to peer */
+ struct in6_addr bex_address; /* test, for storing address during the base exchange */
+ uint32_t lsi_peer;
+ uint32_t lsi_our;
+ int esp_transform;
+ uint64_t birthday;
+ char *dh_shared_key;
+ size_t dh_shared_key_len;
+
+ /* The initiator computes the keys when it receives R1.
+ * The keys are needed only when R2 is received. We store them
+ * here in the mean time.
+ */
+ struct hip_crypto_key hip_enc_out; /* outgoing HIP packets */
+ struct hip_crypto_key hip_hmac_out;
+ struct hip_crypto_key esp_out; /* outgoing ESP packets */
+ struct hip_crypto_key auth_out;
+ struct hip_crypto_key hip_enc_in; /* incoming HIP packets */
+ struct hip_crypto_key hip_hmac_in;
+ struct hip_crypto_key esp_in; /* incoming ESP packets */
+ struct hip_crypto_key auth_in;
+
+ uint16_t current_keymat_index; /* the byte offset index in draft chapter HIP KEYMAT */
+ uint8_t keymat_calc_index; /* the one byte index number used
+ * during the keymat calculation */
+ unsigned char current_keymat_K[HIP_AH_SHA_LEN]; /* last Kn, where n is keymat_calc_index */
+ uint32_t update_id_out; /* stored outgoing UPDATE ID counter */
+ uint32_t update_id_in; /* stored incoming UPDATE ID counter */
+
+ int skbtest; /* just for testing */
+};
+
+struct hip_cookie_entry {
+ int used;
+ struct in6_addr peer_hit;
+ uint64_t i;
+ uint64_t j; /* not needed ? */
+ uint64_t k;
+ uint64_t hash_target;
+ struct in6_addr initiator;
+ struct in6_addr responder;
+};
+
+struct hip_work_order {
+ int type;
+ int subtype;
+ void *arg1;
+ void *arg2;
+ union {
+ char ch[8];
+ uint32_t u32[2];
+ uint64_t u64;
+ } arg;
+ struct list_head queue;
+ void (*destructor)(struct hip_work_order *hwo);
+};
+
+
+struct hip_host_id_entry {
+/* this needs to be first (list_for_each_entry, list
+ head being of different type) */
+ struct list_head next;
+
+ struct hip_lhi lhi;
+ /* struct in_addr lsi; */
+ /* struct in6_addr ipv6_addr[MAXIP]; */
+ struct hip_host_id *host_id; /* allocated dynamically */
+};
+
+struct hip_eid_owner_info {
+ uid_t uid;
+ gid_t gid;
+};
+
+struct hip_eid_db_entry {
+ struct list_head next;
+ struct hip_eid_owner_info owner_info;
+ struct sockaddr_eid eid; /* XX FIXME: the port is unneeded */
+ struct hip_lhi lhi;
+};
+
+#define HIP_UNIT_ERR_LOG_MSG_MAX_LEN 200
+#endif /* __KERNEL__ */
+
+/* Some default settings for HIPL */
+//#define HIP_DEFAULT_HIP_ENCR HIP_ENCR_3DES /* HIP transform in R1 */
+//#define HIP_DEFAULT_ESP_ENCR HIP_ENCR_3DES /* ESP transform in R1 */
+#define HIP_DEFAULT_AUTH HIP_AUTH_SHA /* AUTH transform in R1 */
+#define HIP_DEFAULT_RVA_LIFETIME 600 /* in seconds? */
+#endif /* _NET_HIP */
diff -urN linux-2.6.10/include/net/ipv6.h linux-2.6.10-hip/include/net/ipv6.h
--- linux-2.6.10/include/net/ipv6.h 2004-12-24 23:35:24.000000000 +0200
+++ linux-2.6.10-hip/include/net/ipv6.h 2005-02-22 00:06:17.000000000 +0200
@@ -311,6 +311,21 @@
a->s6_addr32[2] | a->s6_addr32[3] ) == 0);
}
+
+
+static inline int ipv6_addr_is_hit(const struct in6_addr *a)
+{
+ int t;
+
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ t = a->s6_addr[0] & 0xC0;
+#else
+ t = 0;
+#endif
+ return ((t == 0x40) ||
+ (t == 0x80));
+}
+
/*
* Prototypes exported by ipv6
*/
diff -urN linux-2.6.10/include/net/xfrm.h linux-2.6.10-hip/include/net/xfrm.h
--- linux-2.6.10/include/net/xfrm.h 2004-12-24 23:35:24.000000000 +0200
+++ linux-2.6.10-hip/include/net/xfrm.h 2005-02-22 00:06:17.000000000 +0200
@@ -20,6 +20,10 @@
extern struct semaphore xfrm_cfg_sem;
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+#include
+#endif
+
/* Organization of SPD aka "XFRM rules"
------------------------------------
@@ -114,6 +118,9 @@
int trailer_len;
} props;
+#if defined(CONFIG_ESPBEET)
+ xfrm_address_t outeraddr;
+#endif
struct xfrm_lifetime_cfg lft;
/* Data for transformer */
@@ -146,6 +153,11 @@
/* Private data of this transformer, format is opaque,
* interpreted by xfrm_type methods. */
void *data;
+
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ hip_hit_t src_hit;
+ hip_hit_t dst_hit;
+#endif
};
enum {
@@ -157,6 +169,12 @@
XFRM_STATE_DEAD
};
+enum {
+ XFRM_MODE_TRANSPORT=0,
+ XFRM_MODE_TUNNEL,
+ XFRM_MODE_BEET
+};
+
struct xfrm_type;
struct xfrm_dst;
struct xfrm_policy_afinfo {
diff -urN linux-2.6.10/Makefile linux-2.6.10-hip/Makefile
--- linux-2.6.10/Makefile 2004-12-24 23:35:01.000000000 +0200
+++ linux-2.6.10-hip/Makefile 2005-03-18 00:01:12.000000000 +0200
@@ -1,7 +1,7 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 10
-EXTRAVERSION =
+EXTRAVERSION = -hip
NAME=Woozy Numbat
# *DOCUMENTATION*
diff -urN linux-2.6.10/net/ipv6/addrconf.c linux-2.6.10-hip/net/ipv6/addrconf.c
--- linux-2.6.10/net/ipv6/addrconf.c 2004-12-24 23:35:28.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/addrconf.c 2005-02-22 00:06:26.000000000 +0200
@@ -81,6 +81,10 @@
#include
#include
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+#include
+#endif
+
/* Set to 3 to get tracing... */
#define ACONF_DEBUG 2
@@ -477,6 +481,7 @@
inet6_ifa_count--;
kfree(ifp);
}
+EXPORT_SYMBOL(inet6_ifa_finish_destroy);
/* On success it returns ifp with increased reference count */
@@ -965,6 +970,7 @@
return ifp;
}
+EXPORT_SYMBOL(ipv6_get_ifaddr);
int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
{
@@ -1638,6 +1644,16 @@
*/
if (idev->addr_list == NULL)
addrconf_ifdown(idev->dev, 1);
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ /* We must avoid sending multiple UPDATEs when
+ * network device goes down. addrconf_ifdown
+ * sends NETDEV_DOWN notification, so we
+ * handle address deletion only when there are
+ * addresses left after deletion of an IPv6
+ * address */
+ else
+ HIP_CALLFUNC(hip_handle_inet6_addr_del, 0)(ifindex);
+#endif
return 0;
}
}
@@ -2075,6 +2091,7 @@
neigh_ifdown(&nd_tbl, dev);
in6_dev_put(idev);
}
+
return 0;
}
@@ -2237,6 +2254,16 @@
addrconf_mod_timer(ifp, AC_RS, ifp->idev->cnf.rtr_solicit_interval);
spin_unlock_bh(&ifp->lock);
}
+
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ if (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL) {
+ printk(KERN_DEBUG "DAD: skipping HIP event on link local address\n");
+ } else {
+ printk(KERN_DEBUG "DAD: ifa address=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ NIP6(ifp->addr));
+ HIP_CALLFUNC(hip_handle_ipv6_dad_completed, 0)(dev->ifindex);
+ }
+#endif
}
#ifdef CONFIG_PROC_FS
diff -urN linux-2.6.10/net/ipv6/af_inet6.c linux-2.6.10-hip/net/ipv6/af_inet6.c
--- linux-2.6.10/net/ipv6/af_inet6.c 2004-12-24 23:34:58.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/af_inet6.c 2005-02-22 00:06:26.000000000 +0200
@@ -112,7 +112,11 @@
return (struct ipv6_pinfo *)(((u8 *)sk) + offset);
}
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+int inet6_create(struct socket *sock, int protocol)
+#else
static int inet6_create(struct socket *sock, int protocol)
+#endif /* CONFIG_HIP */
{
struct inet_opt *inet;
struct ipv6_pinfo *np;
@@ -519,6 +523,13 @@
.owner = THIS_MODULE,
};
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+EXPORT_SYMBOL(inet6_family_ops);
+EXPORT_SYMBOL(inet6_stream_ops);
+EXPORT_SYMBOL(inet6_dgram_ops);
+EXPORT_SYMBOL(inet6_create);
+#endif /* CONFIG_HIP */
+
#ifdef CONFIG_SYSCTL
extern void ipv6_sysctl_register(void);
extern void ipv6_sysctl_unregister(void);
diff -urN linux-2.6.10/net/ipv6/esp6.c linux-2.6.10-hip/net/ipv6/esp6.c
--- linux-2.6.10/net/ipv6/esp6.c 2004-12-24 23:35:01.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/esp6.c 2005-02-01 17:16:24.000000000 +0200
@@ -37,12 +37,23 @@
#include
#include
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+#include
+#endif
+
static int esp6_output(struct sk_buff *skb)
{
int err;
int hdr_len;
struct dst_entry *dst = skb->dst;
struct xfrm_state *x = dst->xfrm;
+#if 1
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ struct xfrm_state *x_tmp = NULL, *x_orig = x;
+ uint32_t default_spi;
+ int state_ok = 0;
+#endif
+#endif
struct ipv6hdr *top_iph;
struct ipv6_esp_hdr *esph;
struct crypto_tfm *tfm;
@@ -52,10 +63,39 @@
int clen;
int alen;
int nfrags;
+#if 1
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ /* Because the SAs are cached somewhere deep in
+ skb/skb->dst/skb->dst->xfrm and currently I do not know how
+ to relookup them while the socket is open, we have to
+ lookup the current SA every time when sending out ESP. */
+
+ /* I know that this is a huge performance drawback. Currently we
+ forget the cached SA from dst until I know how to fix the
+ problem without kernel panic. */
+ if (ipv6_addr_is_hit(&x->dst_hit)) {
+ default_spi = HIP_CALLFUNC(hip_get_default_spi_out, 0)(&x->dst_hit, &state_ok);
+ if (!default_spi || !state_ok) {
+ printk(KERN_DEBUG "default_spi not found or SPI state not ok\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ x_tmp = xfrm_state_lookup((xfrm_address_t *)&x->dst_hit, htonl(default_spi),
+ IPPROTO_ESP, AF_INET6);
+ if (!x_tmp) {
+ printk(KERN_DEBUG "SPI 0x%x not found\n", default_spi);
+ err = -EINVAL;
+ goto error;
+ }
+ x = x_tmp;
+ }
+#endif
+#endif
esp = x->data;
hdr_len = skb->h.raw - skb->data +
- sizeof(*esph) + esp->conf.ivlen;
+ sizeof(*esph) + esp->conf.ivlen;
/* Strip IP+ESP header. */
__skb_pull(skb, hdr_len);
@@ -92,6 +132,16 @@
*(u8*)(trailer->tail - 1) = *skb->nh.raw;
*skb->nh.raw = IPPROTO_ESP;
+#ifdef CONFIG_ESPBEET
+ if (x->props.mode == XFRM_MODE_BEET) {
+ ipv6_addr_copy(&top_iph->daddr,
+ (struct in6_addr *)&x->outeraddr);
+ /* hopefully the source address is overwritten later...
+ if not, we might have to call get_saddr() here?
+ */
+ }
+#endif
+
esph->spi = x->id.spi;
esph->seq_no = htonl(++x->replay.oseq);
@@ -126,6 +176,13 @@
err = 0;
error:
+#if 1
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ if (x_tmp)
+ xfrm_state_put(x_tmp);
+ dst->xfrm = x_orig;
+#endif
+#endif
return err;
}
diff -urN linux-2.6.10/net/ipv6/hip/builder.c linux-2.6.10-hip/net/ipv6/hip/builder.c
--- linux-2.6.10/net/ipv6/hip/builder.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/builder.c 2005-03-16 15:34:40.000000000 +0200
@@ -0,0 +1,2341 @@
+/*
+ * Building and parsing functions for hipconf and HIP kernel module messages.
+ * These functions work both in the userspace and in the kernel. Keep in mind
+ * the following things when using the builder:
+ * - Never access members of hip_common and hip_tlv_common directly. Use
+ * the accessor functions to hide byte ordering and length manipulation.
+ * - Remember always to use __attribute__ ((packed)) (see hip.h) with builder
+ * because compiler adds padding into the structures.
+ * - This file is shared between userspace and kernel: do not put any memory
+ * allocations or other kernel/userspace specific stuff into here!
+ * - If you build more functions like build_signature2_contents(), remember
+ * to use hip_build_generic_param() in them!
+ * - Macros for doing ntohs() and htons() conversion? Currently they are
+ * used in a platform dependent way.
+ * - Why does build network header return void whereas build daemon does not?
+ * - There is a small TODO list in hip_build_network_hdr()
+ *
+ * Authors:
+ * - Miika Komu
+ * - Mika Kousa
+ *
+ * USAGE EXAMPLES:
+ * - sender of "add mapping", i.e. the hip module in kernel
+ * - struct hip_common *msg = k/malloc(HIP_MAX_PACKET);
+ * - hip_msg_init(msg);
+ * - err = hip_build_user_hdr(msg, SO_HIP_ADD_MAP_HIT_IP, 0);
+ * - err = hip_build_param_contents(msg, &hit,
+ * HIP_PARAM_HIT, sizeof(struct in6_addr));
+ * - err = hip_build_param_contents(msg, &ip,
+ * HIP_PARAM_IPV6_ADDR, sizeof(struct in6_addr));
+ * - send the message to user space
+ * - receiver of "add mapping", i.e. the daemon
+ * - struct hip_common *msg = k/malloc(HIP_MAX_PACKET);
+ * - receive the message from kernel
+ * - if (msg->err) goto_error_handler;
+ * - hit = (struct in6addr *) hip_get_param_contents(msg, HIP_PARAM_HIT);
+ * - note: hit can be null, if the param was not found
+ * - ip = (struct in6addr *) hip_get_param_object(msg, HIP_PARAM_IPV6ADDR);
+ * - note: hit can be null
+ * - note: in network packets, you should use hip_build_network_hdr()
+ * instead of hip_build_user_hdr() !
+ *
+ */
+
+#include
+
+#include "builder.h"
+#include "debug.h"
+
+/**
+ * hip_msg_init - initialize a network/daemon message
+ * @msg: the message to be initialized
+ *
+ * Initialize a message to be sent to the daemon or into the network.
+ * Initialization must be done before any parameters are build into
+ * the message. Otherwise the writing of the parameters will result in bizarre
+ * behaviour.
+ *
+ */
+void hip_msg_init(struct hip_common *msg) {
+ /* note: this is used both for daemon and network messages */
+ memset(msg, 0, HIP_MAX_PACKET);
+}
+
+/**
+ * hip_msg_alloc - allocate and initialize a HIP packet
+ *
+ * Return: initialized HIP packet if successful, %NULL on error.
+ */
+struct hip_common *hip_msg_alloc(void)
+{
+ struct hip_common *ptr;
+
+#ifdef __KERNEL__
+ ptr = (struct hip_common *) kmalloc(HIP_MAX_PACKET, GFP_ATOMIC);
+#else
+ ptr = (struct hip_common *) malloc(HIP_MAX_PACKET);
+#endif /* __KERNEL__ */
+ if (!ptr)
+ return NULL;
+ hip_msg_init(ptr);
+ return ptr;
+}
+
+/**
+ * hip_msg_free - deallocate a HIP packet
+ * @msg: the packet to be deallocated
+ */
+void hip_msg_free(struct hip_common *msg)
+{
+#ifdef __KERNEL__
+ kfree(msg);
+#else
+ free(msg);
+#endif /* __KERNEL__ */
+}
+
+/**
+ * hip_convert_msg_total_len_to_bytes - convert message total length to bytes
+ * @len: the length of the HIP header as it is in the header
+ * (in host byte order)
+ *
+ * Returns: the real size of HIP header in bytes (host byte order)
+ */
+uint16_t hip_convert_msg_total_len_to_bytes(hip_hdr_len_t len) {
+ return (len == 0) ? 0 : ((len + 1) << 3);
+}
+
+/**
+ * hip_get_msg_total_len - get the real, total size of the header in bytes
+ * @msg: pointer to the beginning of the message header
+ *
+ * Returns: the real, total size of the message in bytes (host byte order).
+ */
+uint16_t hip_get_msg_total_len(const struct hip_common *msg) {
+ return hip_convert_msg_total_len_to_bytes(msg->payload_len);
+}
+
+/**
+ * hip_get_msg_contents_len - get message size excluding type and length
+ * @msg: pointer to the beginning of the message header
+ *
+ * Returns: the real, total size of the message in bytes (host byte order)
+ * excluding the the length of the type and length fields
+ */
+uint16_t hip_get_msg_contents_len(const struct hip_common *msg) {
+ HIP_ASSERT(hip_get_msg_total_len(msg) >=
+ sizeof(struct hip_common));
+ return hip_get_msg_total_len(msg) - sizeof(struct hip_common);
+}
+
+/**
+ * hip_set_msg_total_len - set the total message length in bytes
+ * @msg: pointer to the beginning of the message header
+ * @len: the total size of the message in bytes (host byte order)
+ */
+void hip_set_msg_total_len(struct hip_common *msg, uint16_t len) {
+ /* assert len % 8 == 0 ? */
+ msg->payload_len = (len < 8) ? 0 : ((len >> 3) - 1);
+}
+
+/**
+ * hip_get_msg_type - get the type of the message in host byte order
+ * @msg: pointer to the beginning of the message header
+ *
+ * Returns: the type of the message (in host byte order)
+ *
+ */
+hip_hdr_type_t hip_get_msg_type(const struct hip_common *msg) {
+ return msg->type_hdr;
+}
+
+/**
+ * hip_set_msg_type - set the type of the message
+ * @msg: pointer to the beginning of the message header
+ * @type: the type of the message (in host byte order)
+ *
+ */
+void hip_set_msg_type(struct hip_common *msg, hip_hdr_type_t type) {
+ msg->type_hdr = type;
+}
+
+/**
+ * hip_get_msg_err - get the error values from daemon message header
+ * @msg: pointer to the beginning of the message header
+ *
+ * Returns: the error value from the message (in host byte order)
+ *
+ */
+hip_hdr_err_t hip_get_msg_err(const struct hip_common *msg) {
+ /* Note: error value is stored in checksum field for daemon messages.
+ This should be fixed later on by defining an own header for
+ daemon messages. This function should then input void* as
+ the message argument and cast it to the daemon message header
+ structure. */
+ return msg->checksum; /* 1 byte, no ntohs() */
+}
+
+/**
+ * hip_set_msg_err - set the error value of the daemon message
+ * @msg: pointer to the beginning of the message header
+ * @err: the error value
+ */
+void hip_set_msg_err(struct hip_common *msg, hip_hdr_err_t err) {
+ /* note: error value is stored in checksum field for daemon messages */
+ msg->checksum = err;
+}
+
+/**
+ * hip_zero_msg_checksum - zero message checksum
+ */
+void hip_zero_msg_checksum(struct hip_common *msg) {
+ msg->checksum = 0;
+}
+
+
+/**
+ * hip_get_param_total_len - get total size of message parameter
+ * @tlv_common: pointer to the parameter
+ *
+ * Returns: the total length of the parameter in bytes (host byte
+ * order), including the padding.
+ */
+hip_tlv_len_t hip_get_param_total_len(const void *tlv_common) {
+ return HIP_LEN_PAD(sizeof(struct hip_tlv_common) +
+ ntohs(((const struct hip_tlv_common *)
+ tlv_common)->length));
+}
+
+/**
+ * hip_get_param_contents_len - get the size of the parameter contents
+ * @tlv_common: pointer to the parameter
+ *
+ * Returns: the length of the parameter in bytes (in host byte order),
+ * excluding padding and the length of "type" and "length" fields
+ */
+hip_tlv_len_t hip_get_param_contents_len(const void *tlv_common) {
+ return ntohs(((const struct hip_tlv_common *)tlv_common)->length);
+}
+
+/**
+ * hip_set_param_contents_len - set parameter length
+ * @tlv_common: pointer to the parameter
+ * @len: the length of the parameter in bytes (in host byte order),
+ * excluding padding and the length of "type" and "length" fields
+ */
+void hip_set_param_contents_len(void *tlv_common,
+ hip_tlv_len_t len) {
+ ((struct hip_tlv_common *)tlv_common)->length = htons(len);
+}
+
+/**
+ * hip_get_param_type - get type of parameter
+ * @tlv_common: pointer to the parameter
+ *
+ * Returns: The type of the parameter (in host byte order).
+ */
+hip_tlv_type_t hip_get_param_type(const void *tlv_common) {
+ return ntohs(((const struct hip_tlv_common *)tlv_common)->type);
+}
+
+/**
+ * hip_set_param_type - set parameter type
+ * @tlv_common: pointer to the parameter
+ * @type: type of the parameter (in host byte order)
+ */
+void hip_set_param_type(void *tlv_common, hip_tlv_type_t type) {
+ ((struct hip_tlv_common *)tlv_common)->type = htons(type);
+}
+
+/**
+ * hip_get_diffie_hellman_param_public_value_contents - get dh public value contents
+ * @tlv_common: pointer to the dh parameter
+ *
+ * Returns: pointer to the public value of Diffie-Hellman parameter
+ */
+void *hip_get_diffie_hellman_param_public_value_contents(const void *tlv_common) {
+ return (void *) tlv_common + sizeof(struct hip_diffie_hellman);
+}
+
+/**
+ * hip_get_diffie_hellman_param_public_value_len - get dh public value real length
+ * @dh: pointer to the Diffie-Hellman parameter
+ *
+ * Returns: the length of the public value Diffie-Hellman parameter in bytes
+ * (in host byte order).
+ */
+hip_tlv_len_t hip_get_diffie_hellman_param_public_value_len(const struct hip_diffie_hellman *dh)
+{
+ return hip_get_param_contents_len(dh) - sizeof(uint8_t);
+}
+
+/**
+ * hip_set_param_spi_value - set the spi value in spi_lsi parameter
+ * @spi_lsi: the spi_lsi parameter
+ * @spi: the value of the spi in the spi_lsi value in host byte order
+ *
+ */
+void hip_set_param_spi_value(struct hip_spi *hspi, uint32_t spi)
+{
+ hspi->spi = htonl(spi);
+}
+
+/**
+ * hip_get_param_spi_value - get the spi value from spi_lsi parameter
+ * @spi_lsi: the spi_lsi parameter
+ *
+ * Returns: the spi value in host byte order
+ */
+uint32_t hip_get_param_spi_value(const struct hip_spi *hspi)
+{
+ return ntohl(hspi->spi);
+}
+
+/**
+ * hip_get_unit_test_suite_param_id - get suite id from unit test parameter
+ * @test: pointer to the unit test parameter
+ *
+ * Returns: the id of the test suite (in host byte order) of the unit test
+ * parameter
+ */
+uint16_t hip_get_unit_test_suite_param_id(const struct hip_unit_test *test)
+{
+ return ntohs(test->suiteid);
+}
+
+/**
+ * hip_get_unit_test_case_param_id - get test case id from unit test parameter
+ * @test: pointer to the unit test parameter
+ *
+ * Returns: the id of the test case (in host byte order) of the unit test
+ * parameter
+ */
+uint16_t hip_get_unit_test_case_param_id(const struct hip_unit_test *test)
+{
+ return ntohs(test->caseid);
+}
+
+uint8_t hip_get_host_id_algo(const struct hip_host_id *host_id) {
+ return host_id->rdata.algorithm; /* 8 bits, no ntons() */
+}
+
+/**
+ * hip_check_msg_len - check validity of message length
+ * @msg: pointer to the message
+ *
+ * Returns: 1 if the message length is valid, or 0 if the message length is
+ * invalid
+ */
+int hip_check_msg_len(const struct hip_common *msg) {
+ uint16_t len;
+
+ HIP_ASSERT(msg);
+ len = hip_get_msg_total_len(msg);
+
+ if (len < sizeof(struct hip_common) || len > HIP_MAX_PACKET) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/**
+ * hip_check_network_msg_type - check the type of the network message
+ * @msg: pointer to the message
+ *
+ * Returns: 1 if the message type is valid, or 0 if the message type is
+ * invalid
+ */
+int hip_check_network_msg_type(const struct hip_common *msg) {
+ int ok = 0;
+ hip_hdr_type_t supported[] =
+ {
+ HIP_I1,
+ HIP_R1,
+ HIP_I2,
+ HIP_R2,
+ HIP_UPDATE,
+ HIP_NOTIFY,
+ HIP_BOS
+ };
+ hip_hdr_type_t i;
+ hip_hdr_type_t type = hip_get_msg_type(msg);
+
+ for (i = 0; i < sizeof(supported) / sizeof(hip_hdr_type_t); i++) {
+ if (type == supported[i]) {
+ ok = 1;
+ break;
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * hip_check_userspace_param_type - check the userspace parameter type
+ * @param: pointer to the parameter
+ *
+ * Returns: 1 if parameter type is valid, or 0 if parameter type is invalid
+ */
+int hip_check_userspace_param_type(const struct hip_tlv_common *param)
+{
+ return 1;
+}
+
+/**
+ * hip_check_network_param_type - check network parameter type
+ * @param: the network parameter
+ *
+ * Returns: 1 if parameter type is valid, or 0 if parameter type
+ * is not valid. "Valid" means all optional and non-optional parameters
+ * in the HIP draft.
+ *
+ * Optional parameters are not checked, because the code just does not
+ * use them if they are not supported.
+ */
+int hip_check_network_param_type(const struct hip_tlv_common *param)
+{
+ int ok = 0;
+ hip_tlv_type_t i;
+ hip_tlv_type_t valid[] =
+ {
+ HIP_PARAM_SPI,
+ HIP_PARAM_R1_COUNTER,
+ HIP_PARAM_REA,
+ HIP_PARAM_PUZZLE,
+ HIP_PARAM_SOLUTION,
+ HIP_PARAM_NES,
+ HIP_PARAM_SEQ,
+ HIP_PARAM_ACK,
+ HIP_PARAM_DIFFIE_HELLMAN,
+ HIP_PARAM_HIP_TRANSFORM,
+ HIP_PARAM_ESP_TRANSFORM,
+ HIP_PARAM_ENCRYPTED,
+ HIP_PARAM_HOST_ID,
+ HIP_PARAM_CERT,
+ HIP_PARAM_RVA_REQUEST,
+ HIP_PARAM_RVA_REPLY,
+ HIP_PARAM_NOTIFY,
+ HIP_PARAM_ECHO_REQUEST_SIGN,
+ HIP_PARAM_ECHO_RESPONSE_SIGN,
+ HIP_PARAM_FROM_SIGN,
+ HIP_PARAM_TO_SIGN,
+ HIP_PARAM_HMAC,
+ HIP_PARAM_HMAC2,
+ HIP_PARAM_HIP_SIGNATURE2,
+ HIP_PARAM_HIP_SIGNATURE,
+ HIP_PARAM_ECHO_REQUEST,
+ HIP_PARAM_ECHO_RESPONSE,
+ HIP_PARAM_FROM,
+ HIP_PARAM_TO,
+ HIP_PARAM_HMAC,
+ HIP_PARAM_VIA_RVS
+ };
+ hip_tlv_type_t type = hip_get_param_type(param);
+
+ /* XX TODO: check the lengths of the parameters */
+
+ for (i = 0; i < ARRAY_SIZE(valid); i++) {
+ if (type == valid[i]) {
+ ok = 1;
+ break;
+ }
+ }
+
+ return ok;
+}
+
+/**
+ * hip_check_param_contents_len - check validity of parameter contents length
+ * @msg: pointer to the beginning of the message
+ * @param: pointer to the parameter to be checked for contents length
+ *
+ * The @msg is passed also in to check to the parameter will not cause buffer
+ * overflows.
+ *
+ * Returns: 1 if the length of the parameter contents length was valid
+ * (the length was not too small or too large to fit into the
+ * message). Zero is returned on invalid contents length.
+ */
+int hip_check_param_contents_len(const struct hip_common *msg,
+ const struct hip_tlv_common *param) {
+ int ok = 0;
+ int param_len = hip_get_param_total_len(param);
+ void *pos = (void *) param;
+
+ /* Note: the lower limit is not checked, because there really is no
+ lower limit. */
+
+ if (pos == ((void *)msg)) {
+ HIP_ERROR("use hip_check_msg_len()\n");
+ } else if (pos + param_len > ((void *) msg) + HIP_MAX_PACKET) {
+ _HIP_DEBUG("param far too long (%d)\n", param_len);
+ } else if (param_len > hip_get_msg_total_len(msg)) {
+ _HIP_DEBUG("param too long (%d)\n", param_len);
+ } else {
+ _HIP_DEBUG("param ok\n");
+ ok = 1;
+ }
+ return ok;
+}
+
+/**
+ * hip_get_next_param - iterate to the next parameter
+ * @msg: pointer to the beginning of the message header
+ * @current_param: pointer to the current parameter, or NULL if the @msg
+ * is to be searched from the beginning
+ *
+ * Returns: the next parameter after the @current_param in @msg, or NULL
+ * if no parameters were found
+ */
+struct hip_tlv_common *hip_get_next_param(const struct hip_common *msg,
+ const struct hip_tlv_common *current_param)
+{
+ struct hip_tlv_common *next_param = NULL;
+ void *pos = (void *) current_param;
+
+ _HIP_DEBUG("\n");
+
+ if (!msg) {
+ HIP_ERROR("msg null\n");
+ goto out;
+ }
+
+ if (current_param == NULL) {
+ pos = (void *) msg;
+ }
+
+ if (pos == msg)
+ pos += sizeof(struct hip_common);
+ else
+ pos += hip_get_param_total_len(current_param);
+
+ next_param = (struct hip_tlv_common *) pos;
+
+ /* check that the next parameter does not point
+ a) outside of the message
+ b) out of the buffer with check_param_contents_len()
+ c) to an empty slot in the message */
+ if (((char *) next_param) - ((char *) msg) >=
+ hip_get_msg_total_len(msg) || /* a */
+ !hip_check_param_contents_len(msg, next_param) || /* b */
+ hip_get_param_contents_len(next_param) == 0) { /* c */
+ _HIP_DEBUG("no more parameters found\n");
+ next_param = NULL;
+ } else {
+ /* next parameter successfully found */
+ _HIP_DEBUG("next param: type=%d, len=%d\n",
+ hip_get_param_type(next_param),
+ hip_get_param_contents_len(next_param));
+ }
+
+ out:
+ return next_param;
+
+
+}
+
+/**
+ * hip_get_param - get the first parameter of the given type
+ * @msg: pointer to the beginning of the message header
+ * @param_type: the type of the parameter to be searched from @msg
+ * (in host byte order)
+ *
+ * If there are multiple parameters of the same type, one should use
+ * hip_get_next_param after calling this function to iterate through
+ * them all.
+ *
+ * Returns: a pointer to the first parameter of the type @param_type, or
+ * NULL if no parameters of the type @param_type were not found.
+ */
+void *hip_get_param(const struct hip_common *msg,
+ hip_tlv_type_t param_type)
+{
+ void *matched = NULL;
+ struct hip_tlv_common *current_param = NULL;
+
+ _HIP_DEBUG("searching for type %d\n", param_type);
+
+/* XXX: Optimize: stop when next parameter's type is greater than the searched one */
+
+ while((current_param = hip_get_next_param(msg, current_param))
+ != NULL) {
+ _HIP_DEBUG("current param %d\n",
+ hip_get_param_type(current_param));
+ if (hip_get_param_type(current_param) == param_type) {
+ matched = current_param;
+ break;
+ }
+ }
+
+ return matched;
+}
+
+/**
+ * hip_get_param_contents - get the first parameter contents of the given type
+ * @msg: pointer to the beginning of the message header
+ * @param_type: the type of the parameter to be searched from @msg
+ * (in host byte order)
+ *
+ * If there are multiple parameters of the same type, one should use
+ * hip_get_next_param after calling this function to iterate through
+ * them all.
+ *
+ * Returns: a pointer to the contents of the first parameter of the type
+ * @param_type, or NULL if no parameters of the type @param_type
+ * were not found.
+ */
+void *hip_get_param_contents(const struct hip_common *msg,
+ hip_tlv_type_t param_type)
+{
+ void *contents = hip_get_param(msg,param_type);
+ if (contents)
+ contents += sizeof(struct hip_tlv_common);
+ return contents;
+}
+
+/**
+ * hip_get_param_contents_direct - get parameter contents direct from TLV
+ * @tlv_common: pointer to a parameter
+ *
+ * Returns: pointer to the contents of the @tlv_common (just after the
+ * the type and length fields)
+ */
+void *hip_get_param_contents_direct(const void *tlv_common) {
+ return ((void *)tlv_common) + sizeof(struct hip_tlv_common);
+}
+
+
+/* hip_get_nth_param - get nth parameter of given type from the message
+ * @msg: pointer to the beginning of the message header
+ * @param_type: the type of the parameter to be searched from @msg
+ * (in host byte order)
+ * @n: index number to be get
+ *
+ * Returns: the nth parameter from the message if found, else %NULL.
+ */
+void *hip_get_nth_param(const struct hip_common *msg,
+ hip_tlv_type_t param_type, int n)
+{
+ struct hip_tlv_common *param = NULL;
+ int i = 0;
+
+ if (n < 1) {
+ HIP_ERROR("n < 1 (n=%d)\n", n);
+ return NULL;
+ }
+
+ while((param = hip_get_next_param(msg, param))) {
+ if (hip_get_param_type(param) == param_type) {
+ i++;
+ if (i == n)
+ return param;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * hip_find_free_param - find the first free position in message
+ * @msg: pointer to the beginning of the message header
+ *
+ * This function does not check whether the new parameter to be appended
+ * would overflow the msg buffer. It is the responsibilty of the caller
+ * to check such circumstances because this function does not know
+ * the length of the object to be appended in the message. Still, this
+ * function checks the special situation where the buffer is completely
+ * full and returns a null value in such a case.
+ *
+ * Returns: pointer to the first free (padded) position, or NULL if
+ * the message was completely full
+ */
+void *hip_find_free_param(const struct hip_common *msg)
+{
+ /* XX TODO: this function should return hip_tlv_common ? */
+ struct hip_tlv_common *current_param = NULL;
+ struct hip_tlv_common *last_used_pos = NULL;
+ void *free_pos = NULL;
+ void *first_pos = ((void *) msg) + sizeof(struct hip_common);
+
+ /* Check for no parameters: this has to be checked separately because
+ we cannot tell from the return value of get_next_param() whether
+ the message was completely full or there just were no parameters.
+ The length is used for checking the existance of parameter, because
+ type field may be zero (SPI_LSI = 0) and therefore it cannot be
+ used for checking the existance. */
+ if (hip_get_param_contents_len((struct hip_tlv_common *) first_pos)
+ == 0) {
+ _HIP_DEBUG("no parameters\n");
+ free_pos = first_pos;
+ goto out;
+ }
+
+ while((current_param = hip_get_next_param(msg, current_param))
+ != NULL) {
+ last_used_pos = current_param;
+ _HIP_DEBUG("not free: type=%d, contents_len=%d\n",
+ hip_get_param_type(current_param),
+ hip_get_param_contents_len(current_param));
+ }
+
+ if (last_used_pos == NULL) {
+ free_pos = NULL; /* the message was full */
+ } else {
+ free_pos = ((void *) last_used_pos) +
+ hip_get_param_total_len(last_used_pos);
+ }
+
+ out:
+ return free_pos;
+}
+
+
+/**
+ * hip_calc_hdr_len - update messsage header length
+ * @msg: pointer to the beginning of the message header
+ *
+ * This function is called always when a parameter has added or the
+ * daemon/network header was written. This functions writes the new
+ * header length directly into the message.
+ */
+void hip_calc_hdr_len(struct hip_common *msg)
+{
+ struct hip_tlv_common *param = NULL;
+ void *pos = (void *) msg;
+
+ _HIP_DEBUG("\n");
+
+ /* We cannot call get_next() or get_free() because they need a valid
+ header length which is to be (possibly) calculated now. So, the
+ header length must be calculated manually here. */
+
+ if (hip_get_msg_total_len(msg) == 0) {
+ /* msg len is zero when
+ 1) calling build_param() for the first time
+ 2) calling just the build_hdr() without building
+ any parameters, e.g. in plain error messages */
+ _HIP_DEBUG("case 1,2\n");
+ hip_set_msg_total_len(msg, sizeof(struct hip_common));
+ } else {
+ /* 3) do nothing, build_param()+ */
+ /* 4) do nothing, build_param()+ and build_hdr() */
+ _HIP_DEBUG("case 3,4\n");
+ }
+
+ pos += hip_get_msg_total_len(msg);
+ param = (struct hip_tlv_common *) pos;
+ if (hip_get_param_contents_len(param) != 0) {
+ /* Case 1 and 3: a new parameter (with a valid length) has
+ been added and the message length has not been updated. */
+ _HIP_DEBUG("case 1,3\n");
+ hip_set_msg_total_len(msg, hip_get_msg_total_len(msg) +
+ hip_get_param_total_len(param));
+ /* XX assert: new pos must be of type 0 (assume only one
+ header has been added) */
+ } else {
+ /* case 2 and 4: the message length does not need to be
+ updated */
+ _HIP_DEBUG("case 2,4\n");
+ }
+
+ _HIP_DEBUG("msg len %d\n", hip_get_msg_total_len(msg));
+}
+
+/**
+ * hip_calc_generic_param_len - calculate and write the length of any parameter
+ * @tlv_common: pointer to the beginning of the parameter
+ * @tlv_size: size of the TLV header (in host byte order)
+ * @contents_size: size of the contents after the TLV header
+ * (in host byte order)
+ *
+ * This function can be used for semi-automatic calculation of parameter
+ * length field. This function should always be used instead of manual
+ * calculation of parameter lengths. The @tlv_size is usually just
+ * sizeof(struct hip_tlv_common), but it can include other fields than
+ * just the type and length. For example, DIFFIE_HELLMAN parameter includes
+ * the group field as in hip_build_param_diffie_hellman_contents().
+ */
+void hip_calc_generic_param_len(void *tlv_common,
+ hip_tlv_len_t tlv_size,
+ hip_tlv_len_t contents_size)
+{
+ hip_set_param_contents_len(tlv_common,
+ tlv_size + contents_size -
+ sizeof(struct hip_tlv_common));
+}
+
+/**
+ * hip_calc_param_len - calculate the length of a "normal" TLV structure
+ * @tlv_common: pointer to the beginning of the TLV structure
+ * @contents_size: size of the contents after type and length fields
+ * (in host byte order)
+ *
+ * This function calculates and writes the length of TLV structure field.
+ * This function is different from hip_calc_generic_param_len() because
+ * it assumes that the length of the header of the TLV is just
+ * sizeof(struct hip_tlv_common).
+ */
+void hip_calc_param_len(void *tlv_common, hip_tlv_len_t contents_size)
+{
+ hip_calc_generic_param_len(tlv_common, sizeof(struct hip_tlv_common),
+ contents_size);
+}
+
+/**
+ * hip_dump_msg - dump the message contents using HIP debug interface
+ * @msg: the message to be dumped using the HIP debug interface
+ *
+ * Do not call this function directly, use the HIP_DUMP_MSG macro instead.
+ */
+void hip_dump_msg(const struct hip_common *msg)
+{
+ struct hip_tlv_common *current_param = NULL;
+ void *contents = NULL;
+
+ HIP_DEBUG("msg: type=%d, len=%d, err=%d\n",
+ hip_get_msg_type(msg), hip_get_msg_total_len(msg),
+ hip_get_msg_err(msg));
+
+ while((current_param = hip_get_next_param(msg, current_param))
+ != NULL) {
+ HIP_DEBUG("param: type=%d, len=%d\n",
+ hip_get_param_type(current_param),
+ hip_get_param_contents_len(current_param));
+ contents = hip_get_param_contents_direct(current_param);
+ HIP_HEXDUMP("contents", contents,
+ hip_get_param_contents_len(current_param));
+ }
+
+}
+
+/**
+ * hip_check_userspace msg - check userspace message for integrity
+ * @msg: the message to be verified for integrity
+ *
+ * Returns: zero if the message was ok, or negative error value on error.
+ */
+int hip_check_userspace_msg(const struct hip_common *msg) {
+ struct hip_tlv_common *current_param = NULL;
+ int err = 0;
+
+ if (!hip_check_msg_len(msg)) {
+ err = -EMSGSIZE;
+ HIP_ERROR("bad msg len %d\n", hip_get_msg_total_len(msg));
+ goto out;
+ }
+
+ while((current_param = hip_get_next_param(msg, current_param))
+ != NULL) {
+ if(!hip_check_param_contents_len(msg, current_param)) {
+ err = -EMSGSIZE;
+ HIP_ERROR("bad param len\n");
+ break;
+ } else if (!hip_check_userspace_param_type(current_param)) {
+ err = -EINVAL;
+ HIP_ERROR("bad param type\n");
+ break;
+ }
+ }
+
+ out:
+ return err;
+}
+
+/**
+ * hip_check_network_param_attributes - check parameter attributes
+ * @param: the parameter to checked
+ *
+ * This is the function where one can test special attributes such as algo,
+ * groupid, suiteid, etc of a HIP parameter. If the parameter does not require
+ * other than just the validation of length and type fields, one should not
+ * add any checks for that parameter here.
+ *
+ * Returns: zero if the message was ok, or negative error value on error.
+ *
+ * XX TODO: this function may be unneccessary because the input handlers
+ * already do some checking. Currently they are double checked..
+ */
+int hip_check_network_param_attributes(const struct hip_tlv_common *param)
+{
+ hip_tlv_type_t type = hip_get_param_type(param);
+ int err = 0;
+
+ _HIP_DEBUG("type=%u\n", type);
+
+ switch(type) {
+ case HIP_PARAM_HIP_TRANSFORM:
+ case HIP_PARAM_ESP_TRANSFORM:
+ {
+ /* Search for one supported transform */
+ hip_transform_suite_t suite;
+
+ _HIP_DEBUG("Checking %s transform\n",
+ type == HIP_PARAM_HIP_TRANSFORM ? "HIP" : "ESP");
+ suite = hip_get_param_transform_suite_id(param, 0);
+ if (suite == 0) {
+ HIP_ERROR("Could not find suitable %s transform\n",
+ type == HIP_PARAM_HIP_TRANSFORM ? "HIP" : "ESP");
+ err = -EPROTONOSUPPORT;
+ }
+ break;
+ }
+ case HIP_PARAM_HOST_ID:
+ {
+ uint8_t algo =
+ hip_get_host_id_algo((struct hip_host_id *) param);
+ if (algo != HIP_HI_DSA && algo != HIP_HI_RSA) {
+ err = -EPROTONOSUPPORT;
+ HIP_ERROR("Host id algo %d not supported\n", algo);
+ }
+ break;
+ }
+ }
+ _HIP_DEBUG("err=%d\n", err);
+ return err;
+}
+
+/**
+ * hip_check_network_msg - check network message for integrity
+ * @msg: the message to be verified for integrity
+ *
+ * Returns: zero if the message was ok, or negative error value on error.
+ */
+int hip_check_network_msg(const struct hip_common *msg)
+{
+ struct hip_tlv_common *current_param = NULL;
+ hip_tlv_type_t current_param_type = 0, prev_param_type = 0;
+ int err = 0;
+
+ /* Checksum of the message header is verified in input.c */
+
+ if (!hip_check_network_msg_type(msg)) {
+ err = -EINVAL;
+ HIP_ERROR("bad msg type (%d)\n", hip_get_msg_type(msg));
+ goto out;
+ }
+
+ if (!hip_check_msg_len(msg)) {
+ err = -EMSGSIZE;
+ HIP_ERROR("bad msg len %d\n", hip_get_msg_total_len(msg));
+ goto out;
+ }
+
+ /* Checking of param types, lengths and ordering. */
+ while((current_param = hip_get_next_param(msg, current_param))
+ != NULL) {
+ current_param_type = hip_get_param_type(current_param);
+ if(!hip_check_param_contents_len(msg, current_param)) {
+ err = -EMSGSIZE;
+ HIP_ERROR("bad param len\n");
+ break;
+ } else if (!hip_check_network_param_type(current_param)) {
+ err = -EINVAL;
+ HIP_ERROR("bad param type, current param=%u\n",
+ hip_get_param_type(current_param));
+ break;
+ } else if (current_param_type < prev_param_type) {
+ err = -ENOMSG;
+ HIP_ERROR("Wrong order of parameters (%d, %d)\n",
+ prev_param_type, current_param_type);
+ break;
+ } else if (hip_check_network_param_attributes(current_param)) {
+ HIP_ERROR("bad param attributes\n");
+ err = -EINVAL;
+ break;
+ }
+ prev_param_type = current_param_type;
+ }
+
+ out:
+ return err;
+}
+
+/**
+ * hip_build_generic_param - build and insert a parameter into the message
+ * @msg: the message where the parameter is to be appended
+ * @parameter_hdr: pointer to the header of the parameter
+ * @param_hdr_size: size of @parameter_hdr structure (in host byte order)
+ * @contents: the contents of the parameter; the data to be inserted
+ * after the @parameter_hdr (in host byte order)
+ *
+ * This to function should be used by other parameter builder functions
+ * to append parameters into a message. Do not do that manually or you will
+ * break the builder system.
+ *
+ * This function updates the message header length to keep the next free
+ * parameter slot quickly accessible for faster writing of the parameters.
+ *
+ * Returns: zero on success, or negative on error
+ */
+int hip_build_generic_param(struct hip_common *msg,
+ const void *parameter_hdr,
+ hip_tlv_len_t param_hdr_size,
+ const void *contents)
+{
+ const struct hip_tlv_common *param =
+ (struct hip_tlv_common *) parameter_hdr;
+ void *src = NULL;
+ void *dst = NULL;
+ int err = 0;
+ int size = 0;
+ void *max_dst = ((void *) msg) + HIP_MAX_PACKET;
+
+ _HIP_DEBUG("\n");
+
+ if (!msg) {
+ HIP_ERROR("message is null\n");
+ err = -EFAULT;
+ goto out;
+ }
+
+ if (!contents) {
+ HIP_ERROR("object is null\n");
+ err = -EFAULT;
+ goto out;
+ }
+
+ if (param_hdr_size < sizeof(struct hip_tlv_common)) {
+ HIP_ERROR("parameter size too small\n");
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ dst = hip_find_free_param(msg);
+ if (!dst) {
+ err = -EMSGSIZE;
+ HIP_ERROR("msg full\n");
+ goto out;
+ }
+
+ _HIP_DEBUG("found free: %d\n", dst - ((void *)msg));
+
+ if (dst + hip_get_param_total_len(param) > max_dst) {
+ err = -EMSGSIZE;
+ HIP_ERROR("hipd build param: contents size (%d) too long\n",
+ hip_get_param_contents_len(param));
+ goto out;
+ }
+
+ /* copy header */
+ src = (void *) param;
+ size = param_hdr_size;
+ memcpy(dst, src, size);
+
+ /* copy contents */
+ dst += param_hdr_size;
+ src = (void *) contents;
+ /* Copy the right amount of contents, see jokela draft for TLV
+ format. For example, this skips the algo in struct hip_sig2
+ (which is included in the length), see the
+ build_param_signature2_contents() function below. */
+ size = hip_get_param_contents_len(param) -
+ (param_hdr_size - sizeof(struct hip_tlv_common));
+ memcpy(dst, src, size);
+
+ _HIP_DEBUG("contents copied %d bytes\n", size);
+
+ /* we have to update header length or otherwise hip_find_free_param
+ will fail when it checks the header length */
+ hip_calc_hdr_len(msg);
+ if (hip_get_msg_total_len(msg) == 0) {
+ HIP_ERROR("could not calculate temporary header length\n");
+ err = -EFAULT;
+ }
+
+ _HIP_DEBUG("dumping msg, len = %d\n", hip_get_msg_total_len(msg));
+ _HIP_HEXDUMP("build msg: ", (void *) msg,
+ hip_get_msg_total_len(msg));
+ out:
+
+ return err;
+}
+
+/**
+ * hip_build_param_contents - build and append parameter contents into message
+ * @msg: the message where the parameter will be appended
+ * @contents: the data after the type and length fields
+ * @param_type: the type of the parameter (in host byte order)
+ * @contents_size: the size of @contents (in host byte order)
+ *
+ * This function differs from hip_build_generic_param only because it
+ * assumes that the parameter header is just sizeof(struct hip_tlv_common).
+ *
+ * This function updates the message header length to keep the next free
+ * parameter slot quickly accessible for faster writing of the parameters.
+ *
+ * Returns: zero on success, or negative on error
+ */
+int hip_build_param_contents(struct hip_common *msg,
+ const void *contents,
+ hip_tlv_type_t param_type,
+ hip_tlv_len_t contents_size)
+{
+ struct hip_tlv_common param;
+
+ hip_set_param_type(¶m, param_type);
+ hip_set_param_contents_len(¶m, contents_size);
+
+ return hip_build_generic_param(msg, ¶m,
+ sizeof(struct hip_tlv_common),
+ contents);
+}
+
+/**
+ * hip_build_param - append a complete parameter into message
+ * @msg: the message where the parameter will be appended
+ * @tlv_common: pointer to the network byte ordered parameter that will be
+ * appended into the message
+ *
+ * This function differs from hip_build_param_contents() and
+ * hip_build_generic_param() because it takes a complete network byte ordered
+ * parameter as its input. It means that this function can be used for
+ * e.g. copying a parameter from a message to another.
+ *
+ * This function updates the message header length to keep the next free
+ * parameter slot quickly accessible for faster writing of the parameters.
+ *
+ * Returns: zero on success, or negative on error
+ */
+int hip_build_param(struct hip_common *msg, const void *tlv_common)
+{
+ int err = 0;
+ void *contents = ((void *) tlv_common) + sizeof(struct hip_tlv_common);
+
+ if (tlv_common == NULL) {
+ err = -EFAULT;
+ HIP_ERROR("param null\n");
+ goto out;
+ }
+
+ err = hip_build_param_contents(msg, contents,
+ hip_get_param_type(tlv_common),
+ hip_get_param_contents_len(tlv_common));
+ if (err) {
+ HIP_ERROR("could not build contents (%d)\n", err);
+ }
+
+ out:
+ return err;
+}
+
+/**
+ * hip_build_user_hdr - build header for userspace-kernel communication
+ * @msg: the message where the userspace header is to be written
+ * @base_type: the type of the message
+ * @err_val: a positive error value to be communicated for the receiver
+ * (usually just zero for no errors)
+ *
+ * This function builds the header that can be used for HIP kernel-userspace
+ * communication. It is commonly used by the daemon, hipconf, resolver or
+ * the kernel module itself. This function can be called before or after
+ * building the parameters for the message.
+ *
+ * This function does not write the header length into the message. It should
+ * be written by the build_param_functions.
+ *
+ * Returns: zero on success, or negative on error
+ */
+int hip_build_user_hdr(struct hip_common *msg,
+ hip_hdr_type_t base_type,
+ hip_hdr_err_t err_val)
+{
+ int err = 0;
+
+ _HIP_DEBUG("\n");
+
+ hip_set_msg_type(msg, base_type);
+ hip_set_msg_err(msg, err_val);
+ /* Note: final header length is usually calculated by the
+ last call to build_param() but it is possible to build a
+ msg with just the header, so we have to calculate the
+ header length anyway. */
+ hip_calc_hdr_len(msg);
+ if (hip_get_msg_total_len(msg) == 0) {
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ /* some error checking on types and for null values */
+
+ if (!msg) {
+ err = -EINVAL;
+ HIP_ERROR("msg null\n");
+ goto out;
+ }
+
+ if (hip_get_msg_total_len(msg) == 0) {
+ HIP_ERROR("hipd build hdr: could not calc size\n");
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ if (!hip_check_msg_len(msg)) {
+ HIP_ERROR("hipd build hdr: msg len (%d) invalid\n",
+ hip_get_msg_total_len(msg));
+ err = -EMSGSIZE;
+ goto out;
+ }
+
+ out:
+ return err;
+}
+
+#ifdef __KERNEL__
+/**
+ * hip_build_network_hdr - write network header into message
+ * @msg: the message where the HIP network should be written
+ * @type_hdr: the type of the HIP header as specified in the drafts
+ * @control: HIP control bits in host byte order
+ * @hit_sender: source HIT in network byte order
+ * @hit_receiver: destination HIT in network byte order
+ *
+ * This function does not write the header length into the message. It should
+ * be written by the build_param_functions. The checksum field is not written
+ * either because it is done in hip_csum_send().
+ */
+void hip_build_network_hdr(struct hip_common *msg, uint8_t type_hdr,
+ uint16_t control, const struct in6_addr *hit_sender,
+ const struct in6_addr *hit_receiver)
+{
+ /*
+ * XX TODO: build HIP network header in the same fashion as in
+ * build_daemon_hdr().
+ * - Write missing headers in the header using accessor functions
+ * (see hip_get/set_XXX() functions in the beginning of this file).
+ * You have to create couple of new ones, but daemon and network
+ * messages use the same locations for storing len and type
+ * (hip_common->err is stored in the hip_common->checksum) and
+ * they can be used as they are.
+ * - payload_proto
+ * - payload_len: see how build_daemon_hdr() works
+ * - ver_res
+ * - checksum (move the checksum function from hip.c to this file
+ * because this file is shared by kernel and userspace)
+ * - write the parameters of this function into the message
+ * - couple of notes:
+ * - use _only_ accessors to hide byte order and size conversion
+ * issues!!!
+ */
+
+ msg->payload_proto = IPPROTO_NONE; /* 1 byte, no htons() */
+ /* Do not touch the length; it is written by param builders */
+ msg->type_hdr = type_hdr; /* 1 byte, no htons() */
+ msg->ver_res = HIP_VER_RES; /* 1 byte, no htons() */
+
+ msg->control = htons(control);
+ msg->checksum = htons(0); /* this will be written by xmit */
+
+ ipv6_addr_copy(&msg->hits, hit_sender ? hit_sender : &in6addr_any);
+ ipv6_addr_copy(&msg->hitr, hit_receiver ? hit_receiver : &in6addr_any);
+}
+
+/**
+ * hip_build_param_hmac_contents - build and append a HIP hmac parameter
+ * @msg: the message where the hmac parameter will be appended
+ * @key: pointer to a key used for HMAC
+ *
+ * This function calculates the also the HMAC value from the whole message
+ * as specified in the drafts.
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_hmac_contents(struct hip_common *msg,
+ struct hip_crypto_key *key)
+{
+ int err = 0;
+ struct hip_hmac hmac;
+
+ hip_set_param_type(&hmac, HIP_PARAM_HMAC);
+ hip_calc_generic_param_len(&hmac, sizeof(struct hip_hmac), 0);
+
+ if (!hip_write_hmac(HIP_DIGEST_SHA1_HMAC, key->key, msg,
+ hip_get_msg_total_len(msg),
+ hmac.hmac_data)) {
+ HIP_ERROR("Error while building HMAC\n");
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ err = hip_build_param(msg, &hmac);
+ out_err:
+ return err;
+}
+
+/**
+ * hip_build_param_encrypted_aes_sha1 - build the hip_encrypted parameter
+ * @msg: the message where the parameter will be appended
+ * @host_id: the host id parameter that will contained in the hip_encrypted
+ * parameter
+ *
+ * Note that this function does not actually encrypt anything, it just builds
+ * the parameter. The @host_id that will be encapsulated in the hip_encrypted
+ * parameter has to be encrypted using a different function call.
+ *
+ * Returns: zero on success, or negative on failure
+ */
+int hip_build_param_encrypted_aes_sha1(struct hip_common *msg,
+ struct hip_host_id *host_id)
+{
+ int rem, err = 0;
+ struct hip_encrypted_aes_sha1 enc;
+ int host_id_len = hip_get_param_total_len(host_id);
+ struct hip_host_id *hid = host_id;
+ char *host_id_padded = NULL;
+
+ hip_set_param_type(&enc, HIP_PARAM_ENCRYPTED);
+ enc.reserved = htonl(0);
+ memset(&enc.iv, 0, 16);
+
+ /* copy the IV *IF* needed, and then the encrypted data */
+
+ /* AES block size must be multiple of 16 bytes */
+ rem = host_id_len % 16;
+ if (rem) {
+ HIP_DEBUG("Adjusting host id size to AES block size\n");
+
+ host_id_padded = kmalloc(host_id_len + rem, GFP_KERNEL);
+ if (!host_id_padded) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* this kind of padding works against Ericsson/OpenSSL
+ (method 4: RFC2630 method) */
+ /* http://www.di-mgt.com.au/cryptopad.html#exampleaes */
+ memcpy(host_id_padded, host_id, host_id_len);
+ memset(host_id_padded + host_id_len, rem, rem);
+
+ hid = (struct hip_host_id *) host_id_padded;
+ host_id_len += rem;
+ }
+
+ hip_calc_param_len(&enc, sizeof(enc) -
+ sizeof(struct hip_tlv_common) +
+ host_id_len);
+
+ err = hip_build_generic_param(msg, &enc, sizeof(enc), hid);
+
+ out_err:
+
+ if (host_id_padded)
+ kfree(host_id_padded);
+
+ return err;
+}
+
+/**
+ * hip_build_param_hmac2_contents - build and append a HIP hmac2 parameter
+ * @msg: the message where the hmac parameter will be appended
+ * @key: pointer to a key used for HMAC
+ *
+ * This function calculates the also the HMAC value from the whole message
+ * as specified in the drafts. Assumes that the hmac includes only the header
+ * and host id.
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_hmac2_contents(struct hip_common *msg,
+ struct hip_crypto_key *key,
+ struct hip_host_id *host_id)
+{
+ int err = 0;
+ struct hip_hmac hmac2;
+ struct hip_common *tmp = NULL;
+ struct hip_spi *spi;
+
+ tmp = hip_msg_alloc();
+ if (!tmp) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ memcpy(tmp, msg, sizeof(struct hip_common));
+ hip_set_msg_total_len(tmp, 0);
+ /* assume no checksum yet */
+
+ spi = hip_get_param(msg, HIP_PARAM_SPI);
+ HIP_ASSERT(spi);
+ err = hip_build_param(tmp, spi);
+ if (err) {
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ hip_set_param_type(&hmac2, HIP_PARAM_HMAC2);
+ hip_calc_generic_param_len(&hmac2, sizeof(struct hip_hmac), 0);
+
+ err = hip_build_param(tmp, host_id);
+ if (err) {
+ HIP_ERROR("Failed to append pseudo host id to R2\n");
+ goto out_err;
+ }
+
+ HIP_HEXDUMP("HMAC data", tmp, hip_get_msg_total_len(tmp));
+ HIP_HEXDUMP("HMAC key\n", key->key, 20);
+
+ if (!hip_write_hmac(HIP_DIGEST_SHA1_HMAC, key->key, tmp,
+ hip_get_msg_total_len(tmp),
+ hmac2.hmac_data)) {
+ HIP_ERROR("Error while building HMAC\n");
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ err = hip_build_param(msg, &hmac2);
+ out_err:
+ if (tmp)
+ kfree(tmp);
+
+ return err;
+}
+
+#endif /* __KERNEL__ */
+
+/**
+ * hip_build_param_signature2_contents - build HIP signature2
+ * @msg: the message
+ * @contents: pointer to the signature contents (the data to be written
+ * after the signature field)
+ * @contents_size: size of the contents of the signature (the data after the
+ * algorithm field)
+ * @algorithm: the algorithm as in the HIP drafts that was used for
+ * producing the signature
+ *
+ * build_param_contents() is not very suitable for building a hip_sig2 struct,
+ * because hip_sig2 has a troublesome algorithm field which need some special
+ * attention from htons(). Thereby here is a separate builder for hip_sig2 for
+ * conveniency. It uses internally hip_build_generic_param() for actually
+ * writing the signature parameter into the message.
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_signature2_contents(struct hip_common *msg,
+ const void *contents,
+ hip_tlv_len_t contents_size,
+ uint8_t algorithm)
+{
+ /* note: if you make changes in this function, make them also in
+ build_param_signature_contents(), because it is almost the same */
+
+ int err = 0;
+ struct hip_sig2 sig2;
+
+ HIP_ASSERT(sizeof(struct hip_sig2) >= sizeof(struct hip_tlv_common));
+
+ hip_set_param_type(&sig2, HIP_PARAM_HIP_SIGNATURE2);
+ hip_calc_generic_param_len(&sig2, sizeof(struct hip_sig2),
+ contents_size);
+ sig2.algorithm = algorithm; /* algo is 8 bits, no htons */
+
+ err = hip_build_generic_param(msg, &sig2,
+ sizeof(struct hip_sig2), contents);
+
+ return err;
+}
+
+/**
+ * hip_build_param_signature_contents - build HIP signature1
+ * @msg: the message
+ * @contents: pointer to the signature contents (the data to be written
+ * after the signature field)
+ * @contents_size: size of the contents of the signature (the data after the
+ * algorithm field)
+ * @algorithm: the algorithm as in the HIP drafts that was used for
+ * producing the signature
+ *
+ * This is almost the same as the previous, but the type is sig1.
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_signature_contents(struct hip_common *msg,
+ const void *contents,
+ hip_tlv_len_t contents_size,
+ uint8_t algorithm)
+{
+ /* note: if you make changes in this function, make them also in
+ build_param_signature_contents2(), because it is almost the same */
+
+ int err = 0;
+ struct hip_sig sig;
+
+ HIP_ASSERT(sizeof(struct hip_sig) >= sizeof(struct hip_tlv_common));
+
+ hip_set_param_type(&sig, HIP_PARAM_HIP_SIGNATURE);
+ hip_calc_generic_param_len(&sig, sizeof(struct hip_sig),
+ contents_size);
+ sig.algorithm = algorithm; /* algo is 8 bits, no htons */
+
+ err = hip_build_generic_param(msg, &sig,
+ sizeof(struct hip_sig), contents);
+
+ return err;
+}
+
+/**
+ * hip_build_param_from - build HIP FROM parameter
+ * @msg: the message
+ * @addr: An IPv6 address or an IPv4-in-IPv6 format IPv4 address
+ * @sign: true if parameter is under signature, false otherwise
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_from(struct hip_common *msg, struct in6_addr *addr, int sign)
+{
+ struct hip_from from;
+ int err;
+
+ hip_set_param_type(&from, sign ? HIP_PARAM_FROM_SIGN : HIP_PARAM_FROM);
+ memcpy((struct in6_addr *)&from.address, addr, 16);
+
+ hip_calc_generic_param_len(&from, sizeof(struct hip_from), 0);
+ err = hip_build_param(msg, &from);
+ return err;
+}
+
+/**
+ * hip_build_param_echo - build HIP ECHO parameter
+ * @msg: the message
+ * @opaque: opaque data copied to the parameter
+ * len: the length of the parameter
+ * @sign: true if parameter is under signature, false otherwise
+ * @request: true if parameter is ECHO_REQUEST, otherwise parameter is ECHO_RESPONSE
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_echo(struct hip_common *msg, void *opaque, int len,
+ int sign, int request)
+{
+ struct hip_echo_request ping;
+ int err;
+
+ if (request)
+ hip_set_param_type(&ping, sign ? HIP_PARAM_ECHO_REQUEST_SIGN : HIP_PARAM_ECHO_REQUEST);
+ else
+ hip_set_param_type(&ping, sign ? HIP_PARAM_ECHO_RESPONSE_SIGN : HIP_PARAM_ECHO_RESPONSE);
+
+ hip_set_param_contents_len(&ping, len);
+ err = hip_build_generic_param(msg, &ping, sizeof(struct hip_echo_request),
+ opaque);
+ return err;
+}
+
+/**
+ * hip_build_param_r1_counter - build HIP R1_COUNTER parameter
+ * @msg: the message
+ * @generation: R1 generation counter
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_r1_counter(struct hip_common *msg, uint64_t generation)
+{
+ struct hip_r1_counter r1gen;
+ int err = 0;
+
+ /* note: the length cannot be calculated with calc_param_len() */
+ hip_set_param_contents_len(&r1gen,
+ sizeof(struct hip_r1_counter) -
+ sizeof(struct hip_tlv_common));
+ /* Type 2 (in R1) or 3 (in I2) */
+ hip_set_param_type(&r1gen, HIP_PARAM_R1_COUNTER);
+
+ /* only the random_j_k is in host byte order */
+ r1gen.generation = generation;
+
+ err = hip_build_param(msg, &r1gen);
+ return err;
+}
+
+/**
+ * hip_build_param_rva - build HIP RVA parameter
+ * @msg: the message
+ * @lifetime: lifetime in seconds in host bute order
+ * @type_list: list of types (in host byte order) to be appended
+ * @cnt: number of addresses in @type_list
+ * @request: true if parameter is RVA_REQUEST, otherwise parameter is RVA_REPLY
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_rva(struct hip_common *msg, uint32_t lifetime,
+ int *type_list, int cnt, int request)
+{
+ int err = 0;
+ int i;
+ struct hip_rva_reply rrep;
+
+ hip_set_param_type(&rrep, (request ? HIP_PARAM_RVA_REQUEST : HIP_PARAM_RVA_REPLY));
+ hip_calc_generic_param_len(&rrep, sizeof(struct hip_rva_reply),
+ cnt * sizeof(uint16_t));
+
+ for(i=0;i> 8;
+ /* puzzle.opaque[2] = (opaque & 0xFF0000) >> 16; */
+ puzzle.I = random_i;
+
+ err = hip_build_generic_param(msg, &puzzle,
+ sizeof(struct hip_tlv_common),
+ hip_get_param_contents_direct(&puzzle));
+ return err;
+
+}
+
+/**
+ * hip_build_param_solution - build and append a HIP solution into the message
+ * @msg: the message where the solution is to be appended
+ * @pz: values from the corresponding puzzle copied to the solution
+ * @val_J: J value for the solution (in host byte order)
+ *
+ * The puzzle mechanism assumes that every value is in network byte order
+ * except for the hip_birthday_cookie.cv union, where the value is in
+ * host byte order. This is an exception to the normal builder rules, where
+ * input arguments are normally always in host byte order.
+ *
+ * Returns: zero for success, or non-zero on error
+ */
+int hip_build_param_solution(struct hip_common *msg, struct hip_puzzle *pz,
+ uint64_t val_J)
+{
+ struct hip_solution cookie;
+ int err = 0;
+
+ /* note: the length cannot be calculated with calc_param_len() */
+ hip_set_param_contents_len(&cookie,
+ sizeof(struct hip_solution) -
+ sizeof(struct hip_tlv_common));
+ /* Type 2 (in R1) or 3 (in I2) */
+ hip_set_param_type(&cookie, HIP_PARAM_SOLUTION);
+
+ cookie.J = hton64(val_J);
+ memcpy(&cookie.K, &pz->K, 12); /* copy: K (1), reserved (1),
+ opaque (2) and I (8 bytes). */
+ cookie.reserved = 0;
+ err = hip_build_generic_param(msg, &cookie,
+ sizeof(struct hip_tlv_common),
+ hip_get_param_contents_direct(&cookie));
+ return err;
+}
+
+/**
+ * hip_build_param_diffie_hellman_contents - build HIP DH contents
+ * @msg: the message where the DH parameter will be appended
+ * @group_id: the group id of the DH parameter as specified in the drafts
+ * @pubkey: the public key part of the DH
+ *
+ * Returns: zero on success, or non-zero on error
+ */
+int hip_build_param_diffie_hellman_contents(struct hip_common *msg,
+ uint8_t group_id,
+ void *pubkey,
+ hip_tlv_len_t pubkey_len)
+{
+ int err = 0;
+ struct hip_diffie_hellman diffie_hellman;
+
+ HIP_ASSERT(pubkey_len >= sizeof(struct hip_tlv_common));
+
+ hip_set_param_type(&diffie_hellman, HIP_PARAM_DIFFIE_HELLMAN);
+ hip_calc_generic_param_len(&diffie_hellman, sizeof(struct hip_diffie_hellman),
+ pubkey_len);
+ diffie_hellman.group_id = group_id; /* 1 byte, no htons() */
+
+ err = hip_build_generic_param(msg, &diffie_hellman,
+ sizeof(struct hip_diffie_hellman), pubkey);
+
+ return err;
+}
+
+/**
+ * hip_get_transform_max - find out the maximum number of transform suite ids
+ * @transform_type: the type of the transform
+ *
+ * Returns: the number of suite ids that can be used for @transform_type
+ */
+uint16_t hip_get_transform_max(hip_tlv_type_t transform_type)
+{
+ uint16_t transform_max = 0;
+
+ switch (transform_type) {
+ case HIP_PARAM_HIP_TRANSFORM:
+ transform_max = HIP_TRANSFORM_HIP_MAX;
+ break;
+ case HIP_PARAM_ESP_TRANSFORM:
+ transform_max = HIP_TRANSFORM_ESP_MAX;
+ break;
+ default:
+ HIP_ERROR("Unknown transform type %d\n", transform_type);
+ }
+
+ return transform_max;
+
+}
+
+/**
+ * hip_build_param_transform - build an HIP or ESP transform
+ * @msg: the message where the parameter will be appended
+ * @transform_type: HIP_PARAM_HIP_TRANSFORM or HIP_PARAM_ESP_TRANSFORM
+ * in host byte order
+ * @transform_suite: an array of transform suite ids in host byte order
+ * @transform_count: number of transform suites in @transform_suite (in host
+ * byte order)
+ *
+ * Returns: zero on success, or negative on error
+ */
+int hip_build_param_transform(struct hip_common *msg,
+ const hip_tlv_type_t transform_type,
+ const hip_transform_suite_t transform_suite[],
+ const uint16_t transform_count)
+{
+ int err = 0;
+ uint16_t i;
+ uint16_t transform_max;
+ struct hip_any_transform transform_param;
+
+ transform_max = hip_get_transform_max(transform_type);
+
+ if (!(transform_type == HIP_PARAM_ESP_TRANSFORM ||
+ transform_type == HIP_PARAM_HIP_TRANSFORM)) {
+ err = -EINVAL;
+ HIP_ERROR("Invalid transform type %d\n", transform_type);
+ goto out_err;
+ }
+
+ /* Check that the maximum number of transforms is not overflowed */
+ if (transform_max > 0 && transform_count > transform_max) {
+ err = -E2BIG;
+ HIP_ERROR("Too many transforms (%d) for type %d.\n",
+ transform_count, transform_type);
+ goto out_err;
+ }
+
+ if (transform_type == HIP_PARAM_ESP_TRANSFORM) {
+ ((struct hip_esp_transform *)&transform_param)->reserved = 0;
+ }
+
+ /* Copy and convert transforms to network byte order. */
+ for(i = 0; i < transform_count; i++) {
+ if (transform_type == HIP_PARAM_ESP_TRANSFORM) {
+ ((struct hip_esp_transform *)&transform_param)->suite_id[i] = htons(transform_suite[i]);
+ } else {
+ ((struct hip_hip_transform *)&transform_param)->suite_id[i] = htons(transform_suite[i]);
+ }
+ }
+
+ hip_set_param_type(&transform_param, transform_type);
+ if (transform_type == HIP_PARAM_ESP_TRANSFORM) {
+ hip_calc_param_len(&transform_param,
+ 2+transform_count * sizeof(hip_transform_suite_t));
+ } else {
+ hip_calc_param_len(&transform_param,
+ transform_count * sizeof(hip_transform_suite_t));
+ }
+ err = hip_build_param(msg, &transform_param);
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_get_param_transform_suite_id - get a suite id from a transform structure
+ * @transform_tlv: the transform structure
+ * @index: the index of the suite id in @transform_tlv
+ *
+ * XX FIXME: REMOVE INDEX, XX RENAME
+ *
+ * Returns: the suite id on @transform_tlv on index @index
+ */
+hip_transform_suite_t hip_get_param_transform_suite_id(const void *transform_tlv,
+ const uint16_t index)
+{
+ /* XX FIXME: WHY DO WE HAVE HIP_SELECT_ESP_TRANSFORM SEPARATELY??? */
+
+ hip_tlv_type_t type;
+ uint16_t supported_hip_tf[] = { HIP_HIP_NULL_SHA1,
+ HIP_HIP_3DES_SHA1,
+ HIP_HIP_AES_SHA1};
+ uint16_t supported_esp_tf[] = { HIP_ESP_NULL_SHA1,
+ HIP_ESP_3DES_SHA1,
+ HIP_ESP_AES_SHA1 };
+ uint16_t *table = NULL;
+ uint16_t *tfm;
+ int table_n = 0, pkt_tfms = 0, i;
+
+ _HIP_DEBUG("tfm len = %d\n", hip_get_param_contents_len(transform_tlv));
+
+ type = hip_get_param_type(transform_tlv);
+ if (type == HIP_PARAM_HIP_TRANSFORM) {
+ table = supported_hip_tf;
+ table_n = sizeof(supported_hip_tf)/sizeof(uint16_t);
+ tfm = (void *)transform_tlv+sizeof(struct hip_tlv_common);
+ pkt_tfms = hip_get_param_contents_len(transform_tlv)/sizeof(uint16_t);
+ } else if (type == HIP_PARAM_ESP_TRANSFORM) {
+ table = supported_esp_tf;
+ table_n = sizeof(supported_esp_tf)/sizeof(uint16_t);
+ tfm = (void *)transform_tlv+sizeof(struct hip_tlv_common)+sizeof(uint16_t);
+ pkt_tfms = (hip_get_param_contents_len(transform_tlv)-sizeof(uint16_t))/sizeof(uint16_t);
+ } else {
+ HIP_ERROR("Invalid type %u\n", type);
+ return 0;
+ }
+
+ for (i = 0; i < pkt_tfms; i++, tfm++) {
+ int j;
+ _HIP_DEBUG("testing pkt tfm=%u\n", ntohs(*tfm));
+ for (j = 0; j < table_n; j++) {
+ if (ntohs(*tfm) == table[j]) {
+ _HIP_DEBUG("found supported tfm %u, pkt tlv index of tfm=%d\n",
+ table[j], i);
+ return table[j];
+ }
+ }
+ }
+ HIP_ERROR("usable suite not found\n");
+ return 0;
+}
+
+/**
+ * hip_build_param_rea - build HIP REA parameter
+ *
+ * @msg: the message where the REA will be appended
+ * @spi: SPI in host byte order
+ * @addresses: list of addresses
+ * @address_count: number of addresses in @addresses
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_rea(struct hip_common *msg,
+ uint32_t spi,
+ struct hip_rea_info_addr_item *addresses,
+ int address_count)
+{
+ int err = 0;
+ struct hip_rea rea_info;
+ int addrs_len = address_count *
+ (sizeof(struct hip_rea_info_addr_item));
+
+ hip_set_param_type(&rea_info, HIP_PARAM_REA);
+ hip_calc_generic_param_len(&rea_info,
+ sizeof(struct hip_rea),
+ addrs_len);
+ _HIP_DEBUG("params size=%d\n", sizeof(struct hip_rea) -
+ sizeof(struct hip_tlv_common) +
+ addrs_len);
+ rea_info.spi = htonl(spi);
+ err = hip_build_param(msg, &rea_info);
+ if (err)
+ return err;
+ _HIP_DEBUG("msgtotlen=%d addrs_len=%d\n", hip_get_msg_total_len(msg),
+ addrs_len);
+ if (addrs_len > 0)
+ memcpy((void *)msg+hip_get_msg_total_len(msg)-addrs_len,
+ addresses, addrs_len);
+
+ return err;
+}
+
+/**
+ * hip_build_param_nes - build and append HIP NES parameter
+ * @msg: the message where the parameter will be appended
+ * @is_reply: 1 if this packet is a reply to another UPDATE
+ * @keymat_index: Keymat Index in host byte order
+ * @old_spi: Old SPI value in host byte order
+ * @new_spi: New SPI value in host byte order
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_nes(struct hip_common *msg, uint16_t keymat_index,
+ uint32_t old_spi, uint32_t new_spi)
+{
+ int err = 0;
+ struct hip_nes nes;
+
+ hip_set_param_type(&nes, HIP_PARAM_NES);
+ hip_calc_generic_param_len(&nes, sizeof(struct hip_nes), 0);
+ nes.keymat_index = htons(keymat_index);
+ nes.old_spi = htonl(old_spi);
+ nes.new_spi = htonl(new_spi);
+ err = hip_build_param(msg, &nes);
+ return err;
+}
+
+/**
+ * hip_build_param_seq - build and append HIP SEQ parameter
+ * @msg: the message where the parameter will be appended
+ * @seq: Update ID
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_seq(struct hip_common *msg, uint32_t update_id)
+{
+ int err = 0;
+ struct hip_seq seq;
+
+ hip_set_param_type(&seq, HIP_PARAM_SEQ);
+ hip_calc_generic_param_len(&seq, sizeof(struct hip_seq), 0);
+ seq.update_id = htonl(update_id);
+ err = hip_build_param(msg, &seq);
+ return err;
+}
+
+/**
+ * hip_build_param_ack - build and append HIP ACK parameter
+ * @msg: the message where the parameter will be appended
+ * @peer_update_id: peer Update ID
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_ack(struct hip_common *msg, uint32_t peer_update_id)
+{
+ int err = 0;
+ struct hip_ack ack;
+
+ hip_set_param_type(&ack, HIP_PARAM_ACK);
+ hip_calc_generic_param_len(&ack, sizeof(struct hip_ack), 0);
+ ack.peer_update_id = htonl(peer_update_id);
+ err = hip_build_param(msg, &ack);
+ return err;
+}
+
+/**
+ * hip_build_param_unit_test - build and insert an unit test parameter
+ * @msg: the message where the parameter will be appended
+ * @suiteid: the id of the test suite
+ * @caseid: the id of the test case
+ *
+ * This parameter is used for triggering the unit test suite in the kernel.
+ * It is only for implementation internal purposes only.
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_param_unit_test(struct hip_common *msg, uint16_t suiteid,
+ uint16_t caseid)
+{
+ int err = 0;
+ struct hip_unit_test ut;
+
+ hip_set_param_type(&ut, HIP_PARAM_UNIT_TEST);
+ hip_calc_generic_param_len(&ut, sizeof(struct hip_unit_test), 0);
+ ut.suiteid = htons(suiteid);
+ ut.caseid = htons(caseid);
+
+ err = hip_build_param(msg, &ut);
+ return err;
+}
+
+/**
+ * hip_build_param_spi_lsi - build the SPI_LSI parameter
+ * @msg: the message where the parameter will be appended
+ * @lsi: the value of the lsi (in host byte order)
+ * @spi: the value of the spi (in host byte order)
+ *
+ * Returns: zero on success, or negative on failure
+ */
+int hip_build_param_spi(struct hip_common *msg, uint32_t spi)
+{
+ int err = 0;
+ struct hip_spi hspi;
+
+ hip_set_param_type(&hspi, HIP_PARAM_SPI);
+ hip_calc_generic_param_len(&hspi, sizeof(struct hip_spi), 0);
+ hspi.spi = htonl(spi);
+
+ err = hip_build_param(msg, &hspi);
+ return err;
+}
+
+/**
+ * hip_build_param_encrypted_3des_sha1 - build the hip_encrypted parameter
+ * @msg: the message where the parameter will be appended
+ * @host_id: the host id parameter that will contained in the hip_encrypted
+ * parameter
+ *
+ * Note that this function does not actually encrypt anything, it just builds
+ * the parameter. The @host_id that will be encapsulated in the hip_encrypted
+ * parameter has to be encrypted using a different function call.
+ *
+ * Returns: zero on success, or negative on failure
+ */
+int hip_build_param_encrypted_3des_sha1(struct hip_common *msg,
+ struct hip_host_id *host_id)
+{
+ int err = 0;
+ struct hip_encrypted_3des_sha1 enc;
+
+ hip_set_param_type(&enc, HIP_PARAM_ENCRYPTED);
+ hip_calc_param_len(&enc, sizeof(enc) -
+ sizeof(struct hip_tlv_common) +
+ hip_get_param_total_len(host_id));
+ enc.reserved = htonl(0);
+ memset(&enc.iv, 0, 8);
+
+ /* copy the IV *IF* needed, and then the encrypted data */
+
+ err = hip_build_generic_param(msg, &enc, sizeof(enc), host_id);
+
+ return err;
+}
+
+/**
+ * hip_build_param_encrypted_null_sha1 - build the hip_encrypted parameter
+ * @msg: the message where the parameter will be appended
+ * @host_id: the host id parameter that will contained in the hip_encrypted
+ * parameter
+ *
+ * Note that this function does not actually encrypt anything, it just builds
+ * the parameter. The @host_id that will be encapsulated in the hip_encrypted
+ * parameter has to be encrypted using a different function call.
+ *
+ * Returns: zero on success, or negative on failure
+ */
+int hip_build_param_encrypted_null_sha1(struct hip_common *msg,
+ struct hip_host_id *host_id)
+{
+ int err = 0;
+ struct hip_encrypted_null_sha1 enc;
+
+ hip_set_param_type(&enc, HIP_PARAM_ENCRYPTED);
+ hip_calc_param_len(&enc, sizeof(enc) -
+ sizeof(struct hip_tlv_common) +
+ hip_get_param_total_len(host_id));
+ enc.reserved = htonl(0);
+
+ /* copy the IV *IF* needed, and then the encrypted data */
+
+ err = hip_build_generic_param(msg, &enc, sizeof(enc), host_id);
+
+ return err;
+}
+
+void hip_build_param_host_id_hdr(struct hip_host_id *host_id_hdr,
+ const char *hostname,
+ hip_tlv_len_t rr_data_len,
+ uint8_t algorithm)
+{
+ uint16_t hi_len = sizeof(struct hip_host_id_key_rdata) + rr_data_len;
+ uint16_t fqdn_len;
+
+ /* reserve 1 byte for NULL termination */
+ if (hostname)
+ fqdn_len = (strlen(hostname) + 1) & 0x0FFF;
+ else
+ fqdn_len = 0;
+
+ host_id_hdr->hi_length = htons(hi_len);
+ /* length = 12 bits, di_type = 4 bits */
+ host_id_hdr->di_type_length = htons(fqdn_len | 0x1000);
+ /* if the length is 0, then the type should also be zero */
+ if (host_id_hdr->di_type_length == ntohs(0x1000))
+ host_id_hdr->di_type_length = 0;
+
+ hip_set_param_type(host_id_hdr, HIP_PARAM_HOST_ID);
+ hip_calc_generic_param_len(host_id_hdr, sizeof(struct hip_host_id),
+ hi_len -
+ sizeof(struct hip_host_id_key_rdata) +
+ fqdn_len);
+
+ host_id_hdr->rdata.flags = htons(0x0202); /* key is for a host */
+ host_id_hdr->rdata.protocol = 0xFF; /* RFC 2535 */
+ /* algo is 8 bits, no htons */
+ host_id_hdr->rdata.algorithm = algorithm;
+
+ _HIP_DEBUG("hilen=%d totlen=%d contlen=%d\n",
+ ntohs(host_id_hdr->hi_length),
+ hip_get_param_contents_len(host_id_hdr),
+ hip_get_param_total_len(host_id_hdr));
+}
+
+void hip_build_param_host_id_only(struct hip_host_id *host_id,
+ const void *rr_data,
+ const char *fqdn)
+{
+ unsigned int rr_len = ntohs(host_id->hi_length) -
+ sizeof(struct hip_host_id_key_rdata);
+ char *ptr = (char *) (host_id + 1);
+ uint16_t fqdn_len;
+
+ _HIP_DEBUG("hi len: %d\n", ntohs(host_id->hi_length));
+ _HIP_DEBUG("Copying %d bytes\n", rr_len);
+
+ memcpy(ptr, rr_data, rr_len);
+ ptr += rr_len;
+
+ fqdn_len = ntohs(host_id->di_type_length) & 0x0FFF;
+ _HIP_DEBUG("fqdn len: %d\n", fqdn_len);
+ if (fqdn_len)
+ memcpy(ptr, fqdn, fqdn_len);
+}
+
+/**
+ * hip_build_param_host_id - build and append host id into message
+ * @XXTODO: XX TODO
+ * @algorithm: the crypto algorithm used for the host id (as in the drafts)
+ *
+ */
+int hip_build_param_host_id(struct hip_common *msg,
+ struct hip_host_id *host_id_hdr,
+ const void *rr_data,
+ const char *fqdn)
+{
+ int err = 0;
+ hip_build_param_host_id_only(host_id_hdr, rr_data, fqdn);
+ err = hip_build_param(msg, host_id_hdr);
+ return err;
+}
+
+int hip_get_param_host_id_di_type_len(struct hip_host_id *host, char **id, int *len)
+{
+ int type;
+ static char *debuglist[3] = {"none", "FQDN", "NAI"};
+
+ type = ntohs(host->di_type_length);
+ *len = type & 0x0FFF;
+ type = (type & 0xF000) >> 12;
+
+ if (type > 2) {
+ HIP_ERROR("Illegal DI-type: %d\n",type);
+ return -1;
+ }
+
+ *id = debuglist[type];
+ return 0;
+}
+
+char *hip_get_param_host_id_hostname(struct hip_host_id *hostid)
+{
+ int hilen;
+ char *ptr;
+
+ hilen = ntohs(hostid->hi_length) - sizeof(struct hip_host_id_key_rdata);
+ _HIP_DEBUG("Hilen: %d\n",hilen);
+ ptr = (char *)(hostid + 1) + hilen;
+ return ptr;
+}
+
+/*
+ * - endpoint is not padded
+ */
+void hip_build_endpoint_hdr(struct endpoint_hip *endpoint_hdr,
+ const char *hostname,
+ se_hip_flags_t endpoint_flags,
+ uint8_t host_id_algo,
+ unsigned int rr_data_len)
+{
+ hip_build_param_host_id_hdr(&endpoint_hdr->id.host_id,
+ hostname, rr_data_len, host_id_algo);
+ endpoint_hdr->family = PF_HIP;
+ /* The length is not hip-length-padded, so it has be calculated
+ manually. sizeof(hip_host_id) is already included both in the
+ sizeof(struct endpoint_hip) and get_total_len(), so it has be
+ subtracted once. */
+ endpoint_hdr->length = sizeof(struct endpoint_hip) +
+ hip_get_param_total_len(&endpoint_hdr->id.host_id) -
+ sizeof(struct hip_host_id);
+ endpoint_hdr->flags = endpoint_flags;
+ HIP_DEBUG("%d %d %d\n",
+ sizeof(struct endpoint_hip),
+ hip_get_param_total_len(&endpoint_hdr->id.host_id),
+ sizeof(struct hip_host_id));
+ HIP_DEBUG("endpoint hdr length: %d\n", endpoint_hdr->length);
+}
+
+/*
+ * - endpoint is not padded
+ * - caller is responsible of reserving enough mem for endpoint
+ */
+void hip_build_endpoint(struct endpoint_hip *endpoint,
+ const struct endpoint_hip *endpoint_hdr,
+ const char *hostname,
+ const unsigned char *key_rr,
+ unsigned int key_rr_len)
+{
+ HIP_DEBUG("len=%d ep=%d rr=%d hostid=%d\n",
+ endpoint_hdr->length,
+ sizeof(struct endpoint_hip),
+ key_rr_len,
+ sizeof(struct hip_host_id));
+ HIP_ASSERT(endpoint_hdr->length == sizeof(struct endpoint_hip) +
+ hip_get_param_total_len(&endpoint_hdr->id.host_id) -
+ sizeof(struct hip_host_id));
+ memcpy(endpoint, endpoint_hdr, sizeof(struct endpoint_hip));
+ hip_build_param_host_id_only(&endpoint->id.host_id, key_rr, hostname);
+}
+
+int hip_build_param_eid_endpoint_from_host_id(struct hip_common *msg,
+ const struct endpoint_hip *endpoint)
+{
+ int err = 0;
+
+ HIP_ASSERT(!(endpoint->flags & HIP_ENDPOINT_FLAG_HIT));
+
+ err = hip_build_param_contents(msg, endpoint, HIP_PARAM_EID_ENDPOINT,
+ endpoint->length);
+ return err;
+}
+
+int hip_build_param_eid_endpoint_from_hit(struct hip_common *msg,
+ const struct endpoint_hip *endpoint)
+{
+ struct hip_eid_endpoint eid_endpoint;
+ int err = 0;
+
+ HIP_ASSERT(endpoint->flags & HIP_ENDPOINT_FLAG_HIT);
+
+ hip_set_param_type(&eid_endpoint, HIP_PARAM_EID_ENDPOINT);
+
+ hip_calc_param_len(&eid_endpoint,
+ sizeof(struct hip_eid_endpoint) -
+ sizeof (struct hip_tlv_common));
+
+ memcpy(&eid_endpoint.endpoint, endpoint, sizeof(struct endpoint_hip));
+
+ err = hip_build_param(msg, &eid_endpoint);
+
+ return err;
+}
+
+/*
+ * hip_build_param_eid_endpoint - build eid endpoint parameter
+ * @msg: the message where the eid endpoint paramater will be appended
+ * @endpoint: the endpoint to be wrapped into the eid endpoint structure
+ * @port: the dst/src port used for the endpoint
+ *
+ * Used for passing endpoints to the kernel. The endpoint is wrapped into
+ * an eid endpoint structure because endpoint_hip is not padded but all
+ * parameter need to be padded in the builder interface.
+ */
+int hip_build_param_eid_endpoint(struct hip_common *msg,
+ const struct endpoint_hip *endpoint)
+{
+ int err = 0;
+
+ if (endpoint->flags & HIP_ENDPOINT_FLAG_HIT) {
+ err = hip_build_param_eid_endpoint_from_hit(msg, endpoint);
+ } else {
+ err = hip_build_param_eid_endpoint_from_host_id(msg, endpoint);
+ }
+ HIP_DEBUG("err=%d\n", err);
+ return err;
+}
+
+int hip_build_param_eid_iface(struct hip_common *msg,
+ hip_eid_iface_type_t if_index)
+{
+ int err = 0;
+ struct hip_eid_iface param;
+
+ hip_set_param_type(¶m, HIP_PARAM_EID_IFACE);
+ hip_calc_generic_param_len(¶m, sizeof(param), 0);
+ param.if_index = htons(if_index);
+ err = hip_build_param(msg, ¶m);
+
+ return err;
+}
+
+int hip_build_param_eid_sockaddr(struct hip_common *msg,
+ struct sockaddr *sockaddr,
+ size_t sockaddr_len)
+{
+ int err = 0;
+ _HIP_DEBUG("build family=%d, len=%d\n", sockaddr->sa_family,
+ sockaddr_len);
+ err = hip_build_param_contents(msg, sockaddr, HIP_PARAM_EID_SOCKADDR,
+ sockaddr_len);
+ return err;
+}
+
+/**
+ * hip_build_param_notify - build the HIP NOTIFY parameter
+ * @msg: the message where the parameter will be appended
+ * @msgtype: Notify Message Type
+ * @notification_data: the Notification data that will contained in the HIP NOTIFY
+ * parameter
+ * @notification_data_len: length of @notification_data
+ *
+ * Returns: zero on success, or negative on failure
+ */
+int hip_build_param_notify(struct hip_common *msg, uint16_t msgtype,
+ void *notification_data, size_t notification_data_len)
+{
+ int err = 0;
+ struct hip_notify notify;
+
+ hip_set_param_type(¬ify, HIP_PARAM_NOTIFY);
+ hip_calc_param_len(¬ify, sizeof(struct hip_notify) -
+ sizeof(struct hip_tlv_common) +
+ notification_data_len);
+ notify.reserved = 0;
+ notify.msgtype = htons(msgtype);
+
+ err = hip_build_generic_param(msg, ¬ify,
+ sizeof(struct hip_notify),
+ notification_data);
+ return err;
+}
+
+/**
+ * hip_create_control_flags - create control flags to HIP packet header
+ * @anon: true if anonymous flag is set
+ * @cert: true if certificate flag is set
+ * @sht: value to be put into the SHT field
+ * @dht: value to be put into the DHT field
+ *
+ * SHT and DHT are maximum three bits long.
+ *
+ * Returns: the control flag value.
+ */
+uint16_t hip_create_control_flags(int anon, int cert, int sht, int dht)
+{
+ uint16_t flags = HIP_CONTROL_NONE;
+
+ if (anon)
+ flags |= HIP_CONTROL_HIT_ANON;
+ if (cert)
+ flags |= HIP_CONTROL_CERTIFICATES;
+ if (sht)
+ flags |= (sht << HIP_CONTROL_SHT_SHIFT);
+ if (dht)
+ flags |= (dht << HIP_CONTROL_DHT_SHIFT);
+
+ HIP_DEBUG("flags=0x%x\n", flags);
+ return flags;
+}
diff -urN linux-2.6.10/net/ipv6/hip/builder.h linux-2.6.10-hip/net/ipv6/hip/builder.h
--- linux-2.6.10/net/ipv6/hip/builder.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/builder.h 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,170 @@
+#ifndef HIP_BUILDER
+#define HIP_BUILDER
+
+/*
+ * Authors:
+ * - Miika Komu
+ * - Mika Kousa
+ *
+ */
+
+#ifdef __KERNEL__
+
+#include
+#include
+#include
+
+
+#else /* not __KERNEL__ */
+
+#include
+#include
+#include
+#include
+#include "libinet6/debug.h"
+
+/* ARRAY_SIZE is defined in linux/kernel.h, but it is in #ifdef __KERNEL__ */
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#endif /* __KERNEL__ */
+
+
+void hip_msg_init(struct hip_common *msg);
+struct hip_common *hip_msg_alloc(void);
+void hip_msg_free(struct hip_common *msg);
+void hip_build_network_hdr(struct hip_common *msg, uint8_t type_hdr,
+ uint16_t control, const struct in6_addr *hit_sender,
+ const struct in6_addr *hit_receiver);
+
+uint16_t hip_convert_msg_total_len_to_bytes(hip_hdr_len_t len);
+uint16_t hip_get_msg_total_len(const struct hip_common *msg);
+uint16_t hip_get_msg_contents_len(const struct hip_common *msg);
+void hip_set_msg_total_len(struct hip_common *msg, uint16_t len);
+hip_hdr_type_t hip_get_msg_type(const struct hip_common *msg);
+void hip_set_msg_type(struct hip_common *msg, hip_hdr_type_t type);
+hip_hdr_err_t hip_get_msg_err(const struct hip_common *msg);
+void hip_set_msg_err(struct hip_common *msg, hip_hdr_err_t err);
+void hip_zero_msg_checksum(struct hip_common *msg);
+hip_tlv_len_t hip_get_param_total_len(const void *tlv_common);
+hip_tlv_len_t hip_get_param_contents_len(const void *tlv_common);
+void hip_set_param_contents_len(void *tlv_common, hip_tlv_len_t len);
+hip_tlv_type_t hip_get_param_type(const void *tlv_common);
+void hip_set_param_type(void *tlv_common, hip_tlv_type_t type);
+void *hip_get_diffie_hellman_param_public_value_contents(const void *tlv_common);
+hip_tlv_len_t hip_get_diffie_hellman_param_public_value_len(const struct hip_diffie_hellman *dh);
+void hip_set_param_spi_value(struct hip_spi *hspi, uint32_t spi);
+void hip_set_param_lsi_value(struct hip_spi *hspi, uint32_t lsi);
+uint32_t hip_get_param_spi_value(const struct hip_spi *hspi);
+uint32_t hip_get_param_lsi_value(const struct hip_spi *hspi);
+uint16_t hip_get_unit_test_suite_param_id(const struct hip_unit_test *test);
+uint16_t hip_get_unit_test_case_param_id(const struct hip_unit_test *test);
+uint8_t hip_get_host_id_algo(const struct hip_host_id *host_id);
+
+int hip_check_msg_len(const struct hip_common *msg);
+int hip_check_userspace_msg_type(const struct hip_common *msg);
+struct hip_tlv_common *hip_get_next_param(const struct hip_common *msg,
+ const struct hip_tlv_common *current_param);
+void *hip_get_param_contents(const struct hip_common *msg,
+ hip_tlv_type_t param_type);
+void *hip_get_param_contents_direct(const void *tlv_common);
+void *hip_get_param(const struct hip_common *msg,
+ hip_tlv_type_t param_type);
+void *hip_get_nth_param(const struct hip_common *msg,
+ hip_tlv_type_t param_type, int n);
+void *hip_find_free_param(const struct hip_common *msg);
+void hip_calc_hdr_len(struct hip_common *msg);
+void hip_dump_msg(const struct hip_common *msg);
+int hip_check_userspace_msg(const struct hip_common *msg);
+int hip_check_network_msg(const struct hip_common *msg);
+int hip_build_param_contents(struct hip_common *msg, const void *contents,
+ hip_tlv_type_t param_type, hip_tlv_type_t contents_size);
+int hip_build_param(struct hip_common *msg, const void *tlv_common);
+int hip_build_user_hdr(struct hip_common *msg, hip_hdr_type_t base_type,
+ hip_hdr_err_t err_val);
+
+#ifdef __KERNEL__
+int hip_write_hmac(int type, void *key, void *in, int in_len, void *out);
+int hip_build_param_hmac2_contents(struct hip_common *msg,
+ struct hip_crypto_key *key,
+ struct hip_host_id *host_id);
+int hip_build_param_hmac_contents(struct hip_common *msg,
+ struct hip_crypto_key *key);
+#endif /* __KERNEL__ */
+
+int hip_build_param_signature2_contents(struct hip_common *msg,
+ const void *contents,
+ hip_tlv_len_t contents_size,
+ uint8_t algorithm);
+int hip_build_param_signature_contents(struct hip_common *msg,
+ const void *contents,
+ hip_tlv_len_t contents_size,
+ uint8_t algorithm);
+int hip_build_param_diffie_hellman_contents(struct hip_common *msg,
+ uint8_t group_id,
+ void *pubkey,
+ hip_tlv_len_t pub_len);
+int hip_build_param_transform(struct hip_common *msg,
+ const hip_tlv_type_t transform_type,
+ const hip_transform_suite_t transform_suite[],
+ const uint16_t transform_count);
+hip_transform_suite_t hip_get_param_transform_suite_id(const void *transform_tlv, const uint16_t index);
+int hip_build_param_rea(struct hip_common *msg,
+ uint32_t spi,
+ struct hip_rea_info_addr_item *addresses,
+ int address_count);
+int hip_build_param_nes(struct hip_common *msg, uint16_t keymat_index,
+ uint32_t old_spi, uint32_t new_spi);
+int hip_build_param_seq(struct hip_common *msg, uint32_t update_id);
+int hip_build_param_ack(struct hip_common *msg, uint32_t peer_update_id);
+int hip_build_param_unit_test(struct hip_common *msg, uint16_t suiteid,
+ uint16_t caseid);
+int hip_build_param_spi(struct hip_common *msg, uint32_t spi);
+int hip_build_param_encrypted_aes_sha1(struct hip_common *msg,
+ struct hip_host_id *host_id);
+int hip_build_param_encrypted_3des_sha1(struct hip_common *msg,
+ struct hip_host_id *host_id);
+int hip_build_param_encrypted_null_sha1(struct hip_common *msg,
+ struct hip_host_id *host_id);
+int hip_build_param_eid_endpoint(struct hip_common *msg,
+ const struct endpoint_hip *endpoint);
+void hip_build_endpoint_hdr(struct endpoint_hip *endpoint_hdr,
+ const char *hostname,
+ se_hip_flags_t endpoint_flags,
+ uint8_t host_id_algo,
+ unsigned int rr_data_len);
+void hip_build_endpoint(struct endpoint_hip *endpoint,
+ const struct endpoint_hip *endpoint_hdr,
+ const char *hostname,
+ const unsigned char *key_rr,
+ unsigned int key_rr_len);
+int hip_build_param_eid_iface(struct hip_common *msg,
+ hip_eid_iface_type_t if_index);
+int hip_build_param_eid_sockaddr(struct hip_common *msg,
+ struct sockaddr *sockaddr,
+ size_t sockaddr_len);
+
+int hip_build_param_puzzle(struct hip_common *msg, uint8_t val_K,
+ uint8_t lifetime, uint32_t opaque, uint64_t random_i);
+
+int hip_build_param_solution(struct hip_common *msg, struct hip_puzzle *puzzle,
+ uint64_t val_J);
+
+int hip_build_param_r1_counter(struct hip_common *msg, uint64_t generation);
+
+int hip_build_param_rva(struct hip_common *msg, uint32_t lifetime,
+ int *type_list, int cnt, int request);
+
+int hip_build_param_echo(struct hip_common *msg, void *opaque, int len,
+ int sign, int request);
+
+int hip_build_param_from(struct hip_common *msg, struct in6_addr *addr, int sign);
+
+int hip_get_param_host_id_di_type_len(struct hip_host_id *host, char **id, int *len);
+char *hip_get_param_host_id_hostname(struct hip_host_id *hostid);
+int hip_build_param_notify(struct hip_common *msg, uint16_t msgtype,
+ void *notification_data, size_t notification_data_len);
+uint16_t hip_create_control_flags(int anon, int cert, int sht, int dht);
+
+#endif /* HIP_BUILDER */
diff -urN linux-2.6.10/net/ipv6/hip/cookie.c linux-2.6.10-hip/net/ipv6/hip/cookie.c
--- linux-2.6.10/net/ipv6/hip/cookie.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/cookie.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,464 @@
+/*
+ * HIP cookie handling
+ *
+ * Licence: GNU/GPL
+ * Authors: Kristian Slavov
+ * Miika Komu
+ *
+ */
+
+#include
+#include
+#include
+
+#include "cookie.h"
+#include "debug.h"
+#include "hip.h"
+#include "builder.h"
+
+struct hip_r1entry *hip_r1table;
+
+/**
+ * hip_calc_cookie_idx - get an index
+ * @ip_i: Initiator's IPv6 address
+ * @ip_r: Responder's IPv6 address
+ *
+ * Return 0 <= x < HIP_R1TABLESIZE
+ */
+static int hip_calc_cookie_idx(struct in6_addr *ip_i, struct in6_addr *ip_r)
+{
+ register u32 base=0;
+ int i;
+
+ for(i = 0; i < 4; i++) {
+ base ^= ip_i->s6_addr32[i];
+ base ^= ip_r->s6_addr32[i];
+ }
+
+ for(i = 0; i < 3; i++) {
+ base ^= ((base >> (24 - i * 8)) & 0xFF);
+ }
+
+ /* base ready */
+
+ return (base) % HIP_R1TABLESIZE;
+}
+
+#if 0
+/**
+ * hip_create_new_puzzle - Create/Change the puzzle in R1
+ * @pz: Old puzzle
+ * @r1: R1entry
+ * @tv: Timevalue, that is inserted into the opaque field in puzzle
+ *
+ * Stores the old K, I and opaque values and generates new ones.
+ * Storing is required since we need to support puzzles sent just before
+ * we decide to change the puzzle.
+ */
+static void hip_create_new_puzzle(struct hip_puzzle *pz, struct hip_r1entry *r1,
+ struct timeval *tv)
+{
+ uint64_t random_i;
+
+ r1->Ck = pz->K;
+ r1->Ci = pz->I;
+ memcpy(r1->Copaque, pz->opaque, 3);
+
+ get_random_bytes(&random_i, sizeof(uint64_t));
+ pz->I = random_i;
+ tv->tv_sec &= 0xFFFFFF;
+ pz->opaque[0] = (tv->tv_sec & 0xFF);
+ pz->opaque[1] = ((tv->tv_sec >> 8) & 0xFF);
+ pz->opaque[2] = ((tv->tv_sec >> 16) & 0xFF);
+}
+#endif
+
+/**
+ * hip_fetch_cookie_entry - Get an R1entry structure
+ * @ip_i: Initiator's IPv6
+ * @ip_r: Responder's IPv6
+ *
+ * Comments for the #if 0 code are inlined below.
+ *
+ * Returns NULL if error.
+ */
+static struct hip_r1entry *hip_fetch_cookie_entry(struct in6_addr *ip_i,
+ struct in6_addr *ip_r)
+{
+#if 0
+ struct timeval tv;
+ struct hip_puzzle *pz;
+ int diff, ts;
+#endif
+ struct hip_r1entry *r1;
+ int idx;
+
+ idx = hip_calc_cookie_idx(ip_i, ip_r);
+ _HIP_DEBUG("Calculated index: %d\n", idx);
+ r1 = &hip_r1table[idx];
+
+ /* the code under #if 0 periodically changes the puzzle. It is not included
+ in compilation as there is currently no easy way of signing the R1 packet
+ after having changed its puzzle.
+ */
+#if 0
+ /* generating opaque data */
+
+ do_gettimeofday(&tv);
+
+ /* extract the puzzle */
+ pz = hip_get_param(r1->r1, HIP_PARAM_PUZZLE);
+ if (!pz) {
+ HIP_ERROR("Internal error: Could not find PUZZLE parameter in precreated R1 packet\n");
+ return NULL;
+ }
+
+ ts = pz->opaque[0];
+ ts |= ((int)pz->opaque[1] << 8);
+ //ts |= ((int)pz->opaque[2] << 16);
+
+ if (ts != 0) {
+ /* check if the cookie is too old */
+ diff = (tv.tv_sec & 0xFFFFFF) - ts;
+ if (diff < 0)
+ diff += 0x1000000;
+
+ HIP_DEBUG("Old puzzle still valid\n");
+ if (diff <= HIP_PUZZLE_MAX_LIFETIME)
+ return r1;
+ }
+
+ /* either ts == 0 or diff > HIP_PUZZLE_MAX_LIFETIME */
+ _HIP_DEBUG("Creating new puzzle\n");
+ hip_create_new_puzzle(pz, r1, &tv);
+
+ /* XXX: sign the R1 */
+#endif
+ return r1;
+}
+
+
+/**
+ * hip_solve_puzzle - Solve puzzle.
+ * @puzzle_or_solution: Either a pointer to hip_puzzle or hip_solution structure
+ * @hdr: The incoming R1/I2 packet header.
+ * @mode: Either HIP_VERIFY_PUZZLE of HIP_SOLVE_PUZZLE
+ *
+ * The K and I is read from the @puzzle_or_solution.
+ *
+ * The J that solves the puzzle is returned, or 0 to indicate an error.
+ * NOTE! I don't see why 0 couldn't solve the puzzle too, but since the
+ * odds are 1/2^64 to try 0, I don't see the point in improving this now.
+ */
+uint64_t hip_solve_puzzle(void *puzzle_or_solution, struct hip_common *hdr,
+ int mode)
+{
+ uint64_t mask;
+ uint64_t randval;
+ uint64_t maxtries = 0;
+ uint64_t digest;
+ u8 cookie[48];
+ struct scatterlist sg[2];
+ unsigned int nsg = 2;
+ int err;
+ union {
+ struct hip_puzzle pz;
+ struct hip_solution sl;
+ } *u;
+
+ _HIP_DEBUG("\n");
+ /* pre-create cookie */
+ u = puzzle_or_solution;
+
+ HIP_DEBUG("current hip_cookie_max_k_r1=%d\n",
+ hip_sys_config.hip_cookie_max_k_r1);
+ if (u->pz.K > hip_sys_config.hip_cookie_max_k_r1) {
+ HIP_ERROR("Cookie K %u is higher than we are willing to calculate"
+ " (current max K=%d)\n",
+ u->pz.K, hip_sys_config.hip_cookie_max_k_r1);
+ return 0;
+ }
+
+ mask = hton64((1ULL << u->pz.K) - 1);
+ memcpy(cookie, (u8 *)&(u->pz.I), sizeof(uint64_t));
+
+ if (mode == HIP_VERIFY_PUZZLE) {
+ ipv6_addr_copy((hip_hit_t *)(cookie+8), &hdr->hits);
+ ipv6_addr_copy((hip_hit_t *)(cookie+24), &hdr->hitr);
+ //randval = ntoh64(u->sl.J);
+ randval = u->sl.J;
+ _HIP_DEBUG("u->sl.J: 0x%llx\n", u->sl.J);
+ maxtries = 1;
+ } else if (mode == HIP_SOLVE_PUZZLE) {
+ ipv6_addr_copy((hip_hit_t *)(cookie+8), &hdr->hitr);
+ ipv6_addr_copy((hip_hit_t *)(cookie+24), &hdr->hits);
+ maxtries = 1ULL << (u->pz.K + 2); /* fix */
+ get_random_bytes(&randval, sizeof(u_int64_t));
+ } else {
+ HIP_ERROR("Unknown mode: %d\n", mode);
+ goto out_err;
+ }
+
+ /* pre map the memory region (for SHA) */
+ err = hip_map_virtual_to_pages(sg, &nsg, cookie, 48);
+ if (err || nsg < 1 ) {
+ HIP_ERROR("Error mapping virtual addresses to physical pages\n");
+ return 0; // !ok
+ }
+
+ HIP_DEBUG("K=%u, maxtries (with k+2)=%llu\n", u->pz.K, maxtries);
+ /* while loops should work even if the maxtries is unsigned
+ * if maxtries = 1 ---> while(1 > 0) [maxtries == 0 now]...
+ * the next round while (0 > 0) [maxtries > 0 now]
+ */
+ while(maxtries-- > 0) {
+ u8 sha_digest[HIP_AH_SHA_LEN];
+
+ /* must be 8 */
+ memcpy(cookie + 40, (u8*) &randval, sizeof(uint64_t));
+ hip_build_digest_repeat(impl_sha1, sg, nsg, sha_digest);
+ /* copy the last 8 bytes for checking */
+ memcpy(&digest, sha_digest + 12, 8);
+
+ /* now, in order to be able to do correctly the bitwise
+ * AND-operation we have to remember that little endian
+ * processors will interpret the digest and mask reversely.
+ * digest is the last 64 bits of the sha1-digest.. how that is
+ * ordered in processors registers etc.. does not matter to us.
+ * If the last 64 bits of the sha1-digest is
+ * 0x12345678DEADBEEF, whether we have 0xEFBEADDE78563412
+ * doesn't matter because the mask matters... if the mask is
+ * 0x000000000000FFFF (or in other endianness
+ * 0xFFFF000000000000). Either ways... the result is
+ * 0x000000000000BEEF or 0xEFBE000000000000, which the cpu
+ * interprets as 0xBEEF. The mask is converted to network byte
+ * order (above).
+ */
+ if ((digest & mask) == 0) {
+ HIP_DEBUG("*** Puzzle solved ***: 0x%llx\n",randval);
+ _HIP_HEXDUMP("digest", sha_digest, HIP_AH_SHA_LEN);
+ _HIP_HEXDUMP("cookie", cookie, sizeof(cookie));
+ return randval;
+ }
+
+ /* It seems like the puzzle was not correctly solved */
+ if (mode == HIP_VERIFY_PUZZLE) {
+ HIP_ERROR("Puzzle incorrect\n");
+ return 0;
+ }
+ randval++;
+ }
+
+ out_err:
+ HIP_ERROR("Could not solve the puzzle, no solution found\n");
+ return 0;
+}
+
+
+int hip_init_r1(void)
+{
+ int res=0;
+
+ hip_r1table = kmalloc(sizeof(struct hip_r1entry) * HIP_R1TABLESIZE,
+ GFP_KERNEL);
+ if (!hip_r1table) {
+ HIP_ERROR("Could not allocate memory for R1 table\n");
+ goto err_out;
+ }
+
+ memset(hip_r1table, 0, sizeof(struct hip_r1entry) * HIP_R1TABLESIZE);
+ res = 1;
+ err_out:
+ return res;
+}
+
+int hip_precreate_r1(const struct in6_addr *src_hit)
+{
+ int i=0;
+ struct hip_common *pkt;
+
+ for(i = 0; i < HIP_R1TABLESIZE; i++) {
+ pkt = hip_create_r1(src_hit);
+ if (!pkt) {
+ HIP_ERROR("Unable to precreate R1s\n");
+ goto err_out;
+ }
+ hip_r1table[i].r1 = pkt;
+ HIP_DEBUG("Packet %d created\n",i);
+ }
+
+ return 1;
+
+ err_out:
+ if (hip_r1table) {
+ hip_uninit_r1();
+ hip_r1table = NULL;
+ }
+ return 0;
+}
+
+void hip_uninit_r1(void)
+{
+ int i;
+
+ /* The R1 packet consist of 2 memory blocks. One contains the actual
+ * buffer where the packet is formed, while the other contains
+ * pointers to different TLVs to speed up parsing etc.
+ * The r1->common is the actual buffer, and r1 is the structure
+ * holding only pointers to the TLVs.
+ */
+ if (hip_r1table) {
+ for(i=0; i < HIP_R1TABLESIZE; i++) {
+ if (hip_r1table[i].r1) {
+ kfree(hip_r1table[i].r1);
+ }
+ }
+ kfree(hip_r1table);
+ }
+}
+
+
+/**
+ * hip_get_r1 - Fetch a precreated R1 and return it.
+ * @ip_i: Initiator's IPv6 address
+ * @ip_r: Responder's IPv6 address
+ *
+ */
+struct hip_common *hip_get_r1(struct in6_addr *ip_i, struct in6_addr *ip_r)
+{
+ struct hip_r1entry *r1e;
+
+ r1e = hip_fetch_cookie_entry(ip_i, ip_r);
+ if (r1e == NULL)
+ return NULL;
+
+ return r1e->r1;
+}
+
+int hip_verify_generation(struct in6_addr *ip_i, struct in6_addr *ip_r,
+ uint64_t birthday)
+{
+#if 0
+ uint64_t generation;
+#endif
+ struct hip_r1entry *r1e;
+
+ r1e = hip_fetch_cookie_entry(ip_i, ip_r);
+ if (r1e == NULL)
+ return -ENOENT;
+
+ /* if we some day support changing the puzzle, we could take
+ the generation into account when veifrying packets etc...
+ */
+#if 0
+ generation = ((uint64_t)load_time) << 32 | r1e->generation;
+
+ if (birthday + 1 < generation) {
+ HIP_ERROR("R1 generation too old\n");
+ return -EINVAL;
+ }
+
+ if (birthday > generation) {
+ HIP_ERROR("R1 generation from future\n");
+ return -EINVAL;
+ }
+#endif
+ return 0;
+}
+
+/**
+ * hip_verify_cookie - Verify solution to the puzzle
+ * @ip_i: Initiator's IPv6
+ * @ip_r: Responder's IPv6
+ * @hdr: Received HIP packet
+ * @solution: Solution structure
+ *
+ * First we check that K and I are the same as in the puzzle we sent.
+ * If not, then we check the previous ones (since the puzzle might just
+ * have been expired).
+ *
+ * Returns 1 if puzzle ok, 0 if !ok.
+ */
+int hip_verify_cookie(struct in6_addr *ip_i, struct in6_addr *ip_r,
+ struct hip_common *hdr,
+ struct hip_solution *solution)
+{
+ struct hip_puzzle *puzzle;
+ struct hip_r1entry *result;
+ int res;
+
+ result = hip_fetch_cookie_entry(ip_i, ip_r);
+ if (result == NULL) {
+ HIP_ERROR("No matching entry\n");
+ return 0;
+ }
+
+ puzzle = hip_get_param(result->r1, HIP_PARAM_PUZZLE);
+ if (!puzzle) {
+ HIP_ERROR("Internal error: could not find the cookie\n");
+ return 0;
+ }
+
+ _HIP_HEXDUMP("opaque in solution", solution->opaque,
+ HIP_PUZZLE_OPAQUE_LEN);
+ _HIP_HEXDUMP("opaque in result", result->Copaque,
+ HIP_PUZZLE_OPAQUE_LEN);
+ _HIP_HEXDUMP("opaque in puzzle", puzzle->opaque,
+ HIP_PUZZLE_OPAQUE_LEN);
+
+ if (memcmp(solution->opaque, puzzle->opaque,
+ HIP_PUZZLE_OPAQUE_LEN) != 0) {
+ HIP_ERROR("Received cookie opaque does not match the sent opaque\n");
+ return 0;
+ }
+
+ HIP_DEBUG("Solution's I (0x%llx), sent I (0x%llx)\n",
+ solution->I, puzzle->I);
+
+ _HIP_HEXDUMP("opaque in solution", solution->opaque, 3);
+ _HIP_HEXDUMP("opaque in result", result->Copaque, 3);
+ _HIP_HEXDUMP("opaque in puzzle", puzzle->opaque, 3);
+
+ if (solution->K != puzzle->K) {
+ HIP_INFO("Solution's K (%d) does not match sent K (%d)\n",
+ solution->K, puzzle->K);
+
+ if (solution->K != result->Ck) {
+ HIP_ERROR("Solution's K did not match any sent Ks.\n");
+ return 0;
+ }
+
+ if (solution->I != result->Ci) {
+ HIP_ERROR("Solution's I did not match the sent I\n");
+ return 0;
+ }
+
+ if (memcmp(solution->opaque, result->Copaque,
+ HIP_PUZZLE_OPAQUE_LEN) != 0) {
+ HIP_ERROR("Solution's opaque data does not match sent opaque data\n");
+ return 0;
+ }
+
+ HIP_DEBUG("Received solution to an old puzzle\n");
+
+ res = hip_solve_puzzle(solution, hdr, HIP_VERIFY_PUZZLE);
+ if (!res)
+ HIP_ERROR("Old puzzle incorrectly solved\n");
+ } else {
+ if (solution->I != puzzle->I) {
+ HIP_ERROR("Solution's I did not match the sent I\n");
+ return 0;
+ }
+
+ if (memcmp(solution->opaque, puzzle->opaque, 3) != 0) {
+ HIP_ERROR("Solution's opaque data does not match the opaque data sent\n");
+ return 0;
+ }
+
+ res = hip_solve_puzzle(solution, hdr, HIP_VERIFY_PUZZLE);
+ if (!res)
+ HIP_ERROR("Puzzle incorrectly solved\n");
+ }
+
+ return res;
+}
diff -urN linux-2.6.10/net/ipv6/hip/cookie.h linux-2.6.10-hip/net/ipv6/hip/cookie.h
--- linux-2.6.10/net/ipv6/hip/cookie.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/cookie.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,32 @@
+#ifndef HIP_COOKIE_H
+#define HIP_COOKIE_H
+
+#include
+#include
+#include
+
+struct hip_r1entry {
+ struct hip_common *r1;
+ uint32_t generation;
+ uint64_t Ci;
+ uint8_t Ck;
+ uint8_t Copaque[3];
+};
+
+#define HIP_PUZZLE_MAX_LIFETIME 60 /* in seconds */
+#define HIP_R1TABLESIZE 3 /* precreate only this many R1s */
+#define HIP_DEFAULT_COOKIE_K 10ULL
+
+struct hip_common *hip_get_r1(struct in6_addr *ip_i, struct in6_addr *ip_r);
+int hip_init_r1(void);
+void hip_uninit_r1(void);
+int hip_precreate_r1(const struct in6_addr *src_hit);
+int hip_verify_cookie(struct in6_addr *ip_i, struct in6_addr *ip_r,
+ struct hip_common *hdr,
+ struct hip_solution *cookie);
+uint64_t hip_solve_puzzle(void *puzzle, struct hip_common *hdr, int mode);
+int hip_verify_generation(struct in6_addr *ip_i, struct in6_addr *ip_r,
+ uint64_t birthday);
+
+
+#endif /* HIP_COOKIE_H */
diff -urN linux-2.6.10/net/ipv6/hip/crypto/dh.c linux-2.6.10-hip/net/ipv6/hip/crypto/dh.c
--- linux-2.6.10/net/ipv6/hip/crypto/dh.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/dh.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,304 @@
+#include "dh.h"
+
+struct dh_params_str {
+ const char *prime;
+ unsigned int generator;
+};
+
+//static const char *dh_group_prime[] = {
+static const struct dh_params_str dh_params[] = {
+ /*** group 0 (invalid) ***/
+ {"",0},
+/*** group 1 (384-bit group) from base draft appendix ***/
+ {"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"\
+ "29024E088A67CC74020BBEA63B13B202FFFFFFFFFFFFFFFF",2},
+/*** group 2 (OAKLEY well defined group 1) ****/
+ {"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD12"\
+ "9024E088A67CC74020BBEA63B139B22514A08798E3404DDEF"\
+ "9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E48"\
+ "5B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF",2},
+/*** group 3 (MODP 1536-bit) ***/
+ {"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD12"\
+ "9024E088A67CC74020BBEA63B139B22514A08798E3404DDEF"\
+ "9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E48"\
+ "5B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE38"\
+ "6BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007"\
+ "CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D"\
+ "23DCA3AD961C62F356208552BB9ED529077096966D670C354"\
+ "E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF",2},
+/*** group 4 (MODP 3072-bit) ***/
+ {"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"\
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"\
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"\
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"\
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"\
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"\
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"\
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"\
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"\
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"\
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"\
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"\
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"\
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"\
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"\
+ "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF",2},
+/*** group 5 (MODP 6144-bit) ***/
+ {"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"\
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"\
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"\
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"\
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"\
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"\
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"\
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"\
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"\
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"\
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"\
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"\
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"\
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"\
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"\
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"\
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"\
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"\
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"\
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"\
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"\
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"\
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"\
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"\
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"\
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"\
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"\
+ "6DCC4024FFFFFFFFFFFFFFFF",2},
+/*** group 6 (MODP 8192-bit) ***/
+ {"FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1"\
+ "29024E088A67CC74020BBEA63B139B22514A08798E3404DD"\
+ "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245"\
+ "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED"\
+ "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D"\
+ "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F"\
+ "83655D23DCA3AD961C62F356208552BB9ED529077096966D"\
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B"\
+ "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9"\
+ "DE2BCBF6955817183995497CEA956AE515D2261898FA0510"\
+ "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64"\
+ "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7"\
+ "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B"\
+ "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C"\
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31"\
+ "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7"\
+ "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA"\
+ "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6"\
+ "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED"\
+ "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9"\
+ "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"\
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD"\
+ "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831"\
+ "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B"\
+ "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF"\
+ "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6"\
+ "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3"\
+ "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"\
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328"\
+ "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C"\
+ "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE"\
+ "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4"\
+ "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300"\
+ "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568"\
+ "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"\
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B"\
+ "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A"\
+ "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36"\
+ "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1"\
+ "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92"\
+ "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47"\
+ "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"\
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",2}
+};
+
+
+static
+void _generate_dh_keypair(DH *dh)
+{
+// priv_key = random(group_prime)
+ gcry_mpi_randomize(dh->priv_key, gcry_mpi_get_nbits(dh->p)-1, 0);
+
+// pub_key = group_generator ^ priv_key (mod p)
+ gcry_mpi_powm(dh->pub_key, dh->g, dh->priv_key, dh->p);
+}
+
+static
+void _compute_dh_key(MPI res, MPI peer_key, DH *dh)
+{
+// res = peer_key ^ priv_key (mod p)
+ gcry_mpi_powm(res, peer_key, dh->priv_key, dh->p);
+}
+
+DH *hip_generate_dh_key(int group_id)
+{
+ DH *dh;
+
+ if (group_id < 1 || group_id > HIP_MAX_DH_GROUP_ID) {
+ return NULL;
+ }
+
+ dh = gcry_malloc(sizeof(DH));
+ if (!dh) {
+ log_error("Out of memory error\n");
+ return NULL;
+ }
+
+ memset(dh,0,sizeof(DH));
+
+ dh->g = mpi_alloc(1);
+ dh->pub_key = mpi_alloc(1);
+ dh->priv_key = mpi_alloc(1);
+ if (dh->g == NULL || dh->pub_key == NULL || dh->priv_key == NULL) {
+ log_error("could not allocate MPI\n");
+ goto cleanup;
+ }
+
+
+/* use HEX */
+ if (gcry_mpi_scan(&dh->p,GCRYMPI_FMT_HEX,dh_params[group_id].prime,0) != 0)
+ {
+ log_error("Could not read the group_prime for group_id: %d\n",group_id);
+ goto cleanup;
+ }
+
+
+ _gcry_mpi_set_ui(dh->g, dh_params[group_id].generator);
+
+ _generate_dh_keypair(dh);
+
+ return dh;
+ cleanup:
+ hip_free_dh_structure(dh);
+ return NULL;
+}
+
+int hip_encode_dh_publickey(DH *dh, u8 *out, int outlen)
+{
+ size_t len;
+
+
+ _HIP_HEXDUMP("DH pubkey", dh->pub_key->d, dh->pub_key->nlimbs*4);
+
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, NULL, &len, dh->pub_key) != 0) {
+ log_error("Could not process the public key: %d\n",(int)dh->pub_key);
+ return -EINVAL;
+ }
+
+ _HIP_DEBUG("We need %d bytes for DH key\n",len);
+
+ if (outlen < len) {
+ log_error("Output buffer too small. %d bytes required\n",len);
+ return -EINVAL;
+ }
+
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, out, &len, dh->pub_key) != 0) {
+ log_error("Could not export MPI to the output buffer\n");
+ return -EINVAL;
+ }
+
+ return len;
+}
+
+int hip_gen_dh_shared_key(DH *dh, u8 *peer_key, size_t peer_len, u8 *out, size_t outlen)
+{
+ MPI peer_mpi;
+ MPI shared_key = NULL;
+ size_t plen = peer_len;
+
+ if (dh == NULL) {
+ log_error("No DH context\n");
+ return -EINVAL;
+ }
+
+ if (gcry_mpi_scan(&peer_mpi,GCRYMPI_FMT_USG,peer_key,&plen) != 0) {
+ log_error("Unable to read peer_key\n");
+ return -EINVAL;
+ }
+
+ shared_key = mpi_alloc(1);
+ if (!shared_key) {
+ log_error("No memory for shared_key\n");
+ return -EINVAL;
+ }
+
+ _compute_dh_key(shared_key, peer_mpi, dh);
+
+ // reusing plen
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, NULL, &plen, shared_key) != 0) {
+ log_error("Error parsing shared key\n");
+ return -EINVAL;
+ }
+
+ if (plen > outlen) {
+ log_error("Output buffer too small. %d bytes required\n",plen);
+ return -EINVAL;
+ }
+
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, out, &plen, shared_key) != 0) {
+ log_error("Could not export MPI to the output buffer\n");
+ return -EINVAL;
+ }
+
+ return plen;
+}
+
+
+void hip_free_dh_structure(DH *target)
+{
+ if (target) {
+ if (target->p)
+ mpi_free(target->p);
+ if (target->g)
+ mpi_free(target->g);
+ if (target->pub_key)
+ mpi_free(target->pub_key);
+ if (target->priv_key)
+ mpi_free(target->priv_key);
+ gcry_free(target);
+ }
+}
+
+DH *hip_dh_clone(DH *src)
+{
+ DH *tgt;
+
+ tgt = gcry_malloc(sizeof(DH));
+ if (!tgt)
+ return NULL;
+
+ tgt->p = _gcry_mpi_copy(src->p);
+ if (!tgt->p) {
+ log_error("Cloning error (p)\n");
+ goto cleanup;
+ }
+
+ tgt->g = _gcry_mpi_copy(src->g);
+ if (!tgt->g) {
+ log_error("Cloning error (g)\n");
+ goto cleanup;
+ }
+
+ tgt->pub_key = _gcry_mpi_copy(src->pub_key);
+ if (!tgt->pub_key) {
+ log_error("Cloning error (pub_key)\n");
+ goto cleanup;
+ }
+
+ tgt->priv_key = _gcry_mpi_copy(src->priv_key);
+ if (!tgt->priv_key) {
+ log_error("Cloning error (priv_key)\n");
+ goto cleanup;
+ }
+
+ return tgt;
+ cleanup:
+ hip_free_dh_structure(tgt);
+ return NULL;
+}
diff -urN linux-2.6.10/net/ipv6/hip/crypto/dh.h linux-2.6.10-hip/net/ipv6/hip/crypto/dh.h
--- linux-2.6.10/net/ipv6/hip/crypto/dh.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/dh.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,27 @@
+#ifndef DH_H
+#define DH_H
+
+#include
+#include
+#include
+#include
+
+#include "kernel-interface.h"
+
+typedef struct DH_str {
+ MPI p;
+ MPI g;
+ MPI pub_key;
+ MPI priv_key;
+} DH;
+
+/* this should be consistent with the table length in dh.c */
+#define HIP_MAX_DH_GROUP_ID 7
+
+int hip_gen_dh_shared_key(DH *dh, u8 *peer_key, size_t peer_len, u8 *out, size_t outlen);
+int hip_encode_dh_publickey(DH *dh, u8 *out, int outlen);
+DH *hip_generate_dh_key(int group_id);
+DH *hip_dh_clone(DH *src);
+void hip_free_dh_structure(DH *target);
+
+#endif /* DH_H */
diff -urN linux-2.6.10/net/ipv6/hip/crypto/dsa.c linux-2.6.10-hip/net/ipv6/hip/crypto/dsa.c
--- linux-2.6.10/net/ipv6/hip/crypto/dsa.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/dsa.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,412 @@
+/* dsa.c - DSA signature scheme
+ * Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#include "dsa.h"
+
+typedef struct {
+ MPI p; /* prime */
+ MPI q; /* group order */
+ MPI g; /* group generator */
+ MPI y; /* g^x mod p */
+} DSA_public_key;
+
+
+typedef struct {
+ MPI p; /* prime */
+ MPI q; /* group order */
+ MPI g; /* group generator */
+ MPI y; /* g^x mod p */
+ MPI x; /* secret exponent */
+} DSA_secret_key;
+
+
+static MPI gen_k( MPI q );
+static void sign(MPI r, MPI s, MPI input, DSA_secret_key *skey);
+static int verify(MPI r, MPI s, MPI input, DSA_public_key *pkey);
+
+
+/****************
+ * Generate a random secret exponent k less than q
+ */
+static MPI
+gen_k( MPI q )
+{
+ MPI k = mpi_alloc_secure( mpi_get_nlimbs(q) );
+ unsigned int nbits = mpi_get_nbits(q);
+ unsigned int nbytes = (nbits+7)/8;
+ char *rndbuf = NULL;
+
+ for(;;) {
+ if( !rndbuf || nbits < 32 ) {
+ gcry_free(rndbuf);
+ rndbuf = gcry_random_bytes_secure( (nbits+7)/8,
+ GCRY_STRONG_RANDOM );
+ }
+ else { /* change only some of the higher bits */
+ /* we could imporove this by directly requesting more memory
+ * at the first call to get_random_bytes() and use this the here
+ * maybe it is easier to do this directly in random.c */
+ char *pp = gcry_random_bytes_secure( 4, GCRY_STRONG_RANDOM );
+ memcpy( rndbuf,pp, 4 );
+ gcry_free(pp);
+ }
+ _gcry_mpi_set_buffer( k, rndbuf, nbytes, 0 );
+ if( mpi_test_bit( k, nbits-1 ) )
+ mpi_set_highbit( k, nbits-1 );
+ else {
+ mpi_set_highbit( k, nbits-1 );
+ mpi_clear_bit( k, nbits-1 );
+ }
+
+ if( !(mpi_cmp( k, q ) < 0) ) { /* check: k < q */
+ continue; /* no */
+ }
+ if( !(mpi_cmp_ui( k, 0 ) > 0) ) { /* check: k > 0 */
+ continue; /* no */
+ }
+ break; /* okay */
+ }
+ gcry_free(rndbuf);
+
+ return k;
+}
+
+
+/****************
+ * Make a DSA signature from HASH and put it into r and s.
+ */
+
+static void
+sign(MPI r, MPI s, MPI hash, DSA_secret_key *skey )
+{
+ MPI k;
+ MPI kinv;
+ MPI tmp;
+
+ /* select a random k with 0 < k < q */
+ k = gen_k( skey->q );
+
+ /* r = (a^k mod p) mod q */
+ gcry_mpi_powm( r, skey->g, k, skey->p );
+ mpi_fdiv_r( r, r, skey->q );
+
+ /* kinv = k^(-1) mod q */
+ kinv = mpi_alloc( mpi_get_nlimbs(k) );
+ mpi_invm(kinv, k, skey->q );
+
+ /* s = (kinv * ( hash + x * r)) mod q */
+ tmp = mpi_alloc( mpi_get_nlimbs(skey->p) );
+ mpi_mul( tmp, skey->x, r );
+ mpi_add( tmp, tmp, hash );
+ mpi_mulm( s , kinv, tmp, skey->q );
+
+ mpi_free(k);
+ mpi_free(kinv);
+ mpi_free(tmp);
+}
+
+
+/****************
+ * Returns true if the signature composed from R and S is valid.
+ */
+static int
+verify(MPI r, MPI s, MPI hash, DSA_public_key *pkey )
+{
+ int rc;
+ MPI w, u1, u2, v;
+ MPI base[3];
+ MPI exp[3];
+
+
+ if( !(mpi_cmp_ui( r, 0 ) > 0) ) {
+ log_error("assertion 0 < r failed\n");
+ return 0;
+ }
+ if( !(mpi_cmp( r, pkey->q ) < 0) ) {
+ log_error("assertion r < q failed\n");
+ return 0; /* assertion 0 < r < q failed */
+ }
+ if( !(mpi_cmp_ui( s, 0 ) > 0) ) {
+ log_error("assertion 0 < s failed\n");
+ return 0;
+ }
+
+ if( !(mpi_cmp( s, pkey->q ) < 0) ) {
+ log_error("assertion s < q failed\n");
+ return 0;
+ }
+
+ w = mpi_alloc( mpi_get_nlimbs(pkey->q) );
+ u1 = mpi_alloc( mpi_get_nlimbs(pkey->q) );
+ u2 = mpi_alloc( mpi_get_nlimbs(pkey->q) );
+ v = mpi_alloc( mpi_get_nlimbs(pkey->p) );
+
+ /* w = s^(-1) mod q */
+ mpi_invm( w, s, pkey->q );
+
+ /* u1 = (hash * w) mod q */
+ mpi_mulm( u1, hash, w, pkey->q );
+
+ /* u2 = r * w mod q */
+ mpi_mulm( u2, r, w, pkey->q );
+
+ /* v = g^u1 * y^u2 mod p mod q */
+ base[0] = pkey->g; exp[0] = u1;
+ base[1] = pkey->y; exp[1] = u2;
+ base[2] = NULL; exp[2] = NULL;
+ mpi_mulpowm( v, base, exp, pkey->p );
+ mpi_fdiv_r( v, v, pkey->q );
+
+ rc = !mpi_cmp( v, r );
+
+ mpi_free(w);
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(v);
+ return rc;
+}
+
+
+
+int hip_dsa_sign(u8 *digest, u8 *private_key, u8 *signature)
+{
+ DSA_secret_key sk = {0};
+ MPI r=NULL,s=NULL,m;
+ int pos = 0;
+ int err = -EINVAL;
+ size_t tmp;
+ u8 t;
+
+ t = *(private_key+pos);
+ if (t > 8) {
+ log_error("Illegal DSA key\n");
+ goto cleanup;
+ }
+ pos++;
+
+ /* XXX: add checking of tmp variable after the scanning to see that
+ the required amount of bytes was actually read. */
+ tmp = 20;
+
+ _HIP_HEXDUMP("key-dump",private_key+pos,20);
+
+ if (gcry_mpi_scan(&sk.q,GCRYMPI_FMT_USG,private_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA private_key component Q\n");
+ goto cleanup;
+ }
+ pos += 20;
+
+ tmp = 64+8*t;
+ if (gcry_mpi_scan(&sk.p,GCRYMPI_FMT_USG,private_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA private_key component P\n");
+ goto cleanup;
+ }
+ pos += 64+8*t;
+
+ tmp = 64+8*t;
+ if (gcry_mpi_scan(&sk.g,GCRYMPI_FMT_USG,private_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA private_key component G\n");
+ goto cleanup;
+ }
+ pos += 64+8*t;
+
+ tmp = 64+8*t;
+ if (gcry_mpi_scan(&sk.y,GCRYMPI_FMT_USG,private_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA private_key component Y\n");
+ goto cleanup;
+ }
+ pos += 64+8*t;
+
+ tmp = 20;
+ if (gcry_mpi_scan(&sk.x,GCRYMPI_FMT_USG,private_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA private_key component X\n");
+ goto cleanup;
+ }
+
+ /* p,q,g,x,y retrieved */
+
+ if (gcry_mpi_scan(&m,GCRYMPI_FMT_USG,digest,&tmp) != 0) {
+ log_error("Error parsing DSA digest\n");
+ goto cleanup;
+ }
+
+
+ r = mpi_alloc(mpi_get_nlimbs(sk.p));
+ s = mpi_alloc(mpi_get_nlimbs(sk.p));
+
+ _HIP_DEBUG("Using following numbers for signing:\n");
+ _HIP_HEXDUMP("Q", sk.q->d, sk.q->nlimbs*4);
+ _HIP_HEXDUMP("P", sk.p->d, sk.p->nlimbs*4);
+ _HIP_HEXDUMP("G", sk.g->d, sk.g->nlimbs*4);
+ _HIP_HEXDUMP("Y", sk.y->d, sk.y->nlimbs*4);
+ _HIP_HEXDUMP("X", sk.x->d, sk.x->nlimbs*4);
+
+ sign(r,s,m,&sk);
+
+ _HIP_HEXDUMP("R",r->d,r->nlimbs*4);
+ _HIP_HEXDUMP("S",s->d,s->nlimbs*4);
+
+ /* encode now */
+
+ pos = 0;
+ *signature = t;
+ pos++;
+
+ tmp = 20;
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, signature+1,
+ &tmp, r) != 0) {
+ log_error("Error encoding DSA signature component R\n");
+ goto cleanup;
+ }
+ pos += tmp;
+
+ tmp = 20;
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, signature+21,
+ &tmp, s) != 0) {
+ log_error("Error encoding DSA signature component s\n");
+ goto cleanup;
+ }
+
+ err = 0;
+ cleanup:
+ if (s)
+ mpi_free(s);
+ if (r)
+ mpi_free(r);
+ if (sk.x)
+ mpi_free(sk.x);
+ if (sk.y)
+ mpi_free(sk.y);
+ if (sk.g)
+ mpi_free(sk.g);
+ if (sk.p)
+ mpi_free(sk.p);
+ if (sk.q)
+ mpi_free(sk.q);
+
+ return err;
+
+}
+
+/* return 0: ok, 1: verify error, -EINVAL: something sucked */
+int hip_dsa_verify(u8 *digest, u8 *public_key, u8 *signature)
+{
+ DSA_public_key sk;
+ MPI r=NULL,s=NULL,m;
+ int pos = 0;
+ int err = -EINVAL;
+ size_t tmp;
+ u8 t;
+
+ t = *public_key;
+
+ if (t > 8) {
+ log_error("DSA public key invalid\n");
+ return EINVAL;
+ }
+ pos++;
+
+ tmp = 20;
+ if (gcry_mpi_scan(&sk.q,GCRYMPI_FMT_USG,public_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA public_key component Q\n");
+ goto cleanup;
+ }
+ pos += 20;
+
+ tmp = 64+8*t;
+ if (gcry_mpi_scan(&sk.p,GCRYMPI_FMT_USG,public_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA public_key component P\n");
+ goto cleanup;
+ }
+ pos += 64+8*t;
+
+ tmp = 64+8*t;
+ if (gcry_mpi_scan(&sk.g,GCRYMPI_FMT_USG,public_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA public_key component G\n");
+ goto cleanup;
+ }
+ pos += 64+8*t;
+
+ tmp = 64+8*t;
+ if (gcry_mpi_scan(&sk.y,GCRYMPI_FMT_USG,public_key+pos,&tmp) != 0) {
+ log_error("Error parsing DSA public_key component Y\n");
+ goto cleanup;
+ }
+
+ /* p,q,g,y retrieved */
+
+ tmp = 20;
+ if (gcry_mpi_scan(&m,GCRYMPI_FMT_USG,digest,&tmp) != 0) {
+ log_error("Error parsing DSA digest\n");
+ goto cleanup;
+ }
+
+ tmp = 20;
+ if (gcry_mpi_scan(&r,GCRYMPI_FMT_USG,signature+1,&tmp) != 0) {
+ log_error("Error parsing DSA signature component R\n");
+ goto cleanup;
+ }
+
+
+ tmp = 20;
+ if (gcry_mpi_scan(&s,GCRYMPI_FMT_USG,signature+21,&tmp) != 0) {
+ log_error("Error parsing DSA signature component S\n");
+ goto cleanup;
+ }
+
+
+ _HIP_DEBUG("Using following numbers for signing:\n");
+ _HIP_HEXDUMP("Q", sk.q->d, sk.q->nlimbs*4);
+ _HIP_HEXDUMP("P", sk.p->d, sk.p->nlimbs*4);
+ _HIP_HEXDUMP("G", sk.g->d, sk.g->nlimbs*4);
+ _HIP_HEXDUMP("Y", sk.y->d, sk.y->nlimbs*4);
+ _HIP_HEXDUMP("R", r->d, r->nlimbs*4);
+ _HIP_HEXDUMP("S", s->d, s->nlimbs*4);
+
+ if (verify(r,s,m,&sk))
+ err = 0;
+ else
+ err = 1;
+
+ cleanup:
+ if (s)
+ mpi_free(s);
+ if (r)
+ mpi_free(r);
+ if (sk.y)
+ mpi_free(sk.y);
+ if (sk.g)
+ mpi_free(sk.g);
+ if (sk.p)
+ mpi_free(sk.p);
+ if (sk.q)
+ mpi_free(sk.q);
+
+ return err;
+}
+
+unsigned int
+_gcry_dsa_get_nbits( int algo, MPI *pkey )
+{
+ algo = 1;
+ return mpi_get_nbits( pkey[0] );
+}
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/dsa.h linux-2.6.10-hip/net/ipv6/hip/crypto/dsa.h
--- linux-2.6.10/net/ipv6/hip/crypto/dsa.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/dsa.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,28 @@
+/* dsa.h - DSA signature scheme
+ * Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 G10_DSA_H
+#define G10_DSA_H
+
+#include "kernel-interface.h"
+
+int hip_dsa_sign(u8 *digest, u8 *private_key, u8 *signature);
+int hip_dsa_verify(u8 *digest, u8 *public_key, u8 *signature);
+
+#endif /*G10_DSA_H*/
diff -urN linux-2.6.10/net/ipv6/hip/crypto/gcrypt.h linux-2.6.10-hip/net/ipv6/hip/crypto/gcrypt.h
--- linux-2.6.10/net/ipv6/hip/crypto/gcrypt.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/gcrypt.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,953 @@
+/* gcrypt.h - GNU cryptographic library interface
+ * Copyright (C) 1998,1999,2000,2001,2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 _GCRYPT_H
+#define _GCRYPT_H
+
+#ifdef __cplusplus
+extern "C" {
+#if 0 /* keep Emacsens's auto-indent happy */
+}
+#endif
+#endif
+
+/* The version of this header should match the one of the library It
+ should not be used by a program because gcry_check_version() should
+ reurn the same version. The purpose of this macro is to let
+ autoconf (using the AM_PATH_GCRYPT macro) check that this header
+ matches the installed library. Note: Do not edit the next line as
+ configure may fix the string here. */
+#define GCRYPT_VERSION "1.1.12"
+
+/* Internal: We can't use the convenience macros for the multi
+ precision integer functions when building this library. */
+#ifdef _GCRYPT_IN_LIBGCRYPT
+# ifndef GCRYPT_NO_MPI_MACROS
+# define GCRYPT_NO_MPI_MACROS 1
+# endif
+#endif
+
+/* We want to use gcc attributes when possible. Warning: Don't use
+ these macros in your progranms: As indicated by the leading
+ underscore they are subject to change without notice. */
+
+/* Actually we choose not to use those in kernel :) */
+#define _GCRY_GCC_ATTR_PURE
+#define _GCRY_GCC_ATTR_MALLOC
+
+struct gcry_mpi {
+ int alloced; /* array size (# of allocated limbs) */
+ int nlimbs; /* number of valid limbs */
+ int sign; /* indicates a negative number and is used for opaque
+ * MPIs to store the length */
+ unsigned flags; /* bit 0: array must be allocated in secure memory space */
+ /* bit 2: the limb is a pointer to some m_alloced data */
+ mpi_limb_t *d; /* array with the limbs */
+};
+
+typedef struct gcry_mpi *MPI;
+
+
+/* The data object used to hold a multi precision integer. GcryMPI is
+ the preferred one. */
+struct gcry_mpi;
+typedef struct gcry_mpi *GCRY_MPI;
+typedef struct gcry_mpi *GcryMPI;
+
+
+/* Error handling etc. */
+
+/* The error numbers used by Libgcrypt. */
+/* FIXME: We should use the same values as they were used in GnuPG
+ 1.0. gpg --status-fd may print some of these values. */
+enum
+ {
+ GCRYERR_SUCCESS = 0, /* "no error" (this is guaranteed to be 0) */
+ GCRYERR_GENERAL = 1, /* catch all the other errors code */
+
+ GCRYERR_INV_PK_ALGO = 4, /* invalid public key algorithm */
+ GCRYERR_INV_MD_ALGO = 5, /* invalid message digest algorithm */
+ GCRYERR_BAD_PUBLIC_KEY = 6, /* Bad public key */
+ GCRYERR_BAD_SECRET_KEY = 7, /* Bad secret key */
+ GCRYERR_BAD_SIGNATURE = 8, /* Bad signature */
+
+ GCRYERR_INV_CIPHER_ALGO = 12, /* invalid cipher algorithm */
+ GCRYERR_BAD_MPI = 30, /* problem with an MPI's value*/
+ GCRYERR_WRONG_PK_ALGO = 41, /* wrong public key algorithm */
+ GCRYERR_WEAK_KEY = 43, /* weak encryption key */
+ GCRYERR_INV_KEYLEN = 44, /* invalid length of a key*/
+ GCRYERR_INV_ARG = 45, /* invalid argument */
+ GCRYERR_SELFTEST = 50, /* selftest failed */
+
+ /* error codes not used in GnuPG 1.0 */
+ GCRYERR_INV_OP = 61, /* invalid operation code or ctl command */
+ GCRYERR_NO_MEM = 62, /* out of core */
+ GCRYERR_INTERNAL = 63, /* internal error */
+ GCRYERR_EOF = 64, /* (-1) is remapped to this value */
+ GCRYERR_INV_OBJ = 65, /* an object is not valid */
+ GCRYERR_TOO_SHORT = 66, /* provided buffer/object too short */
+ GCRYERR_TOO_LARGE = 67, /* object is too large */
+ GCRYERR_NO_OBJ = 68, /* Missing item in an object */
+ GCRYERR_NOT_IMPL = 69, /* Not implemented */
+ GCRYERR_CONFLICT = 70, /* conflicting use of functions/values */
+ GCRYERR_INV_CIPHER_MODE = 71, /* invalid/unsupported cipher mode */
+ GCRYERR_INV_FLAG = 72, /* invalid flag */
+
+ /* error codes pertaining to S-expressions */
+ GCRYERR_SEXP_INV_LEN_SPEC = 201,
+ GCRYERR_SEXP_STRING_TOO_LONG = 202,
+ GCRYERR_SEXP_UNMATCHED_PAREN = 203,
+ GCRYERR_SEXP_NOT_CANONICAL = 204,
+ GCRYERR_SEXP_BAD_CHARACTER = 205,
+ GCRYERR_SEXP_BAD_QUOTATION = 206,/* or invalid hex or octal value */
+ GCRYERR_SEXP_ZERO_PREFIX = 207,/* first character of a length is 0 */
+ GCRYERR_SEXP_NESTED_DH = 208,/* nested display hints */
+ GCRYERR_SEXP_UNMATCHED_DH = 209,/* unmatched display hint */
+ GCRYERR_SEXP_UNEXPECTED_PUNC = 210,/* unexpected reserved punctuation */
+ GCRYERR_SEXP_BAD_HEX_CHAR = 211,
+ GCRYERR_SEXP_ODD_HEX_NUMBERS = 212,
+ GCRYERR_SEXP_BAD_OCT_CHAR = 213
+ };
+
+/* Check that the library fulfills the version requirement. */
+const char *gcry_check_version (const char *req_version);
+
+/* Return the error number for the last failed function call. */
+int gcry_errno(void) _GCRY_GCC_ATTR_PURE;
+
+/* Map an error number to a string. */
+const char *gcry_strerror (int ec);
+
+/* Codes used with the gcry_control function. */
+enum gcry_ctl_cmds
+ {
+ GCRYCTL_SET_KEY = 1,
+ GCRYCTL_SET_IV = 2,
+ GCRYCTL_CFB_SYNC = 3,
+ GCRYCTL_RESET = 4, /* e.g. for MDs */
+ GCRYCTL_FINALIZE = 5,
+ GCRYCTL_GET_KEYLEN = 6,
+ GCRYCTL_GET_BLKLEN = 7,
+ GCRYCTL_TEST_ALGO = 8,
+ GCRYCTL_IS_SECURE = 9,
+ GCRYCTL_GET_ASNOID = 10,
+ GCRYCTL_ENABLE_ALGO = 11,
+ GCRYCTL_DISABLE_ALGO = 12,
+ GCRYCTL_DUMP_RANDOM_STATS = 13,
+ GCRYCTL_DUMP_SECMEM_STATS = 14,
+ GCRYCTL_GET_ALGO_NPKEY = 15,
+ GCRYCTL_GET_ALGO_NSKEY = 16,
+ GCRYCTL_GET_ALGO_NSIGN = 17,
+ GCRYCTL_GET_ALGO_NENCR = 18,
+ GCRYCTL_SET_VERBOSITY = 19,
+ GCRYCTL_SET_DEBUG_FLAGS = 20,
+ GCRYCTL_CLEAR_DEBUG_FLAGS = 21,
+ GCRYCTL_USE_SECURE_RNDPOOL= 22,
+ GCRYCTL_DUMP_MEMORY_STATS = 23,
+ GCRYCTL_INIT_SECMEM = 24,
+ GCRYCTL_TERM_SECMEM = 25,
+ GCRYCTL_DISABLE_SECMEM_WARN = 27,
+ GCRYCTL_SUSPEND_SECMEM_WARN = 28,
+ GCRYCTL_RESUME_SECMEM_WARN = 29,
+ GCRYCTL_DROP_PRIVS = 30,
+ GCRYCTL_ENABLE_M_GUARD = 31,
+ GCRYCTL_START_DUMP = 32,
+ GCRYCTL_STOP_DUMP = 33,
+ GCRYCTL_GET_ALGO_USAGE = 34,
+ GCRYCTL_IS_ALGO_ENABLED = 35,
+ GCRYCTL_DISABLE_INTERNAL_LOCKING = 36,
+ GCRYCTL_DISABLE_SECMEM = 37,
+ GCRYCTL_INITIALIZATION_FINISHED = 38,
+ GCRYCTL_INITIALIZATION_FINISHED_P = 39,
+ GCRYCTL_ANY_INITIALIZATION_P = 40,
+ GCRYCTL_SET_CBC_CTS = 41
+ };
+
+/* Perform various operations defined by CMD. */
+int gcry_control (enum gcry_ctl_cmds CMD, ...);
+
+
+
+/* S-expression management. */
+
+/* The object to represent an S-expression as used with the
+ public key functions. GcrySexp is the preferrred form. */
+struct gcry_sexp;
+typedef struct gcry_sexp *GCRY_SEXP;
+typedef struct gcry_sexp *GcrySexp;
+
+/* The possible values for the S-expression format. */
+enum gcry_sexp_format {
+ GCRYSEXP_FMT_DEFAULT = 0,
+ GCRYSEXP_FMT_CANON = 1,
+ GCRYSEXP_FMT_BASE64 = 2,
+ GCRYSEXP_FMT_ADVANCED = 3
+};
+
+/* Create an new S-expression object from BUFFER of size LENGTH and
+ return it in RETSEXP. With AUTODETECT set to 0 the data in BUFFER
+ is expected to be in canonized format */
+int gcry_sexp_new (GcrySexp *retsexp, const void *buffer, size_t length,
+ int autodetect);
+
+/* Same as gcry_sexp_new but allows to pass a FREEFNC which has the
+ effect to transfer ownership of BUFFER to the created object. */
+int gcry_sexp_create (GcrySexp *retsexp, void *buffer, size_t length,
+ int autodetect, void (*freefnc)(void*) );
+
+/* Scan BUFFER and return a new S-expression object in RETSEXP. This
+ function expects a printf like string in BUFFER. */
+int gcry_sexp_sscan (GcrySexp *retsexp, size_t *erroff,
+ const char *buffer, size_t length );
+
+/* Same as gcry_sexp_sscan but expects a string in FORMAT and can thus
+ only be used for certain encodings. */
+int gcry_sexp_build (GcrySexp *retsexp, size_t *erroff,
+ const char *format, ... );
+
+/* Release the S-expression object SEXP */
+void gcry_sexp_release (GcrySexp sexp);
+
+/* Calculate the length of an canonized S-expresion in BUFFER and
+ check for a valid encoding. */
+size_t gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
+ size_t *erroff, int *errcode);
+
+/* Copies the S-expression object SEXP into BUFFER using the format
+ specified in MODE. */
+size_t gcry_sexp_sprint (GCRY_SEXP sexp, int mode, char *buffer,
+ size_t maxlength );
+
+void gcry_sexp_dump( const GCRY_SEXP a );
+GCRY_SEXP gcry_sexp_cons( const GCRY_SEXP a, const GCRY_SEXP b );
+GCRY_SEXP gcry_sexp_alist( const GCRY_SEXP *array );
+GCRY_SEXP gcry_sexp_vlist( const GCRY_SEXP a, ... );
+GCRY_SEXP gcry_sexp_append( const GCRY_SEXP a, const GCRY_SEXP n );
+GCRY_SEXP gcry_sexp_prepend( const GCRY_SEXP a, const GCRY_SEXP n );
+GCRY_SEXP gcry_sexp_find_token( GCRY_SEXP list,
+ const char *tok, size_t toklen );
+int gcry_sexp_length( const GCRY_SEXP list );
+GCRY_SEXP gcry_sexp_nth( const GCRY_SEXP list, int number );
+GCRY_SEXP gcry_sexp_car( const GCRY_SEXP list );
+GCRY_SEXP gcry_sexp_cdr( const GCRY_SEXP list );
+GCRY_SEXP gcry_sexp_cadr( const GCRY_SEXP list );
+const char *gcry_sexp_nth_data( const GCRY_SEXP list, int number,
+ size_t *datalen );
+GCRY_MPI gcry_sexp_nth_mpi( GCRY_SEXP list, int number, int mpifmt );
+
+
+
+/*******************************************
+ * *
+ * multi precision integer functions *
+ * *
+ *******************************************/
+
+/* Different formats of external big integer representation. */
+enum gcry_mpi_format
+ {
+ GCRYMPI_FMT_NONE= 0,
+ GCRYMPI_FMT_STD = 1, /* twos complement stored without length */
+ GCRYMPI_FMT_PGP = 2, /* As used by OpenPGP (only defined as unsigned)*/
+ GCRYMPI_FMT_SSH = 3, /* As used by SSH (same as 1 but with length)*/
+ GCRYMPI_FMT_HEX = 4, /* hex format */
+ GCRYMPI_FMT_USG = 5 /* like STD but this is an unsigned one */
+ };
+
+/* Flags used for creating big integers. */
+enum gcry_mpi_flag
+ {
+ GCRYMPI_FLAG_SECURE = 1, /* Allocate the number in "secure" memory. */
+ GCRYMPI_FLAG_OPAQUE = 2 /* The number is not a real one but just a
+ way to store some bytes. This is
+ useful for encrypted big integers. */
+ };
+
+
+/* Allocate a new big integer object, initialize it with 0 and
+ initially allocate memory for a number of at least NBITS. */
+GcryMPI gcry_mpi_new (unsigned int nbits);
+
+/* Same as gcry_mpi_new() but allocate in "secure" memory. */
+GcryMPI gcry_mpi_snew (unsigned int nbits);
+
+/* Release the number A and free all associated resources. */
+void gcry_mpi_release (GcryMPI a);
+
+/* Create a new number with the same value as A. */
+GcryMPI gcry_mpi_copy (const GcryMPI a);
+
+/* Store the big integer value U in W. */
+GcryMPI gcry_mpi_set (GcryMPI w, const GcryMPI u);
+
+/* Store the unsigned integer value U in W. */
+GcryMPI gcry_mpi_set_ui (GcryMPI w, unsigned long u);
+
+/* Swap the values of A and B. */
+void gcry_mpi_swap (GcryMPI a, GcryMPI b);
+
+/* Compare the big integer number U and V returning 0 for equality, a
+ positive value for U > V and a negative for U < V. */
+int gcry_mpi_cmp (const GcryMPI u, const GcryMPI v);
+
+/* Compare the big integer number U with the unsigned integer V
+ returning 0 for equality, a positive value for U > V and a negative
+ for U < V. */
+int gcry_mpi_cmp_ui (const GcryMPI u, unsigned long v);
+
+/* Convert the external representation of an integer stored in BUFFER
+ with a size of (*NBYTES) in a newly create MPI returned in RET_MPI.
+ For certain formats a length is not required and may be passed as
+ NULL. After a successful operation NBYTES received the number of
+ bytes actually scanned. */
+int gcry_mpi_scan (GcryMPI *ret_mpi, enum gcry_mpi_format format,
+ const char *buffer, size_t *nbytes);
+
+/* Convert the big integer A into the external representation
+ described by FORMAT and store it in the provided BUFFER which has
+ the size (*NBYTES). NBYTES receives the actual length of the
+ external representation. */
+int gcry_mpi_print (enum gcry_mpi_format format,
+ char *buffer, size_t *nbytes, const GcryMPI a);
+
+/* Convert the big integer A int the external representation desribed
+ by FORMAT and store it in a newly allocated buffer which address
+ will be put into BUFFER. NBYTES receives the actual lengths of the
+ external representation. */
+int gcry_mpi_aprint (enum gcry_mpi_format format,
+ void **buffer, size_t *nbytes, const GcryMPI a);
+
+/* W = U + V. */
+void gcry_mpi_add (GcryMPI w, GcryMPI u, GcryMPI v);
+
+/* W = U + V. V is an unsigned integer. */
+void gcry_mpi_add_ui (GcryMPI w, GcryMPI u, unsigned long v);
+
+/* W = U + V mod M. */
+void gcry_mpi_addm (GcryMPI w, GcryMPI u, GcryMPI v, GcryMPI m);
+
+/* W = U - V. */
+void gcry_mpi_sub (GcryMPI w, GcryMPI u, GcryMPI v);
+
+/* W = U - V. V is an unsigned integer. */
+void gcry_mpi_sub_ui (GcryMPI w, GcryMPI u, unsigned long v );
+
+/* W = U - V mod M */
+void gcry_mpi_subm (GcryMPI w, GcryMPI u, GcryMPI v, GcryMPI m);
+
+/* W = U * V. */
+void gcry_mpi_mul (GcryMPI w, GcryMPI u, GcryMPI v);
+
+/* W = U * V. V is an unsigned integer. */
+void gcry_mpi_mul_ui (GcryMPI w, GcryMPI u, unsigned long v );
+
+/* W = U * V mod M. */
+void gcry_mpi_mulm (GcryMPI w, GcryMPI u, GcryMPI v, GcryMPI m);
+
+/* W = U * (2 ^ CNT). */
+void gcry_mpi_mul_2exp (GcryMPI w, GcryMPI u, unsigned long cnt);
+
+/* Q = DIVIDEND / DIVISOR, R = DIVIDEND % DIVISOR,
+ Q or R may be passed as NULL. ROUND should be negative or 0. */
+void gcry_mpi_div (GcryMPI q, GcryMPI r,
+ GcryMPI dividend, GcryMPI divisor, int round);
+
+/* R = DIVIDEND % DIVISOR */
+void gcry_mpi_mod (GcryMPI r, GcryMPI dividend, GcryMPI divisor);
+
+/* W = B ^ E mod M. */
+void gcry_mpi_powm (GcryMPI w,
+ const GcryMPI b, const GcryMPI e, const GcryMPI m);
+
+/* Set G to the greatest common divisor of A and B.
+ Return true if the G is 1. */
+int gcry_mpi_gcd (GcryMPI g, GcryMPI a, GcryMPI b);
+
+/* Set X to the multiplicative inverse of A mod M.
+ Return true if the value exists. */
+int gcry_mpi_invm (GcryMPI x, GcryMPI a, GcryMPI m);
+
+
+/* Return the number of bits required to represent A. */
+unsigned int gcry_mpi_get_nbits (GcryMPI a);
+
+/* Return true when bit number N (counting from 0) is set in A. */
+int gcry_mpi_test_bit (GcryMPI a, unsigned int n);
+
+/* Set bit number N in A. */
+void gcry_mpi_set_bit (GcryMPI a, unsigned int n);
+
+/* Clear bit number N in A. */
+void gcry_mpi_clear_bit (GcryMPI a, unsigned int n);
+
+/* Set bit number N in A and clear all bits greater than N. */
+void gcry_mpi_set_highbit (GcryMPI a, unsigned int n);
+
+/* Clear bit number N in A and all bits greater than N. */
+void gcry_mpi_clear_highbit (GcryMPI a, unsigned int n);
+
+/* Shift the value of A by N bits to the right and store the result in X. */
+void gcry_mpi_rshift (GcryMPI x, GcryMPI a, unsigned int n);
+
+/* Store NBITS of the value P points to in A and mark A as an opaque
+ value. */
+GcryMPI gcry_mpi_set_opaque (GcryMPI a, void *p, unsigned int nbits);
+
+/* creturn a pointer to an opaque value stored in A and return its
+ size in NBITS. Note that the returned pointer is still owned by A
+ and that the function should never be used for an non-opaque
+ MPI. */
+void *gcry_mpi_get_opaque (GcryMPI a, unsigned int *nbits);
+
+/* Set the FLAG for the big integer A. Currently only the flag
+ GCRYMPI_FLAG_SECURE is allowed to convert A into an big intger
+ stored in "secure" memory. */
+void gcry_mpi_set_flag (GcryMPI a, enum gcry_mpi_flag flag);
+
+/* Clear FLAG for the big integer A. Note that this function is
+ currently useless as no flags are allowed. */
+void gcry_mpi_clear_flag (GcryMPI a, enum gcry_mpi_flag flag);
+
+/* Return true when the FLAG is set for A. */
+int gcry_mpi_get_flag (GcryMPI a, enum gcry_mpi_flag flag);
+
+/* Unless the GCRYPT_NO_MPI_MACROS is used, provide a couple of
+ convenience macors for the big integer functions. */
+#ifndef GCRYPT_NO_MPI_MACROS
+#define mpi_new(n) gcry_mpi_new( (n) )
+#define mpi_secure_new( n ) gcry_mpi_snew( (n) )
+#define mpi_release( a ) do { gcry_mpi_release( (a) ); \
+ (a) = NULL; } while(0)
+#define mpi_copy( a ) gcry_mpi_copy( (a) )
+#define mpi_set( w, u) gcry_mpi_set( (w), (u) )
+#define mpi_set_ui( w, u) gcry_mpi_set_ui( (w), (u) )
+#define mpi_cmp( u, v ) gcry_mpi_cmp( (u), (v) )
+#define mpi_cmp_ui( u, v ) gcry_mpi_cmp_ui( (u), (v) )
+
+#define mpi_add_ui(w,u,v) gcry_mpi_add_ui((w),(u),(v))
+#define mpi_add(w,u,v) gcry_mpi_add ((w),(u),(v))
+#define mpi_addm(w,u,v,m) gcry_mpi_addm ((w),(u),(v),(m))
+#define mpi_sub_ui(w,u,v) gcry_mpi_sub_ui ((w),(u),(v))
+#define mpi_sub(w,u,v) gcry_mpi_sub ((w),(u),(v))
+#define mpi_subm(w,u,v,m) gcry_mpi_subm ((w),(u),(v),(m))
+#define mpi_mul_ui(w,u,v) gcry_mpi_mul_ui ((w),(u),(v))
+#define mpi_mul_2exp(w,u,v) gcry_mpi_mul_2exp ((w),(u),(v))
+#define mpi_mul(w,u,v) gcry_mpi_mul ((w),(u),(v))
+#define mpi_mulm(w,u,v,m) gcry_mpi_mulm ((w),(u),(v),(m))
+#define mpi_powm(w,b,e,m) gcry_mpi_powm ( (w), (b), (e), (m) )
+#define mpi_tdiv(q,r,a,m) gcry_mpi_div ( (q), (r), (a), (m), 0)
+#define mpi_fdiv(q,r,a,m) gcry_mpi_div ( (q), (r), (a), (m), -1)
+#define mpi_mod(r,a,m) gcry_mpi_mod ((r), (a), (m))
+#define mpi_gcd(g,a,b) gcry_mpi_gcd ( (g), (a), (b) )
+#define mpi_invm(g,a,b) gcry_mpi_invm ( (g), (a), (b) )
+
+#define mpi_get_nbits(a) gcry_mpi_get_nbits ((a))
+#define mpi_test_bit(a,b) gcry_mpi_test_bit ((a),(b))
+#define mpi_set_bit(a,b) gcry_mpi_set_bit ((a),(b))
+#define mpi_set_highbit(a,b) gcry_mpi_set_highbit ((a),(b))
+#define mpi_clear_bit(a,b) gcry_mpi_clear_bit ((a),(b))
+#define mpi_clear_highbit(a,b) gcry_mpi_clear_highbit ((a),(b))
+#define mpi_rshift(a,b,c) gcry_mpi_rshift ((a),(b),(c))
+
+#define mpi_set_opaque(a,b,c) gcry_mpi_set_opaque( (a), (b), (c) )
+#define mpi_get_opaque(a,b) gcry_mpi_get_opaque( (a), (b) )
+#endif /* GCRYPT_NO_MPI_MACROS */
+
+
+
+/************************************
+ * *
+ * symmetric cipher functions *
+ * *
+ ************************************/
+
+/* The data object used to hold a handle to an encryption opject.
+ GcryCipherHd is the preferred one. */
+struct gcry_cipher_handle;
+typedef struct gcry_cipher_handle *GCRY_CIPHER_HD;
+typedef struct gcry_cipher_handle *GcryCipherHd;
+
+/* All symmetric encryption algorithms are identified by their IDs.
+ More IDs may be registered at runtime. */
+enum gcry_cipher_algos
+ {
+ GCRY_CIPHER_NONE = 0,
+ GCRY_CIPHER_IDEA = 1,
+ GCRY_CIPHER_3DES = 2,
+ GCRY_CIPHER_CAST5 = 3,
+ GCRY_CIPHER_BLOWFISH = 4,
+ GCRY_CIPHER_SAFER_SK128 = 5,
+ GCRY_CIPHER_DES_SK = 6,
+ GCRY_CIPHER_AES = 7,
+ GCRY_CIPHER_AES192 = 8,
+ GCRY_CIPHER_AES256 = 9,
+ GCRY_CIPHER_TWOFISH = 10,
+ /* other cipher numbers are above 300 for OpenPGP reasons. */
+ GCRY_CIPHER_ARCFOUR = 301, /* fully compatible with RSA's RC4 (tm). */
+ GCRY_CIPHER_DES = 302 /* Yes, this is single key 56 bit DES. */
+ };
+
+/* The Rijndael algorithm is basically AES, so provide some macros. */
+#define GCRY_CIPHER_AES128 GCRY_CIPHER_AES
+#define GCRY_CIPHER_RIJNDAEL GCRY_CIPHER_AES
+#define GCRY_CIPHER_RIJNDAEL128 GCRY_CIPHER_AES128
+#define GCRY_CIPHER_RIJNDAEL192 GCRY_CIPHER_AES192
+#define GCRY_CIPHER_RIJNDAEL256 GCRY_CIPHER_AES256
+
+/* The supported encryption modes. NOte that not all of them are
+ supported for each algorithm. */
+enum gcry_cipher_modes
+ {
+ GCRY_CIPHER_MODE_NONE = 0, /* Not yet specified. */
+ GCRY_CIPHER_MODE_ECB = 1, /* Electronic codebook. */
+ GCRY_CIPHER_MODE_CFB = 2, /* Cipher feedback. */
+ GCRY_CIPHER_MODE_CBC = 3, /* Cipher block chaining. */
+ GCRY_CIPHER_MODE_STREAM = 4, /* Used with stream ciphers. */
+ GCRY_CIPHER_MODE_OFB = 5 /* Outer feedback. */
+ };
+
+/* Flags used with the open function. */
+enum gcry_cipher_flags
+ {
+ GCRY_CIPHER_SECURE = 1, /* Allocate in secure memory. */
+ GCRY_CIPHER_ENABLE_SYNC = 2, /* Enable CFB sync mode. */
+ GCRY_CIPHER_CBC_CTS = 4 /* Enable CBC cipher text stealing (CTS). */
+ };
+
+
+/* Create a handle for algorithm ALGO to be used in MODE. FLAGS may
+ be given as an bitwise OR of the gcry_cipher_flags values. */
+GcryCipherHd gcry_cipher_open (int algo, int mode, unsigned int flags);
+
+/* Close the cioher handle H and release all resource. */
+void gcry_cipher_close (GcryCipherHd h);
+
+/* Perform various operations on the cipher object H. */
+int gcry_cipher_ctl( GcryCipherHd h, int cmd, void *buffer, size_t buflen);
+
+/* Retrieve various information about the cipher object H. */
+int gcry_cipher_info( GcryCipherHd h, int what, void *buffer, size_t *nbytes);
+
+/* Retrieve various information about the cipher algorithm ALGO. */
+int gcry_cipher_algo_info (int algo, int what, void *buffer, size_t *nbytes);
+
+/* Map the cipher algorithm id ALGO to a string representation of that
+ algorithm name. For unknown algorithms this functions returns an
+ empty string. */
+const char *gcry_cipher_algo_name (int algo) _GCRY_GCC_ATTR_PURE;
+
+/* Map the algorithm name NAME to an cipher algorithm ID. Return 0 if
+ the algorithm name is not known. */
+int gcry_cipher_map_name (const char *name) _GCRY_GCC_ATTR_PURE;
+
+/* Given an ASN.1 object identifier in standard IETF dotted decimal
+ format in STING, return the encryption mode associated with that
+ OID or 0 if not known or applicable. */
+int gcry_cipher_mode_from_oid (const char *string) _GCRY_GCC_ATTR_PURE;
+
+/* Encrypt the plaintext of size INLEN in IN using the cipher handle H
+ into the buffer OUT which has an allocated length of OUTSIZE. For
+ most algorithms it is possible to pass NULL for in and 0 for INLEN
+ and do a in-place decryption of the data provided in OUT. */
+int gcry_cipher_encrypt (GcryCipherHd h,
+ unsigned char *out, size_t outsize,
+ const unsigned char *in, size_t inlen);
+
+/* The counterpart to gcry_cipher_encrypt. */
+int gcry_cipher_decrypt (GcryCipherHd h,
+ unsigned char *out, size_t outsize,
+ const unsigned char *in, size_t inlen);
+
+/* Set key K of length L for the cipher handle H.
+ (We have to cast away a const char* here - this catch-all ctl
+ function was probably not the best choice) */
+#define gcry_cipher_setkey(h,k,l) gcry_cipher_ctl( (h), GCRYCTL_SET_KEY, \
+ (char*)(k), (l) )
+
+/* Set initialization vector K of length L for the cipher handle H. */
+#define gcry_cipher_setiv(h,k,l) gcry_cipher_ctl( (h), GCRYCTL_SET_IV, \
+ (char*)(k), (l) )
+
+/* Perform the the OppenPGP sync operation if this is enabled for the
+ cipher handle H. */
+#define gcry_cipher_sync(h) gcry_cipher_ctl( (h), GCRYCTL_CFB_SYNC, \
+ NULL, 0 )
+
+/* Enable or disable CTS in future calls to gcry_encrypt(). CBC mode only. */
+#define gcry_cipher_cts(h,on) gcry_cipher_ctl( (h), GCRYCTL_SET_CBC_CTS, \
+ NULL, on )
+
+/* Retrieved the key length used with algorithm A. */
+#define gcry_cipher_get_algo_keylen(a) \
+ gcry_cipher_algo_info( (a), GCRYCTL_GET_KEYLEN, NULL, NULL )
+
+/* Retrieve the block length used with algorithm A. */
+#define gcry_cipher_get_algo_blklen(a) \
+ gcry_cipher_algo_info( (a), GCRYCTL_GET_BLKLEN, NULL, NULL )
+
+/* Return 0 if the algorithm A is available for use. */
+#define gcry_cipher_test_algo(a) \
+ gcry_cipher_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL )
+
+
+
+/************************************
+ * *
+ * asymmetric cipher functions *
+ * *
+ ************************************/
+
+/* The algorithms and their IDs we support. */
+enum gcry_pk_algos
+ {
+ GCRY_PK_RSA = 1,
+ GCRY_PK_RSA_E = 2, /* deprecated */
+ GCRY_PK_RSA_S = 3, /* deprecated */
+ GCRY_PK_ELG_E = 16, /* use only for OpenPGP */
+ GCRY_PK_DSA = 17,
+ GCRY_PK_ELG = 20
+ };
+
+/* Flags describing usage capabilities of a PK algorithm. */
+#define GCRY_PK_USAGE_SIGN 1
+#define GCRY_PK_USAGE_ENCR 2
+
+/* Encrypt the DATA using the public key PKEY and store the result as
+ a newly created S-expression at RESULT. */
+int gcry_pk_encrypt (GcrySexp *result, GcrySexp data, GcrySexp pkey);
+
+/* Decrypt the DATA using the private key SKEY and store the result as
+ a newly created S-expression at RESULT. */
+int gcry_pk_decrypt (GcrySexp *result, GcrySexp data, GcrySexp skey);
+
+/* Sign the DATA using the private key SKEY and store the result as
+ a newly created S-expression at RESULT. */
+int gcry_pk_sign (GcrySexp *result, GcrySexp data, GcrySexp skey);
+
+/* Check the signature SIGVAL on DATA using the public key PKEY. */
+int gcry_pk_verify (GcrySexp sigval, GcrySexp data, GcrySexp pkey);
+
+/* Check that KEY (either private or public) is sane. */
+int gcry_pk_testkey (GcrySexp key);
+
+/* Generate a new key pair according to the parameters given in
+ S_PARMS. The new key pair is returned in as an S-expression in
+ R_KEY. */
+int gcry_pk_genkey (GcrySexp *r_key, GcrySexp s_parms);
+
+/* Catch all function for miscellaneous operations. */
+int gcry_pk_ctl (int cmd, void *buffer, size_t buflen);
+
+/* Retrieve information about the public key algorithm ALGO. */
+int gcry_pk_algo_info (int algo, int what, void *buffer, size_t *nbytes);
+
+/* Map the public key algorithm id ALGO to a string representation of the
+ algorithm name. For unknown algorithms this functions returns an
+ empty string. */
+const char *gcry_pk_algo_name (int algo) _GCRY_GCC_ATTR_PURE;
+
+/* Map the algorithm NAME to a public key algorithm Id. Return 0 if
+ the algorithm name is not known. */
+int gcry_pk_map_name (const char* name) _GCRY_GCC_ATTR_PURE;
+
+/* Return what is commonly referred as the key length for the given
+ public or private KEY. */
+unsigned int gcry_pk_get_nbits (GcrySexp key) _GCRY_GCC_ATTR_PURE;
+
+/* Please note that keygrip is still experimental and should not be
+ used without contacting the author. */
+unsigned char *gcry_pk_get_keygrip (GcrySexp key, unsigned char *array);
+
+/* Return 0 if the public key algorithm A is available for use. */
+#define gcry_pk_test_algo(a) \
+ gcry_pk_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL )
+
+
+
+/************************************
+ * *
+ * cryptograhic hash functions *
+ * *
+ ************************************/
+
+/* Algorithm IDs for the hash functions we know about. Not all of them
+ are implemnted. */
+enum gcry_md_algos
+ {
+ GCRY_MD_NONE = 0,
+ GCRY_MD_MD5 = 1,
+ GCRY_MD_SHA1 = 2,
+ GCRY_MD_RMD160 = 3,
+ GCRY_MD_MD2 = 5,
+ GCRY_MD_TIGER = 6, /* TIGER/192. */
+ GCRY_MD_HAVAL = 7, /* HAVAL, 5 pass, 160 bit. */
+ GCRY_MD_SHA256 = 8,
+ GCRY_MD_SHA384 = 9,
+ GCRY_MD_SHA512 = 10,
+ GCRY_MD_MD4 = 301
+ };
+
+/* Flags used with the open function. */
+enum gcry_md_flags
+ {
+ GCRY_MD_FLAG_SECURE = 1, /* Allocate all buffers in "secure" memory */
+ GCRY_MD_FLAG_HMAC = 2 /* Make an HMAC out of this algorithm. */
+ };
+
+
+/* This object is used to hold a handle to an message digest object.
+ GcryCipherHd is the preferred type. */
+struct gcry_md_context;
+struct gcry_md_handle
+ { /* This structure is private - only to be used by the gcry_md_ macros. */
+ struct gcry_md_context *ctx;
+ int bufpos;
+ int bufsize;
+ unsigned char buf[1];
+ };
+typedef struct gcry_md_handle *GCRY_MD_HD;
+typedef struct gcry_md_handle *GcryMDHd;
+
+
+/* Create a message digest object for algorithm ALGO. FLAGS may be
+ given as an bitwise OR of the gcry_md_flags values. ALGO may be
+ given as 0 if the algorithms to be used are later set using
+ gcry_md_enable. */
+GcryMDHd gcry_md_open (int algo, unsigned int flags);
+
+/* Release the message digest object HD. */
+void gcry_md_close (GcryMDHd hd);
+
+/* Add the message digest algorithm ALGO to the digest object HD. */
+int gcry_md_enable( GcryMDHd hd, int algo );
+
+/* Create a new digest object as an exact copy of the object HD. */
+GcryMDHd gcry_md_copy (GcryMDHd hd);
+
+/* Reset the digest object HD to its initail state. */
+void gcry_md_reset (GcryMDHd hd);
+
+/* Perform various operations on the digets object HD. */
+int gcry_md_ctl (GcryMDHd hd, int cmd, unsigned char *buffer, size_t buflen);
+
+/* Pass LENGTH bytes of data in BUFFER to the digest object HD so that
+ it can update the digest values. This is the actual hash
+ function. */
+void gcry_md_write (GcryMDHd hd, const void *buffer, size_t length);
+
+/* Read out the final digest from HD return the digest value for
+ algorithm ALGO. */
+unsigned char *gcry_md_read (GcryMDHd hd, int algo);
+
+/* Convenience function to calculate the hash from the data in BUFFER
+ of size LENGTH using the algorithm ALGO avoiding the creating of a
+ hash object. The hash is returned in the caller provided buffer
+ DIGEST which must be large enough to hold the digest of the given
+ algorithm. */
+void gcry_md_hash_buffer (int algo, void *digest,
+ const void *buffer, size_t length);
+
+/* Retrieve the algorithm used with HD. This does not work reliable
+ if more than one algorithm is enabled in HD. */
+int gcry_md_get_algo (GcryMDHd hd);
+
+/* Retrieved the length in bytes of the digest yielded by algorithm
+ ALGO. */
+unsigned int gcry_md_get_algo_dlen (int algo);
+
+/* Retrieve various information about the object H. */
+int gcry_md_info (GcryMDHd h, int what, void *buffer, size_t *nbytes);
+
+/* Retrieve various information about the algorithm ALGO. */
+int gcry_md_algo_info( int algo, int what, void *buffer, size_t *nbytes);
+
+/* Map the digest algorithm id ALGO to a string representation of the
+ algorithm name. For unknown algorithms this functions returns an
+ empty string. */
+const char *gcry_md_algo_name (int algo) _GCRY_GCC_ATTR_PURE;
+
+/* Map the algorithm NAME to a digest algorithm Id. Return 0 if
+ the algorithm name is not known. */
+int gcry_md_map_name (const char* name) _GCRY_GCC_ATTR_PURE;
+
+/* For use with the HMAC feature, the set MAC key to the KEY of
+ KEYLEN. */
+int gcry_md_setkey (GcryMDHd hd, const void *key, size_t keylen);
+
+/* Update the hash(s) of H with the character C. This is a buffered
+ version of the gcry_md_write function. */
+#define gcry_md_putc(h,c) \
+ do { \
+ GcryMDHd h__ = (h); \
+ if( (h__)->bufpos == (h__)->bufsize ) \
+ gcry_md_write( (h__), NULL, 0 ); \
+ (h__)->buf[(h__)->bufpos++] = (c) & 0xff; \
+ } while(0)
+
+/* Finalize the digest calculation. This is not really needed because
+ gcry_md_read() does this implicitly. */
+#define gcry_md_final(a) \
+ gcry_md_ctl ((a), GCRYCTL_FINALIZE, NULL, 0)
+
+/* Return true when the digest object is allocated in "secure" memory. */
+#define gcry_md_is_secure(a) \
+ gcry_md_info( (a), GCRYCTL_IS_SECURE, NULL, NULL )
+
+/* Return 0 if the algorithm A is available for use. */
+#define gcry_md_test_algo(a) \
+ gcry_md_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL )
+
+/* Return an DER encoded ASN.1 OID for the algorithm A in buffer B. N
+ must point to size_t variable with the available size of buffer B.
+ After return it will receive the actual size of the returned
+ OID. */
+#define gcry_md_get_asnoid(a,b,n) \
+ gcry_md_algo_info((a), GCRYCTL_GET_ASNOID, (b), (n))
+
+/* Enable debugging for digets object A; i.e. create files named
+ dbgmd-. while hashing. B is a string used as the suffix
+ for the filename. */
+#define gcry_md_start_debug(a,b) \
+ gcry_md_ctl( (a), GCRYCTL_START_DUMP, (b), 0 )
+
+/* Disable the debugging of A. */
+#define gcry_md_stop_debug(a,b) \
+ gcry_md_ctl( (a), GCRYCTL_STOP_DUMP, (b), 0 )
+
+
+
+/************************************
+ * *
+ * random generating functions *
+ * *
+ ************************************/
+
+/* The possible values for the random quality. The rule of thumb is
+ to use WEAK for random number which don't need to be
+ cryptographically strong, STRONG for session keys and VERY_STRONG
+ for key material. */
+enum gcry_random_level
+ {
+ GCRY_WEAK_RANDOM = 0,
+ GCRY_STRONG_RANDOM = 1,
+ GCRY_VERY_STRONG_RANDOM = 2
+ };
+
+/* Fill BUFFER with LENGTH bytes of random, using random numbers of
+ quality LEVEL. */
+void gcry_randomize (unsigned char *buffer, size_t length,
+ enum gcry_random_level level);
+
+/* Return NBYTES of allocated random using a random numbers of quality
+ LEVEL. */
+void *gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
+ _GCRY_GCC_ATTR_MALLOC;
+
+/* Return NBYTES of allocated random using a random numbers of quality
+ LEVEL. The random numbers are created returned in "secure"
+ memory. */
+void *gcry_random_bytes_secure (size_t nbytes, enum gcry_random_level level)
+ _GCRY_GCC_ATTR_MALLOC;
+
+
+/* Set the big inetger W to a random value of NBITS using a random
+ generator with quality LEVEL. */
+void gcry_mpi_randomize (GcryMPI w,
+ unsigned int nbits, enum gcry_random_level level);
+
+
+
+/************************************
+ * *
+ * miscellaneous stuff *
+ * *
+ ************************************/
+
+/* Log leveles used by the internal logging facility. */
+enum gcry_log_levels
+ {
+ GCRY_LOG_CONT = 0, /* continue the last log line */
+ GCRY_LOG_INFO = 10,
+ GCRY_LOG_WARN = 20,
+ GCRY_LOG_ERROR = 30,
+ GCRY_LOG_FATAL = 40,
+ GCRY_LOG_BUG = 50,
+ GCRY_LOG_DEBUG = 100
+ };
+
+
+/* Certain operations can provide progress information. This function
+ is used to register a handler for retrieving these information. */
+void gcry_set_progress_handler (void (*cb)(void *,const char*,int, int, int),
+ void *cb_data);
+
+
+
+/* Register a custom memory allocation functions. */
+void gcry_set_allocation_handler (void *(*new_alloc_func)(size_t n),
+ void *(*new_alloc_secure_func)(size_t n),
+ int (*new_is_secure_func)(const void*),
+ void *(*new_realloc_func)(void *p, size_t n),
+ void (*new_free_func)(void*));
+
+/* Register a function used instead of the internal out of memory
+ handler. */
+void gcry_set_outofcore_handler (int (*h)(void*, size_t, unsigned int),
+ void *opaque );
+
+/* Register a function used instead of the internal fatal error
+ handler. */
+void gcry_set_fatalerror_handler (void (*fnc)(void*,int, const char*),
+ void *opaque);
+
+/* Reserved for future use. */
+void gcry_set_gettext_handler (const char *(*f)(const char*));
+
+/* Regstier a function used instead of the internal logging
+ facility. */
+void gcry_set_log_handler (void (*f)(void*,int, const char*, va_list),
+ void *opaque);
+
+
+/* Libgcrypt uses its own memory allocation. It is important to use
+ gcry_free () to release memory allocated by libgcrypt. */
+void *gcry_malloc (size_t n) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_calloc (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_malloc_secure (size_t n) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_calloc_secure (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_realloc (void *a, size_t n);
+char *gcry_strdup (const char *string) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_xmalloc (size_t n) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_xcalloc (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_xmalloc_secure (size_t n) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_xcalloc_secure (size_t n, size_t m) _GCRY_GCC_ATTR_MALLOC;
+void *gcry_xrealloc (void *a, size_t n);
+char *gcry_xstrdup (const char * a) _GCRY_GCC_ATTR_MALLOC;
+void gcry_free (void *a);
+
+/* Return true if A is allocated in "secure" memory. */
+int gcry_is_secure (const void *a) _GCRY_GCC_ATTR_PURE;
+
+#define MPI_NULL NULL
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _GCRYPT_H */
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/asm-syntax.h linux-2.6.10-hip/net/ipv6/hip/crypto/i386/asm-syntax.h
--- linux-2.6.10/net/ipv6/hip/crypto/i386/asm-syntax.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/asm-syntax.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,75 @@
+#define ELF_SYNTAX
+/* syntax.h -- Definitions for x86 syntax variations.
+ *
+ * Copyright (C) 1992, 1994, 1995, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#define C_SYMBOL_NAME(name) name
+#define SIZEOF_UNSIGNED_SHORT 2
+#define SIZEOF_UNSIGNED_INT 4
+#define SIZEOF_UNSIGNED_LONG 4
+#define BYTES_PER_MPI_LIMB (SIZEOF_UNSIGNED_LONG)
+
+#undef ALIGN
+
+#if defined (BSD_SYNTAX) || defined (ELF_SYNTAX)
+#define R(r) %r
+#define MEM(base)(base)
+#define MEM_DISP(base,displacement)displacement(R(base))
+#define MEM_INDEX(base,index,size)(R(base),R(index),size)
+#ifdef __STDC__
+#define INSN1(mnemonic,size_suffix,dst)mnemonic##size_suffix dst
+#define INSN2(mnemonic,size_suffix,dst,src)mnemonic##size_suffix src,dst
+#else
+#define INSN1(mnemonic,size_suffix,dst)mnemonic/**/size_suffix dst
+#define INSN2(mnemonic,size_suffix,dst,src)mnemonic/**/size_suffix src,dst
+#endif
+#define TEXT .text
+#if defined (BSD_SYNTAX)
+#define ALIGN(log) .align log
+#endif
+#if defined (ELF_SYNTAX)
+#define ALIGN(log) .align 1<<(log)
+#endif
+#define GLOBL .globl
+#endif
+
+#ifdef INTEL_SYNTAX
+#define R(r) r
+#define MEM(base)[base]
+#define MEM_DISP(base,displacement)[base+(displacement)]
+#define MEM_INDEX(base,index,size)[base+index*size]
+#define INSN1(mnemonic,size_suffix,dst)mnemonic dst
+#define INSN2(mnemonic,size_suffix,dst,src)mnemonic dst,src
+#define TEXT .text
+#define ALIGN(log) .align log
+#define GLOBL .globl
+#endif
+
+#ifdef X86_BROKEN_ALIGN
+#undef ALIGN
+#define ALIGN(log) .align log,0x90
+#endif
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/Makefile linux-2.6.10-hip/net/ipv6/hip/crypto/i386/Makefile
--- linux-2.6.10/net/ipv6/hip/crypto/i386/Makefile 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/Makefile 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,7 @@
+#
+# Krisu
+#
+# No matter, whether or not a module.
+
+obj-y += mpih-add1.o mpih-lshift.o mpih-mul1.o mpih-mul2.o mpih-mul3.o
+obj-y += mpih-rshift.o mpih-sub1.o
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-add1.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-add1.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-add1.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-add1.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,115 @@
+/* i80386 add_n -- Add two limb vectors of the same length > 0 and store
+ * sum in a third limb vector.
+ *
+ * Copyright (C) 1992, 1994, 1995, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_add_n( mpi_ptr_t res_ptr, (sp + 4)
+ * mpi_ptr_t s1_ptr, (sp + 8)
+ * mpi_ptr_t s2_ptr, (sp + 12)
+ * mpi_size_t size) (sp + 16)
+ */
+
+.text
+ ALIGN (3)
+ .globl C_SYMBOL_NAME(_gcry_mpih_add_n)
+C_SYMBOL_NAME(_gcry_mpih_add_n:)
+ pushl %edi
+ pushl %esi
+
+ movl 12(%esp),%edi /* res_ptr */
+ movl 16(%esp),%esi /* s1_ptr */
+ movl 20(%esp),%edx /* s2_ptr */
+ movl 24(%esp),%ecx /* size */
+
+ movl %ecx,%eax
+ shrl $3,%ecx /* compute count for unrolled loop */
+ negl %eax
+ andl $7,%eax /* get index where to start loop */
+ jz Loop /* necessary special case for 0 */
+ incl %ecx /* adjust loop count */
+ shll $2,%eax /* adjustment for pointers... */
+ subl %eax,%edi /* ... since they are offset ... */
+ subl %eax,%esi /* ... by a constant when we ... */
+ subl %eax,%edx /* ... enter the loop */
+ shrl $2,%eax /* restore previous value */
+#ifdef PIC
+/* Calculate start address in loop for PIC. Due to limitations in some
+ assemblers, Loop-L0-3 cannot be put into the leal */
+ call L0
+L0: leal (%eax,%eax,8),%eax
+ addl (%esp),%eax
+ addl $(Loop-L0-3),%eax
+ addl $4,%esp
+#else
+/* Calculate start address in loop for non-PIC. */
+ leal (Loop - 3)(%eax,%eax,8),%eax
+#endif
+ jmp *%eax /* jump into loop */
+ ALIGN (3)
+Loop: movl (%esi),%eax
+ adcl (%edx),%eax
+ movl %eax,(%edi)
+ movl 4(%esi),%eax
+ adcl 4(%edx),%eax
+ movl %eax,4(%edi)
+ movl 8(%esi),%eax
+ adcl 8(%edx),%eax
+ movl %eax,8(%edi)
+ movl 12(%esi),%eax
+ adcl 12(%edx),%eax
+ movl %eax,12(%edi)
+ movl 16(%esi),%eax
+ adcl 16(%edx),%eax
+ movl %eax,16(%edi)
+ movl 20(%esi),%eax
+ adcl 20(%edx),%eax
+ movl %eax,20(%edi)
+ movl 24(%esi),%eax
+ adcl 24(%edx),%eax
+ movl %eax,24(%edi)
+ movl 28(%esi),%eax
+ adcl 28(%edx),%eax
+ movl %eax,28(%edi)
+ leal 32(%edi),%edi
+ leal 32(%esi),%esi
+ leal 32(%edx),%edx
+ decl %ecx
+ jnz Loop
+
+ sbbl %eax,%eax
+ negl %eax
+
+ popl %esi
+ popl %edi
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-lshift.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-lshift.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-lshift.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-lshift.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,93 @@
+/* i80386 lshift
+ * Copyright (C) 1992, 1994, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_lshift( mpi_ptr_t wp, (sp + 4)
+ * mpi_ptr_t up, (sp + 8)
+ * mpi_size_t usize, (sp + 12)
+ * unsigned cnt) (sp + 16)
+ */
+
+.text
+ ALIGN (3)
+ .globl C_SYMBOL_NAME(_gcry_mpih_lshift)
+C_SYMBOL_NAME(_gcry_mpih_lshift:)
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+
+ movl 16(%esp),%edi /* res_ptr */
+ movl 20(%esp),%esi /* s_ptr */
+ movl 24(%esp),%edx /* size */
+ movl 28(%esp),%ecx /* cnt */
+
+ subl $4,%esi /* adjust s_ptr */
+
+ movl (%esi,%edx,4),%ebx /* read most significant limb */
+ xorl %eax,%eax
+ shldl %cl,%ebx,%eax /* compute carry limb */
+ decl %edx
+ jz Lend
+ pushl %eax /* push carry limb onto stack */
+/* testb $1,%edx (compiler warns... below should be ok) */
+ testb $1,%dl
+ jnz L1 /* enter loop in the middle */
+ movl %ebx,%eax
+
+ ALIGN (3)
+Loop: movl (%esi,%edx,4),%ebx /* load next lower limb */
+ shldl %cl,%ebx,%eax /* compute result limb */
+ movl %eax,(%edi,%edx,4) /* store it */
+ decl %edx
+L1: movl (%esi,%edx,4),%eax
+ shldl %cl,%eax,%ebx
+ movl %ebx,(%edi,%edx,4)
+ decl %edx
+ jnz Loop
+
+ shll %cl,%eax /* compute least significant limb */
+ movl %eax,(%edi) /* store it */
+
+ popl %eax /* pop carry limb */
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ ret
+
+Lend: shll %cl,%ebx /* compute least significant limb */
+ movl %ebx,(%edi) /* store it */
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-mul1.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-mul1.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-mul1.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-mul1.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,83 @@
+/* i80386 mul_1 -- Multiply a limb vector with a limb and store
+ * the result in a second limb vector.
+ * Copyright (C) 1992, 1994, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_mul_1( mpi_ptr_t res_ptr, (sp + 4)
+ * mpi_ptr_t s1_ptr, (sp + 8)
+ * mpi_size_t s1_size, (sp + 12)
+ * mpi_limb_t s2_limb) (sp + 16)
+ */
+
+#define res_ptr edi
+#define s1_ptr esi
+#define size ecx
+#define s2_limb ebp
+
+ TEXT
+ ALIGN (3)
+ GLOBL C_SYMBOL_NAME(_gcry_mpih_mul_1)
+C_SYMBOL_NAME(_gcry_mpih_mul_1:)
+
+ INSN1(push,l ,R(edi))
+ INSN1(push,l ,R(esi))
+ INSN1(push,l ,R(ebx))
+ INSN1(push,l ,R(ebp))
+
+ INSN2(mov,l ,R(res_ptr),MEM_DISP(esp,20))
+ INSN2(mov,l ,R(s1_ptr),MEM_DISP(esp,24))
+ INSN2(mov,l ,R(size),MEM_DISP(esp,28))
+ INSN2(mov,l ,R(s2_limb),MEM_DISP(esp,32))
+
+ INSN2(lea,l ,R(res_ptr),MEM_INDEX(res_ptr,size,4))
+ INSN2(lea,l ,R(s1_ptr),MEM_INDEX(s1_ptr,size,4))
+ INSN1(neg,l ,R(size))
+ INSN2(xor,l ,R(ebx),R(ebx))
+ ALIGN (3)
+Loop:
+ INSN2(mov,l ,R(eax),MEM_INDEX(s1_ptr,size,4))
+ INSN1(mul,l ,R(s2_limb))
+ INSN2(add,l ,R(eax),R(ebx))
+ INSN2(mov,l ,MEM_INDEX(res_ptr,size,4),R(eax))
+ INSN2(adc,l ,R(edx),$0)
+ INSN2(mov,l ,R(ebx),R(edx))
+
+ INSN1(inc,l ,R(size))
+ INSN1(jnz, ,Loop)
+ INSN2(mov,l ,R(eax),R(ebx))
+
+ INSN1(pop,l ,R(ebp))
+ INSN1(pop,l ,R(ebx))
+ INSN1(pop,l ,R(esi))
+ INSN1(pop,l ,R(edi))
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-mul2.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-mul2.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-mul2.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-mul2.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,85 @@
+/* i80386 addmul_1 -- Multiply a limb vector with a limb and add
+ * the result to a second limb vector.
+ *
+ * Copyright (C) 1992, 1994, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_addmul_1( mpi_ptr_t res_ptr, (sp + 4)
+ * mpi_ptr_t s1_ptr, (sp + 8)
+ * mpi_size_t s1_size, (sp + 12)
+ * mpi_limb_t s2_limb) (sp + 16)
+ */
+
+#define res_ptr edi
+#define s1_ptr esi
+#define size ecx
+#define s2_limb ebp
+
+ TEXT
+ ALIGN (3)
+ GLOBL C_SYMBOL_NAME(_gcry_mpih_addmul_1)
+C_SYMBOL_NAME(_gcry_mpih_addmul_1:)
+
+ INSN1(push,l ,R(edi))
+ INSN1(push,l ,R(esi))
+ INSN1(push,l ,R(ebx))
+ INSN1(push,l ,R(ebp))
+
+ INSN2(mov,l ,R(res_ptr),MEM_DISP(esp,20))
+ INSN2(mov,l ,R(s1_ptr),MEM_DISP(esp,24))
+ INSN2(mov,l ,R(size),MEM_DISP(esp,28))
+ INSN2(mov,l ,R(s2_limb),MEM_DISP(esp,32))
+
+ INSN2(lea,l ,R(res_ptr),MEM_INDEX(res_ptr,size,4))
+ INSN2(lea,l ,R(s1_ptr),MEM_INDEX(s1_ptr,size,4))
+ INSN1(neg,l ,R(size))
+ INSN2(xor,l ,R(ebx),R(ebx))
+ ALIGN (3)
+Loop:
+ INSN2(mov,l ,R(eax),MEM_INDEX(s1_ptr,size,4))
+ INSN1(mul,l ,R(s2_limb))
+ INSN2(add,l ,R(eax),R(ebx))
+ INSN2(adc,l ,R(edx),$0)
+ INSN2(add,l ,MEM_INDEX(res_ptr,size,4),R(eax))
+ INSN2(adc,l ,R(edx),$0)
+ INSN2(mov,l ,R(ebx),R(edx))
+
+ INSN1(inc,l ,R(size))
+ INSN1(jnz, ,Loop)
+ INSN2(mov,l ,R(eax),R(ebx))
+
+ INSN1(pop,l ,R(ebp))
+ INSN1(pop,l ,R(ebx))
+ INSN1(pop,l ,R(esi))
+ INSN1(pop,l ,R(edi))
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-mul3.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-mul3.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-mul3.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-mul3.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,85 @@
+/* i80386 submul_1 -- Multiply a limb vector with a limb and add
+ * the result to a second limb vector.
+ *
+ * Copyright (C) 1992, 1994, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_submul_1( mpi_ptr_t res_ptr, (sp + 4)
+ * mpi_ptr_t s1_ptr, (sp + 8)
+ * mpi_size_t s1_size, (sp + 12)
+ * mpi_limb_t s2_limb) (sp + 16)
+ */
+
+#define res_ptr edi
+#define s1_ptr esi
+#define size ecx
+#define s2_limb ebp
+
+ TEXT
+ ALIGN (3)
+ GLOBL C_SYMBOL_NAME(_gcry_mpih_submul_1)
+C_SYMBOL_NAME(_gcry_mpih_submul_1:)
+
+ INSN1(push,l ,R(edi))
+ INSN1(push,l ,R(esi))
+ INSN1(push,l ,R(ebx))
+ INSN1(push,l ,R(ebp))
+
+ INSN2(mov,l ,R(res_ptr),MEM_DISP(esp,20))
+ INSN2(mov,l ,R(s1_ptr),MEM_DISP(esp,24))
+ INSN2(mov,l ,R(size),MEM_DISP(esp,28))
+ INSN2(mov,l ,R(s2_limb),MEM_DISP(esp,32))
+
+ INSN2(lea,l ,R(res_ptr),MEM_INDEX(res_ptr,size,4))
+ INSN2(lea,l ,R(s1_ptr),MEM_INDEX(s1_ptr,size,4))
+ INSN1(neg,l ,R(size))
+ INSN2(xor,l ,R(ebx),R(ebx))
+ ALIGN (3)
+Loop:
+ INSN2(mov,l ,R(eax),MEM_INDEX(s1_ptr,size,4))
+ INSN1(mul,l ,R(s2_limb))
+ INSN2(add,l ,R(eax),R(ebx))
+ INSN2(adc,l ,R(edx),$0)
+ INSN2(sub,l ,MEM_INDEX(res_ptr,size,4),R(eax))
+ INSN2(adc,l ,R(edx),$0)
+ INSN2(mov,l ,R(ebx),R(edx))
+
+ INSN1(inc,l ,R(size))
+ INSN1(jnz, ,Loop)
+ INSN2(mov,l ,R(eax),R(ebx))
+
+ INSN1(pop,l ,R(ebp))
+ INSN1(pop,l ,R(ebx))
+ INSN1(pop,l ,R(esi))
+ INSN1(pop,l ,R(edi))
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-rshift.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-rshift.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-rshift.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-rshift.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,97 @@
+/* i80386 rshift
+ *
+ * Copyright (C) 1992, 1994, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_rshift( mpi_ptr_t wp, (sp + 4)
+ * mpi_ptr_t up, (sp + 8)
+ * mpi_size_t usize, (sp + 12)
+ * unsigned cnt) (sp + 16)
+ */
+
+.text
+ ALIGN (3)
+ .globl C_SYMBOL_NAME(_gcry_mpih_rshift)
+C_SYMBOL_NAME(_gcry_mpih_rshift:)
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+
+ movl 16(%esp),%edi /* wp */
+ movl 20(%esp),%esi /* up */
+ movl 24(%esp),%edx /* usize */
+ movl 28(%esp),%ecx /* cnt */
+
+ leal -4(%edi,%edx,4),%edi
+ leal (%esi,%edx,4),%esi
+ negl %edx
+
+ movl (%esi,%edx,4),%ebx /* read least significant limb */
+ xorl %eax,%eax
+ shrdl %cl,%ebx,%eax /* compute carry limb */
+ incl %edx
+ jz Lend2
+ pushl %eax /* push carry limb onto stack */
+/* testb $1,%edx (Compiler warns. Below should be ok) */
+ testb $1,%dl
+ jnz L2 /* enter loop in the middle */
+ movl %ebx,%eax
+
+ ALIGN (3)
+Loop2: movl (%esi,%edx,4),%ebx /* load next higher limb */
+ shrdl %cl,%ebx,%eax /* compute result limb */
+ movl %eax,(%edi,%edx,4) /* store it */
+ incl %edx
+L2: movl (%esi,%edx,4),%eax
+ shrdl %cl,%eax,%ebx
+ movl %ebx,(%edi,%edx,4)
+ incl %edx
+ jnz Loop2
+
+ shrl %cl,%eax /* compute most significant limb */
+ movl %eax,(%edi) /* store it */
+
+ popl %eax /* pop carry limb */
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ ret
+
+Lend2: shrl %cl,%ebx /* compute most significant limb */
+ movl %ebx,(%edi) /* store it */
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-sub1.S linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-sub1.S
--- linux-2.6.10/net/ipv6/hip/crypto/i386/mpih-sub1.S 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/i386/mpih-sub1.S 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,115 @@
+/* i80386 sub_n -- Sub two limb vectors of the same length > 0 and store
+ * sum in a third limb vector.
+ *
+ * Copyright (C) 1992, 1994, 1995, 1998,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#include "asm-syntax.h"
+
+
+/*******************
+ * mpi_limb_t
+ * _gcry_mpih_sub_n( mpi_ptr_t res_ptr, (sp + 4)
+ * mpi_ptr_t s1_ptr, (sp + 8)
+ * mpi_ptr_t s2_ptr, (sp + 12)
+ * mpi_size_t size) (sp + 16)
+ */
+
+
+.text
+ ALIGN (3)
+ .globl C_SYMBOL_NAME(_gcry_mpih_sub_n)
+C_SYMBOL_NAME(_gcry_mpih_sub_n:)
+ pushl %edi
+ pushl %esi
+
+ movl 12(%esp),%edi /* res_ptr */
+ movl 16(%esp),%esi /* s1_ptr */
+ movl 20(%esp),%edx /* s2_ptr */
+ movl 24(%esp),%ecx /* size */
+
+ movl %ecx,%eax
+ shrl $3,%ecx /* compute count for unrolled loop */
+ negl %eax
+ andl $7,%eax /* get index where to start loop */
+ jz Loop /* necessary special case for 0 */
+ incl %ecx /* adjust loop count */
+ shll $2,%eax /* adjustment for pointers... */
+ subl %eax,%edi /* ... since they are offset ... */
+ subl %eax,%esi /* ... by a constant when we ... */
+ subl %eax,%edx /* ... enter the loop */
+ shrl $2,%eax /* restore previous value */
+#ifdef PIC
+/* Calculate start address in loop for PIC. Due to limitations in some
+ assemblers, Loop-L0-3 cannot be put into the leal */
+ call L0
+L0: leal (%eax,%eax,8),%eax
+ addl (%esp),%eax
+ addl $(Loop-L0-3),%eax
+ addl $4,%esp
+#else
+/* Calculate start address in loop for non-PIC. */
+ leal (Loop - 3)(%eax,%eax,8),%eax
+#endif
+ jmp *%eax /* jump into loop */
+ ALIGN (3)
+Loop: movl (%esi),%eax
+ sbbl (%edx),%eax
+ movl %eax,(%edi)
+ movl 4(%esi),%eax
+ sbbl 4(%edx),%eax
+ movl %eax,4(%edi)
+ movl 8(%esi),%eax
+ sbbl 8(%edx),%eax
+ movl %eax,8(%edi)
+ movl 12(%esi),%eax
+ sbbl 12(%edx),%eax
+ movl %eax,12(%edi)
+ movl 16(%esi),%eax
+ sbbl 16(%edx),%eax
+ movl %eax,16(%edi)
+ movl 20(%esi),%eax
+ sbbl 20(%edx),%eax
+ movl %eax,20(%edi)
+ movl 24(%esi),%eax
+ sbbl 24(%edx),%eax
+ movl %eax,24(%edi)
+ movl 28(%esi),%eax
+ sbbl 28(%edx),%eax
+ movl %eax,28(%edi)
+ leal 32(%edi),%edi
+ leal 32(%esi),%esi
+ leal 32(%edx),%edx
+ decl %ecx
+ jnz Loop
+
+ sbbl %eax,%eax
+ negl %eax
+
+ popl %esi
+ popl %edi
+ ret
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/kernel-interface.c linux-2.6.10-hip/net/ipv6/hip/crypto/kernel-interface.c
--- linux-2.6.10/net/ipv6/hip/crypto/kernel-interface.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/kernel-interface.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,169 @@
+/* laitapa defineä kehiin */
+
+#include "kernel-interface.h"
+
+void *gcry_random_bytes (size_t nbytes, enum gcry_random_level level)
+{
+ u8 *buffer;
+
+ buffer = gcry_malloc(nbytes);
+ if (!buffer)
+ return NULL;
+
+ get_random_bytes(buffer,nbytes);
+ return buffer;
+}
+
+void *gcry_random_bytes_secure(size_t nbytes, enum gcry_random_level level)
+{
+ return gcry_random_bytes(nbytes,level);
+}
+
+/*
+ * Have to use some magic here. We will record the size of the memory area
+ * to first 4 bytes, so that we can later "resize" the area
+ *
+ * NOTE! ALL memory areas allocated with gcry_malloc _MUST_ be freed
+ * with gcry_free!!!
+ */
+
+void * gcry_malloc (size_t n)
+{
+ u8 *ptr;
+ u32 *kludge;
+
+ ptr = kmalloc(n+sizeof(u32),GFP_KERNEL);
+ if (!ptr) {
+ HIP_DEBUG("No memory\n");
+ }
+
+ kludge = (u32 *)ptr;
+ *kludge = n;
+ return ptr+sizeof(u32);
+}
+
+void * gcry_malloc_secure (size_t n)
+{
+ /* is this secure? kernel memory should not be swappable
+ at least for now. How about future and with patches?
+ */
+ return gcry_malloc(n);
+}
+
+int gcry_is_secure( const void *a)
+{
+ /* eveyrhing is secure for now */
+ return 1;
+}
+
+void _gcry_check_heap( const void *a)
+{
+ /* not implemented in user space, and for now,
+ not in kernel space */
+ return;
+}
+
+void *gcry_realloc(void *a, size_t n)
+{
+ u32 *kludge;
+ u8 *ptr;
+ u8 *newptr;
+
+ ptr = (u8 *)a - sizeof(u32);
+
+ kludge = (u32 *)ptr;
+
+ newptr = gcry_malloc(n);
+ memcpy(newptr,a,*kludge);
+ return newptr;
+}
+
+void gcry_free(void *p)
+{
+ u8 *ptr;
+
+ if (!p)
+ return;
+
+ ptr = (u8 *)p - sizeof(u32);
+
+ kfree(ptr);
+}
+
+void *gcry_calloc(size_t n, size_t m)
+{
+ size_t bytes;
+ void *p;
+
+ bytes = n*m;
+ p = gcry_malloc(bytes);
+ if (p) {
+ memset(p,0,bytes);
+ }
+
+ return p;
+}
+
+
+void *gcry_calloc_secure (size_t n, size_t m)
+{
+ return gcry_calloc(n,m);
+}
+
+
+char *
+gcry_strdup( const char *string )
+{
+ int l;
+ char *str;
+
+ l = strlen(string);
+ str = (char *)gcry_malloc(l);
+ if (str) {
+ strncpy(str,string,l);
+ }
+
+ return str;
+}
+
+void *
+gcry_xmalloc( size_t n )
+{
+ /* just a wrapper for malloc.
+ We don't have oomhandler */
+ return gcry_malloc(n);
+}
+
+void *
+gcry_xrealloc( void *a, size_t n )
+{
+ /* same */
+ return gcry_realloc(a,n);
+}
+
+void *
+gcry_xmalloc_secure( size_t n )
+{
+ /* and same */
+ return gcry_malloc(n);
+}
+
+void *
+gcry_xcalloc( size_t n, size_t m )
+{
+ /* puuh */
+ return gcry_calloc(n,m);
+}
+
+void *
+gcry_xcalloc_secure( size_t n, size_t m )
+{
+ /* pleaseeee */
+ return gcry_calloc(n,m);
+}
+
+char *
+gcry_xstrdup( const char *string )
+{
+ return gcry_strdup(string);
+}
diff -urN linux-2.6.10/net/ipv6/hip/crypto/kernel-interface.h linux-2.6.10-hip/net/ipv6/hip/crypto/kernel-interface.h
--- linux-2.6.10/net/ipv6/hip/crypto/kernel-interface.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/kernel-interface.h 2005-03-16 00:14:54.000000000 +0200
@@ -0,0 +1,46 @@
+#ifndef KERNEL_INTERFACE_H
+#define KERNEL_INTERFACE_H
+
+#ifndef __KERNEL__
+#error This file is ment only for kernel compilation
+#endif
+
+#include
+#include
+#include
+
+#include "mpi-defs.h"
+
+#include "../debug.h"
+#include "mpi-internal.h"
+
+#define log_error HIP_ERROR
+#define log_bug HIP_ERROR
+#define assert(x) do { \
+ if (!(x)) \
+ BUG(); \
+} while(0)
+
+typedef union {
+ int a;
+ short b;
+ char c[1];
+ long d;
+ #ifdef HAVE_U64_TYPEDEF
+ u64 e;
+ #endif
+ float f;
+ double g;
+} PROPERLY_ALIGNED_TYPE;
+
+typedef struct string_list {
+ struct string_list *next;
+ unsigned int flags;
+ char d[1];
+} *STRLIST;
+
+
+#define GCRYPT_NO_MPI_MACROS
+
+
+#endif /* KERNEL_INTERFACE_H */
diff -urN linux-2.6.10/net/ipv6/hip/crypto/longlong.h linux-2.6.10-hip/net/ipv6/hip/crypto/longlong.h
--- linux-2.6.10/net/ipv6/hip/crypto/longlong.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/longlong.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,1503 @@
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+ Note: I added some stuff for use with gnupg
+
+Copyright (C) 1991, 1992, 1993, 1994, 1996, 1998,
+ 2000, 2001, 2002 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify
+it under the terms of the GNU Library General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This file 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 Library General Public
+License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this file; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+MA 02111-1307, USA. */
+
+/* You have to define the following before including this file:
+
+ UWtype -- An unsigned type, default type for operations (typically a "word")
+ UHWtype -- An unsigned type, at least half the size of UWtype.
+ UDWtype -- An unsigned type, at least twice as large a UWtype
+ W_TYPE_SIZE -- size in bits of UWtype
+
+ SItype, USItype -- Signed and unsigned 32 bit types.
+ DItype, UDItype -- Signed and unsigned 64 bit types.
+
+ On a 32 bit machine UWtype should typically be USItype;
+ on a 64 bit machine, UWtype should typically be UDItype.
+*/
+
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+/* This is used to make sure no undesirable sharing between different libraries
+ that use this file takes place. */
+#ifndef __MPN
+#define __MPN(x) __##x
+#endif
+
+/* Define auxiliary asm macros.
+
+ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
+ UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
+ word product in HIGH_PROD and LOW_PROD.
+
+ 2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a
+ UDWtype product. This is just a variant of umul_ppmm.
+
+ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator) divides a UDWtype, composed by the UWtype integers
+ HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+ in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less
+ than DENOMINATOR for correct operation. If, in addition, the most
+ significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+ UDIV_NEEDS_NORMALIZATION is defined to 1.
+
+ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+ denominator). Like udiv_qrnnd but the numbers are signed. The quotient
+ is rounded towards 0.
+
+ 5) count_leading_zeros(count, x) counts the number of zero-bits from the
+ msb to the first non-zero bit in the UWtype X. This is the number of
+ steps X needs to be shifted left to set the msb. Undefined for X == 0,
+ unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value.
+
+ 6) count_trailing_zeros(count, x) like count_leading_zeros, but counts
+ from the least significant end.
+
+ 7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+ high_addend_2, low_addend_2) adds two UWtype integers, composed by
+ HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+ respectively. The result is placed in HIGH_SUM and LOW_SUM. Overflow
+ (i.e. carry out) is not stored anywhere, and is lost.
+
+ 8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+ high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+ composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+ LOW_SUBTRAHEND_2 respectively. The result is placed in HIGH_DIFFERENCE
+ and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
+ and is lost.
+
+ If any of these macros are left undefined for a particular CPU,
+ C macros are used. */
+
+/* The CPUs come in alphabetical order below.
+
+ Please add support for more CPUs here, or improve the current support
+ for the CPUs below! */
+
+#ifdef __riscos__
+#pragma continue_after_hash_error
+#else /* !__riscos__ */
+#if defined (__GNUC__) && !defined (NO_ASM)
+
+/* We sometimes need to clobber "cc" with gcc2, but that would not be
+ understood by gcc1. Use cpp to avoid major code duplication. */
+#if __GNUC__ < 2
+#define __CLOBBER_CC
+#define __AND_CLOBBER_CC
+#else /* __GNUC__ >= 2 */
+#define __CLOBBER_CC : "cc"
+#define __AND_CLOBBER_CC , "cc"
+#endif /* __GNUC__ < 2 */
+
+
+/***************************************
+ ************** A29K *****************
+ ***************************************/
+#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %1,%4,%5\n" \
+ "addc %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %1,%4,%5\n" \
+ "subc %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("multiplu %0,%1,%2" \
+ : "=r" ((USItype)(xl)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ __asm__ ("multmu %0,%1,%2" \
+ : "=r" ((USItype)(xh)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("dividu %0,%3,%4" \
+ : "=r" ((USItype)(q)), \
+ "=q" ((USItype)(r)) \
+ : "1" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "r" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x)))
+#define COUNT_LEADING_ZEROS_0 32
+#endif /* __a29k__ */
+
+
+#if defined (__alpha) && W_TYPE_SIZE == 64
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ UDItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("umulh %r1,%2,%0" \
+ : "=r" ((UDItype) ph) \
+ : "%rJ" (__m0), \
+ "rI" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define UMUL_TIME 46
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { UDItype __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern UDItype __udiv_qrnnd ();
+#define UDIV_TIME 220
+#endif /* LONGLONG_STANDALONE */
+#endif /* __alpha */
+
+/***************************************
+ ************** ARM ******************
+ ***************************************/
+#if defined (__arm__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("adds %1, %4, %5\n" \
+ "adc %0, %2, %3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subs %1, %4, %5\n" \
+ "sbc %0, %2, %3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "r" ((USItype)(al)), \
+ "rI" ((USItype)(bl)))
+#if defined __ARM_ARCH_2__ || defined __ARM_ARCH_3__
+#define umul_ppmm(xh, xl, a, b) \
+ __asm__ ("%@ Inlined umul_ppmm\n" \
+ "mov %|r0, %2, lsr #16 @ AAAA\n" \
+ "mov %|r2, %3, lsr #16 @ BBBB\n" \
+ "bic %|r1, %2, %|r0, lsl #16 @ aaaa\n" \
+ "bic %0, %3, %|r2, lsl #16 @ bbbb\n" \
+ "mul %1, %|r1, %|r2 @ aaaa * BBBB\n" \
+ "mul %|r2, %|r0, %|r2 @ AAAA * BBBB\n" \
+ "mul %|r1, %0, %|r1 @ aaaa * bbbb\n" \
+ "mul %0, %|r0, %0 @ AAAA * bbbb\n" \
+ "adds %|r0, %1, %0 @ central sum\n" \
+ "addcs %|r2, %|r2, #65536\n" \
+ "adds %1, %|r1, %|r0, lsl #16\n" \
+ "adc %0, %|r2, %|r0, lsr #16" \
+ : "=&r" ((USItype)(xh)), \
+ "=r" ((USItype)(xl)) \
+ : "r" ((USItype)(a)), \
+ "r" ((USItype)(b)) \
+ : "r0", "r1", "r2")
+#else
+#define umul_ppmm(xh, xl, a, b) \
+ __asm__ ("%@ Inlined umul_ppmm\n" \
+ "umull %r1, %r0, %r2, %r3" \
+ : "=&r" ((USItype)(xh)), \
+ "=r" ((USItype)(xl)) \
+ : "r" ((USItype)(a)), \
+ "r" ((USItype)(b)) \
+ : "r0", "r1")
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 100
+#endif /* __arm__ */
+
+/***************************************
+ ************** CLIPPER **************
+ ***************************************/
+#if defined (__clipper__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__xx.__ll) \
+ : "%0" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define smul_ppmm(w1, w0, u, v) \
+ ({union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("mulwx %2,%0" \
+ : "=r" (__xx.__ll) \
+ : "%0" ((SItype)(u)), \
+ "r" ((SItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("mulwux %2,%0" \
+ : "=r" (__w) \
+ : "%0" ((USItype)(u)), \
+ "r" ((USItype)(v))); \
+ __w; })
+#endif /* __clipper__ */
+
+
+/***************************************
+ ************** GMICRO ***************
+ ***************************************/
+#if defined (__gmicro__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add.w %5,%1\n" \
+ "addx %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub.w %5,%1\n" \
+ "subx %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ __asm__ ("mulx %3,%0,%1" \
+ : "=g" ((USItype)(ph)), \
+ "=r" ((USItype)(pl)) \
+ : "%0" ((USItype)(m0)), \
+ "g" ((USItype)(m1)))
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("divx %4,%0,%1" \
+ : "=g" ((USItype)(q)), \
+ "=r" ((USItype)(r)) \
+ : "1" ((USItype)(nh)), \
+ "0" ((USItype)(nl)), \
+ "g" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bsch/1 %1,%0" \
+ : "=g" (count) \
+ : "g" ((USItype)(x)), \
+ "0" ((USItype)0))
+#endif
+
+
+/***************************************
+ ************** HPPA *****************
+ ***************************************/
+#if defined (__hppa) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %4,%5,%1\n" \
+ "addc %2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rM" ((USItype)(ah)), \
+ "rM" ((USItype)(bh)), \
+ "%rM" ((USItype)(al)), \
+ "rM" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %4,%5,%1\n" \
+ "subb %2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rM" ((USItype)(ah)), \
+ "rM" ((USItype)(bh)), \
+ "rM" ((USItype)(al)), \
+ "rM" ((USItype)(bl)))
+#if defined (_PA_RISC1_1)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ ("xmpyu %1,%2,%0" \
+ : "=*f" (__xx.__ll) \
+ : "*f" ((USItype)(u)), \
+ "*f" ((USItype)(v))); \
+ (wh) = __xx.__i.__h; \
+ (wl) = __xx.__i.__l; \
+ } while (0)
+#define UMUL_TIME 8
+#define UDIV_TIME 60
+#else
+#define UMUL_TIME 40
+#define UDIV_TIME 80
+#endif
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { USItype __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern USItype __udiv_qrnnd ();
+#endif /* LONGLONG_STANDALONE */
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __tmp; \
+ __asm__ ( \
+ "ldi 1,%0 \n" \
+ "extru,= %1,15,16,%%r0 ; Bits 31..16 zero? \n" \
+ "extru,tr %1,15,16,%1 ; No. Shift down, skip add.\n" \
+ "ldo 16(%0),%0 ; Yes. Perform add. \n" \
+ "extru,= %1,23,8,%%r0 ; Bits 15..8 zero? \n" \
+ "extru,tr %1,23,8,%1 ; No. Shift down, skip add.\n" \
+ "ldo 8(%0),%0 ; Yes. Perform add. \n" \
+ "extru,= %1,27,4,%%r0 ; Bits 7..4 zero? \n" \
+ "extru,tr %1,27,4,%1 ; No. Shift down, skip add.\n" \
+ "ldo 4(%0),%0 ; Yes. Perform add. \n" \
+ "extru,= %1,29,2,%%r0 ; Bits 3..2 zero? \n" \
+ "extru,tr %1,29,2,%1 ; No. Shift down, skip add.\n" \
+ "ldo 2(%0),%0 ; Yes. Perform add. \n" \
+ "extru %1,30,1,%1 ; Extract bit 1. \n" \
+ "sub %0,%1,%0 ; Subtract it. " \
+ : "=r" (count), "=r" (__tmp) : "1" (x)); \
+ } while (0)
+#endif /* hppa */
+
+
+/***************************************
+ ************** I370 *****************
+ ***************************************/
+#if (defined (__i370__) || defined (__mvs__)) && W_TYPE_SIZE == 32
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mr %0,%3" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "%1" (__m0), \
+ "r" (__m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define smul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {DItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ ("mr %0,%3" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "%1" (m0), \
+ "r" (m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __xx.__i.__h = n1; __xx.__i.__l = n0; \
+ __asm__ ("dr %0,%2" \
+ : "=r" (__xx.__ll) \
+ : "0" (__xx.__ll), "r" (d)); \
+ (q) = __xx.__i.__l; (r) = __xx.__i.__h; \
+ } while (0)
+#endif
+
+
+/***************************************
+ ************** I386 *****************
+ ***************************************/
+#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl %5,%1\n" \
+ "adcl %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl %5,%1\n" \
+ "sbbl %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mull %3" \
+ : "=a" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "%0" ((USItype)(u)), \
+ "rm" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divl %4" \
+ : "=a" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "rm" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("bsrl %1,%0" \
+ : "=r" (__cbtmp) : "rm" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define count_trailing_zeros(count, x) \
+ __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
+#ifndef UMUL_TIME
+#define UMUL_TIME 40
+#endif
+#ifndef UDIV_TIME
+#define UDIV_TIME 40
+#endif
+#endif /* 80x86 */
+
+
+/***************************************
+ ************** I860 *****************
+ ***************************************/
+#if defined (__i860__) && W_TYPE_SIZE == 32
+#define rshift_rhlc(r,h,l,c) \
+ __asm__ ("shr %3,r0,r0\n" \
+ "shrd %1,%2,%0" \
+ "=r" (r) : "r" (h), "r" (l), "rn" (c))
+#endif /* i860 */
+
+/***************************************
+ ************** I960 *****************
+ ***************************************/
+#if defined (__i960__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("cmpo 1,0\n" \
+ "addc %5,%4,%1\n" \
+ "addc %3,%2,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%dI" ((USItype)(ah)), \
+ "dI" ((USItype)(bh)), \
+ "%dI" ((USItype)(al)), \
+ "dI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("cmpo 0,0\n" \
+ "subc %5,%4,%1\n" \
+ "subc %3,%2,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "dI" ((USItype)(ah)), \
+ "dI" ((USItype)(bh)), \
+ "dI" ((USItype)(al)), \
+ "dI" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__xx.__ll) \
+ : "%dI" ((USItype)(u)), \
+ "dI" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("emul %2,%1,%0" \
+ : "=d" (__w) \
+ : "%dI" ((USItype)(u)), \
+ "dI" ((USItype)(v))); \
+ __w; })
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __nn; \
+ __nn.__i.__h = (nh); __nn.__i.__l = (nl); \
+ __asm__ ("ediv %d,%n,%0" \
+ : "=d" (__rq.__ll) \
+ : "dI" (__nn.__ll), \
+ "dI" ((USItype)(d))); \
+ (r) = __rq.__i.__l; (q) = __rq.__i.__h; \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("scanbit %1,%0" \
+ : "=r" (__cbtmp) \
+ : "r" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
+#if defined (__i960mx) /* what is the proper symbol to test??? */
+#define rshift_rhlc(r,h,l,c) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __nn; \
+ __nn.__i.__h = (h); __nn.__i.__l = (l); \
+ __asm__ ("shre %2,%1,%0" \
+ : "=d" (r) : "dI" (__nn.__ll), "dI" (c)); \
+ }
+#endif /* i960mx */
+#endif /* i960 */
+
+
+/***************************************
+ ************** 68000 ****************
+ ***************************************/
+#if (defined (__mc68000__) || defined (__mc68020__) || defined (__NeXT__) || defined(mc68020)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add%.l %5,%1\n" \
+ "addx%.l %3,%0" \
+ : "=d" ((USItype)(sh)), \
+ "=&d" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "d" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub%.l %5,%1\n" \
+ "subx%.l %3,%0" \
+ : "=d" ((USItype)(sh)), \
+ "=&d" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "d" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#if (defined (__mc68020__) || defined (__NeXT__) || defined(mc68020))
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("mulu%.l %3,%1:%0" \
+ : "=d" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "%0" ((USItype)(u)), \
+ "dmi" ((USItype)(v)))
+#define UMUL_TIME 45
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divu%.l %4,%1:%0" \
+ : "=d" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "dmi" ((USItype)(d)))
+#define UDIV_TIME 90
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("divs%.l %4,%1:%0" \
+ : "=d" ((USItype)(q)), \
+ "=d" ((USItype)(r)) \
+ : "0" ((USItype)(n0)), \
+ "1" ((USItype)(n1)), \
+ "dmi" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+ __asm__ ("bfffo %1{%b2:%b2},%0" \
+ : "=d" ((USItype)(count)) \
+ : "od" ((USItype)(x)), "n" (0))
+#define COUNT_LEADING_ZEROS_0 32
+#else /* not mc68020 */
+#define umul_ppmm(xh, xl, a, b) \
+ do { USItype __umul_tmp1, __umul_tmp2; \
+ __asm__ ("| Inlined umul_ppmm \n" \
+ " move%.l %5,%3 \n" \
+ " move%.l %2,%0 \n" \
+ " move%.w %3,%1 \n" \
+ " swap %3 \n" \
+ " swap %0 \n" \
+ " mulu %2,%1 \n" \
+ " mulu %3,%0 \n" \
+ " mulu %2,%3 \n" \
+ " swap %2 \n" \
+ " mulu %5,%2 \n" \
+ " add%.l %3,%2 \n" \
+ " jcc 1f \n" \
+ " add%.l %#0x10000,%0 \n" \
+ "1: move%.l %2,%3 \n" \
+ " clr%.w %2 \n" \
+ " swap %2 \n" \
+ " swap %3 \n" \
+ " clr%.w %3 \n" \
+ " add%.l %3,%1 \n" \
+ " addx%.l %2,%0 \n" \
+ " | End inlined umul_ppmm" \
+ : "=&d" ((USItype)(xh)), "=&d" ((USItype)(xl)), \
+ "=d" (__umul_tmp1), "=&d" (__umul_tmp2) \
+ : "%2" ((USItype)(a)), "d" ((USItype)(b))); \
+ } while (0)
+#define UMUL_TIME 100
+#define UDIV_TIME 400
+#endif /* not mc68020 */
+#endif /* mc68000 */
+
+
+/***************************************
+ ************** 88000 ****************
+ ***************************************/
+#if defined (__m88000__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addu.co %1,%r4,%r5\n" \
+ "addu.ci %0,%r2,%r3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rJ" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rJ" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subu.co %1,%r4,%r5\n" \
+ "subu.ci %0,%r2,%r3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rJ" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rJ" ((USItype)(bl)))
+#define count_leading_zeros(count, x) \
+ do { \
+ USItype __cbtmp; \
+ __asm__ ("ff1 %0,%1" \
+ : "=r" (__cbtmp) \
+ : "r" ((USItype)(x))); \
+ (count) = __cbtmp ^ 31; \
+ } while (0)
+#define COUNT_LEADING_ZEROS_0 63 /* sic */
+#if defined (__m88110__)
+#define umul_ppmm(wh, wl, u, v) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x; \
+ __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v)); \
+ (wh) = __x.__i.__h; \
+ (wl) = __x.__i.__l; \
+ } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __x, __q; \
+ __x.__i.__h = (n1); __x.__i.__l = (n0); \
+ __asm__ ("divu.d %0,%1,%2" \
+ : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d)); \
+ (r) = (n0) - __q.__l * (d); (q) = __q.__l; })
+#define UMUL_TIME 5
+#define UDIV_TIME 25
+#else
+#define UMUL_TIME 17
+#define UDIV_TIME 150
+#endif /* __m88110__ */
+#endif /* __m88000__ */
+
+/***************************************
+ ************** MIPS *****************
+ ***************************************/
+#if defined (__mips__) && W_TYPE_SIZE == 32
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3" \
+ : "=l" ((USItype)(w0)), \
+ "=h" ((USItype)(w1)) \
+ : "d" ((USItype)(u)), \
+ "d" ((USItype)(v)))
+#else
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("multu %2,%3 \n" \
+ "mflo %0 \n" \
+ "mfhi %1" \
+ : "=d" ((USItype)(w0)), \
+ "=d" ((USItype)(w1)) \
+ : "d" ((USItype)(u)), \
+ "d" ((USItype)(v)))
+#endif
+#define UMUL_TIME 10
+#define UDIV_TIME 100
+#endif /* __mips__ */
+
+/***************************************
+ ************** MIPS/64 **************
+ ***************************************/
+#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmultu %2,%3" \
+ : "=l" ((UDItype)(w0)), \
+ "=h" ((UDItype)(w1)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v)))
+#else
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("dmultu %2,%3 \n" \
+ "mflo %0 \n" \
+ "mfhi %1" \
+ : "=d" ((UDItype)(w0)), \
+ "=d" ((UDItype)(w1)) \
+ : "d" ((UDItype)(u)), \
+ "d" ((UDItype)(v)))
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 140
+#endif /* __mips__ */
+
+
+/***************************************
+ ************** 32000 ****************
+ ***************************************/
+#if defined (__ns32000__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__xx.__ll) \
+ : "%0" ((USItype)(u)), \
+ "g" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+ ({UDItype __w; \
+ __asm__ ("meid %2,%0" \
+ : "=g" (__w) \
+ : "%0" ((USItype)(u)), \
+ "g" ((USItype)(v))); \
+ __w; })
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ ({union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ __xx.__i.__h = (n1); __xx.__i.__l = (n0); \
+ __asm__ ("deid %2,%0" \
+ : "=g" (__xx.__ll) \
+ : "0" (__xx.__ll), \
+ "g" ((USItype)(d))); \
+ (r) = __xx.__i.__l; (q) = __xx.__i.__h; })
+#define count_trailing_zeros(count,x) \
+ do {
+ __asm__ ("ffsd %2,%0" \
+ : "=r" ((USItype) (count)) \
+ : "0" ((USItype) 0), \
+ "r" ((USItype) (x))); \
+ } while (0)
+#endif /* __ns32000__ */
+
+
+/***************************************
+ ************** PPC ******************
+ ***************************************/
+#if (defined (_ARCH_PPC) || defined (_IBMR2)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
+ __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ else \
+ __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%r" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "%r" ((USItype)(al)), \
+ "rI" ((USItype)(bl))); \
+ } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ if (__builtin_constant_p (ah) && (ah) == 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (ah) && (ah) ==~(USItype) 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) == 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0) \
+ __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ else \
+ __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "r" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "rI" ((USItype)(al)), \
+ "r" ((USItype)(bl))); \
+ } while (0)
+#define count_leading_zeros(count, x) \
+ __asm__ ("{cntlz|cntlzw} %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x)))
+#define COUNT_LEADING_ZEROS_0 32
+#if defined (_ARCH_PPC)
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhwu %0,%1,%2" \
+ : "=r" ((USItype) ph) \
+ : "%r" (__m0), \
+ "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+ do { \
+ SItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mulhw %0,%1,%2" \
+ : "=r" ((SItype) ph) \
+ : "%r" (__m0), \
+ "r" (__m1)); \
+ (pl) = __m0 * __m1; \
+ } while (0)
+#define SMUL_TIME 14
+#define UDIV_TIME 120
+#else
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mul %0,%2,%3" \
+ : "=r" ((USItype)(xh)), \
+ "=q" ((USItype)(xl)) \
+ : "r" (__m0), \
+ "r" (__m1)); \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define UMUL_TIME 8
+#define smul_ppmm(xh, xl, m0, m1) \
+ __asm__ ("mul %0,%2,%3" \
+ : "=r" ((SItype)(xh)), \
+ "=q" ((SItype)(xl)) \
+ : "r" (m0), \
+ "r" (m1))
+#define SMUL_TIME 4
+#define sdiv_qrnnd(q, r, nh, nl, d) \
+ __asm__ ("div %0,%2,%4" \
+ : "=r" ((SItype)(q)), "=q" ((SItype)(r)) \
+ : "r" ((SItype)(nh)), "1" ((SItype)(nl)), "r" ((SItype)(d)))
+#define UDIV_TIME 100
+#endif
+#endif /* Power architecture variants. */
+
+
+/***************************************
+ ************** PYR ******************
+ ***************************************/
+#if defined (__pyr__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addw %5,%1 \n" \
+ "addwc %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subw %5,%1 \n" \
+ "subwb %3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+/* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP. */
+#define umul_ppmm(w1, w0, u, v) \
+ ({union {UDItype __ll; \
+ struct {USItype __h, __l;} __i; \
+ } __xx; \
+ __asm__ ("movw %1,%R0 \n" \
+ "uemul %2,%0" \
+ : "=&r" (__xx.__ll) \
+ : "g" ((USItype) (u)), \
+ "g" ((USItype)(v))); \
+ (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#endif /* __pyr__ */
+
+
+/***************************************
+ ************** RT/ROMP **************
+ ***************************************/
+#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("a %1,%5 \n" \
+ "ae %0,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "r" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("s %1,%5\n" \
+ "se %0,%3" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "r" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "r" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+ do { \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ( \
+ "s r2,r2 \n" \
+ "mts r10,%2 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "m r2,%3 \n" \
+ "cas %0,r2,r0 \n" \
+ "mfs r10,%1" \
+ : "=r" ((USItype)(ph)), \
+ "=r" ((USItype)(pl)) \
+ : "%r" (__m0), \
+ "r" (__m1) \
+ : "r2"); \
+ (ph) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define UMUL_TIME 20
+#define UDIV_TIME 200
+#define count_leading_zeros(count, x) \
+ do { \
+ if ((x) >= 0x10000) \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x) >> 16)); \
+ else \
+ { \
+ __asm__ ("clz %0,%1" \
+ : "=r" ((USItype)(count)) \
+ : "r" ((USItype)(x))); \
+ (count) += 16; \
+ } \
+ } while (0)
+#endif /* RT/ROMP */
+
+
+/***************************************
+ ************** SH2 ******************
+ ***************************************/
+#if defined (__sh2__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ( \
+ "dmulu.l %2,%3\n" \
+ "sts macl,%1\n" \
+ "sts mach,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)) \
+ : "macl", "mach")
+#define UMUL_TIME 5
+#endif
+
+/***************************************
+ ************** SPARC ****************
+ ***************************************/
+#if defined (__sparc__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addcc %r4,%5,%1\n" \
+ "addx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "%rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "%rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subcc %r4,%5,%1\n" \
+ "subx %r2,%3,%0" \
+ : "=r" ((USItype)(sh)), \
+ "=&r" ((USItype)(sl)) \
+ : "rJ" ((USItype)(ah)), \
+ "rI" ((USItype)(bh)), \
+ "rJ" ((USItype)(al)), \
+ "rI" ((USItype)(bl)) \
+ __CLOBBER_CC)
+#if defined (__sparc_v8__)
+/* Don't match immediate range because, 1) it is not often useful,
+ 2) the 'I' flag thinks of the range as a 13 bit signed interval,
+ while we want to match a 13 bit interval, sign extended to 32 bits,
+ but INTERPRETED AS UNSIGNED. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)))
+#define UMUL_TIME 5
+#ifndef SUPERSPARC /* SuperSPARC's udiv only handles 53 bit dividends */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ USItype __q; \
+ __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0" \
+ : "=r" ((USItype)(__q)) \
+ : "r" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "r" ((USItype)(d))); \
+ (r) = (n0) - __q * (d); \
+ (q) = __q; \
+ } while (0)
+#define UDIV_TIME 25
+#endif /* SUPERSPARC */
+#else /* ! __sparc_v8__ */
+#if defined (__sparclite__)
+/* This has hardware multiply but not divide. It also has two additional
+ instructions scan (ffs from high bit) and divscc. */
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("umul %2,%3,%1;rd %%y,%0" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "r" ((USItype)(u)), \
+ "r" ((USItype)(v)))
+#define UMUL_TIME 5
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ __asm__ ("! Inlined udiv_qrnnd \n" \
+ " wr %%g0,%2,%%y ! Not a delayed write for sparclite \n" \
+ " tst %%g0 \n" \
+ " divscc %3,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%%g1 \n" \
+ " divscc %%g1,%4,%0 \n" \
+ " rd %%y,%1 \n" \
+ " bl,a 1f \n" \
+ " add %1,%4,%1 \n" \
+ "1: ! End of inline udiv_qrnnd" \
+ : "=r" ((USItype)(q)), \
+ "=r" ((USItype)(r)) \
+ : "r" ((USItype)(n1)), \
+ "r" ((USItype)(n0)), \
+ "rI" ((USItype)(d)) \
+ : "%g1" __AND_CLOBBER_CC)
+#define UDIV_TIME 37
+#define count_leading_zeros(count, x) \
+ __asm__ ("scan %1,0,%0" \
+ : "=r" ((USItype)(x)) \
+ : "r" ((USItype)(count)))
+/* Early sparclites return 63 for an argument of 0, but they warn that future
+ implementations might change this. Therefore, leave COUNT_LEADING_ZEROS_0
+ undefined. */
+#endif /* __sparclite__ */
+#endif /* __sparc_v8__ */
+/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd. */
+#ifndef umul_ppmm
+#define umul_ppmm(w1, w0, u, v) \
+ __asm__ ("! Inlined umul_ppmm \n" \
+ " wr %%g0,%2,%%y ! SPARC has 0-3 delay insn after a wr \n" \
+ " sra %3,31,%%g2 ! Don't move this insn \n" \
+ " and %2,%%g2,%%g2 ! Don't move this insn \n" \
+ " andcc %%g0,0,%%g1 ! Don't move this insn \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,%3,%%g1 \n" \
+ " mulscc %%g1,0,%%g1 \n" \
+ " add %%g1,%%g2,%0 \n" \
+ " rd %%y,%1" \
+ : "=r" ((USItype)(w1)), \
+ "=r" ((USItype)(w0)) \
+ : "%rI" ((USItype)(u)), \
+ "r" ((USItype)(v)) \
+ : "%g1", "%g2" __AND_CLOBBER_CC)
+#define UMUL_TIME 39 /* 39 instructions */
+#endif
+#ifndef udiv_qrnnd
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+ do { USItype __r; \
+ (q) = __udiv_qrnnd (&__r, (n1), (n0), (d)); \
+ (r) = __r; \
+ } while (0)
+extern USItype __udiv_qrnnd ();
+#define UDIV_TIME 140
+#endif /* LONGLONG_STANDALONE */
+#endif /* udiv_qrnnd */
+#endif /* __sparc__ */
+
+
+/***************************************
+ ************** VAX ******************
+ ***************************************/
+#if defined (__vax__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("addl2 %5,%1\n" \
+ "adwc %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "%0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "%1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("subl2 %5,%1\n" \
+ "sbwc %3,%0" \
+ : "=g" ((USItype)(sh)), \
+ "=&g" ((USItype)(sl)) \
+ : "0" ((USItype)(ah)), \
+ "g" ((USItype)(bh)), \
+ "1" ((USItype)(al)), \
+ "g" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {UDItype __ll; \
+ struct {USItype __l, __h;} __i; \
+ } __xx; \
+ USItype __m0 = (m0), __m1 = (m1); \
+ __asm__ ("emul %1,%2,$0,%0" \
+ : "=g" (__xx.__ll) \
+ : "g" (__m0), \
+ "g" (__m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ (xh) += ((((SItype) __m0 >> 31) & __m1) \
+ + (((SItype) __m1 >> 31) & __m0)); \
+ } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+ do { \
+ union {DItype __ll; \
+ struct {SItype __l, __h;} __i; \
+ } __xx; \
+ __xx.__i.__h = n1; __xx.__i.__l = n0; \
+ __asm__ ("ediv %3,%2,%0,%1" \
+ : "=g" (q), "=g" (r) \
+ : "g" (__xx.__ll), "g" (d)); \
+ } while (0)
+#endif /* __vax__ */
+
+
+/***************************************
+ ************** Z8000 ****************
+ ***************************************/
+#if defined (__z8000__) && W_TYPE_SIZE == 16
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ __asm__ ("add %H1,%H5\n\tadc %H0,%H3" \
+ : "=r" ((unsigned int)(sh)), \
+ "=&r" ((unsigned int)(sl)) \
+ : "%0" ((unsigned int)(ah)), \
+ "r" ((unsigned int)(bh)), \
+ "%1" ((unsigned int)(al)), \
+ "rQR" ((unsigned int)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ __asm__ ("sub %H1,%H5\n\tsbc %H0,%H3" \
+ : "=r" ((unsigned int)(sh)), \
+ "=&r" ((unsigned int)(sl)) \
+ : "0" ((unsigned int)(ah)), \
+ "r" ((unsigned int)(bh)), \
+ "1" ((unsigned int)(al)), \
+ "rQR" ((unsigned int)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+ do { \
+ union {long int __ll; \
+ struct {unsigned int __h, __l;} __i; \
+ } __xx; \
+ unsigned int __m0 = (m0), __m1 = (m1); \
+ __asm__ ("mult %S0,%H3" \
+ : "=r" (__xx.__i.__h), \
+ "=r" (__xx.__i.__l) \
+ : "%1" (__m0), \
+ "rQR" (__m1)); \
+ (xh) = __xx.__i.__h; (xl) = __xx.__i.__l; \
+ (xh) += ((((signed int) __m0 >> 15) & __m1) \
+ + (((signed int) __m1 >> 15) & __m0)); \
+ } while (0)
+#endif /* __z8000__ */
+
+#endif /* __GNUC__ */
+#endif /* !__riscos__ */
+
+
+/***************************************
+ *********** Generic Versions ********
+ ***************************************/
+#if !defined (umul_ppmm) && defined (__umulsidi3)
+#define umul_ppmm(ph, pl, m0, m1) \
+ { \
+ UDWtype __ll = __umulsidi3 (m0, m1); \
+ ph = (UWtype) (__ll >> W_TYPE_SIZE); \
+ pl = (UWtype) __ll; \
+ }
+#endif
+
+#if !defined (__umulsidi3)
+#define __umulsidi3(u, v) \
+ ({UWtype __hi, __lo; \
+ umul_ppmm (__hi, __lo, u, v); \
+ ((UDWtype) __hi << W_TYPE_SIZE) | __lo; })
+#endif
+
+/* If this machine has no inline assembler, use C macros. */
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ UWtype __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - (__x > (al)); \
+ (sl) = __x; \
+ } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __x0, __x1, __x2, __x3; \
+ UHWtype __ul, __vl, __uh, __vh; \
+ UWtype __u = (u), __v = (v); \
+ \
+ __ul = __ll_lowpart (__u); \
+ __uh = __ll_highpart (__u); \
+ __vl = __ll_lowpart (__v); \
+ __vh = __ll_highpart (__v); \
+ \
+ __x0 = (UWtype) __ul * __vl; \
+ __x1 = (UWtype) __ul * __vh; \
+ __x2 = (UWtype) __uh * __vl; \
+ __x3 = (UWtype) __uh * __vh; \
+ \
+ __x1 += __ll_highpart (__x0);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += __ll_B; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + __ll_highpart (__x1); \
+ (w0) = (__ll_lowpart (__x1) << W_TYPE_SIZE/2) + __ll_lowpart (__x0);\
+ } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define smul_ppmm(w1, w0, u, v) \
+ do { \
+ UWtype __w1; \
+ UWtype __m0 = (u), __m1 = (v); \
+ umul_ppmm (__w1, w0, __m0, __m1); \
+ (w1) = __w1 - (-(__m0 >> (W_TYPE_SIZE - 1)) & __m1) \
+ - (-(__m1 >> (W_TYPE_SIZE - 1)) & __m0); \
+ } while (0)
+#endif
+
+/* Define this unconditionally, so it can be used for debugging. */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+ do { \
+ UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m; \
+ __d1 = __ll_highpart (d); \
+ __d0 = __ll_lowpart (d); \
+ \
+ __r1 = (n1) % __d1; \
+ __q1 = (n1) / __d1; \
+ __m = (UWtype) __q1 * __d0; \
+ __r1 = __r1 * __ll_B | __ll_highpart (n0); \
+ if (__r1 < __m) \
+ { \
+ __q1--, __r1 += (d); \
+ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+ if (__r1 < __m) \
+ __q1--, __r1 += (d); \
+ } \
+ __r1 -= __m; \
+ \
+ __r0 = __r1 % __d1; \
+ __q0 = __r1 / __d1; \
+ __m = (UWtype) __q0 * __d0; \
+ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \
+ if (__r0 < __m) \
+ { \
+ __q0--, __r0 += (d); \
+ if (__r0 >= (d)) \
+ if (__r0 < __m) \
+ __q0--, __r0 += (d); \
+ } \
+ __r0 -= __m; \
+ \
+ (q) = (UWtype) __q1 * __ll_B | __q0; \
+ (r) = __r0; \
+ } while (0)
+
+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
+ __udiv_w_sdiv (defined in libgcc or elsewhere). */
+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
+#define udiv_qrnnd(q, r, nh, nl, d) \
+ do { \
+ UWtype __r; \
+ (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d); \
+ (r) = __r; \
+ } while (0)
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+#if !defined (count_leading_zeros)
+extern
+#ifdef __STDC__
+const
+#endif
+unsigned char __clz_tab[];
+#define MPI_INTERNAL_NEED_CLZ_TAB 1
+#define count_leading_zeros(count, x) \
+ do { \
+ UWtype __xr = (x); \
+ UWtype __a; \
+ \
+ if (W_TYPE_SIZE <= 32) \
+ { \
+ __a = __xr < ((UWtype) 1 << 2*__BITS4) \
+ ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4) \
+ : (__xr < ((UWtype) 1 << 3*__BITS4) ? 2*__BITS4 : 3*__BITS4);\
+ } \
+ else \
+ { \
+ for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8) \
+ if (((__xr >> __a) & 0xff) != 0) \
+ break; \
+ } \
+ \
+ (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a); \
+ } while (0)
+/* This version gives a well-defined value for zero. */
+#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
+#endif
+
+#if !defined (count_trailing_zeros)
+/* Define count_trailing_zeros using count_leading_zeros. The latter might be
+ defined in asm, but if it is not, the C version above is good enough. */
+#define count_trailing_zeros(count, x) \
+ do { \
+ UWtype __ctz_x = (x); \
+ UWtype __ctz_c; \
+ count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x); \
+ (count) = W_TYPE_SIZE - 1 - __ctz_c; \
+ } while (0)
+#endif
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+#define UDIV_NEEDS_NORMALIZATION 0
+#endif
diff -urN linux-2.6.10/net/ipv6/hip/crypto/Makefile linux-2.6.10-hip/net/ipv6/hip/crypto/Makefile
--- linux-2.6.10/net/ipv6/hip/crypto/Makefile 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/Makefile 2005-03-03 00:01:38.000000000 +0200
@@ -0,0 +1,14 @@
+# rocks
+#
+
+ifneq ($(SUBARCH),)
+REALARCH := $(SUBARCH)
+else
+REALARCH := $(ARCH)
+endif
+
+#O_TARGET := cryptocode.o
+obj-y += $(REALARCH)/ mpi-bit.o mpi-div.o mpi-inline.o mpi-mpow.o mpi-pow.o
+obj-y += mpicoder.o mpih-mul.o mpi-add.o mpi-cmp.o mpi-gcd.o mpi-inv.o
+obj-y += mpi-mul.o mpi-scan.o mpih-div.o mpiutil.o kernel-interface.o
+obj-y += dh.o dsa.o rsa.o
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-add.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-add.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-add.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-add.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,239 @@
+/* mpi-add.c - MPI functions
+ * Copyright (C) 1994, 1996, 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#include "kernel-interface.h"
+
+/****************
+ * Add the unsigned integer V to the mpi-integer U and store the
+ * result in W. U and V may be the same.
+ */
+void
+gcry_mpi_add_ui(MPI w, MPI u, unsigned long v )
+{
+ mpi_ptr_t wp, up;
+ mpi_size_t usize, wsize;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+ wsign = 0;
+
+ /* If not space for W (and possible carry), increase space. */
+ wsize = usize + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize);
+
+ /* These must be after realloc (U may be the same as W). */
+ up = u->d;
+ wp = w->d;
+
+ if( !usize ) { /* simple */
+ wp[0] = v;
+ wsize = v? 1:0;
+ }
+ else if( !usign ) { /* mpi is not negative */
+ mpi_limb_t cy;
+ cy = _gcry_mpih_add_1(wp, up, usize, v);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ }
+ else { /* The signs are different. Need exact comparison to determine
+ * which operand to subtract from which. */
+ if( usize == 1 && up[0] < v ) {
+ wp[0] = v - up[0];
+ wsize = 1;
+ }
+ else {
+ _gcry_mpih_sub_1(wp, up, usize, v);
+ /* Size can decrease with at most one limb. */
+ wsize = usize - (wp[usize-1]==0);
+ wsign = 1;
+ }
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+void
+gcry_mpi_add(MPI w, MPI u, MPI v)
+{
+ mpi_ptr_t wp, up, vp;
+ mpi_size_t usize, vsize, wsize;
+ int usign, vsign, wsign;
+
+ if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+ usize = v->nlimbs;
+ usign = v->sign;
+ vsize = u->nlimbs;
+ vsign = u->sign;
+ wsize = usize + 1;
+ RESIZE_IF_NEEDED(w, wsize);
+ /* These must be after realloc (u or v may be the same as w). */
+ up = v->d;
+ vp = u->d;
+ }
+ else {
+ usize = u->nlimbs;
+ usign = u->sign;
+ vsize = v->nlimbs;
+ vsign = v->sign;
+ wsize = usize + 1;
+ RESIZE_IF_NEEDED(w, wsize);
+ /* These must be after realloc (u or v may be the same as w). */
+ up = u->d;
+ vp = v->d;
+ }
+ wp = w->d;
+ wsign = 0;
+
+ if( !vsize ) { /* simple */
+ MPN_COPY(wp, up, usize );
+ wsize = usize;
+ wsign = usign;
+ }
+ else if( usign != vsign ) { /* different sign */
+ /* This test is right since USIZE >= VSIZE */
+ if( usize != vsize ) {
+ _gcry_mpih_sub(wp, up, usize, vp, vsize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ wsign = usign;
+ }
+ else if( _gcry_mpih_cmp(up, vp, usize) < 0 ) {
+ _gcry_mpih_sub_n(wp, vp, up, usize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ if( !usign )
+ wsign = 1;
+ }
+ else {
+ _gcry_mpih_sub_n(wp, up, vp, usize);
+ wsize = usize;
+ MPN_NORMALIZE(wp, wsize);
+ if( usign )
+ wsign = 1;
+ }
+ }
+ else { /* U and V have same sign. Add them. */
+ mpi_limb_t cy = _gcry_mpih_add(wp, up, usize, vp, vsize);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ if( usign )
+ wsign = 1;
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+/****************
+ * Subtract the unsigned integer V from the mpi-integer U and store the
+ * result in W.
+ */
+void
+gcry_mpi_sub_ui(MPI w, MPI u, unsigned long v )
+{
+ mpi_ptr_t wp, up;
+ mpi_size_t usize, wsize;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+ wsign = 0;
+
+ /* If not space for W (and possible carry), increase space. */
+ wsize = usize + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize);
+
+ /* These must be after realloc (U may be the same as W). */
+ up = u->d;
+ wp = w->d;
+
+ if( !usize ) { /* simple */
+ wp[0] = v;
+ wsize = v? 1:0;
+ wsign = 1;
+ }
+ else if( usign ) { /* mpi and v are negative */
+ mpi_limb_t cy;
+ cy = _gcry_mpih_add_1(wp, up, usize, v);
+ wp[usize] = cy;
+ wsize = usize + cy;
+ }
+ else { /* The signs are different. Need exact comparison to determine
+ * which operand to subtract from which. */
+ if( usize == 1 && up[0] < v ) {
+ wp[0] = v - up[0];
+ wsize = 1;
+ wsign = 1;
+ }
+ else {
+ _gcry_mpih_sub_1(wp, up, usize, v);
+ /* Size can decrease with at most one limb. */
+ wsize = usize - (wp[usize-1]==0);
+ }
+ }
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+void
+gcry_mpi_sub(MPI w, MPI u, MPI v)
+{
+ if( w == v ) {
+ MPI vv = mpi_copy(v);
+ vv->sign = !vv->sign;
+ gcry_mpi_add( w, u, vv );
+ mpi_free(vv);
+ }
+ else {
+ /* fixme: this is not thread-save (we temp. modify v) */
+ v->sign = !v->sign;
+ gcry_mpi_add( w, u, v );
+ v->sign = !v->sign;
+ }
+}
+
+
+void
+gcry_mpi_addm( MPI w, MPI u, MPI v, MPI m)
+{
+ gcry_mpi_add(w, u, v);
+ _gcry_mpi_fdiv_r( w, w, m );
+}
+
+void
+gcry_mpi_subm( MPI w, MPI u, MPI v, MPI m)
+{
+ gcry_mpi_sub(w, u, v);
+ _gcry_mpi_fdiv_r( w, w, m );
+}
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-bit.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-bit.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-bit.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-bit.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,259 @@
+/* mpi-bit.c - MPI bit level fucntions
+ * Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*
+#include
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+#include "longlong.h"
+
+
+#ifdef MPI_INTERNAL_NEED_CLZ_TAB
+#ifdef __STDC__
+const
+#endif
+unsigned char
+__clz_tab[] =
+{
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+#endif
+
+
+#define A_LIMB_1 ((mpi_limb_t)1)
+
+
+/****************
+ * Sometimes we have MSL (most significant limbs) which are 0;
+ * this is for some reasons not good, so this function removes them.
+ */
+void
+_gcry_mpi_normalize( MPI a )
+{
+ if( mpi_is_opaque(a) )
+ return;
+
+ for( ; a->nlimbs && !a->d[a->nlimbs-1]; a->nlimbs-- )
+ ;
+}
+
+
+
+/****************
+ * Return the number of bits in A.
+ */
+unsigned int
+gcry_mpi_get_nbits( MPI a )
+{
+ unsigned n;
+
+ if( mpi_is_opaque(a) ) {
+ return a->sign; /* which holds the number of bits */
+ }
+
+ _gcry_mpi_normalize( a );
+ if( a->nlimbs ) {
+ mpi_limb_t alimb = a->d[a->nlimbs-1];
+ if( alimb )
+ count_leading_zeros( n, alimb );
+ else
+ n = BITS_PER_MPI_LIMB;
+ n = BITS_PER_MPI_LIMB - n + (a->nlimbs-1) * BITS_PER_MPI_LIMB;
+ }
+ else
+ n = 0;
+ return n;
+}
+
+
+/****************
+ * Test whether bit N is set.
+ */
+int
+gcry_mpi_test_bit( MPI a, unsigned int n )
+{
+ unsigned int limbno, bitno;
+ mpi_limb_t limb;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs )
+ return 0; /* too far left: this is a 0 */
+ limb = a->d[limbno];
+ return (limb & (A_LIMB_1 << bitno))? 1: 0;
+}
+
+
+/****************
+ * Set bit N of A.
+ */
+void
+gcry_mpi_set_bit( MPI a, unsigned int n )
+{
+ unsigned int limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs ) { /* resize */
+ if( a->alloced >= limbno )
+ mpi_resize(a, limbno+1 );
+ a->nlimbs = limbno+1;
+ }
+ a->d[limbno] |= (A_LIMB_1<= a->nlimbs ) { /* resize */
+ if( a->alloced >= limbno )
+ mpi_resize(a, limbno+1 );
+ a->nlimbs = limbno+1;
+ }
+ a->d[limbno] |= (A_LIMB_1<d[limbno] &= ~(A_LIMB_1 << bitno);
+ a->nlimbs = limbno+1;
+}
+
+/****************
+ * clear bit N of A and all bits above
+ */
+void
+gcry_mpi_clear_highbit( MPI a, unsigned int n )
+{
+ unsigned int limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs )
+ return; /* not allocated, so need to clear bits :-) */
+
+ for( ; bitno < BITS_PER_MPI_LIMB; bitno++ )
+ a->d[limbno] &= ~(A_LIMB_1 << bitno);
+ a->nlimbs = limbno+1;
+}
+
+/****************
+ * Clear bit N of A.
+ */
+void
+gcry_mpi_clear_bit( MPI a, unsigned int n )
+{
+ unsigned int limbno, bitno;
+
+ limbno = n / BITS_PER_MPI_LIMB;
+ bitno = n % BITS_PER_MPI_LIMB;
+
+ if( limbno >= a->nlimbs )
+ return; /* don't need to clear this bit, it's to far to left */
+ a->d[limbno] &= ~(A_LIMB_1 << bitno);
+}
+
+
+/****************
+ * Shift A by N bits to the right
+ * FIXME: should use alloc_limb if X and A are same.
+ */
+void
+gcry_mpi_rshift( MPI x, MPI a, unsigned n )
+{
+ mpi_ptr_t xp;
+ mpi_size_t xsize;
+
+ xsize = a->nlimbs;
+ x->sign = a->sign;
+ RESIZE_IF_NEEDED(x, xsize);
+ xp = x->d;
+
+ if( xsize ) {
+ _gcry_mpih_rshift( xp, a->d, xsize, n);
+ MPN_NORMALIZE( xp, xsize);
+ }
+ x->nlimbs = xsize;
+}
+
+
+/****************
+ * Shift A by COUNT limbs to the left
+ * This is used only within the MPI library
+ */
+void
+_gcry_mpi_lshift_limbs( MPI a, unsigned int count )
+{
+ mpi_ptr_t ap = a->d;
+ int n = a->nlimbs;
+ int i;
+
+ if( !count || !n )
+ return;
+
+ RESIZE_IF_NEEDED( a, n+count );
+
+ for( i = n-1; i >= 0; i-- )
+ ap[i+count] = ap[i];
+ for(i=0; i < count; i++ )
+ ap[i] = 0;
+ a->nlimbs += count;
+}
+
+
+/****************
+ * Shift A by COUNT limbs to the right
+ * This is used only within the MPI library
+ */
+void
+_gcry_mpi_rshift_limbs( MPI a, unsigned int count )
+{
+ mpi_ptr_t ap = a->d;
+ mpi_size_t n = a->nlimbs;
+ unsigned int i;
+
+ if( count >= n ) {
+ a->nlimbs = 0;
+ return;
+ }
+
+ for( i = 0; i < n - count; i++ )
+ ap[i] = ap[i+count];
+ ap[i] = 0;
+ a->nlimbs -= count;
+}
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-cmp.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-cmp.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-cmp.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-cmp.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,76 @@
+/* mpi-cmp.c - MPI functions
+ * Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#include "kernel-interface.h"
+
+int
+gcry_mpi_cmp_ui( MPI u, unsigned long v )
+{
+ mpi_limb_t limb = v;
+
+ _gcry_mpi_normalize( u );
+ if( !u->nlimbs && !limb )
+ return 0;
+ if( u->sign )
+ return -1;
+ if( u->nlimbs > 1 )
+ return 1;
+
+ if( u->d[0] == limb )
+ return 0;
+ else if( u->d[0] > limb )
+ return 1;
+ else
+ return -1;
+}
+
+int
+gcry_mpi_cmp( MPI u, MPI v )
+{
+ mpi_size_t usize, vsize;
+ int cmp;
+
+ _gcry_mpi_normalize( u );
+ _gcry_mpi_normalize( v );
+ usize = u->nlimbs;
+ vsize = v->nlimbs;
+
+ if( !u->sign && v->sign ) {
+ return 1;
+ }
+ if( u->sign && !v->sign )
+ return -1;
+ if( usize != vsize && !u->sign && !v->sign )
+ return usize - vsize;
+ if( usize != vsize && u->sign && v->sign )
+ return vsize + usize;
+ if( !usize ) {
+ return 0;
+ }
+ if( !(cmp = _gcry_mpih_cmp( u->d, v->d, usize )) ) {
+ return 0;
+ }
+ if( (cmp < 0?1:0) == (u->sign?1:0)) {
+ return 1;
+ }
+ return -1;
+}
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpicoder.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpicoder.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpicoder.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpicoder.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,652 @@
+/* mpicoder.c - Coder for the external representation of MPIs
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*#include
+#include
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+
+#define MAX_EXTERN_MPI_BITS 16384
+
+
+#if 0
+static MPI
+mpi_read_from_buffer(byte *buffer, unsigned *ret_nread, int secure)
+{
+ int i, j;
+ unsigned int nbits, nbytes, nlimbs, nread=0;
+ mpi_limb_t a;
+ MPI val = MPI_NULL;
+
+ if( *ret_nread < 2 )
+ goto leave;
+ nbits = buffer[0] << 8 | buffer[1];
+ if( nbits > MAX_EXTERN_MPI_BITS ) {
+ log_error("mpi too large (%u bits)\n", nbits);
+ goto leave;
+ }
+ else if( !nbits ) {
+ log_error("an mpi of size 0 is not allowed\n");
+ goto leave;
+ }
+ buffer += 2;
+ nread = 2;
+
+ nbytes = (nbits+7) / 8;
+ nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
+ val = secure? mpi_alloc_secure( nlimbs )
+ : mpi_alloc( nlimbs );
+ i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+ i %= BYTES_PER_MPI_LIMB;
+ j= val->nlimbs = nlimbs;
+ val->sign = 0;
+ for( ; j > 0; j-- ) {
+ a = 0;
+ for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+ if( ++nread > *ret_nread )
+ log_bug("mpi larger than buffer");
+ a <<= 8;
+ a |= *buffer++;
+ }
+ i = 0;
+ val->d[j-1] = a;
+ }
+
+ leave:
+ *ret_nread = nread;
+ return val;
+}
+#endif
+
+/****************
+ * Make an mpi from a hex character string.
+ */
+static int
+mpi_fromstr(MPI val, const char *str)
+{
+ int sign=0, prepend_zero=0, i, j, c, c1, c2;
+ unsigned nbits, nbytes, nlimbs;
+ mpi_limb_t a;
+
+ if( *str == '-' ) {
+ sign = 1;
+ str++;
+ }
+
+ /* skip optional hex prefix */
+ if ( *str == '0' && str[1] == 'x' ) {
+ str += 2;
+ }
+
+ nbits = strlen(str)*4;
+ if( nbits % 8 )
+ prepend_zero = 1;
+ nbytes = (nbits+7) / 8;
+ nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
+ if( val->alloced < nlimbs )
+ mpi_resize(val, nlimbs );
+ i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+ i %= BYTES_PER_MPI_LIMB;
+ j= val->nlimbs = nlimbs;
+ val->sign = sign;
+ for( ; j > 0; j-- ) {
+ a = 0;
+ for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+ if( prepend_zero ) {
+ c1 = '0';
+ prepend_zero = 0;
+ }
+ else
+ c1 = *str++;
+// assert(c1);
+ c2 = *str++;
+// assert(c2);
+ if( c1 >= '0' && c1 <= '9' )
+ c = c1 - '0';
+ else if( c1 >= 'a' && c1 <= 'f' )
+ c = c1 - 'a' + 10;
+ else if( c1 >= 'A' && c1 <= 'F' )
+ c = c1 - 'A' + 10;
+ else {
+ mpi_clear(val);
+ return 1;
+ }
+ c <<= 4;
+ if( c2 >= '0' && c2 <= '9' )
+ c |= c2 - '0';
+ else if( c2 >= 'a' && c2 <= 'f' )
+ c |= c2 - 'a' + 10;
+ else if( c2 >= 'A' && c2 <= 'F' )
+ c |= c2 - 'A' + 10;
+ else {
+ mpi_clear(val);
+ return 1;
+ }
+ a <<= 8;
+ a |= c;
+ }
+ i = 0;
+ val->d[j-1] = a;
+ }
+
+ return 0;
+}
+
+
+/****************
+ * print an MPI to the given stream and return the number of characters
+ * printed.
+ * FIXME: Replace this by the more generic gcry_mpi_print()
+ */
+#if 0
+static int
+mpi_print( FILE *fp, MPI a, int mode )
+{
+ int i, n=0;
+
+ if( a == MPI_NULL )
+ return fprintf(fp, "[MPI_NULL]");
+ if( !mode ) {
+ unsigned int n1;
+ n1 = mpi_get_nbits(a);
+ n += fprintf(fp, "[%u bits]", n1);
+ }
+ else {
+ if( a->sign )
+ putc('-', fp);
+ #if BYTES_PER_MPI_LIMB == 2
+ #define X "4"
+ #elif BYTES_PER_MPI_LIMB == 4
+ #define X "8"
+ #elif BYTES_PER_MPI_LIMB == 8
+ #define X "16"
+ #else
+ #error please define the format here
+ #endif
+ for(i=a->nlimbs; i > 0 ; i-- ) {
+ n += fprintf(fp, i!=a->nlimbs? "%0" X "lX":"%lX", (ulong)a->d[i-1]);
+ #undef X
+ }
+ if( !a->nlimbs )
+ putc('0', fp );
+ }
+ return n;
+}
+#endif
+
+#if 0
+#if __GNUC__ >= 2
+#warning We should move this function to elsewhere
+#endif
+void
+_gcry_log_mpidump( const char *text, MPI a )
+{
+ FILE *fp = stderr; /* used to be log_stream() */
+
+ /* FIXME: Replace this function by a g10_log_xxx one */
+ fprintf(fp,"%s: ",text);
+ mpi_print(fp, a, 1 );
+ fputc('\n', fp);
+}
+
+#endif
+
+/****************
+ * Return an m_alloced buffer with the MPI (msb first).
+ * NBYTES receives the length of this buffer. Caller must free the
+ * return string (This function does return a 0 byte buffer with NBYTES
+ * set to zero if the value of A is zero. If sign is not NULL, it will
+ * be set to the sign of the A.
+ */
+static byte *
+do_get_buffer( MPI a, unsigned *nbytes, int *sign, int force_secure )
+{
+ byte *p, *buffer;
+ mpi_limb_t alimb;
+ int i;
+ size_t n;
+
+ if( sign )
+ *sign = a->sign;
+ *nbytes = a->nlimbs * BYTES_PER_MPI_LIMB;
+ n = *nbytes? *nbytes:1; /* allocate at least one byte */
+ p = buffer = force_secure || mpi_is_secure(a) ? gcry_xmalloc_secure(n)
+ : gcry_xmalloc(n);
+
+ for(i=a->nlimbs-1; i >= 0; i-- ) {
+ alimb = a->d[i];
+ #if BYTES_PER_MPI_LIMB == 4
+ *p++ = alimb >> 24;
+ *p++ = alimb >> 16;
+ *p++ = alimb >> 8;
+ *p++ = alimb ;
+ #elif BYTES_PER_MPI_LIMB == 8
+ *p++ = alimb >> 56;
+ *p++ = alimb >> 48;
+ *p++ = alimb >> 40;
+ *p++ = alimb >> 32;
+ *p++ = alimb >> 24;
+ *p++ = alimb >> 16;
+ *p++ = alimb >> 8;
+ *p++ = alimb ;
+ #else
+ #error please implement for this limb size.
+ #endif
+ }
+
+#if defined(CONFIG_HIP) || defined(CONFIG_HIP_MODULE)
+ return buffer;
+ /* we don't want to shift/lose our 0x00 bytes */
+#endif
+
+ /* this is sub-optimal but we need to do the shift oepration because
+ * the caller has to free the returned buffer */
+ for(p=buffer; !*p && *nbytes; p++, --*nbytes )
+ ;
+ if( p != buffer )
+ memmove(buffer,p, *nbytes);
+ return buffer;
+}
+
+
+byte *
+_gcry_mpi_get_buffer( MPI a, unsigned *nbytes, int *sign )
+{
+ return do_get_buffer( a, nbytes, sign, 0 );
+}
+
+byte *
+_gcry_mpi_get_secure_buffer( MPI a, unsigned *nbytes, int *sign )
+{
+ return do_get_buffer( a, nbytes, sign, 1 );
+}
+
+/****************
+ * Use BUFFER to update MPI.
+ */
+void
+_gcry_mpi_set_buffer( MPI a, const byte *buffer, unsigned nbytes, int sign )
+{
+ const byte *p;
+ mpi_limb_t alimb;
+ int nlimbs;
+ int i;
+
+ nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+ RESIZE_IF_NEEDED(a, nlimbs);
+ a->sign = sign;
+
+ for(i=0, p = buffer+nbytes-1; p >= buffer+BYTES_PER_MPI_LIMB; ) {
+#if BYTES_PER_MPI_LIMB == 4
+ alimb = *p-- ;
+ alimb |= *p-- << 8 ;
+ alimb |= *p-- << 16 ;
+ alimb |= *p-- << 24 ;
+#elif BYTES_PER_MPI_LIMB == 8
+ alimb = (mpi_limb_t)*p-- ;
+ alimb |= (mpi_limb_t)*p-- << 8 ;
+ alimb |= (mpi_limb_t)*p-- << 16 ;
+ alimb |= (mpi_limb_t)*p-- << 24 ;
+ alimb |= (mpi_limb_t)*p-- << 32 ;
+ alimb |= (mpi_limb_t)*p-- << 40 ;
+ alimb |= (mpi_limb_t)*p-- << 48 ;
+ alimb |= (mpi_limb_t)*p-- << 56 ;
+#else
+#error please implement for this limb size.
+#endif
+ a->d[i++] = alimb;
+ }
+ if( p >= buffer ) {
+#if BYTES_PER_MPI_LIMB == 4
+ alimb = *p-- ;
+ if( p >= buffer ) alimb |= *p-- << 8 ;
+ if( p >= buffer ) alimb |= *p-- << 16 ;
+ if( p >= buffer ) alimb |= *p-- << 24 ;
+#elif BYTES_PER_MPI_LIMB == 8
+ alimb = (mpi_limb_t)*p-- ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 8 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 16 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 24 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 32 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 40 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 48 ;
+ if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 56 ;
+#else
+#error please implement for this limb size.
+#endif
+ a->d[i++] = alimb;
+ }
+ a->nlimbs = i;
+// assert( i == nlimbs );
+}
+
+
+
+int
+gcry_mpi_scan( struct gcry_mpi **ret_mpi, enum gcry_mpi_format format,
+ const char *buffer, size_t *nbytes )
+{
+ struct gcry_mpi *a = NULL;
+ unsigned int len;
+
+
+ len = nbytes? *nbytes : (format == GCRYMPI_FMT_SSH? 0 : strlen(buffer));
+
+#if 0
+ /* TODO: add a way to allocate the MPI in secure memory
+ * Hmmm: maybe it is better to retrieve this information from
+ * the provided buffer. */
+#if __GNUC__ >= 2
+#warning secure memory is not used here.
+#endif
+#endif
+ if( format == GCRYMPI_FMT_STD ) {
+ const byte *s = buffer;
+
+ a = mpi_alloc( (len+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+ if( len ) { /* not zero */
+ a->sign = *s & 0x80;
+ if( a->sign ) {
+ /* FIXME: we have to convert from 2compl to magnitude format */
+ mpi_free(a);
+ return GCRYERR_INTERNAL;
+ }
+ else
+ _gcry_mpi_set_buffer( a, s, len, 0 );
+ }
+ if( ret_mpi ) {
+ mpi_normalize ( a );
+ *ret_mpi = a;
+ }
+ else
+ mpi_free(a);
+ return 0;
+ }
+
+ else if( format == GCRYMPI_FMT_USG ) {
+ a = mpi_alloc( (len+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+ if( len ) /* not zero */
+ _gcry_mpi_set_buffer( a, buffer, len, 0 );
+ if( ret_mpi ) {
+ mpi_normalize ( a );
+ *ret_mpi = a;
+ }
+ else
+ mpi_free(a);
+ return 0;
+ }
+#if 0
+ else if( format == GCRYMPI_FMT_PGP ) {
+ a = mpi_read_from_buffer( (char*)buffer, &len, 0 );
+ if( nbytes )
+ *nbytes = len;
+ if( ret_mpi && a ) {
+ mpi_normalize ( a );
+ *ret_mpi = a;
+ }
+ else
+ mpi_free(a);
+ return a? 0 : GCRYERR_INV_OBJ;
+ }
+ else if( format == GCRYMPI_FMT_SSH ) {
+ const byte *s = buffer;
+ size_t n;
+
+ if( len && len < 4 )
+ return GCRYERR_TOO_SHORT;
+ n = s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3];
+ s += 4;
+ if (len)
+ len -= 4;
+ if( len && n > len )
+ return GCRYERR_TOO_LARGE; /* or should it be too_short */
+
+ a = mpi_alloc( (n+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+ if( n ) { /* not zero */
+ a->sign = *s & 0x80;
+ if( a->sign ) {
+ /* FIXME: we have to convert from 2compl to magnitude format */
+ mpi_free(a);
+ return GCRYERR_INTERNAL;
+ }
+ else
+ _gcry_mpi_set_buffer( a, s, n, 0 );
+ }
+ if( nbytes )
+ *nbytes = n+4;
+ if( ret_mpi ) {
+ mpi_normalize ( a );
+ *ret_mpi = a;
+ }
+ else
+ mpi_free(a);
+ return 0;
+ }
+#endif /* 0 */
+ else if( format == GCRYMPI_FMT_HEX ) {
+ if( nbytes )
+ return GCRYERR_INV_ARG; /* can only handle C strings for now */
+ a = mpi_alloc(0);
+ if( mpi_fromstr( a, buffer ) )
+ return GCRYERR_INV_OBJ;
+ if( ret_mpi ) {
+ mpi_normalize ( a );
+ *ret_mpi = a;
+ }
+ else
+ mpi_free(a);
+ return 0;
+ }
+ else
+ return GCRYERR_INV_ARG;
+}
+
+/****************
+ * Write A using FORMAT into buffer which has a length of *NBYTES.
+ * Returns the number of bytes actually written in nbytes.
+ * Buffer maybe NULL to query the required length of the buffer
+ */
+int
+gcry_mpi_print( enum gcry_mpi_format format, char *buffer, size_t *nbytes,
+ struct gcry_mpi *a )
+{
+ unsigned int nbits = mpi_get_nbits(a);
+ size_t len;
+
+ if( !nbytes )
+ return GCRYERR_INV_ARG;
+
+ len = *nbytes;
+ *nbytes = 0;
+ if( format == GCRYMPI_FMT_STD ) {
+ char *tmp;
+ int extra = 0;
+ unsigned int n;
+
+ if( a->sign )
+ return GCRYERR_INTERNAL; /* can't handle it yet */
+
+ tmp = _gcry_mpi_get_buffer( a, &n, NULL );
+ if( n && (*tmp & 0x80) ) {
+ n++;
+ extra=1;
+ }
+
+ if( n > len && buffer ) {
+ gcry_free(tmp);
+ return GCRYERR_TOO_SHORT; /* the provided buffer is too short */
+ }
+ if( buffer ) {
+ byte *s = buffer;
+ if( extra )
+ *s++ = 0;
+
+ memcpy( s, tmp, n-extra );
+ }
+ gcry_free(tmp);
+ *nbytes = n;
+ return 0;
+ }
+ else if( format == GCRYMPI_FMT_USG ) {
+ unsigned int n = (nbits + 7)/8;
+
+ /* we ignore the sign for this format */
+ /* FIXME: for performance reasons we should put this into
+ * mpi_aprint becuase we can then use the buffer directly */
+ if( n > len && buffer )
+ return GCRYERR_TOO_SHORT; /* the provided buffer is too short */
+ if( buffer ) {
+ char *tmp;
+ tmp = _gcry_mpi_get_buffer( a, &n, NULL );
+ memcpy( buffer, tmp, n );
+ gcry_free(tmp);
+ }
+ *nbytes = n;
+ return 0;
+ }
+#if 0
+ else if( format == GCRYMPI_FMT_PGP ) {
+ unsigned int n = (nbits + 7)/8;
+
+ if( a->sign )
+ return GCRYERR_INV_ARG; /* pgp format can only handle unsigned */
+
+ if( n+2 > len && buffer )
+ return GCRYERR_TOO_SHORT; /* the provided buffer is too short */
+ if( buffer ) {
+ char *tmp;
+ byte *s = buffer;
+ s[0] = nbits >> 8;
+ s[1] = nbits;
+
+ tmp = _gcry_mpi_get_buffer( a, &n, NULL );
+ memcpy( s+2, tmp, n );
+ gcry_free(tmp);
+ }
+ *nbytes = n+2;
+ return 0;
+ }
+ else if( format == GCRYMPI_FMT_SSH ) {
+ char *tmp;
+ int extra = 0;
+ unsigned int n;
+
+ if( a->sign )
+ return GCRYERR_INTERNAL; /* can't handle it yet */
+
+ tmp = _gcry_mpi_get_buffer( a, &n, NULL );
+ if( n && (*tmp & 0x80) ) {
+ n++;
+ extra=1;
+ }
+
+ if( n+4 > len && buffer ) {
+ gcry_free(tmp);
+ return GCRYERR_TOO_SHORT; /* the provided buffer is too short */
+ }
+ if( buffer ) {
+ byte *s = buffer;
+ *s++ = n >> 24;
+ *s++ = n >> 16;
+ *s++ = n >> 8;
+ *s++ = n;
+ if( extra )
+ *s++ = 0;
+
+ memcpy( s, tmp, n-extra );
+ }
+ gcry_free(tmp);
+ *nbytes = 4+n;
+ return 0;
+ }
+ else if( format == GCRYMPI_FMT_HEX ) {
+ byte *tmp;
+ int i;
+ int extra = 0;
+ unsigned int n=0;
+
+ tmp = _gcry_mpi_get_buffer( a, &n, NULL );
+ if( !n || (*tmp & 0x80) )
+ extra=2;
+
+ if( 2*n + extra + !!a->sign + 1 > len && buffer ) {
+ gcry_free(tmp);
+ return GCRYERR_TOO_SHORT; /* the provided buffer is too short */
+ }
+ if( buffer ) {
+ byte *s = buffer;
+ if( a->sign )
+ *s++ = '-';
+ if( extra ) {
+ *s++ = '0';
+ *s++ = '0';
+ }
+
+ for(i=0; i < n; i++ ) {
+ unsigned int c = tmp[i];
+ *s++ = (c >> 4) < 10? '0'+(c>>4) : 'A'+(c>>4)-10 ;
+ c &= 15;
+ *s++ = c < 10? '0'+c : 'A'+c-10 ;
+ }
+ *s++ = 0;
+ *nbytes = (char*)s - buffer;
+ }
+ else {
+ *nbytes = 2*n + extra + !!a->sign + 1;
+ }
+ gcry_free(tmp);
+ return 0;
+ }
+#endif
+ else
+ return GCRYERR_INV_ARG;
+}
+
+/****************
+ * Like gcry_mpi_print but this function allocates the buffer itself.
+ * The caller has to supply the address of a pointer. nbytes may be
+ * NULL.
+ */
+int
+gcry_mpi_aprint( enum gcry_mpi_format format, void **buffer, size_t *nbytes,
+ struct gcry_mpi *a )
+{
+ size_t n;
+ int rc;
+
+ *buffer = NULL;
+ rc = gcry_mpi_print( format, NULL, &n, a );
+ if( rc )
+ return rc;
+ *buffer = mpi_is_secure(a) ? gcry_xmalloc_secure( n ) : gcry_xmalloc( n );
+ rc = gcry_mpi_print( format, *buffer, &n, a );
+ if( rc ) {
+ gcry_free(*buffer);
+ *buffer = NULL;
+ }
+ else if( nbytes )
+ *nbytes = n;
+ return rc;
+}
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-defs.h linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-defs.h
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-defs.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-defs.h 2005-03-16 00:14:54.000000000 +0200
@@ -0,0 +1,19 @@
+#ifndef MPI_DEFS_H
+#define MPI_DEFS_H
+
+#include
+
+#define SIZEOF_UNSIGNED_SHORT 2
+#define SIZEOF_UNSIGNED_INT 4
+#define SIZEOF_UNSIGNED_LONG 4
+
+typedef unsigned char byte;
+//typedef unsigned short ushort;
+//typedef unsigned long ulong;
+
+#define HAVE_U64_TYPEDEF
+#define C_SYMBOL_NAME(name) name
+//#define __i386__
+#define BYTES_PER_MPI_LIMB (SIZEOF_UNSIGNED_LONG)
+
+#endif
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-div.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-div.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-div.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-div.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,353 @@
+/* mpi-div.c - MPI functions
+ * Copyright (C) 1994, 1996, 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#include "kernel-interface.h"
+#include "longlong.h"
+
+
+void
+_gcry_mpi_fdiv_r( MPI rem, MPI dividend, MPI divisor )
+{
+ int divisor_sign = divisor->sign;
+ MPI temp_divisor = NULL;
+
+ /* We need the original value of the divisor after the remainder has been
+ * preliminary calculated. We have to copy it to temporary space if it's
+ * the same variable as REM. */
+ if( rem == divisor ) {
+ temp_divisor = mpi_copy( divisor );
+ divisor = temp_divisor;
+ }
+
+ _gcry_mpi_tdiv_r( rem, dividend, divisor );
+
+ if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+ gcry_mpi_add( rem, rem, divisor);
+
+ if( temp_divisor )
+ mpi_free(temp_divisor);
+}
+
+
+
+/****************
+ * Division rounding the quotient towards -infinity.
+ * The remainder gets the same sign as the denominator.
+ * rem is optional
+ */
+
+ulong
+_gcry_mpi_fdiv_r_ui( MPI rem, MPI dividend, ulong divisor )
+{
+ mpi_limb_t rlimb;
+
+ rlimb = _gcry_mpih_mod_1( dividend->d, dividend->nlimbs, divisor );
+ if( rlimb && dividend->sign )
+ rlimb = divisor - rlimb;
+
+ if( rem ) {
+ rem->d[0] = rlimb;
+ rem->nlimbs = rlimb? 1:0;
+ }
+ return rlimb;
+}
+
+
+void
+_gcry_mpi_fdiv_q( MPI quot, MPI dividend, MPI divisor )
+{
+ MPI tmp = mpi_alloc( mpi_get_nlimbs(quot) );
+ _gcry_mpi_fdiv_qr( quot, tmp, dividend, divisor);
+ mpi_free(tmp);
+}
+
+void
+_gcry_mpi_fdiv_qr( MPI quot, MPI rem, MPI dividend, MPI divisor )
+{
+ int divisor_sign = divisor->sign;
+ MPI temp_divisor = NULL;
+
+ if( quot == divisor || rem == divisor ) {
+ temp_divisor = mpi_copy( divisor );
+ divisor = temp_divisor;
+ }
+
+ _gcry_mpi_tdiv_qr( quot, rem, dividend, divisor );
+
+ if( (divisor_sign ^ dividend->sign) && rem->nlimbs ) {
+ gcry_mpi_sub_ui( quot, quot, 1 );
+ gcry_mpi_add( rem, rem, divisor);
+ }
+
+ if( temp_divisor )
+ mpi_free(temp_divisor);
+}
+
+
+/* If den == quot, den needs temporary storage.
+ * If den == rem, den needs temporary storage.
+ * If num == quot, num needs temporary storage.
+ * If den has temporary storage, it can be normalized while being copied,
+ * i.e no extra storage should be allocated.
+ */
+
+void
+_gcry_mpi_tdiv_r( MPI rem, MPI num, MPI den)
+{
+ _gcry_mpi_tdiv_qr(NULL, rem, num, den );
+}
+
+void
+_gcry_mpi_tdiv_qr( MPI quot, MPI rem, MPI num, MPI den)
+{
+ mpi_ptr_t np, dp;
+ mpi_ptr_t qp, rp;
+ mpi_size_t nsize = num->nlimbs;
+ mpi_size_t dsize = den->nlimbs;
+ mpi_size_t qsize, rsize;
+ mpi_size_t sign_remainder = num->sign;
+ mpi_size_t sign_quotient = num->sign ^ den->sign;
+ unsigned normalization_steps;
+ mpi_limb_t q_limb;
+ mpi_ptr_t marker[5];
+ int markidx=0;
+
+ /* Ensure space is enough for quotient and remainder.
+ * We need space for an extra limb in the remainder, because it's
+ * up-shifted (normalized) below. */
+ rsize = nsize + 1;
+ mpi_resize( rem, rsize);
+
+ qsize = rsize - dsize; /* qsize cannot be bigger than this. */
+ if( qsize <= 0 ) {
+ if( num != rem ) {
+ rem->nlimbs = num->nlimbs;
+ rem->sign = num->sign;
+ MPN_COPY(rem->d, num->d, nsize);
+ }
+ if( quot ) {
+ /* This needs to follow the assignment to rem, in case the
+ * numerator and quotient are the same. */
+ quot->nlimbs = 0;
+ quot->sign = 0;
+ }
+ return;
+ }
+
+ if( quot )
+ mpi_resize( quot, qsize);
+
+ /* Read pointers here, when reallocation is finished. */
+ np = num->d;
+ dp = den->d;
+ rp = rem->d;
+
+ /* Optimize division by a single-limb divisor. */
+ if( dsize == 1 ) {
+ mpi_limb_t rlimb;
+ if( quot ) {
+ qp = quot->d;
+ rlimb = _gcry_mpih_divmod_1( qp, np, nsize, dp[0] );
+ qsize -= qp[qsize - 1] == 0;
+ quot->nlimbs = qsize;
+ quot->sign = sign_quotient;
+ }
+ else
+ rlimb = _gcry_mpih_mod_1( np, nsize, dp[0] );
+ rp[0] = rlimb;
+ rsize = rlimb != 0?1:0;
+ rem->nlimbs = rsize;
+ rem->sign = sign_remainder;
+ return;
+ }
+
+
+ if( quot ) {
+ qp = quot->d;
+ /* Make sure QP and NP point to different objects. Otherwise the
+ * numerator would be gradually overwritten by the quotient limbs. */
+ if(qp == np) { /* Copy NP object to temporary space. */
+ np = marker[markidx++] = mpi_alloc_limb_space(nsize,
+ mpi_is_secure(quot));
+ MPN_COPY(np, qp, nsize);
+ }
+ }
+ else /* Put quotient at top of remainder. */
+ qp = rp + dsize;
+
+ count_leading_zeros( normalization_steps, dp[dsize - 1] );
+
+ /* Normalize the denominator, i.e. make its most significant bit set by
+ * shifting it NORMALIZATION_STEPS bits to the left. Also shift the
+ * numerator the same number of steps (to keep the quotient the same!).
+ */
+ if( normalization_steps ) {
+ mpi_ptr_t tp;
+ mpi_limb_t nlimb;
+
+ /* Shift up the denominator setting the most significant bit of
+ * the most significant word. Use temporary storage not to clobber
+ * the original contents of the denominator. */
+ tp = marker[markidx++] = mpi_alloc_limb_space(dsize,mpi_is_secure(den));
+ _gcry_mpih_lshift( tp, dp, dsize, normalization_steps );
+ dp = tp;
+
+ /* Shift up the numerator, possibly introducing a new most
+ * significant word. Move the shifted numerator in the remainder
+ * meanwhile. */
+ nlimb = _gcry_mpih_lshift(rp, np, nsize, normalization_steps);
+ if( nlimb ) {
+ rp[nsize] = nlimb;
+ rsize = nsize + 1;
+ }
+ else
+ rsize = nsize;
+ }
+ else {
+ /* The denominator is already normalized, as required. Copy it to
+ * temporary space if it overlaps with the quotient or remainder. */
+ if( dp == rp || (quot && (dp == qp))) {
+ mpi_ptr_t tp;
+
+ tp = marker[markidx++] = mpi_alloc_limb_space(dsize, mpi_is_secure(den));
+ MPN_COPY( tp, dp, dsize );
+ dp = tp;
+ }
+
+ /* Move the numerator to the remainder. */
+ if( rp != np )
+ MPN_COPY(rp, np, nsize);
+
+ rsize = nsize;
+ }
+
+ q_limb = _gcry_mpih_divrem( qp, 0, rp, rsize, dp, dsize );
+
+ if( quot ) {
+ qsize = rsize - dsize;
+ if(q_limb) {
+ qp[qsize] = q_limb;
+ qsize += 1;
+ }
+
+ quot->nlimbs = qsize;
+ quot->sign = sign_quotient;
+ }
+
+ rsize = dsize;
+ MPN_NORMALIZE (rp, rsize);
+
+ if( normalization_steps && rsize ) {
+ _gcry_mpih_rshift(rp, rp, rsize, normalization_steps);
+ rsize -= rp[rsize - 1] == 0?1:0;
+ }
+
+ rem->nlimbs = rsize;
+ rem->sign = sign_remainder;
+ while( markidx )
+ mpi_free_limb_space(marker[--markidx]);
+}
+
+void
+_gcry_mpi_tdiv_q_2exp( MPI w, MPI u, unsigned int count )
+{
+ mpi_size_t usize, wsize;
+ mpi_size_t limb_cnt;
+
+ usize = u->nlimbs;
+ limb_cnt = count / BITS_PER_MPI_LIMB;
+ wsize = usize - limb_cnt;
+ if( limb_cnt >= usize )
+ w->nlimbs = 0;
+ else {
+ mpi_ptr_t wp;
+ mpi_ptr_t up;
+
+ RESIZE_IF_NEEDED( w, wsize );
+ wp = w->d;
+ up = u->d;
+
+ count %= BITS_PER_MPI_LIMB;
+ if( count ) {
+ _gcry_mpih_rshift( wp, up + limb_cnt, wsize, count );
+ wsize -= !wp[wsize - 1];
+ }
+ else {
+ MPN_COPY_INCR( wp, up + limb_cnt, wsize);
+ }
+
+ w->nlimbs = wsize;
+ }
+}
+
+/****************
+ * Check whether dividend is divisible by divisor
+ * (note: divisor must fit into a limb)
+ */
+int
+_gcry_mpi_divisible_ui(MPI dividend, ulong divisor )
+{
+ return !_gcry_mpih_mod_1( dividend->d, dividend->nlimbs, divisor );
+}
+
+
+void
+gcry_mpi_div (MPI quot, MPI rem, MPI dividend, MPI divisor, int round)
+{
+ if (!round)
+ {
+ if (!rem)
+ {
+ MPI tmp = mpi_alloc (mpi_get_nlimbs(quot));
+ _gcry_mpi_tdiv_qr (quot, tmp, dividend, divisor);
+ mpi_free (tmp);
+ }
+ else
+ _gcry_mpi_tdiv_qr (quot, rem, dividend, divisor);
+ }
+ else if (round < 0)
+ {
+ if (!rem)
+ _gcry_mpi_fdiv_q (quot, dividend, divisor);
+ else if (!quot)
+ _gcry_mpi_fdiv_r (rem, dividend, divisor);
+ else
+ _gcry_mpi_fdiv_qr (quot, rem, dividend, divisor);
+ }
+ else
+// log_bug ("mpi rounding to ceiling not yet implemented\n");
+ assert(0);
+ round = 0;
+}
+
+
+void
+gcry_mpi_mod (MPI rem, MPI dividend, MPI divisor)
+{
+ _gcry_mpi_fdiv_r (rem, dividend, divisor);
+ rem->sign = 0;
+}
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-gcd.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-gcd.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-gcd.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-gcd.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,56 @@
+/* mpi-gcd.c - MPI functions
+ * Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+
+/****************
+ * Find the greatest common divisor G of A and B.
+ * Return: true if this 1, false in all other cases
+ */
+int
+gcry_mpi_gcd( MPI g, MPI xa, MPI xb )
+{
+ MPI a, b;
+
+ a = mpi_copy(xa);
+ b = mpi_copy(xb);
+
+ /* TAOCP Vol II, 4.5.2, Algorithm A */
+ a->sign = 0;
+ b->sign = 0;
+ while( gcry_mpi_cmp_ui( b, 0 ) ) {
+ _gcry_mpi_fdiv_r( g, a, b ); /* g used as temorary variable */
+ mpi_set(a,b);
+ mpi_set(b,g);
+ }
+ mpi_set(g, a);
+
+ mpi_free(a);
+ mpi_free(b);
+ return !gcry_mpi_cmp_ui( g, 1);
+}
+
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpih-div.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpih-div.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpih-div.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpih-div.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,536 @@
+/* mpih-div.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 2000,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+/*#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+#include "longlong.h"
+
+#ifndef UMUL_TIME
+ #define UMUL_TIME 1
+#endif
+#ifndef UDIV_TIME
+ #define UDIV_TIME UMUL_TIME
+#endif
+
+/* FIXME: We should be using invert_limb (or invert_normalized_limb)
+ * here (not udiv_qrnnd).
+ */
+
+mpi_limb_t
+_gcry_mpih_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb)
+{
+ mpi_size_t i;
+ mpi_limb_t n1, n0, r;
+ int dummy;
+
+ /* Botch: Should this be handled at all? Rely on callers? */
+ if( !dividend_size )
+ return 0;
+
+ /* If multiplication is much faster than division, and the
+ * dividend is large, pre-invert the divisor, and use
+ * only multiplications in the inner loop.
+ *
+ * This test should be read:
+ * Does it ever help to use udiv_qrnnd_preinv?
+ * && Does what we save compensate for the inversion overhead?
+ */
+ if( UDIV_TIME > (2 * UMUL_TIME + 6)
+ && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+ int normalization_steps;
+
+ count_leading_zeros( normalization_steps, divisor_limb );
+ if( normalization_steps ) {
+ mpi_limb_t divisor_limb_inverted;
+
+ divisor_limb <<= normalization_steps;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ *
+ * Special case for DIVISOR_LIMB == 100...000.
+ */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV(dummy, r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb, divisor_limb_inverted);
+ n1 = n0;
+ }
+ UDIV_QRNND_PREINV(dummy, r, r,
+ n1 << normalization_steps,
+ divisor_limb, divisor_limb_inverted);
+ return r >> normalization_steps;
+ }
+ else {
+ mpi_limb_t divisor_limb_inverted;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ *
+ * Special case for DIVISOR_LIMB == 100...000.
+ */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if( r >= divisor_limb )
+ r = 0;
+ else
+ i--;
+
+ for( ; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV(dummy, r, r,
+ n0, divisor_limb, divisor_limb_inverted);
+ }
+ return r;
+ }
+ }
+ else {
+ if( UDIV_NEEDS_NORMALIZATION ) {
+ int normalization_steps;
+
+ count_leading_zeros(normalization_steps, divisor_limb);
+ if( normalization_steps ) {
+ divisor_limb <<= normalization_steps;
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for(i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (dummy, r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb);
+ n1 = n0;
+ }
+ udiv_qrnnd (dummy, r, r,
+ n1 << normalization_steps,
+ divisor_limb);
+ return r >> normalization_steps;
+ }
+ }
+ /* No normalization needed, either because udiv_qrnnd doesn't require
+ * it, or because DIVISOR_LIMB is already normalized. */
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if(r >= divisor_limb)
+ r = 0;
+ else
+ i--;
+
+ for(; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (dummy, r, r, n0, divisor_limb);
+ }
+ return r;
+ }
+}
+
+/* Divide num (NP/NSIZE) by den (DP/DSIZE) and write
+ * the NSIZE-DSIZE least significant quotient limbs at QP
+ * and the DSIZE long remainder at NP. If QEXTRA_LIMBS is
+ * non-zero, generate that many fraction bits and append them after the
+ * other quotient limbs.
+ * Return the most significant limb of the quotient, this is always 0 or 1.
+ *
+ * Preconditions:
+ * 0. NSIZE >= DSIZE.
+ * 1. The most significant bit of the divisor must be set.
+ * 2. QP must either not overlap with the input operands at all, or
+ * QP + DSIZE >= NP must hold true. (This means that it's
+ * possible to put the quotient in the high part of NUM, right after the
+ * remainder in NUM.
+ * 3. NSIZE >= DSIZE, even if QEXTRA_LIMBS is non-zero.
+ */
+
+mpi_limb_t
+_gcry_mpih_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+ mpi_ptr_t np, mpi_size_t nsize,
+ mpi_ptr_t dp, mpi_size_t dsize)
+{
+ mpi_limb_t most_significant_q_limb = 0;
+
+ switch(dsize) {
+ case 0:
+ /* We are asked to divide by zero, so go ahead and do it! (To make
+ the compiler not remove this statement, return the value.) */
+ return 1 / dsize;
+
+ case 1:
+ {
+ mpi_size_t i;
+ mpi_limb_t n1;
+ mpi_limb_t d;
+
+ d = dp[0];
+ n1 = np[nsize - 1];
+
+ if( n1 >= d ) {
+ n1 -= d;
+ most_significant_q_limb = 1;
+ }
+
+ qp += qextra_limbs;
+ for( i = nsize - 2; i >= 0; i--)
+ udiv_qrnnd( qp[i], n1, n1, np[i], d );
+ qp -= qextra_limbs;
+
+ for( i = qextra_limbs - 1; i >= 0; i-- )
+ udiv_qrnnd (qp[i], n1, n1, 0, d);
+
+ np[0] = n1;
+ }
+ break;
+
+ case 2:
+ {
+ mpi_size_t i;
+ mpi_limb_t n1, n0, n2;
+ mpi_limb_t d1, d0;
+
+ np += nsize - 2;
+ d1 = dp[1];
+ d0 = dp[0];
+ n1 = np[1];
+ n0 = np[0];
+
+ if( n1 >= d1 && (n1 > d1 || n0 >= d0) ) {
+ sub_ddmmss (n1, n0, n1, n0, d1, d0);
+ most_significant_q_limb = 1;
+ }
+
+ for( i = qextra_limbs + nsize - 2 - 1; i >= 0; i-- ) {
+ mpi_limb_t q;
+ mpi_limb_t r;
+
+ if( i >= qextra_limbs )
+ np--;
+ else
+ np[0] = 0;
+
+ if( n1 == d1 ) {
+ /* Q should be either 111..111 or 111..110. Need special
+ * treatment of this rare case as normal division would
+ * give overflow. */
+ q = ~(mpi_limb_t)0;
+
+ r = n0 + d1;
+ if( r < d1 ) { /* Carry in the addition? */
+ add_ssaaaa( n1, n0, r - d0, np[0], 0, d0 );
+ qp[i] = q;
+ continue;
+ }
+ n1 = d0 - (d0 != 0?1:0);
+ n0 = -d0;
+ }
+ else {
+ udiv_qrnnd (q, r, n1, n0, d1);
+ umul_ppmm (n1, n0, d0, q);
+ }
+
+ n2 = np[0];
+ q_test:
+ if( n1 > r || (n1 == r && n0 > n2) ) {
+ /* The estimated Q was too large. */
+ q--;
+ sub_ddmmss (n1, n0, n1, n0, 0, d0);
+ r += d1;
+ if( r >= d1 ) /* If not carry, test Q again. */
+ goto q_test;
+ }
+
+ qp[i] = q;
+ sub_ddmmss (n1, n0, r, n2, n1, n0);
+ }
+ np[1] = n1;
+ np[0] = n0;
+ }
+ break;
+
+ default:
+ {
+ mpi_size_t i;
+ mpi_limb_t dX, d1, n0;
+
+ np += nsize - dsize;
+ dX = dp[dsize - 1];
+ d1 = dp[dsize - 2];
+ n0 = np[dsize - 1];
+
+ if( n0 >= dX ) {
+ if(n0 > dX || _gcry_mpih_cmp(np, dp, dsize - 1) >= 0 ) {
+ _gcry_mpih_sub_n(np, np, dp, dsize);
+ n0 = np[dsize - 1];
+ most_significant_q_limb = 1;
+ }
+ }
+
+ for( i = qextra_limbs + nsize - dsize - 1; i >= 0; i--) {
+ mpi_limb_t q;
+ mpi_limb_t n1, n2;
+ mpi_limb_t cy_limb;
+
+ if( i >= qextra_limbs ) {
+ np--;
+ n2 = np[dsize];
+ }
+ else {
+ n2 = np[dsize - 1];
+ MPN_COPY_DECR (np + 1, np, dsize - 1);
+ np[0] = 0;
+ }
+
+ if( n0 == dX ) {
+ /* This might over-estimate q, but it's probably not worth
+ * the extra code here to find out. */
+ q = ~(mpi_limb_t)0;
+ }
+ else {
+ mpi_limb_t r;
+
+ udiv_qrnnd(q, r, n0, np[dsize - 1], dX);
+ umul_ppmm(n1, n0, d1, q);
+
+ while( n1 > r || (n1 == r && n0 > np[dsize - 2])) {
+ q--;
+ r += dX;
+ if( r < dX ) /* I.e. "carry in previous addition?" */
+ break;
+ n1 -= n0 < d1;
+ n0 -= d1;
+ }
+ }
+
+ /* Possible optimization: We already have (q * n0) and (1 * n1)
+ * after the calculation of q. Taking advantage of that, we
+ * could make this loop make two iterations less. */
+ cy_limb = _gcry_mpih_submul_1(np, dp, dsize, q);
+
+ if( n2 != cy_limb ) {
+ _gcry_mpih_add_n(np, np, dp, dsize);
+ q--;
+ }
+
+ qp[i] = q;
+ n0 = np[dsize - 1];
+ }
+ }
+ }
+
+ return most_significant_q_limb;
+}
+
+
+/****************
+ * Divide (DIVIDEND_PTR,,DIVIDEND_SIZE) by DIVISOR_LIMB.
+ * Write DIVIDEND_SIZE limbs of quotient at QUOT_PTR.
+ * Return the single-limb remainder.
+ * There are no constraints on the value of the divisor.
+ *
+ * QUOT_PTR and DIVIDEND_PTR might point to the same limb.
+ */
+
+mpi_limb_t
+_gcry_mpih_divmod_1( mpi_ptr_t quot_ptr,
+ mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb)
+{
+ mpi_size_t i;
+ mpi_limb_t n1, n0, r;
+ int dummy;
+
+ if( !dividend_size )
+ return 0;
+
+ /* If multiplication is much faster than division, and the
+ * dividend is large, pre-invert the divisor, and use
+ * only multiplications in the inner loop.
+ *
+ * This test should be read:
+ * Does it ever help to use udiv_qrnnd_preinv?
+ * && Does what we save compensate for the inversion overhead?
+ */
+ if( UDIV_TIME > (2 * UMUL_TIME + 6)
+ && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+ int normalization_steps;
+
+ count_leading_zeros( normalization_steps, divisor_limb );
+ if( normalization_steps ) {
+ mpi_limb_t divisor_limb_inverted;
+
+ divisor_limb <<= normalization_steps;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ */
+ /* Special case for DIVISOR_LIMB == 100...000. */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t)0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV( quot_ptr[i + 1], r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb, divisor_limb_inverted);
+ n1 = n0;
+ }
+ UDIV_QRNND_PREINV( quot_ptr[0], r, r,
+ n1 << normalization_steps,
+ divisor_limb, divisor_limb_inverted);
+ return r >> normalization_steps;
+ }
+ else {
+ mpi_limb_t divisor_limb_inverted;
+
+ /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB. The
+ * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+ * most significant bit (with weight 2**N) implicit.
+ */
+ /* Special case for DIVISOR_LIMB == 100...000. */
+ if( !(divisor_limb << 1) )
+ divisor_limb_inverted = ~(mpi_limb_t) 0;
+ else
+ udiv_qrnnd(divisor_limb_inverted, dummy,
+ -divisor_limb, 0, divisor_limb);
+
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if( r >= divisor_limb )
+ r = 0;
+ else
+ quot_ptr[i--] = 0;
+
+ for( ; i >= 0; i-- ) {
+ n0 = dividend_ptr[i];
+ UDIV_QRNND_PREINV( quot_ptr[i], r, r,
+ n0, divisor_limb, divisor_limb_inverted);
+ }
+ return r;
+ }
+ }
+ else {
+ if(UDIV_NEEDS_NORMALIZATION) {
+ int normalization_steps;
+
+ count_leading_zeros (normalization_steps, divisor_limb);
+ if( normalization_steps ) {
+ divisor_limb <<= normalization_steps;
+
+ n1 = dividend_ptr[dividend_size - 1];
+ r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+ /* Possible optimization:
+ * if (r == 0
+ * && divisor_limb > ((n1 << normalization_steps)
+ * | (dividend_ptr[dividend_size - 2] >> ...)))
+ * ...one division less...
+ */
+ for( i = dividend_size - 2; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd (quot_ptr[i + 1], r, r,
+ ((n1 << normalization_steps)
+ | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+ divisor_limb);
+ n1 = n0;
+ }
+ udiv_qrnnd (quot_ptr[0], r, r,
+ n1 << normalization_steps,
+ divisor_limb);
+ return r >> normalization_steps;
+ }
+ }
+ /* No normalization needed, either because udiv_qrnnd doesn't require
+ * it, or because DIVISOR_LIMB is already normalized. */
+ i = dividend_size - 1;
+ r = dividend_ptr[i];
+
+ if(r >= divisor_limb)
+ r = 0;
+ else
+ quot_ptr[i--] = 0;
+
+ for(; i >= 0; i--) {
+ n0 = dividend_ptr[i];
+ udiv_qrnnd( quot_ptr[i], r, r, n0, divisor_limb );
+ }
+ return r;
+ }
+}
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpih-mul.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpih-mul.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpih-mul.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpih-mul.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,527 @@
+/* mpih-mul.c - MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 1999, 2000,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+/*#include
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+#include "longlong.h"
+//#include "g10lib.h"
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+ do { \
+ if( (size) < KARATSUBA_THRESHOLD ) \
+ mul_n_basecase (prodp, up, vp, size); \
+ else \
+ mul_n (prodp, up, vp, size, tspace); \
+ } while (0);
+
+#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
+ do { \
+ if ((size) < KARATSUBA_THRESHOLD) \
+ _gcry_mpih_sqr_n_basecase (prodp, up, size); \
+ else \
+ _gcry_mpih_sqr_n (prodp, up, size, tspace); \
+ } while (0);
+
+
+
+
+/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
+ * both with SIZE limbs, and store the result at PRODP. 2 * SIZE limbs are
+ * always stored. Return the most significant limb.
+ *
+ * Argument constraints:
+ * 1. PRODP != UP and PRODP != VP, i.e. the destination
+ * must be distinct from the multiplier and the multiplicand.
+ *
+ *
+ * Handle simple cases with traditional multiplication.
+ *
+ * This is the most critical code of multiplication. All multiplies rely
+ * on this, both small and huge. Small ones arrive here immediately. Huge
+ * ones arrive here as this is the base case for Karatsuba's recursive
+ * algorithm below.
+ */
+
+static mpi_limb_t
+mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
+ mpi_ptr_t vp, mpi_size_t size)
+{
+ mpi_size_t i;
+ mpi_limb_t cy;
+ mpi_limb_t v_limb;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = vp[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, size );
+ else
+ MPN_ZERO( prodp, size );
+ cy = 0;
+ }
+ else
+ cy = _gcry_mpih_mul_1( prodp, up, size, v_limb );
+
+ prodp[size] = cy;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i = 1; i < size; i++ ) {
+ v_limb = vp[i];
+ if( v_limb <= 1 ) {
+ cy = 0;
+ if( v_limb == 1 )
+ cy = _gcry_mpih_add_n(prodp, prodp, up, size);
+ }
+ else
+ cy = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
+
+ prodp[size] = cy;
+ prodp++;
+ }
+
+ return cy;
+}
+
+static void
+mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+ mpi_size_t size, mpi_ptr_t tspace )
+{
+ if( size & 1 ) {
+ /* The size is odd, and the code below doesn't handle that.
+ * Multiply the least significant (size - 1) limbs with a recursive
+ * call, and handle the most significant limb of S1 and S2
+ * separately.
+ * A slightly faster way to do this would be to make the Karatsuba
+ * code below behave as if the size were even, and let it check for
+ * odd size in the end. I.e., in essence move this code to the end.
+ * Doing so would save us a recursive call, and potentially make the
+ * stack grow a lot less.
+ */
+ mpi_size_t esize = size - 1; /* even size */
+ mpi_limb_t cy_limb;
+
+ MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, vp[esize] );
+ prodp[esize + esize] = cy_limb;
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, vp, size, up[esize] );
+ prodp[esize + size] = cy_limb;
+ }
+ else {
+ /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
+ *
+ * Split U in two pieces, U1 and U0, such that
+ * U = U0 + U1*(B**n),
+ * and V in V1 and V0, such that
+ * V = V0 + V1*(B**n).
+ *
+ * UV is then computed recursively using the identity
+ *
+ * 2n n n n
+ * UV = (B + B )U V + B (U -U )(V -V ) + (B + 1)U V
+ * 1 1 1 0 0 1 0 0
+ *
+ * Where B = 2**BITS_PER_MP_LIMB.
+ */
+ mpi_size_t hsize = size >> 1;
+ mpi_limb_t cy;
+ int negflg;
+
+ /* Product H. ________________ ________________
+ * |_____U1 x V1____||____U0 x V0_____|
+ * Put result in upper part of PROD and pass low part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
+
+ /* Product M. ________________
+ * |_(U1-U0)(V0-V1)_|
+ */
+ if( _gcry_mpih_cmp(up + hsize, up, hsize) >= 0 ) {
+ _gcry_mpih_sub_n(prodp, up + hsize, up, hsize);
+ negflg = 0;
+ }
+ else {
+ _gcry_mpih_sub_n(prodp, up, up + hsize, hsize);
+ negflg = 1;
+ }
+ if( _gcry_mpih_cmp(vp + hsize, vp, hsize) >= 0 ) {
+ _gcry_mpih_sub_n(prodp + hsize, vp + hsize, vp, hsize);
+ negflg ^= 1;
+ }
+ else {
+ _gcry_mpih_sub_n(prodp + hsize, vp, vp + hsize, hsize);
+ /* No change of NEGFLG. */
+ }
+ /* Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
+
+ /* Add/copy product H. */
+ MPN_COPY (prodp + hsize, prodp + size, hsize);
+ cy = _gcry_mpih_add_n( prodp + size, prodp + size,
+ prodp + size + hsize, hsize);
+
+ /* Add product M (if NEGFLG M is a negative number) */
+ if(negflg)
+ cy -= _gcry_mpih_sub_n(prodp + hsize, prodp + hsize, tspace, size);
+ else
+ cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
+
+ /* Product L. ________________ ________________
+ * |________________||____U0 x V0_____|
+ * Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
+
+ /* Add/copy Product L (twice) */
+
+ cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
+ if( cy )
+ _gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
+
+ MPN_COPY(prodp, tspace, hsize);
+ cy = _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+ if( cy )
+ _gcry_mpih_add_1(prodp + size, prodp + size, size, 1);
+ }
+}
+
+
+void
+_gcry_mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
+{
+ mpi_size_t i;
+ mpi_limb_t cy_limb;
+ mpi_limb_t v_limb;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = up[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, size );
+ else
+ MPN_ZERO(prodp, size);
+ cy_limb = 0;
+ }
+ else
+ cy_limb = _gcry_mpih_mul_1( prodp, up, size, v_limb );
+
+ prodp[size] = cy_limb;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i=1; i < size; i++) {
+ v_limb = up[i];
+ if( v_limb <= 1 ) {
+ cy_limb = 0;
+ if( v_limb == 1 )
+ cy_limb = _gcry_mpih_add_n(prodp, prodp, up, size);
+ }
+ else
+ cy_limb = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
+
+ prodp[size] = cy_limb;
+ prodp++;
+ }
+}
+
+
+void
+_gcry_mpih_sqr_n( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
+{
+ if( size & 1 ) {
+ /* The size is odd, and the code below doesn't handle that.
+ * Multiply the least significant (size - 1) limbs with a recursive
+ * call, and handle the most significant limb of S1 and S2
+ * separately.
+ * A slightly faster way to do this would be to make the Karatsuba
+ * code below behave as if the size were even, and let it check for
+ * odd size in the end. I.e., in essence move this code to the end.
+ * Doing so would save us a recursive call, and potentially make the
+ * stack grow a lot less.
+ */
+ mpi_size_t esize = size - 1; /* even size */
+ mpi_limb_t cy_limb;
+
+ MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, up[esize] );
+ prodp[esize + esize] = cy_limb;
+ cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, size, up[esize] );
+
+ prodp[esize + size] = cy_limb;
+ }
+ else {
+ mpi_size_t hsize = size >> 1;
+ mpi_limb_t cy;
+
+ /* Product H. ________________ ________________
+ * |_____U1 x U1____||____U0 x U0_____|
+ * Put result in upper part of PROD and pass low part of TSPACE
+ * as new TSPACE.
+ */
+ MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
+
+ /* Product M. ________________
+ * |_(U1-U0)(U0-U1)_|
+ */
+ if( _gcry_mpih_cmp( up + hsize, up, hsize) >= 0 )
+ _gcry_mpih_sub_n( prodp, up + hsize, up, hsize);
+ else
+ _gcry_mpih_sub_n (prodp, up, up + hsize, hsize);
+
+ /* Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE. */
+ MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
+
+ /* Add/copy product H */
+ MPN_COPY(prodp + hsize, prodp + size, hsize);
+ cy = _gcry_mpih_add_n(prodp + size, prodp + size,
+ prodp + size + hsize, hsize);
+
+ /* Add product M (if NEGFLG M is a negative number). */
+ cy -= _gcry_mpih_sub_n (prodp + hsize, prodp + hsize, tspace, size);
+
+ /* Product L. ________________ ________________
+ * |________________||____U0 x U0_____|
+ * Read temporary operands from low part of PROD.
+ * Put result in low part of TSPACE using upper part of TSPACE
+ * as new TSPACE. */
+ MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
+
+ /* Add/copy Product L (twice). */
+ cy += _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace, size);
+ if( cy )
+ _gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size,
+ hsize, cy);
+
+ MPN_COPY(prodp, tspace, hsize);
+ cy = _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+ if( cy )
+ _gcry_mpih_add_1 (prodp + size, prodp + size, size, 1);
+ }
+}
+
+
+/* This should be made into an inline function in gmp.h. */
+void
+_gcry_mpih_mul_n( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
+{
+ int secure;
+
+ if( up == vp ) {
+ if( size < KARATSUBA_THRESHOLD )
+ _gcry_mpih_sqr_n_basecase( prodp, up, size );
+ else {
+ mpi_ptr_t tspace;
+ secure = gcry_is_secure( up );
+ tspace = mpi_alloc_limb_space( 2 * size, secure );
+ _gcry_mpih_sqr_n( prodp, up, size, tspace );
+ mpi_free_limb_space( tspace );
+ }
+ }
+ else {
+ if( size < KARATSUBA_THRESHOLD )
+ mul_n_basecase( prodp, up, vp, size );
+ else {
+ mpi_ptr_t tspace;
+ secure = gcry_is_secure( up ) || gcry_is_secure( vp );
+ tspace = mpi_alloc_limb_space( 2 * size, secure );
+ mul_n (prodp, up, vp, size, tspace);
+ mpi_free_limb_space( tspace );
+ }
+ }
+}
+
+
+
+void
+_gcry_mpih_mul_karatsuba_case( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize,
+ struct karatsuba_ctx *ctx )
+{
+ mpi_limb_t cy;
+
+ if( !ctx->tspace || ctx->tspace_size < vsize ) {
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ ctx->tspace = mpi_alloc_limb_space( 2 * vsize,
+ gcry_is_secure( up ) || gcry_is_secure( vp ) );
+ ctx->tspace_size = vsize;
+ }
+
+ MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
+
+ prodp += vsize;
+ up += vsize;
+ usize -= vsize;
+ if( usize >= vsize ) {
+ if( !ctx->tp || ctx->tp_size < vsize ) {
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ ctx->tp = mpi_alloc_limb_space( 2 * vsize, gcry_is_secure( up )
+ || gcry_is_secure( vp ) );
+ ctx->tp_size = vsize;
+ }
+
+ do {
+ MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
+ cy = _gcry_mpih_add_n( prodp, prodp, ctx->tp, vsize );
+ _gcry_mpih_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
+ prodp += vsize;
+ up += vsize;
+ usize -= vsize;
+ } while( usize >= vsize );
+ }
+
+ if( usize ) {
+ if( usize < KARATSUBA_THRESHOLD ) {
+ _gcry_mpih_mul( ctx->tspace, vp, vsize, up, usize );
+ }
+ else {
+ if( !ctx->next ) {
+ ctx->next = gcry_xcalloc( 1, sizeof *ctx );
+ }
+ _gcry_mpih_mul_karatsuba_case( ctx->tspace,
+ vp, vsize,
+ up, usize,
+ ctx->next );
+ }
+
+ cy = _gcry_mpih_add_n( prodp, prodp, ctx->tspace, vsize);
+ _gcry_mpih_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
+ }
+}
+
+
+void
+_gcry_mpih_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
+{
+ struct karatsuba_ctx *ctx2;
+
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ for( ctx=ctx->next; ctx; ctx = ctx2 ) {
+ ctx2 = ctx->next;
+ if( ctx->tp )
+ mpi_free_limb_space( ctx->tp );
+ if( ctx->tspace )
+ mpi_free_limb_space( ctx->tspace );
+ gcry_free( ctx );
+ }
+}
+
+/* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
+ * and v (pointed to by VP, with VSIZE limbs), and store the result at
+ * PRODP. USIZE + VSIZE limbs are always stored, but if the input
+ * operands are normalized. Return the most significant limb of the
+ * result.
+ *
+ * NOTE: The space pointed to by PRODP is overwritten before finished
+ * with U and V, so overlap is an error.
+ *
+ * Argument constraints:
+ * 1. USIZE >= VSIZE.
+ * 2. PRODP != UP and PRODP != VP, i.e. the destination
+ * must be distinct from the multiplier and the multiplicand.
+ */
+
+mpi_limb_t
+_gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize)
+{
+ mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
+ mpi_limb_t cy;
+ struct karatsuba_ctx ctx;
+
+ if( vsize < KARATSUBA_THRESHOLD ) {
+ mpi_size_t i;
+ mpi_limb_t v_limb;
+
+ if( !vsize )
+ return 0;
+
+ /* Multiply by the first limb in V separately, as the result can be
+ * stored (not added) to PROD. We also avoid a loop for zeroing. */
+ v_limb = vp[0];
+ if( v_limb <= 1 ) {
+ if( v_limb == 1 )
+ MPN_COPY( prodp, up, usize );
+ else
+ MPN_ZERO( prodp, usize );
+ cy = 0;
+ }
+ else
+ cy = _gcry_mpih_mul_1( prodp, up, usize, v_limb );
+
+ prodp[usize] = cy;
+ prodp++;
+
+ /* For each iteration in the outer loop, multiply one limb from
+ * U with one limb from V, and add it to PROD. */
+ for( i = 1; i < vsize; i++ ) {
+ v_limb = vp[i];
+ if( v_limb <= 1 ) {
+ cy = 0;
+ if( v_limb == 1 )
+ cy = _gcry_mpih_add_n(prodp, prodp, up, usize);
+ }
+ else
+ cy = _gcry_mpih_addmul_1(prodp, up, usize, v_limb);
+
+ prodp[usize] = cy;
+ prodp++;
+ }
+
+ return cy;
+ }
+
+ memset( &ctx, 0, sizeof ctx );
+ _gcry_mpih_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
+ _gcry_mpih_release_karatsuba_ctx( &ctx );
+ return *prod_endp;
+}
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-inline.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-inline.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-inline.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-inline.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,38 @@
+/* mpi-inline.c
+ * Copyright (C) 1999, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*
+#include
+#include
+#include
+*/
+
+/* put the inline functions as real functions into the lib */
+#define G10_MPI_INLINE_DECL
+
+#include "kernel-interface.h"
+
+/* always include the header becuase it is only
+ * included by mpi-internal if __GCC__ is defined but we
+ * need it here in all cases and the above definition of
+ * of the macro allows us to do so
+ */
+#include "mpi-inline.h"
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-inline.h linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-inline.h
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-inline.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-inline.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,154 @@
+/* mpi-inline.h - Internal to the Multi Precision Integers
+ * Copyright (C) 1994, 1996, 1998, 1999,
+ * 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#ifndef G10_MPI_INLINE_H
+#define G10_MPI_INLINE_H
+
+#ifndef G10_MPI_INLINE_DECL
+ #define G10_MPI_INLINE_DECL extern __inline__
+#endif
+
+G10_MPI_INLINE_DECL mpi_limb_t
+_gcry_mpih_add_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+ mpi_limb_t x;
+
+ x = *s1_ptr++;
+ s2_limb += x;
+ *res_ptr++ = s2_limb;
+ if( s2_limb < x ) { /* sum is less than the left operand: handle carry */
+ while( --s1_size ) {
+ x = *s1_ptr++ + 1; /* add carry */
+ *res_ptr++ = x; /* and store */
+ if( x ) /* not 0 (no overflow): we can stop */
+ goto leave;
+ }
+ return 1; /* return carry (size of s1 to small) */
+ }
+
+ leave:
+ if( res_ptr != s1_ptr ) { /* not the same variable */
+ mpi_size_t i; /* copy the rest */
+ for( i=0; i < s1_size-1; i++ )
+ res_ptr[i] = s1_ptr[i];
+ }
+ return 0; /* no carry */
+}
+
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+_gcry_mpih_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+ mpi_limb_t cy = 0;
+
+ if( s2_size )
+ cy = _gcry_mpih_add_n( res_ptr, s1_ptr, s2_ptr, s2_size );
+
+ if( s1_size - s2_size )
+ cy = _gcry_mpih_add_1( res_ptr + s2_size, s1_ptr + s2_size,
+ s1_size - s2_size, cy);
+ return cy;
+}
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+_gcry_mpih_sub_1(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb )
+{
+ mpi_limb_t x;
+
+ x = *s1_ptr++;
+ s2_limb = x - s2_limb;
+ *res_ptr++ = s2_limb;
+ if( s2_limb > x ) {
+ while( --s1_size ) {
+ x = *s1_ptr++;
+ *res_ptr++ = x - 1;
+ if( x )
+ goto leave;
+ }
+ return 1;
+ }
+
+ leave:
+ if( res_ptr != s1_ptr ) {
+ mpi_size_t i;
+ for( i=0; i < s1_size-1; i++ )
+ res_ptr[i] = s1_ptr[i];
+ }
+ return 0;
+}
+
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+_gcry_mpih_sub( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+ mpi_limb_t cy = 0;
+
+ if( s2_size )
+ cy = _gcry_mpih_sub_n(res_ptr, s1_ptr, s2_ptr, s2_size);
+
+ if( s1_size - s2_size )
+ cy = _gcry_mpih_sub_1(res_ptr + s2_size, s1_ptr + s2_size,
+ s1_size - s2_size, cy);
+ return cy;
+}
+
+/****************
+ * Compare OP1_PTR/OP1_SIZE with OP2_PTR/OP2_SIZE.
+ * There are no restrictions on the relative sizes of
+ * the two arguments.
+ * Return 1 if OP1 > OP2, 0 if they are equal, and -1 if OP1 < OP2.
+ */
+G10_MPI_INLINE_DECL int
+_gcry_mpih_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size )
+{
+ mpi_size_t i;
+ mpi_limb_t op1_word, op2_word;
+
+ for( i = size - 1; i >= 0 ; i--) {
+ op1_word = op1_ptr[i];
+ op2_word = op2_ptr[i];
+ if( op1_word != op2_word )
+ goto diff;
+ }
+ return 0;
+
+ diff:
+ /* This can *not* be simplified to
+ * op2_word - op2_word
+ * since that expression might give signed overflow. */
+ return (op1_word > op2_word) ? 1 : -1;
+}
+
+
+#endif /*G10_MPI_INLINE_H*/
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-internal.h linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-internal.h
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-internal.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-internal.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,330 @@
+/* mpi-internal.h - Internal to the Multi Precision Integers
+ * Copyright (C) 1998 Free Software Foundation, Inc.
+ * Copyright (C) 1994, 1996, 2000, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+#ifndef G10_MPI_INTERNAL_H
+#define G10_MPI_INTERNAL_H
+
+#ifndef BITS_PER_MPI_LIMB
+#if BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_INT
+ typedef unsigned int mpi_limb_t;
+ typedef signed int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG
+ typedef unsigned long int mpi_limb_t;
+ typedef signed long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG_LONG
+ typedef unsigned long long int mpi_limb_t;
+ typedef signed long long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_SHORT
+ typedef unsigned short int mpi_limb_t;
+ typedef signed short int mpi_limb_signed_t;
+#else
+ #error BYTES_PER_MPI_LIMB does not match any C type
+#endif
+#define BITS_PER_MPI_LIMB (8*BYTES_PER_MPI_LIMB)
+#endif /*BITS_PER_MPI_LIMB*/
+
+/* If KARATSUBA_THRESHOLD is not already defined, define it to a
+ * value which is good on most machines. */
+
+/* tested 4, 16, 32 and 64, where 16 gave the best performance when
+ * checking a 768 and a 1024 bit ElGamal signature.
+ * (wk 22.12.97) */
+#ifndef KARATSUBA_THRESHOLD
+ #define KARATSUBA_THRESHOLD 16
+#endif
+
+/* The code can't handle KARATSUBA_THRESHOLD smaller than 2. */
+#if KARATSUBA_THRESHOLD < 2
+ #undef KARATSUBA_THRESHOLD
+ #define KARATSUBA_THRESHOLD 2
+#endif
+
+typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
+typedef int mpi_size_t; /* (must be a signed type) */
+
+
+#include "gcrypt.h"
+
+#define mpi_is_opaque(a) ((a) && ((a)->flags&4))
+#define mpi_resize(a,b) _gcry_mpi_resize((a),(b))
+#define mpi_free(a) _gcry_mpi_free((a) )
+#define mpi_alloc(n) _gcry_mpi_alloc((n) )
+#define mpi_get_nlimbs(a) ((a)->nlimbs)
+#define mpi_is_neg(a) ((a)->sign)
+#define mpi_is_secure(a) ((a) && ((a)->flags&1))
+#define mpi_alloc_set_ui(a) _gcry_mpi_alloc_set_ui ((a))
+#define mpi_alloc_secure(n) _gcry_mpi_alloc_secure((n) )
+#define mpi_clear(a) _gcry_mpi_clear ((a))
+#define mpi_normalize(a) _gcry_mpi_normalize ((a))
+#define mpi_fdiv_r_ui(a,b,c) _gcry_mpi_fdiv_r_ui((a),(b),(c))
+#define mpi_fdiv_r(a,b,c) _gcry_mpi_fdiv_r((a),(b),(c))
+#define mpi_fdiv_q(a,b,c) _gcry_mpi_fdiv_q((a),(b),(c))
+#define mpi_fdiv_qr(a,b,c,d) _gcry_mpi_fdiv_qr((a),(b),(c),(d))
+#define mpi_tdiv_r(a,b,c) _gcry_mpi_tdiv_r((a),(b),(c))
+#define mpi_tdiv_qr(a,b,c,d) _gcry_mpi_tdiv_qr((a),(b),(c),(d))
+#define mpi_tdiv_q_2exp(a,b,c) _gcry_mpi_tdiv_q_2exp((a),(b),(c))
+#define mpi_divisible_ui(a,b) _gcry_mpi_divisible_ui((a),(b))
+
+void _gcry_mpi_normalize( MPI a );
+
+ulong _gcry_mpi_fdiv_r_ui( MPI rem, MPI dividend, ulong divisor );
+void _gcry_mpi_fdiv_r( MPI rem, MPI dividend, MPI divisor );
+void _gcry_mpi_fdiv_q( MPI quot, MPI dividend, MPI divisor );
+void _gcry_mpi_fdiv_qr( MPI quot, MPI rem, MPI dividend, MPI divisor );
+void _gcry_mpi_tdiv_r( MPI rem, MPI num, MPI den);
+void _gcry_mpi_tdiv_qr( MPI quot, MPI rem, MPI num, MPI den);
+void _gcry_mpi_tdiv_q_2exp( MPI w, MPI u, unsigned count );
+int _gcry_mpi_divisible_ui(MPI dividend, ulong divisor );
+void _gcry_check_heap( const void *a );
+
+MPI _gcry_mpi_alloc( unsigned nlimbs );
+MPI _gcry_mpi_alloc_secure( unsigned nlimbs );
+void _gcry_mpi_free( MPI a );
+void _gcry_mpi_resize( MPI a, unsigned nlimbs );
+MPI _gcry_mpi_copy( MPI a );
+void _gcry_mpi_clear( MPI a );
+MPI _gcry_mpi_alloc_like( MPI a );
+void _gcry_mpi_set( MPI w, MPI u);
+void _gcry_mpi_set_ui( MPI w, ulong u);
+MPI _gcry_mpi_alloc_set_ui( unsigned long u);
+void _gcry_mpi_m_check( MPI a );
+void _gcry_mpi_swap( MPI a, MPI b);
+
+void _gcry_mpi_set_buffer( MPI a, const byte *buffer, unsigned nbytes, int sign );
+#define mpi_mulpowm(a,b,c,d) _gcry_mpi_mulpowm ((a),(b),(c),(d))
+void _gcry_mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI mod);
+
+
+
+#define ABS(x) (x >= 0 ? x : -x)
+#define MIN(l,o) ((l) < (o) ? (l) : (o))
+#define MAX(h,i) ((h) > (i) ? (h) : (i))
+#define RESIZE_IF_NEEDED(a,b) \
+ do { \
+ if( (a)->alloced < (b) ) \
+ mpi_resize((a), (b)); \
+ } while(0)
+
+/* Copy N limbs from S to D. */
+#define MPN_COPY( d, s, n) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = (s)[_i]; \
+ } while(0)
+
+#define MPN_COPY_INCR( d, s, n) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = (d)[_i]; \
+ } while (0)
+
+#define MPN_COPY_DECR( d, s, n ) \
+ do { \
+ mpi_size_t _i; \
+ for( _i = (n)-1; _i >= 0; _i--) \
+ (d)[_i] = (s)[_i]; \
+ } while(0)
+
+/* Zero N limbs at D */
+#define MPN_ZERO(d, n) \
+ do { \
+ int _i; \
+ for( _i = 0; _i < (n); _i++ ) \
+ (d)[_i] = 0; \
+ } while (0)
+
+#define MPN_NORMALIZE(d, n) \
+ do { \
+ while( (n) > 0 ) { \
+ if( (d)[(n)-1] ) \
+ break; \
+ (n)--; \
+ } \
+ } while(0)
+
+#define MPN_NORMALIZE_NOT_ZERO(d, n) \
+ do { \
+ for(;;) { \
+ if( (d)[(n)-1] ) \
+ break; \
+ (n)--; \
+ } \
+ } while(0)
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+ do { \
+ if( (size) < KARATSUBA_THRESHOLD ) \
+ mul_n_basecase (prodp, up, vp, size); \
+ else \
+ mul_n (prodp, up, vp, size, tspace); \
+ } while (0);
+
+
+/* Divide the two-limb number in (NH,,NL) by D, with DI being the largest
+ * limb not larger than (2**(2*BITS_PER_MP_LIMB))/D - (2**BITS_PER_MP_LIMB).
+ * If this would yield overflow, DI should be the largest possible number
+ * (i.e., only ones). For correct operation, the most significant bit of D
+ * has to be set. Put the quotient in Q and the remainder in R.
+ */
+#define UDIV_QRNND_PREINV(q, r, nh, nl, d, di) \
+ do { \
+ mpi_limb_t _q, _ql, _r; \
+ mpi_limb_t _xh, _xl; \
+ umul_ppmm (_q, _ql, (nh), (di)); \
+ _q += (nh); /* DI is 2**BITS_PER_MPI_LIMB too small */ \
+ umul_ppmm (_xh, _xl, _q, (d)); \
+ sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl); \
+ if( _xh ) { \
+ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
+ _q++; \
+ if( _xh) { \
+ sub_ddmmss (_xh, _r, _xh, _r, 0, (d)); \
+ _q++; \
+ } \
+ } \
+ if( _r >= (d) ) { \
+ _r -= (d); \
+ _q++; \
+ } \
+ (r) = _r; \
+ (q) = _q; \
+ } while (0)
+
+
+/*-- mpiutil.c --*/
+#ifdef M_DEBUG
+ #define mpi_alloc_limb_space(n,f) _gcry_mpi_debug_alloc_limb_space((n),(f), M_DBGINFO( __LINE__ ) )
+ #define mpi_free_limb_space(n) _gcry_mpi_debug_free_limb_space((n), M_DBGINFO( __LINE__ ) )
+ mpi_ptr_t _gcry_mpi_debug_alloc_limb_space( unsigned nlimbs, int sec, const char *info );
+ void _gcry_mpi_debug_free_limb_space( mpi_ptr_t a, const char *info );
+#else
+ #define mpi_alloc_limb_space(n,f) _gcry_mpi_alloc_limb_space((n),(f))
+ #define mpi_free_limb_space(n) _gcry_mpi_free_limb_space((n))
+ mpi_ptr_t _gcry_mpi_alloc_limb_space( unsigned nlimbs, int sec );
+ void _gcry_mpi_free_limb_space( mpi_ptr_t a );
+#endif
+void _gcry_mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs );
+
+/*-- mpi-bit.c --*/
+void _gcry_mpi_rshift_limbs( MPI a, unsigned int count );
+void _gcry_mpi_lshift_limbs( MPI a, unsigned int count );
+
+
+/*-- mpih-add.c --*/
+mpi_limb_t _gcry_mpih_add_1(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb );
+mpi_limb_t _gcry_mpih_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size);
+mpi_limb_t _gcry_mpih_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size);
+
+/*-- mpih-sub.c --*/
+mpi_limb_t _gcry_mpih_sub_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb );
+mpi_limb_t _gcry_mpih_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_ptr_t s2_ptr, mpi_size_t size);
+mpi_limb_t _gcry_mpih_sub(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+ mpi_ptr_t s2_ptr, mpi_size_t s2_size);
+
+/*-- mpih-cmp.c --*/
+int _gcry_mpih_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size );
+
+/*-- mpih-mul.c --*/
+
+struct karatsuba_ctx {
+ struct karatsuba_ctx *next;
+ mpi_ptr_t tspace;
+ mpi_size_t tspace_size;
+ mpi_ptr_t tp;
+ mpi_size_t tp_size;
+};
+
+void _gcry_mpih_release_karatsuba_ctx( struct karatsuba_ctx *ctx );
+
+mpi_limb_t _gcry_mpih_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb);
+mpi_limb_t _gcry_mpih_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb);
+void _gcry_mpih_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+ mpi_size_t size);
+mpi_limb_t _gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize);
+void _gcry_mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size );
+void _gcry_mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size,
+ mpi_ptr_t tspace);
+
+void _gcry_mpih_mul_karatsuba_case( mpi_ptr_t prodp,
+ mpi_ptr_t up, mpi_size_t usize,
+ mpi_ptr_t vp, mpi_size_t vsize,
+ struct karatsuba_ctx *ctx );
+
+
+/*-- mpih-mul_1.c (or xxx/cpu/ *.S) --*/
+mpi_limb_t _gcry_mpih_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+ mpi_size_t s1_size, mpi_limb_t s2_limb);
+
+/*-- mpih-div.c --*/
+mpi_limb_t _gcry_mpih_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb);
+mpi_limb_t _gcry_mpih_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+ mpi_ptr_t np, mpi_size_t nsize,
+ mpi_ptr_t dp, mpi_size_t dsize);
+mpi_limb_t _gcry_mpih_divmod_1( mpi_ptr_t quot_ptr,
+ mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+ mpi_limb_t divisor_limb);
+
+/*-- mpih-shift.c --*/
+mpi_limb_t _gcry_mpih_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+ unsigned cnt);
+mpi_limb_t _gcry_mpih_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+ unsigned cnt);
+
+
+/* Define stuff for longlong.h. */
+#define W_TYPE_SIZE BITS_PER_MPI_LIMB
+ typedef mpi_limb_t UWtype;
+ typedef unsigned int UHWtype;
+#if defined (__GNUC__)
+ typedef unsigned int UQItype __attribute__ ((mode (QI)));
+ typedef int SItype __attribute__ ((mode (SI)));
+ typedef unsigned int USItype __attribute__ ((mode (SI)));
+ typedef int DItype __attribute__ ((mode (DI)));
+ typedef unsigned int UDItype __attribute__ ((mode (DI)));
+#else
+ typedef unsigned char UQItype;
+ typedef long SItype;
+ typedef unsigned long USItype;
+#endif
+
+#include "kernel-interface.h"
+
+
+
+#endif /*G10_MPI_INTERNAL_H*/
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-inv.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-inv.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-inv.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-inv.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,277 @@
+/* mpi-inv.c - MPI functions
+ * Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+//#include "g10lib.h"
+
+/****************
+ * Calculate the multiplicative inverse X of A mod N
+ * That is: Find the solution x for
+ * 1 = (a*x) mod n
+ */
+void
+_gcry_mpi_invm( MPI x, MPI a, MPI n )
+{
+ #if 0
+ MPI u, v, u1, u2, u3, v1, v2, v3, q, t1, t2, t3;
+ MPI ta, tb, tc;
+
+ u = mpi_copy(a);
+ v = mpi_copy(n);
+ u1 = mpi_alloc_set_ui(1);
+ u2 = mpi_alloc_set_ui(0);
+ u3 = mpi_copy(u);
+ v1 = mpi_alloc_set_ui(0);
+ v2 = mpi_alloc_set_ui(1);
+ v3 = mpi_copy(v);
+ q = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ t1 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ t2 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ t3 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+ while( mpi_cmp_ui( v3, 0 ) ) {
+ mpi_fdiv_q( q, u3, v3 );
+ mpi_mul(t1, v1, q); mpi_mul(t2, v2, q); mpi_mul(t3, v3, q);
+ mpi_sub(t1, u1, t1); mpi_sub(t2, u2, t2); mpi_sub(t3, u3, t3);
+ mpi_set(u1, v1); mpi_set(u2, v2); mpi_set(u3, v3);
+ mpi_set(v1, t1); mpi_set(v2, t2); mpi_set(v3, t3);
+ }
+ /* log_debug("result:\n");
+ log_mpidump("q =", q );
+ log_mpidump("u1=", u1);
+ log_mpidump("u2=", u2);
+ log_mpidump("u3=", u3);
+ log_mpidump("v1=", v1);
+ log_mpidump("v2=", v2); */
+ mpi_set(x, u1);
+
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(u3);
+ mpi_free(v1);
+ mpi_free(v2);
+ mpi_free(v3);
+ mpi_free(q);
+ mpi_free(t1);
+ mpi_free(t2);
+ mpi_free(t3);
+ mpi_free(u);
+ mpi_free(v);
+ #elif 0
+ /* Extended Euclid's algorithm (See TAOCP Vol II, 4.5.2, Alg X)
+ * modified according to Michael Penk's solution for Exercise 35 */
+
+ /* FIXME: we can simplify this in most cases (see Knuth) */
+ MPI u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3;
+ unsigned k;
+ int sign;
+
+ u = mpi_copy(a);
+ v = mpi_copy(n);
+ for(k=0; !mpi_test_bit(u,0) && !mpi_test_bit(v,0); k++ ) {
+ mpi_rshift(u, u, 1);
+ mpi_rshift(v, v, 1);
+ }
+
+
+ u1 = mpi_alloc_set_ui(1);
+ u2 = mpi_alloc_set_ui(0);
+ u3 = mpi_copy(u);
+ v1 = mpi_copy(v); /* !-- used as const 1 */
+ v2 = mpi_alloc( mpi_get_nlimbs(u) ); mpi_sub( v2, u1, u );
+ v3 = mpi_copy(v);
+ if( mpi_test_bit(u, 0) ) { /* u is odd */
+ t1 = mpi_alloc_set_ui(0);
+ t2 = mpi_alloc_set_ui(1); t2->sign = 1;
+ t3 = mpi_copy(v); t3->sign = !t3->sign;
+ goto Y4;
+ }
+ else {
+ t1 = mpi_alloc_set_ui(1);
+ t2 = mpi_alloc_set_ui(0);
+ t3 = mpi_copy(u);
+ }
+ do {
+ do {
+ if( mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0) ) { /* one is odd */
+ mpi_add(t1, t1, v);
+ mpi_sub(t2, t2, u);
+ }
+ mpi_rshift(t1, t1, 1);
+ mpi_rshift(t2, t2, 1);
+ mpi_rshift(t3, t3, 1);
+ Y4:
+ ;
+ } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
+
+ if( !t3->sign ) {
+ mpi_set(u1, t1);
+ mpi_set(u2, t2);
+ mpi_set(u3, t3);
+ }
+ else {
+ mpi_sub(v1, v, t1);
+ sign = u->sign; u->sign = !u->sign;
+ mpi_sub(v2, u, t2);
+ u->sign = sign;
+ sign = t3->sign; t3->sign = !t3->sign;
+ mpi_set(v3, t3);
+ t3->sign = sign;
+ }
+ mpi_sub(t1, u1, v1);
+ mpi_sub(t2, u2, v2);
+ mpi_sub(t3, u3, v3);
+ if( t1->sign ) {
+ mpi_add(t1, t1, v);
+ mpi_sub(t2, t2, u);
+ }
+ } while( mpi_cmp_ui( t3, 0 ) ); /* while t3 != 0 */
+ /* mpi_lshift( u3, k ); */
+ mpi_set(x, u1);
+
+ mpi_free(u1);
+ mpi_free(u2);
+ mpi_free(u3);
+ mpi_free(v1);
+ mpi_free(v2);
+ mpi_free(v3);
+ mpi_free(t1);
+ mpi_free(t2);
+ mpi_free(t3);
+ #else
+ /* Extended Euclid's algorithm (See TAOCP Vol II, 4.5.2, Alg X)
+ * modified according to Michael Penk's solution for Exercise 35
+ * with further enhancement */
+ MPI u, v, u1, u2=NULL, u3, v1, v2=NULL, v3, t1, t2=NULL, t3;
+ unsigned k;
+ int sign;
+ int odd ;
+
+ u = mpi_copy(a);
+ v = mpi_copy(n);
+
+ for(k=0; !mpi_test_bit(u,0) && !mpi_test_bit(v,0); k++ ) {
+ mpi_rshift(u, u, 1);
+ mpi_rshift(v, v, 1);
+ }
+ odd = mpi_test_bit(v,0);
+
+ u1 = mpi_alloc_set_ui(1);
+ if( !odd )
+ u2 = mpi_alloc_set_ui(0);
+ u3 = mpi_copy(u);
+ v1 = mpi_copy(v);
+ if( !odd ) {
+ v2 = mpi_alloc( mpi_get_nlimbs(u) );
+ mpi_sub( v2, u1, u ); /* U is used as const 1 */
+ }
+ v3 = mpi_copy(v);
+ if( mpi_test_bit(u, 0) ) { /* u is odd */
+ t1 = mpi_alloc_set_ui(0);
+ if( !odd ) {
+ t2 = mpi_alloc_set_ui(1); t2->sign = 1;
+ }
+ t3 = mpi_copy(v); t3->sign = !t3->sign;
+ goto Y4;
+ }
+ else {
+ t1 = mpi_alloc_set_ui(1);
+ if( !odd )
+ t2 = mpi_alloc_set_ui(0);
+ t3 = mpi_copy(u);
+ }
+ do {
+ do {
+ if( !odd ) {
+ if( mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0) ) { /* one is odd */
+ mpi_add(t1, t1, v);
+ mpi_sub(t2, t2, u);
+ }
+ mpi_rshift(t1, t1, 1);
+ mpi_rshift(t2, t2, 1);
+ mpi_rshift(t3, t3, 1);
+ }
+ else {
+ if( mpi_test_bit(t1, 0) )
+ mpi_add(t1, t1, v);
+ mpi_rshift(t1, t1, 1);
+ mpi_rshift(t3, t3, 1);
+ }
+ Y4:
+ ;
+ } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
+
+ if( !t3->sign ) {
+ mpi_set(u1, t1);
+ if( !odd )
+ mpi_set(u2, t2);
+ mpi_set(u3, t3);
+ }
+ else {
+ mpi_sub(v1, v, t1);
+ sign = u->sign; u->sign = !u->sign;
+ if( !odd )
+ mpi_sub(v2, u, t2);
+ u->sign = sign;
+ sign = t3->sign; t3->sign = !t3->sign;
+ mpi_set(v3, t3);
+ t3->sign = sign;
+ }
+ mpi_sub(t1, u1, v1);
+ if( !odd )
+ mpi_sub(t2, u2, v2);
+ mpi_sub(t3, u3, v3);
+ if( t1->sign ) {
+ mpi_add(t1, t1, v);
+ if( !odd )
+ mpi_sub(t2, t2, u);
+ }
+ } while( mpi_cmp_ui( t3, 0 ) ); /* while t3 != 0 */
+ /* mpi_lshift( u3, k ); */
+ mpi_set(x, u1);
+
+ mpi_free(u1);
+ mpi_free(v1);
+ mpi_free(t1);
+ if( !odd ) {
+ mpi_free(u2);
+ mpi_free(v2);
+ mpi_free(t2);
+ }
+ mpi_free(u3);
+ mpi_free(v3);
+ mpi_free(t3);
+
+ mpi_free(u);
+ mpi_free(v);
+ #endif
+}
+
+
+int
+gcry_mpi_invm (MPI x, MPI a, MPI n)
+{
+ _gcry_mpi_invm (x, a, n);
+ return 1;
+}
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-mpow.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-mpow.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-mpow.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-mpow.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,226 @@
+/* mpi-mpow.c - MPI functions
+ * Copyright (C) 1998, 1999, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+#include "longlong.h"
+//#include "g10lib.h"
+//#include
+
+
+/* Barrett is slower than the classical way. It can be tweaked by
+ * using partial multiplications
+ */
+/*#define USE_BARRETT*/
+
+
+
+#ifdef USE_BARRETT
+static void barrett_mulm( MPI w, MPI u, MPI v, MPI m, MPI y, int k, MPI r1, MPI r2 );
+static MPI init_barrett( MPI m, int *k, MPI *r1, MPI *r2 );
+static int calc_barrett( MPI r, MPI x, MPI m, MPI y, int k, MPI r1, MPI r2 );
+#else
+#define barrett_mulm( w, u, v, m, y, k, r1, r2 ) gcry_mpi_mulm( (w), (u), (v), (m) )
+#endif
+
+
+static int
+build_index( MPI *exparray, int k, int i, int t )
+{
+ int j, bitno;
+ int idx = 0;
+
+ bitno = t-i;
+ for(j=k-1; j >= 0; j-- ) {
+ idx <<= 1;
+ if( mpi_test_bit( exparray[j], bitno ) )
+ idx |= 1;
+ }
+ /*log_debug("t=%d i=%d idx=%d\n", t, i, idx );*/
+ return idx;
+}
+
+/****************
+ * RES = (BASE[0] ^ EXP[0]) * (BASE[1] ^ EXP[1]) * ... * mod M
+ */
+void
+_gcry_mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI m)
+{
+ int k; /* number of elements */
+ int t; /* bit size of largest exponent */
+ int i, j, idx;
+ MPI *G; /* table with precomputed values of size 2^k */
+ MPI tmp;
+ #ifdef USE_BARRETT
+ MPI barrett_y, barrett_r1, barrett_r2;
+ int barrett_k;
+ #endif
+
+ for(k=0; basearray[k]; k++ )
+ ;
+// assert(k);
+ for(t=0, i=0; (tmp=exparray[i]); i++ ) {
+ /*log_mpidump("exp: ", tmp );*/
+ j = mpi_get_nbits(tmp);
+ if( j > t )
+ t = j;
+ }
+ /*log_mpidump("mod: ", m );*/
+// assert(i==k);
+// assert(t);
+// assert( k < 10 );
+
+ G = gcry_xcalloc( (1<= 0 && idx < (1< 3 ? k-3:0;
+
+ mpi_normalize( x );
+ if( mpi_get_nlimbs(x) > 2*k )
+ return 1; /* can't do it */
+
+ /* 1. q1 = floor( x / b^k-1)
+ * q2 = q1 * y
+ * q3 = floor( q2 / b^k+1 )
+ * Actually, we don't need qx, we can work direct on r2
+ */
+ mpi_set( r2, x );
+ mpi_rshift_limbs( r2, k-1 );
+ mpi_mul( r2, r2, y );
+ mpi_rshift_limbs( r2, k+1 );
+
+ /* 2. r1 = x mod b^k+1
+ * r2 = q3 * m mod b^k+1
+ * r = r1 - r2
+ * 3. if r < 0 then r = r + b^k+1
+ */
+ mpi_set( r1, x );
+ if( r1->nlimbs > k+1 ) /* quick modulo operation */
+ r1->nlimbs = k+1;
+ mpi_mul( r2, r2, m );
+ if( r2->nlimbs > k+1 ) /* quick modulo operation */
+ r2->nlimbs = k+1;
+ mpi_sub( r, r1, r2 );
+
+ if( mpi_is_neg( r ) ) {
+ MPI tmp;
+
+ tmp = mpi_alloc( k + 2 );
+ mpi_set_ui( tmp, 1 );
+ mpi_lshift_limbs( tmp, k+1 );
+ mpi_add( r, r, tmp );
+ mpi_free(tmp);
+ }
+
+ /* 4. while r >= m do r = r - m */
+ while( mpi_cmp( r, m ) >= 0 )
+ mpi_sub( r, r, m );
+
+ return 0;
+}
+#endif /* USE_BARRETT */
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-mul.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-mul.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-mul.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-mul.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,219 @@
+/* mpi-mul.c - MPI functions
+ * Copyright (C) 1994, 1996, 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+/*#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+
+
+void
+gcry_mpi_mul_ui( MPI prod, MPI mult, unsigned long small_mult )
+{
+ mpi_size_t size, prod_size;
+ mpi_ptr_t prod_ptr;
+ mpi_limb_t cy;
+ int sign;
+
+ size = mult->nlimbs;
+ sign = mult->sign;
+
+ if( !size || !small_mult ) {
+ prod->nlimbs = 0;
+ prod->sign = 0;
+ return;
+ }
+
+ prod_size = size + 1;
+ if( prod->alloced < prod_size )
+ mpi_resize( prod, prod_size );
+ prod_ptr = prod->d;
+
+ cy = _gcry_mpih_mul_1( prod_ptr, mult->d, size, (mpi_limb_t)small_mult );
+ if( cy )
+ prod_ptr[size++] = cy;
+ prod->nlimbs = size;
+ prod->sign = sign;
+}
+
+
+void
+_gcry_mpi_mul_2exp( MPI w, MPI u, unsigned long cnt)
+{
+ mpi_size_t usize, wsize, limb_cnt;
+ mpi_ptr_t wp;
+ mpi_limb_t wlimb;
+ int usign, wsign;
+
+ usize = u->nlimbs;
+ usign = u->sign;
+
+ if( !usize ) {
+ w->nlimbs = 0;
+ w->sign = 0;
+ return;
+ }
+
+ limb_cnt = cnt / BITS_PER_MPI_LIMB;
+ wsize = usize + limb_cnt + 1;
+ if( w->alloced < wsize )
+ mpi_resize(w, wsize );
+ wp = w->d;
+ wsize = usize + limb_cnt;
+ wsign = usign;
+
+ cnt %= BITS_PER_MPI_LIMB;
+ if( cnt ) {
+ wlimb = _gcry_mpih_lshift( wp + limb_cnt, u->d, usize, cnt );
+ if( wlimb ) {
+ wp[wsize] = wlimb;
+ wsize++;
+ }
+ }
+ else {
+ MPN_COPY_DECR( wp + limb_cnt, u->d, usize );
+ }
+
+ /* Zero all whole limbs at low end. Do it here and not before calling
+ * mpn_lshift, not to lose for U == W. */
+ MPN_ZERO( wp, limb_cnt );
+
+ w->nlimbs = wsize;
+ w->sign = wsign;
+}
+
+
+void
+gcry_mpi_mul_2exp( MPI w, MPI u, unsigned long cnt)
+{
+ _gcry_mpi_mul_2exp (w, u, cnt);
+}
+
+
+void
+gcry_mpi_mul( MPI w, MPI u, MPI v)
+{
+ mpi_size_t usize, vsize, wsize;
+ mpi_ptr_t up, vp, wp;
+ mpi_limb_t cy;
+ int usign, vsign, usecure, vsecure, sign_product;
+ int assign_wp=0;
+ mpi_ptr_t tmp_limb=NULL;
+
+
+ if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+ usize = v->nlimbs;
+ usign = v->sign;
+ usecure = mpi_is_secure(v);
+ up = v->d;
+ vsize = u->nlimbs;
+ vsign = u->sign;
+ vsecure = mpi_is_secure(u);
+ vp = u->d;
+ }
+ else {
+ usize = u->nlimbs;
+ usign = u->sign;
+ usecure = mpi_is_secure(u);
+ up = u->d;
+ vsize = v->nlimbs;
+ vsign = v->sign;
+ vsecure = mpi_is_secure(v);
+ vp = v->d;
+ }
+ sign_product = usign ^ vsign;
+ wp = w->d;
+
+ /* Ensure W has space enough to store the result. */
+ wsize = usize + vsize;
+ if ( !mpi_is_secure (w) && (mpi_is_secure (u) || mpi_is_secure (v)) ) {
+ /* w is not allocated in secure space but u or v is. To make sure
+ * that no temporray results are stored in w, we temporary use
+ * a newly allocated limb space for w */
+ wp = mpi_alloc_limb_space( wsize, 1 );
+ assign_wp = 2; /* mark it as 2 so that we can later copy it back to
+ * mormal memory */
+ }
+ else if( w->alloced < wsize ) {
+ if( wp == up || wp == vp ) {
+ wp = mpi_alloc_limb_space( wsize, mpi_is_secure(w) );
+ assign_wp = 1;
+ }
+ else {
+ mpi_resize(w, wsize );
+ wp = w->d;
+ }
+ }
+ else { /* Make U and V not overlap with W. */
+ if( wp == up ) {
+ /* W and U are identical. Allocate temporary space for U. */
+ up = tmp_limb = mpi_alloc_limb_space( usize, usecure );
+ /* Is V identical too? Keep it identical with U. */
+ if( wp == vp )
+ vp = up;
+ /* Copy to the temporary space. */
+ MPN_COPY( up, wp, usize );
+ }
+ else if( wp == vp ) {
+ /* W and V are identical. Allocate temporary space for V. */
+ vp = tmp_limb = mpi_alloc_limb_space( vsize, vsecure );
+ /* Copy to the temporary space. */
+ MPN_COPY( vp, wp, vsize );
+ }
+ }
+
+ if( !vsize )
+ wsize = 0;
+ else {
+ cy = _gcry_mpih_mul( wp, up, usize, vp, vsize );
+ wsize -= cy? 0:1;
+ }
+
+ if( assign_wp ) {
+ if (assign_wp == 2) {
+ /* copy the temp wp from secure memory back to normal memory */
+ mpi_ptr_t tmp_wp = mpi_alloc_limb_space (wsize, 0);
+ MPN_COPY (tmp_wp, wp, wsize);
+ mpi_free_limb_space (wp);
+ wp = tmp_wp;
+ }
+ _gcry_mpi_assign_limb_space( w, wp, wsize );
+ }
+ w->nlimbs = wsize;
+ w->sign = sign_product;
+ if( tmp_limb )
+ mpi_free_limb_space( tmp_limb );
+}
+
+
+void
+gcry_mpi_mulm( MPI w, MPI u, MPI v, MPI m)
+{
+ gcry_mpi_mul(w, u, v);
+ _gcry_mpi_fdiv_r( w, w, m );
+}
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-pow.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-pow.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-pow.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-pow.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,294 @@
+/* mpi-pow.c - MPI functions
+ * Copyright (C) 1994, 1996, 1998, 2000, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ * Actually it's the same code with only minor changes in the
+ * way the data is stored; this is to support the abstraction
+ * of an optional secure memory allocation which may be used
+ * to avoid revealing of sensitive data due to paging etc.
+ */
+
+/*
+#include
+#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+#include "longlong.h"
+//#include
+
+
+/****************
+ * RES = BASE ^ EXP mod MOD
+ */
+void
+gcry_mpi_powm( MPI res, MPI base, MPI exp, MPI mod)
+{
+ mpi_ptr_t rp, ep, mp, bp;
+ mpi_size_t esize, msize, bsize, rsize;
+ int esign, msign, bsign, rsign;
+ int esec, msec, bsec, rsec;
+ mpi_size_t size;
+ int mod_shift_cnt;
+ int negative_result;
+ mpi_ptr_t mp_marker=NULL, bp_marker=NULL, ep_marker=NULL;
+ mpi_ptr_t xp_marker=NULL;
+ int assign_rp=0;
+ mpi_ptr_t tspace = NULL;
+ mpi_size_t tsize=0; /* to avoid compiler warning */
+ /* fixme: we should check that the warning is void*/
+
+ esize = exp->nlimbs;
+ msize = mod->nlimbs;
+ size = 2 * msize;
+ esign = exp->sign;
+ msign = mod->sign;
+
+ esec = mpi_is_secure(exp);
+ msec = mpi_is_secure(mod);
+ bsec = mpi_is_secure(base);
+ rsec = mpi_is_secure(res);
+
+ rp = res->d;
+ ep = exp->d;
+
+ if( !msize )
+ msize = 1 / msize; /* provoke a signal */
+
+ if( !esize ) {
+ /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
+ * depending on if MOD equals 1. */
+ rp[0] = 1;
+ res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
+ res->sign = 0;
+ goto leave;
+ }
+
+ /* Normalize MOD (i.e. make its most significant bit set) as required by
+ * mpn_divrem. This will make the intermediate values in the calculation
+ * slightly larger, but the correct result is obtained after a final
+ * reduction using the original MOD value. */
+ mp = mp_marker = mpi_alloc_limb_space(msize, msec);
+ count_leading_zeros( mod_shift_cnt, mod->d[msize-1] );
+ if( mod_shift_cnt )
+ _gcry_mpih_lshift( mp, mod->d, msize, mod_shift_cnt );
+ else
+ MPN_COPY( mp, mod->d, msize );
+
+ bsize = base->nlimbs;
+ bsign = base->sign;
+ if( bsize > msize ) { /* The base is larger than the module. Reduce it. */
+ /* Allocate (BSIZE + 1) with space for remainder and quotient.
+ * (The quotient is (bsize - msize + 1) limbs.) */
+ bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
+ MPN_COPY( bp, base->d, bsize );
+ /* We don't care about the quotient, store it above the remainder,
+ * at BP + MSIZE. */
+ _gcry_mpih_divrem( bp + msize, 0, bp, bsize, mp, msize );
+ bsize = msize;
+ /* Canonicalize the base, since we are going to multiply with it
+ * quite a few times. */
+ MPN_NORMALIZE( bp, bsize );
+ }
+ else
+ bp = base->d;
+
+ if( !bsize ) {
+ res->nlimbs = 0;
+ res->sign = 0;
+ goto leave;
+ }
+
+ if( res->alloced < size ) {
+ /* We have to allocate more space for RES. If any of the input
+ * parameters are identical to RES, defer deallocation of the old
+ * space. */
+ if( rp == ep || rp == mp || rp == bp ) {
+ rp = mpi_alloc_limb_space( size, rsec );
+ assign_rp = 1;
+ }
+ else {
+ mpi_resize( res, size );
+ rp = res->d;
+ }
+ }
+ else { /* Make BASE, EXP and MOD not overlap with RES. */
+ if( rp == bp ) {
+ /* RES and BASE are identical. Allocate temp. space for BASE. */
+// assert( !bp_marker );
+ bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
+ MPN_COPY(bp, rp, bsize);
+ }
+ if( rp == ep ) {
+ /* RES and EXP are identical. Allocate temp. space for EXP. */
+ ep = ep_marker = mpi_alloc_limb_space( esize, esec );
+ MPN_COPY(ep, rp, esize);
+ }
+ if( rp == mp ) {
+ /* RES and MOD are identical. Allocate temporary space for MOD.*/
+// assert( !mp_marker );
+ mp = mp_marker = mpi_alloc_limb_space( msize, msec );
+ MPN_COPY(mp, rp, msize);
+ }
+ }
+
+ MPN_COPY( rp, bp, bsize );
+ rsize = bsize;
+ rsign = bsign;
+
+ {
+ mpi_size_t i;
+ mpi_ptr_t xp = xp_marker = mpi_alloc_limb_space( 2 * (msize + 1), msec );
+ int c;
+ mpi_limb_t e;
+ mpi_limb_t carry_limb;
+ struct karatsuba_ctx karactx;
+
+ memset( &karactx, 0, sizeof karactx );
+ negative_result = (ep[0] & 1) && base->sign;
+
+ i = esize - 1;
+ e = ep[i];
+ count_leading_zeros (c, e);
+ e = (e << c) << 1; /* shift the exp bits to the left, lose msb */
+ c = BITS_PER_MPI_LIMB - 1 - c;
+
+ /* Main loop.
+ *
+ * Make the result be pointed to alternately by XP and RP. This
+ * helps us avoid block copying, which would otherwise be necessary
+ * with the overlap restrictions of _gcry_mpih_divmod. With 50% probability
+ * the result after this loop will be in the area originally pointed
+ * by RP (==RES->d), and with 50% probability in the area originally
+ * pointed to by XP.
+ */
+
+ for(;;) {
+ while( c ) {
+ mpi_ptr_t tp;
+ mpi_size_t xsize;
+
+ /*mpih_mul_n(xp, rp, rp, rsize);*/
+ if( rsize < KARATSUBA_THRESHOLD )
+ _gcry_mpih_sqr_n_basecase( xp, rp, rsize );
+ else {
+ if( !tspace ) {
+ tsize = 2 * rsize;
+ tspace = mpi_alloc_limb_space( tsize, 0 );
+ }
+ else if( tsize < (2*rsize) ) {
+ mpi_free_limb_space( tspace );
+ tsize = 2 * rsize;
+ tspace = mpi_alloc_limb_space( tsize, 0 );
+ }
+ _gcry_mpih_sqr_n( xp, rp, rsize, tspace );
+ }
+
+ xsize = 2 * rsize;
+ if( xsize > msize ) {
+ _gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
+ xsize = msize;
+ }
+
+ tp = rp; rp = xp; xp = tp;
+ rsize = xsize;
+
+ if( (mpi_limb_signed_t)e < 0 ) {
+ /*mpih_mul( xp, rp, rsize, bp, bsize );*/
+ if( bsize < KARATSUBA_THRESHOLD ) {
+ _gcry_mpih_mul( xp, rp, rsize, bp, bsize );
+ }
+ else {
+ _gcry_mpih_mul_karatsuba_case(
+ xp, rp, rsize, bp, bsize, &karactx );
+ }
+
+ xsize = rsize + bsize;
+ if( xsize > msize ) {
+ _gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
+ xsize = msize;
+ }
+
+ tp = rp; rp = xp; xp = tp;
+ rsize = xsize;
+ }
+ e <<= 1;
+ c--;
+ }
+
+ i--;
+ if( i < 0 )
+ break;
+ e = ep[i];
+ c = BITS_PER_MPI_LIMB;
+ }
+
+ /* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
+ * steps. Adjust the result by reducing it with the original MOD.
+ *
+ * Also make sure the result is put in RES->d (where it already
+ * might be, see above).
+ */
+ if( mod_shift_cnt ) {
+ carry_limb = _gcry_mpih_lshift( res->d, rp, rsize, mod_shift_cnt);
+ rp = res->d;
+ if( carry_limb ) {
+ rp[rsize] = carry_limb;
+ rsize++;
+ }
+ }
+ else {
+ MPN_COPY( res->d, rp, rsize);
+ rp = res->d;
+ }
+
+ if( rsize >= msize ) {
+ _gcry_mpih_divrem(rp + msize, 0, rp, rsize, mp, msize);
+ rsize = msize;
+ }
+
+ /* Remove any leading zero words from the result. */
+ if( mod_shift_cnt )
+ _gcry_mpih_rshift( rp, rp, rsize, mod_shift_cnt);
+ MPN_NORMALIZE (rp, rsize);
+
+ _gcry_mpih_release_karatsuba_ctx( &karactx );
+ }
+
+ if( negative_result && rsize ) {
+ if( mod_shift_cnt )
+ _gcry_mpih_rshift( mp, mp, msize, mod_shift_cnt);
+ _gcry_mpih_sub( rp, mp, msize, rp, rsize);
+ rsize = msize;
+ rsign = msign;
+ MPN_NORMALIZE(rp, rsize);
+ }
+ res->nlimbs = rsize;
+ res->sign = rsign;
+
+ leave:
+ if( assign_rp ) _gcry_mpi_assign_limb_space( res, rp, size );
+ if( mp_marker ) _gcry_mpi_free_limb_space( mp_marker );
+ if( bp_marker ) _gcry_mpi_free_limb_space( bp_marker );
+ if( ep_marker ) _gcry_mpi_free_limb_space( ep_marker );
+ if( xp_marker ) _gcry_mpi_free_limb_space( xp_marker );
+ if( tspace ) _gcry_mpi_free_limb_space( tspace );
+}
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpi-scan.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-scan.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpi-scan.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpi-scan.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,133 @@
+/* mpi-scan.c - MPI functions
+ * Copyright (C) 1998, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/*#include
+#include
+#include
+*/
+#include "kernel-interface.h"
+#include "longlong.h"
+
+/****************
+ * Scan through an mpi and return byte for byte. a -1 is returned to indicate
+ * the end of the mpi. Scanning is done from the lsb to the msb, returned
+ * values are in the range of 0 .. 255.
+ *
+ * FIXME: This code is VERY ugly!
+ */
+int
+_gcry_mpi_getbyte( MPI a, unsigned idx )
+{
+ int i, j;
+ unsigned n;
+ mpi_ptr_t ap;
+ mpi_limb_t limb;
+
+ ap = a->d;
+ for(n=0,i=0; i < a->nlimbs; i++ ) {
+ limb = ap[i];
+ for( j=0; j < BYTES_PER_MPI_LIMB; j++, n++ )
+ if( n == idx )
+ return (limb >> j*8) & 0xff;
+ }
+ return -1;
+}
+
+
+/****************
+ * Put a value at position IDX into A. idx counts from lsb to msb
+ */
+void
+_gcry_mpi_putbyte( MPI a, unsigned idx, int xc )
+{
+ int i, j;
+ unsigned n;
+ mpi_ptr_t ap;
+ mpi_limb_t limb, c;
+
+ c = xc & 0xff;
+ ap = a->d;
+ for(n=0,i=0; i < a->alloced; i++ ) {
+ limb = ap[i];
+ for( j=0; j < BYTES_PER_MPI_LIMB; j++, n++ )
+ if( n == idx ) {
+ #if BYTES_PER_MPI_LIMB == 4
+ if( j == 0 )
+ limb = (limb & 0xffffff00) | c;
+ else if( j == 1 )
+ limb = (limb & 0xffff00ff) | (c<<8);
+ else if( j == 2 )
+ limb = (limb & 0xff00ffff) | (c<<16);
+ else
+ limb = (limb & 0x00ffffff) | (c<<24);
+ #elif BYTES_PER_MPI_LIMB == 8
+ if( j == 0 )
+ limb = (limb & 0xffffffffffffff00) | c;
+ else if( j == 1 )
+ limb = (limb & 0xffffffffffff00ff) | (c<<8);
+ else if( j == 2 )
+ limb = (limb & 0xffffffffff00ffff) | (c<<16);
+ else if( j == 3 )
+ limb = (limb & 0xffffffff00ffffff) | (c<<24);
+ else if( j == 4 )
+ limb = (limb & 0xffffff00ffffffff) | (c<<32);
+ else if( j == 5 )
+ limb = (limb & 0xffff00ffffffffff) | (c<<40);
+ else if( j == 6 )
+ limb = (limb & 0xff00ffffffffffff) | (c<<48);
+ else
+ limb = (limb & 0x00ffffffffffffff) | (c<<56);
+ #else
+ #error please enhance this function, its ugly - i know.
+ #endif
+ if( a->nlimbs <= i )
+ a->nlimbs = i+1;
+ ap[i] = limb;
+ return;
+ }
+ }
+// abort(); /* index out of range */
+}
+
+
+/****************
+ * Count the number of zerobits at the low end of A
+ */
+unsigned
+_gcry_mpi_trailing_zeros( MPI a )
+{
+ unsigned n, count = 0;
+
+ for(n=0; n < a->nlimbs; n++ ) {
+ if( a->d[n] ) {
+ unsigned nn;
+ mpi_limb_t alimb = a->d[n];
+
+ count_trailing_zeros( nn, alimb );
+ count += nn;
+ break;
+ }
+ count += BITS_PER_MPI_LIMB;
+ }
+ return count;
+
+}
+
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/mpiutil.c linux-2.6.10-hip/net/ipv6/hip/crypto/mpiutil.c
--- linux-2.6.10/net/ipv6/hip/crypto/mpiutil.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/mpiutil.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,394 @@
+/* mpiutil.ac - Utility functions for MPI
+ * Copyright (C) 1998, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+#include "kernel-interface.h"
+
+/****************
+ * Note: It was a bad idea to use the number of limbs to allocate
+ * because on a alpha the limbs are large but we normally need
+ * integers of n bits - So we should chnage this to bits (or bytes).
+ *
+ * But mpi_alloc is used in a lot of places :-)
+ */
+MPI
+_gcry_mpi_alloc( unsigned nlimbs )
+{
+ MPI a;
+
+ a = gcry_xmalloc( sizeof *a );
+ a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 0 ) : NULL;
+ a->alloced = nlimbs;
+ a->nlimbs = 0;
+ a->sign = 0;
+ a->flags = 0;
+ return a;
+}
+
+void
+_gcry_mpi_m_check( MPI a )
+{
+ _gcry_check_heap(a);
+ _gcry_check_heap(a->d);
+}
+
+MPI
+_gcry_mpi_alloc_secure( unsigned nlimbs )
+{
+ MPI a;
+
+ a = gcry_xmalloc( sizeof *a );
+ a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 1 ) : NULL;
+ a->alloced = nlimbs;
+ a->flags = 1;
+ a->nlimbs = 0;
+ a->sign = 0;
+ return a;
+}
+
+
+
+mpi_ptr_t
+_gcry_mpi_alloc_limb_space( unsigned nlimbs, int secure )
+{
+ size_t len = nlimbs * sizeof(mpi_limb_t);
+ mpi_ptr_t p;
+
+ p = secure? gcry_xmalloc_secure( len ) : gcry_xmalloc( len );
+
+ return p;
+}
+
+void
+_gcry_mpi_free_limb_space( mpi_ptr_t a )
+{
+ if( !a )
+ return;
+ gcry_free(a);
+}
+
+
+void
+_gcry_mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs )
+{
+ mpi_free_limb_space(a->d);
+ a->d = ap;
+ a->alloced = nlimbs;
+}
+
+
+
+/****************
+ * Resize the array of A to NLIMBS. the additional space is cleared
+ * (set to 0) [done by gcry_realloc()]
+ */
+void
+_gcry_mpi_resize( MPI a, unsigned nlimbs )
+{
+ if( nlimbs <= a->alloced )
+ return; /* no need to do it */
+ /* Note: a->secure is not used - instead the realloc functions
+ * take care of it. Maybe we should drop a->secure completely
+ * and rely on a mpi_is_secure function, which would be
+ * a wrapper around gcry_is_secure
+ */
+ if( a->d )
+ a->d = gcry_xrealloc(a->d, nlimbs * sizeof(mpi_limb_t) );
+ else /* FIXME: It may not be allocted in secure memory */
+ a->d = gcry_xcalloc( nlimbs , sizeof(mpi_limb_t) );
+ a->alloced = nlimbs;
+}
+
+void
+_gcry_mpi_clear( MPI a )
+{
+ a->nlimbs = 0;
+ a->flags = 0;
+}
+
+
+void
+_gcry_mpi_free( MPI a )
+{
+ if( !a )
+ return;
+ if( a->flags & 4 )
+ gcry_free( a->d );
+ else {
+ mpi_free_limb_space(a->d);
+ }
+ if( a->flags & ~7 )
+ log_bug("invalid flag value in mpi\n");
+ gcry_free(a);
+}
+
+static void
+mpi_set_secure( MPI a )
+{
+ mpi_ptr_t ap, bp;
+
+ if( (a->flags & 1) )
+ return;
+ a->flags |= 1;
+ ap = a->d;
+ if( !a->nlimbs ) {
+// assert(!ap);
+ return;
+ }
+ bp = mpi_alloc_limb_space( a->nlimbs, 1 );
+ MPN_COPY( bp, ap, a->nlimbs );
+ a->d = bp;
+ mpi_free_limb_space(ap);
+}
+
+
+MPI
+gcry_mpi_set_opaque( MPI a, void *p, unsigned int nbits )
+{
+ if( !a ) {
+ a = mpi_alloc(0);
+ }
+
+ if( a->flags & 4 )
+ gcry_free( a->d );
+ else {
+ mpi_free_limb_space(a->d);
+ }
+
+ a->d = p;
+ a->alloced = 0;
+ a->nlimbs = 0;
+ a->sign = nbits;
+ a->flags = 4;
+ return a;
+}
+
+
+void *
+gcry_mpi_get_opaque( MPI a, unsigned int *nbits )
+{
+ if( !(a->flags & 4) )
+ log_bug("mpi_get_opaque on normal mpi\n");
+ if( nbits )
+ *nbits = a->sign;
+ return a->d;
+}
+
+
+/****************
+ * Note: This copy function should not interpret the MPI
+ * but copy it transparently.
+ */
+MPI
+_gcry_mpi_copy( MPI a )
+{
+ int i;
+ MPI b;
+
+ if( a && (a->flags & 4) ) {
+ void *p = gcry_is_secure(a->d)? gcry_xmalloc_secure( (a->sign+7)/8 )
+ : gcry_xmalloc( (a->sign+7)/8 );
+ memcpy( p, a->d, (a->sign+7)/8 );
+ b = gcry_mpi_set_opaque( NULL, p, a->sign );
+ }
+ else if( a ) {
+ b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+ : mpi_alloc( a->nlimbs );
+ b->nlimbs = a->nlimbs;
+ b->sign = a->sign;
+ b->flags = a->flags;
+ for(i=0; i < b->nlimbs; i++ )
+ b->d[i] = a->d[i];
+ }
+ else
+ b = NULL;
+ return b;
+}
+
+
+/****************
+ * This function allocates an MPI which is optimized to hold
+ * a value as large as the one given in the argument and allocates it
+ * with the same flags as A.
+ */
+MPI
+_gcry_mpi_alloc_like( MPI a )
+{
+ MPI b;
+
+ if( a && (a->flags & 4) ) {
+ int n = (a->sign+7)/8;
+ void *p = gcry_is_secure(a->d)? gcry_malloc_secure( n )
+ : gcry_malloc( n );
+ memcpy( p, a->d, n );
+ b = gcry_mpi_set_opaque( NULL, p, a->sign );
+ }
+ else if( a ) {
+ b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+ : mpi_alloc( a->nlimbs );
+ b->nlimbs = 0;
+ b->sign = 0;
+ b->flags = a->flags;
+ }
+ else
+ b = NULL;
+ return b;
+}
+
+
+void
+_gcry_mpi_set( MPI w, MPI u)
+{
+ mpi_ptr_t wp, up;
+ mpi_size_t usize = u->nlimbs;
+ int usign = u->sign;
+
+ RESIZE_IF_NEEDED(w, usize);
+ wp = w->d;
+ up = u->d;
+ MPN_COPY( wp, up, usize );
+ w->nlimbs = usize;
+ w->flags = u->flags;
+ w->sign = usign;
+}
+
+
+void
+_gcry_mpi_set_ui( MPI w, unsigned long u)
+{
+ RESIZE_IF_NEEDED(w, 1);
+ w->d[0] = u;
+ w->nlimbs = u? 1:0;
+ w->sign = 0;
+ w->flags = 0;
+}
+
+
+MPI
+_gcry_mpi_alloc_set_ui( unsigned long u)
+{
+ MPI w = mpi_alloc(1);
+ w->d[0] = u;
+ w->nlimbs = u? 1:0;
+ w->sign = 0;
+ return w;
+}
+
+
+void
+_gcry_mpi_swap( MPI a, MPI b)
+{
+ struct gcry_mpi tmp;
+
+ tmp = *a; *a = *b; *b = tmp;
+}
+
+void
+gcry_mpi_swap( MPI a, MPI b)
+{
+ _gcry_mpi_swap (a, b);
+}
+
+
+GCRY_MPI
+gcry_mpi_new( unsigned int nbits )
+{
+ return _gcry_mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+
+GCRY_MPI
+gcry_mpi_snew( unsigned int nbits )
+{
+ return _gcry_mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+void
+gcry_mpi_release( GCRY_MPI a )
+{
+ _gcry_mpi_free( a );
+}
+
+GCRY_MPI
+gcry_mpi_copy( const GCRY_MPI a )
+{
+ return _gcry_mpi_copy( (GCRY_MPI)a );
+}
+
+GCRY_MPI
+gcry_mpi_set( GCRY_MPI w, const GCRY_MPI u )
+{
+ if( !w )
+ w = _gcry_mpi_alloc( mpi_get_nlimbs(u) );
+ _gcry_mpi_set( w, (GCRY_MPI)u );
+ return w;
+}
+
+GCRY_MPI
+gcry_mpi_set_ui( GCRY_MPI w, unsigned long u )
+{
+ if( !w )
+ w = _gcry_mpi_alloc(1);
+ _gcry_mpi_set_ui( w, u );
+ return w;
+}
+
+
+void
+gcry_mpi_randomize( GCRY_MPI w,
+ unsigned int nbits, enum gcry_random_level level )
+{
+ char *p = mpi_is_secure(w) ? gcry_random_bytes( (nbits+7)/8, level )
+ : gcry_random_bytes_secure( (nbits+7)/8, level );
+ _gcry_mpi_set_buffer( w, p, (nbits+7)/8, 0 );
+ gcry_free(p);
+}
+
+
+void
+gcry_mpi_set_flag( GCRY_MPI a, enum gcry_mpi_flag flag )
+{
+ switch( flag ) {
+ case GCRYMPI_FLAG_SECURE: mpi_set_secure(a); break;
+ case GCRYMPI_FLAG_OPAQUE:
+ default: log_bug("invalid flag value\n");
+ }
+}
+
+void
+gcry_mpi_clear_flag( GCRY_MPI a, enum gcry_mpi_flag flag )
+{
+ switch( flag ) {
+ case GCRYMPI_FLAG_SECURE:
+ case GCRYMPI_FLAG_OPAQUE:
+ default: log_bug("invalid flag value\n");
+ }
+}
+
+int
+gcry_mpi_get_flag( GCRY_MPI a, enum gcry_mpi_flag flag )
+{
+ switch( flag ) {
+ case GCRYMPI_FLAG_SECURE: return (a->flags & 1);
+ case GCRYMPI_FLAG_OPAQUE: return (a->flags & 4);
+ default: log_bug("invalid flag value\n");
+ }
+ return 0; /* keep compiler happy */
+}
+
diff -urN linux-2.6.10/net/ipv6/hip/crypto/rsa.c linux-2.6.10-hip/net/ipv6/hip/crypto/rsa.c
--- linux-2.6.10/net/ipv6/hip/crypto/rsa.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/rsa.c 2005-03-18 00:01:05.000000000 +0200
@@ -0,0 +1,507 @@
+/* rsa.c - RSA function
+ * Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn)
+ * Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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
+ */
+
+/* This code uses an algorithm protected by U.S. Patent #4,405,829
+ which expired on September 20, 2000. The patent holder placed that
+ patent into the public domain on Sep 6th, 2000.
+*/
+
+#include
+
+#include "kernel-interface.h"
+#include "gcrypt.h"
+#include "rsa.h"
+
+
+typedef struct {
+ MPI n; /* modulus */
+ MPI e; /* exponent */
+} RSA_public_key;
+
+
+typedef struct {
+ MPI n; /* public modulus */
+ MPI e; /* public exponent */
+ MPI d; /* exponent */
+ MPI p; /* prime p. */
+ MPI q; /* prime q. */
+ MPI u; /* inverse of p mod q. */
+} RSA_secret_key;
+
+
+static void public(MPI output, MPI input, RSA_public_key *skey );
+static void secret(MPI output, MPI input, RSA_secret_key *skey );
+
+
+/****************
+ * Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT.
+ *
+ * c = m^e mod n
+ *
+ * Where c is OUTPUT, m is INPUT and e,n are elements of PKEY.
+ */
+static void
+public(MPI output, MPI input, RSA_public_key *pkey )
+{
+ if( output == input ) { /* powm doesn't like output and input the same */
+ MPI x = mpi_alloc( mpi_get_nlimbs(input)*2 );
+ mpi_powm( x, input, pkey->e, pkey->n );
+ mpi_set(output, x);
+ mpi_free(x);
+ }
+ else
+ mpi_powm( output, input, pkey->e, pkey->n );
+}
+
+/****************
+ * Secret key operation. Encrypt INPUT with SKEY and put result into OUTPUT.
+ *
+ * m = c^d mod n
+ *
+ * Or faster:
+ *
+ * m1 = c ^ (d mod (p-1)) mod p
+ * m2 = c ^ (d mod (q-1)) mod q
+ * h = u * (m2 - m1) mod q
+ * m = m1 + h * p
+ *
+ * Where m is OUTPUT, c is INPUT and d,n,p,q,u are elements of SKEY.
+ */
+static void
+secret(MPI output, MPI input, RSA_secret_key *skey )
+{
+ #if 0
+ mpi_powm( output, input, skey->d, skey->n );
+ #else
+ MPI m1 = mpi_alloc_secure( mpi_get_nlimbs(skey->n)+1 );
+ MPI m2 = mpi_alloc_secure( mpi_get_nlimbs(skey->n)+1 );
+ MPI h = mpi_alloc_secure( mpi_get_nlimbs(skey->n)+1 );
+
+ /* m1 = c ^ (d mod (p-1)) mod p */
+ mpi_sub_ui( h, skey->p, 1 );
+ mpi_fdiv_r( h, skey->d, h );
+ mpi_powm( m1, input, h, skey->p );
+ /* m2 = c ^ (d mod (q-1)) mod q */
+ mpi_sub_ui( h, skey->q, 1 );
+ mpi_fdiv_r( h, skey->d, h );
+ mpi_powm( m2, input, h, skey->q );
+ /* h = u * ( m2 - m1 ) mod q */
+ mpi_sub( h, m2, m1 );
+ if ( mpi_is_neg( h ) )
+ mpi_add ( h, h, skey->q );
+ mpi_mulm( h, skey->u, h, skey->q );
+ /* m = m2 + h * p */
+ mpi_mul ( h, h, skey->p );
+ mpi_add ( output, m1, h );
+ /* ready */
+
+ mpi_free ( h );
+ mpi_free ( m1 );
+ mpi_free ( m2 );
+ #endif
+}
+
+
+
+/*********************************************
+ ************** interface ******************
+ *********************************************/
+
+int
+_gcry_rsa_encrypt( int algo, MPI *resarr, MPI data, MPI *pkey )
+{
+ RSA_public_key pk;
+
+ if( algo != 1 && algo != 2 )
+ return GCRYERR_INV_PK_ALGO;
+
+ pk.n = pkey[0];
+ pk.e = pkey[1];
+ resarr[0] = mpi_alloc( mpi_get_nlimbs( pk.n ) );
+ public( resarr[0], data, &pk );
+ return 0;
+}
+
+int
+_gcry_rsa_decrypt( int algo, MPI *result, MPI *data, MPI *skey )
+{
+ RSA_secret_key sk;
+
+ if( algo != 1 && algo != 2 )
+ return GCRYERR_INV_PK_ALGO;
+
+ sk.n = skey[0];
+ sk.e = skey[1];
+ sk.d = skey[2];
+ sk.p = skey[3];
+ sk.q = skey[4];
+ sk.u = skey[5];
+ *result = mpi_alloc_secure( mpi_get_nlimbs( sk.n ) );
+ secret( *result, data[0], &sk );
+ return 0;
+}
+
+int
+_gcry_rsa_sign( int algo, MPI *resarr, MPI data, MPI *skey )
+{
+ RSA_secret_key sk;
+
+ if( algo != 1 && algo != 3 )
+ return GCRYERR_INV_PK_ALGO;
+
+ sk.n = skey[0];
+ sk.e = skey[1];
+ sk.d = skey[2];
+ sk.p = skey[3];
+ sk.q = skey[4];
+ sk.u = skey[5];
+ resarr[0] = mpi_alloc( mpi_get_nlimbs( sk.n ) );
+ secret( resarr[0], data, &sk );
+
+ return 0;
+}
+
+int
+_gcry_rsa_verify( int algo, MPI hash, MPI *data, MPI *pkey,
+ int (*cmp)(void *opaque, MPI tmp), void *opaquev )
+{
+ RSA_public_key pk;
+ MPI result;
+ int rc;
+
+ if( algo != 1 && algo != 3 )
+ return GCRYERR_INV_PK_ALGO;
+ pk.n = pkey[0];
+ pk.e = pkey[1];
+ result = gcry_mpi_new ( 160 );
+ public( result, data[0], &pk );
+ /*rc = (*cmp)( opaquev, result );*/
+ rc = mpi_cmp( result, hash )? GCRYERR_BAD_SIGNATURE:0;
+ gcry_mpi_release (result);
+
+ return rc;
+}
+
+/****************
+ * Return some information about the algorithm. We need algo here to
+ * distinguish different flavors of the algorithm.
+ * Returns: A pointer to string describing the algorithm or NULL if
+ * the ALGO is invalid.
+ * Usage: Bit 0 set : allows signing
+ * 1 set : allows encryption
+ */
+const char *
+_gcry_rsa_get_info( int algo,
+ int *npkey, int *nskey, int *nenc, int *nsig, int *r_usage )
+{
+ *npkey = 2;
+ *nskey = 6;
+ *nenc = 1;
+ *nsig = 1;
+
+ switch( algo ) {
+ case 1: *r_usage = GCRY_PK_USAGE_SIGN | GCRY_PK_USAGE_ENCR; return "RSA";
+ case 2: *r_usage = GCRY_PK_USAGE_ENCR; return "RSA-E";
+ case 3: *r_usage = GCRY_PK_USAGE_SIGN; return "RSA-S";
+ default:*r_usage = 0; return NULL;
+ }
+}
+
+int hip_rsa_sign(u8 *digest, u8 *private_key, u8 *signature,
+ int priv_klen)
+{
+ RSA_secret_key rsk = {0};
+ MPI data = NULL, msig = NULL; /* tmp_mpi */
+ int err = 0, len, slice;
+ u8 *c = private_key;
+ u8 *buf = NULL;
+ u8 asn_prefix[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B,
+ 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04,
+ 0x14};
+ int keylen = 0;
+
+ _HIP_DEBUG("private key len: %d\n",priv_klen);
+ // if (*c == 0)
+ //len = 3;
+ //else
+ //len = 1;
+ HIP_ASSERT(*c!=0);
+ len = *c;
+
+ c++;
+
+ if (gcry_mpi_scan(&rsk.e, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA private e\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("mpi scan E", c, len);
+ c += len;
+
+ slice = (priv_klen - len) / 6;
+ _HIP_DEBUG("slice:%d\n",slice);
+ len = 2 * slice;
+ if (gcry_mpi_scan(&rsk.n, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA private n\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("mpi scan N", c, len);
+ c += len;
+
+ len = 2 * slice;
+ if (gcry_mpi_scan(&rsk.d, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA private d\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("mpi scan D", c, len);
+ c += len;
+
+ len = slice;
+ if (gcry_mpi_scan(&rsk.p, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA private p\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("mpi scan P", c, len);
+ c += len;
+
+ len = slice;
+ if (gcry_mpi_scan(&rsk.q, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA private q\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("mpi scan Q", c, len);
+ c += len;
+
+ keylen = (mpi_get_nbits(rsk.n) + 7) / 8;
+
+ rsk.u = gcry_mpi_new(mpi_get_nbits(rsk.d));
+ mpi_invm(rsk.u,rsk.p,rsk.q);
+
+ buf = kmalloc(keylen, GFP_KERNEL);
+ if (!buf) {
+ HIP_ERROR("\n");
+ goto cleanup;
+ }
+
+ /* shalen + asn prefix len + 01 00 */
+ len = HIP_AH_SHA_LEN + 15;// + 2;
+ // 128 - 20 - 3 = 105
+ slice = keylen - len -3;
+ _HIP_DEBUG("slice: %d, rsk.n %d\n", slice,
+ keylen);
+
+ c = buf;
+
+ *c = 1;
+ c++;
+
+ memset(c, 0xff, slice);
+ c += slice;
+
+ *c = 0;
+ c++;
+
+ memcpy(c, asn_prefix, 15);
+ c += 15;
+
+ memcpy(c, digest, HIP_AH_SHA_LEN);
+
+ c += HIP_AH_SHA_LEN;
+ *c = 0;
+
+ _HIP_HEXDUMP("digest", digest, HIP_AH_SHA_LEN);
+
+ len = keylen - 1;
+ if (gcry_mpi_scan(&data, GCRYMPI_FMT_USG, buf, &len) != 0) {
+ log_error("Error parsing signature data\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("Data to be signed", buf, len);
+
+ msig = mpi_alloc(keylen);
+ //mpi_get_nbits(rsk.n) / 8);
+
+ /* use the code in comments to check the signature */
+ //tmp_mpi = mpi_alloc(mpi_get_nbits(rsk.n) / 8);
+ secret(msig, data, &rsk);
+ //public(tmp_mpi, msig, (RSA_public_key *)&rsk);
+ //if (mpi_cmp( tmp_mpi, data))
+ // HIP_DEBUG("Signature is buggy!\n");
+
+ len = keylen; //(mpi_get_nbits(rsk.n) + 7) / 8;
+ /* 8 must not be 127, it should be always 128. This should be
+ handled better */
+ // len += 1; /* rsk.n */
+ if (gcry_mpi_print(GCRYMPI_FMT_USG, signature,
+ &len, msig) != 0) {
+ log_error("Error encoding RSA signature\n");
+ goto cleanup;
+ }
+ HIP_HEXDUMP("calculated signature", signature, len);
+
+ cleanup:
+ if(rsk.e)
+ mpi_free(rsk.e);
+ if(rsk.n)
+ mpi_free(rsk.n);
+ if(rsk.d)
+ mpi_free(rsk.d);
+ if(rsk.p)
+ mpi_free(rsk.p);
+ if(rsk.q)
+ mpi_free(rsk.q);
+ if(rsk.u)
+ mpi_free(rsk.u);
+ if(data)
+ mpi_free(data);
+ if (msig)
+ mpi_free(msig);
+ if (buf)
+ kfree(buf);
+ return err;
+
+}
+
+int hip_rsa_verify(u8 *digest, u8 *public_key, u8 *signature, int pub_klen)
+{
+ MPI result = NULL;
+ MPI data = NULL, orig = NULL;
+ RSA_public_key rpk = {0};
+ int len, slice, err = -1;
+ u8 *c = public_key;
+ u8 *buf = NULL;
+ u8 asn_prefix[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B,
+ 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04,
+ 0x14};
+ int keylen = 0;
+
+ HIP_DEBUG("public key len: %d\n",pub_klen);
+ //if (*c == 0)
+ //len = 3;
+ //else
+ //len = 1;
+ HIP_ASSERT(*c!=0);
+ len = *c;
+
+ c++;
+
+ if (gcry_mpi_scan(&rpk.e, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA public e\n");
+ goto cleanup;
+ }
+
+ _HIP_HEXDUMP("RSA public E", c, len);
+ c += len;
+
+ /* XX CHECK: should this be divided by 6 ?*/
+ /*slice = (pub_klen - len);*/
+ /* XX CHECK: should slice affect len ? */
+ //slice = (pub_klen - len) / 2; // XX CHANGE: MIIKA TEST
+ //len = 2 * slice;
+ len = pub_klen - len - 1;
+
+ if (gcry_mpi_scan(&rpk.n, GCRYMPI_FMT_USG, c, &len) != 0) {
+ log_error("Error parsing RSA public n\n");
+ goto cleanup;
+ }
+ _HIP_HEXDUMP("RSA public N", c, len);
+ _HIP_DEBUG("mpi_get_nbits:%d\n",mpi_get_nbits(rpk.n));
+
+ keylen = (mpi_get_nbits(rpk.n) / 8);
+
+ buf = kmalloc(keylen, GFP_KERNEL);
+ if (!buf) {
+ HIP_ERROR("kmalloc failed\n");
+ goto cleanup;
+ }
+
+ /* shalen + asn prefix len + 01 00 */
+ len = HIP_AH_SHA_LEN + 15;// + 2;
+ slice = keylen - len - 3;
+ //(mpi_get_nbits(rpk.n) + 7)/ 8 - len -2;
+ _HIP_DEBUG("slice:%d,mpi_get_nbits:%d\n",slice,mpi_get_nbits(rpk.n));
+
+ c = buf;
+
+ *c = 1;
+ c++;
+
+ memset(c, 0xff, slice);
+ c += slice;
+
+ *c = 0;
+ c++;
+
+ memcpy(c, asn_prefix, 15);
+ c += 15;
+
+ memcpy(c, digest, HIP_AH_SHA_LEN);
+
+ HIP_HEXDUMP("digest", digest, HIP_AH_SHA_LEN);
+
+ len = keylen - 1; //(mpi_get_nbits(rpk.n) + 7) / 8;
+ if (gcry_mpi_scan(&data, GCRYMPI_FMT_USG, buf, &len) != 0) {
+ log_error("Error parsing signature data\n");
+ goto cleanup;
+ }
+
+ _HIP_HEXDUMP("Data to be signed", buf, len);
+
+ result = mpi_alloc(keylen);
+ //mpi_get_nbits(rpk.n) / 8);
+ if (!result) {
+ log_error("mpi alloc rpk.n\n");
+ goto cleanup;
+ }
+
+ len = keylen; //(mpi_get_nbits(rpk.n) + 7) / 8;
+ if (gcry_mpi_scan(&orig, GCRYMPI_FMT_USG, signature, &len) != 0)
+ {
+ log_error("Error reading signature data\n");
+ goto cleanup;
+ }
+
+ _HIP_HEXDUMP("original signature", signature, len);
+ _HIP_HEXDUMP("orig.num", orig->d, len);
+
+ public(result, orig, &rpk);
+ _HIP_HEXDUMP("public result", result->d, len);
+ _HIP_HEXDUMP("data to be verified:", data->d, len);
+
+ err = mpi_cmp(data, result);
+
+ cleanup:
+ /* XX FIX: OTHER MPIs (data, orig, )?!? see the e.g. DSA functions */
+
+ if (result)
+ mpi_free(result);
+ if (rpk.e)
+ mpi_free(rpk.e);
+ if (rpk.n)
+ mpi_free(rpk.n);
+ if (data)
+ mpi_free(data);
+ if (orig)
+ mpi_free(orig);
+ if (buf)
+ kfree(buf);
+
+ return err;
+}
diff -urN linux-2.6.10/net/ipv6/hip/crypto/rsa.h linux-2.6.10-hip/net/ipv6/hip/crypto/rsa.h
--- linux-2.6.10/net/ipv6/hip/crypto/rsa.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/crypto/rsa.h 2005-03-03 00:01:38.000000000 +0200
@@ -0,0 +1,29 @@
+/* rsa.h
+ * Copyright (C) 1997,1998 by Werner Koch (dd9jn)
+ * Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser 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 G10_RSA_H
+#define G10_RSA_H
+
+int hip_rsa_sign(u8 *digest, u8 *private_key, u8 *signature,
+ int priv_klen);
+int hip_rsa_verify(u8 *digest, u8 *public_key, u8 *signature,
+ int pub_klen);
+
+#endif /*G10_RSA_H*/
diff -urN linux-2.6.10/net/ipv6/hip/db.c linux-2.6.10-hip/net/ipv6/hip/db.c
--- linux-2.6.10/net/ipv6/hip/db.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/db.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,946 @@
+/*
+ * HIP host id database and accessors.
+ *
+ * Authors: Janne Lundberg
+ * Miika Komu
+ * Mika Kousa
+ * Kristian Slavov
+ *
+ */
+
+#include
+
+#include "db.h"
+#include "misc.h"
+#include "builder.h"
+#include "socket.h"
+#include "output.h"
+#include "update.h"
+
+/*
+ * Do not access these databases directly: use the accessors in this file.
+ */
+
+HIP_INIT_DB(hip_peer_hostid_db, "peer_hid");
+HIP_INIT_DB(hip_local_hostid_db, "local_hid");
+HIP_INIT_DB(hip_local_eid_db, "local_eid");
+HIP_INIT_DB(hip_peer_eid_db, "peer_eid");
+
+/*
+ *
+ *
+ * Static functions follow. These functions _MUST_ only be used in conjunction
+ * with adequate locking. If the operation only fetches data, then READ lock is
+ * enough. All contexts except the hip thread _SHOULD_ use READ locks.
+ * The hip thread(s) is/are allowed to write to the databases. For this purpose
+ * it/they will acquire the WRITE lock.
+ *
+ *
+ */
+
+
+/**
+ * hip_uninit_hostid_db - uninitialize local/peer Host Id table
+ * @db: Database structure to delete.
+ *
+ * All elements of the @db are deleted. Since local and peer host id databases
+ * include dynamically allocated host_id element, it is also freed.
+ */
+void hip_uninit_hostid_db(struct hip_db_struct *db)
+{
+ struct list_head *curr, *iter;
+ struct hip_host_id_entry *tmp;
+ unsigned long lf;
+
+ HIP_WRITE_LOCK_DB(db);
+
+ list_for_each_safe(curr,iter,&db->db_head) {
+ tmp = list_entry(curr,struct hip_host_id_entry,next);
+ if (tmp->host_id)
+ kfree(tmp->host_id);
+ kfree(tmp);
+ }
+
+ HIP_WRITE_UNLOCK_DB(db);
+}
+
+/**
+ * hip_uninit_eid_db - uninitialize local/peer eid db
+ * @db: Database structure to delete.
+ *
+ * All elements of the @db are deleted.
+ */
+void hip_uninit_eid_db(struct hip_db_struct *db)
+{
+ struct list_head *curr, *iter;
+ struct hip_host_id_entry *tmp;
+ unsigned long lf;
+
+ HIP_WRITE_LOCK_DB(db);
+
+ list_for_each_safe(curr,iter,&db->db_head) {
+ tmp = list_entry(curr, struct hip_host_id_entry, next);
+ kfree(tmp);
+ }
+
+ HIP_WRITE_UNLOCK_DB(db);
+}
+
+void hip_uninit_all_eid_db(void)
+{
+ hip_uninit_eid_db(&hip_peer_eid_db);
+ hip_uninit_eid_db(&hip_local_eid_db);
+}
+
+
+/**
+ * hip_get_hostid_entry_by_lhi - finds the host id corresponding to the given @lhi
+ * @db: Database to be searched. Usually either %HIP_DB_PEER_HID or %HIP_DB_LOCAL_HID
+ * @lhi: the local host id to be searched
+ *
+ * If lhi is null, finds the first used host id.
+ *
+ * Returns: %NULL, if failed or non-NULL if succeeded.
+ */
+static
+struct hip_host_id_entry *hip_get_hostid_entry_by_lhi(struct hip_db_struct *db,
+ const struct hip_lhi *lhi)
+{
+ struct hip_host_id_entry *id_entry;
+
+ /* should (id->used == used) test be binaric? */
+
+ list_for_each_entry(id_entry,&db->db_head,next) {
+ if ((lhi == NULL || hip_lhi_are_equal(&id_entry->lhi, lhi)))
+ return id_entry;
+ }
+
+ return NULL;
+}
+
+/**
+ *
+ *
+ */
+static
+struct hip_host_id_entry *hip_get_hostid_entry_by_lhi_and_algo(struct hip_db_struct *db,
+ const struct hip_lhi *lhi,
+ int algo)
+{
+ struct hip_host_id_entry *id_entry;
+
+ list_for_each_entry(id_entry,&db->db_head,next) {
+ if ((lhi == NULL || hip_lhi_are_equal(&id_entry->lhi, lhi)) &&
+ (hip_get_host_id_algo(*(&id_entry->host_id))==algo))
+ return id_entry;
+ }
+ return NULL;
+}
+
+/*
+ *
+ *
+ * Interface functions to access databases.
+ *
+ *
+ *
+ */
+
+/***
+ * ARG/TYPE arguments in following functions.
+ *
+ * arg is used as a database key. It is _REQUIRED_ to be of type
+ * struct in6_addr *, _OR_ uint32. The first type is used IF AND ONLY IF,
+ * the type argument equals to HIP_ARG_HIT. For all other values of
+ * type, arg is assumed to be uint32 and the database is searched for
+ * a corresponding own_spi.
+ * In HIP_ARG_HIT case, the database is searched for corresponding
+ * hit_peer field.
+ ***
+ */
+
+/**
+ * hip_uninit_host_id_dbs - Delete both host id databases
+ *
+ */
+void hip_uninit_host_id_dbs(void)
+{
+ hip_uninit_hostid_db(&hip_local_hostid_db);
+ hip_uninit_hostid_db(&hip_peer_hostid_db);
+}
+
+
+/**
+ * hip_add_host_id - add the given HI into the database
+ * @db: Database structure
+ * @lhi: HIT
+ * @host_id: HI
+ *
+ * Checks for duplicates. If one is found, the current HI is _NOT_
+ * stored.
+ *
+ * On success returns 0, otherwise an negative error value is returned.
+ */
+int hip_add_host_id(struct hip_db_struct *db,
+ const struct hip_lhi *lhi,
+ const struct hip_host_id *host_id)
+{
+ int err = 0;
+ struct hip_host_id_entry *id_entry;
+ struct hip_host_id_entry *old_entry;
+ unsigned long lf;
+
+ _HIP_HEXDUMP("adding host id",lhi,sizeof(struct hip_lhi));
+
+ HIP_ASSERT(lhi != NULL);
+
+ id_entry = kmalloc(sizeof(id_entry),GFP_KERNEL);
+ if (id_entry == NULL) {
+ HIP_ERROR("No memory available for host id\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ id_entry->host_id = kmalloc(hip_get_param_total_len(host_id),
+ GFP_KERNEL);
+ if (!id_entry->host_id) {
+ HIP_ERROR("lhost_id mem alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* copy lhi and host_id (host_id is already in network byte order) */
+ id_entry->lhi.anonymous = lhi->anonymous;
+ ipv6_addr_copy(&id_entry->lhi.hit, &lhi->hit);
+ memcpy(id_entry->host_id, host_id, hip_get_param_total_len(host_id));
+
+ HIP_WRITE_LOCK_DB(db);
+
+ /* check for duplicates */
+ old_entry = hip_get_hostid_entry_by_lhi(db, lhi);
+ if (old_entry != NULL) {
+ HIP_WRITE_UNLOCK_DB(db);
+ HIP_ERROR("Trying to add duplicate lhi\n");
+ err = -EEXIST;
+ goto out_err;
+ }
+
+ list_add(&id_entry->next, &db->db_head);
+
+ HIP_WRITE_UNLOCK_DB(db);
+
+ return err;
+
+ out_err:
+ if (id_entry) {
+ if (id_entry->host_id)
+ kfree(id_entry->host_id);
+ kfree(id_entry);
+ }
+
+ return err;
+}
+
+/**
+ * hip_add_localhost_id - add a localhost id to the databases
+ * @lhi: the HIT of the host
+ * @host_id: the host id of the host
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_add_localhost_id(const struct hip_lhi *lhi,
+ const struct hip_host_id *host_id)
+{
+ return hip_add_host_id(&hip_local_hostid_db, lhi, host_id);
+}
+
+
+/**
+ * hip_del_host_id - delete the given HI (network byte order) from the database.
+ * @db: Database from which to delete
+ * @lhi: the HIT to be deleted from the database
+ *
+ * Matches HIs based on the HIT
+ *
+ * Returns: returns 0, otherwise returns negative.
+ */
+int hip_del_host_id(struct hip_db_struct *db, struct hip_lhi *lhi)
+{
+ int err = -ENOENT;
+ struct hip_host_id_entry *id = NULL;
+ unsigned long lf;
+
+ HIP_ASSERT(lhi != NULL);
+
+ HIP_WRITE_LOCK_DB(db);
+
+ id = hip_get_hostid_entry_by_lhi(db, lhi);
+ if (id == NULL) {
+ HIP_WRITE_UNLOCK_DB(db);
+ HIP_ERROR("lhi not found\n");
+ err = -ENOENT;
+ return err;
+ }
+
+ list_del(&id->next);
+
+ HIP_WRITE_UNLOCK_DB(db);
+
+ /* free the dynamically reserved memory and
+ set host_id to null to signal that it is free */
+ kfree(id->host_id);
+ kfree(id);
+ err = 0;
+ return err;
+}
+
+
+/**
+ * hip_copy_any_locahost_hit - Copy to the the @target the first
+ * local HIT that is found.
+ * @target: Placeholder for the target
+ *
+ * Returns 0 if ok, and negative if failed.
+ */
+int hip_copy_any_localhost_hit(struct in6_addr *target)
+{
+ struct hip_host_id_entry *entry;
+ int err = 0;
+ unsigned long lf;
+
+ HIP_READ_LOCK_DB(&hip_local_hostid_db);
+
+ entry = hip_get_hostid_entry_by_lhi(&hip_local_hostid_db,NULL);
+ if (!entry) {
+ err=-ENOENT;
+ goto out;
+ }
+
+ ipv6_addr_copy(target,&entry->lhi.hit);
+ err = 0;
+
+ out:
+ HIP_READ_UNLOCK_DB(&hip_local_hostid_db);
+ return err;
+}
+
+int hip_copy_any_localhost_hit_by_algo(struct in6_addr *target, int algo)
+{
+ struct hip_host_id_entry *entry;
+ int err = 0;
+ unsigned long lf;
+
+ HIP_READ_LOCK_DB(&hip_local_hostid_db);
+
+ entry = hip_get_hostid_entry_by_lhi_and_algo(&hip_local_hostid_db,NULL,algo);
+ if (!entry) {
+ err=-ENOENT;
+ goto out;
+ }
+
+ ipv6_addr_copy(target,&entry->lhi.hit);
+ err = 0;
+
+ out:
+ HIP_READ_UNLOCK_DB(&hip_local_hostid_db);
+ return err;
+}
+
+
+/**
+ * hip_copy_different_localhost_hit - Copy HIT that is not the same as the
+ * argument HIT.
+ * @target: Pointer to the area, where the differing HIT is copied.
+ * @source: Pointer to the HIT that is used as a reference.
+ *
+ * If unable to find differing HIT, -ENOENT is returned. Otherwise 0.
+ */
+int hip_copy_different_localhost_hit(struct in6_addr *target,
+ struct in6_addr *source)
+{
+ struct hip_host_id_entry *entry;
+ unsigned long lf;
+ int err = -ENOENT;
+
+ HIP_READ_LOCK_DB(&hip_local_hostid_db);
+
+ list_for_each_entry(entry,&hip_local_hostid_db.db_head,next) {
+ if (ipv6_addr_cmp(&entry->lhi.hit,source)) {
+ HIP_DEBUG("Found different\n");
+ ipv6_addr_copy(target,&entry->lhi.hit);
+ err = 0;
+ break;
+ }
+ }
+
+ HIP_READ_UNLOCK_DB(&hip_local_hostid_db);
+ return err;
+}
+
+/* Get a LHI from given DB @db */
+/* @res must be previously allocated */
+/* Returns: 0 if a lhi was copied successfully to @res, < 0 otherwise. */
+int hip_get_any_hit(struct hip_db_struct *db, struct hip_lhi *res,
+ uint8_t algo)
+{
+ struct hip_host_id_entry *entry;
+ unsigned long lf;
+
+ if (!res)
+ return -EINVAL;
+ if (list_empty(&db->db_head))
+ return -EINVAL;
+
+ HIP_READ_LOCK_DB(db);
+
+ list_for_each_entry(entry, db->db_head.next, next) {
+ if (hip_get_host_id_algo(entry->host_id) == algo) {
+ memcpy(res, &entry->lhi, sizeof(struct hip_lhi));
+ HIP_READ_UNLOCK_DB(db);
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+int hip_get_any_local_hit(struct in6_addr *dst, uint8_t algo)
+{
+ struct hip_lhi lhi;
+
+ if (!dst) {
+ HIP_ERROR("NULL dst\n");
+ return -EINVAL;
+ }
+ if (hip_get_any_hit(&hip_local_hostid_db, &lhi, algo) != 0) {
+ HIP_ERROR("Could not retrieve any local HIT\n");
+ return -ENOENT;
+ }
+ ipv6_addr_copy(dst, &lhi.hit);
+ return 0;
+}
+
+int hip_hit_is_our(struct in6_addr *hit)
+{
+ struct hip_host_id_entry *entry;
+ unsigned long lf;
+
+ if (!hit) {
+ HIP_ERROR("NULL hit\n");
+ return 0;
+ }
+
+ HIP_READ_LOCK_DB(&hip_local_hostid_db);
+ list_for_each_entry(entry, &hip_local_hostid_db.db_head, next) {
+ if (!ipv6_addr_cmp(&entry->lhi.hit, hit)) {
+ HIP_READ_UNLOCK_DB(&hip_local_hostid_db);
+ return 1;
+ }
+ }
+ HIP_READ_UNLOCK_DB(&hip_local_hostid_db);
+ return 0;
+}
+
+/**
+ * hip_get_host_id - Copies the host id into newly allocated memory
+ * and returns it to the caller.
+ * @db: Database
+ * @lhi: HIT that is used as a database search key
+ *
+ * NOTE: Remember to free the returned host id structure.
+ * This function should be only called by the HIP thread as it allocates
+ * GFP_KERNEL memory.
+ * XXX: The memory that is allocated is 1024 bytes. If the key is longer,
+ * we fail.
+ *
+ * Returns hip_host_id structure, or %NULL, if the entry was not found.
+ */
+struct hip_host_id *hip_get_host_id(struct hip_db_struct *db,
+ struct hip_lhi *lhi)
+{
+
+ struct hip_host_id_entry *tmp;
+ struct hip_host_id *result;
+ unsigned long lf;
+ int t;
+
+ result = kmalloc(1024, GFP_ATOMIC);
+ if (!result) {
+ HIP_ERROR("no memory\n");
+ return NULL;
+ }
+
+ memset(result, 0, 1024);
+
+ HIP_READ_LOCK_DB(db);
+
+ tmp = hip_get_hostid_entry_by_lhi(db, lhi);
+ if (!tmp) {
+ HIP_READ_UNLOCK_DB(db);
+ HIP_ERROR("No host id found\n");
+ return NULL;
+ }
+
+ t = hip_get_param_total_len(tmp->host_id);
+ if (t > 1024) {
+ HIP_READ_UNLOCK_DB(db);
+ kfree(result);
+ return NULL;
+ }
+
+ memcpy(result, tmp->host_id, t);
+
+ HIP_READ_UNLOCK_DB(db);
+
+ return result;
+}
+
+struct hip_host_id *hip_get_host_id_by_algo(struct hip_db_struct *db,
+ struct hip_lhi *lhi, int algo)
+{
+
+ struct hip_host_id_entry *tmp;
+ struct hip_host_id *result;
+ unsigned long lf;
+ int t;
+
+ result = kmalloc(1024, GFP_ATOMIC);
+ if (!result) {
+ HIP_ERROR("no memory\n");
+ return NULL;
+ }
+
+ memset(result, 0, 1024);
+
+ HIP_READ_LOCK_DB(db);
+
+ tmp = hip_get_hostid_entry_by_lhi_and_algo(db, lhi, algo);
+ if (!tmp) {
+ HIP_READ_UNLOCK_DB(db);
+ HIP_ERROR("No host id found\n");
+ return NULL;
+ }
+
+ t = hip_get_param_total_len(tmp->host_id);
+ if (t > 1024) {
+ HIP_READ_UNLOCK_DB(db);
+ kfree(result);
+ return NULL;
+ }
+
+ memcpy(result, tmp->host_id, t);
+
+ HIP_READ_UNLOCK_DB(db);
+
+ return result;
+}
+
+/**
+ * hip_get_any_localhost_host_id - Self documenting.
+ *
+ * NOTE: Remember to free the host id structure after use.
+ *
+ * Returns pointer to newly allocated area that contains a localhost
+ * HI. %NULL is returned is problems are encountered.
+ */
+struct hip_host_id *hip_get_any_localhost_host_id(int algo)
+{
+ struct hip_host_id *result;
+ /* XX TODO: use the algo */
+ result = hip_get_host_id_by_algo(&hip_local_hostid_db,NULL, algo);
+ return result;
+}
+
+
+/**
+ * hip_get_any_localhost_dsa_public_key - Self documenting.
+ *
+ * NOTE: Remember to free the return value.
+ *
+ * Returns newly allocated area that contains the public key part of
+ * the localhost host identity. %NULL is returned if errors detected.
+ */
+struct hip_host_id *hip_get_any_localhost_dsa_public_key(void)
+{
+ struct hip_host_id *tmp;
+ hip_tlv_len_t len;
+ uint16_t dilen;
+ char *from, *to;
+ u8 T;
+
+ /* T could easily have been an int, since the compiler will
+ probably add 3 alignment bytes here anyway. */
+
+ tmp = hip_get_host_id_by_algo(&hip_local_hostid_db,NULL,HIP_HI_DSA);
+ if (tmp == NULL) {
+ HIP_ERROR("No host id for localhost\n");
+ return NULL;
+ }
+
+ /* check T, Miika won't like this */
+ T = *((u8 *)(tmp + 1));
+ if (T > 8) {
+ HIP_ERROR("Invalid T-value in DSA key (0x%x)\n",T);
+ kfree(tmp);
+ return NULL;
+ }
+
+ if (T != 8) {
+ HIP_DEBUG("T-value in DSA-key not 8 (0x%x)!\n",T);
+ }
+
+ _HIP_HEXDUMP("HOSTID...",tmp, hip_get_param_total_len(tmp));
+ /* assuming all local keys are full DSA keys */
+ len = hip_get_param_contents_len(tmp);
+
+ _HIP_DEBUG("Host ID len before cut-off: %d\n",
+ hip_get_param_total_len(tmp));
+
+ /* the secret component of the DSA key is always 20 bytes */
+
+ tmp->hi_length = htons(ntohs(tmp->hi_length) - 20);
+
+ _HIP_DEBUG("hi->hi_length=%d\n", htons(tmp->hi_length));
+
+ /* Move the hostname 20 bytes earlier */
+
+ dilen = ntohs(tmp->di_type_length) & 0x0FFF;
+
+ to = ((char *)(tmp + 1)) - sizeof(struct hip_host_id_key_rdata) + ntohs(tmp->hi_length);
+ from = to + 20;
+ memmove(to, from, dilen);
+
+ hip_set_param_contents_len(tmp, (len - 20));
+
+ _HIP_DEBUG("Host ID len after cut-off: %d\n",
+ hip_get_param_total_len(tmp));
+
+ /* make sure that the padding is zero (and not to reveal any bytes of the
+ private key */
+ to = (char *)tmp + hip_get_param_contents_len(tmp) + sizeof(struct hip_tlv_common);
+ memset(to, 0, 8);
+
+ _HIP_HEXDUMP("HOSTID... (public)", tmp, hip_get_param_total_len(tmp));
+
+ return tmp;
+}
+
+
+/**
+ * hip_get_any_localhost_rsa_public_key - Self documenting.
+ *
+ * NOTE: Remember to free the return value.
+ *
+ * Returns newly allocated area that contains the public key part of
+ * the localhost host identity. %NULL is returned if errors detected.
+ */
+struct hip_host_id *hip_get_any_localhost_rsa_public_key(void)
+{
+ struct hip_host_id *tmp;
+ hip_tlv_len_t len;
+ uint16_t dilen;
+ char *from, *to;
+
+ tmp = hip_get_host_id_by_algo(&hip_local_hostid_db,NULL, HIP_HI_RSA);
+ if (tmp == NULL) {
+ HIP_ERROR("No host id for localhost\n");
+ return NULL;
+ }
+
+ /* XX TODO: check some value in the RSA key? */
+
+ _HIP_HEXDUMP("HOSTID...",tmp, hip_get_param_total_len(tmp));
+
+ len = hip_get_param_contents_len(tmp);
+
+ _HIP_DEBUG("Host ID len before cut-off: %d\n",
+ hip_get_param_total_len(tmp));
+
+ /* the secret component of the RSA key is always d+p+q bytes */
+ /* note: it's assumed that RSA key length is 1024 bits */
+
+ tmp->hi_length = htons(ntohs(tmp->hi_length) - (128+64+64));
+
+ _HIP_DEBUG("hi->hi_length=%d\n", htons(tmp->hi_length));
+
+ /* Move the hostname d+p+q bytes earlier */
+
+ dilen = ntohs(tmp->di_type_length) & 0x0FFF;
+
+ HIP_DEBUG("dilen: %d\n", dilen);
+
+ to = ((char *)(tmp + 1)) - sizeof(struct hip_host_id_key_rdata) + ntohs(tmp->hi_length);
+ from = to + (128+64+64); /* d, p, q*/
+ memmove(to, from, dilen);
+
+ hip_set_param_contents_len(tmp, (len - (128+64+64)));
+
+ HIP_DEBUG("Host ID len after cut-off: %d\n",
+ hip_get_param_total_len(tmp));
+
+ /* make sure that the padding is zero (and not to reveal any bytes of
+ the private key */
+ to = (char *)tmp + hip_get_param_contents_len(tmp) +
+ sizeof(struct hip_tlv_common);
+ memset(to, 0, 8);
+
+ _HIP_HEXDUMP("HOSTID... (public)", tmp, hip_get_param_total_len(tmp));
+
+ return tmp;
+
+}
+
+/**
+ * hip_get_any_localhost_public_key - Self documenting.
+ *
+ * NOTE: Remember to free the return value.
+ *
+ * Returns newly allocated area that contains the public key part of
+ * the localhost host identity. %NULL is returned if errors detected.
+ */
+struct hip_host_id *hip_get_any_localhost_public_key(int algo) {
+
+ struct hip_host_id *hi = NULL;
+
+ if(algo == HIP_HI_DSA) {
+ hi = hip_get_any_localhost_dsa_public_key();
+ } else if (algo == HIP_HI_RSA) {
+ hi = hip_get_any_localhost_rsa_public_key();
+ } else {
+ HIP_ERROR("unknown hi algo: (%d)",algo);
+ }
+ return hi;
+}
+
+
+/* PROC_FS FUNCTIONS */
+
+
+#ifdef CONFIG_PROC_FS
+
+/**
+ * hip_proc_read_lhi - debug function for dumping LHIs from procfs file /proc/net/hip/lhi
+ * @page: where dumped data is written to
+ * @start: ignored
+ * @off: ignored
+ * @count: how many bytes to read
+ * @eof: pointer where end of file flag is stored, always set to 1
+ * @data: ignored
+ *
+ * Returns: number of bytes written to @page.
+ */
+int hip_proc_read_lhi(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ /* XX: Called with sdb lock held ? */
+
+ int len = 0;
+ int i;
+ unsigned long lf = 0;
+ struct hip_host_id_entry *item;
+ char in6buf[INET6_ADDRSTRLEN];
+
+ _HIP_DEBUG("off=%d count=%d eof=%d\n", (int) off, count, *eof);
+
+
+ len += snprintf(page, count, "# used type algo HIT\n");
+ if (len >= count)
+ goto err;
+
+ HIP_READ_LOCK_DB(&hip_local_hostid_db);
+
+ i=0;
+ list_for_each_entry(item,&hip_local_hostid_db.db_head,next) {
+ hip_in6_ntop(&item->lhi.hit, in6buf);
+ len += snprintf(page+len, count-len, "%d %d %s %s %s\n",
+ ++i,
+ 1,
+ item->lhi.anonymous?"anon":"public",
+ hip_algorithm_to_string
+ (hip_get_host_id_algo(item->host_id)),
+ in6buf);
+ if (len >= count)
+ break;
+ }
+
+ HIP_READ_UNLOCK_DB(&hip_local_hostid_db);
+
+ if (len >= count) {
+ page[count-1] = '\0';
+ len = count;
+ } else {
+ page[len] = '\0';
+ }
+
+ err:
+ *eof = 1;
+ return(len);
+}
+
+int hip_proc_send_update(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ HIP_DEBUG("\n");
+ hip_send_update_all(NULL, 0, 0, 0);
+ *eof = 1;
+
+ return 0;
+}
+
+/* only during testing */
+int hip_proc_send_notify(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ hip_send_notify_all();
+ *eof = 1;
+ return 0;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+
+struct hip_eid_db_entry *hip_db_find_eid_entry_by_hit_no_lock(struct hip_db_struct *db,
+ const struct hip_lhi *lhi)
+{
+ struct hip_eid_db_entry *entry;
+
+ HIP_DEBUG("\n");
+
+ list_for_each_entry(entry, &db->db_head, next) {
+ /* XX TODO: Skip the anonymous bit. Is it ok? */
+ if (!ipv6_addr_cmp(&entry->lhi.hit,
+ (struct in6_addr *) &lhi->hit))
+ return entry;
+ }
+
+ return NULL;
+}
+
+struct hip_eid_db_entry *hip_db_find_eid_entry_by_eid_no_lock(struct hip_db_struct *db,
+ const struct sockaddr_eid *eid)
+{
+ struct hip_eid_db_entry *entry;
+
+ list_for_each_entry(entry, &db->db_head, next) {
+ HIP_DEBUG("comparing %d with %d\n",
+ ntohs(entry->eid.eid_val), ntohs(eid->eid_val));
+ if (entry->eid.eid_val == eid->eid_val)
+ return entry;
+ }
+
+ return NULL;
+}
+
+int hip_db_set_eid(struct sockaddr_eid *eid,
+ const struct hip_lhi *lhi,
+ const struct hip_eid_owner_info *owner_info,
+ int is_local)
+{
+ struct hip_db_struct *db;
+ int err = 0;
+ unsigned long lf;
+ struct hip_eid_db_entry *entry = NULL;
+
+ HIP_DEBUG("Accessing %s eid db\n", ((is_local) ? "local" : "peer"));
+
+ db = (is_local) ? &hip_local_eid_db : &hip_peer_eid_db;
+
+ HIP_WRITE_LOCK_DB(db);
+
+ entry = hip_db_find_eid_entry_by_hit_no_lock(db, lhi);
+ if (!entry) {
+ entry = kmalloc(sizeof(struct hip_eid_db_entry), GFP_KERNEL);
+ if (!entry) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ entry->eid.eid_val = ((is_local) ?
+ htons(hip_create_unique_local_eid()) :
+ htons(hip_create_unique_peer_eid()));
+ entry->eid.eid_family = PF_HIP;
+ memcpy(eid, &entry->eid, sizeof(struct sockaddr_eid));
+
+ HIP_DEBUG("Generated eid val %d\n", entry->eid.eid_val);
+
+ memcpy(&entry->lhi, lhi, sizeof(struct hip_lhi));
+ memcpy(&entry->owner_info, owner_info,
+ sizeof(struct hip_eid_owner_info));
+
+ /* Finished. Add the entry to the list. */
+ list_add(&entry->next, &db->db_head);
+ } else {
+ /* XX TODO: Ownership is not changed here; should it? */
+ memcpy(eid, &entry->eid, sizeof(struct sockaddr_eid));
+ }
+
+ out_err:
+ HIP_WRITE_UNLOCK_DB(db);
+
+ return err;
+}
+
+int hip_db_set_my_eid(struct sockaddr_eid *eid,
+ const struct hip_lhi *lhi,
+ const struct hip_eid_owner_info *owner_info)
+{
+ return hip_db_set_eid(eid, lhi, owner_info, 1);
+}
+
+int hip_db_set_peer_eid(struct sockaddr_eid *eid,
+ const struct hip_lhi *lhi,
+ const struct hip_eid_owner_info *owner_info)
+{
+ return hip_db_set_eid(eid, lhi, owner_info, 0);
+}
+
+int hip_db_get_lhi_by_eid(const struct sockaddr_eid *eid,
+ struct hip_lhi *lhi,
+ struct hip_eid_owner_info *owner_info,
+ int is_local)
+{
+ struct hip_db_struct *db;
+ int err = 0;
+ unsigned long lf;
+ struct hip_eid_db_entry *entry = NULL;
+
+ HIP_DEBUG("Accessing %s eid db\n", ((is_local) ? "local" : "peer"));
+
+ db = (is_local) ? &hip_local_eid_db : &hip_peer_eid_db;
+
+ HIP_READ_LOCK_DB(db);
+
+ entry = hip_db_find_eid_entry_by_eid_no_lock(db, eid);
+ if (!entry) {
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ memcpy(lhi, &entry->lhi, sizeof(struct hip_lhi));
+ memcpy(owner_info, &entry->owner_info,
+ sizeof(struct hip_eid_owner_info));
+
+ out_err:
+ HIP_READ_UNLOCK_DB(db);
+
+ return err;
+
+}
+
+int hip_db_get_peer_lhi_by_eid(const struct sockaddr_eid *eid,
+ struct hip_lhi *lhi,
+ struct hip_eid_owner_info *owner_info)
+{
+ return hip_db_get_lhi_by_eid(eid, lhi, owner_info, 0);
+}
+
+int hip_db_get_my_lhi_by_eid(const struct sockaddr_eid *eid,
+ struct hip_lhi *lhi,
+ struct hip_eid_owner_info *owner_info)
+{
+ return hip_db_get_lhi_by_eid(eid, lhi, owner_info, 1);
+}
+
+#undef HIP_READ_LOCK_DB
+#undef HIP_WRITE_LOCK_DB
+#undef HIP_READ_UNLOCK_DB
+#undef HIP_WRITE_UNLOCK_DB
diff -urN linux-2.6.10/net/ipv6/hip/db.h linux-2.6.10-hip/net/ipv6/hip/db.h
--- linux-2.6.10/net/ipv6/hip/db.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/db.h 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,131 @@
+#ifndef _HIP_DB
+#define _HIP_DB
+
+#include
+#include
+#include
+#include
+#include "hip.h"
+
+#define HIP_MAX_COOKIE_INFO 10
+/* for debugging with in6_ntop */
+#define INET6_ADDRSTRLEN 46
+
+/* should implement with another data structure. 2.6.x will provide
+ * ready code, so for now, the linked-list is fine.
+ */
+struct hip_db_struct {
+ struct list_head db_head;
+ rwlock_t db_lock;
+ char * db_name;
+ int db_cnt;
+};
+
+#define HIP_INIT_DB(name,id) \
+ struct hip_db_struct name = { LIST_HEAD_INIT(name.db_head), \
+ RW_LOCK_UNLOCKED, id, 0}
+
+
+/*
+ * Note: lhit->hit and hid are stored in network byte order.
+ */
+
+#define HIP_ARG_HIT 0x000001
+#define HIP_ARG_SPI 0x000002
+#define HIP_HADB_ACCESS_ARGS (HIP_ARG_HIT | HIP_ARG_SPI)
+
+#define HIP_DB_LOCAL_HID (&hip_local_hostid_db)
+#define HIP_DB_PEER_HID (&hip_peer_hostid_db)
+
+#define HIP_READ_LOCK_DB(db) do { \
+ KRISU_START_TIMER(KMM_SPINLOCK);\
+ read_lock_irqsave(&(db)->db_lock,lf); \
+ } while(0)
+
+#define HIP_WRITE_LOCK_DB(db) do { \
+ KRISU_START_TIMER(KMM_SPINLOCK);\
+ write_lock_irqsave(&(db)->db_lock,lf); \
+ } while(0)
+
+#define HIP_READ_UNLOCK_DB(db) do { \
+ KRISU_STOP_TIMER(KMM_SPINLOCK,"read lock "__FUNCTION__);\
+ read_unlock_irqrestore(&(db)->db_lock,lf); \
+ } while(0)
+
+#define HIP_WRITE_UNLOCK_DB(db) do { \
+ write_unlock_irqrestore(&(db)->db_lock,lf); \
+ } while(0)
+
+
+struct hip_entry_list {
+ struct list_head list;
+ struct in6_addr peer_hit;
+ /* These two _MUST_ be left untouched. Feel free to add more
+ * to the end */
+};
+
+struct hip_hadb_multi {
+ struct list_head m_head;
+ void * m_arg;
+ int m_type;
+};
+
+typedef struct hip_host_id HIP_HID;
+
+// host id functions
+int hip_get_any_local_hit(struct in6_addr *dst, uint8_t algo);
+
+int hip_add_host_id(struct hip_db_struct *db,const struct hip_lhi *lhi,
+ const struct hip_host_id *host_id);
+int hip_add_localhost_id(const struct hip_lhi *lhi,
+ const struct hip_host_id *host_id);
+int hip_add_peer_info(struct in6_addr *hit, struct in6_addr *addr);
+int hip_copy_any_localhost_hit(struct in6_addr *target);
+int hip_copy_any_localhost_hit_by_algo(struct in6_addr *target, int algo);
+HIP_HID *hip_get_any_localhost_host_id(int);
+int hip_insert_any_localhost_public_key(uint8_t *target);
+struct hip_host_id *hip_get_any_localhost_public_key(int);
+int hip_hit_is_our(struct in6_addr *hit);
+struct hip_host_id *hip_get_host_id(struct hip_db_struct *db,
+ struct hip_lhi *lhi);
+int hip_proc_read_hadb_peer_addrs(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int hip_proc_read_hadb_state(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int hip_proc_read_lhi(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+/* for update packet testing */
+int hip_proc_send_update(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+/* for notify packet testing */
+int hip_proc_send_notify(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+
+void hip_uninit_host_id_dbs(void);
+void hip_uninit_all_eid_db(void);
+int hip_db_set_eid(struct sockaddr_eid *eid,
+ const struct hip_lhi *lhi,
+ const struct hip_eid_owner_info *owner_info,
+ int is_local);
+int hip_db_set_my_eid(struct sockaddr_eid *eid,
+ const struct hip_lhi *lhi,
+ const struct hip_eid_owner_info *owner_info);
+int hip_db_set_peer_eid(struct sockaddr_eid *eid,
+ const struct hip_lhi *lhi,
+ const struct hip_eid_owner_info *owner_info);
+int hip_db_get_lhi_by_eid(const struct sockaddr_eid *eid,
+ struct hip_lhi *lhi,
+ struct hip_eid_owner_info *owner_info,
+ int is_local);
+int hip_db_get_peer_lhi_by_eid(const struct sockaddr_eid *eid,
+ struct hip_lhi *lhi,
+ struct hip_eid_owner_info *owner_info);
+int hip_db_get_my_lhi_by_eid(const struct sockaddr_eid *eid,
+ struct hip_lhi *lhi,
+ struct hip_eid_owner_info *owner_info);
+
+extern struct hip_db_struct hip_peer_hostid_db;
+extern struct hip_db_struct hip_local_hostid_db;
+
+#endif /* _HIP_DB */
diff -urN linux-2.6.10/net/ipv6/hip/debug.c linux-2.6.10-hip/net/ipv6/hip/debug.c
--- linux-2.6.10/net/ipv6/hip/debug.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/debug.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,123 @@
+/*
+ * HIP kernelspace debugging functions
+ *
+ * Licence: GNU/GPL
+ * Authors:
+ * - Miika Komu
+ * - Mika Kousa
+ */
+
+#include "debug.h"
+#include "misc.h"
+
+inline void hip_debug_skb(const struct ipv6hdr *hdr, const struct sk_buff *skb)
+{
+ struct ipv6hdr *ip6hdr;
+ char src[INET6_ADDRSTRLEN];
+ char dst[INET6_ADDRSTRLEN];
+ _HIP_DEBUG("hdr=%p skb=%p\n", hdr, skb);
+ if (hdr && skb) {
+ ip6hdr = skb->nh.ipv6h;
+ _HIP_DEBUG("ip6hdr=%p src %p dst %p skbdev %p\n",
+ ip6hdr, &ip6hdr->saddr, &ip6hdr->daddr,
+ skb->dev);
+ hip_in6_ntop(&ip6hdr->saddr, src);
+ hip_in6_ntop(&ip6hdr->daddr, dst);
+ HIP_DEBUG("pkt out: saddr %s daddr %s\n", src, dst);
+ if (skb->dev) {
+ HIP_DEBUG("pkt out: dev %s (ifindex %d)\n",
+ skb->dev->name, skb->dev->ifindex);
+ }
+ }
+}
+
+/**
+ * hip_print_hit - print a HIT
+ * @str: string to be printed before the HIT
+ * @hit: the HIT to be printed
+ */
+inline void hip_print_hit(const char *str, const struct in6_addr *hit)
+{
+ char dst[INET6_ADDRSTRLEN];
+
+ hip_in6_ntop(hit, dst);
+ HIP_DEBUG("%s: %s\n", str, dst);
+ return;
+}
+
+/**
+ * khexdump - hexdumper for HIP kernel module
+ * @tag: a start tag (a string ending in \0) that will be printed before
+ * the actual hexdump
+ * @data: the data to be hexdumped
+ * @len: the length of the data to hexdumped
+ *
+ * Hexdumps data starting from address @data of length @len.
+ */
+inline void hip_khexdump(const char *tag, const void *data, const int len)
+{
+ char *buf, *bufpos;
+ const void *datapos;
+ int buflen, i;
+ unsigned char c;
+
+ if (!data || len < 0) {
+ HIP_ERROR("NULL data or len < 0 (len=%d)\n", len);
+ return;
+ }
+
+ /* every hexdump line contains offset+": "+32 bytes of data (space every 4 bytes) */
+ buflen = 4+2+2*32+((32-1)/4)+1;
+ buf = kmalloc(buflen, GFP_ATOMIC);
+ if (!buf)
+ return;
+
+ HIP_DEBUG("%s: begin dump %d bytes from 0x%p\n", tag, len, data);
+ datapos = data;
+
+ i = 0;
+ while (i < len) {
+ int j;
+
+ bufpos = buf;
+ memset(buf, 0, buflen);
+ sprintf(bufpos, "%4d: ", i);
+ bufpos += 4+2;
+ for (j = 0; i < len && bufpos < buf+buflen-1;
+ j++, i++, bufpos += 2*sizeof(char)) {
+ c = (unsigned char)(*(((unsigned char *)data)+i));
+ if (j && !(j%4)) {
+ sprintf(bufpos, " ");
+ bufpos += sizeof(char);
+ }
+ sprintf(bufpos, "%02x", c);
+ }
+ printk(KERN_DEBUG "%s\n", buf);
+ }
+
+ HIP_DEBUG("end of dump (0x%p)\n", data+len);
+ kfree(buf);
+ return;
+}
+
+
+/**
+ * hip_state_str - get name for a state
+ * @state: state value
+ *
+ * Returns: state name as a string.
+ */
+inline const char *hip_state_str(unsigned int state)
+{
+ const char *str = "UNKNOWN";
+ static const char *states[] =
+ { "NONE", "UNASSOCIATED", "I1_SENT",
+ "I2_SENT", "R2_SENT", "ESTABLISHED", "REKEYING",
+ "FAILED" };
+ if (state <= ARRAY_SIZE(states))
+ str = states[state];
+ else
+ HIP_ERROR("invalid state %u\n", state);
+
+ return str;
+}
diff -urN linux-2.6.10/net/ipv6/hip/debug.h linux-2.6.10-hip/net/ipv6/hip/debug.h
--- linux-2.6.10/net/ipv6/hip/debug.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/debug.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,68 @@
+#ifndef HIP_KERNEL_DEBUG_H
+#define HIP_KERNEL_DEBUG_H
+
+#include
+#include
+//#include
+//#include
+
+/* for debugging with in6_ntop */
+#define INET6_ADDRSTRLEN 46
+
+/* Informational and error messages are always logged */
+#define HIP_INFO(fmt, args...) \
+ printk(KERN_DEBUG "%s: " fmt , __FUNCTION__ , ##args)
+#define HIP_ERROR(fmt, args...) \
+ printk(KERN_DEBUG "%s: ***ERROR***: " fmt , __FUNCTION__ , ##args)
+#define HIP_ASSERT(s) do {\
+ if (!(s)) { \
+ HIP_ERROR("assertion failed on line %d\n", __LINE__); \
+ BUG(); \
+ } \
+} while(0)
+
+/* Do not remove useful debug lines, just prefix them with an underscore */
+#define _HIP_INFO(fmt, args...)
+#define _HIP_DEBUG(fmt, args...)
+#define _HIP_ERROR(fmt, args...)
+#define _HIP_HEXDUMP(tag, data, len)
+
+#define _HIP_DUMP_MSG(msg)
+#define _HIP_ASSERT(s)
+#define _HIP_DEBUG_IN6ADDR(str, in6)
+#define _HIP_DEBUG_HIT(str, hit)
+#define _HIP_DEBUG_SKB(hdr, skb)
+
+/* Debugging messages are only printed in development code */
+#ifdef CONFIG_HIP_DEBUG
+
+# define HIP_DEBUG(fmt, args...) \
+ printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+# define HIP_HEXDUMP(tag, data, len) hip_khexdump(tag, data, len)
+# define HIP_DUMP_MSG(msg) { printk(KERN_DEBUG " %s dump:\n", __FUNCTION__); \
+ hip_dump_msg(msg); }
+# define HIP_DEBUG_SKB(hdr, skb) hip_debug_skb(hdr, skb)
+# define HIP_DEBUG_IN6ADDR(str, in6) hip_print_hit(str, in6)
+# define HIP_DEBUG_HIT(str, hit) hip_print_hit(str, hit)
+
+#else
+
+ #define HIP_DEBUG(fmt, args...) do { } while(0)
+ #define HIP_HEXDUMP(tag, data, len) do { } while(0)
+ #define HIP_DUMP_MSG(msg) do { } while(0)
+ #define HIP_DEBUG_SKB(hdr, skb) do { } while(0)
+ #define HIP_DEBUG_IN6ADDR(str, in6) do { } while(0)
+ #define HIP_DEBUG_HIT(str, hit) do { } while(0)
+
+#endif /* CONFIG_HIP_DEBUG */
+
+/* Forward declarations */
+
+extern void hip_khexdump(const char *tag, const void *data, const int len);
+extern void hip_print_hit(const char *str, const struct in6_addr *hit);
+extern void hip_debug_skb(const struct ipv6hdr *hdr,
+ const struct sk_buff *skb);
+extern const char *hip_state_str(unsigned int state);
+
+#endif /* HIP_KERNEL_DEBUG_H */
+
diff -urN linux-2.6.10/net/ipv6/hip/hadb.c linux-2.6.10-hip/net/ipv6/hip/hadb.c
--- linux-2.6.10/net/ipv6/hip/hadb.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/hadb.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,2139 @@
+#include "hadb.h"
+#include "debug.h"
+#include "misc.h"
+#include "db.h"
+#include "security.h"
+#include "hashtable.h"
+#include "builder.h"
+
+#include
+
+
+HIP_HASHTABLE hadb_hit;
+HIP_HASHTABLE hadb_spi_list;
+
+static struct list_head hadb_byhit[HIP_HADB_SIZE];
+static struct list_head hadb_byspi_list[HIP_HADB_SIZE];
+
+/*
+ Support for multiple inbound IPsec SAs:
+
+ We need a separate hashtable containing elements HIT and SPI, which
+ tells which HIT has the inbound SPI. When an ESP packet is received,
+ we first get the SPI from it and perform a lookup on the HIT-SPI
+ hashtable to get the mapping. Then we perform another lookup from
+ the HIT hashtable using the HIT we got from the previous
+ lookup. This way we get the HA beloning to the connection.
+
+ hs = HIT-SPI (struct hip_hit_spi)
+
+ (functions hip_ .. _hs)
+*/
+
+static int hip_hadb_match_spi(void *key_1, void *key_2)
+{
+ uint32_t spi1,spi2;
+
+ spi1 = (uint32_t)key_1;
+ spi2 = (uint32_t)key_2;
+ return (spi1 == spi2);
+}
+
+static void hip_hadb_hold_entry(void *entry)
+{
+ hip_ha_t *ha = (hip_ha_t *)entry;
+
+ if (!entry)
+ return;
+
+ atomic_inc(&ha->refcnt);
+ _HIP_DEBUG("HA: %p, refcnt incremented to: %d\n", ha, atomic_read(&ha->refcnt));
+}
+
+static void hip_hadb_put_entry(void *entry)
+{
+ hip_ha_t *ha = (hip_ha_t *)entry;
+
+ if (!entry)
+ return;
+
+ if (atomic_dec_and_test(&ha->refcnt)) {
+ HIP_DEBUG("HA: refcnt decremented to 0, deleting %p\n", ha);
+ hip_hadb_delete_state(ha);
+ HIP_DEBUG("HA: %p deleted\n", ha);
+ } else {
+ _HIP_DEBUG("HA: %p, refcnt decremented to: %d\n", ha, atomic_read(&ha->refcnt));
+ }
+}
+
+void hip_hadb_delete_hs(struct hip_hit_spi *hs)
+{
+ HIP_DEBUG("hs=0x%p SPI=0x%x\n", hs, hs->spi);
+ kfree(hs);
+}
+
+static void hip_hadb_put_hs(void *entry)
+{
+ struct hip_hit_spi *hs = (struct hip_hit_spi *) entry;
+ if (!hs)
+ return;
+
+ if (atomic_dec_and_test(&hs->refcnt)) {
+ HIP_DEBUG("HS: refcnt decremented to 0, deleting %p\n", hs);
+ hip_hadb_delete_hs(hs);
+ HIP_DEBUG("HS: %p deleted\n", hs);
+ } else {
+ _HIP_DEBUG("HS: %p, refcnt decremented to: %d\n", hs, atomic_read(&hs->refcnt));
+ }
+}
+
+static void hip_hadb_hold_hs(void *entry)
+{
+ struct hip_hit_spi *hs = (struct hip_hit_spi *) entry;
+ if (!hs)
+ return;
+
+ atomic_inc(&hs->refcnt);
+ _HIP_DEBUG("HS: %p, refcnt incremented to: %d\n", hs, atomic_read(&hs->refcnt));
+}
+
+
+void hip_hadb_remove_hs(uint32_t spi)
+{
+ struct hip_hit_spi *hs;
+
+ hs = (struct hip_hit_spi *) hip_ht_find(&hadb_spi_list, (void *)spi);
+ if (!hs) {
+ HIP_DEBUG("HS not found for SPI=0x%x\n", spi);
+ return;
+ }
+
+ HIP_LOCK_HS(hs);
+ HIP_DEBUG("hs=0x%p SPI=0x%x\n", hs, hs->spi);
+ hip_ht_delete(&hadb_spi_list, hs);
+ HIP_UNLOCK_HS(hs);
+ hip_hadb_put_hs(hs); /* verify that put_hs is safe after unlocking */
+}
+
+/* test */
+void hip_hadb_remove_hs2(struct hip_hit_spi *hs)
+{
+ if (!hs) {
+ HIP_ERROR("NULL HS\n");
+ return;
+ }
+
+ HIP_LOCK_HS(hs);
+ HIP_DEBUG("hs=0x%p SPI=0x%x\n", hs, hs->spi);
+ hip_ht_delete(&hadb_spi_list, hs);
+ HIP_ERROR("TODO: CALL HS_PUT ?\n");
+ HIP_UNLOCK_HS(hs);
+}
+
+static void *hip_hadb_get_key_hit(void *entry)
+{
+ return (void *)&(((hip_ha_t *)entry)->hit_peer);
+}
+
+static void *hip_hadb_get_key_spi_list(void *entry)
+{
+ return (void *)(((struct hip_hit_spi *)entry)->spi);
+}
+
+/**
+ * hip_hadb_rem_state_hit - Remove HA from HIT table
+ * @entry: HA
+ * HA must be locked.
+ */
+static inline void hip_hadb_rem_state_hit(void *entry)
+{
+ hip_ha_t *ha = (hip_ha_t *)entry;
+
+ ha->hastate &= ~HIP_HASTATE_HITOK;
+ hip_ht_delete(&hadb_hit, entry);
+}
+
+
+/*
+ **********************************************************************
+ * All the primitive functions up to this point are static, to force
+ * some information hiding. The construct functions can access these
+ * functions directly.
+ *
+ **********************************************************************
+ */
+
+
+/*********************** PRIMITIVES ***************************/
+
+/* find HA by inbound SPI */
+hip_ha_t *hip_hadb_find_byspi_list(u32 spi)
+{
+ struct hip_hit_spi *hs;
+ hip_hit_t hit;
+ hip_ha_t *ha;
+
+ hs = (struct hip_hit_spi *) hip_ht_find(&hadb_spi_list, (void *)spi);
+ if (!hs) {
+ HIP_DEBUG("HIT-SPI not found for SPI=0x%x\n", spi);
+ return NULL;
+ }
+
+ ipv6_addr_copy(&hit, &hs->hit);
+ hip_hadb_put_hs(hs);
+
+ ha = hip_hadb_find_byhit(&hit);
+ if (!ha) {
+ HIP_DEBUG("HA not found for SPI=0x%x\n", spi);
+ }
+
+ return ha;
+}
+
+hip_ha_t *hip_hadb_find_byhit(hip_hit_t *hit)
+{
+ return (hip_ha_t *)hip_ht_find(&hadb_hit, (void *)hit);
+}
+
+/**
+ * hip_hadb_remove_state_hit - Remove HA from HIT hash table.
+ * @ha: HA
+ */
+void hip_hadb_remove_state_hit(hip_ha_t *ha)
+{
+ HIP_LOCK_HA(ha);
+ if ((ha->hastate & HIP_HASTATE_HITOK) == HIP_HASTATE_HITOK) {
+ hip_hadb_rem_state_hit(ha);
+ }
+ HIP_UNLOCK_HA(ha);
+}
+
+
+/**
+ * hip_hadb_insert_state - Insert state to hash tables.
+ *
+ * *** TODO: SPI STUFF IS DEPRECATED ***
+ *
+ * Adds @ha to either SPI or HIT hash table, or _BOTH_.
+ * As a side effect updates the hastate of the @ha.
+ *
+ * Function can be called even if the HA is in either or
+ * both hash tables already.
+ *
+ * PRECONDITIONS: To add to the SPI hash table the @ha->spi_in
+ * must be non-zero. To add to the HIT hash table the @ha->hit_peer
+ * must be non-zero (tested with ipv6_addr_any).
+ *
+ * Returns the hastate of the HA:
+ * HIP_HASTATE_VALID = HA added to (or is in) both hash tables
+ * HIP_HASTATE_SPIOK = HA added to (or is in) SPI hash table
+ * HIP_HASTATE_HITOK = HA added to (or is in) HIT hash table
+ * HIP_HASTATE_INVALID = HA was not added, nor is in either of the hash tables.
+ */
+int hip_hadb_insert_state(hip_ha_t *ha)
+{
+ hip_hastate_t st;
+ hip_ha_t *tmp;
+
+ /* assume already locked ha */
+
+ HIP_ASSERT(!(ipv6_addr_any(&ha->hit_peer)));
+
+ st = ha->hastate;
+
+ if (!ipv6_addr_any(&ha->hit_peer) && !(st & HIP_HASTATE_HITOK)) {
+ tmp = hip_ht_find(&hadb_hit, (void *)&(ha->hit_peer));
+ if (!tmp) {
+ hip_ht_add(&hadb_hit, ha);
+ st |= HIP_HASTATE_HITOK;
+ } else {
+ hip_put_ha(tmp);
+ HIP_DEBUG("HIT already taken\n");
+ }
+ }
+
+ ha->hastate = st;
+ return st;
+}
+
+/*
+ * XXXXXX Returns: 0 if @spi was added to the inbound SPI list of the HA @ha, otherwise < 0.
+ */
+int hip_hadb_insert_state_spi_list(hip_ha_t *entry, uint32_t spi)
+{
+ int err = 0;
+ struct hip_hit_spi *tmp;
+ hip_hit_t hit;
+ struct hip_hit_spi *new_item;
+
+ /* assume already locked entry */
+
+ _HIP_DEBUG("SPI LIST HT_ADD HA=0x%p SPI=0x%x\n", entry, spi);
+ ipv6_addr_copy(&hit, &entry->hit_peer);
+
+ tmp = hip_ht_find(&hadb_spi_list, (void *)spi);
+ if (tmp) {
+ hip_hadb_put_hs(tmp);
+ HIP_ERROR("BUG, SPI already inserted\n");
+ err = -EEXIST;
+ goto out_err;
+ }
+
+ new_item = kmalloc(sizeof(struct hip_hit_spi), GFP_ATOMIC);
+ if (!new_item) {
+ HIP_ERROR("new_item kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ atomic_set(&new_item->refcnt, 0);
+ spin_lock_init(&new_item->lock);
+ new_item->spi = spi;
+ ipv6_addr_copy(&new_item->hit, &hit);
+
+ hip_ht_add(&hadb_spi_list, new_item);
+ _HIP_DEBUG("SPI 0x%x added to HT spi_list, HS=%p\n", spi, new_item);
+ _HIP_DEBUG("HS TABLE:\n");
+ //hip_hadb_dump_hs_ht();
+ out_err:
+ return err;
+}
+
+/**
+ * hip_hadb_delete_state - Delete HA state (and deallocate memory)
+ * @ha: HA
+ *
+ * Deletes all associates IPSEC SAs and frees the memory occupied
+ * by the HA state.
+ *
+ * ASSERT: The HA must be unlinked from the global hadb hash tables
+ * (SPI and HIT). This function should only be called when absolutely
+ * sure that nobody else has a reference to it.
+ */
+void hip_hadb_delete_state(hip_ha_t *ha)
+{
+ HIP_DEBUG("ha=0x%p\n", ha);
+
+ /* Delete SAs */
+ hip_hadb_delete_inbound_spis(ha);
+ hip_hadb_delete_outbound_spis(ha);
+ if (ha->dh_shared_key)
+ kfree(ha->dh_shared_key);
+ kfree(ha);
+}
+
+/**
+ * hip_hadb_create_state - Allocates and initializes a new HA structure
+ * @gfpmask - passed directly to kmalloc().
+ *
+ * Return NULL if memory allocation failed, otherwise the HA.
+ */
+hip_ha_t *hip_hadb_create_state(int gfpmask)
+{
+ hip_ha_t *entry = NULL;
+
+ entry = kmalloc(sizeof(struct hip_hadb_state), gfpmask);
+ if (!entry)
+ return NULL;
+
+ memset(entry, 0, sizeof(*entry));
+
+ INIT_LIST_HEAD(&entry->next_hit);
+ INIT_LIST_HEAD(&entry->spis_in);
+ INIT_LIST_HEAD(&entry->spis_out);
+
+ spin_lock_init(&entry->lock);
+ atomic_set(&entry->refcnt,0);
+
+ entry->state = HIP_STATE_UNASSOCIATED;
+ entry->hastate = HIP_HASTATE_INVALID;
+
+ return entry;
+}
+
+/**
+ * hip_hadb_remove_state - Removes the HA from either or both hash tables.
+ * @ha: HA
+ *
+ * The caller should have one reference (as he is calling us). That
+ * prevents deletion of HA structure from happening under spin locks.
+ */
+void hip_hadb_remove_state(hip_ha_t *ha)
+{
+ int r;
+
+ HIP_ASSERT(atomic_read(&ha->refcnt) >= 1);
+ HIP_LOCK_HA(ha);
+
+ r = atomic_read(&ha->refcnt);
+
+ if ((ha->hastate & HIP_HASTATE_HITOK) && !ipv6_addr_any(&ha->hit_peer))
+ hip_hadb_rem_state_hit(ha);
+
+ HIP_DEBUG("Removed HA: %p from HADB hash tables. " \
+ "References remaining before removing: %d\n",
+ ha, r);
+ HIP_UNLOCK_HA(ha);
+}
+
+/************** END OF PRIMITIVE FUNCTIONS **************/
+
+#if 1
+/* select the preferred address within the addresses of the given SPI */
+/* selected address is copied to @addr, it is is non-NULL */
+int hip_hadb_select_spi_addr(hip_ha_t *entry, struct hip_spi_out_item *spi_out, struct in6_addr *addr)
+{
+ int err = 0;
+ struct hip_peer_addr_list_item *s, *candidate = NULL;
+ struct timeval latest, dt;
+#ifdef CONFIG_HIP_DEBUG
+ char addrstr[INET6_ADDRSTRLEN];
+#endif
+
+ list_for_each_entry(s, &spi_out->peer_addr_list, list) {
+#ifdef CONFIG_HIP_DEBUG
+ hip_in6_ntop(&s->address, addrstr);
+ _HIP_DEBUG("%s modified_time=%ld.%06ld\n", addrstr,
+ s->modified_time.tv_sec, s->modified_time.tv_usec);
+#endif
+ if (s->address_state != PEER_ADDR_STATE_ACTIVE) {
+ HIP_DEBUG("skipping non-active address %s\n", addrstr);
+ continue;
+ }
+
+ if (candidate) {
+ int this_is_later;
+ this_is_later = hip_timeval_diff(&s->modified_time, &latest, &dt);
+ _HIP_DEBUG("latest=%ld.%06ld\n", latest.tv_sec, latest.tv_usec);
+ _HIP_DEBUG("dt=%ld.%06ld\n", dt.tv_sec, dt.tv_usec);
+ if (this_is_later) {
+ _HIP_DEBUG("is later, change\n");
+ memcpy(&latest, &s->modified_time, sizeof(struct timeval));
+ candidate = s;
+ }
+ } else {
+ candidate = s;
+ memcpy(&latest, &s->modified_time, sizeof(struct timeval));
+ }
+ }
+
+ if (!candidate) {
+ HIP_ERROR("did not find usable peer address\n");
+ HIP_DEBUG("todo: select from other SPIs ?\n");
+ /* todo: select other SPI as the default SPI out */
+ err = -ENOMSG;
+ } else {
+ ipv6_addr_copy(addr, &candidate->address);
+#ifdef CONFIG_HIP_DEBUG
+ hip_in6_ntop(addr, addrstr);
+ _HIP_DEBUG("select %s from if=0x%x\n", addrstr, s->interface_id);
+#endif
+ }
+
+ return err;
+}
+#endif
+
+/**
+ * hip_hadb_get_peer_addr - Get some of the peer's usable IPv6 address
+ * @entry: corresponding hadb entry of the peer
+ * @addr: where the selected IPv6 address of the peer is copied to
+ *
+ * Current destination address selection algorithm:
+ * 1. use preferred address of the HA, if any (should be set)
+ *
+ * 2. use preferred address of the default outbound SPI, if any
+ * (should be set, suspect bug if we get this far)
+ *
+ * 3. select among the active addresses of the default outbound SPI
+ * (select the address which was added/updated last)
+ *
+ * Returns: 0 if some of the addresses was copied successfully, else < 0.
+ */
+int hip_hadb_get_peer_addr(hip_ha_t *entry, struct in6_addr *addr)
+{
+ int err = 0;
+ struct hip_spi_out_item *spi_out;
+
+ /* assume already locked entry */
+
+ //hip_print_hit("entry def addr", &entry->preferred_address);
+ if (ipv6_addr_any(&entry->preferred_address)) {
+ /* possibly ongoing bex */
+ _HIP_DEBUG("no preferred address set\n");
+ } else {
+ ipv6_addr_copy(addr, &entry->preferred_address);
+ _HIP_DEBUG("found preferred address\n");
+ goto out;
+ }
+
+ if (entry->default_spi_out == 0) {
+ HIP_DEBUG("default SPI out is 0, try to use the bex address\n");
+ if (ipv6_addr_any(&entry->bex_address)) {
+ HIP_DEBUG("no bex address\n");
+ err = -EINVAL;
+ } else
+ ipv6_addr_copy(addr, &entry->bex_address);
+ goto out;
+ }
+
+ /* try to select a peer address among the ones belonging to
+ * the default outgoing SPI */
+ spi_out = hip_hadb_get_spi_list(entry, entry->default_spi_out);
+ if (!spi_out) {
+ HIP_ERROR("did not find SPI list for default_spi_out 0x%x\n",
+ entry->default_spi_out);
+ err = -EEXIST;
+ goto out;
+ }
+#if 1
+ /* not tested well yet */
+ if (!ipv6_addr_any(&spi_out->preferred_address)) {
+ HIP_DEBUG("TEST CODE\n");
+ HIP_DEBUG("found preferred address for SPI 0x%x\n",
+ spi_out->spi);
+ ipv6_addr_copy(addr, &spi_out->preferred_address);
+ goto out;
+ }
+ err = -EINVAL; /* usable address not found */
+ HIP_ERROR("Did not find an usable peer address\n");
+#endif
+ out:
+ return err;
+}
+
+/**
+ * hip_hadb_get_peer_addr_info - get infomation on the given peer IPv6 address
+ * @entry: corresponding hadb entry of the peer
+ * @addr: the IPv6 address for which the information is to be retrieved
+ * @spi: where the outbound SPI of @addr is copied to
+ * @lifetime: where the lifetime of @addr is copied to
+ * @modified_time: where the time when @addr was added or updated is copied to
+ *
+ * Returns: if @entry has the address @addr in its peer address list
+ * parameters @spi, @lifetime, and @modified_time are
+ * assigned if they are non-NULL and 1 is returned, else @interface_id
+ * and @lifetime are not assigned a value and 0 is returned.
+ */
+int hip_hadb_get_peer_addr_info(hip_ha_t *entry, struct in6_addr *addr,
+ uint32_t *spi, uint32_t *lifetime,
+ struct timeval *modified_time)
+{
+ struct hip_peer_addr_list_item *s;
+#ifdef CONFIG_HIP_DEBUG
+ char addrstr[INET6_ADDRSTRLEN];
+#endif
+ int i = 1;
+ struct hip_spi_out_item *spi_out, *tmp;
+
+ /* assumes already locked entry */
+
+ list_for_each_entry_safe(spi_out, tmp, &entry->spis_out, list) {
+ list_for_each_entry(s, &spi_out->peer_addr_list, list) {
+#ifdef CONFIG_HIP_DEBUG
+ hip_in6_ntop(&s->address, addrstr);
+ _HIP_DEBUG("address %d: %s, lifetime 0x%x (%u)\n",
+ i, addrstr, s->lifetime, s->lifetime);
+#endif
+ if (!ipv6_addr_cmp(&s->address, addr)) {
+ _HIP_DEBUG("found\n");
+ if (lifetime)
+ *lifetime = s->lifetime;
+ if (modified_time) {
+ modified_time->tv_sec = s->modified_time.tv_sec;
+ modified_time->tv_usec = s->modified_time.tv_usec;
+ }
+ if (spi)
+ *spi = spi_out->spi;
+ return 1;
+ }
+ i++;
+ }
+ }
+
+ _HIP_DEBUG("not found\n");
+ return 0;
+}
+
+/**
+ * hip_hadb_set_peer_address_info - set entry's peer address address lifetime
+ * @entry: corresponding hadb entry of the peer
+ * @addr: the IPv6 address for which the information is to be set
+ * @lifetime: address lifetime
+ *
+ * Set @entry's peer address @addr address lifetime to given
+ * values. If @lifetime is non-NULL @addr's lifetime is changed to
+ * value pointed to by @lifetime.
+ *
+ * Returns: if @entry has the address @addr in its peer address list
+ * parameters @interface_id and @lifetime were assigned and 1 is
+ * returned. Else no values were assigned and 0 is returned.
+ */
+int hip_hadb_set_peer_addr_info(hip_ha_t *entry, struct in6_addr *addr,
+ uint32_t *lifetime)
+{
+ /* XX: Called with sdb lock held ? */
+
+ /* add parameter uint32_t spi */
+
+ struct hip_peer_addr_list_item *s;
+ int i = 1;
+#ifdef CONFIG_HIP_DEBUG
+ char addrstr[INET6_ADDRSTRLEN];
+#endif
+ struct hip_spi_out_item *spi_out, *tmp;
+
+ HIP_ERROR("USELESS/DEPRECATED ?\n");
+
+ HIP_LOCK_HA(entry);
+
+ list_for_each_entry_safe(spi_out, tmp, &entry->spis_out, list) {
+ list_for_each_entry(s, &spi_out->peer_addr_list, list) {
+
+ /* todo: update s->modified_time ? if yes, when and where ?
+ * when SPI/lifetime is changed ?
+ */
+#ifdef CONFIG_HIP_DEBUG
+ hip_in6_ntop(&s->address, addrstr);
+ _HIP_DEBUG("address %d: %s, lifetime 0x%x (%u)\n",
+ i, addrstr, s->lifetime, s->lifetime);
+#endif
+ if (!ipv6_addr_cmp(&s->address, addr)) {
+ if (lifetime) {
+ HIP_DEBUG("updating lifetime 0x%x -> 0x%x\n",
+ s->lifetime, *lifetime);
+ s->lifetime = *lifetime;
+ }
+ HIP_UNLOCK_HA(entry);
+ return 1;
+ }
+ i++;
+ }
+ }
+ HIP_UNLOCK_HA(entry);
+
+ _HIP_DEBUG("not found\n");
+ return 0;
+}
+
+
+
+/**
+ * hip_hadb_add_peer_addr - add a new peer IPv6 address to the entry's list of peer addresses
+ * @entry: corresponding hadb entry of the peer
+ * @new_addr: IPv6 address to be added
+ * @spi: outbound SPI to which the @new_addr is related to
+ * @lifetime: address lifetime of the address
+ * @state: address state
+ *
+ * Returns: if @new_addr already exists, 0 is returned. If address was
+ * added successfully 0 is returned, else < 0.
+ *
+*/
+int hip_hadb_add_peer_addr(hip_ha_t *entry, struct in6_addr *new_addr,
+ uint32_t spi, uint32_t lifetime, int state)
+{
+ int err = 0;
+ struct hip_peer_addr_list_item *item;
+ char addrstr[INET6_ADDRSTRLEN];
+ uint32_t prev_spi;
+ struct hip_spi_out_item *spi_out, *tmp;
+ int found_spi_list = 0;
+
+ /* assumes already locked entry */
+
+ /* check if we are adding the peer's address during the base
+ * exchange */
+ if (spi == 0) {
+ HIP_DEBUG("SPI is 0, set address as the bex address\n");
+ if (!ipv6_addr_any(&entry->bex_address)) {
+ hip_in6_ntop(&entry->bex_address, addrstr);
+ HIP_DEBUG("warning, overwriting existing bex address %s\n",
+ addrstr);
+ }
+ ipv6_addr_copy(&entry->bex_address, new_addr);
+ goto out_err;
+ }
+
+ /* todo: replace following with hip_hadb_get_spi_list */
+ list_for_each_entry_safe(spi_out, tmp, &entry->spis_out, list) {
+ if (spi_out->spi == spi) {
+ found_spi_list = 1;
+ break;
+ }
+ }
+
+ if (!found_spi_list) {
+ HIP_ERROR("did not find SPI list for SPI 0x%x\n", spi);
+ err = -EEXIST;
+ goto out_err;
+ }
+
+#ifdef CONFIG_HIP_DEBUG
+ hip_in6_ntop(new_addr, addrstr);
+ HIP_DEBUG("new_addr %s, spi 0x%x, lifetime 0x%x (%u)\n",
+ addrstr, spi, lifetime, lifetime);
+#endif
+
+ err = hip_hadb_get_peer_addr_info(entry, new_addr, &prev_spi, NULL, NULL);
+ if (err) {
+ /* todo: validate previous vs. new interface id for
+ * the new_addr ? */
+ if (prev_spi != spi)
+ HIP_DEBUG("todo: SPI changed: prev=%u new=%u\n", prev_spi,
+ spi);
+
+ HIP_DEBUG("duplicate address not added (todo: update address lifetime ?)\n");
+ /* todo: update address lifetime ? */
+ err = 0;
+ goto out_err;
+ }
+
+ item = kmalloc(sizeof(struct hip_peer_addr_list_item), GFP_KERNEL);
+ if (!item) {
+ HIP_ERROR("item kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ _HIP_DEBUG("kmalloced item=0x%p\n", item);
+
+ item->lifetime = lifetime;
+ ipv6_addr_copy(&item->address, new_addr);
+ item->address_state = state;
+ do_gettimeofday(&item->modified_time);
+
+ list_add_tail(&item->list, &spi_out->peer_addr_list);
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_hadb_delete_peer_address_list_one - delete IPv6 address from the entry's list
+ * of peer addresses
+ * @entry: corresponding hadb entry of the peer
+ * @addr: IPv6 address to be deleted
+ */
+void hip_hadb_delete_peer_addrlist_one(hip_ha_t *entry, struct in6_addr *addr)
+{
+ struct hip_peer_addr_list_item *item, *tmp;
+ int i = 1;
+#ifdef CONFIG_HIP_DEBUG
+ char addrstr[INET6_ADDRSTRLEN];
+#endif
+ struct hip_spi_out_item *spi_out, *spi_tmp;
+
+ /* possibly deprecated function .. */
+
+ HIP_LOCK_HA(entry);
+
+ list_for_each_entry_safe(spi_out, spi_tmp, &entry->spis_out, list) {
+ list_for_each_entry_safe(item, tmp, &spi_out->peer_addr_list, list) {
+#ifdef CONFIG_HIP_DEBUG
+ hip_in6_ntop(&item->address, addrstr);
+ _HIP_DEBUG("%p: address %d: %s interface_id 0x%x (%u), lifetime 0x%x (%u)\n",
+ item, i, addrstr, item->interface_id, item->interface_id, item->lifetime, item->lifetime);
+#endif
+ if (!ipv6_addr_cmp(&item->address, addr)) {
+ _HIP_DEBUG("deleting address\n");
+ list_del(&item->list);
+ kfree(item);
+ /* if address is on more than one spi list then do not goto out */
+ goto out;
+ }
+ i++;
+ }
+ }
+ out:
+ HIP_UNLOCK_HA(entry);
+ return;
+}
+
+/**
+ * Currently deletes the whole entry...
+ */
+int hip_del_peer_info(struct in6_addr *hit, struct in6_addr *addr)
+{
+ hip_ha_t *ha;
+
+ ha = hip_hadb_find_byhit(hit);
+ if (!ha) {
+ return -ENOENT;
+ }
+
+ if (ipv6_addr_any(addr)) {
+ hip_delete_esp(ha);
+ hip_hadb_remove_state_hit(ha);
+ /* by now, if everything is according to plans, the refcnt should be 1 */
+ hip_put_ha(ha);
+ /* and now zero --> deleted*/
+ } else {
+ hip_hadb_delete_peer_addrlist_one(ha, addr);
+ hip_put_ha(ha);
+ }
+
+ return 0;
+}
+
+/* Practically called only by when adding a HIT-IP mapping before bex */
+int hip_hadb_add_peer_info(hip_hit_t *hit, struct in6_addr *addr)
+{
+ int err = 0;
+ hip_ha_t *entry;
+ char str[INET6_ADDRSTRLEN];
+
+ /* old comment ? note: can't lock here or else
+ * hip_sdb_add_peer_address will block
+ *
+ * unsigned long flags = 0;
+ * spin_lock_irqsave(&hip_sdb_lock, flags);
+ */
+
+ hip_in6_ntop(hit, str);
+ HIP_DEBUG("called: HIT %s\n", str);
+
+ entry = hip_hadb_find_byhit(hit);
+ if (!entry) {
+ entry = hip_hadb_create_state(GFP_KERNEL);
+ if (!entry) {
+ HIP_ERROR("Unable to create a new entry\n");
+ return -1;
+ }
+ _HIP_DEBUG("created a new sdb entry\n");
+ ipv6_addr_copy(&entry->hit_peer, hit);
+
+ /* XXX: This is wrong. As soon as we have native socket API, we
+ * should enter here the correct sender... (currently unknown).
+ */
+ if (hip_get_any_local_hit(&entry->hit_our,
+ HIP_HI_DEFAULT_ALGO) == 0)
+ _HIP_DEBUG_HIT("our hit seems to be", &entry->hit_our);
+ else
+ HIP_INFO("Could not assign local hit, continuing\n");
+ hip_hadb_insert_state(entry);
+ hip_hold_ha(entry); /* released at the end */
+ }
+
+ /* add initial HIT-IP mapping */
+ if (entry && entry->state == HIP_STATE_UNASSOCIATED) {
+ err = hip_hadb_add_peer_addr(entry, addr, 0, 0,
+ PEER_ADDR_STATE_ACTIVE);
+ if (err) {
+ HIP_ERROR("error while adding a new peer address\n");
+ err = -2;
+ goto out;
+ }
+ } else
+ HIP_DEBUG("Not adding HIT-IP mapping in state %s\n",
+ hip_state_str(entry->state));
+ out:
+ if (entry)
+ hip_put_ha(entry);
+ return err;
+}
+
+/* assume already locked entry */
+int hip_hadb_add_inbound_spi(hip_ha_t *entry, struct hip_spi_in_item *data)
+{
+ int err = 0;
+ struct hip_spi_in_item *item, *tmp;
+ uint32_t spi_in;
+
+ spi_in = data->spi;
+
+ /* assumes locked entry */
+ _HIP_DEBUG("SPI_in=0x%x\n", spi_in);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ if (item->spi == spi_in) {
+ HIP_DEBUG("not adding duplicate SPI 0x%x\n", spi_in);
+ goto out;
+ }
+ }
+
+ item = kmalloc(sizeof(struct hip_spi_in_item), GFP_ATOMIC);
+ if (!item) {
+ HIP_ERROR("item kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memcpy(item, data, sizeof(struct hip_spi_in_item));
+ item->timestamp = jiffies;
+ list_add(&item->list, &entry->spis_in);
+ HIP_DEBUG("added SPI 0x%x to the inbound SPI list\n", spi_in);
+ // hip_hold_ha(entry); ?
+
+ _HIP_DEBUG("inserting SPI to HIT-SPI hashtable\n");
+ err = hip_hadb_insert_state_spi_list(entry, spi_in);
+ if (err == -EEXIST)
+ err = 0;
+ out_err:
+ out:
+ return err;
+}
+
+/* assume already locked entry */
+int hip_hadb_add_outbound_spi(hip_ha_t *entry, struct hip_spi_out_item *data)
+{
+ int err = 0;
+ struct hip_spi_out_item *item, *tmp;
+ uint32_t spi_out;
+
+ /* assumes locked entry ? */
+
+ spi_out = data->spi;
+
+ _HIP_DEBUG("SPI_out=0x%x\n", spi_out);
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ if (item->spi == spi_out) {
+ HIP_DEBUG("not adding duplicate SPI 0x%x\n", spi_out);
+ goto out;
+ }
+ }
+
+ item = kmalloc(sizeof(struct hip_spi_out_item), GFP_ATOMIC);
+ if (!item) {
+ HIP_ERROR("item kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memcpy(item, data, sizeof(struct hip_spi_out_item));
+ INIT_LIST_HEAD(&item->peer_addr_list);
+ ipv6_addr_copy(&item->preferred_address, &in6addr_any);
+ list_add(&item->list, &entry->spis_out);
+ HIP_DEBUG("added SPI 0x%x to the outbound SPI list\n", spi_out);
+
+ out_err:
+ out:
+ return err;
+}
+
+/* assume already locked entry */
+int hip_hadb_add_spi(hip_ha_t *entry, int direction, void *data)
+{
+ int err = -EINVAL;
+
+ if (direction == HIP_SPI_DIRECTION_IN)
+ err = hip_hadb_add_inbound_spi(entry, (struct hip_spi_in_item *) data);
+ else if (direction == HIP_SPI_DIRECTION_OUT)
+ err = hip_hadb_add_outbound_spi(entry, (struct hip_spi_out_item *) data);
+ else
+ HIP_ERROR("bug, invalid direction %d\n", direction);
+
+ return err;
+}
+
+
+void hip_hadb_delete_inbound_spi(hip_ha_t *entry, uint32_t spi)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ /* assumes locked entry */
+
+ HIP_DEBUG("SPI=0x%x\n", spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ if (item->spi == spi) {
+ HIP_DEBUG("deleting SPI_in=0x%x SPI_in_new=0x%x from inbound list, item=0x%p\n",
+ item->spi, item->new_spi, item);
+ HIP_ERROR("remove SPI from HIT-SPI HT\n");
+ hip_hadb_remove_hs(item->spi);
+ hip_delete_sa(item->spi, &entry->hit_our);
+ hip_delete_sa(item->new_spi, &entry->hit_our);
+ list_del(&item->list);
+ kfree(item);
+ break;
+ }
+ }
+}
+
+/* delete all entry's inbound SAs */
+void hip_hadb_delete_inbound_spis(hip_ha_t *entry)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ /* assumes locked entry */
+ _HIP_DEBUG("entry=0x%p\n", entry);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ HIP_DEBUG("deleting SPI_in=0x%x SPI_in_new=0x%x from inbound list, item=0x%p\n",
+ item->spi, item->new_spi, item);
+ HIP_DEBUG("remove SPI from HIT-SPI HT\n");
+ hip_hadb_remove_hs(item->spi);
+ hip_delete_sa(item->spi, &entry->hit_our);
+ hip_delete_sa(item->new_spi, &entry->hit_our);
+ list_del(&item->list);
+ kfree(item);
+ }
+}
+
+void hip_hadb_delete_outbound_spi(hip_ha_t *entry, uint32_t spi)
+{
+ struct hip_spi_out_item *item, *tmp;
+
+ /* assumes locked entry */
+ HIP_DEBUG("entry=0x%p SPI=0x%x\n", entry, spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ if (item->spi == spi) {
+ struct hip_peer_addr_list_item *addr_item, *addr_tmp;
+
+ HIP_DEBUG("deleting SPI_out=0x%x SPI_out_new=0x%x from outbound list, item=0x%p\n",
+ item->spi, item->new_spi, item);
+ hip_delete_sa(item->spi, &entry->hit_peer);
+ hip_delete_sa(item->new_spi, &entry->hit_peer);
+ /* delete peer's addresses */
+ list_for_each_entry_safe(addr_item, addr_tmp, &item->peer_addr_list, list) {
+ list_del(&addr_item->list);
+ kfree(addr_item);
+ }
+ list_del(&item->list);
+ kfree(item);
+ }
+ }
+}
+
+
+/* delete all entry's outbound SAs */
+void hip_hadb_delete_outbound_spis(hip_ha_t *entry)
+{
+ struct hip_spi_out_item *spi_out, *spi_tmp;
+
+ /* assumes locked entry */
+
+ _HIP_DEBUG("entry=0x%p\n", entry);
+ list_for_each_entry_safe(spi_out, spi_tmp, &entry->spis_out, list) {
+ struct hip_peer_addr_list_item *addr_item, *addr_tmp;
+
+ HIP_DEBUG("deleting SPI_out=0x%x SPI_out_new=0x%x from outbound list, spi_out=0x%p\n",
+ spi_out->spi, spi_out->new_spi, spi_out);
+ hip_delete_sa(spi_out->spi, &entry->hit_peer);
+ hip_delete_sa(spi_out->new_spi, &entry->hit_peer);
+ /* delete peer's addresses */
+ list_for_each_entry_safe(addr_item, addr_tmp, &spi_out->peer_addr_list, list) {
+ list_del(&addr_item->list);
+ kfree(addr_item);
+ }
+ list_del(&spi_out->list);
+ kfree(spi_out);
+ }
+}
+
+
+/* Set the ifindex of given SPI */
+/* assumes locked HA */
+void hip_hadb_set_spi_ifindex(hip_ha_t *entry, uint32_t spi, int ifindex)
+{
+ struct hip_spi_in_item *item, *tmp;
+ /* assumes that inbound spi already exists in ha's spis_in */
+ HIP_DEBUG("SPI=0x%x ifindex=%d\n", spi, ifindex);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x\n", item->ifindex, item->spi);
+ if (item->spi == spi) {
+ HIP_DEBUG("found updated spi-ifindex mapping\n");
+ item->ifindex = ifindex;
+ return;
+ }
+ }
+ HIP_DEBUG("SPI not found, returning\n");
+}
+
+/* Get the ifindex of given SPI, returns 0 if SPI was not found */
+int hip_hadb_get_spi_ifindex(hip_ha_t *entry, uint32_t spi)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("spi=0x%x\n", spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x\n", item->ifindex, item->spi);
+ if (item->spi == spi || item->new_spi == spi) {
+ _HIP_DEBUG("found\n");
+ return item->ifindex;
+ }
+ }
+ HIP_DEBUG("ifindex not found for the SPI 0x%x\n", spi);
+ return 0;
+}
+
+/* Get the SPI of given ifindex, returns 0 if ifindex was not found */
+uint32_t hip_hadb_get_spi(hip_ha_t *entry, int ifindex)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ HIP_DEBUG("ifindex=%d\n", ifindex);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x\n", item->ifindex, item->spi);
+ if (item->ifindex == ifindex) {
+ HIP_DEBUG("found SPI 0x%x\n", item->spi);
+ return item->spi;
+ }
+ }
+ HIP_DEBUG("SPI not found for the ifindex\n");
+ return 0;
+}
+
+uint32_t hip_update_get_prev_spi_in(hip_ha_t *entry, uint32_t peer_update_id)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ HIP_DEBUG("peer_update_id=%u\n", peer_update_id);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x nes_spi_out=0x%x seq_id=%u\n",
+ item->ifindex, item->spi, item->nes_spi_out, item->seq_update_id);
+ if (item->seq_update_id == peer_update_id) {
+ HIP_DEBUG("found SPI 0x%x\n", item->spi);
+ return item->spi;
+ }
+ }
+ HIP_DEBUG("SPI not found\n");
+ return 0;
+}
+
+/* Get the SPI of the SA belonging to the interface through
+ which we received the UPDATE */
+/* also sets updating flag of SPI to 1 */
+uint32_t hip_get_spi_to_update_in_established(hip_ha_t *entry, struct in6_addr *dev_addr)
+{
+ struct hip_spi_in_item *item, *tmp;
+ int ifindex;
+
+ hip_print_hit("dst dev_addr", dev_addr);
+ ifindex = hip_ipv6_devaddr2ifindex(dev_addr);
+ HIP_DEBUG("ifindex of dst dev=%d\n", ifindex);
+ if (!ifindex)
+ return 0;
+
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x\n", item->ifindex, item->spi);
+ if (item->ifindex == ifindex) {
+ item->updating = 1;
+ return item->spi;
+ }
+ }
+
+ HIP_DEBUG("SPI not found for ifindex\n");
+ return 0;
+}
+
+void hip_set_spi_update_status(hip_ha_t *entry, uint32_t spi, int set)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ HIP_DEBUG("spi=0x%x set=%d\n", spi, set);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x updating=%d\n",
+ item->ifindex, item->spi, item->updating);
+ if (item->spi == spi) {
+ HIP_DEBUG("setting updating status to %d\n", set);
+ item->updating = set;
+ break;
+ }
+ }
+}
+
+void hip_update_clear_status(hip_ha_t *entry, uint32_t spi)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("spi=0x%x\n", spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi=0x%x\n", item->spi);
+ if (item->spi == spi) {
+ _HIP_DEBUG("clearing SPI status\n");
+ item->update_state_flags = 0;
+ memset(&item->stored_received_nes, 0, sizeof(struct hip_nes));
+ break;
+ }
+ }
+}
+
+/* spi_out is the SPI which was in the received NES Old SPI field */
+void hip_update_set_new_spi_in(hip_ha_t *entry, uint32_t spi, uint32_t new_spi,
+ uint32_t spi_out /* test */)
+{
+ struct hip_spi_in_item *item, *tmp;
+ _HIP_DEBUG("spi=0x%x new_spi=0x%x spi_out=0x%x\n", spi, new_spi, spi_out);
+
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi=0x%x new_spi=0x%x\n",
+ item->spi, item->new_spi);
+ if (item->spi == spi) {
+ HIP_DEBUG("setting new_spi\n");
+ if (!item->updating) {
+ HIP_ERROR("SA update not in progress, continuing anyway\n");
+ }
+ if ((item->spi != item->new_spi) && item->new_spi) {
+ HIP_ERROR("warning: previous new_spi is not zero: 0x%x\n",
+ item->new_spi);
+ }
+ item->new_spi = new_spi;
+ item->nes_spi_out = spi_out; /* maybe useless */
+ break;
+ }
+ }
+}
+
+/* just sets the new_spi field */
+void hip_update_set_new_spi_out(hip_ha_t *entry, uint32_t spi, uint32_t new_spi)
+{
+ struct hip_spi_out_item *item, *tmp;
+
+ _HIP_DEBUG("spi=0x%x new_spi=0x%x\n", spi, new_spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ _HIP_DEBUG("test item: spi=0x%x new_spi=0x%x\n",
+ item->spi, item->new_spi);
+ if (item->spi == spi) {
+ _HIP_DEBUG("setting new_spi\n");
+ if (item->new_spi) {
+ HIP_ERROR("previous new_spi is not zero: 0x%x\n", item->new_spi);
+ HIP_ERROR("todo: delete previous new_spi\n");
+ }
+ item->new_spi = new_spi;
+ break;
+ }
+ }
+}
+
+
+uint32_t hip_update_get_new_spi_in(hip_ha_t *entry, uint32_t peer_update_id)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("peer_update_id=%u\n", peer_update_id);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi=0x%x new_spi=0x%x\n",
+ item->spi, item->new_spi);
+ if (item->seq_update_id == peer_update_id) {
+ return item->new_spi;
+
+ }
+ }
+ HIP_DEBUG("New SPI not found\n");
+ return 0;
+}
+
+/* switch from Old SPI to New SPI (inbound SA) */
+/* caller must delete the Old SPI */
+void hip_update_switch_spi_in(hip_ha_t *entry, uint32_t old_spi)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("old_spi=0x%x\n", old_spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: ifindex=%d spi=0x%x new_spi=0x%x nes_spi_out=0x%x seq_id=%u\n",
+ item->ifindex, item->spi, item->new_spi,
+ item->nes_spi_out, item->seq_update_id);
+ if (item->spi == old_spi) {
+ _HIP_DEBUG("switching\n");
+ item->spi = item->new_spi;
+ item->new_spi = 0;
+ item->nes_spi_out = 0;
+ break;
+ }
+ }
+ _HIP_DEBUG("returning\n");
+}
+
+/* switch from Old SPI to New SPI (outbound SA) */
+/* caller must delete the Old SPI */
+void hip_update_switch_spi_out(hip_ha_t *entry, uint32_t old_spi)
+{
+ struct hip_spi_out_item *item, *tmp;
+
+ _HIP_DEBUG("old_spi=0x%x\n", old_spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ _HIP_DEBUG("test item: spi=0x%x new_spi=0x%x seq_id=%u\n",
+ item->spi, item->new_spi, item->seq_update_id);
+ if (item->spi == old_spi) {
+ _HIP_DEBUG("switching\n");
+ item->spi = item->new_spi;
+ item->new_spi = 0;
+ break;
+ }
+ }
+ _HIP_DEBUG("returning\n");
+}
+
+
+void hip_update_set_status(hip_ha_t *entry, uint32_t spi, int set_flags,
+ uint32_t update_id, int update_flags_or,
+ struct hip_nes *nes, uint16_t keymat_index)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("spi=0x%x update_id=%u update_flags_or=0x%x keymat_index=%u nes=0x%p\n",
+ spi, update_id, update_flags_or, keymat_index, nes);
+ if (nes)
+ _HIP_DEBUG("NES: old_spi=0x%x new_spi=0x%x keymat_index=%u\n",
+ ntohl(nes->old_spi), ntohl(nes->new_spi), ntohs(nes->keymat_index));
+
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi_in=0x%x new_spi=0x%x\n",
+ item->spi, item->new_spi);
+ if (item->spi == spi) {
+ _HIP_DEBUG("setting new values\n");
+ if (set_flags & 0x1)
+ item->seq_update_id = update_id;
+ if (set_flags & 0x2)
+ item->update_state_flags |= update_flags_or;
+ if (nes && (set_flags & 0x4)) {
+ item->stored_received_nes.old_spi = ntohl(nes->old_spi);
+ item->stored_received_nes.new_spi = ntohl(nes->new_spi);
+ item->stored_received_nes.keymat_index = ntohs(nes->keymat_index);
+ }
+ if (set_flags & 0x8)
+ item->keymat_index = keymat_index;
+
+ return;
+ }
+ }
+ HIP_ERROR("SPI not found\n");
+}
+
+
+/* returns 1 if given SPI belongs to the SA having direction
+ * @direction, else 0. If @test_new_spi is 1 then test new_spi instead
+ * of spi */
+int hip_update_exists_spi(hip_ha_t *entry, uint32_t spi,
+ int direction, int test_new_spi)
+{
+ /* assumes locked entry */
+
+ _HIP_DEBUG("spi=0x%x direction=%d test_new_spi=%d\n",
+ spi, direction, test_new_spi);
+
+ if (direction == HIP_SPI_DIRECTION_IN) {
+ struct hip_spi_in_item *item, *tmp;
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi_in=0x%x new_spi=0x%x\n",
+ item->spi, item->new_spi);
+ if ( (item->spi == spi && !test_new_spi) ||
+ (item->new_spi == spi && test_new_spi) )
+ return 1;
+ }
+ } else {
+ struct hip_spi_out_item *item, *tmp;
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ _HIP_DEBUG("test item: spi_out=0x%x new_spi=0x%x\n",
+ item->spi, item->new_spi);
+ if ( (item->spi == spi && !test_new_spi) ||
+ (item->new_spi == spi && test_new_spi) )
+ return 1;
+ }
+ }
+ HIP_DEBUG("not found\n");
+ return 0;
+}
+
+/* Get an usable outbound SPI, SPI must contain ACTIVE addresses */
+/* todo: return void instead of spi */
+
+/* returns the new default outbound SPI is succesful, or 0 if no
+ * usable address was found */
+uint32_t hip_hadb_relookup_default_out(hip_ha_t *entry)
+{
+ uint32_t spi = 0;
+ struct hip_spi_out_item *spi_out, *spi_out_tmp;
+
+ /* assumes locked entry */
+
+ HIP_DEBUG("\n");
+ /* latest outbound SPIs are usually in the beginning of the list */
+ list_for_each_entry_safe(spi_out, spi_out_tmp, &entry->spis_out, list) {
+ int ret;
+ struct in6_addr addr;
+
+ _HIP_DEBUG("checking SPI 0x%x\n", spi_out->spi);
+ ret = hip_hadb_select_spi_addr(entry, spi_out, &addr);
+ if (ret == 0) {
+ hip_hadb_set_default_out_addr(entry, spi_out, &addr);
+ spi = spi_out->spi;
+ goto out;
+ }
+ }
+
+ if (spi)
+ HIP_DEBUG("Set SPI 0x%x as the default outbound SPI\n", spi);
+ else
+ HIP_DEBUG("Did not find an usable outbound SPI\n");
+ out:
+ return spi;
+}
+
+/* if add is non-NULL, set addr as the default address for both
+ * entry's default address and outbound SPI list's default address*/
+
+/* if addr is null, select some address from the SPI list */
+void hip_hadb_set_default_out_addr(hip_ha_t *entry, struct hip_spi_out_item *spi_out,
+ struct in6_addr *addr)
+{
+ if (!spi_out) {
+ HIP_ERROR("NULL spi_out\n");
+ return;
+ }
+
+ if (addr) {
+ HIP_DEBUG("testing, setting given address as default out addr\n");
+ ipv6_addr_copy(&spi_out->preferred_address, addr);
+ ipv6_addr_copy(&entry->preferred_address, addr);
+ } else {
+ /* useless ? */
+ struct in6_addr a;
+ int err = hip_hadb_select_spi_addr(entry, spi_out, &a);
+ _HIP_DEBUG("setting address as default out addr\n");
+ if (!err) {
+ ipv6_addr_copy(&spi_out->preferred_address, &a);
+ ipv6_addr_copy(&entry->preferred_address, &a);
+ } else
+ HIP_ERROR("couldn't select and set preferred address\n");
+ }
+ HIP_DEBUG("setting default SPI out to 0x%x\n", spi_out->spi);
+ entry->default_spi_out = spi_out->spi;
+}
+
+/* have_nes is 1, if there is NES in the same packet as the ACK was */
+void hip_update_handle_ack(hip_ha_t *entry, struct hip_ack *ack, int have_nes,
+ struct hip_echo_response *echo_resp)
+{
+ size_t n, i;
+ uint32_t *peer_update_id;
+
+ /* assumes locked entry */
+
+ HIP_DEBUG("have_nes=%d\n", have_nes);
+
+ if (!ack) {
+ HIP_ERROR("NULL ack\n");
+ goto out_err;
+ }
+
+ if (hip_get_param_contents_len(ack) % sizeof(uint32_t)) {
+ HIP_ERROR("ACK param length not divisible by 4 (%u)\n",
+ hip_get_param_contents_len(ack));
+ goto out_err;
+ }
+
+ n = hip_get_param_contents_len(ack) / sizeof(uint32_t);
+ HIP_DEBUG("%d pUIDs in ACK param\n", n);
+ peer_update_id = (uint32_t *) ((void *)ack+sizeof(struct hip_tlv_common));
+ for (i = 0; i < n; i++, peer_update_id++) {
+ struct hip_spi_in_item *in_item, *in_tmp;
+ struct hip_spi_out_item *out_item, *out_tmp;
+ uint32_t puid = ntohl(*peer_update_id);
+
+ _HIP_DEBUG("peer Update ID=%u\n", puid);
+
+ /* see if your NES is acked and maybe if corresponging NES was received */
+ list_for_each_entry_safe(in_item, in_tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi_in=0x%x seq=%u\n",
+ in_item->spi, in_item->seq_update_id);
+ if (in_item->seq_update_id == puid) {
+ _HIP_DEBUG("SEQ and ACK match\n");
+ in_item->update_state_flags |= 0x1; /* recv'd ACK */
+ if (have_nes)
+ in_item->update_state_flags |= 0x2; /* recv'd also NES */
+ }
+ }
+
+ /* see if the ACK was response to address verification */
+ if (echo_resp) {
+ list_for_each_entry_safe(out_item, out_tmp, &entry->spis_out, list) {
+ struct hip_peer_addr_list_item *addr, *addr_tmp;
+
+ list_for_each_entry_safe(addr, addr_tmp, &out_item->peer_addr_list, list) {
+ _HIP_DEBUG("checking address, seq=%u\n", addr->seq_update_id);
+ if (addr->seq_update_id == puid) {
+ if (hip_get_param_contents_len(echo_resp) != sizeof(addr->echo_data)) {
+ HIP_ERROR("echo data len mismatch\n");
+ continue;
+ }
+ if (memcmp(addr->echo_data,
+ (void *)echo_resp+sizeof(struct hip_tlv_common),
+ sizeof(addr->echo_data)) != 0) {
+ HIP_ERROR("ECHO_RESPONSE differs from ECHO_REQUEST\n");
+ continue;
+ }
+ _HIP_DEBUG("address verified successfully, setting state to ACTIVE\n");
+ addr->address_state = PEER_ADDR_STATE_ACTIVE;
+ do_gettimeofday(&addr->modified_time);
+
+ if (addr->is_preferred) {
+ /* maybe we should do this default address selection
+ after handling the REA .. */
+ hip_hadb_set_default_out_addr(entry, out_item, &addr->address);
+ } else
+ HIP_DEBUG("address was not set as preferred address in REA\n");
+ }
+ }
+ }
+ entry->skbtest = 1;
+ _HIP_DEBUG("set skbtest to 1\n");
+ } else {
+ HIP_DEBUG("no ECHO_RESPONSE in same packet with ACK\n");
+ }
+ }
+ out_err:
+ return;
+}
+
+void hip_update_handle_nes(hip_ha_t *entry, uint32_t peer_update_id)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("peer_update_id=%u\n", peer_update_id);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi_in=0x%x seq=%u\n",
+ item->spi, item->seq_update_id);
+ if (item->seq_update_id == peer_update_id) {
+ _HIP_DEBUG("received peer's NES\n");
+ item->update_state_flags |= 0x2; /* recv'd NES */
+ }
+ }
+}
+
+/* works if update contains only one NES */
+int hip_update_get_spi_keymat_index(hip_ha_t *entry, uint32_t peer_update_id)
+{
+ struct hip_spi_in_item *item, *tmp;
+
+ _HIP_DEBUG("peer_update_id=%u\n", peer_update_id);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi_in=0x%x seq_update_id=%u keymat_index=%u\n",
+ item->spi, item->seq_update_id, item->keymat_index);
+ if (item->seq_update_id == peer_update_id) {
+ return item->keymat_index;
+ }
+ }
+ return 0;
+}
+
+/* todo: use jiffies instead of timestamp */
+uint32_t hip_hadb_get_latest_inbound_spi(hip_ha_t *entry)
+{
+ struct hip_spi_in_item *item, *tmp;
+ uint32_t spi = 0;
+ unsigned int now = jiffies;
+ unsigned long t = ULONG_MAX;
+
+ /* assumes already locked entry */
+
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ if (now - item->timestamp < t) {
+ spi = item->spi;
+ t = now - item->timestamp;
+ }
+ }
+
+ _HIP_DEBUG("newest spi_in is 0x%x\n", spi);
+ return spi;
+}
+
+/* get pointer to the SPI list or NULL if SPI list does not exist */
+struct hip_spi_out_item *hip_hadb_get_spi_list(hip_ha_t *entry, uint32_t spi)
+{
+ struct hip_spi_out_item *item, *tmp;
+
+ /* assumes already locked entry */
+
+ _HIP_DEBUG("SPI=0x%x\n", spi);
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ if (item->spi == spi)
+ return item;
+ }
+ return NULL;
+}
+
+/* add an address belonging to the SPI list */
+/* or update old values */
+int hip_hadb_add_addr_to_spi(hip_ha_t *entry, uint32_t spi, struct in6_addr *addr,
+ int is_bex_address, uint32_t lifetime,
+ int is_preferred_addr)
+{
+ int err = 0;
+ struct hip_spi_out_item *spi_list;
+ struct hip_peer_addr_list_item *new_addr = NULL;
+ struct hip_peer_addr_list_item *a, *tmp;
+ int new = 1;
+
+ /* assumes already locked entry */
+
+ HIP_DEBUG("spi=0x%x is_preferred_addr=%d\n", spi, is_preferred_addr);
+
+ spi_list = hip_hadb_get_spi_list(entry, spi);
+ if (!spi_list) {
+ HIP_ERROR("SPI list for 0x%x not found\n", spi);
+ err = -EEXIST;
+ goto out_err;
+ }
+
+ /* Check if addr already exists. If yes, then just update values. */
+ list_for_each_entry_safe(a, tmp, &spi_list->peer_addr_list, list) {
+ if (!ipv6_addr_cmp(&a->address, addr)) {
+ new_addr = a;
+ new = 0;
+ break;
+ }
+ }
+
+ if (new) {
+ _HIP_DEBUG("create new addr item to SPI list\n");
+ /* SPI list does not contain the address, add the address to the SPI list */
+ new_addr = kmalloc(sizeof(struct hip_peer_addr_list_item), GFP_KERNEL);
+ if (!new_addr) {
+ HIP_ERROR("item kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ } else
+ _HIP_DEBUG("update old addr item\n");
+
+ new_addr->lifetime = lifetime;
+ if (new)
+ ipv6_addr_copy(&new_addr->address, addr);
+
+ /* If the address is already bound, its lifetime is updated.
+ If the status of the address is DEPRECATED, the status is
+ changed to UNVERIFIED. If the address is not already bound,
+ the address is added, and its status is set to UNVERIFIED. */
+ if (!new) {
+ switch (new_addr->address_state) {
+ case PEER_ADDR_STATE_DEPRECATED:
+ new_addr->address_state = PEER_ADDR_STATE_UNVERIFIED;
+ HIP_DEBUG("updated address state DEPRECATED->UNVERIFIED\n");
+ break;
+ case PEER_ADDR_STATE_ACTIVE:
+ HIP_DEBUG("address state stays in ACTIVE\n");
+ break;
+ default:
+ HIP_ERROR("state is UNVERIFIED, shouldn't even be here ?\n");
+ break;
+ }
+ } else {
+ if (is_bex_address) {
+ /* workaround for special case */
+ HIP_DEBUG("address is base exchange address, setting state to ACTIVE\n");
+ new_addr->address_state = PEER_ADDR_STATE_ACTIVE;
+ HIP_DEBUG("setting bex addr as preferred address\n");
+ ipv6_addr_copy(&entry->preferred_address, addr);
+ new_addr->seq_update_id = 0;
+ } else {
+ new_addr->address_state = PEER_ADDR_STATE_UNVERIFIED;
+ _HIP_DEBUG("set initial address state UNVERIFIED\n");
+ }
+ }
+
+ do_gettimeofday(&new_addr->modified_time);
+ new_addr->is_preferred = is_preferred_addr;
+
+#if 0
+ if (is_preferred_addr) {
+ ipv6_addr_copy(&spi_list->preferred_address, addr);
+ ipv6_addr_copy(&entry->preferred_address, addr); // test
+ }
+#endif
+ if (new) {
+ _HIP_DEBUG("adding new addr to SPI list\n");
+ list_add_tail(&new_addr->list, &spi_list->peer_addr_list);
+ }
+
+ out_err:
+ _HIP_DEBUG("returning, err=%d\n", err);
+ return err;
+}
+
+/** hip_get_default_spi_out - Get the SPI to use in the outbound ESP packet
+ * @hit: peer HIT
+ * @state_ok: status of SPI lookup
+ *
+ * On successful return state_ok is 1, on error it is 0.
+ *
+ * Returns: the SPI value to use in the packet, or 0 on error.
+*/
+uint32_t hip_get_default_spi_out(struct in6_addr *hit, int *state_ok)
+{
+ uint32_t spi;
+ hip_ha_t *entry;
+
+ _HIP_DEBUG("\n");
+
+ entry = hip_hadb_find_byhit(hit);
+ if (!entry) {
+ HIP_DEBUG("entry not found\n");
+ *state_ok = 0;
+ return 0;
+ }
+
+ HIP_LOCK_HA(entry);
+ spi = entry->default_spi_out;
+ HIP_UNLOCK_HA(entry);
+ hip_put_ha(entry);
+ *state_ok = spi ? 1 : 0;
+ return spi;
+}
+
+/**
+ * hip_for_each_ha - Map function @func to every HA in HIT hash table
+ * @func: Mapper function
+ * @opaque: Opaque data for the mapper function.
+ *
+ * The hash table is LOCKED while we process all the entries. This means
+ * that the mapper function MUST be very short and _NOT_ do any operations
+ * that might sleep!
+ *
+ * Returns negative if an error occurs. If an error occurs during traversal of
+ * a the HIT hash table, then the traversal is stopped and function returns.
+ * Returns the last return value of applying the mapper function to the last
+ * element in the hash table.
+ */
+int hip_for_each_ha(int (*func)(hip_ha_t *entry, void *opaq), void *opaque)
+{
+ int i, fail = 0;
+ hip_ha_t *this, *tmp;
+
+ if (!func)
+ return -EINVAL;
+
+ HIP_LOCK_HT(&hadb_hit);
+ for(i = 0; i < HIP_HADB_SIZE; i++) {
+ list_for_each_entry_safe(this, tmp, &hadb_byhit[i], next_hit) {
+ hip_hold_ha(this);
+ fail = func(this, opaque);
+ hip_put_ha(this);
+ if (fail)
+ break;
+ }
+ if (fail)
+ break;
+ }
+ HIP_UNLOCK_HT(&hadb_hit);
+ return fail;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+typedef struct {
+ char *page;
+ int count;
+ int len;
+ int i; /* counter */
+} hip_proc_opaque_t;
+
+
+static int hip_proc_hadb_state_func(hip_ha_t *entry, void *opaque)
+{
+ hip_proc_opaque_t *op = (hip_proc_opaque_t *)opaque;
+ char *esp_transforms[] = { "none/reserved", "aes-sha1", "3des-sha1", "3des-md5",
+ "blowfish-sha1", "null-sha1", "null-md5" };
+ char addr_str[INET6_ADDRSTRLEN];
+ char *page = op->page;
+ int len = op->len;
+ int count = op->count;
+ int i = op->i;
+
+ HIP_LOCK_HA(entry);
+
+ if ( (len += snprintf(page+len, count-len, "%s 0x%x %d 0x%x",
+ hip_state_str(entry->state),
+ entry->hastate,
+ atomic_read(&entry->refcnt),
+ entry->peer_controls)) >= count)
+ goto error;
+
+ hip_in6_ntop(&entry->hit_our, addr_str);
+ if ( (len += snprintf(page+len, count-len, " %s", addr_str)) >= count)
+ goto error;
+
+ hip_in6_ntop(&entry->hit_peer, addr_str);
+ if ( (len += snprintf(page+len, count-len, " %s", addr_str)) >= count)
+ goto error;
+
+ if ( (len += snprintf(page+len, count-len,
+ " 0x%08x 0x%08x 0x%08x %s",
+ entry->default_spi_out, entry->lsi_our,
+ entry->lsi_peer,
+ entry->esp_transform <=
+ (sizeof(esp_transforms)/sizeof(esp_transforms[0]))
+ ? esp_transforms[entry->esp_transform] :
+ "UNKNOWN")) >= count)
+ goto error;
+
+ if ( (len += snprintf(page+len, count-len,
+ " 0x%llx %u %u %u %u %u\n",
+ entry->birthday,
+ entry->current_keymat_index,
+ entry->keymat_calc_index, entry->update_id_in,
+ entry->update_id_out, entry->dh_shared_key_len )) >= count)
+ goto error;
+
+ if (len >= count)
+ goto error;
+
+ HIP_UNLOCK_HA(entry);
+
+ op->len = len;
+ op->count = count;
+ op->i = i;
+ return 0;
+
+ error:
+ HIP_UNLOCK_HA(entry);
+ HIP_DEBUG("PROC read max len exceeded\n");
+ return -1;
+}
+
+
+static int hip_proc_read_hadb_peer_addrs_func(hip_ha_t *entry, void *opaque)
+{
+ hip_proc_opaque_t *op = (hip_proc_opaque_t *)opaque;
+ struct timeval now, addr_age;
+ char addr_str[INET6_ADDRSTRLEN];
+ struct hip_peer_addr_list_item *s;
+ int i = op->i;
+ char *page = op->page;
+ int len = op->len;
+ int count = op->count;
+ struct hip_spi_out_item *spi_out, *spi_tmp;
+ const char *state_name[] = { "NONE", "UNVERIFIED", "ACTIVE", "DEPRECATED" };
+
+ do_gettimeofday(&now);
+
+ HIP_LOCK_HA(entry);
+
+ hip_in6_ntop(&entry->hit_peer, addr_str);
+ if ( (len += snprintf(page+len, count-len, "HIT %s", addr_str)) >= count)
+ goto error;
+
+ if (entry->default_spi_out == 0) {
+ /* extra check for addr_any ? */
+ hip_in6_ntop(&entry->bex_address, addr_str);
+ if ( (len += snprintf(page+len, count-len,
+ "\n SPI 0x0\n %s", addr_str)) >= count)
+ goto error;
+ }
+
+ list_for_each_entry_safe(spi_out, spi_tmp, &entry->spis_out, list) {
+ int n_addrs = 0;
+
+ if ( (len += snprintf(page+len, count-len,
+ "\n SPI 0x%x", spi_out->spi)) >= count)
+ goto error;
+
+ if (spi_out->spi == entry->default_spi_out &&
+ (len += snprintf(page+len, count-len, " preferred")) >= count)
+ goto error;
+
+ list_for_each_entry(s, &spi_out->peer_addr_list, list) {
+ n_addrs++;
+ hip_in6_ntop(&s->address, addr_str);
+ hip_timeval_diff(&now, &s->modified_time, &addr_age);
+ if ( (len += snprintf(page+len, count-len,
+ "\n %s state=%s lifetime=0x%x "
+ "age=%ld.%01ld seq=%u REA_preferred=%d",
+ addr_str, state_name[s->address_state],
+ s->lifetime, addr_age.tv_sec,
+ addr_age.tv_usec / 100000 /* show 1/10th sec */,
+ s->seq_update_id, s->is_preferred)
+ ) >= count)
+ goto error;
+
+ if (!ipv6_addr_cmp(&s->address, &spi_out->preferred_address) &&
+ (len += snprintf(page+len, count-len, " preferred")) >= count)
+ goto error;
+
+ i++;
+ }
+
+ if (n_addrs == 0 && (len += snprintf(page+len, count-len, "\n no addresses")) >= count)
+ goto error;
+ }
+
+ if ( (len += snprintf(page+len, count-len, "\n")) >= count)
+ goto error;
+
+ HIP_UNLOCK_HA(entry);
+
+ op->len = len;
+ op->count = count;
+ op->i = i;
+ return 0;
+ error:
+ HIP_UNLOCK_HA(entry);
+ HIP_DEBUG("PROC read peer addresses buffer exceeded\n");
+ return -1;
+}
+
+/**
+ * hip_proc_read_hadb_state - debug function for dumping hip_sdb_state
+ * @page: where dumped data is written to
+ * @start: ignored
+ * @off: ignored
+ * @count: how many bytes to read
+ * @eof: pointer where end of file flag is stored, always set to 1
+ * @data: ignored
+ *
+ * hip_hadb_state can be dumped from file /proc/net/hip/sdb_state
+ *
+ * Returns: number of bytes written to @page.
+ */
+int hip_proc_read_hadb_state(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ hip_proc_opaque_t ps;
+ int fail;
+
+ ps.page = page;
+ ps.count = count;
+
+ ps.len = snprintf(page, count,
+ "state hastate refcnt peer_controls hit_our hit_peer "
+ "default_spi_out lsi_our lsi_peer esp_transform "
+ "birthday keymat_index keymat_calc_index "
+ "update_id_in update_id_out dh_len\n");
+
+ if (ps.len >= count) {
+ fail = 1;
+ goto err;
+ }
+
+ *eof = 1;
+ fail = hip_for_each_ha(hip_proc_hadb_state_func, &ps);
+
+ err:
+ if (fail) {
+ page[ps.count-1] = '\0';
+ ps.len = ps.count;
+ } else
+ page[ps.len] = '\0';
+
+ return ps.len;
+}
+
+
+/**
+ * hip_proc_read_hadb_peer_addrs - dump properties of IPv6 addresses of every peer
+ * @page: where dumped data is written to
+ * @start: ignored
+ * @off: ignored
+ * @count: how many bytes to read
+ * @eof: pointer where end of file flag is stored, always set to 1
+ * @data: ignored
+ *
+ * This debug function lists every IPv6 address and their properties
+ * for every peer. The list can be dumped from from file
+ * /proc/net/hip/sdb_peer_addrs
+ *
+ * Returns: number of bytes written to @page.
+ */
+int hip_proc_read_hadb_peer_addrs(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ hip_proc_opaque_t ps;
+ int fail;
+
+ ps.page = page;
+ ps.count = count;
+ ps.len = 0;
+ *eof = 1;
+
+ fail = hip_for_each_ha(hip_proc_read_hadb_peer_addrs_func, &ps);
+ if (fail) {
+ page[ps.count-1] = '\0';
+ ps.len = ps.count;
+ } else
+ page[ps.len] = '\0';
+
+ return ps.len;
+}
+
+#endif /* CONFIG_PROC_FS */
+
+
+/**
+ * hip_hadb_dump_hits - Dump the contents of the HIT hash table.
+ *
+ * Should be safe to call from any context. THIS IS FOR DEBUGGING ONLY.
+ * DONT USE IT IF YOU DONT UNDERSTAND IT.
+ */
+void hip_hadb_dump_hits(void)
+{
+ int i;
+ hip_ha_t *entry;
+ char *string;
+ int cnt, k;
+
+ string = kmalloc(4096,GFP_ATOMIC);
+ if (!string) {
+ HIP_ERROR("Cannot dump HADB... out of memory\n");
+ return;
+ }
+
+ HIP_LOCK_HT(&hadb_hit);
+
+ for(i = 0; i 3900) {
+ string[cnt] = '\0';
+ printk(KERN_ALERT "%s\n", string);
+ cnt = 0;
+ }
+
+ k = hip_in6_ntop2(&entry->hit_peer, string+cnt);
+ cnt+=k;
+ hip_put_ha(entry);
+ }
+ string[cnt] = '\0';
+ printk(KERN_ALERT "%s\n", string);
+ }
+ }
+
+ HIP_UNLOCK_HT(&hadb_hit);
+ kfree(string);
+}
+
+
+void hip_hadb_dump_hs_ht(void)
+{
+ int i;
+ struct hip_hit_spi *hs, *tmp_hs;
+ char str[INET6_ADDRSTRLEN];
+
+ HIP_DEBUG("start\n");
+ HIP_LOCK_HT(&hadb_spi_list);
+
+ for(i = 0; i < HIP_HADB_SIZE; i++) {
+ if (!list_empty(&hadb_byspi_list[i])) {
+ _HIP_DEBUG("HT[%d]\n", i);
+ list_for_each_entry_safe(hs, tmp_hs, &hadb_byspi_list[i], list) {
+ hip_hadb_hold_hs(hs);
+ hip_in6_ntop(&hs->hit, str);
+ HIP_DEBUG("HIT=%s SPI=0x%x refcnt=%d\n",
+ str, hs->spi, atomic_read(&hs->refcnt));
+ hip_hadb_put_hs(hs);
+ }
+ }
+ }
+
+ HIP_UNLOCK_HT(&hadb_spi_list);
+ HIP_DEBUG("end\n");
+}
+
+
+void hip_hadb_dump_spis_in(hip_ha_t *entry)
+{
+ struct hip_spi_in_item *item, *tmp;
+#ifdef CONFIG_HIP_DEBUG
+ unsigned long now = jiffies;
+#endif
+
+ HIP_DEBUG("start\n");
+ HIP_LOCK_HA(entry);
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ HIP_DEBUG(" SPI=0x%x new_SPI=0x%x nes_SPI_out=0x%x ifindex=%d "
+ "ts=%lu updating=%d keymat_index=%u upd_flags=0x%x seq_update_id=%u NES=old 0x%x,new 0x%x,km %u\n",
+ item->spi, item->new_spi, item->nes_spi_out, item->ifindex,
+ now - item->timestamp, item->updating, item->keymat_index,
+ item->update_state_flags, item->seq_update_id,
+ item->stored_received_nes.old_spi,
+ item->stored_received_nes.old_spi,
+ item->stored_received_nes.keymat_index);
+ }
+ HIP_UNLOCK_HA(entry);
+ HIP_DEBUG("end\n");
+}
+
+void hip_hadb_dump_spis_out(hip_ha_t *entry)
+{
+ struct hip_spi_out_item *item, *tmp;
+
+ HIP_DEBUG("start\n");
+ HIP_LOCK_HA(entry);
+ list_for_each_entry_safe(item, tmp, &entry->spis_out, list) {
+ HIP_DEBUG(" SPI=0x%x new_SPI=0x%x seq_update_id=%u\n",
+ item->spi, item->new_spi, item->seq_update_id);
+ }
+ HIP_UNLOCK_HA(entry);
+ HIP_DEBUG("end\n");
+}
+
+
+void hip_init_hadb(void)
+{
+ memset(&hadb_hit,0,sizeof(hadb_hit));
+ memset(&hadb_spi_list,0,sizeof(hadb_spi_list));
+
+ hadb_hit.head = hadb_byhit;
+ hadb_hit.hashsize = HIP_HADB_SIZE;
+ hadb_hit.offset = offsetof(hip_ha_t, next_hit);
+ hadb_hit.hash = hip_hash_hit;
+ hadb_hit.compare = hip_match_hit;
+ hadb_hit.hold = hip_hadb_hold_entry;
+ hadb_hit.put = hip_hadb_put_entry;
+ hadb_hit.get_key = hip_hadb_get_key_hit;
+
+ strncpy(hadb_hit.name,"HADB_BY_HIT", 15);
+ hadb_hit.name[15] = 0;
+
+ hadb_spi_list.head = hadb_byspi_list;
+ hadb_spi_list.hashsize = HIP_HADB_SIZE;
+ hadb_spi_list.offset = offsetof(struct hip_hit_spi, list);
+ hadb_spi_list.hash = hip_hash_spi;
+ hadb_spi_list.compare = hip_hadb_match_spi;
+ hadb_spi_list.hold = hip_hadb_hold_hs;
+ hadb_spi_list.put = hip_hadb_put_hs;
+ hadb_spi_list.get_key = hip_hadb_get_key_spi_list;
+
+ strncpy(hadb_spi_list.name,"HADB_BY_SPI_LIST", 15);
+ hadb_spi_list.name[15] = 0;
+
+ hip_ht_init(&hadb_hit);
+ hip_ht_init(&hadb_spi_list);
+}
+
+void hip_uninit_hadb()
+{
+ int i;
+ hip_ha_t *ha, *tmp;
+ struct hip_hit_spi *hs, *tmp_hs;
+
+ HIP_DEBUG("\n");
+
+ HIP_DEBUG("DEBUG: DUMP SPI LISTS\n");
+ hip_hadb_dump_hs_ht();
+
+ /* I think this is not very safe deallocation.
+ * Locking the hadb_spi and hadb_hit could be one option, but I'm not
+ * very sure that it will work, as they are locked later in
+ * hip_hadb_remove_state() for a while.
+ *
+ * The list traversing is not safe in smp way :(
+ */
+ HIP_DEBUG("DELETING HA HT\n");
+ for(i = 0; i < HIP_HADB_SIZE; i++) {
+ list_for_each_entry_safe(ha, tmp, &hadb_byhit[i], next_hit) {
+ if (atomic_read(&ha->refcnt) > 2)
+ HIP_ERROR("HA: %p, in use while removing it from HADB\n", ha);
+ hip_hold_ha(ha);
+ hip_hadb_remove_state(ha);
+ hip_put_ha(ha);
+ }
+ }
+
+ /* HIT-SPI mappings should be already deleted by now, but check anyway */
+ HIP_DEBUG("DELETING HS HT\n");
+ for(i = 0; i < HIP_HADB_SIZE; i++) {
+ _HIP_DEBUG("HS HT [%d]\n", i);
+ list_for_each_entry_safe(hs, tmp_hs, &hadb_byspi_list[i], list) {
+ HIP_ERROR("BUG: HS NOT ALREADY DELETED, DELETING HS %p, HS SPI=0x%x\n",
+ hs, hs->spi);
+ if (atomic_read(&hs->refcnt) > 1)
+ HIP_ERROR("HS: %p, in use while removing it from HADB\n", hs);
+ hip_hadb_hold_hs(hs);
+ //hip_hadb_delete_hs(hs);
+ hip_hadb_remove_hs2(hs);
+ hip_hadb_put_hs(hs);
+ //} else
+ // HIP_DEBUG("HS refcnt < 1, BUG ?\n");
+ }
+ }
+ HIP_DEBUG("DONE DELETING HS HT\n");
+}
diff -urN linux-2.6.10/net/ipv6/hip/hadb.h linux-2.6.10-hip/net/ipv6/hip/hadb.h
--- linux-2.6.10/net/ipv6/hip/hadb.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/hadb.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,136 @@
+#ifndef HIP_HADB_H
+#define HIP_HADB_H
+
+#include "debug.h"
+
+#include
+
+#define HIP_HADB_SIZE 53
+#define HIP_MAX_HAS 100
+
+#define HIP_LOCK_HA(ha) do { spin_lock_bh(&ha->lock); } while(0)
+#define HIP_UNLOCK_HA(ha) do { spin_unlock_bh(&ha->lock); } while(0)
+#define HIP_LOCK_HS(hs) do { spin_lock_bh(&hs->lock); } while(0)
+#define HIP_UNLOCK_HS(hs) do { spin_unlock_bh(&hs->lock); } while(0)
+
+
+/*************** BASE FUNCTIONS *******************/
+
+/* Initialization functions */
+void hip_init_hadb(void);
+void hip_uninit_hadb(void);
+
+/* Accessors */
+hip_ha_t *hip_hadb_find_byspi_list(u32 spi);
+hip_ha_t *hip_hadb_find_byhit(hip_hit_t *hit);
+
+/* insert/create/delete */
+int hip_hadb_insert_state(hip_ha_t *ha);
+int hip_hadb_insert_state_spi_list(hip_ha_t *ha, uint32_t spi);
+void hip_hadb_remove_state(hip_ha_t *ha);
+void hip_hadb_remove_state_hit(hip_ha_t *ha);
+void hip_hadb_remove_hs(uint32_t spi);
+
+/* existence */
+int hip_hadb_exists_entry(void *key, int type);
+
+/* debugging */
+void hip_hadb_dump_hits(void);
+void hip_hadb_dump_spis(void);
+
+/*************** CONSTRUCTS ********************/
+int hip_hadb_exists_entry(void *arg, int type);
+
+int hip_hadb_get_peer_addr(hip_ha_t *entry, struct in6_addr *addr);
+
+int hip_hadb_get_peer_addr_info(hip_ha_t *entry, struct in6_addr *addr,
+ uint32_t *spi, uint32_t *lifetime,
+ struct timeval *modified_time);
+
+int hip_hadb_set_peer_addr_info(hip_ha_t *entry, struct in6_addr *addr,
+ uint32_t *lifetime);
+
+int hip_hadb_add_peer_addr(hip_ha_t *entry, struct in6_addr *new_addr,
+ uint32_t interface_id, uint32_t lifetime,
+ int state);
+
+void hip_hadb_delete_peer_addrlist_one(hip_ha_t *entry, struct in6_addr *addr);
+
+void hip_hadb_delete_peer_addrlist(hip_ha_t *entry);
+
+int hip_for_each_ha(int (func)(hip_ha_t *entry, void *opaq), void *opaque);
+
+int hip_hadb_add_peer_info(hip_hit_t *hit, struct in6_addr *addr);
+
+int hip_del_peer_info(struct in6_addr *hit, struct in6_addr *addr);
+
+int hip_hadb_add_spi(hip_ha_t *entry, int direction, void *data);
+void hip_hadb_delete_inbound_spi(hip_ha_t *entry, uint32_t spi);
+void hip_hadb_delete_inbound_spis(hip_ha_t *entry);
+void hip_hadb_delete_outbound_spi(hip_ha_t *entry, uint32_t spi);
+void hip_hadb_delete_outbound_spis(hip_ha_t *entry);
+
+uint32_t hip_hadb_get_latest_inbound_spi(hip_ha_t *entry);
+
+void hip_hadb_set_spi_ifindex(hip_ha_t *entry, uint32_t spi, int ifindex);
+uint32_t hip_hadb_get_spi(hip_ha_t *entry, int ifindex);
+int hip_hadb_get_spi_ifindex(hip_ha_t *entry, uint32_t spi);
+uint32_t hip_update_get_prev_spi_in(hip_ha_t *entry, uint32_t prev_spi_out);
+uint32_t hip_get_spi_to_update(hip_ha_t *entry);
+uint32_t hip_get_spi_to_update_in_established(hip_ha_t *entry, struct in6_addr *dev_addr);
+void hip_set_spi_update_status(hip_ha_t *entry, uint32_t spi, int set);
+void hip_update_set_new_spi_in(hip_ha_t *entry, uint32_t spi, uint32_t new_spi, uint32_t spi_out);
+void hip_update_set_new_spi_out(hip_ha_t *entry, uint32_t spi, uint32_t new_spi);
+uint32_t hip_update_get_new_spi_in(hip_ha_t *entry, uint32_t spi);
+void hip_update_switch_spi_in(hip_ha_t *entry, uint32_t old_spi);
+void hip_update_switch_spi_out(hip_ha_t *entry, uint32_t old_spi);
+void hip_update_set_status(hip_ha_t *entry, uint32_t spi, int set_flags,
+ uint32_t update_id, int update_flags_or, struct hip_nes *nes,
+ uint16_t keymat_index);
+void hip_update_clear_status(hip_ha_t *entry, uint32_t spi);
+int hip_update_exists_spi(hip_ha_t *entry, uint32_t spi,
+ int direction, int test_new_spi);
+uint32_t hip_hadb_relookup_default_out(hip_ha_t *entry);
+void hip_hadb_set_default_out_addr(hip_ha_t *entry, struct hip_spi_out_item *spi_out,
+ struct in6_addr *addr);
+void hip_update_handle_ack(hip_ha_t *entry, struct hip_ack *ack, int have_nes,
+ struct hip_echo_response *echo_esp);
+void hip_update_handle_nes(hip_ha_t *entry, uint32_t peer_update_id);
+int hip_update_get_spi_keymat_index(hip_ha_t *entry, uint32_t spi);
+
+struct hip_spi_out_item *hip_hadb_get_spi_list(hip_ha_t *entry, uint32_t spi);
+int hip_hadb_add_addr_to_spi(hip_ha_t *entry, uint32_t spi, struct in6_addr *addr,
+ int address_state, uint32_t lifetime,
+ int is_preferred_addr);
+uint32_t hip_get_default_spi_out(struct in6_addr *hit, int *state_ok);
+
+/***********************************************/
+int hip_proc_read_hadb_state(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+int hip_proc_read_hadb_peer_addrs(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+/**************** other useful ******************/
+void hip_hadb_delete_state(hip_ha_t *ha);
+hip_ha_t *hip_hadb_create_state(int gfpmask);
+void hip_hadb_deactivate_hs_spi(uint32_t spi);
+
+void hip_hadb_dump_spis_in(hip_ha_t *entry);
+void hip_hadb_dump_spis_out(hip_ha_t *entry);
+void hip_hadb_dump_hs_ht(void);
+
+#define hip_hold_ha(ha) do { \
+ atomic_inc(&ha->refcnt); \
+ _HIP_DEBUG("HA: %p, refcnt incremented to: %d\n",ha, atomic_read(&ha->refcnt)); \
+} while(0)
+
+#define hip_put_ha(ha) do { \
+ if (atomic_dec_and_test(&ha->refcnt)) { \
+ HIP_DEBUG("HA: deleting %p\n", ha); \
+ hip_hadb_delete_state(ha); \
+ HIP_DEBUG("HA: %p deleted\n", ha); \
+ } else { \
+ _HIP_DEBUG("HA: %p, refcnt decremented to: %d\n", ha, atomic_read(&ha->refcnt)); \
+ } \
+} while(0)
+
+#endif /* HIP_HADB_H */
diff -urN linux-2.6.10/net/ipv6/hip/hashtable.c linux-2.6.10-hip/net/ipv6/hip/hashtable.c
--- linux-2.6.10/net/ipv6/hip/hashtable.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/hashtable.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,147 @@
+#include "hashtable.h"
+#include "debug.h"
+
+#include
+#include
+
+#define hip_ht_get_content(type, ptr, offset) \
+ (type *)((u8 *)ptr - offset)
+
+#define hip_ht_get_list(ptr, offset) \
+ (struct list_head *)((u8 *)ptr + offset)
+
+/**
+ * hip_ht_find - Find an element in a hash table
+ * @ht: hash table
+ * @key: key
+ *
+ * Returns NULL, or the entry that matches the @key
+ */
+void *hip_ht_find(HIP_HASHTABLE *ht, void *key)
+{
+ struct list_head *chain;
+ void *entry;
+ void *key_to_be_matched;
+ int hash;
+
+ hash = ht->hash(key, ht->hashsize);
+ _HIP_DEBUG("hash=%d HT=%s\n", hash, ht->name);
+ HIP_LOCK_HT(ht);
+ {
+ list_for_each(chain, &ht->head[hash]) {
+ entry = hip_ht_get_content(void, chain, ht->offset);
+
+ key_to_be_matched = ht->get_key(entry);
+ _HIP_DEBUG("entry=0x%p key=0x%p\n", entry, key_to_be_matched);
+ if (ht->compare(key, key_to_be_matched)) {
+ ht->hold(entry);
+ HIP_UNLOCK_HT(ht);
+ return entry;
+ }
+ }
+ }
+ HIP_UNLOCK_HT(ht);
+ return NULL;
+}
+
+/**
+ * hip_ht_add - Add an element to a hash table
+ * @ht: hash table
+ * @entry: element to add
+ *
+ * Automatically holds (increases ref count) of the element.
+ * [since the hash table stores a reference to the object]
+ *
+ * Returns 0
+ */
+int hip_ht_add(HIP_HASHTABLE *ht, void *entry)
+{
+ int hash = ht->hash(ht->get_key(entry), ht->hashsize);
+ _HIP_DEBUG("hash=%d HT=%s\n", hash, ht->name);
+ HIP_LOCK_HT(ht);
+ list_add(hip_ht_get_list(entry, ht->offset), &ht->head[hash]);
+ ht->hold(entry);
+ HIP_UNLOCK_HT(ht);
+
+ return 0;
+}
+
+/**
+ * hip_ht_delete - Delete an element from a hash table
+ * @ht: hash table
+ * @entry: element to delete
+ *
+ * Automatically puts (decreases ref count) of the element
+ * Does not explicitly delete the element.
+ */
+void hip_ht_delete(HIP_HASHTABLE *ht, void *entry)
+{
+ HIP_LOCK_HT(ht);
+ list_del(hip_ht_get_list(entry, ht->offset));
+ ht->put(entry);
+ HIP_UNLOCK_HT(ht);
+}
+
+
+/**
+ * hip_ht_init - Initialize a hash table
+ * @ht: Prefilled with following elements:
+ * head: Pointer to memory area to be used as hash table
+ * hashsize: Size of the hashtable (ie. number of chains).
+ * offset: offset of the struct list_head that links the elements
+ * hash: function that hashes the key
+ * compare: function that compares two keys
+ * hold: function that increases element's ref count
+ * put: function that decreases element's ref count
+ * get_key: function that returns element's key from the element structure
+ * name: id (for debugging purposes)
+ *
+ * Returns 0
+ */
+int hip_ht_init(HIP_HASHTABLE *ht)
+{
+ int i;
+
+ if (ht->name)
+ HIP_DEBUG("Initializing hash table: %s\n",ht->name);
+ else
+ HIP_DEBUG("Initializing hash table\n");
+
+ HIP_ASSERT(ht);
+ HIP_ASSERT(ht->head);
+ HIP_ASSERT(ht->hashsize);
+ //HIP_ASSERT(ht->offset);
+ HIP_ASSERT(ht->hash);
+ HIP_ASSERT(ht->compare);
+ HIP_ASSERT(ht->hold);
+ HIP_ASSERT(ht->put);
+ HIP_ASSERT(ht->get_key);
+
+ spin_lock_init(&ht->lock);
+
+ for(i=0; ihashsize; i++)
+ INIT_LIST_HEAD(&ht->head[i]);
+
+ HIP_DEBUG("Initialization of hash table complete\n");
+ return 0;
+}
+
+/**
+ * hip_ht_uninit - Uninitialize a hash table
+ * @ht: hash table
+ *
+ * traverses through the hash table and puts every element
+ * [= notifies that we no longer have a reference to the element].
+ */
+void hip_ht_uninit(HIP_HASHTABLE *ht)
+{
+ int i;
+ struct list_head *item, *tmp;
+
+ for(i=0;ihashsize;i++) {
+ list_for_each_safe(item, tmp, &ht->head[i]) {
+ list_del(item);
+ ht->put(hip_ht_get_content(void, item, ht->offset));
+ }
+ }
+}
diff -urN linux-2.6.10/net/ipv6/hip/hashtable.h linux-2.6.10-hip/net/ipv6/hip/hashtable.h
--- linux-2.6.10/net/ipv6/hip/hashtable.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/hashtable.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,41 @@
+#ifndef HIP_HASHTABLE_H
+#define HIP_HASHTABLE_H
+
+#include
+#include
+#include
+
+struct hip_ht_common {
+ struct list_head *head;
+ spinlock_t lock;
+ size_t hashsize;
+ int offset;
+ int (*hash)(void *key, int range);
+ int (*compare)(void *key_to_match, void *key_to_be_matched);
+ void (*hold)(void *entry);
+ void (*put)(void *entry);
+ void *(*get_key)(void *entry);
+ char name[16];
+};
+
+typedef struct hip_ht_common HIP_HASHTABLE;
+
+/************ primitives *************/
+
+int hip_ht_init(HIP_HASHTABLE *ht);
+void hip_ht_uninit(HIP_HASHTABLE *ht);
+
+void *hip_ht_find(HIP_HASHTABLE *ht, void *key);
+int hip_ht_add(HIP_HASHTABLE *ht, void *entry);
+void hip_ht_delete(HIP_HASHTABLE *ht, void *entry);
+
+#define HIP_LOCK_HT(hash) do { \
+ spin_lock_bh(&(hash)->lock); \
+} while(0)
+
+#define HIP_UNLOCK_HT(hash) do { \
+ spin_unlock_bh(&(hash)->lock); \
+} while(0)
+
+#endif
+
diff -urN linux-2.6.10/net/ipv6/hip/hip.c linux-2.6.10-hip/net/ipv6/hip/hip.c
--- linux-2.6.10/net/ipv6/hip/hip.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/hip.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,2278 @@
+/*
+ * HIP init/uninit and other miscellaneous functions
+ *
+ * Authors: Janne Lundberg
+ * Miika Komu
+ * Mika Kousa
+ * Kristian Slavov
+ * Anthony D. Joseph
+ *
+ */
+
+#include "hip.h"
+#include "hadb.h"
+#include "input.h"
+#include "builder.h"
+#include "db.h"
+#include "cookie.h"
+#include "keymat.h"
+#include "security.h"
+#include "misc.h"
+#include "output.h"
+#include "workqueue.h"
+#include "socket.h"
+#include "update.h"
+#ifdef CONFIG_HIP_RVS
+#include "rvs.h"
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#ifdef CONFIG_SYSCTL
+#include
+#endif
+
+static atomic_t hip_working = ATOMIC_INIT(0);
+
+time_t load_time;
+
+static struct notifier_block hip_netdev_notifier;
+static void hip_uninit_cipher(void); // forward decl.
+static void hip_cleanup(void);
+
+/* All cipher and digest implementations we support. */
+static struct crypto_tfm *impl_3des_cbc = NULL;
+static struct crypto_tfm *impl_aes_cbc = NULL;
+
+/* global variables */
+struct socket *hip_output_socket;
+struct crypto_tfm *impl_null = NULL;
+struct crypto_tfm *impl_sha1 = NULL;
+spinlock_t dh_table_lock = SPIN_LOCK_UNLOCKED;
+DH *dh_table[HIP_MAX_DH_GROUP_ID] = {0};
+
+#ifdef KRISUS_THESIS
+struct timeval gtv_start;
+struct timeval gtv_stop;
+struct timeval gtv_result;
+int gtv_inuse;
+int kmm; // krisu_measurement_mode
+#endif
+
+//spinlock_t hip_workqueue_lock = SPIN_LOCK_UNLOCKED;
+struct hip_kthread_data {
+ int cpu;
+ pid_t pid;
+ struct completion kthread_work;
+ int killed;
+};
+
+struct hip_kthread_data hip_kthreads[NR_CPUS];
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *hip_proc_root = NULL;
+#endif /* CONFIG_PROC_FS */
+
+
+static void hip_err_handler(struct sk_buff *skb,
+ struct inet6_skb_parm *opt,
+ int type, int code, int offset,
+ __u32 info);
+
+static struct inet6_protocol hip_protocol = {
+ .handler = hip_inbound,
+ .err_handler = hip_err_handler,
+ .flags = INET6_PROTO_NOPOLICY,
+};
+
+#ifdef CONFIG_SYSCTL
+/* /proc/sys/net/hip */
+int sysctl_hip_test = 0;
+static struct ctl_table_header *hip_sysctl_header = NULL;
+
+static int zero = 0, max_k = 64; /* sysctl table wants pointers to ranges */
+
+struct hip_sys_config hip_sys_config;
+
+static ctl_table hip_table[] = {
+ {
+ .ctl_name = NET_HIP_COOKIE_MAX_K_R1,
+ .procname = "cookie_max_k_r1",
+ .data = &hip_sys_config.hip_cookie_max_k_r1,
+ .maxlen = sizeof (int),
+ .mode = 0600,
+ .proc_handler = &proc_dointvec_minmax,
+ .strategy = &sysctl_intvec,
+ .extra1 = &zero,
+ .extra2 = &max_k
+ },
+ { .ctl_name = 0 }
+};
+
+static ctl_table hip_net_table[] = {
+ {
+ .ctl_name = NET_HIP,
+ .procname = "hip",
+ .mode = 0555,
+ .child = hip_table
+ },
+ { .ctl_name = 0 }
+};
+
+static ctl_table hip_root_table[] = {
+ {
+ .ctl_name = CTL_NET,
+ .procname = "net",
+ .mode = 0555,
+ .child = hip_net_table
+ },
+ { .ctl_name = 0 }
+};
+
+int hip_register_sysctl(void)
+{
+ HIP_DEBUG("\n");
+ hip_sysctl_header = register_sysctl_table(hip_root_table, 0);
+ return (hip_sysctl_header ? 1 : 0);
+}
+
+void hip_unregister_sysctl(void)
+{
+ HIP_DEBUG("\n");
+ if (hip_sysctl_header)
+ unregister_sysctl_table(hip_sysctl_header);
+}
+
+/**
+ * hip_init_sys_config - Initialize HIP related sysctl variables to default values
+ */
+void hip_init_sys_config(void)
+{
+ hip_sys_config.hip_cookie_max_k_r1 = 20;
+}
+#endif
+
+/**
+ * hip_get_dh_size - determine the size for required to store DH shared secret
+ * @hip_dh_group_type: the group type from DIFFIE_HELLMAN parameter
+ *
+ * Returns: 0 on failure, or the size for storing DH shared secret in bytes
+ */
+uint16_t hip_get_dh_size(uint8_t hip_dh_group_type)
+{
+ /* the same values as are supported ? HIP_DH_.. */
+ int dh_size[] = { 0, 384, 768, 1536, 3072, 6144, 8192 };
+ uint16_t ret = -1;
+
+ _HIP_DEBUG("dh_group_type=%u\n", hip_dh_group_type);
+ if (hip_dh_group_type == 0)
+ HIP_ERROR("Trying to use reserved DH group type 0\n");
+ else if (hip_dh_group_type == HIP_DH_384)
+ HIP_ERROR("draft-09: Group ID 1 does not exist yet\n");
+ else if (hip_dh_group_type > ARRAY_SIZE(dh_size))
+ HIP_ERROR("Unknown/unsupported MODP group %d\n", hip_dh_group_type);
+ else
+ ret = dh_size[hip_dh_group_type] / 8;
+
+ return ret + 1;
+}
+
+/**
+ * hip_map_virtual_to_pages - Maps virtual addresses to physical page addresses
+ * @slist: Pointer to an array of scatterlists that contain the phycical page information
+ * @slistcnt: Number of elements in @slist array
+ * @addr: Virtual address
+ * @size: Size of the block that is beeing transformed (from @addr in bytes)
+ *
+ * Cryptoapi requires that all addresses are given in physical pages rather than
+ * virtual addresses. Thus, we need to convert the virtual addresses seen by the
+ * HIPL code to pages.
+ * We will fill at most @slitcnt scatterlists. If more are required, an error is
+ * returned.
+ *
+ * Returns 0, if ok. <0 if an error occured. As a side effect @slist will be filled
+ * with @slistcnt entries. At the exit, the @slistcnt variable will hold the actual
+ * number of scatterlist entries that were written.
+ */
+int hip_map_virtual_to_pages(struct scatterlist *slist, int *slistcnt,
+ const u8 *addr, const u32 size)
+{
+ int err = -1;
+ unsigned long offset,pleft;
+ unsigned int elt = 0;
+ int slcnt = *slistcnt;
+#ifdef CONFIG_HIP_DEBUG
+ unsigned int i;
+#endif
+
+ if (slcnt < 1) {
+ HIP_ERROR("Illegal use of function\n");
+ return err;
+ }
+
+
+ _HIP_DEBUG("Virtual addresses: %p, size: %d\n",addr,size);
+
+ offset = 0;
+ while(offset < size) {
+
+ slist[elt].dma_address = 0;
+ slist[elt].page = virt_to_page(addr+offset);
+ slist[elt].offset = (unsigned long) (addr+offset) % PAGE_SIZE;
+
+ /* page left */
+ /* pleft = how many bytes there are for us in current page */
+ pleft = PAGE_SIZE - slist[elt].offset;
+ HIP_ASSERT(pleft > 0 && pleft <= PAGE_SIZE);
+
+ _HIP_DEBUG("offset: %ld, space on current page: %ld\n",offset,pleft);
+ if (pleft + offset >= size) {
+ slist[elt].length = size - offset;
+ break;
+ }
+ slist[elt].length = pleft;
+
+ elt++;
+ if (elt >= slcnt) {
+ HIP_ERROR("Not enough room for scatterlist vector\n");
+ err = -ENOMEM;
+ return err;
+ }
+ offset += pleft;
+ }
+
+#ifdef CONFIG_HIP_DEBUG
+ for(i=0;i<=elt;i++) {
+ _HIP_DEBUG("Scatterlist: %x, page: %x, offset: %x, length: %x\n",
+ i, (int)slist[i].page, slist[i].offset, slist[i].length);
+ }
+#endif
+ *slistcnt = elt+1;
+ return 0;
+}
+
+/**
+ * hip_build_digest - calculate a digest over given data
+ * @type: the type of digest, e.g. "sha1"
+ * @in: the beginning of the data to be digested
+ * @in_len: the length of data to be digested in octets
+ * @out: the digest
+ *
+ * @out should be long enough to hold the digest. This cannot be
+ * checked!
+ *
+ * Returns: 0 on success, otherwise < 0.
+ */
+int hip_build_digest(const int type, const void *in, int in_len, void *out)
+{
+ struct crypto_tfm *impl = NULL;
+ struct scatterlist sg[HIP_MAX_SCATTERLISTS];
+ unsigned int nsg = HIP_MAX_SCATTERLISTS;
+
+ int err = 0;
+ switch(type) {
+ case HIP_DIGEST_SHA1:
+ impl = impl_sha1;
+ break;
+ case HIP_DIGEST_MD5:
+ HIP_DEBUG("Not implemented\n");
+ default:
+ HIP_ERROR("Unknown digest: %x\n",type);
+ return -EFAULT;
+ }
+
+ _HIP_DEBUG("Mapping virtual to pages\n");
+
+ err = hip_map_virtual_to_pages(sg, &nsg, in, in_len);
+ if (err || nsg < 1 ) {
+ HIP_ERROR("Error mapping virtual addresses to physical pages\n");
+ return -EFAULT;
+ }
+
+ _HIP_DEBUG("Mapping virtual to pages successful\n");
+
+ crypto_digest_init(impl);
+ crypto_digest_digest(impl, sg, nsg, out);
+
+ return 0;
+}
+
+/**
+ * hip_build_digest_repeat - Calculate digest repeatedly
+ * @dgst: Digest transform
+ * @sg: Valid scatterlist array
+ * @nsg: Number of scatterlists in the @sg array.
+ * @out: Output buffer. Should contain enough bytes for the digest.
+ *
+ * Use this function instead of the one above when you need to do repeated
+ * calculations *IN THE SAME MEMORY SPACE (SIZE _AND_ ADDRESS)*
+ * This is an optimization for cookie solving. There we do a lots of digests
+ * in the same memory block and its size is constant.
+ * So instead of calling N times hip_map_virtual_to_pages() the caller maps
+ * once and all the digest iterations use the same pages.
+ * This improves the speed greatly.
+ *
+ * Returns 0 always. The digest is written to @out.
+*/
+int hip_build_digest_repeat(struct crypto_tfm *dgst, struct scatterlist *sg,
+ int nsg, void *out)
+{
+ crypto_digest_init(dgst); // is this necessary always?
+ crypto_digest_digest(dgst, sg, nsg, out);
+ return 0;
+}
+
+
+/**
+ * hip_write_hmac - calculate hmac
+ * @type: Type (digest algorithm) of HMAC
+ * @key: Pointer to the key used for HMAC
+ * @in: Input buffer pointer
+ * @in_len: Length of buffer
+ * @out: Output buffer pointer. For SHA1-HMAC this is 160bits
+ *
+ * Returns true, if ok.
+ */
+int hip_write_hmac(int type, void *key, void *in, int in_len, void *out)
+{
+ int err = 0;
+ int keylen = 20; // anticipating HIP_DIGEST_SHA1_HMAC
+ struct crypto_tfm *impl = NULL;
+ struct scatterlist sg[HIP_MAX_SCATTERLISTS];
+ int nsg = HIP_MAX_SCATTERLISTS;
+
+ switch(type) {
+ case HIP_DIGEST_SHA1_HMAC:
+ impl = impl_sha1;
+ break;
+ case HIP_DIGEST_MD5_HMAC:
+ HIP_DEBUG("MD5_HMAC not implemented\n");
+ default:
+ HIP_ERROR("Unknown HMAC type 0x%x\n",type);
+ return 0;
+ }
+
+ _HIP_HEXDUMP("HMAC key", key, keylen);
+ _HIP_HEXDUMP("write hmac", in, in_len);
+
+ err = hip_map_virtual_to_pages(sg, &nsg, in, in_len);
+ if (err || nsg < 1) {
+ HIP_ERROR("Mapping failed\n");
+ return 0;
+ }
+
+ crypto_hmac(impl, key, &keylen, sg, nsg, out);
+ return 1;
+}
+
+/**
+ * hip_get_current_birthday - set the current birthday counter into the cookie
+ * @bc: cookie where the birthday field is set to
+ *
+ * Birthday is stored in network byte order.
+ *
+ * This function never touches the other fields of the cookie @bc.
+ */
+uint64_t hip_get_current_birthday(void)
+{
+ return ((uint64_t)load_time << 32) | jiffies;
+}
+
+/**
+ * hip_birthday_success - compare two birthday counters
+ * @old_bd: birthday counter
+ * @new_bd: birthday counter used when comparing against @old_bd
+ *
+ * Returns: 1 (true) if new_bd is newer than old_bd, 0 (false) otherwise.
+ */
+int hip_birthday_success(uint64_t old_bd, uint64_t new_bd)
+{
+ return new_bd > old_bd;
+}
+
+/**
+ * hip_create_r1 - construct a new R1-payload
+ * @src_hit: source HIT used in the packet
+ *
+ * Returns 0 on success, or negative on error
+ */
+struct hip_common *hip_create_r1(const struct in6_addr *src_hit)
+{
+ struct hip_common *msg;
+ int err = 0;
+ int use_rsa = 0;
+ u8 *dh_data = NULL;
+ int dh_size,written, mask;
+ /* Supported HIP and ESP transforms. */
+ hip_transform_suite_t transform_hip_suite[] = {
+ HIP_HIP_AES_SHA1,
+ HIP_HIP_3DES_SHA1,
+ HIP_HIP_NULL_SHA1
+
+};
+ hip_transform_suite_t transform_esp_suite[] = {
+ HIP_ESP_AES_SHA1,
+ HIP_ESP_NULL_SHA1,
+ HIP_ESP_3DES_SHA1
+
+};
+ struct hip_host_id *host_id_private = NULL;
+ struct hip_host_id *host_id_pub = NULL;
+ u8 *signature = NULL;
+
+ msg = hip_msg_alloc();
+ if (!msg) {
+ err = -ENOMEM;
+ HIP_ERROR("msg alloc failed\n");
+ goto out_err;
+ }
+
+ /* allocate memory for writing Diffie-Hellman shared secret */
+ dh_size = hip_get_dh_size(HIP_DEFAULT_DH_GROUP_ID);
+ if (dh_size == 0) {
+ HIP_ERROR("Could not get dh size\n");
+ goto out_err;
+ }
+
+ dh_data = kmalloc(dh_size, GFP_ATOMIC);
+ if (!dh_data) {
+ HIP_ERROR("Failed to alloc memory for dh_data\n");
+ goto out_err;
+ }
+ memset(dh_data, 0, dh_size);
+
+ _HIP_DEBUG("dh_size=%d\n", dh_size);
+ /* Get a localhost identity, allocate memory for the public key part
+ and extract the public key from the private key. The public key is
+ needed for writing the host id parameter in R1. */
+
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not acquire localhost host id\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("private hi len: %d\n",
+ hip_get_param_total_len(host_id_private));
+
+ HIP_HEXDUMP("Our pri host id\n", host_id_private,
+ hip_get_param_total_len(host_id_private));
+
+ host_id_pub = hip_get_any_localhost_public_key(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_pub) {
+ HIP_ERROR("Could not acquire localhost public key\n");
+ goto out_err;
+ }
+
+ HIP_HEXDUMP("Our pub host id\n", host_id_pub,
+ hip_get_param_total_len(host_id_pub));
+
+ /* check for the used algorithm */
+ if (hip_get_host_id_algo(host_id_pub) == HIP_HI_RSA) {
+ use_rsa = 1;
+ } else if (hip_get_host_id_algo(host_id_pub) != HIP_HI_DSA) {
+ HIP_ERROR("Unsupported algorithm:%d\n",
+ hip_get_host_id_algo(host_id_pub));
+ goto out_err;
+ }
+
+ signature = kmalloc(MAX(HIP_DSA_SIGNATURE_LEN,
+ HIP_RSA_SIGNATURE_LEN),
+ GFP_KERNEL);
+ if(!signature) {
+ HIP_ERROR("Could not allocate signature \n");
+ goto out_err;
+ }
+
+ /* Ready to begin building of the R1 packet */
+ //mask = HIP_CONTROL_NONE;
+ //mask = HIP_CONTROL_SHT_MASK | HIP_CONTROL_DHT_MASK;
+ mask = HIP_CONTROL_SHT_TYPE1 << HIP_CONTROL_SHT_SHIFT;
+ HIP_DEBUG("mask 1=0x%x\n", mask);
+ mask |= HIP_CONTROL_DHT_TYPE1 << HIP_CONTROL_DHT_SHIFT;
+ HIP_DEBUG("mask 2=0x%x\n", mask);
+#ifdef CONFIG_HIP_RVS
+ mask |= HIP_CONTROL_RVS_CAPABLE; //XX: FIXME
+#endif
+ HIP_DEBUG("mask 3=0x%x\n", mask);
+ hip_build_network_hdr(msg, HIP_R1, mask, src_hit, NULL);
+
+ /********** R1_COUNTER (OPTIONAL) *********/
+
+ /********** PUZZLE ************/
+ {
+ err = hip_build_param_puzzle(msg, HIP_DEFAULT_COOKIE_K,
+ 42 /* 2^(42-32) sec lifetime */, 0, 0);
+ if (err) {
+ HIP_ERROR("Cookies were burned. Bummer!\n");
+ goto out_err;
+ }
+ }
+
+ /********** Diffie-Hellman **********/
+ written = hip_insert_dh(dh_data,dh_size,HIP_DEFAULT_DH_GROUP_ID);
+ if (written < 0) {
+ HIP_ERROR("Could not extract DH public key\n");
+ goto out_err;
+ }
+
+ err = hip_build_param_diffie_hellman_contents(msg,
+ HIP_DEFAULT_DH_GROUP_ID,
+ dh_data, written);
+ if (err) {
+ HIP_ERROR("Building of DH failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /********** HIP transform. **********/
+ err = hip_build_param_transform(msg,
+ HIP_PARAM_HIP_TRANSFORM,
+ transform_hip_suite,
+ sizeof(transform_hip_suite) /
+ sizeof(hip_transform_suite_t));
+ if (err) {
+ HIP_ERROR("Building of HIP transform failed\n");
+ goto out_err;
+ }
+
+ /********** ESP-ENC transform. **********/
+ err = hip_build_param_transform(msg,
+ HIP_PARAM_ESP_TRANSFORM,
+ transform_esp_suite,
+ sizeof(transform_esp_suite) /
+ sizeof(hip_transform_suite_t));
+ if (err) {
+ HIP_ERROR("Building of ESP transform failed\n");
+ goto out_err;
+ }
+
+ /********** Host_id **********/
+
+ _HIP_DEBUG("This HOST ID belongs to: %s\n",
+ hip_get_param_host_id_hostname(host_id_pub));
+ err = hip_build_param(msg, host_id_pub);
+ if (err) {
+ HIP_ERROR("Building of host id failed\n");
+ goto out_err;
+ }
+
+ /********** ECHO_REQUEST_SIGN (OPTIONAL) *********/
+
+ /********** Signature 2 **********/
+ if (!hip_create_signature(msg,
+ hip_get_msg_total_len(msg),
+ host_id_private,
+ signature)) {
+ HIP_ERROR("Signing of R1 failed.\n");
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("R1", msg, hip_get_msg_total_len(msg));
+
+ if (use_rsa) {
+ err = hip_build_param_signature2_contents(msg,
+ signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature2_contents(msg,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of signature failed (%d) on R1\n", err);
+ goto out_err;
+ }
+
+ /********** ECHO_REQUEST (OPTIONAL) *********/
+
+ /* Fill puzzle parameters */
+ {
+ struct hip_puzzle *pz;
+ uint64_t random_i;
+
+ pz = hip_get_param(msg, HIP_PARAM_PUZZLE);
+ if (!pz) {
+ HIP_ERROR("Internal error\n");
+ goto out_err;
+ }
+
+ // FIX ME: this does not always work:
+ //get_random_bytes(pz->opaque, HIP_PUZZLE_OPAQUE_LEN);
+
+ /* hardcode kludge */
+ pz->opaque[0] = 'H';
+ pz->opaque[1] = 'I';
+ //pz->opaque[2] = 'P';
+ /* todo: remove random_i variable */
+ get_random_bytes(&random_i,sizeof(random_i));
+ pz->I = random_i;
+ }
+
+ /************** Packet ready ***************/
+
+ if (host_id_pub)
+ kfree(host_id_pub);
+ if (dh_data)
+ kfree(dh_data);
+
+ HIP_HEXDUMP("r1", msg, hip_get_msg_total_len(msg));
+
+ return msg;
+
+ out_err:
+ if (signature)
+ kfree(signature);
+ if (host_id_pub)
+ kfree(host_id_pub);
+ if (host_id_private)
+ kfree(host_id_private);
+ if (msg)
+ kfree(msg);
+ if (dh_data)
+ kfree(dh_data);
+
+ return NULL;
+}
+
+/**
+ * hip_enc_key_length - get encryption key length of a transform
+ * @tid: transform
+ *
+ * Returns: the encryption key length based on the chosen transform,
+ * otherwise < 0 on error.
+ */
+int hip_enc_key_length(int tid)
+{
+ int ret = -1;
+
+ switch(tid) {
+ case HIP_ESP_AES_SHA1:
+ ret = 16;
+ break;
+ case HIP_ESP_3DES_SHA1:
+ ret = 24;
+ break;
+ case HIP_ESP_NULL_SHA1:
+ case HIP_ESP_NULL_NULL:
+ ret = 0;
+ break;
+ default:
+ HIP_ERROR("unknown tid=%d\n", tid);
+ HIP_ASSERT(0);
+ break;
+ }
+
+ return ret;
+}
+
+
+int hip_hmac_key_length(int tid)
+{
+ int ret = -1;
+ switch(tid) {
+ case HIP_ESP_AES_SHA1:
+ // ret = 16;
+ // break;
+ case HIP_ESP_3DES_SHA1:
+ case HIP_ESP_NULL_SHA1:
+ ret = 20;
+ break;
+ case HIP_ESP_NULL_NULL:
+ ret = 0;
+ break;
+ default:
+ HIP_ERROR("unknown tid=%d\n", tid);
+ HIP_ASSERT(0);
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * hip_transform_key_length - get transform key length of a transform
+ * @tid: transform
+ *
+ * Returns: the transform key length based on the chosen transform,
+ * otherwise < 0 on error.
+ */
+int hip_transform_key_length(int tid)
+{
+ int ret = -1;
+
+ switch(tid) {
+ case HIP_HIP_AES_SHA1:
+ ret = 16;
+ break;
+ case HIP_HIP_3DES_SHA1:
+ ret = 24;
+ break;
+ case HIP_HIP_NULL_SHA1: // XX FIXME: SHOULD BE NULL_SHA1?
+ ret = 0;
+ break;
+ default:
+ HIP_ERROR("unknown tid=%d\n", tid);
+ HIP_ASSERT(0);
+ break;
+ }
+
+ return ret;
+}
+
+
+/**
+ * hip_auth_key_length_esp - get authentication key length of a transform
+ * @tid: transform
+ *
+ * Returns: the authentication key length based on the chosen transform.
+ * otherwise < 0 on error.
+ */
+int hip_auth_key_length_esp(int tid)
+{
+ int ret = -1;
+
+ switch(tid) {
+ case HIP_ESP_AES_SHA1:
+ //ret = 16;
+ //break;
+ case HIP_ESP_NULL_SHA1:
+ case HIP_ESP_3DES_SHA1:
+ ret = 20;
+ break;
+ case HIP_ESP_NULL_NULL:
+ ret = 0;
+ break;
+ default:
+ HIP_ERROR("unknown tid=%d\n", tid);
+ HIP_ASSERT(0);
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * hip_store_base_exchange_keys - store the keys negotiated in base exchange
+ * @ctx: the context inside which the key data will copied around
+ * @is_initiator: true if the localhost is the initiator, or false if
+ * the localhost is the responder
+ *
+ * Returns: 0 if everything was stored successfully, otherwise < 0.
+ */
+int hip_store_base_exchange_keys(struct hip_hadb_state *entry,
+ struct hip_context *ctx, int is_initiator)
+{
+ int err = 0;
+ int hmac_key_len, enc_key_len, auth_key_len;
+
+ hmac_key_len = hip_hmac_key_length(entry->esp_transform);
+ enc_key_len = hip_enc_key_length(entry->esp_transform);
+ auth_key_len = hip_auth_key_length_esp(entry->esp_transform);
+
+ memcpy(&entry->hip_hmac_out, &ctx->hip_hmac_out, hmac_key_len);
+ memcpy(&entry->hip_hmac_in, &ctx->hip_hmac_in, hmac_key_len);
+
+ memcpy(&entry->esp_in.key, &ctx->esp_in.key, enc_key_len);
+ memcpy(&entry->auth_in.key, &ctx->auth_in.key, auth_key_len);
+
+ memcpy(&entry->esp_out.key, &ctx->esp_out.key, enc_key_len);
+ memcpy(&entry->auth_out.key, &ctx->auth_out.key, auth_key_len);
+
+ hip_update_entry_keymat(entry, ctx->current_keymat_index,
+ ctx->keymat_calc_index, ctx->current_keymat_K);
+
+ if (entry->dh_shared_key) {
+ HIP_DEBUG("kfreeing old dh_shared_key\n");
+ kfree(entry->dh_shared_key);
+ }
+
+ entry->dh_shared_key_len = 0;
+ /* todo: reuse pointer, no kmalloc */
+ entry->dh_shared_key = kmalloc(ctx->dh_shared_key_len, GFP_ATOMIC);
+ if (!entry->dh_shared_key) {
+ HIP_ERROR("entry dh_shared kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ entry->dh_shared_key_len = ctx->dh_shared_key_len;
+ memcpy(entry->dh_shared_key, ctx->dh_shared_key, entry->dh_shared_key_len);
+ _HIP_HEXDUMP("Entry DH SHARED", entry->dh_shared_key, entry->dh_shared_key_len);
+ _HIP_HEXDUMP("Entry Kn", entry->current_keymat_K, HIP_AH_SHA_LEN);
+ return err;
+
+ out_err:
+ if (entry->dh_shared_key)
+ kfree(entry->dh_shared_key);
+
+ return err;
+}
+
+/**
+ * hip_select_hip_transform - select a HIP transform to use
+ * @ht: HIP_TRANSFORM payload where the transform is selected from
+ *
+ * Returns: the first acceptable Transform-ID, otherwise < 0 if no
+ * acceptable transform was found. The return value is in host byte order.
+ */
+hip_transform_suite_t hip_select_hip_transform(struct hip_hip_transform *ht)
+{
+ hip_transform_suite_t tid = 0;
+ int i;
+ int length;
+ hip_transform_suite_t *suggestion;
+
+ length = ntohs(ht->length);
+ suggestion = (hip_transform_suite_t *) &ht->suite_id[0];
+
+ if ( (length >> 1) > 6) {
+ HIP_ERROR("Too many transforms (%d)\n", length >> 1);
+ goto out;
+ }
+
+ for (i=0; isuite_id[0];
+
+ if (length > sizeof(struct hip_esp_transform) -
+ sizeof(struct hip_common)) {
+ HIP_ERROR("Too many transforms\n");
+ goto out;
+ }
+
+ for (i=0; i> 3;
+ iv_copy = kmalloc(16, GFP_KERNEL);
+ if (!iv_copy) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memcpy(iv_copy, iv, 16);
+ break;
+ case HIP_HIP_3DES_SHA1:
+ impl = impl_3des_cbc;
+ key_len = ESP_3DES_KEY_BITS >> 3;
+ iv_copy = kmalloc(8, GFP_KERNEL);
+ if (!iv_copy) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memcpy(iv_copy, iv, 8);
+ break;
+ case HIP_HIP_NULL_SHA1:
+ impl = impl_null;
+ key_len = 0;
+ iv_copy = NULL;
+ break;
+ default:
+ HIP_ERROR("Attempted to use unknown CI (enc_alg=%d)\n",
+ enc_alg);
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ _HIP_DEBUG("Mapping virtual to pages\n");
+
+ err = hip_map_virtual_to_pages(src_sg, &src_nsg, result, enc_len);
+ if (err || src_nsg < 1) {
+ HIP_ERROR("Error mapping source data\n");
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ _HIP_DEBUG("Mapping virtual to pages successful\n");
+
+ /* we will write over the source */
+ err = crypto_cipher_setkey(impl, enc_key, key_len);
+ if (err) {
+ if (impl->crt_flags & CRYPTO_TFM_RES_BAD_KEY_SCHED) {
+ HIP_ERROR("key is weak.\n");
+ HIP_HEXDUMP("key", enc_key, key_len);
+ }
+ HIP_ERROR("Could not set encryption/decryption key\n");
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ HIP_DEBUG("enc_len=%d\n", enc_len);
+ switch(direction) {
+ case HIP_DIRECTION_ENCRYPT:
+ if (iv_copy) {
+ err = crypto_cipher_encrypt_iv(impl, src_sg, src_sg,
+ enc_len, iv_copy);
+ /* The encrypt function writes crap on iv */
+ //memset(iv, 0, 8);
+ } else {
+ err = crypto_cipher_encrypt(impl, src_sg, src_sg,
+ enc_len);
+ }
+ if (err) {
+ HIP_ERROR("Encryption failed\n");
+ //err = -EFAULT;
+ goto out_err;
+ }
+
+ break;
+ case HIP_DIRECTION_DECRYPT:
+ if (iv_copy) {
+ err = crypto_cipher_decrypt_iv(impl, src_sg, src_sg,
+ enc_len, iv_copy);
+ } else {
+ err = crypto_cipher_decrypt(impl, src_sg, src_sg,
+ enc_len);
+ }
+ if (err) {
+ HIP_ERROR("Decryption failed\n");
+ //err = -EFAULT;
+ goto out_err;
+ }
+ break;
+ default:
+ HIP_ERROR("Undefined direction (%d)\n", direction);
+ err = -EINVAL;
+ break;
+ }
+
+ memcpy(data, result, enc_len);
+
+ out_err:
+ if (iv_copy)
+ kfree(iv_copy);
+ if (result)
+ kfree(result);
+ return err;
+}
+
+
+/**
+ * hip_unknown_spi - handle an unknown SPI by sending R1
+ * @daddr: destination IPv6 address of the R1 to be sent
+ *
+ * IPsec input code calls this when it does not know about the SPI
+ * received. We reply by sending a R1 containing NULL destination HIT
+ * to the peer which sent the packet containing the unknown SPI.
+ *
+ * No we don't anymore :) [if this is in draft, then the impl. is now
+ * officially broken].
+ */
+void hip_unknown_spi(struct sk_buff *skb, uint32_t spi)
+{
+ if (!hip_is_hit(&(skb->nh.ipv6h->saddr)))
+ return;
+
+ /* draft: If the R1 is a response to an ESP packet with an unknown
+ SPI, the Initiator HIT SHOULD be zero. */
+ HIP_DEBUG("Received Unknown SPI: 0x%x\n", ntohl(spi));
+ HIP_DEBUG("TODO: rekey old SA ?\n"); /* and/or TODO: send NOTIFY ? */
+ return;
+#if 0
+ /* We cannot know the destination HIT */
+ err = hip_xmit_r1(skb, NULL);
+ if (err) {
+ HIP_ERROR("hip_xmit_r1 failed (%d)\n", err);
+ }
+#endif
+}
+
+/**
+ * hip_init_sock - initialize HIP control socket
+ *
+ * Returns: 0 if successful, else < 0.
+ */
+static int hip_init_output_socket(void)
+{
+ int err = 0;
+ struct ipv6_pinfo *np;
+
+ err = sock_create(AF_INET6, SOCK_RAW, IPPROTO_NONE, &hip_output_socket);
+ if (err) {
+ HIP_ERROR("Failed to allocate the HIP control socket (err=%d)\n", err);
+ goto out;
+ }
+
+ /* prevent multicast packets sent out coming back to us */
+ np = inet6_sk(hip_output_socket->sk);
+ if (!np) {
+ HIP_ERROR("Could not get inet6 sock of HIP control socket\n");
+ err = -EFAULT;
+ goto out;
+ } else {
+ np->mc_loop = 0;
+ }
+ /* TODO: same for IPv4 ? */
+ out:
+ return err;
+}
+
+/**
+ * hip_uninit_sock - uninitialize HIP control socket
+ */
+void hip_uninit_output_socket(void)
+{
+ sock_release(hip_output_socket);
+ return;
+}
+
+/**
+ * hip_get_addr - get an IPv6 address of given HIT
+ * @hit: HIT of which IPv6 address is to be copied
+ * @addr: where the IPv6 address is copied to
+ *
+ * Returns: 1 if successful (a peer address was copied to @addr),
+ * else 0.
+ */
+int hip_get_addr(hip_hit_t *hit, struct in6_addr *addr)
+{
+ hip_ha_t *entry;
+ char str[INET6_ADDRSTRLEN];
+
+ if (!hip_is_hit(hit))
+ return 0;
+
+ hip_in6_ntop(hit,str);
+
+ entry = hip_hadb_find_byhit(hit);
+ if (!entry) {
+ HIP_ERROR("Unknown HIT: %s\n", str);
+ return 0;
+ }
+
+ if (hip_hadb_get_peer_addr(entry, addr) < 0) {
+ hip_put_ha(entry);
+ return 0;
+ }
+ hip_put_ha(entry);
+
+ hip_in6_ntop(addr, str);
+ _HIP_DEBUG("selected dst addr: %s\n", str);
+
+ return 1;
+}
+
+
+/**
+ * hip_get_hits - get this host's HIT to be used in source HIT
+ * @hitd: destination HIT
+ * @hits: where the selected source HIT is to be stored
+ *
+ * Returns: 1 if source HIT was copied successfully, otherwise 0.
+ */
+int hip_get_hits(struct in6_addr *hitd, struct in6_addr *hits)
+{
+ if (!ipv6_addr_is_hit(hitd))
+ goto out;
+
+ if (hip_copy_any_localhost_hit(hits) < 0)
+ goto out;
+
+ return 1;
+
+ out:
+ return 0;
+}
+
+/**
+ * hip_get_saddr - get source HIT
+ * @fl: flow containing the destination HIT
+ * @hit_storage: where the source address is to be stored
+ *
+ * ip6_build_xmit() calls this if we have a source address that is not
+ * a HIT and destination address which is a HIT. If that is true, we
+ * make the source a HIT too.
+ */
+int hip_get_saddr(struct flowi *fl, struct in6_addr *hit_storage)
+{
+ hip_ha_t *entry;
+
+ if (!ipv6_addr_is_hit(&fl->fl6_dst)) {
+ HIP_ERROR("dst not a HIT\n");
+ return 0;
+ }
+
+ entry = hip_hadb_find_byhit((hip_hit_t *)&fl->fl6_dst);
+ if (!entry) {
+ HIP_ERROR("Unknown destination HIT\n");
+ return 0;
+ }
+
+ ipv6_addr_copy(hit_storage, &entry->hit_our);
+ hip_put_ha(entry);
+
+ return 1;
+}
+
+/**
+ * hip_get_load_time - set time when this module was loaded into the kernel
+ */
+static void hip_get_load_time(void)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+ load_time = tv.tv_sec;
+ _HIP_DEBUG("load_time=0x%lx\n", load_time);
+ return;
+}
+
+
+/* base exchange IPv6 addresses need to be put into ifindex2spi map,
+ * so a function is needed which gets the ifindex of the network
+ * device which has the address @addr */
+int hip_ipv6_devaddr2ifindex(struct in6_addr *addr)
+{
+ int ifindex = 0;
+ struct inet6_ifaddr *ifp = ipv6_get_ifaddr(addr, NULL, 1);
+ if (ifp) {
+ ifindex = ifp->idev->dev->ifindex;
+ in6_ifa_put(ifp);
+ }
+ return ifindex;
+}
+
+/* Returns 1 if address can be added into REA parameter. Currently all
+ * other than link locals are accepted. */
+static inline int hip_rea_addr_ok(struct in6_addr *addr)
+{
+ if (ipv6_addr_type(addr) & IPV6_ADDR_LINKLOCAL)
+ return 0;
+ return 1;
+}
+
+
+/**
+ * hip_create_device_addrlist - get interface addresses
+ * @event_dev: network device of which addresses are retrieved from
+ * @addr_list: where the pointer to address list is stored
+ * @idev_addr_count: number of addresses in @addr_list
+ *
+ * Caller is responsible for kfreeing @addr_list.
+ *
+ * Returns: 0 if addresses were retrieved successfully. If there was
+ * no error but interface has no IPv6 addresses, @addr_list is NULL
+ * and 0 is returned. Else < 0 is returned and @addr_list contains
+ * NULL.
+ */
+static int hip_create_device_addrlist(struct net_device *event_dev,
+ struct hip_rea_info_addr_item **addr_list,
+ int *idev_addr_count)
+{
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa = NULL;
+ char addrstr[INET6_ADDRSTRLEN];
+ int i = 0;
+ int err = 0;
+ struct hip_rea_info_addr_item *tmp_list = NULL;
+ int n_addrs = 0;
+
+ *idev_addr_count = 0;
+
+ read_lock(&addrconf_lock);
+ idev = in6_dev_get(event_dev);
+ if (!idev) {
+ HIP_DEBUG("event_dev has no IPv6 addrs, returning\n");
+ goto out;
+ }
+ read_lock(&idev->lock);
+
+ for (ifa = idev->addr_list; ifa; ifa = ifa->if_next) {
+ spin_lock_bh(&ifa->lock);
+ hip_in6_ntop(&ifa->addr, addrstr);
+ HIP_DEBUG("addr %d: %s flags=0x%x valid_lft=%u jiffies-ifa_timestamp=%lu\n",
+ n_addrs+1, addrstr, ifa->flags,
+ ifa->valid_lft, (jiffies-ifa->tstamp)/HZ);
+ if (!hip_rea_addr_ok(&ifa->addr)) {
+ HIP_DEBUG("address not accepted into REA\n");
+ } else
+ n_addrs++;
+ spin_unlock_bh(&ifa->lock);
+ }
+ HIP_DEBUG("address list count=%d\n", n_addrs);
+
+ if (n_addrs > 0) {
+ /* create address list for building of REA */
+ tmp_list = kmalloc(n_addrs * sizeof(struct hip_rea_info_addr_item), GFP_ATOMIC);
+ if (!tmp_list) {
+ HIP_DEBUG("addr_list creation failed\n");
+ err = -ENOMEM;
+ goto out_in6_unlock;
+ }
+
+ for (i = 0, ifa = idev->addr_list;
+ ifa && i < n_addrs; ifa = ifa->if_next, i++) {
+ spin_lock_bh(&ifa->lock);
+ if (hip_rea_addr_ok(&ifa->addr)) {
+ ipv6_addr_copy(&tmp_list[i].address, &ifa->addr);
+ /* lifetime: select prefered_lft or valid_lft ? */
+ tmp_list[i].lifetime = htonl(ifa->valid_lft); /* or: (jiffies-ifp->tstamp)/HZ ? */
+ if (i == 0)
+ tmp_list[i].reserved = htonl(1 << 31); /* for testing preferred address */
+ else
+ tmp_list[i].reserved = 0;
+ } else {
+ HIP_DEBUG("not adding link local address\n");
+ }
+ spin_unlock_bh(&ifa->lock);
+ }
+ }
+
+ *addr_list = tmp_list;
+ *idev_addr_count = n_addrs;
+
+ out_in6_unlock:
+ read_unlock(&idev->lock);
+ in6_dev_put(idev);
+ out:
+ read_unlock(&addrconf_lock);
+ return err;
+}
+
+/* helper function for creating and initializing work order for
+ * network events */
+struct hip_work_order *hip_net_event_prepare_hwo(int subtype,
+ int ifindex, int event)
+{
+ struct hip_work_order *hwo;
+
+ hwo = hip_init_job(GFP_ATOMIC);
+ if (!hwo)
+ return NULL;
+
+ hwo->type = HIP_WO_TYPE_MSG;
+ hwo->subtype = subtype;
+ hwo->arg1 = (void *)ifindex;
+ hwo->arg2 = (void *)event;
+ hwo->destructor = NULL;
+ return hwo;
+}
+
+/**
+ * hip_handle_ipv6_dad_completed - handle IPv6 address events
+ * @ifindex: the ifindex of the interface which caused the event
+ *
+ * This function gets notification when DAD procedure has finished
+ * successfully for an address in the network device @ifindex.
+ */
+void hip_handle_ipv6_dad_completed(int ifindex) {
+ struct hip_work_order *hwo;
+
+ HIP_DEBUG("ifindex=%d\n", ifindex);
+ if (!ifindex) {
+ HIP_ERROR("no ifindex\n");
+ return;
+ }
+
+ hwo = hip_net_event_prepare_hwo(HIP_WO_SUBTYPE_IN6_EVENT,
+ ifindex, NETDEV_UP);
+ if (!hwo) {
+ HIP_ERROR("Unable to handle address event\n");
+ } else
+ hip_insert_work_order(hwo);
+ return;
+}
+
+#define EVENTSRC_INET6 0
+#define EVENTSRC_NETDEV 1
+
+#define SEND_UPDATE_NES (1 << 0)
+#define SEND_UPDATE_REA (1 << 1)
+/** hip_net_event - start handling the network device event
+ * @ifindex: the device which caused the event
+ * @event_src: 0 for IPv6 address events and 1 for network device related events
+ * @event: event type, NETDEV_UP or NETDEV_DOWN
+ *
+ * Workqueue runs this function when it is assigned a job related to
+ * networking events.
+ */
+static void hip_net_event(int ifindex, uint32_t event_src, uint32_t event)
+{
+ int err = 0;
+ struct net_device *event_dev;
+ int idev_addr_count = 0;
+ struct hip_rea_info_addr_item *addr_list = NULL;
+
+ HIP_DEBUG("\n");
+
+ if (! (event_src == EVENTSRC_INET6 || event_src == EVENTSRC_NETDEV) ) {
+ HIP_ERROR("unknown event source %d\n", event_src);
+ return;
+ }
+
+ event_dev = dev_get_by_index(ifindex);
+ if (!event_dev) {
+ HIP_DEBUG("Network interface (ifindex=%d) does not exist anymore\n",
+ ifindex);
+ return;
+ }
+ /* dev_get_by_index does a dev_hold */
+
+ HIP_DEBUG("event_src=%s dev=%s ifindex=%d event=%u\n",
+ event_src == EVENTSRC_INET6 ? "inet6" : "netdev",
+ event_dev->name, ifindex, event);
+
+ /* Skip events caused by loopback devices (as long as we do
+ * not have loopback support). TODO: skip tunnels etc. */
+ if (event_dev->flags & IFF_LOOPBACK) {
+ HIP_DEBUG("ignoring event from loopback device\n");
+ dev_put(event_dev);
+ return;
+ }
+
+ err = hip_create_device_addrlist(event_dev, &addr_list, &idev_addr_count);
+ dev_put(event_dev);
+
+ if (err) {
+ HIP_ERROR("hip_create_device_addrlist failed, err=%d\n", err);
+ } else {
+ /* send UPDATEs if there are addresses to be informed to the peers */
+ //if (idev_addr_count > 0 && addr_list)
+ hip_send_update_all(addr_list, idev_addr_count, ifindex, SEND_UPDATE_REA);
+ //else
+ //HIP_DEBUG("Netdev has no addresses to be informed, UPDATE not sent\n");
+ }
+
+ if (addr_list)
+ kfree(addr_list);
+}
+
+/**
+ * hip_handle_inet6_addr_del - handle IPv6 address deletion events
+ * @ifindex: the interface index of the network device which caused the event
+ */
+void hip_handle_inet6_addr_del(int ifindex) {
+ struct hip_work_order *hwo;
+
+ HIP_DEBUG("ifindex=%d\n", ifindex);
+
+ hwo = hip_net_event_prepare_hwo(HIP_WO_SUBTYPE_IN6_EVENT,
+ ifindex, NETDEV_DOWN);
+ if (!hwo) {
+ HIP_ERROR("Unable to handle address event\n");
+ goto out;
+ }
+ hip_insert_work_order(hwo);
+
+ out:
+ return;
+}
+
+
+/**
+ * hip_netdev_event_handler - handle network device events
+ * @notifier_block: device notifier chain
+ * @event: the event
+ * @ptr: pointer to the network device which caused the event
+ *
+ * Currently we handle only NETDEV_DOWN and NETDEV_UNREGISTER.
+ *
+ * Returns: always %NOTIFY_DONE.
+ */
+static int hip_netdev_event_handler(struct notifier_block *notifier_block,
+ unsigned long event, void *ptr)
+{
+ struct net_device *event_dev;
+ struct hip_work_order *hwo;
+
+ if (! (event == NETDEV_DOWN || event == NETDEV_UNREGISTER)) {
+ _HIP_DEBUG("Ignoring event %lu\n", event);
+ return NOTIFY_DONE;
+ }
+
+ HIP_DEBUG("got event NETDEV_%s\n", event == NETDEV_DOWN ? "DOWN" : "UNREGISTER");
+
+ if (event == NETDEV_UNREGISTER) {
+ /* avoid sending rapidly consecutive UPDATEs */
+ HIP_DEBUG("not handling UNREGISTER event, assuming already handled DOWN\n");
+ return NOTIFY_DONE;
+ }
+
+ event_dev = (struct net_device *) ptr;
+ if (!event_dev) {
+ HIP_ERROR("NULL event_dev, shouldn't happen ?\n");
+ return NOTIFY_DONE;
+ }
+ dev_hold(event_dev);
+
+ hwo = hip_net_event_prepare_hwo(HIP_WO_SUBTYPE_DEV_EVENT,
+ event_dev->ifindex, event);
+ if (!hwo) {
+ HIP_ERROR("Unable to handle address event\n");
+ goto out;
+ }
+ hip_insert_work_order(hwo);
+
+ out:
+ dev_put(event_dev);
+ return NOTIFY_DONE;
+}
+
+
+/**
+ * hip_err_handler - ICMPv6 handler
+ * @skb: received ICMPv6 packet
+ * @opt: todo
+ * @type: ICMP type
+ * @code: ICMP code
+ * @offset: offset from the start of the IPv6 header (?)
+ * @info: information related to type and code
+ *
+ * ICMP errors caused by HIP packets are handled by this function.
+ */
+static void hip_err_handler(struct sk_buff *skb, struct inet6_skb_parm *opt,
+ int type, int code, int offset, __u32 info)
+{
+ struct icmp6hdr *hdr;
+ struct ipv6hdr *invoking_hdr; /* RFC 2463 sec 3.1 */
+ struct in6_addr *saddr, *daddr;
+ char str[INET6_ADDRSTRLEN];
+
+ /* todo: option to allow/disallow icmpv6 handling */
+ if (!pskb_may_pull(skb, 4+sizeof(struct ipv6hdr))) {
+ /* already checked in icmpv6_rcv/icmpv6_notify ? */
+ /* RFC 2463 sec 3.1 */
+ HIP_DEBUG("Too short an ICMP packet (skb len=%d)\n", skb->len);
+ return;
+ }
+
+ hdr = (struct icmp6hdr *) skb->h.raw;
+ invoking_hdr = (struct ipv6hdr *) (hdr+1); /* check */
+
+ saddr = &skb->nh.ipv6h->saddr;
+ daddr = &skb->nh.ipv6h->daddr;
+ hip_in6_ntop(saddr, str);
+ HIP_DEBUG("icmp6: outer hdr src=%s\n", str);
+ hip_in6_ntop(daddr, str);
+ HIP_DEBUG("icmp6: outer hdr dst=%s\n", str);
+ HIP_DEBUG("icmp6: type=%d code=%d offset=%d info=%u skb->len=%d\n",
+ type, code, offset, info, skb->len);
+ HIP_DEBUG("dev=%s input_dev=%s real_dev=%s\n",
+ skb->dev ? skb->dev->name : "null",
+ skb->input_dev ? skb->input_dev->name : "null",
+ skb->real_dev ? skb->real_dev->name : "null");
+ hip_in6_ntop(&invoking_hdr->saddr, str);
+ HIP_DEBUG("invoking ip6 hdr: src=%s\n", str);
+ hip_in6_ntop(&invoking_hdr->daddr, str);
+ HIP_DEBUG("invoking ip6 hdr: dst=%s\n", str);
+
+ HIP_DEBUG("invoking pkt remaining len=%d\n", skb->len-offset);
+ HIP_DEBUG("invoking pkt nexthdr=%d\n", invoking_hdr->nexthdr);
+
+ if (invoking_hdr->nexthdr != IPPROTO_HIP) {
+ HIP_ERROR("invoking pkt hext header is not a HIP packet, not handling\n");
+ return;
+ }
+
+ switch (type) {
+ case ICMPV6_DEST_UNREACH:
+ HIP_DEBUG("got DEST_UNREACH\n");
+ switch(code) {
+ case ICMPV6_NOROUTE:
+ case ICMPV6_ADM_PROHIBITED:
+ case ICMPV6_ADDR_UNREACH:
+ HIP_DEBUG("TODO: handle ICMP DU code %d\n", code);
+ /* todo: deactivate invoking_hdr->daddr from every sdb
+ * entry peer addr list */
+ break;
+ default:
+ HIP_DEBUG("ICMP DU code %d not handled\n", code);
+ break;
+ }
+ break;
+ case ICMPV6_PARAMPROB:
+ HIP_DEBUG("got PARAMPROB code=%d\n", code);
+ break;
+ case ICMPV6_TIME_EXCEED:
+ HIP_DEBUG("got TIME_EXCEED code=%d\n", code);
+ break;
+ default:
+ HIP_DEBUG("unhandled type %d code=%d\n", type, code);
+ }
+
+ return;
+}
+
+/* Handler which is notified when SA state has changed.
+ TODO: send UPDATE when SA lifetime has expired or is about to expire. */
+static int hip_xfrm_handler_notify(struct xfrm_state *x, int hard)
+{
+ HIP_DEBUG("SPI=0x%x hard expiration=%d state=%d\n",
+ ntohl(x->id.spi), hard, x->km.state);
+ HIP_DEBUG("TODO..send UPDATE ?\n");
+#if 0
+ if (SA is HIP SA) {
+ hip_ha_t *entry;
+
+ /* todo: zero the spi from hadb */
+ /* was this event caused by inbound SA ?*/
+ entry = hip_hadb_find_byspi(ntohl(x->id.spi));
+ if (entry) {
+ hip_send_update(entry, NULL, 0, 0, 0); /* non-mm UPDATE */
+ hip_ha_put(entry);
+ } else {
+ /* check if SA was outbound .. */
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/* This function is called when XFRM key manager does not know SA for
+ * given destination address. If the destination address is a HIT we
+ * must trigger base exchange to that HIT.
+ *
+ * Also it seems that we get here if the IPsec SA has expired and we
+ * are trying to send HIP traffic to that SA.
+ */
+static int hip_xfrm_handler_acquire(struct xfrm_state *xs,
+ struct xfrm_tmpl *xtmpl,
+ struct xfrm_policy *pol, int dir)
+{
+ int err = -EINVAL;
+ char str[INET6_ADDRSTRLEN];
+ struct ipv6hdr hdr = {0};
+
+ hip_in6_ntop((struct in6_addr *) &(xs->id.daddr), str);
+ HIP_DEBUG("daddr=%s dir=%d\n", str, dir);
+
+ if (! (pol->selector.daddr.a6[0] == htonl(0x40000000) &&
+ pol->selector.prefixlen_d == 2)) {
+ hip_in6_ntop((struct in6_addr *) &(pol->selector.daddr), str);
+ HIP_ERROR("Policy (pol daddr=%s) is not for HIP, returning\n",
+ str);
+ goto out;
+ }
+
+ if (!hip_is_hit((struct in6_addr *) &(xs->id.daddr))) {
+ HIP_ERROR("%s not a HIT\n", str);
+ goto out;
+ }
+
+ ipv6_addr_copy(&hdr.daddr, (struct in6_addr *) &(xs->id.daddr));
+ err = hip_handle_output(&hdr, NULL);
+ if (err)
+ HIP_ERROR("TODO: handle err=%d\n", err);
+ err = 0; /* tell XFRM that we handle this SA acquiring even
+ * if the previous failed */
+
+out:
+ HIP_DEBUG("returning, err=%d\n", err);
+ return err;
+}
+
+/* Called when policy is expired */
+static int hip_xfrm_handler_policy_notify(struct xfrm_policy *xp,
+ int dir, int hard)
+{
+ HIP_DEBUG("xp=0x%p dir=%d hard expiration=%d\n", xp, dir, hard);
+ HIP_DEBUG("TODO..\n");
+ return 0;
+}
+
+/* Callbacks for HIP related IPsec SA management functions */
+static struct xfrm_mgr hip_xfrm_km_mgr = {
+ .id = "HIP",
+ .notify = hip_xfrm_handler_notify,
+ .acquire = hip_xfrm_handler_acquire,
+ .notify_policy = hip_xfrm_handler_policy_notify
+ /* .compile_policy = hip_xfrm_handler_compile_policy, not needed ? */
+};
+
+/* Register handler for XFRM key management calls */
+int hip_register_xfrm_km_handler(void)
+{
+ int err;
+
+ HIP_DEBUG("registering XFRM key management handler\n");
+ err = xfrm_register_km(&hip_xfrm_km_mgr);
+ if (err)
+ HIP_DEBUG("Registration of XFRM km handler failed, err=%d\n", err);
+ return err;
+}
+
+/**
+ * hip_init_cipher - initialize needed cipher algorithms
+ * There is no need to delay initialization and locking of these
+ * algorithms since we require their use. Some optional algorithms
+ * may be initialized later.
+ * Currently we use AES, 3DES-CBC, NULL-ECB?, SHA1(+HMAC), DSA, DH
+ * Returns: 1 if all algorithms were initialized, otherwise < 0.
+ */
+static int hip_init_cipher(void)
+{
+ int err = 0;
+ u32 supported_groups;
+
+ /* instruct the "IPsec" to check for available algorithms */
+ xfrm_probe_algs();
+
+ /* Get implementations for all the ciphers we support */
+ impl_aes_cbc = crypto_alloc_tfm("aes", CRYPTO_TFM_MODE_CBC);
+ if (!impl_aes_cbc) {
+ HIP_ERROR("Unable to register AES cipher\n");
+ err = -1;
+ goto out_err;
+ }
+
+ /* Get implementations for all the ciphers we support */
+ impl_3des_cbc = crypto_alloc_tfm("des3_ede", CRYPTO_TFM_MODE_CBC);
+ if (!impl_3des_cbc) {
+ HIP_ERROR("Unable to register 3DES cipher\n");
+ err = -1;
+ goto out_err;
+ }
+
+ impl_null = crypto_alloc_tfm("cipher_null", CRYPTO_TFM_MODE_ECB);
+ if (!impl_null) {
+ HIP_ERROR("Unable to register NULL cipher\n");
+ err = -1;
+ goto out_err;
+ }
+
+ HIP_DEBUG("Initializing SHA1\n");
+ schedule();
+
+ impl_sha1 = crypto_alloc_tfm("sha1", 0);
+ if (!impl_sha1) {
+ HIP_ERROR("Unable to register SHA1 digest\n");
+ err = -1;
+ goto out_err;
+ }
+
+ HIP_DEBUG("SHA1 initialized\n");
+ schedule();
+
+ supported_groups = (1 << HIP_DH_OAKLEY_1 |
+ 1 << HIP_DH_OAKLEY_5 |
+ 1 << HIP_DH_384);
+
+ /* Does not return errors. Should it?
+ the code will try to regenerate the key if it is
+ missing...
+ */
+ HIP_DEBUG("Generating DH keys\n");
+ schedule();
+
+ hip_regen_dh_keys(supported_groups);
+
+ return 0;
+
+ out_err:
+ hip_uninit_cipher();
+ return err;
+}
+
+/**
+ * hip_uninit_cipher - uninitialize needed cipher algorithms
+ *
+ * Actually this does nothing because it looks like the cipher
+ * implementations do not require freeing, although it seems possible
+ * to unregister them.
+ */
+static void hip_uninit_cipher(void)
+{
+ int i;
+ /*
+ * jlu XXX: If I understand correctly, the implementations do
+ * not require freeing, although it seems possible to unregister them...
+ * Really weird. Something is broken somewhere.
+ */
+ for(i=1;itype, job->subtype);
+
+ switch (job->type) {
+ case HIP_WO_TYPE_INCOMING:
+ switch(job->subtype) {
+ case HIP_WO_SUBTYPE_RECV_I1:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_i1(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"I1");
+ break;
+ case HIP_WO_SUBTYPE_RECV_R1:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_r1(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"R1");
+ break;
+ case HIP_WO_SUBTYPE_RECV_I2:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_i2(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"I2");
+ break;
+ case HIP_WO_SUBTYPE_RECV_R2:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_r2(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"R2");
+ KRISU_STOP_TIMER(KMM_GLOBAL,"Base Exchange");
+ break;
+ case HIP_WO_SUBTYPE_RECV_UPDATE:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_update(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"UPDATE");
+ break;
+ case HIP_WO_SUBTYPE_RECV_NOTIFY:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_notify(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"NOTIFY");
+ break;
+ case HIP_WO_SUBTYPE_RECV_BOS:
+ KRISU_START_TIMER(KMM_PARTIAL);
+ res = hip_receive_bos(job->arg1);
+ KRISU_STOP_TIMER(KMM_PARTIAL,"BOS");
+ break;
+ default:
+ HIP_ERROR("Unknown subtype: %d (type=%d)\n",
+ job->subtype, job->type);
+ break;
+ }
+ if (res < 0)
+ res = KHIPD_ERROR;
+ break;
+ case HIP_WO_TYPE_OUTGOING:
+ HIP_INFO("Nothing for outgoing stuff\n");
+ break;
+ case HIP_WO_TYPE_MSG:
+ switch(job->subtype) {
+ case HIP_WO_SUBTYPE_IN6_EVENT:
+ hip_net_event((int)job->arg1, 0, (uint32_t) job->arg2);
+ res = KHIPD_OK;
+ break;
+ case HIP_WO_SUBTYPE_DEV_EVENT:
+ hip_net_event((int)job->arg1, 1, (uint32_t) job->arg2);
+ res = KHIPD_OK;
+ break;
+ case HIP_WO_SUBTYPE_ADDMAP:
+ /* arg1 = d-hit, arg2=ipv6 */
+ res = hip_hadb_add_peer_info(job->arg1, job->arg2);
+ if (res < 0)
+ res = KHIPD_ERROR;
+ break;
+ case HIP_WO_SUBTYPE_DELMAP:
+ /* arg1 = d-hit arg2=d-ipv6 */
+ res = hip_del_peer_info(job->arg1, job->arg2);
+ if (res < 0)
+ res = KHIPD_ERROR;
+ break;
+#ifdef CONFIG_HIP_RVS
+ case HIP_WO_SUBTYPE_ADDRVS:
+ /* arg1 = d-hit, arg2=ipv6 */
+ res = hip_hadb_add_peer_info(job->arg1, job->arg2);
+ if (res < 0)
+ res = KHIPD_ERROR;
+ hip_rvs_set_request_flag(job->arg1);
+ {
+ struct ipv6hdr hdr = {0};
+ ipv6_addr_copy(&hdr.daddr, job->arg1);
+ hip_handle_output(&hdr, NULL);
+ }
+ res = 0;
+ break;
+#endif
+ case HIP_WO_SUBTYPE_FLUSHMAPS:
+ case HIP_WO_SUBTYPE_ADDHI:
+ case HIP_WO_SUBTYPE_DELHI:
+ case HIP_WO_SUBTYPE_FLUSHHIS:
+ case HIP_WO_SUBTYPE_NEWDH:
+ HIP_INFO("Not implemented subtype: %d (type=%d)\n",
+ job->subtype, job->type);
+ res = KHIPD_ERROR;
+ goto out_err;
+ default:
+ HIP_ERROR("Unknown subtype: %d on type: %d\n",job->subtype,job->type);
+ res = KHIPD_ERROR;
+ goto out_err;
+ }
+ }
+
+ out_err:
+ if (job)
+ hip_free_work_order(job);
+ return res;
+}
+
+/* HIP kernel thread, arg t is per-cpu thread data */
+static int hip_worker(void *t)
+{
+ int result = 0;
+ struct hip_kthread_data *thr = (struct hip_kthread_data *) t;
+ int cpu = thr->cpu;
+ pid_t pid;
+
+ /* set up thread */
+ thr->pid = pid = current->pid;
+ hip_init_workqueue();
+ atomic_inc(&hip_working);
+ daemonize("khipd/%d", cpu);
+ allow_signal(SIGKILL);
+ flush_signals(current);
+ set_cpus_allowed(current, cpumask_of_cpu(cpu));/* TODO: check return value */
+ //set_user_nice(current, 0); //XXX: Set this as you please
+
+ HIP_DEBUG("HIP kernel thread %s pid=%d started\n", current->comm, pid);
+
+ /* work loop */
+ while(1) {
+ if (signal_pending(current)) {
+ HIP_INFO("HIP thread pid %d got SIGKILL, cleaning up\n", pid);
+ /* zero thread pid so we do not kill other
+ * process having the same pid later by accident */
+ thr->pid = 0;
+ thr->killed = 1;
+ _HIP_DEBUG("signalled, flushing signals\n");
+ flush_signals(current);
+ break;
+ }
+
+ /* swsuspend, see eg. drivers/net/irda/sir_kthread.c */
+ if (current->flags & PF_FREEZE) {
+ HIP_DEBUG("handle swsuspend\n");
+ refrigerator(PF_FREEZE);
+ }
+
+ result = hip_do_work();
+ if (result < 0) {
+ if (result == KHIPD_ERROR)
+ HIP_INFO("Recoverable error occured (%d)\n", result);
+ else {
+ /* maybe we should just recover and continue ? */
+ HIP_INFO("Unrecoverable error occured (%d). Cleaning up\n",
+ result);
+ break;
+ }
+ }
+ HIP_DEBUG("Work done (pid=%d, cpu=%d)\n", pid, cpu);
+ }
+
+ /* cleanup and finish thread */
+ hip_uninit_workqueue();
+ atomic_dec(&hip_working);
+ HIP_DEBUG("HIP kernel thread %d exiting on cpu %d\n", pid, cpu);
+
+ /* plain complete and return seemed to cause random oopses */
+ complete_and_exit(&thr->kthread_work, 0);
+}
+
+
+static int __init hip_init(void)
+{
+ int cpu, pid;
+
+ HIP_INFO("Initializing HIP module\n");
+ hip_get_load_time();
+ hip_init_sys_config();
+
+ memset(&hip_kthreads, 0, sizeof(hip_kthreads));
+
+ if(!hip_init_r1())
+ goto out;
+
+ if (hip_init_output_socket() < 0)
+ goto out;
+
+ if (hip_init_cipher() < 0)
+ goto out;
+
+ hip_init_hadb();
+
+#ifdef CONFIG_HIP_RVS
+ hip_init_rvadb();
+#endif
+
+#ifdef CONFIG_PROC_FS
+ if (hip_init_procfs() < 0)
+ goto out;
+#endif /* CONFIG_PROC_FS */
+
+ if (hip_setup_sp(XFRM_POLICY_OUT) < 0)
+ goto out;
+
+ if (hip_setup_sp(XFRM_POLICY_IN) < 0)
+ goto out;
+
+ for(cpu = 0; cpu < num_possible_cpus(); cpu++) {
+ hip_kthreads[cpu].cpu = cpu;
+ init_completion(&hip_kthreads[cpu].kthread_work);
+ hip_kthreads[cpu].killed = 0;
+ pid = kernel_thread(hip_worker, &hip_kthreads[cpu],
+ CLONE_KERNEL | SIGCHLD);
+ if (IS_ERR(ERR_PTR(pid))) {
+ hip_kthreads[cpu].pid = 0;
+ HIP_ERROR("Failed to set up a kernel thread for HIP "
+ "(cpu=%d, ret=%d)\n", cpu, pid);
+ goto out;
+ }
+ }
+
+ HIP_SETCALL(hip_handle_output);
+ HIP_SETCALL(hip_handle_esp);
+ HIP_SETCALL(hip_get_addr);
+ HIP_SETCALL(hip_get_saddr);
+ HIP_SETCALL(hip_unknown_spi);
+ HIP_SETCALL(hip_handle_ipv6_dad_completed);
+ HIP_SETCALL(hip_handle_inet6_addr_del);
+ /* HIP_SETCALL(hip_update_spi_waitlist_ispending); */
+ HIP_SETCALL(hip_get_default_spi_out);
+
+ if (inet6_add_protocol(&hip_protocol, IPPROTO_HIP) < 0) {
+ HIP_ERROR("Could not add HIP protocol\n");
+ goto out;
+ }
+
+ if (hip_init_socket_handler() < 0)
+ goto out;
+
+
+ if (hip_init_netdev_notifier() < 0)
+ goto out;
+
+ if (hip_register_xfrm_km_handler()) {
+ HIP_ERROR("Could not register XFRM key manager for HIP\n");
+ goto out;
+ }
+#ifdef CONFIG_SYSCTL
+ if (!hip_register_sysctl()) {
+ HIP_ERROR("Could not register sysctl for HIP\n");
+ goto out;
+ }
+#endif
+ HIP_INFO("HIP module initialized successfully\n");
+ return 0;
+
+ out:
+ hip_cleanup();
+ HIP_ERROR("Failed to init module\n");
+ return -EINVAL;
+}
+
+/*
+ * We first invalidate the hooks, so that softirqs wouldn't enter them.
+ */
+static void __exit hip_cleanup(void)
+{
+ int i, pid, cpu;
+
+ HIP_INFO("Uninitializing HIP module\n");
+
+#ifdef CONFIG_SYSCTL
+ hip_unregister_sysctl();
+#endif
+ /* unregister XFRM km handler */
+ xfrm_unregister_km(&hip_xfrm_km_mgr);
+
+ /* disable callbacks for HIP packets and notifier chains */
+ inet6_del_protocol(&hip_protocol, IPPROTO_HIP);
+ hip_uninit_netdev_notifier();
+
+ /* disable hooks to call our code */
+ //HIP_INVALIDATE(hip_update_spi_waitlist_ispending);
+ HIP_INVALIDATE(hip_handle_ipv6_dad_completed);
+ HIP_INVALIDATE(hip_handle_inet6_addr_del);
+ HIP_INVALIDATE(hip_unknown_spi);
+ HIP_INVALIDATE(hip_get_saddr);
+ HIP_INVALIDATE(hip_get_addr);
+ HIP_INVALIDATE(hip_handle_esp);
+ HIP_INVALIDATE(hip_handle_output);
+ HIP_INVALIDATE(hip_get_default_spi_out);
+
+ /* kill kernel threads and wait for them to complete */
+ for(i = 0; i < num_possible_cpus(); i++) {
+ pid = hip_kthreads[i].pid;
+ cpu = hip_kthreads[i].cpu;
+ if (pid > 0) {
+ HIP_INFO("Stopping HIP kernel thread pid=%d on cpu=%d\n", pid, cpu);
+ kill_proc(pid, SIGKILL, 1);
+ schedule();
+ wait_for_completion(&hip_kthreads[i].kthread_work);
+ } else if (pid == 0) {
+ _HIP_DEBUG("Already killed HIP kernel thread on cpu=%d ?\n", cpu);
+ if (hip_kthreads[i].killed) {
+ HIP_DEBUG("Waiting killed thread to complete on cpu=%d\n", cpu);
+ wait_for_completion(&hip_kthreads[i].kthread_work);
+ }
+ } else
+ HIP_DEBUG("Invalid HIP kernel thread pid=%d on cpu=%d\n", pid, cpu);
+ }
+ HIP_DEBUG("All HIP threads finished\n");
+
+ hip_delete_sp(XFRM_POLICY_IN);
+ hip_delete_sp(XFRM_POLICY_OUT);
+
+#ifdef CONFIG_PROC_FS
+ hip_uninit_procfs();
+#endif /* CONFIG_PROC_FS */
+
+ hip_uninit_socket_handler();
+ hip_uninit_host_id_dbs();
+#ifdef CONFIG_HIP_RVS
+ hip_uninit_rvadb();
+#endif
+ hip_uninit_hadb();
+ hip_uninit_all_eid_db();
+ hip_uninit_output_socket();
+ hip_uninit_r1();
+
+ /* update_spi_waitlist_delete_all(); */
+ HIP_INFO("HIP module uninitialized successfully\n");
+ return;
+}
+
+MODULE_AUTHOR("HIPL ");
+MODULE_DESCRIPTION("HIP development module");
+MODULE_LICENSE("GPL");
+#ifdef KRISUS_THESIS
+MODULE_PARM(kmm,"i");
+MODULE_PARM_DESC(kmm, "Measuring mode: 1 = Global timing, 2 = {I,R}{1,2} timing, 3 = spinlock timing");
+#endif
+module_init(hip_init);
+module_exit(hip_cleanup);
diff -urN linux-2.6.10/net/ipv6/hip/hip.h linux-2.6.10-hip/net/ipv6/hip/hip.h
--- linux-2.6.10/net/ipv6/hip/hip.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/hip.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,94 @@
+#ifndef HIP_HIP_H
+#define HIP_HIP_H
+
+#include "crypto/dh.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef KRISUS_THESIS
+
+extern int kmm; // hip.c
+extern struct timeval gtv_start, gtv_stop, gtv_result;
+extern int gtv_inuse;
+
+#define KMM_GLOBAL 1
+#define KMM_PARTIAL 2
+#define KMM_SPINLOCK 3
+
+#define KRISU_START_TIMER(mod) do {\
+ if (mod == kmm) {\
+ gtv_inuse = 1;\
+ do_gettimeofday(>v_start);\
+ }\
+ } while(0)
+
+#define KRISU_STOP_TIMER(mod,msg) do {\
+ if (mod == kmm) {\
+ do_gettimeofday(>v_stop);\
+ gtv_inuse = 0;\
+ hip_timeval_diff(>v_start,>v_stop,>v_result);\
+ HIP_INFO("%s: %ld usec\n", msg, \
+ gtv_result.tv_usec + gtv_result.tv_sec * 1000000);\
+ }\
+ } while(0)
+
+#else
+
+#define KRISU_START_TIMER(x)
+#define KRISU_STOP_TIMER(x,y)
+
+#endif /* KRISUS_THESIS */
+
+/* used by hip worker to announce completion of work order */
+#define KHIPD_OK 0
+#define KHIPD_QUIT -1
+#define KHIPD_ERROR -2
+#define KHIPD_UNRECOVERABLE_ERROR -3
+#define HIP_MAX_SCATTERLISTS 5 // is this enough?
+
+
+uint16_t hip_get_dh_size(uint8_t hip_dh_group_type);
+struct hip_common *hip_create_r1(const struct in6_addr *src_hit);
+int hip_build_digest(const int type, const void *in, int in_len, void *out);
+int hip_build_digest_repeat(struct crypto_tfm *dgst, struct scatterlist *sg,
+ int nsg, void *out);
+
+hip_transform_suite_t hip_select_esp_transform(struct hip_esp_transform *ht);
+hip_transform_suite_t hip_select_hip_transform(struct hip_hip_transform *ht);
+int hip_auth_key_length_esp(int tid);
+int hip_transform_key_length(int tid);
+int hip_store_base_exchange_keys(struct hip_hadb_state *entry,
+ struct hip_context *ctx, int is_initiator);
+int hip_hmac_key_length(int tid);
+int hip_enc_key_length(int tid);
+int hip_crypto_encrypted(void *, const void *, int, int, void*, int);
+int hip_birthday_success(uint64_t old_bd, uint64_t new_bd);
+uint64_t hip_get_current_birthday(void);
+int hip_write_hmac(int type, void *key, void *in, int in_len, void *out);
+int hip_map_virtual_to_pages(struct scatterlist *slist, int *slistcnt,
+ const u8 *addr, const u32 size);
+int hip_ipv6_devaddr2ifindex(struct in6_addr *addr);
+
+extern DH *dh_table[HIP_MAX_DH_GROUP_ID]; // see crypto/dh.[ch]
+extern struct crypto_tfm *impl_sha1;
+extern struct crypto_tfm *impl_sha1;
+extern struct semaphore hip_work;
+extern struct socket *hip_output_socket;
+//extern spinlock_t hip_workqueue_lock;
+extern spinlock_t dh_table_lock;
+extern time_t load_time;
+
+#ifdef CONFIG_SYSCTL
+struct hip_sys_config {
+ int hip_cookie_max_k_r1;
+};
+extern struct hip_sys_config hip_sys_config;
+#endif
+
+#endif /* HIP_HIP_H */
diff -urN linux-2.6.10/net/ipv6/hip/input.c linux-2.6.10-hip/net/ipv6/hip/input.c
--- linux-2.6.10/net/ipv6/hip/input.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/input.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,3182 @@
+/*
+ * HIP input
+ *
+ * Licence: GNU/GPL
+ * Authors: Janne Lundberg
+ * Miika Komu
+ * Mika Kousa
+ * Kristian Slavov
+ * Anthony D. Joseph
+ *
+ */
+
+#include
+#include
+
+#include "input.h"
+#include "debug.h"
+#include "hadb.h"
+#include "keymat.h"
+#include "crypto/dsa.h"
+#include "builder.h"
+#include "hip.h"
+#include "security.h"
+#include "misc.h"
+#include "workqueue.h"
+#include "db.h"
+#include "cookie.h"
+#include "output.h"
+#include "socket.h"
+#ifdef CONFIG_HIP_RVS
+#include "rvs.h"
+#endif
+#include "crypto/rsa.h"
+
+static int hip_verify_hmac(struct hip_common *buffer, u8 *hmac,
+ void *hmac_key, int hmac_type);
+
+/*****************************************************************************
+ * UTILITY FUNCTIONS *
+ *****************************************************************************/
+
+/**
+ * hip_controls_sane - check for illegal controls
+ * @controls: control value to be checked
+ * @legal: legal control values to check @controls against
+ *
+ * Controls are given in host byte order.
+ *
+ * Returns 1 if there are no illegal control values in @controls,
+ * otherwise 0.
+ */
+int hip_controls_sane(u16 controls, u16 legal)
+{
+ u16 known;
+
+ known = controls & ( HIP_CONTROL_CERTIFICATES |
+ HIP_CONTROL_HIT_ANON
+#ifdef CONFIG_HIP_RVS
+ | HIP_CONTROL_RVS_CAPABLE //XX:FIXME
+#endif
+ | HIP_CONTROL_SHT_MASK /* should check reserved ? */
+ | HIP_CONTROL_DHT_MASK
+ );
+
+ if ((known | legal) != legal)
+ return 0;
+
+ return 1;
+}
+
+
+/**
+ * hip_handle_esp - handle incoming ESP packet
+ * @spi: SPI from the incoming ESP packet
+ * @hdr: IPv6 header of the packet
+ *
+ * If the packet's SPI belongs to a HIP connection, the IPv6 addresses
+ * are replaced with the corresponding HITs before the packet is
+ * delivered to ESP.
+ */
+void hip_handle_esp(uint32_t spi, struct ipv6hdr *hdr)
+{
+ hip_ha_t *ha;
+
+ /* We are called only from bh.
+ * No locking will take place since the data
+ * that we are copying is very static
+ */
+ _HIP_DEBUG("SPI=0x%x\n", spi);
+ ha = hip_hadb_find_byspi_list(spi);
+ if (!ha) {
+ HIP_INFO("HT BYSPILIST: NOT found, unknown SPI 0x%x\n",spi);
+ return;
+ }
+
+ /* New in draft-10: If we are responder and in some proper state, then
+ as soon as we receive ESP packets for a valid SA, we should transition
+ to ESTABLISHED state.
+ Since we want to avoid excessive hooks, we will do it here, although the
+ SA check is done later... (and the SA might be invalid).
+ */
+ if (ha->state == HIP_STATE_R2_SENT) {
+ ha->state = HIP_STATE_ESTABLISHED;
+ HIP_DEBUG("Transition to ESTABLISHED state from R2_SENT\n");
+ }
+
+ ipv6_addr_copy(&hdr->daddr, &ha->hit_our);
+ ipv6_addr_copy(&hdr->saddr, &ha->hit_peer);
+
+ hip_put_ha(ha);
+ return;
+}
+
+/**
+ * hip_create_signature - Calculate SHA1 hash over the data and sign it.
+ * @buffer_start: Pointer to start of the buffer over which the hash is
+ * calculated.
+ * @buffer_length: Length of the buffer.
+ * @host_id: DSA private key.
+ * @signature: Place for signature.
+ *
+ * Signature size for DSA is 41 bytes.
+ *
+ * Returns 1 if success, otherwise 0.
+ */
+int hip_create_signature(void *buffer_start, int buffer_length,
+ struct hip_host_id *host_id, u8 *signature)
+{
+ int err = 0;
+ int use_rsa = 0;
+ u8 sha1_digest[HIP_AH_SHA_LEN];
+
+ /* this has to be modified so that other signature algorithms
+ are accepted
+ */
+
+ if (hip_get_host_id_algo(host_id) == HIP_HI_RSA)
+ use_rsa = 1;
+ else if (hip_get_host_id_algo(host_id) != HIP_HI_DSA) {
+ HIP_ERROR("Unsupported algorithm:%d\n", hip_get_host_id_algo(host_id));
+ goto out_err;
+ }
+
+
+ _HIP_HEXDUMP("Signature data (create)", buffer_start, buffer_length);
+
+ if (hip_build_digest(HIP_DIGEST_SHA1, buffer_start, buffer_length,
+ sha1_digest) < 0)
+ {
+ HIP_ERROR("Building of SHA1 digest failed\n");
+ goto out_err;
+ }
+
+ HIP_HEXDUMP("create digest", sha1_digest, HIP_AH_SHA_LEN);
+ _HIP_HEXDUMP("dsa key", (u8 *)(host_id + 1), ntohs(host_id->hi_length));
+
+ if (use_rsa) {
+ err = hip_rsa_sign(sha1_digest, (u8 *)(host_id + 1), signature,
+ 3+128*2+64+64
+ /*e+n+d+p+q*/
+ /*1 + 3 + 128 * 3*/ );
+ } else {
+ err = hip_dsa_sign(sha1_digest,(u8 *)(host_id + 1), signature);
+ }
+
+ if (err) {
+ HIP_ERROR("DSA Signing error\n");
+ return 0;
+ }
+
+
+ if(use_rsa) {
+ _HIP_HEXDUMP("signature",signature,HIP_RSA_SIGNATURE_LEN);
+ } else {
+ /* 1 + 20 + 20 */
+ _HIP_HEXDUMP("signature",signature,HIP_DSA_SIGNATURE_LEN);
+ }
+
+ err = 1;
+
+ out_err:
+ return err;
+}
+
+
+/**
+ * hip_verify_signature - Verifies that the signature matches the data it has
+ * been calculated over.
+ * @buffer_start: Pointer to the start of the buffer over which to calculate
+ * SHA1-160 digest.
+ * @buffer_length: Length of the buffer at @buffer_start
+ * @host_id: Pointer to HOST_ID (as specified by the HIP draft).
+ * @signature: Pointer to the signature
+ *
+ * Returns 1 if the signature was ok, else 0
+ */
+int hip_verify_signature(void *buffer_start, int buffer_length,
+ struct hip_host_id *host_id, u8 *signature)
+{
+ u8 *public_key = (u8 *) (host_id + 1);
+ int tmp, ok = 0;
+ unsigned char sha1_digest[HIP_AH_SHA_LEN];
+ int use_rsa = 0;
+
+ /* check for all algorithms */
+
+ if (hip_get_host_id_algo(host_id) == HIP_HI_RSA) {
+ use_rsa = 1;
+ } else if (hip_get_host_id_algo(host_id) != HIP_HI_DSA) {
+ HIP_ERROR("Unsupported algorithm:%d\n",
+ hip_get_host_id_algo(host_id));
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("Signature data (verify)",buffer_start,buffer_length);
+ _HIP_DEBUG("buffer_length=%d\n", buffer_length);
+
+ if (hip_build_digest(HIP_DIGEST_SHA1, buffer_start,
+ buffer_length,sha1_digest)) {
+ HIP_ERROR("Could not calculate SHA1 digest\n");
+ goto out_err;
+ }
+
+ HIP_HEXDUMP("Verify hexdump", sha1_digest, HIP_AH_SHA_LEN);
+
+ //public_key_len = hip_get_param_contents_len(host_id) - 4;
+
+ _HIP_HEXDUMP("verify key", public_key, public_key_len);
+
+ if (use_rsa) {
+ size_t public_key_len;
+ public_key_len = ntohs(host_id->hi_length) -
+ sizeof(struct hip_host_id_key_rdata);
+ _HIP_HEXDUMP("Verify hexdump sig **", signature,
+ HIP_RSA_SIGNATURE_LEN);
+ tmp = hip_rsa_verify(sha1_digest, public_key, signature,
+ public_key_len);
+ HIP_HEXDUMP("public key",public_key,public_key_len);
+ } else {
+ _HIP_HEXDUMP("Verify hexdump sig **", signature,
+ HIP_DSA_SIGNATURE_LEN);
+ tmp = hip_dsa_verify(sha1_digest, public_key, signature);
+ }
+
+ switch(tmp) {
+ case 0:
+ HIP_INFO("Signature: [CORRECT]\n");
+ ok = 1;
+ break;
+ case 1:
+ HIP_INFO("Signature: [INCORRECT]\n");
+ HIP_HEXDUMP("digest",sha1_digest, HIP_AH_SHA_LEN);
+ if(use_rsa) {
+ HIP_HEXDUMP("signature", signature,
+ HIP_RSA_SIGNATURE_LEN);
+ } else {
+ HIP_HEXDUMP("signature", signature,
+ HIP_DSA_SIGNATURE_LEN);
+ }
+ break;
+ default:
+ HIP_ERROR("Signature verification failed: %d\n", tmp);
+ break;
+ }
+
+ out_err:
+ return ok;
+}
+
+/**
+ * hip_verify_packet_hmac - verify packet HMAC
+ * @msg: HIP packet
+ * @entry: HA
+ *
+ * Returns: 0 if HMAC was validated successfully, < 0 if HMAC could
+ * not be validated.
+ */
+int hip_verify_packet_hmac(struct hip_common *msg,
+ struct hip_crypto_key *crypto_key)
+{
+ int err;
+ int len, orig_len;
+ struct hip_crypto_key tmpkey;
+ struct hip_hmac *hmac;
+
+ hmac = hip_get_param(msg, HIP_PARAM_HMAC);
+ if (!hmac) {
+ HIP_ERROR("Packet contained no HMAC parameter\n");
+ err = -ENOMSG;
+ goto out_err;
+ }
+ _HIP_DEBUG("HMAC found\n");
+
+ /* hmac verification modifies the msg length temporarile, so we have
+ to restore the length */
+ orig_len = hip_get_msg_total_len(msg);
+
+ len = (u8 *) hmac - (u8*) msg;
+ hip_set_msg_total_len(msg, len);
+
+ _HIP_HEXDUMP("HMACced data", msg, len);
+ memcpy(&tmpkey, crypto_key, sizeof(tmpkey));
+ err = hip_verify_hmac(msg, hmac->hmac_data,
+ tmpkey.key, HIP_DIGEST_SHA1_HMAC);
+ if (err) {
+ HIP_ERROR("HMAC validation failed\n");
+ goto out_err;
+ }
+
+ hip_set_msg_total_len(msg, orig_len);
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_verify_packet_hmac2 - verify packet HMAC
+ * @msg: HIP packet
+ * @entry: HA
+ *
+ * Returns: 0 if HMAC was validated successfully, < 0 if HMAC could
+ * not be validated. Assumes that the hmac includes only the header
+ * and host id.
+ */
+int hip_verify_packet_hmac2(struct hip_common *msg,
+ struct hip_crypto_key *crypto_key,
+ struct hip_host_id *host_id)
+{
+ int err = 0;
+ struct hip_crypto_key tmpkey;
+ struct hip_hmac *hmac;
+ struct hip_common *msg_copy = NULL;
+ struct hip_spi *spi;
+
+ msg_copy = hip_msg_alloc();
+ if (!msg) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ memcpy(msg_copy, msg, sizeof(struct hip_common));
+ hip_set_msg_total_len(msg_copy, 0);
+ hip_zero_msg_checksum(msg_copy);
+
+ spi = hip_get_param(msg, HIP_PARAM_SPI);
+ HIP_ASSERT(spi);
+ err = hip_build_param(msg_copy, spi);
+ if (err) {
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ hip_build_param(msg_copy, host_id);
+
+ hmac = hip_get_param(msg, HIP_PARAM_HMAC2);
+ if (!hmac) {
+ HIP_ERROR("Packet contained no HMAC parameter\n");
+ err = -ENOMSG;
+ goto out_err;
+ }
+ _HIP_DEBUG("HMAC found\n");
+
+ HIP_HEXDUMP("HMAC data", msg_copy, hip_get_msg_total_len(msg_copy));
+ memcpy(&tmpkey, crypto_key, sizeof(tmpkey));
+ err = hip_verify_hmac(msg_copy, hmac->hmac_data,
+ tmpkey.key, HIP_DIGEST_SHA1_HMAC);
+ if (err) {
+ HIP_ERROR("HMAC validation failed\n");
+ goto out_err;
+ }
+
+ out_err:
+
+ if (msg_copy)
+ kfree(msg_copy);
+
+ return err;
+}
+
+/**
+ * hip_verify_packet_signature - verify packet SIGNATURE
+ * @msg: HIP packet
+ * @hid: HOST ID
+ *
+ * Returns: 0 if SIGNATURE was validated successfully, < 0 if SIGNATURE could
+ * not be validated.
+ */
+int hip_verify_packet_signature(struct hip_common *msg,
+ struct hip_host_id *hid)
+{
+ int err = 0;
+ struct hip_sig *sig;
+ int len, origlen;
+
+ origlen = hip_get_msg_total_len(msg);
+
+ sig = hip_get_param(msg, HIP_PARAM_HIP_SIGNATURE);
+ if (!sig) {
+ err = -ENOENT;
+ HIP_ERROR("Could not find signature\n");
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("SIG", sig, hip_get_param_total_len(sig));
+
+ len = ((u8 *) sig) - ((u8 *) msg);
+ hip_zero_msg_checksum(msg);
+ hip_set_msg_total_len(msg, len);
+
+ if (len < 0) {
+ err = -ENOENT;
+ HIP_ERROR("Invalid signature len\n");
+ goto out_err;
+ }
+
+ if (!hip_verify_signature(msg, len, hid,
+ (u8 *) (sig + 1))) {
+ HIP_ERROR("Verification of signature failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ out_err:
+ hip_set_msg_total_len(msg, origlen);
+ return err;
+}
+
+/**
+ * hip_verify_packet_signature2 - verify packet SIGNATURE2
+ * @msg: HIP packet
+ * @hid: HOST ID
+ *
+ * Returns: 0 if SIGNATURE2 was validated successfully, < 0 if
+ * SIGNATURE2 could not be validated.
+ */
+int hip_verify_packet_signature2(struct hip_common *msg,
+ struct hip_host_id *hid)
+{
+ int err = 0;
+ struct hip_sig2 *sig2;
+ int origlen, len;
+ struct in6_addr tmpaddr;
+ struct hip_puzzle *pz;
+ uint8_t opaque[3];
+ uint64_t randi;
+
+ origlen = hip_get_msg_total_len(msg);
+
+ sig2 = hip_get_param(msg, HIP_PARAM_HIP_SIGNATURE2);
+ if (!sig2) {
+ HIP_ERROR("No SIGNATURE2 found\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ len = (((u8 *)sig2 - ((u8 *) msg)));
+
+ ipv6_addr_copy(&tmpaddr, &msg->hitr);
+ memset(&msg->hitr, 0, sizeof(struct in6_addr));
+
+ hip_set_msg_total_len(msg, len);
+ msg->checksum = 0;
+
+ pz = hip_get_param(msg, HIP_PARAM_PUZZLE);
+ if (!pz) {
+ HIP_ERROR("Illegal R1 packet (puzzle missing)\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ memcpy(opaque, pz->opaque, 3);
+ randi = pz->I;
+
+ memset(pz->opaque, 0, 3);
+ pz->I = 0;
+
+ if (!hip_verify_signature(msg, len, hid,
+ (u8 *)(sig2 + 1))) {
+ HIP_ERROR("Signature verification failed\n");
+ /* well if we fail, then we better dump the packet */
+ HIP_HEXDUMP("Failed packet", msg, len);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ memcpy(pz->opaque, opaque, 3);
+ pz->I = randi;
+
+ ipv6_addr_copy(&msg->hitr, &tmpaddr);
+
+ /* the checksum is not restored because it was already checked in
+ hip_inbound */
+ out_err:
+ hip_set_msg_total_len(msg, origlen);
+ return err;
+}
+
+
+/**
+ * hip_calculate_shared_secret - Creates a shared secret based on the
+ * public key of the peer (passed as an argument) and own DH private key
+ * (created beforehand).
+ * @dhf: Peer's Diffie-Hellman public key
+ * @buffer: Buffer that holds enough space for the shared secret.
+ *
+ * Returns the length of the shared secret in octets if successful,
+ * or -1 if an error occured.
+ */
+int hip_calculate_shared_secret(struct hip_diffie_hellman *dhf, u8* buffer,
+ int bufsize)
+{
+ signed int len;
+ int err;
+
+ if (dh_table[dhf->group_id] == NULL) {
+ HIP_ERROR("Unsupported DH group: %d\n",dhf->group_id);
+ return -1;
+ }
+
+ len = hip_get_param_contents_len(dhf) - 1;
+ _HIP_HEXDUMP("PEER DH key:",(dhf + 1),len);
+ err = hip_gen_dh_shared_key(dh_table[dhf->group_id], (u8*)(dhf+1), len,
+ buffer, bufsize);
+ if (err < 0) {
+ HIP_ERROR("Could not create shared secret\n");
+ return -1;
+ }
+
+ return err;
+}
+
+/**
+ * hip_produce_keying_material - Create shared secret and produce keying material
+ * @msg: the HIP packet received from the peer
+ * @ctx: context
+ *
+ * The initial ESP keys are drawn out of the keying material.
+ *
+ * Returns zero on success, or negative on error.
+ */
+int hip_produce_keying_material(struct hip_common *msg,
+ struct hip_context *ctx)
+{
+ u8 *dh_shared_key = NULL;
+ int hip_transf_length, hmac_transf_length;
+ int auth_transf_length, esp_transf_length;
+ int hip_tfm, esp_tfm;
+ int dh_shared_len = 1024;
+ int err = 0;
+ struct hip_keymat_keymat km;
+ char *keymat = NULL;
+ size_t keymat_len_min; /* how many bytes we need at least for the KEYMAT */
+ size_t keymat_len; /* note SHA boundary */
+ struct hip_tlv_common *param = NULL;
+ int we_are_HITg = 0;
+
+ /* perform light operations first before allocating memory or
+ * using lots of cpu time */
+ param = hip_get_param(msg, HIP_PARAM_HIP_TRANSFORM);
+ if (!param) {
+ HIP_ERROR("Could not find HIP transform\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ hip_tfm = hip_select_hip_transform((struct hip_hip_transform *) param);
+ if (hip_tfm == 0) {
+ HIP_ERROR("Could not select proper HIP transform\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ param = hip_get_param(msg, HIP_PARAM_ESP_TRANSFORM);
+ if (!param) {
+ HIP_ERROR("Could not find ESP transform\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ esp_tfm = hip_select_esp_transform((struct hip_esp_transform *) param);
+ if (esp_tfm == 0) {
+ HIP_ERROR("Could not select proper ESP transform\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ hip_transf_length = hip_transform_key_length(hip_tfm);
+ hmac_transf_length = hip_hmac_key_length(esp_tfm);
+ esp_transf_length = hip_enc_key_length(esp_tfm);
+ auth_transf_length = hip_auth_key_length_esp(esp_tfm);
+
+ HIP_DEBUG("transform lengths: hip=%d, hmac=%d, esp=%d, auth=%d\n",
+ hip_transf_length, hmac_transf_length, esp_transf_length,
+ auth_transf_length);
+
+ /* Create only minumum amount of KEYMAT for now. From draft
+ * chapter HIP KEYMAT we know how many bytes we need for all
+ * keys used in the base exchange. */
+ keymat_len_min = hip_transf_length + hmac_transf_length +
+ hip_transf_length + hmac_transf_length + esp_transf_length +
+ auth_transf_length + esp_transf_length + auth_transf_length;
+
+ keymat_len = keymat_len_min;
+ if (keymat_len % HIP_AH_SHA_LEN)
+ keymat_len += HIP_AH_SHA_LEN - (keymat_len % HIP_AH_SHA_LEN);
+
+ HIP_DEBUG("keymat_len_min=%u keymat_len=%u\n", keymat_len_min,
+ keymat_len);
+
+ keymat = kmalloc(keymat_len, GFP_KERNEL);
+ if (!keymat) {
+ HIP_ERROR("No memory for KEYMAT\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* 1024 should be enough for shared secret. The length of the
+ * shared secret actually depends on the DH Group. */
+
+ /* TODO: 1024 -> hip_get_dh_size ? */
+ dh_shared_key = kmalloc(dh_shared_len, GFP_KERNEL);
+ if (!dh_shared_key) {
+ HIP_ERROR("No memory for DH shared key\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memset(dh_shared_key, 0, dh_shared_len);
+
+ param = hip_get_param(msg, HIP_PARAM_DIFFIE_HELLMAN);
+ if (!param) {
+ err = -ENOENT;
+ HIP_ERROR("No Diffie-Hellman param found\n");
+ goto out_err;
+ }
+
+ dh_shared_len = hip_calculate_shared_secret((struct hip_diffie_hellman *) param,
+ dh_shared_key, dh_shared_len);
+ if (dh_shared_len < 0) {
+ HIP_ERROR("Calculation of shared secret failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ _HIP_DEBUG("dh_shared_len=%u\n", dh_shared_len);
+ _HIP_HEXDUMP("DH SHARED KEY", dh_shared_key, dh_shared_len);
+
+ hip_make_keymat(dh_shared_key, dh_shared_len,
+ &km, keymat, keymat_len,
+ &msg->hits, &msg->hitr, &ctx->keymat_calc_index);
+
+ /* draw from km to keymat, copy keymat to dst, length of
+ * keymat is len */
+#define KEYMAT_DRAW_AND_COPY(dst, len) \
+ { \
+ void *p = hip_keymat_draw(&km, len); \
+ if (!p) { \
+ HIP_ERROR("Could not draw from keymat\n"); \
+ err = -EINVAL; \
+ goto out_err; \
+ } \
+ memcpy(dst, p, len); \
+ }
+
+ /* Draw keys: */
+ we_are_HITg = hip_hit_is_bigger(&msg->hitr, &msg->hits);
+ HIP_DEBUG("we are HIT%c\n", we_are_HITg ? 'g' : 'l');
+ if (we_are_HITg) {
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_enc_out.key, hip_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_hmac_out.key, hmac_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_enc_in.key, hip_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_hmac_in.key, hmac_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->esp_out.key, esp_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->auth_out.key, auth_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->esp_in.key, esp_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->auth_in.key, auth_transf_length);
+ } else {
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_enc_in.key, hip_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_hmac_in.key, hmac_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_enc_out.key, hip_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->hip_hmac_out.key, hmac_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->esp_in.key, esp_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->auth_in.key, auth_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->esp_out.key, esp_transf_length);
+ KEYMAT_DRAW_AND_COPY(&ctx->auth_out.key, auth_transf_length);
+ }
+ HIP_HEXDUMP("HIP-gl encryption", &ctx->hip_enc_out.key, hip_transf_length);
+ HIP_HEXDUMP("HIP-gl integrity (HMAC) key", &ctx->hip_hmac_out.key,
+ hmac_transf_length);
+ _HIP_DEBUG("skipping HIP-lg encryption key, %u bytes\n", hip_transf_length);
+ HIP_HEXDUMP("HIP-lg encryption", &ctx->hip_enc_in.key, hip_transf_length);
+ HIP_HEXDUMP("HIP-lg integrity (HMAC) key", &ctx->hip_hmac_in.key, hmac_transf_length);
+ HIP_HEXDUMP("SA-gl ESP encryption key", &ctx->esp_out.key, esp_transf_length);
+ HIP_HEXDUMP("SA-gl ESP authentication key", &ctx->auth_out.key, auth_transf_length);
+ HIP_HEXDUMP("SA-lg ESP encryption key", &ctx->esp_in.key, esp_transf_length);
+ HIP_HEXDUMP("SA-lg ESP authentication key", &ctx->auth_in.key, auth_transf_length);
+
+#undef KEYMAT_DRAW_AND_COPY
+
+ /* the next byte when creating new keymat */
+ ctx->current_keymat_index = keymat_len_min; /* offset value, so no +1 ? */
+ ctx->keymat_calc_index = (ctx->current_keymat_index / HIP_AH_SHA_LEN) + 1;
+
+ memcpy(ctx->current_keymat_K, keymat+(ctx->keymat_calc_index-1)*HIP_AH_SHA_LEN, HIP_AH_SHA_LEN);
+
+ _HIP_DEBUG("ctx: keymat_calc_index=%u current_keymat_index=%u\n",
+ ctx->keymat_calc_index, ctx->current_keymat_index);
+ _HIP_HEXDUMP("CTX CURRENT KEYMAT", ctx->current_keymat_K, HIP_AH_SHA_LEN);
+
+ /* store DH shared key */
+ ctx->dh_shared_key = dh_shared_key;
+ ctx->dh_shared_key_len = dh_shared_len;
+
+ /* on success kfree for dh_shared_key is called by caller */
+ out_err:
+ if (err) {
+ if (dh_shared_key)
+ kfree(dh_shared_key);
+ }
+ if (keymat)
+ kfree(keymat);
+
+ return err;
+}
+
+
+/*****************************************************************************
+ * PACKET/PROTOCOL HANDLING *
+ *****************************************************************************/
+
+/**
+ * hip_create_i2 - Create I2 packet and send it
+ * @ctx: Context that includes the incoming R1 packet
+ * @solved_puzzle: Value that solves the puzzle
+ * @entry: HA
+ *
+ * Returns: zero on success, non-negative on error.
+ */
+int hip_create_i2(struct hip_context *ctx, uint64_t solved_puzzle,
+ hip_ha_t *entry)
+{
+ int err = 0, dh_size = 0, written, host_id_in_enc_len;
+ uint32_t spi_in = 0;
+ hip_transform_suite_t transform_hip_suite, transform_esp_suite;
+ struct hip_host_id *host_id_pub = NULL;
+ struct hip_host_id *host_id_private = NULL;
+ char *enc_in_msg = NULL, *host_id_in_enc = NULL, *iv = NULL;
+ struct in6_addr daddr;
+ u8 *dh_data = NULL;
+ struct hip_spi *hspi;
+ struct hip_common *i2 = NULL;
+ struct hip_param *param;
+ struct hip_diffie_hellman *dh_req;
+ //int algo;
+ u8 *signature = NULL;
+// u8 signature[HIP_DSA_SIGNATURE_LEN];
+ struct hip_spi_in_item spi_in_data;
+ uint16_t mask;
+
+ HIP_DEBUG("\n");
+
+ HIP_ASSERT(entry);
+
+ /* allocate space for new I2 */
+ i2 = hip_msg_alloc();
+ if (!i2) {
+ HIP_ERROR("Allocation of I2 failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* allocate memory for writing Diffie-Hellman shared secret */
+ dh_size = hip_get_dh_size(HIP_DEFAULT_DH_GROUP_ID);
+ if (dh_size == 0) {
+ HIP_ERROR("Could not get dh size\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ dh_data = kmalloc(dh_size, GFP_KERNEL);
+ if (!dh_data) {
+ HIP_ERROR("Failed to alloc memory for dh_data\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+#if 0 // does not work correctly with DSA-RSA base exhcange -miika
+ {
+ struct hip_host_id *mofo;
+ mofo = hip_get_param(ctx->input, HIP_PARAM_HOST_ID);
+
+ algo = hip_get_host_id_algo(mofo);
+ }
+#endif
+
+ /* Get a localhost identity, allocate memory for the public key part
+ and extract the public key from the private key. The public key is
+ needed in the ESP-ENC below. */
+ host_id_pub = hip_get_any_localhost_public_key(HIP_HI_DEFAULT_ALGO);
+ if (host_id_pub == NULL) {
+ err = -EINVAL;
+ HIP_ERROR("No localhost public key found\n");
+ goto out_err;
+ }
+
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ err = -EINVAL;
+ HIP_ERROR("No localhost private key found\n");
+ goto out_err;
+ }
+
+ {
+ int sigsize;
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ sigsize = HIP_RSA_SIGNATURE_LEN;
+ } else {
+ sigsize = HIP_DSA_SIGNATURE_LEN;
+ }
+
+ signature = kmalloc(sigsize,GFP_KERNEL);
+ if (!signature) {
+ HIP_ERROR("No memory for signature\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ }
+ /* TLV sanity checks are are already done by the caller of this
+ function. Now, begin to build I2 piece by piece. */
+
+ /* Delete old SPDs and SAs, if present */
+ hip_delete_esp(entry);
+
+ /* create I2 */
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(i2, HIP_I2, mask,
+ &(ctx->input->hitr),
+ &(ctx->input->hits));
+
+ /********** SPI **********/
+ /* SPI and LSI are set below where IPsec is set up */
+ err = hip_build_param_spi(i2, 0);
+ if (err) {
+ HIP_ERROR("building of SPI_LSI failed (err=%d)\n", err);
+ goto out_err;
+ }
+
+ /********** R1 COUNTER (OPTIONAL) ********/
+ /* we build this, if we have recorded some value (from previous R1s) */
+ {
+ uint64_t rtmp;
+
+ HIP_LOCK_HA(entry);
+ rtmp = entry->birthday;
+ HIP_UNLOCK_HA(entry);
+
+ if (rtmp) {
+ err = hip_build_param_r1_counter(i2, rtmp);
+ if (err) {
+ HIP_ERROR("Could not build R1 GENERATION parameter\n");
+ goto out_err;
+ }
+ }
+ }
+
+ /********** SOLUTION **********/
+ {
+ struct hip_puzzle *pz;
+
+ pz = hip_get_param(ctx->input, HIP_PARAM_PUZZLE);
+ if (!pz) {
+ HIP_ERROR("Internal error: PUZZLE parameter mysteriously gone\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ err = hip_build_param_solution(i2, pz, ntoh64(solved_puzzle));
+ if (err) {
+ HIP_ERROR("Building of solution failed (%d)\n", err);
+ goto out_err;
+ }
+ }
+
+ /********** Diffie-Hellman *********/
+ dh_req = hip_get_param(ctx->input, HIP_PARAM_DIFFIE_HELLMAN);
+ if (!dh_req) {
+ err = -ENOENT;
+ HIP_ERROR("Internal error\n");
+ goto out_err;
+ }
+
+ written = hip_insert_dh(dh_data, dh_size, dh_req->group_id);
+ if (written < 0) {
+ err = -ENOENT;
+ HIP_ERROR("Error while extracting DH key\n");
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("Own DH key", dh_data, n);
+
+ err = hip_build_param_diffie_hellman_contents(i2,dh_req->group_id,
+ dh_data, written);
+ if (err) {
+ HIP_ERROR("Building of DH failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /********** HIP transform. **********/
+ param = hip_get_param(ctx->input, HIP_PARAM_HIP_TRANSFORM);
+ if (!param) {
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ transform_hip_suite =
+ hip_select_hip_transform((struct hip_hip_transform *) param);
+ if (transform_hip_suite == 0) {
+ HIP_ERROR("Could not find acceptable hip transform suite\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Select only one transform */
+ err = hip_build_param_transform(i2, HIP_PARAM_HIP_TRANSFORM,
+ &transform_hip_suite, 1);
+ if (err) {
+ HIP_ERROR("Building of HIP transform failed\n");
+ goto out_err;
+ }
+
+ /********** ESP-ENC transform. **********/
+ param = hip_get_param(ctx->input, HIP_PARAM_ESP_TRANSFORM);
+ if (!param) {
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ /* Select only one transform */
+ transform_esp_suite =
+ hip_select_esp_transform((struct hip_esp_transform *) param);
+ if (transform_esp_suite == 0) {
+ HIP_ERROR("Could not find acceptable hip transform suite\n");
+ goto out_err;
+ }
+ err = hip_build_param_transform(i2, HIP_PARAM_ESP_TRANSFORM,
+ &transform_esp_suite, 1);
+ if (err) {
+ HIP_ERROR("Building of ESP transform failed\n");
+ goto out_err;
+ }
+
+ /************ Encrypted ***********/
+
+ switch (transform_hip_suite) {
+ case HIP_HIP_AES_SHA1:
+ err = hip_build_param_encrypted_aes_sha1(i2, host_id_pub);
+ enc_in_msg = hip_get_param(i2, HIP_PARAM_ENCRYPTED);
+ HIP_ASSERT(enc_in_msg); /* Builder internal error. */
+ iv = ((struct hip_encrypted_aes_sha1 *) enc_in_msg)->iv;
+ get_random_bytes(iv, 16);
+ host_id_in_enc = enc_in_msg +
+ sizeof(struct hip_encrypted_aes_sha1);
+ break;
+ case HIP_HIP_3DES_SHA1:
+ err = hip_build_param_encrypted_3des_sha1(i2, host_id_pub);
+ enc_in_msg = hip_get_param(i2, HIP_PARAM_ENCRYPTED);
+ HIP_ASSERT(enc_in_msg); /* Builder internal error. */
+ iv = ((struct hip_encrypted_3des_sha1 *) enc_in_msg)->iv;
+ get_random_bytes(iv, 8);
+ host_id_in_enc = enc_in_msg +
+ sizeof(struct hip_encrypted_3des_sha1);
+ break;
+ case HIP_HIP_NULL_SHA1:
+ err = hip_build_param_encrypted_null_sha1(i2, host_id_pub);
+ enc_in_msg = hip_get_param(i2, HIP_PARAM_ENCRYPTED);
+ HIP_ASSERT(enc_in_msg); /* Builder internal error. */
+ iv = NULL;
+ host_id_in_enc = enc_in_msg +
+ sizeof(struct hip_encrypted_null_sha1);
+ break;
+ default:
+ HIP_ERROR("HIP transform not supported (%d)\n",
+ transform_hip_suite);
+ err = -ENOSYS;
+ break;
+ }
+
+ if (err) {
+ HIP_ERROR("Building of param encrypted failed (%d)\n",
+ err);
+ goto out_err;
+ }
+
+ HIP_HEXDUMP("enc(host_id)", host_id_in_enc,
+ hip_get_param_total_len(host_id_in_enc));
+
+ /* Calculate the length of the host id inside the encrypted param */
+ host_id_in_enc_len = hip_get_param_total_len(host_id_in_enc);
+
+ /* Adjust the host id length for AES (block size 16).
+ build_param_encrypted_aes has already taken care that there is
+ enough padding */
+ if (transform_hip_suite == HIP_HIP_AES_SHA1) {
+ int remainder = host_id_in_enc_len % 16;
+ if (remainder) {
+ HIP_DEBUG("Remainder %d (for AES)\n", remainder);
+ host_id_in_enc_len += remainder;
+ }
+ }
+
+ _HIP_HEXDUMP("hostidinmsg", host_id_in_enc,
+ hip_get_param_total_len(host_id_in_enc));
+ _HIP_HEXDUMP("encinmsg", enc_in_msg,
+ hip_get_param_total_len(enc_in_msg));
+ HIP_HEXDUMP("enc key", &ctx->hip_enc_out.key, HIP_MAX_KEY_LEN);
+ _HIP_HEXDUMP("IV", enc_in_msg->iv, 8); // or 16
+ HIP_DEBUG("host id type: %d\n",
+ hip_get_host_id_algo((struct hip_host_id *)host_id_in_enc));
+ err = hip_crypto_encrypted(host_id_in_enc, iv,
+ transform_hip_suite,
+ host_id_in_enc_len,
+ &ctx->hip_enc_out.key,
+ HIP_DIRECTION_ENCRYPT);
+ if (err) {
+ HIP_ERROR("Building of param encrypted failed %d\n", err);
+ goto out_err;
+ }
+ _HIP_HEXDUMP("encinmsg 2", enc_in_msg,
+ hip_get_param_total_len(enc_in_msg));
+ _HIP_HEXDUMP("hostidinmsg 2", host_id_in_enc, x);
+
+ /* it appears as the crypto function overwrites the IV field, which
+ * definitely breaks our 2.4 responder... Perhaps 2.6 and 2.4 cryptos
+ * are not interoprable or we screw things pretty well in 2.4 :)
+ */
+
+ //memset((u8 *)enc_in_msg->iv, 0, 8);
+
+ /* Now that almost everything is set up except the signature, we can
+ * try to set up inbound IPsec SA, similarly as in hip_create_r2 */
+ {
+ int err;
+
+ /* let the setup routine give us a SPI. */
+ spi_in = 0;
+ err = hip_setup_sa(&ctx->input->hits, &ctx->input->hitr,
+ &spi_in, transform_esp_suite,
+ &ctx->esp_in.key, &ctx->auth_in.key,
+ 0, HIP_SPI_DIRECTION_IN);
+
+ if (err) {
+ HIP_ERROR("failed to setup IPsec SPD/SA entries, peer:src (err=%d)\n", err);
+ /* hip_delete_spd/hip_delete_sa ? */
+ goto out_err;
+ }
+ /* XXX: -EAGAIN */
+ HIP_DEBUG("set up inbound IPsec SA, SPI=0x%x (host)\n", spi_in);
+ }
+
+ hspi = hip_get_param(i2, HIP_PARAM_SPI);
+ HIP_ASSERT(hspi); /* Builder internal error */
+ hspi->spi = htonl(spi_in);
+
+ /* LSI not created, as it is local, and we do not support IPv4 */
+
+#ifdef CONFIG_HIP_RVS
+ /************ RVA_REQUEST (OPTIONAL) ***************/
+ {
+ /* we've requested RVS, and the peer is rvs capable */
+ int type = HIP_RVA_RELAY_I1;
+
+ if (!(entry->local_controls & HIP_PSEUDO_CONTROL_REQ_RVS))
+ goto next_echo_resp;
+
+ if (!(entry->peer_controls & HIP_CONTROL_RVS_CAPABLE))
+ goto next_echo_resp;
+
+ err = hip_build_param_rva(i2, 0, &type, 1, 1);
+ if (err) {
+ HIP_ERROR("Could not build RVA_REQUEST parameter\n");
+ goto out_err;
+ }
+ }
+ next_echo_resp:
+
+#endif
+ /********** ECHO_RESPONSE_SIGN (OPTIONAL) **************/
+ /* must reply... */
+ {
+ struct hip_echo_request *ping;
+
+ ping = hip_get_param(ctx->input, HIP_PARAM_ECHO_REQUEST_SIGN);
+ if (ping) {
+ int ln;
+
+ ln = hip_get_param_contents_len(ping);
+ err = hip_build_param_echo(i2, ping + 1, ln, 1, 0);
+ if (err) {
+ HIP_ERROR("Error while creating echo reply parameter\n");
+ goto out_err;
+ }
+ }
+ }
+
+ /************* HMAC ************/
+
+ err = hip_build_param_hmac_contents(i2,
+ &ctx->hip_hmac_out);
+ if (err) {
+ HIP_ERROR("Building of HMAC failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /********** Signature **********/
+
+ /* Should have been fetched during making of hip_encrypted */
+ HIP_ASSERT(host_id_private);
+
+ /* Build a digest of the packet built so far. Signature will
+ be calculated over the digest. */
+ if (!hip_create_signature(i2, hip_get_msg_total_len(i2),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not create signature\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Only DSA supported currently */
+// HIP_ASSERT(hip_get_host_id_algo(host_id_private) == HIP_HI_DSA);
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(i2,signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(i2,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of signature failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /********** ECHO_RESPONSE (OPTIONAL) ************/
+ /* must reply */
+ {
+ struct hip_echo_request *ping;
+
+ ping = hip_get_param(ctx->input, HIP_PARAM_ECHO_REQUEST);
+ if (ping) {
+ int ln;
+
+ ln = hip_get_param_contents_len(ping);
+ err = hip_build_param_echo(i2, (ping + 1), ln, 0, 0);
+ if (err) {
+ HIP_ERROR("Error while creating echo reply parameter\n");
+ goto out_err;
+ }
+ }
+ }
+
+ /********** I2 packet complete **********/
+
+ memset(&spi_in_data, 0, sizeof(struct hip_spi_in_item));
+ spi_in_data.spi = spi_in;
+ spi_in_data.ifindex = hip_ipv6_devaddr2ifindex(&ctx->skb_in->nh.ipv6h->daddr);
+ HIP_LOCK_HA(entry);
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_IN, &spi_in_data);
+ if (err) {
+ HIP_UNLOCK_HA(entry);
+ goto out_err;
+ }
+ entry->esp_transform = transform_esp_suite;
+ /* Store the keys until we receive R2 */
+ err = hip_store_base_exchange_keys(entry,ctx,1);
+ HIP_UNLOCK_HA(entry);
+
+ if (err) {
+ HIP_DEBUG("hip_store_base_exchange_keys failed\n");
+ goto out_err;
+ }
+
+ /* todo: Also store the keys that will be given to ESP later */
+
+ HIP_DEBUG("sending I2\n");
+
+ err = hip_hadb_get_peer_addr(entry, &daddr);
+ if (err) {
+ HIP_DEBUG("hip_sdb_get_peer_address failed, err = %d\n", err);
+ goto out_err;
+ }
+
+ /* state E1: Receive R1, process. If successful,
+ send I2 and go to E2. */
+ err = hip_csum_send(NULL, &daddr, i2);
+ if (err) {
+ HIP_ERROR("sending of I2 failed, err=%d\n", err);
+ goto out_err;
+ }
+
+ HIP_DEBUG("moving to state I2_SENT\n");
+
+ out_err:
+ if (signature)
+ kfree(signature);
+ if (host_id_private)
+ kfree(host_id_private);
+ if (host_id_pub)
+ kfree(host_id_pub);
+ if (i2)
+ kfree(i2);
+ if (dh_data)
+ kfree(dh_data);
+
+ return err;
+}
+
+/**
+ * hip_handle_r1 - handle incoming R1 packet
+ * @skb: sk_buff where the HIP packet is in
+ * @entry: HA
+ *
+ * This function is the actual point from where the processing of R1
+ * is started and corresponding I2 is created.
+ *
+ * On success (R1 payloads are checked and daemon is called) 0 is
+ * returned, otherwise < 0.
+ */
+int hip_handle_r1(struct sk_buff *skb, hip_ha_t *entry)
+{
+ int err = 0;
+ uint64_t solved_puzzle;
+
+ struct hip_common *r1 = NULL;
+ struct hip_context *ctx = NULL;
+ struct hip_host_id *peer_host_id;
+ struct hip_r1_counter *r1cntr;
+ struct hip_lhi peer_lhi;
+
+ HIP_DEBUG("\n");
+
+ ctx = kmalloc(sizeof(struct hip_context), GFP_KERNEL);
+ if (!ctx) {
+ HIP_ERROR("Could not allocate memory for context\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memset(ctx, 0, sizeof(struct hip_context));
+
+ r1 = (struct hip_common*) skb->h.raw;
+ ctx->input = r1;
+ ctx->skb_in = skb;
+
+ /* according to the section 8.6 of the base draft,
+ * we must first check signature
+ */
+ peer_host_id = hip_get_param(r1, HIP_PARAM_HOST_ID);
+ if (!peer_host_id) {
+ HIP_ERROR("No HOST_ID found in R1\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ err = hip_verify_packet_signature2(r1, peer_host_id);
+ if (err) {
+ HIP_ERROR("Verification of R1 signature failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ HIP_DEBUG("SIGNATURE in R1 ok\n");
+
+ /* R1 generation check */
+
+ /* we have problems with creating precreated R1s in reasonable
+ fashion... so we don't mind about generations */
+ r1cntr = hip_get_param(r1, HIP_PARAM_R1_COUNTER);
+#if 0
+ if (r1cntr) {
+ err = -EINVAL;
+ HIP_LOCK_HA(entry);
+ if (entry->state == HIP_STATE_I2_SENT) {
+ if (entry->birthday) {
+ if (entry->birthday < r1cntr->generation)
+ /* perhaps changing the state should be performed somewhere else. */
+ entry->state = HIP_STATE_I1_SENT;
+ else {
+ /* dropping due to generation check */
+ HIP_UNLOCK_HA(entry);
+ HIP_INFO("Dropping R1 due to the generation counter being too small\n");
+ goto out_err;
+ }
+ }
+ }
+ HIP_UNLOCK_HA(entry);
+ }
+#endif
+ /* Do control bit stuff here... */
+
+ /* validate HIT against received host id */
+ {
+ struct in6_addr tmphit;
+
+ hip_host_id_to_hit(peer_host_id, &tmphit,
+ HIP_HIT_TYPE_HASH126);
+
+ if (ipv6_addr_cmp(&tmphit, &r1->hits) != 0) {
+ HIP_ERROR("Sender HIT does not match the advertised host_id\n");
+ HIP_DEBUG_HIT("received", &r1->hits);
+ HIP_DEBUG_HIT("calculated", &tmphit);
+ err = -EINVAL;
+ goto out_err;
+ }
+ }
+
+ /* We must store the R1 generation counter, _IF_ it exists */
+ if (r1cntr) {
+ HIP_DEBUG("Storing R1 generation counter\n");
+ HIP_LOCK_HA(entry);
+ entry->birthday = r1cntr->generation;
+ HIP_UNLOCK_HA(entry);
+ }
+
+ /* solve puzzle */
+ {
+ struct hip_puzzle *pz;
+
+ pz = hip_get_param(r1, HIP_PARAM_PUZZLE);
+ if (!pz) {
+ HIP_ERROR("Malformed R1 packet. PUZZLE parameter missing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ solved_puzzle = hip_solve_puzzle(pz, r1, HIP_SOLVE_PUZZLE);
+ if (solved_puzzle == 0) {
+ /* we should communicate to lower levels that we need a
+ * retransmission of I1
+ */
+ HIP_ERROR("Solving of puzzle failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ }
+
+ HIP_DEBUG("Puzzle solved successfully\n");
+
+ /* calculate shared secret and create keying material */
+ ctx->dh_shared_key = NULL;
+ err = hip_produce_keying_material(r1, ctx);
+ if (err) {
+ HIP_ERROR("Could not produce keying material\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Everything ok, save host id to db */
+ {
+ char *str;
+ int len;
+
+ if (hip_get_param_host_id_di_type_len(peer_host_id, &str,
+ &len) < 0)
+ goto out_err;
+ HIP_DEBUG("Identity type: %s, Length: %d, Name: %s\n",
+ str, len, hip_get_param_host_id_hostname(peer_host_id));
+ }
+
+ peer_lhi.anonymous = 0;
+ ipv6_addr_copy(&peer_lhi.hit, &r1->hits);
+
+ err = hip_add_host_id(HIP_DB_PEER_HID, &peer_lhi,
+ peer_host_id);
+ if (err == -EEXIST) {
+ HIP_INFO("Host id already exists. Ignoring.\n");
+ err = 0;
+ } else if (err) {
+ HIP_ERROR("Failed to add peer host id to the database\n");
+ goto out_err;
+ }
+
+ entry->peer_controls = ntohs(r1->control);
+
+ HIP_DEBUG("R1 Successfully received\n");
+
+ err = hip_create_i2(ctx, solved_puzzle, entry);
+ if (err) {
+ HIP_ERROR("Creation of I2 failed (%d)\n", err);
+ goto out_err;
+ }
+
+ HIP_DEBUG("Created I2 successfully\n");
+
+ out_err:
+ if (ctx->dh_shared_key)
+ kfree(ctx->dh_shared_key);
+ if (ctx)
+ kfree(ctx);
+ return err;
+}
+
+/**
+ * hip_receive_r1 - receive an R1 packet
+ * @skb: sk_buff that contains the HIP packet
+ *
+ * This is the initial function which is called when an R1 packet is
+ * received. First we check if we have sent the corresponding I1. If
+ * yes, then the received R1 is handled in hip_handle_r1. In
+ * established state we also handle the R1. Otherwise the packet is
+ * dropped and not handled in any way.
+ *
+ * Always frees the skb
+ */
+int hip_receive_r1(struct sk_buff *skb)
+{
+ struct hip_common *hip_common;
+ hip_ha_t *entry;
+ int state, mask;
+ int err = 0;
+
+ HIP_DEBUG("Received R1\n");
+
+ hip_common = (struct hip_common*) (skb)->h.raw;
+
+ if (ipv6_addr_any(&hip_common->hitr)) {
+ HIP_DEBUG("Received NULL receiver HIT in R1. Not dropping\n");
+ }
+
+ //mask = HIP_CONTROL_CERTIFICATES | HIP_CONTROL_HIT_ANON |
+ // HIP_CONTROL_RVS_CAPABLE;
+ // mask |= HIP_CONTROL_SHT_MASK | HIP_CONTROL_DHT_MASK; /* test */
+
+ mask = hip_create_control_flags(1, 1, HIP_CONTROL_SHT_ALL,
+ HIP_CONTROL_DHT_ALL);
+ if (!hip_controls_sane(ntohs(hip_common->control), mask)) {
+ HIP_ERROR("Received illegal controls in R1: 0x%x Dropping\n",
+ ntohs(hip_common->control));
+ goto out_drop;
+ }
+
+ entry = hip_hadb_find_byhit(&hip_common->hits);
+ HIP_DEBUG_HIT("RECEIVE R1 SENDER HIT: ", &hip_common->hits);
+ if (!entry) {
+ err = -EFAULT;
+ HIP_ERROR("Received R1 with no local state. Dropping\n");
+ goto out_drop;
+ }
+
+ /* An implicit and insecure REA. If sender's address is different than
+ * the one that was mapped, then we will overwrite the mapping with
+ * the newer address.
+ * This enables us to use the rendezvous server, while not supporting
+ * the REA TLV.
+ */
+
+ {
+ struct in6_addr daddr;
+
+ hip_hadb_get_peer_addr(entry, &daddr);
+ if (ipv6_addr_cmp(&daddr, &skb->nh.ipv6h->saddr) != 0) {
+ HIP_DEBUG("Mapped address didn't match received address\n");
+ HIP_DEBUG("Assuming that the mapped address was actually RVS's.\n");
+ HIP_HEXDUMP("Mapping", &daddr, 16);
+ HIP_HEXDUMP("Received", &skb->nh.ipv6h->saddr, 16);
+ hip_hadb_delete_peer_addrlist_one(entry, &daddr);
+ hip_hadb_add_peer_addr(entry, &skb->nh.ipv6h->saddr,
+ 0,
+ 0,
+ PEER_ADDR_STATE_ACTIVE);
+ }
+
+ }
+
+ /* since the entry is in the hit-list and since the previous
+ * function increments by one, we must have at least 2 references
+ */
+ HIP_ASSERT(atomic_read(&entry->refcnt) >= 2);
+
+ /* I hope wmb() takes care of the locking needs */
+ wmb();
+ state = entry->state;
+
+ HIP_DEBUG("entry->state is %s\n", hip_state_str(state));
+ switch(state) {
+ case HIP_STATE_I1_SENT:
+ case HIP_STATE_I2_SENT:
+ /* E1. The normal case. Process, send I2, goto E2. */
+ err = hip_handle_r1(skb, entry);
+ HIP_LOCK_HA(entry);
+ if (err < 0)
+ HIP_ERROR("Handling of R1 failed\n");
+ else {
+ if (state == HIP_STATE_I1_SENT)
+ entry->state = HIP_STATE_I2_SENT;
+ }
+ HIP_UNLOCK_HA(entry);
+ break;
+ case HIP_STATE_R2_SENT:
+ /* E2. Drop and stay. */
+ HIP_ERROR("Received R1 in state R2_SENT. Dropping\n");
+ break;
+ case HIP_STATE_ESTABLISHED:
+ HIP_ERROR("Received R1 in state ESTABLISHED. Dropping\n");
+ break;
+ case HIP_STATE_REKEYING:
+ HIP_ERROR("Received R1 in state REKEYING. Dropping\n");
+ break;
+ case HIP_STATE_NONE:
+ case HIP_STATE_UNASSOCIATED:
+ default:
+ /* Can't happen. */
+ err = -EFAULT;
+ HIP_ERROR("R1 received in odd state: %d. Dropping.\n", state);
+ break;
+ }
+
+ hip_put_ha(entry);
+ out_drop:
+ return err;
+}
+
+/**
+ * hip_create_r2 - Creates and transmits R2 packet.
+ * @ctx: Context of processed I2 packet.
+ * @entry: HA
+ *
+ * Returns: 0 on success, < 0 on error.
+ */
+int hip_create_r2(struct hip_context *ctx, hip_ha_t *entry)
+{
+ uint32_t spi_in;
+ struct hip_host_id *host_id_private = NULL, *host_id_public = NULL;
+ struct hip_common *r2 = NULL;
+ struct hip_common *i2;
+ int err = 0;
+ //int algo = 0;
+ int clear = 0;
+ u8 *signature;
+// u8 signature[HIP_DSA_SIGNATURE_LEN];
+ uint16_t mask;
+#ifdef CONFIG_HIP_RVS
+ int create_rva = 0;
+#endif
+
+ HIP_DEBUG("\n");
+
+ /* assume already locked entry */
+ i2 = ctx->input;
+
+ /* Build and send R2
+ IP ( HIP ( SPI, HMAC, HIP_SIGNATURE ) ) */
+ r2 = hip_msg_alloc();
+ if (!r2) {
+ err = -ENOMEM;
+ HIP_ERROR("No memory for R2\n");
+ goto out_err;
+ }
+
+ /* just swap the addresses to use the I2's destination HIT as
+ * the R2's source HIT */
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(r2, HIP_R2, mask,
+ &ctx->input->hitr, &ctx->input->hits);
+
+ /********** SPI_LSI **********/
+ barrier();
+ //HIP_DEBUG("entry should have only one spi_in now, fix\n");
+ spi_in = hip_hadb_get_latest_inbound_spi(entry);
+
+ err = hip_build_param_spi(r2, spi_in);
+ if (err) {
+ HIP_ERROR("building of SPI_LSI failed (err=%d)\n", err);
+ goto out_err;
+ }
+
+#ifdef CONFIG_HIP_RVS
+ /* Do the Rendezvous functionality */
+ {
+ struct hip_rva_request *rreq;
+ int rva_types[4] = {0};
+ int num;
+ uint32_t lifetime;
+
+ rreq = hip_get_param(i2, HIP_PARAM_RVA_REQUEST);
+ if (!rreq)
+ goto next_hmac;
+
+ num = hip_select_rva_types(rreq, rva_types, 4);
+ if (!num) {
+ HIP_ERROR("None of the RVA types were accepted. Abandoning connection\n");
+ rva_types[0] = 0;
+ num = 1;
+ }
+
+ lifetime = ntohl(rreq->lifetime);
+ if (lifetime > HIP_DEFAULT_RVA_LIFETIME)
+ lifetime = HIP_DEFAULT_RVA_LIFETIME;
+
+ err = hip_build_param_rva(r2, lifetime, rva_types, num, 0);
+ if (err) {
+ HIP_ERROR("Building of RVA_REPLY failed\n");
+ goto out_err;
+ }
+
+ create_rva = 1;
+ }
+
+ next_hmac:
+#endif
+
+ /*********** HMAC2 ************/
+ {
+ struct hip_crypto_key hmac;
+
+ host_id_public =
+ hip_get_any_localhost_public_key(HIP_HI_DEFAULT_ALGO);
+
+ HIP_HEXDUMP("host id for HMAC2", host_id_public,
+ hip_get_param_total_len(host_id_public));
+
+ memcpy(&hmac, &entry->hip_hmac_out, sizeof(hmac));
+ err = hip_build_param_hmac2_contents(r2, &hmac,
+ host_id_public);
+ if (err) {
+ HIP_ERROR("Building of hmac failed (%d)\n", err);
+ goto out_err;
+ }
+ }
+
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not get own host identity. Can not sign data\n");
+ goto out_err;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ signature = kmalloc(HIP_RSA_SIGNATURE_LEN, GFP_KERNEL);
+ } else {
+ signature = kmalloc(HIP_DSA_SIGNATURE_LEN, GFP_KERNEL);
+ }
+
+ if (!signature) {
+ HIP_ERROR("No mem\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ if (!hip_create_signature(r2, hip_get_msg_total_len(r2),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not sign R2. Failing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(r2, signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(r2, signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of signature failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* Send the packet */
+ HIP_DEBUG("R2 created successfully, sending\n");
+ err = hip_csum_send(NULL, &(ctx->skb_in->nh.ipv6h->saddr), r2);
+ if (err) {
+ HIP_ERROR("csum_send failed\n");
+ }
+
+#ifdef CONFIG_HIP_RVS
+ if (create_rva) {
+ HIP_RVA *rva;
+
+ rva = hip_ha_to_rva(entry, GFP_KERNEL);
+ if (!rva) {
+ /* RVA could not be created... notify the initiator */
+ err = -ENOSYS;
+ goto out_err;
+ }
+
+ err = hip_rva_insert(rva);
+ if (err)
+ HIP_ERROR("Error while inserting RVA into hash table\n");
+
+ hip_put_rva(rva);
+ }
+#endif
+ out_err:
+ if (host_id_public)
+ kfree(host_id_public);
+ if (host_id_private)
+ kfree(host_id_private);
+ if (r2)
+ kfree(r2);
+ if (clear && entry) {/* Hmm, check */
+ HIP_ERROR("TODO: about to do hip_put_ha, should this happen here ?\n");
+ hip_put_ha(entry);
+ }
+ return err;
+}
+
+
+/**
+ * hip_handle_i2 - handle incoming I2 packet
+ * @skb: sk_buff where the HIP packet is in
+ * @ha: HIP HA corresponding to the peer
+ *
+ * This function is the actual point from where the processing of I2
+ * is started and corresponding R2 is created.
+ *
+ * On success (I2 payloads are checked and R2 is created and sent) 0 is
+ * returned, otherwise < 0.
+ */
+int hip_handle_i2(struct sk_buff *skb, hip_ha_t *ha)
+{
+ int err = 0;
+ struct hip_common *i2 = NULL;
+ struct hip_context *ctx = NULL;
+ struct hip_tlv_common *param;
+ char *tmp_enc = NULL, *enc = NULL;
+ struct hip_host_id *host_id_in_enc = NULL;
+ struct hip_r1_counter *r1cntr;
+ struct hip_lhi lhi;
+ struct hip_spi *hspi = NULL;
+ hip_ha_t *entry = ha;
+ hip_transform_suite_t esp_tfm, hip_tfm;
+ uint32_t spi_in, spi_out;
+ uint16_t crypto_len;
+ char *iv;
+ struct in6_addr hit;
+ struct hip_spi_in_item spi_in_data;
+ HIP_DEBUG("\n");
+
+ /* assume already locked ha, if ha is not NULL */
+
+ ctx = kmalloc(sizeof(struct hip_context), GFP_KERNEL);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memset(ctx, 0, sizeof(struct hip_context));
+
+ ctx->skb_in = skb;
+ i2 = (struct hip_common*) skb->h.raw;
+ ctx->input = (struct hip_common*) skb->h.raw;
+
+ /* Check packet validity */
+ /* We MUST check that the responder HIT is one of ours. */
+ /* check the generation counter */
+ /* We do not support generation counter (our precreated R1s suck) */
+
+ r1cntr = hip_get_param(ctx->input, HIP_PARAM_R1_COUNTER);
+#if 0
+ if (!r1cntr) {
+ /* policy decision... */
+ HIP_DEBUG("No R1 COUNTER in I2. Default policy is to drop the packet\n");
+ err = -ENOMSG;
+ goto out_err;
+ }
+
+ err = hip_verify_generation(&skb->nh.ipv6h->saddr,
+ &skb->nh.ipv6h->daddr,
+ r1cntr->generation);
+ if (err) {
+ HIP_ERROR("Birthday check failed\n");
+ goto out_err;
+ }
+
+#endif
+
+ /* check solution for cookie */
+ {
+ struct hip_solution *sol;
+
+ sol = hip_get_param(ctx->input, HIP_PARAM_SOLUTION);
+ if (!sol) {
+ HIP_ERROR("Invalid I2: SOLUTION parameter missing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (!hip_verify_cookie(&skb->nh.ipv6h->saddr,
+ &skb->nh.ipv6h->daddr,
+ i2, sol)) {
+ HIP_ERROR("Cookie solution rejected\n");
+ err = -ENOMSG;
+ goto out_err;
+ }
+ }
+
+ /* Check HIP and ESP transforms, and produce keying material */
+
+ ctx->dh_shared_key = NULL;
+ err = hip_produce_keying_material(ctx->input, ctx);
+ if (err) {
+ HIP_ERROR("Unable to produce keying material. Dropping I2\n");
+ goto out_err;
+ }
+
+ /* verify HMAC */
+ err = hip_verify_packet_hmac(i2, &ctx->hip_hmac_in);
+ if (err) {
+ HIP_ERROR("HMAC validation on r1 failed\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ /* decrypt the HOST_ID and verify it against the sender HIT */
+ enc = hip_get_param(ctx->input, HIP_PARAM_ENCRYPTED);
+ if (!enc) {
+ err = -ENOENT;
+ HIP_ERROR("Could not find enc parameter\n");
+ goto out_err;
+ }
+
+ tmp_enc = kmalloc(hip_get_param_total_len(enc), GFP_KERNEL);
+ if (!tmp_enc) {
+ HIP_ERROR("No memory for temporary host_id\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* little workaround...
+ * We have a function that calculates sha1 digest and then verifies the
+ * signature. But since the sha1 digest in I2 must be calculated over
+ * the encrypted data, and the signature requires that the encrypted
+ * data to be decrypted (it contains peer's host identity),
+ * we are forced to do some temporary copying...
+ * If ultimate speed is required, then calculate the digest here as
+ * usual and feed it to signature verifier.
+ */
+
+ memcpy(tmp_enc, enc, hip_get_param_total_len(enc));
+
+ /* Decrypt ENCRYPTED field*/
+ _HIP_HEXDUMP("Recv. Key", &ctx->hip_enc_in.key, 24);
+ param = hip_get_param(ctx->input, HIP_PARAM_HIP_TRANSFORM);
+ if (!param) {
+ err = -ENOENT;
+ HIP_ERROR("Did not find HIP transform\n");
+ goto out_err;
+ }
+
+ hip_tfm = hip_get_param_transform_suite_id(param, 0);
+ if (hip_tfm == 0) {
+ HIP_ERROR("Bad HIP transform\n");
+ err = -EFAULT;
+ goto out_err;
+ }
+
+ switch (hip_tfm) {
+ case HIP_HIP_AES_SHA1:
+ host_id_in_enc = (struct hip_host_id *)
+ (tmp_enc + sizeof(struct hip_encrypted_aes_sha1));
+ iv = ((struct hip_encrypted_aes_sha1 *) tmp_enc)->iv;
+ /* 4 = reserved, 16 = iv */
+ crypto_len = hip_get_param_contents_len(enc) - 4 - 16;
+ HIP_DEBUG("aes crypto len: %d", crypto_len);
+ break;
+ case HIP_HIP_3DES_SHA1:
+ host_id_in_enc = (struct hip_host_id *)
+ (tmp_enc + sizeof(struct hip_encrypted_3des_sha1));
+ iv = ((struct hip_encrypted_3des_sha1 *) tmp_enc)->iv;
+ /* 4 = reserved, 8 = iv */
+ crypto_len = hip_get_param_contents_len(enc) - 4 - 8;
+ break;
+ case HIP_HIP_NULL_SHA1:
+ host_id_in_enc = (struct hip_host_id *)
+ (tmp_enc + sizeof(struct hip_encrypted_null_sha1));
+ iv = NULL;
+ /* 4 = reserved */
+ crypto_len = hip_get_param_contents_len(enc) - 4;
+ break;
+ default:
+ HIP_ERROR("Unknown HIP transform: %d\n", hip_tfm);
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ HIP_DEBUG("\n");
+ err = hip_crypto_encrypted(host_id_in_enc, iv, hip_tfm,
+ crypto_len, &ctx->hip_enc_in.key,
+ HIP_DIRECTION_DECRYPT);
+ if (err) {
+ err = -EINVAL;
+ HIP_ERROR("Decryption of Host ID failed\n");
+ goto out_err;
+ }
+
+ if (hip_get_param_type(host_id_in_enc) != HIP_PARAM_HOST_ID) {
+ err = -EINVAL;
+ HIP_ERROR("The decrypted parameter is not a host id\n");
+ goto out_err;
+ }
+
+ HIP_HEXDUMP("Decrypted HOST_ID", host_id_in_enc,
+ hip_get_param_total_len(host_id_in_enc));
+
+ /* Verify sender HIT */
+ if (hip_host_id_to_hit(host_id_in_enc, &hit,
+ HIP_HIT_TYPE_HASH126)) {
+ HIP_ERROR("Unable to verify sender's HOST_ID\n");
+ err = -1;
+ goto out_err;
+ }
+
+ if (ipv6_addr_cmp(&hit, &i2->hits) != 0) {
+ HIP_ERROR("Sender's HIT does not match advertised public key\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* HMAC cannot be validated until we draw key material */
+
+ /* NOTE! The original packet has the data still encrypted. But this is
+ * not a problem, since we have decrypted the data into a temporary
+ * storage and nobody uses the data in the original packet.
+ */
+
+ /* validate signature */
+
+ // XX CHECK: is the host_id_in_enc correct??! it points to the temp
+ err = hip_verify_packet_signature(ctx->input, host_id_in_enc);
+ if (err) {
+ HIP_ERROR("Verification of I2 signature failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ _HIP_DEBUG("SIGNATURE in I2 ok\n");
+
+
+ /* do the rest */
+
+
+ /* Add peer's host id to peer_id database (is there need to
+ do this?) */
+ {
+ char *str;
+ int len;
+
+ if (hip_get_param_host_id_di_type_len(host_id_in_enc, &str,
+ &len) < 0)
+ goto out_err;
+
+ HIP_DEBUG("Identity type: %s, Length: %d, Name: %s\n",
+ str, len, hip_get_param_host_id_hostname(host_id_in_enc));
+ }
+
+ lhi.anonymous = 0;
+ ipv6_addr_copy(&lhi.hit, &ctx->input->hits);
+
+ err = hip_add_host_id(HIP_DB_PEER_HID, &lhi, host_id_in_enc);
+ if (err == -EEXIST) {
+ err = 0;
+ HIP_INFO("Host id already exists. Ignoring.\n");
+ } else if (err) {
+ HIP_ERROR("Could not store peer's identity\n");
+ goto out_err;
+ }
+
+ /* Create state (if not previously done) */
+ if (!entry) {
+ /* we have no previous infomation on the peer, create
+ * a new HIP HA */
+ entry = hip_hadb_create_state(GFP_KERNEL);
+ if (!entry) {
+ HIP_ERROR("Failed to create or find entry\n");
+ err = -ENOMSG;
+ goto out_err;
+ }
+
+ /* the rest of the code assume already locked entry,
+ * so lock the newly created entry as well */
+ HIP_LOCK_HA(entry);
+
+ ipv6_addr_copy(&entry->hit_peer, &i2->hits);
+ ipv6_addr_copy(&entry->hit_our, &i2->hitr);
+ HIP_DEBUG("Inserting state\n");
+ hip_hadb_insert_state(entry);
+ hip_hold_ha(entry);
+ /* entry unlock is done below, ok ? */
+ //HIP_UNLOCK_HA(entry);
+ /* insert automatically holds for the data structure
+ * references, but since we continue to use the entry,
+ * we have to hold for our own usage too
+ */
+ }
+
+ /* If we have old SAs with these HITs delete them */
+ hip_delete_esp(entry);
+
+ {
+ struct hip_esp_transform *esp_tf;
+ struct hip_spi_out_item spi_out_data;
+
+ esp_tf = hip_get_param(ctx->input, HIP_PARAM_ESP_TRANSFORM);
+ if (!esp_tf) {
+ err = -ENOENT;
+ HIP_ERROR("Did not find ESP transform on i2\n");
+ goto out_err;
+ }
+
+ hspi = hip_get_param(ctx->input, HIP_PARAM_SPI);
+ if (!hspi) {
+ err = -ENOENT;
+ HIP_ERROR("Did not find SPI LSI on i2\n");
+ goto out_err;
+ }
+
+ if (r1cntr)
+ entry->birthday = r1cntr->generation;
+ entry->peer_controls |= ntohs(i2->control);
+ ipv6_addr_copy(&entry->hit_our, &i2->hitr);
+ ipv6_addr_copy(&entry->hit_peer, &i2->hits);
+
+ /* move this below setup_sa */
+ memset(&spi_out_data, 0, sizeof(struct hip_spi_out_item));
+ spi_out_data.spi = ntohl(hspi->spi);
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_OUT,
+ &spi_out_data);
+ if (err) {
+ goto out_err;
+ }
+ entry->esp_transform = hip_select_esp_transform(esp_tf);
+ esp_tfm = entry->esp_transform;
+
+ if (esp_tfm == 0) {
+ HIP_ERROR("Could not select proper ESP transform\n");
+ goto out_err;
+ }
+ }
+
+ err = hip_hadb_add_peer_addr(entry, &(ctx->skb_in->nh.ipv6h->saddr),
+ 0, 0, PEER_ADDR_STATE_ACTIVE);
+ if (err) {
+ HIP_ERROR("error while adding a new peer address\n");
+ goto out_err;
+ }
+
+ /* Set up IPsec associations */
+
+
+ spi_in = 0;
+ err = hip_setup_sa(&i2->hits, &i2->hitr, &spi_in, esp_tfm,
+ &ctx->esp_in.key, &ctx->auth_in.key, 0,
+ HIP_SPI_DIRECTION_IN);
+
+ if (err) {
+ HIP_ERROR("failed to setup IPsec SPD/SA entries, peer:src (err=%d)\n", err);
+ if (err == -EEXIST)
+ HIP_ERROR("SA for SPI 0x%x already exists, this is perhaps a bug\n",
+ spi_in);
+ hip_delete_esp(entry);
+ goto out_err;
+ }
+ /* XXX: Check -EAGAIN */
+
+ /* ok, found an unused SPI to use */
+ HIP_DEBUG("set up inbound IPsec SA, SPI=0x%x (host)\n",
+ spi_in);
+
+
+ barrier();
+ spi_out = ntohl(hspi->spi);
+ HIP_DEBUG("setting up outbound IPsec SA, SPI=0x%x\n", spi_out);
+ err = hip_setup_sa(&i2->hitr, &i2->hits, &spi_out, esp_tfm,
+ &ctx->esp_out.key, &ctx->auth_out.key, 0,
+ HIP_SPI_DIRECTION_OUT);
+ if (err == -EEXIST) {
+ HIP_DEBUG("SA already exists for the SPI=0x%x\n", spi_out);
+ HIP_DEBUG("TODO: what to do ? currently ignored\n");
+ } else if (err) {
+ HIP_ERROR("failed to setup IPsec SPD/SA entries, peer:dst (err=%d)\n", err);
+ /* delete all IPsec related SPD/SA for this entry */
+ hip_delete_esp(entry);
+ goto out_err;
+ }
+ /* XXX: Check if err = -EAGAIN... */
+ HIP_DEBUG("set up outbound IPsec SA, SPI=0x%x\n", spi_out);
+
+ /* source IPv6 address is implicitly the preferred
+ * address after the base exchange */
+ err = hip_hadb_add_addr_to_spi(entry, spi_out,
+ &ctx->skb_in->nh.ipv6h->saddr,
+ 1, 0, 1);
+ _HIP_DEBUG("add spi err ret=%d\n", err);
+ if (err) {
+ HIP_ERROR("failed to add an address to SPI list\n");
+ goto out_err;
+ }
+
+ memset(&spi_in_data, 0, sizeof(struct hip_spi_in_item));
+ spi_in_data.spi = spi_in;
+ spi_in_data.ifindex = hip_ipv6_devaddr2ifindex(&skb->nh.ipv6h->daddr);
+ if (spi_in_data.ifindex) {
+ HIP_DEBUG("ifindex=%d\n", spi_in_data.ifindex);
+ } else
+ HIP_ERROR("Couldn't get device ifindex of address\n");
+
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_IN, &spi_in_data);
+ if (err) {
+ HIP_UNLOCK_HA(entry);
+ goto out_err;
+ }
+
+ entry->default_spi_out = spi_out;
+ HIP_DEBUG("set default SPI out=0x%x\n", spi_out);
+
+ err = hip_store_base_exchange_keys(entry, ctx, 0);
+ if (err) {
+ HIP_DEBUG("hip_store_base_exchange_keys failed\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("Inserting state\n");
+ hip_hadb_insert_state(entry);
+
+ err = hip_create_r2(ctx, entry);
+ HIP_DEBUG("hip_create_r2 returned %d\n", err);
+ if (err) {
+ HIP_ERROR("Creation of R2 failed\n");
+ goto out_err;
+ }
+
+ /* change SA state from ACQ -> VALID, and wake up sleepers */
+ hip_finalize_sa(&i2->hits, spi_out);
+ hip_finalize_sa(&i2->hitr, spi_in);
+
+ /* we cannot do this outside (in hip_receive_i2) since we don't have
+ the entry there and looking it up there would be unneccesary waste
+ of cycles */
+ if (!ha && entry) {
+ wmb();
+#ifdef CONFIG_HIP_RVS
+ /* XX FIX: this should be dynamic (the rvs information should
+ be stored in the HADB) instead of static */
+ entry->state = HIP_STATE_ESTABLISHED;
+#else
+ entry->state = HIP_STATE_R2_SENT;
+#endif /* CONFIG_HIP_RVS */
+ }
+
+ HIP_DEBUG("Reached %s state\n", hip_state_str(entry->state));
+
+ out_err:
+ /* ha is not NULL if hip_receive_i2() fetched the HA for us.
+ * In that case we must not release our reference to it.
+ * Otherwise, if 'ha' is NULL, then we created the HIP HA in this
+ * function and we should free the reference.
+ */
+ if (!ha) {
+ if (entry) {
+ /* unlock the entry created in this function */
+ HIP_UNLOCK_HA(entry);
+ hip_put_ha(entry);
+ }
+ }
+ if (tmp_enc)
+ kfree(tmp_enc);
+ if (ctx->dh_shared_key)
+ kfree(ctx->dh_shared_key);
+ if (ctx)
+ kfree(ctx);
+
+ return err;
+}
+
+/**
+ * hip_receive_i2 - receive I2 packet
+ * @skb: sk_buff where the HIP packet is in
+ *
+ * This is the initial function which is called when an I2 packet is
+ * received. If we are in correct state, the packet is handled to
+ * hip_handle_i2() for further processing.
+ *
+ * Returns always 0.
+ *
+ * TODO: check if it is correct to return always 0
+ */
+int hip_receive_i2(struct sk_buff *skb)
+{
+ struct hip_common *i2;
+ int state = 0;
+ int err = 0;
+ hip_ha_t *entry;
+ uint16_t mask;
+
+ i2 = (struct hip_common*) (skb)->h.raw;
+
+ HIP_DEBUG("\n");
+
+ if (ipv6_addr_any(&i2->hitr)) {
+ HIP_ERROR("Received NULL receiver HIT in I2. Dropping\n");
+ goto out;
+ }
+
+ mask = hip_create_control_flags(1, 1, HIP_CONTROL_SHT_ALL,
+ HIP_CONTROL_DHT_ALL);
+ if (!hip_controls_sane(ntohs(i2->control), mask
+ //HIP_CONTROL_CERTIFICATES | HIP_CONTROL_HIT_ANON |
+ //HIP_CONTROL_RVS_CAPABLE
+ // | HIP_CONTROL_SHT_MASK | HIP_CONTROL_DHT_MASK)) {
+ )) {
+ HIP_ERROR("Received illegal controls in I2: 0x%x. Dropping\n",
+ ntohs(i2->control));
+ goto out;
+ }
+
+ entry = hip_hadb_find_byhit(&i2->hits);
+ if (!entry) {
+ state = HIP_STATE_UNASSOCIATED;
+ } else {
+ barrier();
+ HIP_LOCK_HA(entry);
+ state = entry->state;
+ }
+
+ switch(state) {
+ case HIP_STATE_UNASSOCIATED:
+ /* possibly no state created yet */
+ err = hip_handle_i2(skb, NULL);
+ break;
+ case HIP_STATE_I1_SENT:
+ case HIP_STATE_I2_SENT:
+ case HIP_STATE_R2_SENT:
+ err = hip_handle_i2(skb, entry);
+ if (!err)
+ entry->state = HIP_STATE_R2_SENT;
+ break;
+ case HIP_STATE_ESTABLISHED:
+ HIP_DEBUG("Received I2 in state ESTABLISHED\n");
+ err = hip_handle_i2(skb, entry);
+ if (!err)
+ entry->state = HIP_STATE_R2_SENT;
+ break;
+ case HIP_STATE_REKEYING:
+ HIP_DEBUG("Received I2 in state REKEYING\n");
+ err = hip_handle_i2(skb, entry);
+ if (!err)
+ entry->state = HIP_STATE_R2_SENT;
+ default:
+ HIP_ERROR("Internal state (%d) is incorrect\n", state);
+ break;
+ }
+
+ if (entry) {
+ HIP_UNLOCK_HA(entry);
+ hip_put_ha(entry);
+ }
+ out:
+ return err;
+}
+
+
+/**
+ * hip_handle_r2 - handle incoming R2 packet
+ * @skb: sk_buff where the HIP packet is in
+ * @entry: HA
+ *
+ * This function is the actual point from where the processing of R2
+ * is started.
+ *
+ * On success (payloads are created and IPsec is set up) 0 is
+ * returned, otherwise < 0.
+ */
+int hip_handle_r2(struct sk_buff *skb, hip_ha_t *entry)
+{
+ int err = 0;
+ uint16_t len;
+ struct hip_context *ctx = NULL;
+ struct in6_addr *sender;
+ struct hip_host_id *peer_id = NULL;
+ struct hip_lhi peer_lhi;
+ struct hip_spi *hspi = NULL;
+ struct hip_sig *sig = NULL;
+ struct hip_common *r2 = NULL;
+ struct hip_spi_out_item spi_out_data;
+ int tfm;
+ uint32_t spi_recvd, spi_in;
+
+ /* assume already locked entry */
+
+ HIP_DEBUG("Entering handle_r2\n");
+
+ ctx = kmalloc(sizeof(struct hip_context), GFP_ATOMIC);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto out_err;
+ }
+ memset(ctx, 0, sizeof(struct hip_context));
+ ctx->skb_in = skb;
+ ctx->input = (struct hip_common *) skb->h.raw;
+ r2 = ctx->input;
+
+ sender = &r2->hits;
+
+ peer_lhi.anonymous = 0;
+ ipv6_addr_copy(&peer_lhi.hit, &r2->hits);
+
+ peer_id = hip_get_host_id(HIP_DB_PEER_HID, &peer_lhi);
+ if (!peer_id) {
+ HIP_ERROR("Unknown peer (no identity found)\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* verify HMAC */
+ err = hip_verify_packet_hmac2(r2, &entry->hip_hmac_in, peer_id);
+ if (err) {
+ HIP_ERROR("HMAC validation on R2 failed\n");
+ goto out_err;
+ }
+ _HIP_DEBUG("HMAC in R2 ok\n");
+
+ _HIP_DUMP_MSG(r2);
+
+ /* signature validation */
+
+ sig = hip_get_param(r2, HIP_PARAM_HIP_SIGNATURE);
+ if (!sig) {
+ err = -ENOENT;
+ HIP_ERROR("No signature found\n");
+ goto out_err;
+ }
+
+ HIP_DUMP_MSG(r2);
+
+ hip_zero_msg_checksum(r2);
+ len = (u8*) sig - (u8*) r2;
+ hip_set_msg_total_len(r2, len);
+
+ if (!hip_verify_signature(r2, len, peer_id,
+ (u8*)(sig + 1))) {
+ HIP_ERROR("R2 signature verification failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* The rest */
+ hspi = hip_get_param(r2, HIP_PARAM_SPI);
+ if (!hspi) {
+ HIP_ERROR("Parameter SPI not found\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ spi_recvd = ntohl(hspi->spi);
+ memset(&spi_out_data, 0, sizeof(struct hip_spi_out_item));
+ spi_out_data.spi = spi_recvd;
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_OUT, &spi_out_data);
+ if (err) {
+ goto out_err;
+ }
+ memcpy(&ctx->esp_out, &entry->esp_out, sizeof(ctx->esp_out));
+ memcpy(&ctx->auth_out, &entry->auth_out, sizeof(ctx->auth_out));
+ HIP_DEBUG("entry should have only one spi_in now, test\n");
+ spi_in = hip_hadb_get_latest_inbound_spi(entry);
+ tfm = entry->esp_transform;
+
+ err = hip_setup_sa(&r2->hitr, sender, &spi_recvd, tfm,
+ &ctx->esp_out.key, &ctx->auth_out.key, 0,
+ HIP_SPI_DIRECTION_OUT);
+ if (err == -EEXIST) {
+ HIP_DEBUG("SA already exists for the SPI=0x%x\n", spi_recvd);
+ HIP_DEBUG("TODO: what to do ? currently ignored\n");
+ } else if (err) {
+ HIP_ERROR("hip_setup_sa failed, peer:dst (err=%d)\n", err);
+ HIP_ERROR("** TODO: remove inbound IPsec SA**\n");
+ }
+ /* XXX: Check for -EAGAIN */
+ HIP_DEBUG("set up outbound IPsec SA, SPI=0x%x (host)\n", spi_recvd);
+
+ /* source IPv6 address is implicitly the preferred
+ * address after the base exchange */
+ err = hip_hadb_add_addr_to_spi(entry, spi_recvd, &skb->nh.ipv6h->saddr,
+ 1, 0, 1);
+ if (err)
+ HIP_ERROR("hip_hadb_add_addr_to_spi err=%d not handled\n", err);
+ entry->default_spi_out = spi_recvd;
+ HIP_DEBUG("set default SPI out=0x%x\n", spi_recvd);
+ _HIP_DEBUG("add spi err ret=%d\n", err);
+
+ err = hip_ipv6_devaddr2ifindex(&skb->nh.ipv6h->daddr);
+ if (err != 0) {
+ HIP_DEBUG("ifindex=%d\n", err);
+ hip_hadb_set_spi_ifindex(entry, spi_in, err);
+ } else
+ HIP_ERROR("Couldn't get device ifindex of address\n");
+ err = 0;
+
+ HIP_DEBUG("clearing the address used during the bex\n");
+ ipv6_addr_copy(&entry->bex_address, &in6addr_any);
+
+ hip_hadb_insert_state(entry);
+ /* these will change SAs' state from ACQUIRE to VALID, and
+ * wake up any transport sockets waiting for a SA */
+ hip_finalize_sa(&r2->hits, spi_recvd);
+ hip_finalize_sa(&r2->hitr, spi_in);
+
+ //HIP_DEBUG("Reached ESTABLISHED state\n"); moved to receive_r2
+
+ out_err:
+ if (peer_id)
+ kfree(peer_id);
+ if (ctx)
+ kfree(ctx);
+ return err;
+}
+
+int hip_handle_i1(struct sk_buff *skb, hip_ha_t *entry)
+{
+ int err;
+ struct hip_common *i1;
+#ifdef CONFIG_HIP_RVS
+ struct hip_from *from;
+#endif
+ struct in6_addr *dst;
+ struct in6_addr *dstip;
+
+ i1 = (struct hip_common *)skb->h.raw;
+
+ dst = &i1->hits;
+ dstip = NULL;
+
+#ifdef CONFIG_HIP_RVS
+ from = hip_get_param(i1, HIP_PARAM_FROM);
+ if (from) {
+ HIP_DEBUG("Found FROM parameter in I1\n");
+ dstip = (struct in6_addr *)&from->address;
+ if (entry) {
+ struct in6_addr daddr;
+
+ /* The entry contains wrong address mapping...
+ instead of the real IP, it has RVS's IP.
+ The RVS should probably be saved into the entry.
+ We need the RVS's IP in double-jump case.
+ */
+ hip_hadb_get_peer_addr(entry, &daddr);
+ hip_hadb_delete_peer_addrlist_one(entry, &daddr);
+ hip_hadb_add_peer_addr(entry, dst, 0, 0, PEER_ADDR_STATE_ACTIVE);
+ }
+ } else {
+ HIP_DEBUG("Didn't find FROM parameter in I1\n");
+ }
+#endif
+
+ err = hip_xmit_r1(skb, dstip, dst);
+ return err;
+}
+
+
+/**
+ * hip_receive_i1 - receive I1 packet
+ * @skb: sk_buff where the HIP packet is in
+ *
+ * This is the initial function which is called when an I1 packet is
+ * received. If we are in correct state we reply with an R1 packet.
+ *
+ * This function never writes into hip_sdb_state entries.
+ *
+ * Returns: zero on success, or negative error value on error.
+ */
+int hip_receive_i1(struct sk_buff *skb)
+{
+ struct hip_common *hip_i1 = (struct hip_common*) skb->h.raw;
+ int err = 0;
+ int state;
+ hip_ha_t *entry;
+ int mask;
+#ifdef CONFIG_HIP_RVS
+ HIP_RVA *rva;
+#endif
+
+ HIP_DEBUG("\n");
+
+ if (ipv6_addr_any(&hip_i1->hitr)) {
+ HIP_ERROR("Received NULL receiver HIT. Opportunistic HIP is not supported yet in I1. Dropping\n");
+ err = -EPROTONOSUPPORT;
+ goto out;
+ }
+ /* we support checking whether we are rvs capable even with RVS support not enabled */
+ //mask = HIP_CONTROL_NONE | HIP_CONTROL_RVS_CAPABLE;
+ //mask |= HIP_CONTROL_SHT_MASK | HIP_CONTROL_DHT_MASK; /* test */
+
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_ALL,
+ HIP_CONTROL_DHT_ALL);
+ if (!hip_controls_sane(ntohs(hip_i1->control), mask)) {
+ HIP_ERROR("Received illegal controls in I1: 0x%x. Dropping\n",
+ ntohs(hip_i1->control));
+ goto out;
+ }
+
+ entry = hip_hadb_find_byhit(&hip_i1->hits);
+ if (entry) {
+ wmb();
+ state = entry->state;
+ hip_put_ha(entry);
+ } else {
+#ifdef CONFIG_HIP_RVS
+ HIP_DEBUG("Doing RVA check\n");
+ rva = hip_rva_find_valid(&hip_i1->hitr);
+ if (rva) {
+ /* we should now relay the I1.
+ We have two options: Rewrite destination address or
+ rewrite both destination and source addresses.
+ We'll try to do the former if the destination is in the
+ same subnet, and we'll fall back to the latter in other
+ cases.
+ */
+
+ err = hip_relay_i1(skb, rva);
+ if (err)
+ HIP_ERROR("Relaying I1 failed\n");
+ else
+ HIP_DEBUG("Relayed I1\n");
+ return err;
+ }
+#endif
+ state = HIP_STATE_NONE;
+ }
+
+ HIP_DEBUG("HIP_LOCK_HA ?\n");
+
+ HIP_DEBUG("Received I1 in state %s\n", hip_state_str(state));
+ switch(state) {
+ case HIP_STATE_NONE:
+ err = hip_handle_i1(skb, NULL);
+ break;
+ case HIP_STATE_UNASSOCIATED:
+ err = hip_handle_i1(skb, entry);
+ break;
+ case HIP_STATE_I1_SENT:
+ err = hip_handle_i1(skb, entry);
+ break;
+ case HIP_STATE_I2_SENT:
+ err = hip_handle_i1(skb, entry);
+ break;
+ case HIP_STATE_R2_SENT:
+ err = hip_handle_i1(skb, entry);
+ HIP_DEBUG("Received I1 in state R2_SENT. Sent R1\n");
+ break;
+ case HIP_STATE_ESTABLISHED:
+ err = hip_handle_i1(skb, entry);
+ break;
+ case HIP_STATE_REKEYING:
+ err = hip_handle_i1(skb, entry);
+ break;
+ default:
+ /* should not happen */
+ HIP_ERROR("DEFAULT CASE, UNIMPLEMENTED STATE HANDLING OR A BUG\n");
+ err = -EINVAL;
+ break;
+ }
+
+ HIP_DEBUG("HIP_UNLOCK_HA ?\n");
+
+ out:
+ return err;
+}
+
+/**
+ * hip_receive_r2 - receive R2 packet
+ * @skb: sk_buff where the HIP packet is in
+ *
+ * This is the initial function which is called when an R1 packet is
+ * received. If we are in correct state, the packet is handled to
+ * hip_handle_r2() for further processing.
+ *
+ * Returns: 0 if R2 was processed succesfully, < 0 otherwise.
+ */
+int hip_receive_r2(struct sk_buff *skb)
+{
+ struct hip_common *hip_common;
+ hip_ha_t *entry = NULL;
+ int err = 0;
+ int state;
+ uint16_t mask;
+
+ hip_common = (struct hip_common *)skb->h.raw;
+
+ if (ipv6_addr_any(&hip_common->hitr)) {
+ HIP_ERROR("Received NULL receiver HIT in R2. Dropping\n");
+ goto out_err;
+ }
+
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_ALL,
+ HIP_CONTROL_DHT_ALL);
+ if (!hip_controls_sane(ntohs(hip_common->control), mask
+ //HIP_CONTROL_NONE | HIP_CONTROL_RVS_CAPABLE
+ //| HIP_CONTROL_SHT_MASK | HIP_CONTROL_DHT_MASK))
+ ))
+ {
+ HIP_ERROR("Received illegal controls in R2: 0x%x. Dropping\n", ntohs(hip_common->control));
+ goto out_err;
+ }
+
+ entry = hip_hadb_find_byhit(&hip_common->hits);
+ if (!entry) {
+ HIP_ERROR("Received R2 by unknown sender\n");
+ //HIP_PRINT_HIT("Sender", &hip_common->hits);
+ err = -EFAULT;
+ goto out_err;
+ }
+ HIP_LOCK_HA(entry);
+
+ state = entry->state;
+
+ switch(state) {
+ case HIP_STATE_UNASSOCIATED:
+ err = -EFAULT;
+ HIP_ERROR("Received R2 in UNASSOCIATED state. Dropping.\n");
+ break;
+ case HIP_STATE_I1_SENT:
+ HIP_ERROR("Received R2 in I1_SENT. Dropping\n");
+ err = -EFAULT;
+ break;
+ case HIP_STATE_I2_SENT:
+ /* The usual case. */
+ err = hip_handle_r2(skb, entry);
+ if (!err) {
+ entry->state = HIP_STATE_ESTABLISHED;
+ HIP_DEBUG("Reached ESTABLISHED state\n");
+ } else {
+ HIP_ERROR("hip_handle_r2 failed (err=%d)\n", err);
+ }
+ break;
+ case HIP_STATE_R2_SENT:
+ HIP_ERROR("Received R2 in R2_SENT. Dropping\n");
+ err = -EFAULT;
+ break;
+ case HIP_STATE_ESTABLISHED:
+ HIP_ERROR("Received R2 in ESTABLISHED. Dropping\n");
+ err = -EFAULT;
+ break;
+ case HIP_STATE_REKEYING:
+ HIP_ERROR("Received R2 in REKEYING. Dropping.\n");
+ err = -EFAULT;
+ break;
+ default:
+ /* Cannot happen. */
+ HIP_ERROR("Received R2. The state machine is confused. Dropping\n");
+ err = -EFAULT;
+ break;
+ }
+
+ out_err:
+ if (entry) {
+ HIP_UNLOCK_HA(entry);
+ hip_put_ha(entry);
+ }
+ return err;
+}
+
+
+/**
+ * hip_receive_notify - receive NOTIFY packet
+ * @skb: sk_buff where the HIP packet is in
+ *
+ * This is the initial function which is called when an NOTIFY packet is
+ * received.
+ *
+ * Returns: 0 if R2 was processed succesfully, < 0 otherwise.
+ */
+int hip_receive_notify(struct sk_buff *skb)
+{
+ struct hip_common *hip_common;
+ hip_ha_t *entry = NULL;
+ int err = 0;
+ struct hip_notify *notify_param;
+ uint16_t mask;
+
+ hip_common = (struct hip_common *)skb->h.raw;
+
+ HIP_HEXDUMP("Incoming NOTIFY", hip_common,
+ hip_get_msg_total_len(hip_common));
+
+ mask = hip_create_control_flags(1, 1, HIP_CONTROL_SHT_ALL,
+ HIP_CONTROL_DHT_ALL);
+ if (!hip_controls_sane(ntohs(hip_common->control), mask)) {
+ HIP_ERROR("Received illegal controls in NOTIFY: 0x%x. Dropping\n",
+ ntohs(hip_common->control));
+ goto out_err;
+ }
+
+ entry = hip_hadb_find_byhit(&hip_common->hits);
+ if (!entry) {
+ HIP_ERROR("Received NOTIFY by unknown sender\n");
+ err = -EFAULT;
+ goto out_err;
+ }
+ /* lock here */
+ /* todo: check state */
+
+ /* while (notify_param = hip_get_nth_param(msg, HIP_PARAM_NOTIFY, i)) { .. */
+
+ notify_param = hip_get_param(hip_common, HIP_PARAM_NOTIFY);
+ if (notify_param) {
+ HIP_DEBUG("NOTIFY parameter:\n");
+ HIP_DEBUG(" msgtype=%u\n", ntohs(notify_param->msgtype));
+ }
+
+ out_err:
+ if (entry) {
+ /* unlock here */
+ hip_put_ha(entry);
+ }
+ return err;
+}
+
+/**
+ * hip_handle_bos - handle incoming BOS packet
+ * @skb: sk_buff where the HIP packet is in
+ * @entry: HA
+ *
+ * This function is the actual point from where the processing of BOS
+ * is started.
+ *
+ * On success (BOS payloads are checked) 0 is returned, otherwise < 0.
+ */
+int hip_handle_bos(struct sk_buff *skb, hip_ha_t *entry)
+{
+ int err = 0;
+ struct hip_host_id *peer_host_id;
+ struct hip_common *bos = NULL;
+ struct hip_lhi peer_lhi;
+ struct in6_addr peer_hit;
+ char *str;
+ int len;
+ struct ipv6hdr *ip6hdr;
+ struct in6_addr *dstip;
+ char src[INET6_ADDRSTRLEN];
+
+ HIP_DEBUG("\n");
+
+ bos = (struct hip_common*) skb->h.raw;
+
+ /* according to the section 8.6 of the base draft,
+ * we must first check signature
+ */
+
+ peer_host_id = hip_get_param(bos, HIP_PARAM_HOST_ID);
+ if (!peer_host_id) {
+ HIP_ERROR("No HOST_ID found in BOS\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ err = hip_verify_packet_signature(bos, peer_host_id);
+ if (err) {
+ HIP_ERROR("Verification of BOS signature failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ HIP_DEBUG("SIGNATURE in BOS ok\n");
+
+ /* validate HIT against received host id */
+ hip_host_id_to_hit(peer_host_id, &peer_hit, HIP_HIT_TYPE_HASH126);
+
+ if (ipv6_addr_cmp(&peer_hit, &bos->hits) != 0) {
+ HIP_ERROR("Sender HIT does not match the advertised host_id\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Everything ok, first save host id to db */
+ if (hip_get_param_host_id_di_type_len(peer_host_id, &str, &len) < 0)
+ goto out_err;
+ HIP_DEBUG("Identity type: %s, Length: %d, Name: %s\n",
+ str, len, hip_get_param_host_id_hostname(peer_host_id));
+
+ peer_lhi.anonymous = 0;
+ ipv6_addr_copy(&peer_lhi.hit, &bos->hits);
+
+ err = hip_add_host_id(HIP_DB_PEER_HID, &peer_lhi, peer_host_id);
+ if (err == -EEXIST) {
+ HIP_INFO("Host ID already exists. Ignoring.\n");
+ err = 0;
+ } else if (err) {
+ HIP_ERROR("Failed to add peer host id to the database\n");
+ goto out_err;
+ }
+
+ /* Now save the peer IP address */
+ ip6hdr = skb->nh.ipv6h;
+ dstip = (struct in6_addr *)&ip6hdr->saddr;
+ hip_in6_ntop(dstip, src);
+ HIP_DEBUG("BOS sender IP: saddr %s\n", src);
+
+ if (entry) {
+ struct in6_addr daddr;
+
+ HIP_DEBUG("I guess we should not even get here ..\n");
+
+ /* The entry may contain the wrong address mapping... */
+ HIP_DEBUG("Updating existing entry\n");
+ hip_hadb_get_peer_addr(entry, &daddr);
+ if (ipv6_addr_cmp(&daddr, dstip) != 0) {
+ HIP_DEBUG("Mapped address doesn't match received address\n");
+ HIP_DEBUG("Assuming that the mapped address was actually RVS's.\n");
+ HIP_HEXDUMP("Mapping", &daddr, 16);
+ HIP_HEXDUMP("Received", dstip, 16);
+ hip_hadb_delete_peer_addrlist_one(entry, &daddr);
+ HIP_ERROR("assuming we are doing base exchange\n");
+ hip_hadb_add_peer_addr(entry, dstip, 0, 0, 0);
+ }
+ } else {
+ HIP_DEBUG("Adding new peer entry\n");
+ hip_in6_ntop(&bos->hits, src);
+ HIP_DEBUG("map HIT: %s\n", src);
+ hip_in6_ntop(dstip, src);
+ HIP_DEBUG("map IP: %s\n", src);
+
+ err = hip_insert_peer_map_work_order(&bos->hits, dstip, 1, 0);
+ if (err) {
+ HIP_ERROR("Failed to insert peer map work order (%d)\n", err);
+ goto out_err;
+ }
+ }
+
+ HIP_INFO("BOS Successfully received\n");
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_receive_bos - receive BOS packet
+ * @skb: sk_buff where the HIP packet is in
+ *
+ * This function is called when a BOS packet is received. We add the
+ * received HIT and HOST_ID to the database.
+ *
+ * Returns always 0.
+ *
+ * TODO: check if it is correct to return always 0
+ */
+int hip_receive_bos(struct sk_buff *skb)
+{
+ struct hip_common *bos;
+ int err = 0;
+ hip_ha_t *entry;
+ int state = 0;
+
+ bos = (struct hip_common*) (skb)->h.raw;
+
+ HIP_DEBUG("\n");
+
+ if (ipv6_addr_any(&bos->hits)) {
+ HIP_ERROR("Received NULL sender HIT in BOS. Dropping\n");
+ goto out;
+ }
+
+ if (!ipv6_addr_any(&bos->hitr)) {
+ HIP_ERROR("Received non-NULL receiver HIT in BOS. Dropping\n");
+ goto out;
+ }
+
+ entry = hip_hadb_find_byhit(&bos->hits);
+ if (!entry) {
+ state = HIP_STATE_UNASSOCIATED;
+ } else {
+ /* Received BOS packet from already known sender */
+ /* TODO: should return right now */
+ state = entry->state;
+ }
+ HIP_DEBUG("Received BOS packet in state %s\n", hip_state_str(state));
+
+ if (entry)
+ HIP_DEBUG("---LOCKING---\n");
+
+ switch(state) {
+ case HIP_STATE_UNASSOCIATED:
+ case HIP_STATE_I1_SENT:
+ case HIP_STATE_I2_SENT:
+ /* possibly no state created yet */
+ err = hip_handle_bos(skb, entry);
+ break;
+ case HIP_STATE_R2_SENT:
+ case HIP_STATE_ESTABLISHED:
+ case HIP_STATE_REKEYING:
+ HIP_DEBUG("BOS not handled in state %s\n", hip_state_str(state));
+ break;
+ default:
+ HIP_ERROR("Internal state (%d) is incorrect\n", state);
+ break;
+ }
+
+ if (entry) {
+ HIP_DEBUG("---UNLOCKING---\n");
+ hip_put_ha(entry);
+ }
+ out:
+ return err;
+}
+
+
+/**
+ * hip_verify_hmac - verify HMAC
+ * @buffer: the packet data used in HMAC calculation
+ * @hmac: the HMAC to be verified
+ * @hmac_key: integrity key used with HMAC
+ * @hmac_type: type of the HMAC digest algorithm.
+ *
+ * Returns: 0 if calculated HMAC is same as @hmac, otherwise < 0. On
+ * error < 0 is returned.
+ *
+ * FIX THE PACKET LEN BEFORE CALLING THIS FUNCTION
+ */
+static int hip_verify_hmac(struct hip_common *buffer, u8 *hmac,
+ void *hmac_key, int hmac_type)
+{
+ int err = 0;
+ u8 *hmac_res = NULL;
+
+ hmac_res = kmalloc(HIP_AH_SHA_LEN, GFP_ATOMIC);
+ if (!hmac_res) {
+ HIP_ERROR("kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("HMAC data", buffer, hip_get_msg_total_len(buffer));
+
+ if (!hip_write_hmac(hmac_type, hmac_key, buffer,
+ hip_get_msg_total_len(buffer), hmac_res)) {
+ HIP_ERROR("Could not build hmac\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("HMAC", hmac_res, HIP_AH_SHA_LEN);
+
+ if (memcmp(hmac_res, hmac, HIP_AH_SHA_LEN) != 0) {
+ HIP_DEBUG("invalid HMAC\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ out_err:
+ if (hmac_res)
+ kfree(hmac_res);
+
+ return err;
+}
+
+
+/**
+ * hip_verify_network_header - validate an incoming HIP header
+ * @hip_common: pointer to the HIP header
+ * @skb: sk_buff in which the HIP packet is in
+ *
+ * Returns: zero if the HIP message header was ok, or negative error value on
+ * failure
+ */
+int hip_verify_network_header(struct hip_common *hip_common,
+ struct sk_buff **skb)
+{
+ int err = 0;
+ uint16_t csum;
+
+ HIP_DEBUG("skb len=%d, skb data_len=%d, v6hdr payload_len=%d msgtotlen=%d\n",
+ (*skb)->len, (*skb)->data_len,
+ ntohs((*skb)->nh.ipv6h->payload_len),
+ hip_get_msg_total_len(hip_common));
+
+ if (ntohs((*skb)->nh.ipv6h->payload_len) !=
+ hip_get_msg_total_len(hip_common)) {
+ HIP_ERROR("Invalid HIP packet length (IPv6 hdr payload_len=%d/HIP pkt payloadlen=%d). Dropping\n",
+ ntohs((*skb)->nh.ipv6h->payload_len),
+ hip_get_msg_total_len(hip_common));
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Currently no support for piggybacking */
+ if (hip_common->payload_proto != IPPROTO_NONE) {
+ HIP_ERROR("Protocol in packet (%u) was not IPPROTO_NONE. Dropping\n",
+ hip_common->payload_proto);
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ if ((hip_common->ver_res & HIP_VER_MASK) != HIP_VER_RES) {
+ HIP_ERROR("Invalid version in received packet. Dropping\n");
+ err = -EPROTOTYPE;
+ goto out_err;
+ }
+
+ if (!hip_is_hit(&hip_common->hits)) {
+ HIP_ERROR("Received a non-HIT in HIT-source. Dropping\n");
+ err = -EAFNOSUPPORT;
+ goto out_err;
+ }
+
+ if (!hip_is_hit(&hip_common->hitr) &&
+ !ipv6_addr_any(&hip_common->hitr)) {
+ HIP_ERROR("Received a non-HIT or non NULL in HIT-receiver. Dropping\n");
+ err = -EAFNOSUPPORT;
+ goto out_err;
+ }
+
+ if (ipv6_addr_any(&hip_common->hits)) {
+ HIP_ERROR("Received a NULL in HIT-sender. Dropping\n");
+ err = -EAFNOSUPPORT;
+ goto out_err;
+ }
+
+ /*
+ * XX FIXME: handle the RVS case better
+ */
+ if (ipv6_addr_any(&hip_common->hitr)) {
+ /* Required for e.g. BOS */
+ HIP_DEBUG("Received opportunistic HIT\n");
+#ifdef CONFIG_HIP_RVS
+ } else
+ HIP_DEBUG("Received HIT is ours or we are RVS\n");
+#else
+ } else if (!hip_hit_is_our(&hip_common->hitr)) {
+ HIP_ERROR("Receiver HIT is not ours\n");
+ err = -EFAULT;
+ goto out_err;
+ } else
+ _HIP_DEBUG("Receiver HIT is ours\n");
+#endif
+
+ if (!ipv6_addr_cmp(&hip_common->hits, &hip_common->hitr)) {
+ HIP_DEBUG("Dropping HIP packet. Loopback not supported.\n");
+ err = -ENOSYS;
+ goto out_err;
+ }
+
+ /* Check checksum. */
+ /* jlu XXX: We should not write into received skbuffs! */
+ csum = hip_common->checksum;
+ hip_zero_msg_checksum(hip_common);
+ /* Interop with Julien: no htons here */
+ if (hip_csum_verify(*skb) != csum) {
+ HIP_ERROR("HIP checksum failed (0x%x). Should have been: 0x%x\n",
+ csum, ntohs(hip_csum_verify(*skb)) );
+ err = -EBADMSG;
+ }
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_inbound - entry point for processing of an incoming HIP packet
+ * @skb: sk_buff containing the HIP packet
+ * @nhoff: XXX unused ?
+ *
+ * This function if the entry point for all incoming HIP packet. First
+ * we try to parse and validate the HIP header, and if it is valid the
+ * packet type is determined and control is passed to corresponding
+ * handler function which processes the packet.
+ *
+ * We must free the skb by ourselves, if an error occures!
+ *
+ * Return 0, if packet accepted
+ * <0, if error
+ * >0, if other protocol payload (piggybacking)
+ */
+int hip_inbound(struct sk_buff **skb, unsigned int *nhoff)
+{
+ struct hip_common *hip_common;
+ struct hip_work_order *hwo;
+ int err = 0;
+
+ /* See if there is at least the HIP header in the packet */
+ if (!pskb_may_pull(*skb, sizeof(struct hip_common))) {
+ HIP_ERROR("Received packet too small. Dropping\n");
+ goto out_err;
+ }
+
+ hip_common = (struct hip_common*) (*skb)->h.raw;
+ /* TODO: use hip_state_str */
+ HIP_DEBUG("Received HIP packet type %d\n", hip_common->type_hdr);
+ _HIP_DEBUG_SKB((*skb)->nh.ipv6h, skb);
+ _HIP_HEXDUMP("HIP PACKET", hip_common,
+ hip_get_msg_total_len(hip_common));
+
+ err = hip_verify_network_header(hip_common, skb);
+ if (err) {
+ HIP_ERROR("Verifying of the network header failed\n");
+ goto out_err;
+ }
+
+ err = hip_check_network_msg(hip_common);
+ if (err) {
+ HIP_ERROR("HIP packet is invalid\n");
+ goto out_err;
+ }
+
+ hwo = hip_init_job(GFP_ATOMIC);
+ if (!hwo) {
+ HIP_ERROR("No memory, dropping packet\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ hwo->destructor = hip_hwo_input_destructor;
+
+ /* should we do some early state processing now?
+ * we could prevent further DoSsing by dropping
+ * illegal packets right now.
+ */
+
+ _HIP_DEBUG("Entering switch\n");
+ hwo->type = HIP_WO_TYPE_INCOMING;
+ hwo->arg1 = *skb;
+
+ switch(hip_get_msg_type(hip_common)) {
+ case HIP_I1:
+ HIP_DEBUG("Received HIP I1 packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_I1;
+ break;
+ case HIP_R1:
+ HIP_DEBUG("Received HIP R1 packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_R1;
+ break;
+ case HIP_I2:
+ HIP_DEBUG("Received HIP I2 packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_I2;
+ break;
+ case HIP_R2:
+ HIP_DEBUG("Received HIP R2 packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_R2;
+ break;
+ case HIP_UPDATE:
+ HIP_DEBUG("Received HIP UPDATE packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_UPDATE;
+ break;
+ case HIP_NOTIFY:
+ HIP_DEBUG("Received HIP NOTIFY packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_NOTIFY;
+ break;
+ case HIP_BOS:
+ HIP_DEBUG("Received HIP BOS packet\n");
+ hwo->subtype = HIP_WO_SUBTYPE_RECV_BOS;
+ break;
+ default:
+ HIP_ERROR("Received HIP packet of unknown/unimplemented type %d\n",
+ hip_common->type_hdr);
+ kfree_skb(*skb); /* sic */
+ kfree(hwo);
+ /* KRISUXXX: return value? */
+ return -1;
+ break;
+ }
+
+ hip_insert_work_order(hwo);
+
+ out_err:
+ /* We must not use kfree_skb here... (worker thread releases) */
+
+ return 0;
+}
+
+/**
+ * hip_hwo_input_destructor - remove resources allocated by the work order
+ * @hwo: work order
+ *
+ * Assumes that @arg1 (if non-null) is a skb and @arg2 (if non-null)
+ * any other allocated pointer.
+ */
+void hip_hwo_input_destructor(struct hip_work_order *hwo)
+{
+ if (hwo) {
+ if (hwo->arg1)
+ kfree_skb(hwo->arg1);
+ if (hwo->arg2)
+ kfree(hwo->arg2);
+ }
+}
diff -urN linux-2.6.10/net/ipv6/hip/input.h linux-2.6.10-hip/net/ipv6/hip/input.h
--- linux-2.6.10/net/ipv6/hip/input.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/input.h 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,30 @@
+#ifndef HIP_INPUT_H
+#define HIP_INPUT_H
+
+#include
+#include
+#include
+
+#include "workqueue.h"
+
+uint16_t hip_get_next_atomic_val_16(atomic_t *a, spinlock_t *lock);
+int hip_create_signature(void *buffer_start, int buffer_length,
+ struct hip_host_id *host_id, u8 *signature);
+int hip_inbound(struct sk_buff **skb, unsigned int *nhoff);
+void hip_handle_esp(uint32_t spi, struct ipv6hdr *hdr);
+int hip_receive_r1(struct sk_buff *skb);
+int hip_receive_i2(struct sk_buff *skb);
+int hip_receive_i1(struct sk_buff *skb);
+int hip_receive_r2(struct sk_buff *skb);
+int hip_receive_notify(struct sk_buff *skb);
+int hip_receive_bos(struct sk_buff *skb);
+
+void hip_hwo_input_destructor(struct hip_work_order *hwo);
+
+int hip_verify_packet_hmac(struct hip_common *msg,
+ struct hip_crypto_key *crypto_key);
+int hip_verify_packet_signature(struct hip_common *msg,
+ struct hip_host_id *hid);
+int hip_verify_signature(void *buffer_start, int buffer_length,
+ struct hip_host_id *host_id, u8 *signature);
+#endif /* HIP_INPUT_H */
diff -urN linux-2.6.10/net/ipv6/hip/keymat.c linux-2.6.10-hip/net/ipv6/hip/keymat.c
--- linux-2.6.10/net/ipv6/hip/keymat.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/keymat.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,353 @@
+/*
+ * This file contains KEYMAT handling functions for HIPL.
+ *
+ * Licence: GNU/GPL
+ * Authors:
+ * - Mika Kousa
+ * - Kristian Slavov
+ *
+ */
+
+#include
+#include
+
+
+#include "keymat.h"
+#include "misc.h"
+#include "debug.h"
+#include "hip.h"
+
+
+u8 *hip_create_keymat_buffer(u8 *kij, size_t kij_len, size_t hash_len,
+ struct in6_addr *smaller_hit,
+ struct in6_addr *bigger_hit)
+
+{
+ u8 *buffer;
+ size_t requiredmem;
+
+ if (2 * sizeof(struct in6_addr) < hash_len)
+ requiredmem = kij_len + hash_len + sizeof(u8);
+ else
+ requiredmem = kij_len + 2 * sizeof(struct in6_addr) +
+ sizeof(u8);
+
+ buffer = kmalloc(requiredmem, GFP_KERNEL);
+ if (!buffer) {
+ HIP_ERROR("Out of memory\n");
+ return buffer;
+ }
+
+ memcpy(buffer,kij,kij_len);
+ memcpy(buffer+kij_len,(u8 *)smaller_hit,sizeof(struct in6_addr));
+ memcpy(buffer+kij_len+sizeof(struct in6_addr),(u8 *)bigger_hit,
+ sizeof(struct in6_addr));
+ *(buffer+kij_len+sizeof(struct in6_addr)*2) = 1;
+
+ return buffer;
+}
+
+void hip_update_keymat_buffer(u8 *keybuf, u8 *Kold, size_t Kold_len,
+ size_t Kij_len, u8 cnt)
+{
+ HIP_ASSERT(keybuf);
+
+ memcpy(keybuf+Kij_len, Kold, Kold_len);
+ *(keybuf + Kij_len + Kold_len) = cnt;
+
+ return;
+}
+
+/**
+ * hip_make_keymat - generate HIP keying material
+ * @kij: Diffie-Hellman Kij (as in the HIP drafts)
+ * @kij_len: the length of the Kij material
+ * @keymat: pointer to a keymat structure which will be updated according
+ * to the generated keymaterial
+ * @dstbuf: the generated keymaterial will be written here
+ * @hit1: source HIT
+ * @hit2: destination HIT
+ * @calc_index: where the one byte index is stored (n of Kn)
+ *
+ */
+void hip_make_keymat(char *kij, size_t kij_len, struct hip_keymat_keymat *keymat,
+ void *dstbuf, size_t dstbuflen, struct in6_addr *hit1,
+ struct in6_addr *hit2, u8 *calc_index)
+{
+ int err;
+ struct crypto_tfm *sha = impl_sha1;
+ uint8_t index_nbr = 1;
+ int dstoffset = 0;
+ void *seedkey;
+ struct in6_addr *smaller_hit, *bigger_hit;
+ int hit1_is_bigger;
+ u8 *shabuffer;
+ struct scatterlist sg[HIP_MAX_SCATTERLISTS];
+ int nsg = HIP_MAX_SCATTERLISTS;
+
+ if (dstbuflen < HIP_AH_SHA_LEN) {
+ HIP_ERROR("dstbuf is too short (%d)\n", dstbuflen);
+ return;
+ }
+
+ _HIP_ASSERT(dstbuflen % 32 == 0);
+ HIP_ASSERT(sizeof(index_nbr) == HIP_KEYMAT_INDEX_NBR_SIZE);
+
+ hit1_is_bigger = hip_hit_is_bigger(hit1, hit2);
+
+ bigger_hit = hit1_is_bigger ? hit1 : hit2;
+ smaller_hit = hit1_is_bigger ? hit2 : hit1;
+
+ _HIP_HEXDUMP("bigger hit", bigger_hit, 16);
+ _HIP_HEXDUMP("smaller hit", smaller_hit, 16);
+ _HIP_HEXDUMP("index_nbr", (char *) &index_nbr,
+ HIP_KEYMAT_INDEX_NBR_SIZE);
+
+ shabuffer = hip_create_keymat_buffer(kij, kij_len, HIP_AH_SHA_LEN,
+ smaller_hit, bigger_hit);
+ if (!shabuffer) {
+ HIP_ERROR("No memory for keymat\n");
+ return;
+ }
+
+ err = hip_map_virtual_to_pages(sg, &nsg, shabuffer,
+ kij_len+2*sizeof(struct in6_addr)+1);
+ HIP_ASSERT(!err);
+
+ crypto_digest_digest(sha, sg, nsg, dstbuf);
+
+ dstoffset = HIP_AH_SHA_LEN;
+ index_nbr++;
+
+ /*
+ * K2 = SHA1(Kij | K1 | 2)
+ * K3 = SHA1(Kij | K2 | 3)
+ * ...
+ */
+ seedkey = dstbuf;
+ hip_update_keymat_buffer(shabuffer, seedkey, HIP_AH_SHA_LEN,
+ kij_len, index_nbr);
+ nsg = HIP_MAX_SCATTERLISTS;
+
+ err = hip_map_virtual_to_pages(sg, &nsg, shabuffer, kij_len + HIP_AH_SHA_LEN + 1);
+ HIP_ASSERT(!err);
+
+ while (dstoffset < dstbuflen) {
+ crypto_digest_digest(sha, sg, nsg, dstbuf + dstoffset);
+ seedkey = dstbuf + dstoffset;
+ dstoffset += HIP_AH_SHA_LEN;
+ index_nbr++;
+ hip_update_keymat_buffer(shabuffer, seedkey, HIP_AH_SHA_LEN,
+ kij_len, index_nbr);
+ }
+
+ keymat->offset = 0;
+ keymat->keymatlen = dstoffset;
+ keymat->keymatdst = dstbuf;
+
+ if (calc_index)
+ *calc_index = index_nbr;
+ else
+ HIP_ERROR("NULL calc_index\n");
+
+ _HIP_DEBUG("keymat index_nbr=%u\n", index_nbr);
+ _HIP_HEXDUMP("GENERATED KEYMAT: ", dstbuf, dstbuflen);
+ if (shabuffer)
+ kfree(shabuffer);
+
+ return;
+}
+
+/**
+ * hip_keymat_draw - draw keying material
+ * @keymat: pointer to the keymat structure which contains information
+ * about the actual
+ * @length: size of keymat structure
+ *
+ * Returns: pointer the next point where one can draw the next keymaterial
+ */
+void* hip_keymat_draw(struct hip_keymat_keymat* keymat, int length)
+{
+ /* todo: remove this function */
+ void *ret = NULL;
+
+ if (length > keymat->keymatlen - keymat->offset) {
+ _HIP_INFO("Tried to draw more keys than are available\n");
+ goto out_err;
+ }
+
+ ret = keymat->keymatdst + keymat->offset;
+
+ keymat->offset += length;
+
+ out_err:
+ return ret;
+}
+
+/** hip_keymat_get_new - calculate new keying material
+ * @key: buffer where the created KEYMAT is stored
+ * @key_len: length of @key in bytes
+ * @kij: Kij, shared key
+ * @kij_len: length of @kij in bytes
+ * @keymat_index: Keymat Index
+ * @calc_index: the one byte index value
+ * @calc_index_keymat: Kn
+ * @Kn_is_at: the byte offset where @calc_index_keymat starts
+ *
+ * This function gets next @key_len bytes of KEYMAT to @key starting
+ * from requested offset @keymat_index. On entry of this function
+ * @calc_index tells the one byte index value which is related to
+ * @calc_index_keymat (for example, if @calc_index_keymat is K3, then
+ * @calc_index is 3).
+ *
+ * On successful return @keymat_index and @calc_index contain the
+ * values used in the last round of calculating Kn of KEYMAT,
+ * @calc_index_keymat contains the last Kn, and @Kn_is_at contains the
+ * byte offset value of @calc_index_keymat.
+ *
+ * Returns: 0 on success, < 0 otherwise.
+*/
+int hip_keymat_get_new(void *key, size_t key_len, char *kij, size_t kij_len,
+ uint16_t *keymat_index, uint8_t *calc_index,
+ unsigned char *calc_index_keymat, uint16_t *Kn_is_at)
+{
+ /* must have the hadb lock when calling this function */
+ int err = 0;
+ int copied = 0;
+ u8 *tmp_data = NULL;
+ size_t tmp_data_len;
+
+ _HIP_DEBUG("key_len=%d, requested keymat_index=%u calc_index=%u Kn_is_at=%u\n",
+ key_len, *keymat_index, *calc_index, *Kn_is_at);
+ _HIP_HEXDUMP("calc_index_keymat", calc_index_keymat, HIP_AH_SHA_LEN);
+
+ if (key_len == 0 || kij_len == 0) {
+ HIP_ERROR("key_len = 0 or kij_len = 0\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ _HIP_DEBUG("one byte index at req'd index in the end should be %u\n",
+ (*keymat_index / HIP_AH_SHA_LEN + 1) % 256);
+
+ if (*keymat_index < *Kn_is_at) {
+ HIP_ERROR("requested keymat index %u is lower than lowest keymat index of Kn (%u)\n",
+ *keymat_index, *Kn_is_at);
+ err = -EINVAL;
+ goto out_err;
+ }
+ /* todo: check here if we have to test *keymat_index < entry->current_keymat_index ? */
+
+ /* before calculating any hashes test if we already have
+ * needed amount of ready keymat
+ *
+ * must first check that the requested keymat_index is within the ready keymat
+ */
+ if (*keymat_index - *Kn_is_at < HIP_AH_SHA_LEN) {
+ int tmp = HIP_AH_SHA_LEN - (*keymat_index - *Kn_is_at);
+ _HIP_DEBUG("test: can copy %d bytes from the end of sha K\n", tmp);
+ if (tmp > HIP_AH_SHA_LEN) {
+ HIP_ERROR("bug: tmp > 20\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (tmp > 0) {
+ memcpy(key, calc_index_keymat + HIP_AH_SHA_LEN - tmp, tmp);
+ copied += tmp;
+ }
+ }
+
+ _HIP_DEBUG("copied=%d\n", copied);
+ _HIP_HEXDUMP("KEY (0)", key, copied);
+
+ if (copied == key_len) {
+ _HIP_DEBUG("copied all, return\n");
+ goto out;
+ }
+
+ _HIP_DEBUG("need %d bytes more data\n", key_len-copied);
+
+ tmp_data_len = kij_len + HIP_AH_SHA_LEN + 1;
+ tmp_data = kmalloc(tmp_data_len, GFP_KERNEL);
+ if (!tmp_data) {
+ HIP_ERROR("kmalloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ memcpy(tmp_data, kij, kij_len); /* fixed part of every Kn round */
+
+ while (copied < key_len) {
+ (*calc_index)++;
+ _HIP_DEBUG("calc_index=%u\n", *calc_index);
+ /* create Kn = SHA-1( Kij | Kn-1 | calc_index) */
+
+ /* Kij | Kn-1 */
+ memcpy(tmp_data+kij_len, calc_index_keymat, HIP_AH_SHA_LEN);
+ /* Kij | Kn-1 | calc_index */
+ memcpy(tmp_data+kij_len+HIP_AH_SHA_LEN, calc_index, HIP_KEYMAT_INDEX_NBR_SIZE);
+ /* SHA-1( Kij | Kn-1 | calc_index) */
+ err = hip_build_digest(HIP_DIGEST_SHA1, tmp_data, tmp_data_len, calc_index_keymat);
+ if (err) {
+ HIP_ERROR("build_digest failed (K%u)\n", *calc_index);
+ goto out_err;
+ }
+ *Kn_is_at += HIP_AH_SHA_LEN;
+#if 0
+ _HIP_DEBUG("keymat K%u from offset %u\n", *calc_index, *Kn_is_at);
+ _HIP_HEXDUMP("", calc_index_keymat, HIP_AH_SHA_LEN);
+#endif
+ if (*Kn_is_at + HIP_AH_SHA_LEN < *keymat_index) {
+ HIP_DEBUG("skip until we are at right offset\n");
+ continue;
+ }
+
+ _HIP_DEBUG("copied=%u, key_len=%u calc_index=%u dst to 0x%p\n", copied, key_len, *calc_index, key+copied);
+ if (copied + HIP_AH_SHA_LEN <= key_len) {
+ _HIP_DEBUG("copy whole sha block\n");
+ memcpy(key+copied, calc_index_keymat, HIP_AH_SHA_LEN);
+ copied += HIP_AH_SHA_LEN;
+ } else {
+ int t = HIP_AH_SHA_LEN - key_len % HIP_AH_SHA_LEN;
+ t = key_len - copied;
+ _HIP_DEBUG("copy partial %d bytes\n", t);
+ memcpy(key+copied, calc_index_keymat, t);
+ copied += t;
+ }
+ }
+
+ _HIP_DEBUG("end: copied=%u\n", copied);
+
+ out:
+ _HIP_HEXDUMP("CALCULATED KEY", key, key_len);
+ _HIP_DEBUG("at end: *keymat_index=%u *calc_index=%u\n",
+ *keymat_index, *calc_index);
+ out_err:
+ if(tmp_data)
+ kfree(tmp_data);
+ return err;
+}
+
+
+/** hip_update_entry_keymat - update HADB's KEYMAT related information
+ * @entry: HADB entry to be update
+ * @new_keymat_index: new Keymat Index value
+ * @new_calc_index: new one byte value
+ * @new_current_keymat: Kn related to @new_calc_index
+ *
+ */
+void hip_update_entry_keymat(struct hip_hadb_state *entry,
+ uint16_t new_keymat_index, uint8_t new_calc_index,
+ unsigned char *new_current_keymat)
+{
+ /* must have the hadb lock when calling this function */
+ entry->current_keymat_index = new_keymat_index;
+ entry->keymat_calc_index = new_calc_index;
+ _HIP_DEBUG("New Entry keymat data: current_keymat_index=%u keymat_calc_index=%u\n",
+ entry->current_keymat_index, entry->keymat_calc_index);
+ if (new_current_keymat) {
+ memcpy(entry->current_keymat_K, new_current_keymat, HIP_AH_SHA_LEN);
+ _HIP_HEXDUMP("new_current_keymat", new_current_keymat, HIP_AH_SHA_LEN);
+ }
+}
diff -urN linux-2.6.10/net/ipv6/hip/keymat.h linux-2.6.10-hip/net/ipv6/hip/keymat.h
--- linux-2.6.10/net/ipv6/hip/keymat.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/keymat.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,17 @@
+#ifndef HIP_KEYMAT_H
+#define HIP_KEYMAT_H
+
+#include
+#include
+
+void hip_make_keymat(char *kij, size_t kij_len, struct hip_keymat_keymat *keymat,
+ void *dstbuf, size_t dstbuflen, struct in6_addr *hit1,
+ struct in6_addr *hit2, u8 *calc_index);
+void hip_update_entry_keymat(struct hip_hadb_state *entry, uint16_t new_keymat_index,
+ uint8_t new_calc_index, unsigned char *new_current_keymat);
+void* hip_keymat_draw(struct hip_keymat_keymat* keymat, int length);
+int hip_keymat_get_new(void *key, size_t key_len, char *kij, size_t kij_len,
+ uint16_t *keymat_offset, uint8_t *calc_index,
+ unsigned char *calc_index_keymat, uint16_t *Kn_is_at);
+
+#endif /* HIP_KEYMAT_H */
diff -urN linux-2.6.10/net/ipv6/hip/Makefile linux-2.6.10-hip/net/ipv6/hip/Makefile
--- linux-2.6.10/net/ipv6/hip/Makefile 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/Makefile 2005-03-03 00:01:38.000000000 +0200
@@ -0,0 +1,26 @@
+#
+# Makefile for the HIP for Linux.
+#
+
+obj-$(CONFIG_HIP) += hipmod.o
+
+hipmod-objs := hip.o builder.o keymat.o unit.o test.o input.o output.o \
+ db.o hadb.o debug.o cookie.o workqueue.o misc.o security.o \
+ socket.o hashtable.o rvs.o update.o
+
+CRYPTOOBJS := mpi-bit.o mpi-div.o mpi-inline.o mpi-mpow.o mpi-pow.o \
+mpicoder.o mpih-mul.o mpi-add.o mpi-cmp.o mpi-gcd.o mpi-inv.o \
+mpi-mul.o mpi-scan.o mpih-div.o mpiutil.o kernel-interface.o \
+dh.o dsa.o rsa.o
+
+ASMFILES := mpih-add1.o mpih-lshift.o mpih-mul1.o mpih-mul2.o mpih-mul3.o
+ASMFILES += mpih-rshift.o mpih-sub1.o
+
+ifneq ($(SUBARCH),)
+REALARCH := $(SUBARCH)
+else
+REALARCH := $(ARCH)
+endif
+
+hipmod-objs += $(addprefix crypto/, $(CRYPTOOBJS))
+hipmod-objs += $(addprefix crypto/$(REALARCH)/, $(ASMFILES))
diff -urN linux-2.6.10/net/ipv6/hip/misc.c linux-2.6.10-hip/net/ipv6/hip/misc.c
--- linux-2.6.10/net/ipv6/hip/misc.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/misc.c 2005-03-11 17:12:21.000000000 +0200
@@ -0,0 +1,373 @@
+/*
+ * Miscellaneous functions
+ *
+ * Licence: GNU/GPL
+ * Authors:
+ * - Miika Komu
+ * - Mika Kousa
+ */
+
+#include
+
+#include "misc.h"
+#include "debug.h"
+#include "builder.h"
+#include "hip.h"
+
+/*
+ * XX TODO: HAA
+ * XX TODO: which one to use: this or the function just below?
+ */
+int hip_dsa_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type)
+{
+ int err = 0;
+ u8 digest[HIP_AH_SHA_LEN];
+ char *key_rr = (char *) (host_id + 1); /* skip the header */
+ /* hit excludes rdata but it is included in hi_length;
+ subtract rdata */
+ unsigned int key_rr_len = ntohs(host_id->hi_length) -
+ sizeof(struct hip_host_id_key_rdata);
+
+ _HIP_DEBUG("key_rr_len=%u\n", key_rr_len);
+
+ if (hit_type != HIP_HIT_TYPE_HASH126) {
+ err = -ENOSYS;
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("key_rr", key_rr, key_rr_len);
+
+ err = hip_build_digest(HIP_DIGEST_SHA1, key_rr, key_rr_len, digest);
+ if (err) {
+ HIP_ERROR("Building of digest failed\n");
+ goto out_err;
+ }
+
+ /* hit_126 := concatenate ( 01 , low_order_bits ( digest, 126 ) ) */
+
+ memcpy(hit, digest + (HIP_AH_SHA_LEN - sizeof(struct in6_addr)),
+ sizeof(struct in6_addr));
+ hit->in6_u.u6_addr8[0] &= 0x3f; // clear the upmost bits
+ hit->in6_u.u6_addr8[0] |= HIP_HIT_TYPE_MASK_126;
+
+ out_err:
+
+ return err;
+}
+
+int hip_rsa_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type)
+{
+ int err;
+ err = hip_dsa_host_id_to_hit(host_id, hit, hit_type);
+ return err;
+}
+
+int hip_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type)
+{
+ int algo = hip_get_host_id_algo(host_id);
+ int err = 0;
+
+ if (algo == HIP_HI_DSA) {
+ err = hip_dsa_host_id_to_hit(host_id, hit,
+ hit_type);
+ } else if (algo == HIP_HI_RSA) {
+ err = hip_rsa_host_id_to_hit(host_id, hit,
+ hit_type);
+ } else {
+ err = -ENOSYS;
+ }
+
+ return err;
+}
+
+int hip_private_dsa_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type)
+{
+ int err = 0;
+ struct hip_host_id *host_id_pub = NULL;
+ int contents_len;
+ int total_len;
+
+ contents_len = hip_get_param_contents_len(host_id);
+ total_len = hip_get_param_total_len(host_id);
+
+ /* XX TODO: add an extra check for the T val */
+
+ if (contents_len <= 20) {
+ err = -EMSGSIZE;
+ HIP_ERROR("Host id too short\n");
+ goto out_err;
+ }
+
+ /* Allocate enough space for host id; there will be 20 bytes extra
+ to avoid hassle with padding. */
+ host_id_pub = kmalloc(total_len, GFP_KERNEL);
+ if (!host_id_pub) {
+ err = -EFAULT;
+ goto out_err;
+ }
+ memset(host_id_pub, 0, total_len);
+
+ memcpy(host_id_pub, host_id,
+ sizeof(struct hip_tlv_common) + contents_len - 20);
+
+ host_id_pub->hi_length = htons(ntohs(host_id_pub->hi_length) - 20);
+ hip_set_param_contents_len(host_id_pub, contents_len - 20);
+
+ _HIP_HEXDUMP("extracted pubkey", host_id_pub,
+ hip_get_param_total_len(host_id_pub));
+
+ err = hip_dsa_host_id_to_hit(host_id_pub, hit, hit_type);
+ if (err) {
+ HIP_ERROR("Failed to convert HI to HIT.\n");
+ goto out_err;
+ }
+
+ out_err:
+
+ if (host_id_pub)
+ kfree(host_id_pub);
+
+ return err;
+}
+
+int hip_private_rsa_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type)
+{
+ int err = 0;
+ struct hip_host_id *host_id_pub = NULL;
+ int contents_len;
+ int total_len;
+
+ contents_len = hip_get_param_contents_len(host_id);
+ total_len = hip_get_param_total_len(host_id);
+
+ /* XX FIX: REMOVE PRIVATE KEY? */
+
+ /* Allocate space for public key */
+ host_id_pub = kmalloc(total_len, GFP_KERNEL);
+ if (!host_id_pub) {
+ err = -EFAULT;
+ goto out_err;
+ }
+ memset(host_id_pub, 0, total_len);
+
+ /* How do we extract the public key from the hip_host_id
+ struct? TODO: CHECK THIS */
+ memcpy(host_id_pub, host_id,
+ sizeof(struct hip_tlv_common) + contents_len - 128 * 2);
+
+ host_id_pub->hi_length = htons(ntohs(host_id_pub->hi_length) - 128*2);
+ hip_set_param_contents_len(host_id_pub, contents_len - 128*2);
+
+ _HIP_HEXDUMP("extracted pubkey", host_id_pub,
+ hip_get_param_total_len(host_id_pub));
+
+ err = hip_rsa_host_id_to_hit(host_id_pub, hit, hit_type);
+
+ if (err) {
+ HIP_ERROR("Failed to convert HI to HIT.\n");
+ goto out_err;
+ }
+
+ out_err:
+
+ if (host_id_pub)
+ kfree(host_id_pub);
+
+ return err;
+}
+
+int hip_private_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type)
+{
+ int algo = hip_get_host_id_algo(host_id);
+ int err = 0;
+
+ if (algo == HIP_HI_DSA) {
+ err = hip_private_dsa_host_id_to_hit(host_id, hit,
+ hit_type);
+ } else if (algo == HIP_HI_RSA) {
+ err = hip_private_rsa_host_id_to_hit(host_id, hit,
+ hit_type);
+ } else {
+ err = -ENOSYS;
+ }
+
+ return err;
+}
+
+/**
+ * hip_set_sockaddr - init sockaddr and copy given address to it
+ * @addr: IPv6 address to be copied
+ * @sin: sockaddr where @addr is copied
+ */
+void hip_set_sockaddr(struct sockaddr_in6 *sin, struct in6_addr *addr)
+{
+ memset(sin, 0, sizeof(struct sockaddr_in6));
+
+ sin->sin6_family = AF_INET6;
+ sin->sin6_port = 0; // Is this needed?
+
+ ipv6_addr_copy(&sin->sin6_addr, addr);
+
+ return;
+}
+
+/** hip_timeval_diff - calculate difference between two timevalues
+ * @t1: timevalue 1
+ * @t2: timevalue 2
+ * @result: where the result is stored
+ *
+ * ** CHECK comments **
+ * @result = @t1 - @t2
+ *
+ * Code taken from http://www.gnu.org/manual/glibc-2.2.5/html_node/Elapsed-Time.html
+ *
+ * Returns: 1 if @t1 is equal or later than @t2, else 0.
+ */
+int hip_timeval_diff(const struct timeval *t1, const struct timeval *t2,
+ struct timeval *result)
+{
+ struct timeval _t1, _t2;
+ _t1 = *t1;
+ _t2 = *t2;
+
+ if (_t1.tv_usec < _t2.tv_usec) {
+ int nsec = (_t2.tv_usec - _t1.tv_usec) / 1000000 + 1;
+ _t2.tv_usec -= 1000000 * nsec;
+ _t2.tv_sec += nsec;
+ }
+ if (_t1.tv_usec - _t2.tv_usec > 1000000) {
+ int nsec = (_t1.tv_usec - _t2.tv_usec) / 1000000;
+ _t2.tv_usec += 1000000 * nsec;
+ _t2.tv_sec -= nsec;
+ }
+
+ result->tv_sec = _t1.tv_sec - _t2.tv_sec;
+ result->tv_usec = _t1.tv_usec - _t2.tv_usec;
+
+ return _t1.tv_sec >= _t2.tv_sec;
+}
+/**
+ * hip_lhi_are_equal - compare two LHIs (Localhost Host Identity)
+ * @lhi1: the first LHI used in comparison
+ * @lhi2: the second LHI used in comparison
+ *
+ * Returns: 1 if @lhi1 and @lhi2 are equal, else 0.
+ */
+int hip_lhi_are_equal(const struct hip_lhi *lhi1,
+ const struct hip_lhi *lhi2)
+{
+ return !ipv6_addr_cmp(&lhi1->hit, &lhi2->hit);
+}
+
+/*
+ * Returns 1 if the host_id contains also the "hidden" private key, else
+ * returns 0.
+ */
+int hip_host_id_contains_private_key(struct hip_host_id *host_id)
+{
+ uint16_t len = hip_get_param_contents_len(host_id);
+ u8 *buf = (u8 *)(host_id + 1);
+ u8 t = *buf;
+
+ return len >= 3 * (64 + 8 * t) + 2 * 20; /* PQGXY 3*(64+8*t) + 2*20 */
+}
+
+/**
+ * hip_hit_is_bigger - compare two HITs
+ * @hit1: the first HIT to be compared
+ * @hit2: the second HIT to be compared
+ *
+ * Returns: 1 if @hit1 was bigger than @hit2, or else 0
+ */
+int hip_hit_is_bigger(const struct in6_addr *hit1,
+ const struct in6_addr *hit2)
+{
+ return (ipv6_addr_cmp(hit1, hit2) > 0);
+}
+
+
+char* hip_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 hip_in6_ntop2(const struct in6_addr *in6, char *buf)
+{
+ if (!buf)
+ return 0;
+ return 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]));
+}
+
+int hip_is_hit(const hip_hit_t *hit)
+{
+ return ipv6_addr_is_hit((struct in6_addr *)hit);
+}
+
+/**
+ * hip_hash_spi - calculate a hash from SPI value
+ * @key: 32-bit SPI value
+ * @range: range of the hash
+ *
+ * Returns value in range: 0 <= x < @range
+ */
+int hip_hash_spi(void *key, int range)
+{
+ u32 spi = (u32) key;
+ /* SPIs are random, so simple modulo is enough? */
+ return spi % range;
+}
+
+/**
+ * hip_hash_hit - calculate a hash from a HIT
+ * @key: pointer to a HIT
+ * @range: range of the hash
+ *
+ * Returns value in range: 0 <= x < range
+ */
+int hip_hash_hit(void *key, int range)
+{
+ hip_hit_t *hit = (hip_hit_t *)key;
+
+ /* HITs are random. (at least the 64 LSBs) */
+ return (hit->s6_addr32[2] ^ hit->s6_addr32[3]) % range;
+}
+
+int hip_match_hit(void *hitA, void *hitB)
+{
+ hip_hit_t *key_1, *key_2;
+
+ key_1 = (hip_hit_t *)hitA;
+ key_2 = (hip_hit_t *)hitB;
+
+ return !ipv6_addr_cmp(key_1, key_2);
+}
+
+const char *hip_algorithm_to_string(int algo)
+{
+ const char *str = "UNKNOWN";
+ static const char *algos[] = { "DSA", "RSA" };
+ if(algo == HIP_HI_DSA)
+ str = algos[0];
+ else if(algo == HIP_HI_RSA)
+ str = algos[1];
+ return str;
+}
diff -urN linux-2.6.10/net/ipv6/hip/misc.h linux-2.6.10-hip/net/ipv6/hip/misc.h
--- linux-2.6.10/net/ipv6/hip/misc.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/misc.h 2005-03-11 17:12:21.000000000 +0200
@@ -0,0 +1,31 @@
+#ifndef HIP_MISC_H
+#define HIP_MISC_H
+
+#include
+#include
+#include
+
+int hip_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type);
+int hip_private_host_id_to_hit(const struct hip_host_id *host_id,
+ struct in6_addr *hit, int hit_type);
+int hip_timeval_diff(const struct timeval *t1, const struct timeval *t2,
+ struct timeval *result);
+void hip_set_sockaddr(struct sockaddr_in6 *sin, struct in6_addr *addr);
+int hip_lhi_are_equal(const struct hip_lhi *lhi1,
+ const struct hip_lhi *lhi2);
+char* hip_in6_ntop(const struct in6_addr *in6, char *buf);
+int hip_in6_ntop2(const struct in6_addr *in6, char *buf);
+char* hip_hit_ntop(const hip_hit_t *hit, char *buf);
+int hip_is_hit(const hip_hit_t *hit);
+int hip_host_id_contains_private_key(struct hip_host_id *host_id);
+u8 *hip_host_id_extract_public_key(u8 *buffer, struct hip_host_id *data);
+int hip_hit_is_bigger(const struct in6_addr *hit1,
+ const struct in6_addr *hit2);
+
+int hip_hash_hit(void *hit, int range);
+int hip_hash_spi(void *spi, int range);
+int hip_match_hit(void *hitA, void *hitB);
+const char *hip_algorithm_to_string(int algo);
+
+#endif /* HIP_MISC_H */
diff -urN linux-2.6.10/net/ipv6/hip/output.c linux-2.6.10-hip/net/ipv6/hip/output.c
--- linux-2.6.10/net/ipv6/hip/output.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/output.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,560 @@
+/*
+ * HIP output
+ *
+ * Licence: GNU/GPL
+ * Authors: Janne Lundberg
+ * Miika Komu
+ * Mika Kousa
+ * Kristian Slavov
+ *
+ */
+
+#include "output.h"
+#include "debug.h"
+#include "misc.h"
+#include "hip.h"
+#include "hadb.h"
+#include "db.h"
+#include "builder.h"
+#include "cookie.h"
+#include "builder.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/**
+ * hip_handle_output - handle outgoing IPv6 packets
+ * @hdr: a pointer to the beginning of IPv6 header in the @skb
+ * @skb: the socket buffer that is going to be output
+ *
+ * Handle outgoing packets sent by the transport layer. Depending on the
+ * current state of the HIP association, the packet may be dropped (if it
+ * has a HIT as the destination address) until base exchange or other HIP
+ * related packet exchange is completed. If the packet is not destined
+ * for a HIT, nothing is done for it.
+ *
+ * The @skb will be freed if the return value is not zero.
+ *
+ * Returns: a negative error value on failure. This will be interpreted as
+ * "drop the packet".
+ * Zero if the destination address
+ * was an ordinary IPv6 address or the state was already established.
+ */
+int hip_handle_output(struct ipv6hdr *hdr, struct sk_buff *skb)
+{
+ /* XX TODO:
+ - remove retransmission of I1 from here and use timers instead
+ - output buffer to temporarily store outgoing packets during
+ base exchange
+ */
+ int err = 0;
+ int state = 0;
+ hip_ha_t *entry;
+
+ if (!ipv6_addr_is_hit(&hdr->daddr)) {
+ /* The address was an IPv6 address, ignore. */
+ return 0;
+ }
+
+ /* The source address is not yet a HIT, just the dst address. */
+ entry = hip_hadb_find_byhit(&hdr->daddr);
+ if (!entry) {
+ HIP_ERROR("Unknown HA\n");
+ err = -EFAULT;
+ goto out;
+ }
+
+ smp_wmb();
+ state = entry->state;
+
+ _HIP_DEBUG("hadb entry state is %s\n", hip_state_str(state));
+
+ switch(state) {
+ case HIP_STATE_NONE:
+ HIP_DEBUG("No state with peer\n");
+ break;
+ case HIP_STATE_UNASSOCIATED:
+ HIP_DEBUG("Initiating connection\n");
+#ifdef KRISUS_THESIS
+ if (!gtv_inuse) {
+ KRISU_START_TIMER(KMM_GLOBAL);
+ gtv_inuse = 1;
+ do_gettimeofday(>v_start);
+ }
+#endif
+ barrier();
+ entry->state = HIP_STATE_I1_SENT;
+
+ err = hip_send_i1(&hdr->daddr, entry);
+ if (err < 0) {
+ HIP_ERROR("Sending of I1 failed (%d)\n", err);
+ err = -ENOMEM;
+
+ barrier();
+ entry->state = HIP_STATE_UNASSOCIATED;
+ goto out;
+ }
+
+ err = -1; // drop the TCP/UDP packet
+ break;
+ case HIP_STATE_I1_SENT:
+ HIP_DEBUG("I1 retransmission\n");
+ /* XX TODO: we should have timers on HIP layer and
+ not depend on transport layer timeouts? In that case
+ we should not send I1 here. For the time being, this
+ will act as a poor man's timeout... */
+ err = hip_send_i1(&hdr->daddr, entry);
+ if (err) {
+ HIP_ERROR("I1 retransmission failed");
+ goto out;
+ }
+ err = -1; // just something to drop the TCP packet;
+ break;
+ case HIP_STATE_I2_SENT:
+ /* XX TODO: Should the packet be buffered instead? */
+ HIP_INFO("Not established yet. Dropping the packet.\n");
+ err = -1;
+ break;
+ case HIP_STATE_ESTABLISHED:
+ /* State is already established; just rewrite HITs to IPv6
+ addresses and continue normal IPv6 packet processing. */
+ /* first get peer IPv6 addr */
+ err = hip_hadb_get_peer_addr(entry, &hdr->daddr);
+ if (err) {
+ HIP_ERROR("Could not find peer address\n");
+ err = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ _HIP_DEBUG_IN6ADDR("dst addr", &hdr->daddr);
+ if (!skb) {
+ HIP_ERROR("ESTABLISHED state and no skb!\n");
+ HIP_ERROR("Called by xfrm state re-acquire ? do BEX again ?\n");
+ err = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ err = ipv6_get_saddr(skb->dst, &hdr->daddr, &hdr->saddr);
+ if (err) {
+ HIP_ERROR("Couldn't get a source address\n");
+ err = -EADDRNOTAVAIL;
+ goto out;
+ }
+
+ break;
+ case HIP_STATE_REKEYING:
+ /* XX TODO: Should the packet be buffered instead? */
+ HIP_INFO("Rekey pending. Dropping the packet.\n");
+ err = -1;
+ break;
+ default:
+ HIP_ERROR("Unknown HIP state %d\n", state);
+ err = -EFAULT;
+ break;
+ }
+
+ if (entry->skbtest) {
+ /* sock needs to relookup its dst, todo */
+ HIP_DEBUG("skbtest is 1, setting back to 0\n");
+ entry->skbtest = 0;
+ err = 5;
+ }
+ out:
+ if (entry)
+ hip_put_ha(entry);
+ _HIP_DEBUG("err=%d\n", err);
+ return err;
+}
+
+/**
+ * hip_getfrag - handle IPv6 fragmentation
+ * @data: start of the data to be copied from
+ * @saddr: source IPv6 address, ignored
+ * @buff: destination buffer where to data is copied to
+ * @offset: offset from the beginning of @data
+ * @len: length of the data to be copied from @data+@offset
+ *
+ * Returns: always 0.
+ */
+static int hip_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
+{
+ memcpy(to, ((u8 *)from)+offset, len);
+ if (skb->ip_summed != CHECKSUM_HW) {
+ unsigned int csum;
+
+ csum = csum_partial((u8 *)from+offset, len, 0);
+ skb->csum = csum_block_add(skb->csum, csum, odd);
+ }
+ return 0;
+}
+
+/**
+ * hip_csum_verify - verify HIP header checksum
+ * @skb: the socket buffer which contains the HIP header
+ *
+ * Returns: the checksum of the HIP header.
+ */
+int hip_csum_verify(struct sk_buff *skb)
+{
+ struct hip_common *hip_common;
+ int len;
+ int csum;
+
+ hip_common = (struct hip_common*) skb->h.raw;
+ len = hip_get_msg_total_len(hip_common);
+
+ _HIP_HEXDUMP("hip_csum_verify data", skb->h.raw, len);
+ _HIP_DEBUG("len=%d\n", len);
+ _HIP_HEXDUMP("saddr", &(skb->nh.ipv6h->saddr),
+ sizeof(struct in6_addr));
+ _HIP_HEXDUMP("daddr", &(skb->nh.ipv6h->daddr),
+ sizeof(struct in6_addr));
+
+ csum = csum_partial((char *) hip_common, len, 0);
+
+ return csum_ipv6_magic(&(skb->nh.ipv6h->saddr),
+ &(skb->nh.ipv6h->daddr),
+ len, IPPROTO_HIP, csum);
+}
+
+/**
+ * hip_csum_send - send a HIP packet
+ * @src_addr: packet's source IPv6 address
+ * @peer_addr: packet's destination IPv6 address
+ * @buf: start of the HIP packet
+ *
+ * If @src_addr is NULL, kernel selects which source IPv6 address to
+ * use is the packet.
+ *
+ * Returns: 0 if packet was delivered to lower layer, < 0 otherwise.
+ */
+int hip_csum_send(struct in6_addr *src_addr, struct in6_addr *peer_addr,
+ struct hip_common* buf)
+{
+ return hip_csum_send_fl(src_addr, peer_addr, buf, (struct flowi *) NULL);
+}
+
+/**
+ * hip_csum_send_fl - send a HIP packet to given address
+ * @src_addr: packet's source IPv6 address
+ * @peer_addr: packet's destination IPv6 address
+ * @buf: start of the HIP packet
+ * @out_fl: flow containing the source and the destination IPv6 address of the packet
+ *
+ * If @src_addr is NULL, kernel selects which source IPv6 address to
+ * use is the packet.
+ *
+ * Returns: 0 if packet was delivered to lower layer, < 0 otherwise.
+ */
+int hip_csum_send_fl(struct in6_addr *src_addr, struct in6_addr *peer_addr,
+ struct hip_common* buf, struct flowi *out_fl)
+{
+
+ int err = 0;
+ struct dst_entry *dst = NULL;
+
+ struct flowi fl, *ofl;
+ unsigned int csum;
+ unsigned int len;
+
+ if (out_fl == NULL) {
+ fl.proto = IPPROTO_HIP;
+ fl.oif = 0;
+ fl.fl6_flowlabel = 0;
+ fl.fl6_dst = *peer_addr;
+ if (src_addr)
+ fl.fl6_src = *src_addr;
+ else
+ memset(&fl.fl6_src, 0, sizeof(*src_addr));
+ ofl = &fl;
+ } else {
+ ofl = out_fl;
+ }
+
+ buf->checksum = htons(0);
+ len = hip_get_msg_total_len(buf);
+ csum = csum_partial((char*) buf, len, 0);
+ _HIP_DEBUG("csum test=0x%x\n", csum);
+
+ lock_sock(hip_output_socket->sk);
+
+ err = ip6_dst_lookup(hip_output_socket->sk, &dst, ofl);
+ if (err) {
+ HIP_ERROR("Unable to route HIP packet\n");
+ release_sock(hip_output_socket->sk);
+ goto out_err;
+ }
+
+ _HIP_DUMP_MSG(buf);
+ HIP_DEBUG("pkt out: len=%d proto=%d csum=0x%x\n", len, ofl->proto, csum);
+ HIP_DEBUG_IN6ADDR("pkt out: src IPv6 addr: ", &(ofl->fl6_src));
+ HIP_DEBUG_IN6ADDR("pkt out: dst IPv6 addr: ", &(ofl->fl6_dst));
+
+ buf->checksum = csum_ipv6_magic(&(ofl->fl6_src), &(ofl->fl6_dst), len,
+ ofl->proto, csum);
+ HIP_DEBUG("pkt out: checksum value (host order): 0x%x\n",
+ ntohs(buf->checksum));
+
+ if (buf->checksum == 0)
+ buf->checksum = -1;
+
+ _HIP_HEXDUMP("whole packet", buf, len);
+
+ err = ip6_append_data(hip_output_socket->sk, hip_getfrag, buf, len, 0,
+ 0xFF, NULL, ofl, (struct rt6_info *)dst, MSG_DONTWAIT);
+ if (err) {
+ HIP_ERROR("ip6_build_xmit failed (err=%d)\n", err);
+ ip6_flush_pending_frames(hip_output_socket->sk);
+ } else {
+ err = ip6_push_pending_frames(hip_output_socket->sk);
+ if (err)
+ HIP_ERROR("Pushing of pending frames failed (%d)\n",
+ err);
+ }
+
+ release_sock(hip_output_socket->sk);
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_send_i1 - send an I1 packet to the responder
+ * @entry: the HIP database entry reserved for the peer
+ *
+ * Send an I1 packet to the responder if an IPv6 address for the peer
+ * is known.
+ *
+ * Returns: 0 on success, otherwise < 0 on error.
+ */
+int hip_send_i1(struct in6_addr *dsthit, hip_ha_t *entry)
+{
+ struct hip_common i1;
+ struct in6_addr daddr;
+ struct in6_addr hit_our;
+ int mask;
+ int err = 0;
+
+ HIP_DEBUG("\n");
+
+ /* TODO: we must use the same algorithm that is used in the dsthit */
+ if (hip_copy_any_localhost_hit_by_algo(&hit_our, HIP_HI_DEFAULT_ALGO) < 0) {
+ HIP_ERROR("Out HIT not found\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ HIP_DEBUG_HIT("DEFAULT ALGO HIT: ", &hit_our);
+#if 0
+ if (hip_copy_any_localhost_hit(&hit_our) < 0) {
+ HIP_ERROR("Out HIT not found\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ HIP_DEBUG_HIT("ANY HIT: ", &hit_our);
+#endif
+ mask = HIP_CONTROL_NONE;
+#ifdef CONFIG_HIP_RVS
+ if ((entry->local_controls & HIP_PSEUDO_CONTROL_REQ_RVS))
+ mask |= HIP_CONTROL_RVS_CAPABLE;
+#endif
+
+ //HIP_DEBUG("mask pre=0x%x\n", mask);
+ //mask |= (HIP_CONTROL_SHT_TYPE1 << HIP_CONTROL_SHT_SHIFT);
+ //HIP_DEBUG("mask post=0x%x\n", mask);
+
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr((struct hip_common* ) &i1, HIP_I1,
+ mask, &hit_our,
+ dsthit);
+ /* Eight octet units, not including first */
+ i1.payload_len = (sizeof(struct hip_common) >> 3) - 1;
+
+ HIP_HEXDUMP("HIT SOURCE in send_i1", &i1.hits,
+ sizeof(struct in6_addr));
+ HIP_HEXDUMP("HIT DEST in send_i1", &i1.hitr,
+ sizeof(struct in6_addr));
+
+ err = hip_hadb_get_peer_addr(entry, &daddr);
+ if (err) {
+ HIP_ERROR("hip_sdb_get_peer_address returned error = %d\n",
+ err);
+ goto out_err;
+ }
+
+ _HIP_DEBUG("hip: send I1 packet\n");
+ err = hip_csum_send(NULL, &daddr, (struct hip_common*) &i1);
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_xmit_r1 - transmit an R1 packet to the network
+ * @dst_addr: the destination IPv6 address where the R1 should be sent
+ * @dst_hit: the destination HIT of peer
+ *
+ * Sends an R1 to the peer and stores the cookie information that was sent.
+ *
+ * Returns: zero on success, or negative error value on error.
+ */
+int hip_xmit_r1(struct sk_buff *skb, struct in6_addr *dst_ip,
+ struct in6_addr *dst_hit)
+{
+ struct hip_common *r1pkt;
+ struct in6_addr *own_addr;
+ struct in6_addr *dst_addr;
+ int err = 0;
+
+ HIP_DEBUG("\n");
+
+ own_addr = &skb->nh.ipv6h->daddr;
+ if (!dst_ip || ipv6_addr_any(dst_ip)) {
+ dst_addr = &skb->nh.ipv6h->saddr;
+ } else {
+ dst_addr = dst_ip;
+ }
+
+ /* dst_addr is the IP address of the Initiator... */
+ r1pkt = hip_get_r1(dst_addr, own_addr);
+ if (!r1pkt) {
+ HIP_ERROR("No precreated R1\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ if (dst_hit)
+ ipv6_addr_copy(&r1pkt->hitr, dst_hit);
+ else
+ memset(&r1pkt->hitr, 0, sizeof(struct in6_addr));
+
+ /* set cookie state to used (more or less temporary solution ?) */
+ _HIP_HEXDUMP("R1 pkt", r1pkt, hip_get_msg_total_len(r1pkt));
+
+ err = hip_csum_send(NULL, dst_addr, r1pkt);
+ if (err) {
+ HIP_ERROR("hip_csum_send failed, err=%d\n", err);
+ goto out_err;
+ }
+
+ HIP_ASSERT(!err);
+ return 0;
+
+ out_err:
+ HIP_ERROR("hip_xmit_r1 failed, err=%d\n", err);
+ return err;
+}
+
+/**
+ * hip_send_r1 - send an R1 to the peer
+ * @skb: the socket buffer for the received I1
+ *
+ * Send an I1 to the peer. The addresses and HITs will be digged
+ * out from the @skb.
+ *
+ * Returns: zero on success, or a negative error value on failure.
+ */
+int hip_send_r1(struct sk_buff *skb)
+{
+ int err = 0;
+ struct in6_addr *dst;
+ dst = &(((struct hip_common *)skb->h.raw)->hits);
+
+ err = hip_xmit_r1(skb, NULL, dst);
+
+ return err;
+}
+
+
+void hip_send_notify(hip_ha_t *entry)
+{
+ int err = 0; /* actually not needed, because we can't do
+ * anything if packet sending fails */
+ struct hip_common *notify_packet;
+ struct in6_addr daddr;
+
+ HIP_DEBUG("\n");
+
+ notify_packet = hip_msg_alloc();
+ if (!notify_packet) {
+ HIP_DEBUG("notify_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ hip_build_network_hdr(notify_packet, HIP_NOTIFY, 0,
+ &entry->hit_our, &entry->hit_peer);
+
+ err = hip_build_param_notify(notify_packet, 1234, "ABCDEFGHIJ", 10);
+ if (err) {
+ HIP_ERROR("building of NOTIFY failed (err=%d)\n", err);
+ goto out_err;
+ }
+
+ err = hip_hadb_get_peer_addr(entry, &daddr);
+ if (err) {
+ HIP_DEBUG("hip_sdb_get_peer_address err = %d\n", err);
+ goto out_err;
+ }
+ HIP_DEBUG("Sending NOTIFY packet\n");
+ err = hip_csum_send(NULL, &daddr, notify_packet);
+
+ out_err:
+ if (notify_packet)
+ kfree(notify_packet);
+ return;
+}
+
+struct hip_rea_kludge {
+ hip_ha_t **array;
+ int count;
+ int length;
+};
+
+static int hip_get_all_valid(hip_ha_t *entry, void *op)
+{
+ struct hip_rea_kludge *rk = op;
+
+ if (rk->count >= rk->length)
+ return -1;
+
+ /* should we check the established status also? */
+ if ((entry->hastate & HIP_HASTATE_VALID) == HIP_HASTATE_VALID) {
+ rk->array[rk->count] = entry;
+ hip_hold_ha(entry);
+ rk->count++;
+ }
+
+ return 0;
+}
+
+void hip_send_notify_all(void)
+{
+ int err = 0, i;
+ hip_ha_t *entries[HIP_MAX_HAS] = {0};
+ struct hip_rea_kludge rk;
+
+ HIP_DEBUG("\n");
+
+ rk.array = entries;
+ rk.count = 0;
+ rk.length = HIP_MAX_HAS;
+
+ err = hip_for_each_ha(hip_get_all_valid, &rk);
+ if (err) {
+ HIP_ERROR("for_each_ha err=%d\n", err);
+ return;
+ }
+
+ for (i = 0; i < rk.count; i++) {
+ if (rk.array[i] != NULL) {
+ hip_send_notify(rk.array[i]);
+ hip_put_ha(rk.array[i]);
+ }
+ }
+
+ return;
+}
diff -urN linux-2.6.10/net/ipv6/hip/output.h linux-2.6.10-hip/net/ipv6/hip/output.h
--- linux-2.6.10/net/ipv6/hip/output.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/output.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,22 @@
+#ifndef HIP_OUTPUT_H
+#define HIP_OUTPUT_H
+
+#include "hadb.h"
+
+#include
+#include
+#include
+
+int hip_xmit_r1(struct sk_buff *skb, struct in6_addr *dst_ip,
+ struct in6_addr *dst_hit);
+int hip_handle_output(struct ipv6hdr *hdr, struct sk_buff *skb);
+int hip_csum_verify(struct sk_buff *skb);
+int hip_csum_send(struct in6_addr *src_addr, struct in6_addr *peer_addr,
+ struct hip_common* buf);
+int hip_csum_send_fl(struct in6_addr *src_addr, struct in6_addr *peer_addr,
+ struct hip_common* buf, struct flowi *out_fl);
+int hip_send_i1(struct in6_addr *dsthit, hip_ha_t *entry);
+int hip_send_r1(struct sk_buff *skb);
+void hip_send_notify_all(void);
+
+#endif /* HIP_OUTPUT_H */
diff -urN linux-2.6.10/net/ipv6/hip/rvs.c linux-2.6.10-hip/net/ipv6/hip/rvs.c
--- linux-2.6.10/net/ipv6/hip/rvs.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/rvs.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,477 @@
+/*
+ * Rendezvous Server functionality for HIP.
+ *
+ * Authors:
+ * - Kristian Slavov
+ *
+ * Licence: GNU/GPL
+ */
+
+#include "rvs.h"
+#include "misc.h"
+#include "builder.h"
+#include "output.h"
+
+#include
+#include
+
+HIP_HASHTABLE rva_table;
+
+static struct list_head rvadb[HIP_RVA_SIZE];
+
+/**
+ * hip_rva_allocate - Allocate and initialize Rendezvous Association
+ * @gfpmask: Mask for kmalloc() that is used to allocate the memory.
+ *
+ * Returns NULL if failure, or a pointer to newly allocated and initialized
+ * RVA atructure
+ */
+HIP_RVA *hip_rva_allocate(int gfpmask)
+{
+ HIP_RVA *res;
+
+ res = kmalloc(sizeof(*res), gfpmask);
+ if (!res)
+ return NULL;
+
+ atomic_set(&res->refcnt, 0);
+ spin_lock_init(&res->rva_lock);
+ res->lifetime = 0;
+ memset(res->ip_addrs, 0, HIP_RVA_MAX_IPS*sizeof(struct in6_addr));
+
+ return res;
+}
+/**
+ * hip_ha_to_rva - Create a Rendezvous Association from Host Association
+ * @ha: HA
+ * @gfpmask: Mask for kmalloc(). Used to allocate memory for the RVA.
+ *
+ * Returns the newly created RVA, with information from HA copied to it.
+ * NULL if there was an error (out of memory).
+ */
+HIP_RVA *hip_ha_to_rva(hip_ha_t *ha, int gfpmask)
+{
+ HIP_RVA *rva;
+ struct hip_peer_addr_list_item *item;
+ int ipcnt = 0;
+ struct hip_spi_out_item *spi_out, *spi_tmp;
+
+ rva = hip_rva_allocate(gfpmask);
+ if (!rva)
+ return NULL;
+
+ hip_hold_rva(rva);
+
+ HIP_LOCK_HA(ha);
+ ipv6_addr_copy(&rva->hit, &ha->hit_peer);
+
+ memcpy(&rva->hmac_our, &ha->hip_hmac_in, sizeof(rva->hmac_our));
+ memcpy(&rva->hmac_peer, &ha->hip_hmac_out, sizeof(rva->hmac_peer));
+
+ if (!ipv6_addr_any(&ha->bex_address)) {
+ HIP_DEBUG("copying bex address\n");
+ ipv6_addr_copy(&rva->ip_addrs[ipcnt], &ha->bex_address);
+ ipcnt++;
+ if (ipcnt >= HIP_RVA_MAX_IPS)
+ goto out;
+ }
+
+ list_for_each_entry_safe(spi_out, spi_tmp, &ha->spis_out, list) {
+ list_for_each_entry(item, &spi_out->peer_addr_list, list) {
+ if (item->address_state != PEER_ADDR_STATE_ACTIVE)
+ continue;
+
+ ipv6_addr_copy(&rva->ip_addrs[ipcnt], &item->address);
+ ipcnt++;
+ if (ipcnt >= HIP_RVA_MAX_IPS)
+ break;
+ }
+ if (ipcnt >= HIP_RVA_MAX_IPS)
+ break;
+ }
+ out:
+ HIP_UNLOCK_HA(ha);
+
+ return rva;
+
+}
+
+/**
+ * hip_rva_find - Get RVA entry corresponding to the argument hit.
+ * @hit: Key
+ *
+ * If a RVA is found, it is automatically holded (refcnt incremented).
+ *
+ * Returns the RVA or NULL if RVA was not found.
+ */
+HIP_RVA *hip_rva_find(struct in6_addr *hit)
+{
+ HIP_RVA *rva;
+
+ rva = hip_ht_find(&rva_table, hit);
+ return rva;
+}
+
+HIP_RVA *hip_rva_find_valid(struct in6_addr *hit)
+{
+ HIP_RVA *rva;
+
+ rva = hip_ht_find(&rva_table, hit);
+ if (rva) {
+ if ((rva->rvastate & HIP_RVASTATE_VALID) == 0) {
+ HIP_ERROR("RVA state not valid\n");
+ hip_put_rva(rva);
+ rva = NULL;
+ }
+ }
+ return rva;
+}
+
+/**
+ * hip_rva_insert_ip_n - Insert/update/overwrite one IP in the RVA.
+ * @rva: RVA
+ * @ip: IP address to be written to the RVA's IP-list.
+ * @n: Repalce n:th element in the IP-list. 0 <= n < HIP_RVA_MAX_IPS
+ *
+ * The IP that is overwritten is the n:th in the list.
+ *
+ */
+void hip_rva_insert_ip_n(HIP_RVA *rva, struct in6_addr *ip, unsigned int n)
+{
+ HIP_ASSERT(n < HIP_RVA_MAX_IPS);
+
+ HIP_LOCK_RVA(rva);
+ ipv6_addr_copy(&rva->ip_addrs[n], ip);
+ HIP_UNLOCK_RVA(rva);
+}
+
+/**
+ * hip_rva_insert_ip - Insert/update/overwrite one IP in the RVA.
+ * @rva: RVA
+ * @ip: IP address to be written to the RVA's IP-list.
+ *
+ * The IP that is overwritten is the first in the list. This can, and probably
+ * will change as we create better algorithms to decide which address to
+ * replace (LRU/MRU/etc).
+ *
+ */
+void hip_rva_insert_ip(HIP_RVA *rva, struct in6_addr *ip)
+{
+ hip_rva_insert_ip_n(rva, ip, 0);
+}
+
+
+/**
+ * hip_rva_fetch_ip_n - Fetch Nth IP-address from the RVA to the destination buffer
+ * @rva: Rendezvous Association
+ * @dst: Target buffer (must be preallocated)
+ * @n: The IP-address to fetch (0 <= n < HIP_RVA_MAX_IPS)
+ *
+ */
+void hip_rva_fetch_ip_n(HIP_RVA *rva, struct in6_addr *dst, unsigned int n)
+{
+ HIP_ASSERT(dst);
+ HIP_ASSERT(n < HIP_RVA_MAX_IPS);
+
+ HIP_LOCK_RVA(rva);
+ ipv6_addr_copy(dst, &rva->ip_addrs[n]);
+ HIP_UNLOCK_RVA(rva);
+}
+
+/**
+ * hip_rva_fetch_ip - Fetch first IP-address from the RVA to the destination buffer
+ * @rva: Rendezvous Association
+ * @dst: Target buffer (must be preallocated)
+ *
+ */
+void hip_rva_fetch_ip(HIP_RVA *rva, struct in6_addr *dst)
+{
+ hip_rva_fetch_ip_n(rva, dst, 0);
+}
+
+/**
+ * hip_rva_get_ip - Allocate memory and copy one IP from RVA's list.
+ * @rva: RVA
+ * @gfpmask: gfpmask
+ *
+ * Memory is allocated and the IP to copy is selected to be the first one
+ * in to RVA's list. Later we might have a better algorithm selecting
+ * a better IP.
+ *
+ * Returns pointer to an IPv6 address that MUST be freed after use.
+ */
+struct in6_addr *hip_rva_get_ip(HIP_RVA *rva,int gfpmask)
+{
+ struct in6_addr *hit;
+
+ hit = hip_rva_get_ip_n(rva,gfpmask,0);
+ return hit;
+}
+
+
+/**
+ * hip_rva_get_ip - Allocate memory and copy one IP from RVA's list.
+ * @rva: RVA
+ * @gfpmask: gfpmask
+ * @n: Element ot get from the RVA's IP-list.
+ *
+ * Memory is allocated and the IP to copy is selected to be the first one
+ * in to RVA's list. Later we might have a better algorithm selecting
+ * a better IP.
+ *
+ * Returns pointer to an IPv6 address that MUST be freed after use.
+ * Or %NULL if failed.
+ */
+struct in6_addr *hip_rva_get_ip_n(HIP_RVA *rva, int gfpmask, unsigned int n)
+{
+ struct in6_addr *hit;
+
+ HIP_ASSERT(n < HIP_RVA_MAX_IPS);
+
+ hit = kmalloc(sizeof(struct in6_addr), gfpmask);
+ if (!hit)
+ return NULL;
+
+ hip_rva_fetch_ip_n(rva, hit, n);
+ return hit;
+}
+
+/**
+ * hip_rva_insert - Insert Rendezvous Association into the RVA hashtable
+ * @rva: The RVA to be added to the hashtable.
+ *
+ * The RVA is automatically holded (refcnt incremented) as a side effect of
+ * inserting it to the hashtable.
+ *
+ * Returns errno, or 0 if ok.
+ */
+int hip_rva_insert(HIP_RVA *rva)
+{
+ HIP_RVA *tmp;
+ int err;
+
+ /* if assertation holds, then we don't need locking */
+ HIP_ASSERT(atomic_read(&rva->refcnt) <= 1);
+
+ if (ipv6_addr_any(&rva->hit)) {
+ HIP_ERROR("Cannot insert RVA entry with NULL hit\n");
+ return -EINVAL;
+ }
+
+ tmp = hip_ht_find(&rva_table, &rva->hit);
+ if (tmp) {
+ HIP_INFO("Duplicate entry... not adding to RVA table\n");
+ return -EEXIST;
+ }
+
+ err = hip_ht_add(&rva_table, rva);
+ if (!err)
+ rva->rvastate |= HIP_RVASTATE_VALID;
+
+ return err;
+}
+
+
+static void hip_rva_hold_entry(void *entry)
+{
+ HIP_RVA *rva = entry;
+
+ HIP_ASSERT(entry);
+ atomic_inc(&rva->refcnt);
+ HIP_DEBUG("RVA: %p, refcnt incremented to: %d\n", rva, atomic_read(&rva->refcnt));
+}
+
+static void hip_rva_put_entry(void *entry)
+{
+ HIP_RVA *rva = entry;
+
+ HIP_ASSERT(entry);
+ if (atomic_dec_and_test(&rva->refcnt)) {
+ HIP_DEBUG("RVA: %p, refcnt reached zero. Deleting...\n",rva);
+ hip_rva_delete(rva);
+ } else {
+ HIP_DEBUG("RVA: %p, refcnt decremented to: %d\n", rva, atomic_read(&rva->refcnt));
+ }
+}
+
+static void *hip_rva_get_key(void *entry)
+{
+ return (void *)&(((HIP_RVA *)entry)->hit);
+}
+
+void hip_init_rvadb()
+{
+ memset(&rva_table,0,sizeof(rva_table));
+
+ rva_table.head = rvadb;
+ rva_table.hashsize = HIP_RVA_SIZE;
+ rva_table.offset = offsetof(HIP_RVA, list_hit);
+ rva_table.hash = hip_hash_hit;
+ rva_table.compare = hip_match_hit;
+ rva_table.hold = hip_rva_hold_entry;
+ rva_table.put = hip_rva_put_entry;
+ rva_table.get_key = hip_rva_get_key;
+
+ strncpy(rva_table.name, "RVA TABLE", 15);
+ rva_table.name[15] = 0;
+
+ hip_ht_init(&rva_table);
+}
+
+void hip_uninit_rvadb()
+{
+ /* do something... */
+}
+
+void hip_rva_delete(HIP_RVA *rva)
+{
+ /* last reference has been deleted by now */
+ kfree(rva);
+}
+
+void hip_rva_remove(HIP_RVA *rva)
+{
+ HIP_ASSERT(rva);
+
+ HIP_LOCK_RVA(rva);
+ if (!(rva->rvastate & HIP_RVASTATE_VALID)) {
+ HIP_DEBUG("RVA not in rva hashtable or state corrupted\n");
+ return;
+ }
+
+ hip_ht_delete(&rva_table, rva);
+ HIP_UNLOCK_RVA(rva);
+
+ /* the refcnt should now be at least 1, since we must have
+ at least two references when calling this function: One in the
+ hashtable, one in the calling function.
+ After the delete operation we should have one less, but still over
+ 0 and thus the HIP_UNLOCK_RVA should not cause problems (accessing
+ deleted memory).
+ */
+}
+
+/**
+ * hip_select_rva_types - Select RVA types that we accept
+ * @rreq: The original request
+ * @type_list: List that holds place for @llen 16-bit integers.
+ * @llen: length of @type_list.
+ *
+ * Returns the amount of types that were accepted.
+ */
+int hip_select_rva_types(struct hip_rva_request *rreq, int *type_list, int llen)
+{
+ uint16_t *types = (uint16_t *)(rreq + 1);
+ int typecnt;
+ int i,j;
+
+ typecnt = hip_get_param_contents_len(rreq) - sizeof(uint32_t) / 2;
+ /* due to padding, the actual amount of types is either types or types-1 */
+
+ for(i=0,j=0;ih.raw;
+ original_src = &skb->nh.ipv6h->saddr;
+
+ new_i1 = hip_msg_alloc();
+ if (!new_i1) {
+ HIP_ERROR("No memory to copy original I1\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ hip_build_network_hdr(new_i1, HIP_I1, 0,
+ &(old_i1->hits),
+ &(old_i1->hitr));
+
+ /* we need to add FROM field */
+
+ while ((tmp = hip_get_next_param(old_i1, NULL))) {
+ if (from_added || ntohs(tmp->type) <= HIP_PARAM_FROM) {
+ /* copy */
+ hip_build_param(new_i1,tmp);
+ continue;
+ }
+
+ /* FROM parameter not added AND type > FROM... */
+
+ /* add FROM _AND_ copy the tmp parameter */
+
+ hip_build_param_from(new_i1, original_src, 0);
+ hip_build_param(new_i1, tmp);
+ from_added = 1;
+ }
+
+ if (!from_added) {
+ hip_build_param_from(new_i1, original_src, 0);
+ }
+
+
+ err = hip_csum_send(NULL, final_dst, new_i1);
+ if (err)
+ HIP_ERROR("Sending the modified I1 (RVS) failed: %d\n",err);
+ else {
+ hip_in6_ntop(final_dst, ipv6dst);
+ HIP_INFO("Relayed I1 to %s\n",ipv6dst);
+ }
+
+ out:
+ kfree(final_dst);
+ return err;
+}
+
+void hip_rvs_set_request_flag(struct in6_addr *hit)
+{
+ hip_ha_t *entry;
+
+ entry = hip_hadb_find_byhit(hit);
+ if (!entry) {
+ HIP_ERROR("Could not set RVS request bit\n");
+ return;
+ }
+
+ entry->local_controls |= HIP_PSEUDO_CONTROL_REQ_RVS;
+ hip_put_ha(entry);
+}
diff -urN linux-2.6.10/net/ipv6/hip/rvs.h linux-2.6.10-hip/net/ipv6/hip/rvs.h
--- linux-2.6.10/net/ipv6/hip/rvs.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/rvs.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,83 @@
+#ifndef HIP_RVS_H
+#define HIP_RVS_H
+
+#include "hadb.h"
+#include "hashtable.h"
+
+#include
+#include
+#include
+#include
+
+#define HIP_RVA_MAX_IPS 2
+#define HIP_RVA_SIZE 7 /* small hash table = less wasted memory :) */
+
+typedef enum { HIP_RVASTATE_INVALID=0, HIP_RVASTATE_VALID=1 } hip_rvastate_t;
+
+struct hip_rendezvous_association
+{
+ struct list_head list_hit;
+
+ atomic_t refcnt;
+ spinlock_t rva_lock;
+
+ hip_rvastate_t rvastate;
+ uint32_t lifetime;
+ struct in6_addr hit;
+ struct in6_addr ip_addrs[HIP_RVA_MAX_IPS];
+
+ struct hip_crypto_key hmac_our;
+ struct hip_crypto_key hmac_peer;
+};
+
+typedef struct hip_rendezvous_association HIP_RVA;
+
+/************* primitives *************/
+
+void hip_init_rvadb(void);
+void hip_uninit_rvadb(void);
+
+HIP_RVA *hip_rva_allocate(int gfpmask);
+HIP_RVA *hip_ha_to_rva(hip_ha_t *ha, int gfpmask);
+
+void hip_rva_remove(HIP_RVA *rva);
+int hip_rva_insert(HIP_RVA *rva);
+void hip_rva_delete(HIP_RVA *rva);
+
+HIP_RVA *hip_rva_find(struct in6_addr *hit);
+HIP_RVA *hip_rva_find_valid(struct in6_addr *hit);
+
+void hip_rva_insert_ip(HIP_RVA *rva, struct in6_addr *ip);
+void hip_rva_insert_ip_n(HIP_RVA *rva, struct in6_addr *ip, unsigned int n);
+struct in6_addr *hip_rva_get_ip(HIP_RVA *rva, int gfpmask);
+struct in6_addr *hip_rva_get_ip_n(HIP_RVA *rva, int gfpmask, unsigned int n);
+void hip_rva_fetch_ip(HIP_RVA *rva, struct in6_addr *dst);
+void hip_rva_fetch_ip_n(HIP_RVA *rva, struct in6_addr *dst, unsigned int n);
+
+
+/************* macros *****************/
+
+#define hip_hold_rva(rva) do { \
+ atomic_inc(&rva->refcnt); \
+ HIP_DEBUG("RVA: %p, refcnt increased to: %d\n",rva, atomic_read(&rva->refcnt)); \
+} while(0)
+
+#define hip_put_rva(rva) do { \
+ if (atomic_dec_and_test(&rva->refcnt)) { \
+ HIP_DEBUG("RVA: %p, refcnt reached zero. Deleting...\n",rva); \
+ hip_rva_delete(rva); \
+ } else { \
+ HIP_DEBUG("RVA: %p, refcnt decremented to: %d\n", rva, atomic_read(&rva->refcnt)); \
+ } \
+} while(0)
+
+#define HIP_LOCK_RVA(rva) spin_lock(&rva->rva_lock)
+#define HIP_UNLOCK_RVA(rva) spin_unlock(&rva->rva_lock)
+
+/************ constructs *************/
+
+int hip_relay_i1(struct sk_buff *skb, HIP_RVA *rva);
+int hip_select_rva_types(struct hip_rva_request *rreq, int *type_list, int llen);
+void hip_rvs_set_request_flag(struct in6_addr *hit);
+
+#endif
diff -urN linux-2.6.10/net/ipv6/hip/security.c linux-2.6.10-hip/net/ipv6/hip/security.c
--- linux-2.6.10/net/ipv6/hip/security.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/security.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,582 @@
+/*
+ * HIPL security related functions
+ *
+ * Licence: GNU/GPL
+ *
+ * Authors:
+ * - Mika Kousa
+ * - Kristian Slavov
+ */
+
+
+#include
+#include
+#include
+#include
+
+
+#include "security.h"
+#include "crypto/dh.h"
+#include "hip.h"
+#include "debug.h"
+
+/**
+ * hip_delete_spd - delete an SPD entry suitable for HIP
+ * @dir: SPD direction, %XFRM_POLICY_IN or %XFRM_POLICY_OUT
+ *
+ * Returns: 0 if successful, else < 0.
+ */
+int hip_delete_sp(int dir)
+{
+ int err = 0;
+ struct xfrm_selector sel;
+
+ memset(&sel, 0, sizeof(sel));
+
+ sel.family = AF_INET6;
+ sel.prefixlen_d = 2;
+ sel.prefixlen_s = 2;
+ sel.proto = 0;
+
+ sel.saddr.a6[0] = htonl(0x40000000);
+ sel.daddr.a6[0] = htonl(0x40000000);
+
+ if (xfrm_policy_bysel(dir, &sel, 1)) {
+ HIP_DEBUG("SPD removed successfully\n");
+ } else {
+ HIP_DEBUG("No SPD entry found\n");
+ err = -ENOENT;
+ }
+ return err;
+}
+
+extern struct list_head *xfrm_state_bydst;
+extern struct list_head *xfrm_state_byspi;
+
+/**
+ * hip_delete_sa - delete a SA
+ * @spi: SPI value of SA
+ * @dst: destination HIT of SA
+ *
+ * Returns: 0 if successful, else < 0.
+ */
+int hip_delete_sa(u32 spi, struct in6_addr *dst)
+{
+ struct xfrm_state *xs;
+ xfrm_address_t *xaddr;
+
+ if (spi == 0) {
+ return -EINVAL;
+ }
+
+ HIP_DEBUG("SPI=0x%x\n", spi);
+ xaddr = (xfrm_address_t *)dst;
+ xs = xfrm_state_lookup(xaddr, htonl(spi), IPPROTO_ESP, AF_INET6);
+ if (!xs) {
+ HIP_ERROR("Could not find SA for SPI 0x%x (already expired ?)\n", spi);
+ return -ENOENT;
+ }
+ xfrm_state_put(xs);
+ xfrm_state_delete(xs);
+
+ return 0;
+}
+
+/* this probably is not used anymore? */
+int hip_delete_esp(hip_ha_t *entry)
+{
+ /* assumes already locked entry */
+ hip_hadb_delete_inbound_spis(entry);
+ hip_hadb_delete_outbound_spis(entry);
+ return 0;
+}
+
+/**
+ * hip_setup_sp - setup IPsec SPD entries
+ * @src: source HIT
+ * @dst: destination HIT
+ * @spi: SPI value
+ * @dir: SPD direction, %XFRM_POLICY_IN or %XFRM_POLICY_OUT
+ *
+ * Returns: 0 if successful, else < 0.
+ */
+int hip_setup_sp(int dir)
+{
+ int err = 0;
+ struct xfrm_policy *xp;
+ struct xfrm_tmpl *tmpl;
+
+ /* SP */
+ xp = xfrm_policy_alloc(GFP_KERNEL);
+ if (!xp) {
+ HIP_ERROR("Failed to allocate memory for new SP\n");
+ return -ENOMEM;
+ }
+
+ memset(&xp->selector.daddr, 0, sizeof(struct in6_addr));
+ memset(&xp->selector.saddr, 0, sizeof(struct in6_addr));
+ xp->selector.daddr.a6[0] = htonl(0x40000000);
+ xp->selector.saddr.a6[0] = htonl(0x40000000);
+ xp->selector.family = xp->family = AF_INET6;
+ xp->selector.prefixlen_d = 2;
+ xp->selector.prefixlen_s = 2;
+ xp->selector.proto = 0;
+ xp->selector.sport = xp->selector.dport = 0;
+ xp->selector.sport_mask = xp->selector.dport_mask = 0;
+
+ /* set policy to never expire */
+ xp->lft.soft_byte_limit = XFRM_INF;
+ xp->lft.hard_byte_limit = XFRM_INF;
+ xp->lft.soft_packet_limit = XFRM_INF;
+ xp->lft.hard_packet_limit = XFRM_INF;
+ xp->lft.soft_add_expires_seconds = 0;
+ xp->lft.hard_add_expires_seconds = 0;
+ xp->lft.soft_use_expires_seconds = 0;
+ xp->lft.hard_use_expires_seconds = 0;
+
+ /* xp->curlft. add_time and use_time are set in xfrm_policy_insert */
+
+ xp->family = AF_INET6; /* ? */
+ xp->action = XFRM_POLICY_ALLOW;
+ xp->flags = 0;
+ xp->dead = 0;
+ xp->xfrm_nr = 1; // one transform?
+
+ tmpl = &xp->xfrm_vec[0];
+
+ tmpl->id.proto = IPPROTO_ESP;
+ tmpl->reqid = 0;
+ tmpl->mode = XFRM_MODE_TRANSPORT;
+ tmpl->share = 0; // unique. Is this the correct number?
+ tmpl->optional = 0; /* check: is 0 ok ? */
+ tmpl->aalgos = ~0;
+ tmpl->ealgos = ~0;
+ tmpl->calgos = ~0;
+
+ err = xfrm_policy_insert(dir, xp, 1);
+ if (err) {
+ if (err == -EEXIST)
+ HIP_ERROR("SP policy already exists, ignore ?\n");
+ else
+ HIP_ERROR("Could not insert new SP, err=%d\n", err);
+ // xfrm_policy_delete(xp); ?
+ xfrm_pol_put(xp);
+ }
+
+ return err;
+}
+
+/* returns 0 if SPI could not be allocated, SPI is in host byte order */
+uint32_t hip_acquire_spi(hip_hit_t *srchit, hip_hit_t *dsthit)
+{
+ struct xfrm_state *xs;
+ uint32_t spi = 0;
+
+ HIP_DEBUG("acquiring a new SPI\n");
+ xs = xfrm_find_acq(XFRM_MODE_TRANSPORT, 0, IPPROTO_ESP,
+ (xfrm_address_t *)dsthit, (xfrm_address_t *)srchit,
+ 1, AF_INET6);
+ if (!xs) {
+ HIP_ERROR("Error while acquiring an SA\n");
+ goto out_err_noput;
+ }
+
+ xfrm_alloc_spi(xs, htonl(256), htonl(0xFFFFFFFF));
+ spi = ntohl(xs->id.spi);
+ if (!spi) {
+ HIP_ERROR("Could not get SPI value for the SA\n");
+ goto out_err;
+ }
+ _HIP_DEBUG("Got SPI value for the SA 0x%x\n", spi);
+
+ out_err:
+ xfrm_state_put(xs);
+ out_err_noput:
+ return spi;
+}
+
+
+/**
+ * hip_setup_sa - set up a new IPsec SA
+ * @srcit: source HIT
+ * @dsthit: destination HIT
+ * @spi: SPI value in host byte order
+ * @alg: ESP algorithm to use
+ * @enckey: ESP encryption key
+ * @authkey: authentication key
+ * @already_acquired: true if @spi was already acquired
+ * @direction: direction of SA
+ *
+ * @spi is a value-result parameter. If @spi is 0 the kernel gets a
+ * free SPI value for us. If @spi is non-zero we try to get the new SA
+ * having @spi as its SPI.
+ *
+ * On success IPsec security association is set up @spi contains the
+ * SPI.
+ *
+ * problems: The SA can be in acquire state, or it can already have timed out.
+ *
+ * Returns: 0 if successful, else < 0.
+ */
+int hip_setup_sa(struct in6_addr *srchit, struct in6_addr *dsthit,
+ uint32_t *spi, int alg, void *enckey, void *authkey,
+ int already_acquired, int direction)
+{
+ int err;
+ struct xfrm_state *xs = NULL;
+ struct xfrm_algo_desc *ead;
+ struct xfrm_algo_desc *aad;
+ size_t akeylen, ekeylen; /* in bits */
+
+ HIP_DEBUG("SPI=0x%x alg=%d already_acquired=%d direction=%s\n",
+ *spi, alg, already_acquired,
+ direction == HIP_SPI_DIRECTION_IN ? "IN" : "OUT");
+ akeylen = ekeylen = 0;
+ err = -EEXIST;
+
+ //hip_print_hit("srchit", srchit);
+ //hip_print_hit("dsthit", dsthit);
+ if (already_acquired) {
+ if (!*spi) {
+ HIP_ERROR("No SPI for already acquired SA\n");
+ err = -EINVAL;
+ goto out;
+ }
+ /* should be found (unless expired) */
+ xs = xfrm_state_lookup(direction == HIP_SPI_DIRECTION_IN ?
+ (xfrm_address_t *)dsthit : (xfrm_address_t *)srchit,
+ htonl(*spi), IPPROTO_ESP, AF_INET6);
+ } else {
+ xs = xfrm_find_acq(XFRM_MODE_TRANSPORT, 0, IPPROTO_ESP,
+ (xfrm_address_t *)dsthit, (xfrm_address_t *)srchit,
+ 1, AF_INET6);
+ }
+
+ if (!xs) {
+ HIP_ERROR("Error while acquiring xfrm state: err=%d\n", err);
+ err = -EEXIST;
+ return err;
+ }
+
+ err = 0;
+
+ /* old comments, todo */
+ /* xs is either newly-created or an old one */
+
+ /* allocation of SPI, will wake up possibly sleeping transport layer, but
+ * this is not a problem since it will retry acquiring of an SA, fail and
+ * sleep again.
+ * Allocation of SPI is, however, very important at this stage, so we
+ * either do it like this, or create our own version of xfrm_alloc_spi()
+ * which would do all the same stuff, without waking up.
+ */
+
+ /* should we lock the state? */
+
+ _HIP_DEBUG("xs->id.spi=0x%x\n", ntohl(xs->id.spi));
+
+ if (!already_acquired) {
+ _HIP_DEBUG("allocate SPI\n");
+ if (*spi) {
+ *spi = htonl(*spi);
+ xfrm_alloc_spi(xs, *spi, *spi);
+ } else {
+ /* Try to find a suitable random SPI within the range
+ * in RFC 2406 section 2.1 */
+ xfrm_alloc_spi(xs, htonl(256), htonl(0xFFFFFFFF));
+ }
+
+ _HIP_DEBUG("allocated xs->id.spi=0x%x\n", ntohl(xs->id.spi));
+ if (xs->id.spi == 0) {
+ HIP_ERROR("Could not allocate SPI value for the SA\n");
+ if (*spi != 0) {
+ err = -EEXIST;
+ goto out;
+ } else {
+ err = -EAGAIN;
+ goto out;
+ }
+ }
+ *spi = ntohl(xs->id.spi);
+ }
+
+ _HIP_DEBUG("SPI setup ok, trying to setup enc/auth algos\n");
+
+ err = -ENOENT;
+ switch (alg) {
+ case HIP_ESP_AES_SHA1:
+ ead = xfrm_ealg_get_byid(SADB_X_EALG_AESCBC);
+ if (!ead) {
+ HIP_ERROR("AES not supported\n");
+ goto out;
+ }
+
+ aad = xfrm_aalg_get_byid(SADB_AALG_SHA1HMAC);
+ if (!aad) {
+ HIP_ERROR("SHA1 not supported\n");
+ goto out;
+ }
+
+ xs->props.ealgo = SADB_X_EALG_AESCBC;
+ xs->props.aalgo = SADB_AALG_SHA1HMAC;
+ break;
+ case HIP_ESP_3DES_SHA1:
+ ead = xfrm_ealg_get_byid(SADB_EALG_3DESCBC);
+ if (!ead) {
+ HIP_ERROR("3DES not supported\n");
+ goto out;
+ }
+
+ aad = xfrm_aalg_get_byid(SADB_AALG_SHA1HMAC);
+ if (!aad) {
+ HIP_ERROR("SHA1 not supported\n");
+ goto out;
+ }
+
+ xs->props.ealgo = SADB_EALG_3DESCBC;
+ xs->props.aalgo = SADB_AALG_SHA1HMAC;
+ break;
+ case HIP_ESP_NULL_SHA1:
+ ead = xfrm_ealg_get_byid(SADB_EALG_NULL);
+ if (!ead) {
+ HIP_ERROR("NULL not supported\n");
+ goto out;
+ }
+
+ aad = xfrm_aalg_get_byid(SADB_AALG_SHA1HMAC);
+ if (!aad) {
+ HIP_ERROR("SHA1 not supported\n");
+ goto out;
+ }
+
+ xs->props.ealgo = SADB_EALG_NULL;
+ xs->props.aalgo = SADB_AALG_SHA1HMAC;
+ break;
+ default:
+ err = -EINVAL;
+ ead = aad = NULL;
+ HIP_ERROR("Unsupported algo type: 0x%x\n", alg);
+ HIP_ASSERT(0);
+ }
+
+ /*
+ * AES max bits is 256 and IPsec standards recommend only 128 bits,
+ * so we're sticking with 128 bits (min bits). These values could
+ * also be passed as arguments to this function...
+ */
+ ekeylen = ead->desc.sadb_alg_minbits;
+ akeylen = aad->desc.sadb_alg_minbits;
+
+ HIP_DEBUG("ekeylen=%d, akeylen=%d\n", ekeylen, akeylen);
+
+ err = -ENOMEM;
+ xs->ealg = kmalloc(sizeof(struct xfrm_algo) + (ekeylen + 7)/8, GFP_ATOMIC);
+ if (!xs->ealg)
+ goto out;
+ xs->aalg = kmalloc(sizeof(struct xfrm_algo) + (akeylen + 7)/8, GFP_ATOMIC);
+ if (!xs->aalg)
+ goto out;
+
+ strcpy(xs->aalg->alg_name, aad->name);
+ strcpy(xs->ealg->alg_name, ead->name);
+ memcpy(xs->aalg->alg_key, authkey, (akeylen + 7)/8);
+ memcpy(xs->ealg->alg_key, enckey, (ekeylen + 7)/8);
+ xs->aalg->alg_key_len = akeylen;
+ xs->ealg->alg_key_len = ekeylen;
+
+ xs->props.replay_window = 32; // XXX: Is this the size of the replay window in bits?
+ xs->sel.dport_mask = 0;
+ xs->sel.sport_mask = 0;
+ /* XXX: sel.ifindex... could we fail because of this? */
+
+ xs->sel.proto = 0; /* all protos */
+
+ err = -ENOENT;
+ xs->type = xfrm_get_type(IPPROTO_ESP, AF_INET6);
+ if (xs->type == NULL) {
+ HIP_ERROR("Could not get XFRM type\n");
+ goto out;
+ }
+
+ /* memory leak ? */
+ if (xs->type->init_state(xs, NULL)) {
+ HIP_ERROR("Could not initialize XFRM type\n");
+ goto out;
+ }
+
+ xfrm_state_put(xs);
+ _HIP_DEBUG("New SA added successfully\n");
+ return 0;
+ out:
+ if (xs) {
+ if (xs->aalg)
+ kfree(xs->aalg);
+ if (xs->ealg)
+ kfree(xs->ealg);
+ // xfrm_state_delete(xs) ? see above
+ xfrm_state_put(xs);
+ }
+
+ HIP_DEBUG("returning on error, err=%d\n", err);
+ return err;
+}
+
+/**
+ * hip_finalize_sa - Finalize SA (change state to VALID).
+ * @hit: Destination HIT of the SA
+ * @spi: SPI of the SA in host byte order.
+ *
+ * As a part of changing the state, we also wake up every
+ * sleeper that are waiting for the SA to become VALID.
+ *
+ */
+void hip_finalize_sa(struct in6_addr *hit, u32 spi)
+{
+ struct xfrm_state *xs;
+
+ _HIP_DEBUG("Searching for spi: 0x%x (net 0x%x)\n", spi, htonl(spi));
+
+ xs = xfrm_state_lookup((xfrm_address_t *)hit, htonl(spi),
+ IPPROTO_ESP, AF_INET6);
+ if (!xs) {
+ HIP_ERROR("Could not finalize SA for SPI 0x%x\n", spi);
+ /* do what? */
+ return;
+ }
+
+ spin_lock_bh(&xs->lock);
+ xs->km.state = XFRM_STATE_VALID;
+ xs->lft.hard_add_expires_seconds = 0;
+ spin_unlock_bh(&xs->lock);
+
+ xfrm_state_put(xs);
+ wake_up_all(&km_waitq);
+}
+
+
+/**
+ * hip_insert_dh - Insert the current DH-key into the buffer
+ *
+ * If a DH-key does not exist, we will create one.
+ * Returns: >0 if ok, -1 if errors
+ */
+int hip_insert_dh(u8 *buffer, int bufsize, int group_id)
+{
+ size_t res;
+ DH *tmp;
+
+/*
+ * First check that we have the key available.
+ * Then encode it into the buffer
+ */
+
+ if (dh_table[group_id] == NULL) {
+ tmp = hip_generate_dh_key(group_id);
+
+ spin_lock(&dh_table_lock);
+ dh_table[group_id] = tmp;
+ spin_unlock(&dh_table_lock);
+
+ if (dh_table[group_id] == NULL) {
+ HIP_ERROR("DH key %d not found and could not create it\n",
+ group_id);
+ return -1;
+ }
+ }
+
+ /* race condition problem... */
+ tmp = hip_dh_clone(dh_table[group_id]);
+
+ if (!tmp) {
+ HIP_ERROR("Could not clone DH-key\n");
+ return -2;
+ }
+
+ res = hip_encode_dh_publickey(tmp,buffer,bufsize);
+ if (res < 0) {
+ HIP_ERROR("Encoding error\n");
+ hip_free_dh_structure(tmp);
+ return -3;
+ }
+
+ hip_free_dh_structure(tmp);
+ return res;
+}
+
+/**
+ * hip_generate_shared_secret - Generate Diffie-Hellman shared secret
+ * @group_id: DH group id
+ * @peerkey: Peer's DH public key
+ * @peer_len: Length of the peer's DH public key
+ * @out: Shared secret buffer
+ * @outlen: Length of the output buffer
+ *
+ * Returns 0, if ok, <0 if error occurs.
+ */
+int hip_generate_shared_secret(int group_id, u8* peerkey, size_t peer_len, u8 *out, size_t outlen)
+{
+ DH *tmp;
+ int k;
+
+ if (dh_table[group_id] == NULL) {
+ HIP_ERROR("No DH-key found for group_id: %d\n",group_id);
+ return -1;
+ }
+
+ /* race */
+ tmp = hip_dh_clone(dh_table[group_id]);
+
+ if (!tmp) {
+ HIP_ERROR("Error cloning DH key\n");
+ return -3;
+ }
+
+ k = hip_gen_dh_shared_key(tmp,peerkey,peer_len,out,outlen);
+ if (k < 0) {
+ HIP_ERROR("Shared key failed\n");
+ hip_free_dh_structure(tmp);
+ return -2;
+ }
+
+ hip_free_dh_structure(tmp);
+ return k;
+}
+
+/**
+ * hip_regen_dh_keys - Regenerate Diffie-Hellman keys for HIP
+ * @bitmask: Mask of groups to generate.
+ *
+ * Use only this function to generate DH keys.
+ */
+void hip_regen_dh_keys(u32 bitmask)
+{
+ DH *tmp,*okey;
+ int maxmask,i;
+ int cnt = 0;
+
+ /* if MAX_DH_GROUP_ID = 4 --> maxmask = 0...01111 */
+ maxmask = (1 << (HIP_MAX_DH_GROUP_ID+1)) - 1;
+ bitmask &= maxmask;
+
+ for(i=1; i<=HIP_MAX_DH_GROUP_ID; i++) {
+ if (bitmask & (1 << i)) {
+ tmp = hip_generate_dh_key(i);
+ if (!tmp) {
+ HIP_INFO("Error while generating group: %d\n",i);
+ continue;
+ }
+
+ spin_lock(&dh_table_lock);
+ okey = dh_table[i];
+ dh_table[i] = tmp;
+ spin_unlock(&dh_table_lock);
+
+ hip_free_dh_structure(okey);
+
+ cnt++;
+
+ HIP_DEBUG("DH key for group %d generated\n",i);
+ }
+ }
+ HIP_DEBUG("%d keys generated\n",cnt);
+}
diff -urN linux-2.6.10/net/ipv6/hip/security.h linux-2.6.10-hip/net/ipv6/hip/security.h
--- linux-2.6.10/net/ipv6/hip/security.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/security.h 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,23 @@
+#ifndef HIP_SECURITY_H
+#define HIP_SECURITY_H
+
+#include
+#include
+
+#include "hadb.h"
+
+int hip_delete_esp(hip_ha_t *entry);
+uint32_t hip_acquire_spi(hip_hit_t *srchit, hip_hit_t *dsthit);
+int hip_setup_sa(struct in6_addr *srchit, struct in6_addr *dsthit,
+ uint32_t *spi, int alg,
+ void *enckey, void *authkey, int already_acquired, int direction);
+
+int hip_insert_dh(u8 *buffer, int bufsize, int group_id);
+int hip_generate_shared_secret(int group_id, u8* peerkey, size_t peer_len, u8 *out, size_t outlen);
+void hip_regen_dh_keys(u32 bitmask);
+int hip_delete_sa(u32 spi, struct in6_addr *dst);
+int hip_delete_sp(int dir);
+int hip_setup_sp(int dir);
+void hip_finalize_sa(struct in6_addr *hit, u32 spi);
+
+#endif /* HIP_SECURITY_H */
diff -urN linux-2.6.10/net/ipv6/hip/socket.c linux-2.6.10-hip/net/ipv6/hip/socket.c
--- linux-2.6.10/net/ipv6/hip/socket.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/socket.c 2005-03-14 00:00:53.000000000 +0200
@@ -0,0 +1,1931 @@
+/*
+ * HIP socket handler - handle PF_HIP type sockets
+ *
+ * Licence: GNU/GPL
+ * Authors:
+ * Miika Komu
+ * Anthony D. Joseph
+ * Mika Kousa
+ */
+
+#include "socket.h"
+#include "debug.h"
+#include "db.h"
+#include "builder.h"
+#include "misc.h"
+#include "workqueue.h"
+#include "misc.h"
+#include "cookie.h"
+#include "unit.h"
+#include "input.h"
+#include "output.h"
+#include "debug.h"
+
+#include
+#include
+
+extern struct net_proto_family hip_family_ops;
+extern struct proto_ops inet_stream_ops;
+extern struct proto_ops inet_dgram_ops;
+extern struct proto_ops inet6_stream_ops;
+extern struct proto_ops inet6_dgram_ops;
+extern int inet6_create(struct socket *sock, int protocol);
+
+/* kernel module unit tests */
+extern struct hip_unit_test_suite_list hip_unit_test_suite_list;
+
+/*
+ * The eid db lock (local or peer) must be obtained before accessing these
+ * variables.
+ */
+static sa_eid_t hip_local_eid_count = 1;
+static sa_eid_t hip_peer_eid_count = 1;
+
+static struct proto_ops hip_socket_ops = {
+ family: PF_HIP,
+
+ release: hip_socket_release,
+ bind: hip_socket_bind,
+ connect: hip_socket_connect,
+ socketpair: hip_socket_socketpair,
+ accept: hip_socket_accept,
+ getname: hip_socket_getname,
+ poll: hip_socket_poll,
+ ioctl: hip_socket_ioctl,
+ listen: hip_socket_listen,
+ shutdown: hip_socket_shutdown,
+ setsockopt: hip_socket_setsockopt,
+ getsockopt: hip_socket_getsockopt,
+ sendmsg: hip_socket_sendmsg,
+ recvmsg: hip_socket_recvmsg,
+ mmap: hip_socket_mmap,
+ sendpage: hip_socket_sendpage
+};
+
+struct net_proto_family hip_family_ops = {
+ family: PF_HIP,
+ create: hip_create_socket
+};
+
+typedef struct hip_peer_addr_opaque {
+ struct in6_addr addr;
+ struct hip_peer_addr_opaque *next;
+} hip_peer_addr_opaque_t; /* Structure to record peer addresses */
+
+typedef struct hip_peer_entry_opaque {
+ unsigned int count;
+ struct hip_host_id *host_id;
+ hip_hit_t hit;
+ hip_peer_addr_opaque_t *addr_list;
+ struct hip_peer_entry_opaque *next;
+} hip_peer_entry_opaque_t; /* Structure to record kernel peer entry */
+
+typedef struct hip_peer_opaque {
+ unsigned int count;
+ struct hip_peer_entry_opaque *head;
+ struct hip_peer_entry_opaque *end;
+} hip_peer_opaque_t; /* Structure to record kernel peer list */
+
+
+sa_eid_t hip_create_unique_local_eid(void)
+{
+ // XX CHECK OVERFLOWS
+ return hip_local_eid_count++;
+}
+
+sa_eid_t hip_create_unique_peer_eid(void)
+{
+ // XX CHECK OVERFLOWS
+ return hip_peer_eid_count++;
+}
+
+int hip_create_socket(struct socket *sock, int protocol)
+{
+ int err = 0;
+
+ HIP_DEBUG("protocol=%d\n", protocol);
+
+ // XX TODO: REPLACE WITH A SELECTOR
+ err = inet6_create(sock, protocol);
+ if (err) {
+ HIP_ERROR("Inet6 creation failed (%d)\n", err);
+ goto out_err;
+ }
+
+ // XX LOCK AND UNLOCK?
+ sock->ops = &hip_socket_ops;
+ /* Note: we cannot set sock->sk->family ever to PF_HIP because it
+ simply does not work if we want to use inet6 sockets. */
+
+ out_err:
+
+ return err;
+}
+
+int hip_init_socket_handler(void)
+{
+ int err = 0;
+
+ err = sock_register(&hip_family_ops);
+ if (err) {
+ HIP_ERROR("HIP socket handler registration failed (%d)\n",
+ err);
+ }
+
+ return err;
+}
+
+void hip_uninit_socket_handler(void)
+{
+ int err = 0;
+
+ err = sock_unregister(PF_HIP);
+ if (err) {
+ HIP_ERROR("HIP socket handler unregistration failed (%d)\n",
+ err);
+ }
+}
+
+int hip_select_socket_handler(struct socket *sock,
+ struct proto_ops **handler)
+{
+ int err = 0;
+
+ HIP_ASSERT(sock && sock->sk);
+
+ HIP_DEBUG("sock_type=%d sk_proto=%d\n",
+ sock->sk->sk_type, sock->sk->sk_protocol);
+
+ /* XX FIXME: How to handle IPPROTO_RAW? */
+ /* XX FIXME: How to react on IPPROTO_HIP */
+
+ switch (sock->sk->sk_protocol) {
+ case IPPROTO_TCP:
+ *handler = &inet6_stream_ops;
+ break;
+ case IPPROTO_UDP:
+ *handler = &inet6_dgram_ops;
+ break;
+ default:
+ *handler = NULL;
+ err = -EPROTONOSUPPORT;
+ HIP_ERROR("Cannot select protocol handler for proto %d.",
+ sock->sk->sk_protocol);
+ break;
+ }
+
+ return err;
+}
+
+int hip_socket_get_eid_info(struct socket *sock,
+ struct proto_ops **socket_handler,
+ const struct sockaddr_eid *eid,
+ int eid_is_local,
+ struct hip_lhi *lhi)
+{
+ struct hip_eid_owner_info owner_info;
+ int err = 0;
+
+ err = hip_select_socket_handler(sock, socket_handler);
+ if (err) {
+ HIP_ERROR("Failed to select a socket handler\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("Querying for eid value %d\n", ntohs(eid->eid_val));
+
+ err = hip_db_get_lhi_by_eid(eid, lhi, &owner_info, eid_is_local);
+ if (err) {
+ HIP_ERROR("Failed to map %s EID to HIT\n",
+ (eid_is_local ? "local" : "peer"));
+ goto out_err;
+ }
+
+ /* XX FIXME: CHECK ACCESS RIGHTS FROM OWNER_INFO */
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_release(struct socket *sock)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ if (sock->sk == NULL)
+ goto out_err;
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->release(sock);
+ if (err) {
+ HIP_ERROR("Socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* XX FIX: RELEASE EID */
+
+ /* XX FIX: DESTROY HI ? */
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_bind(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len)
+{
+ int err = 0;
+ struct sockaddr_in6 sockaddr_in6;
+ struct proto_ops *socket_handler;
+ struct sock *sk = sock->sk;
+ struct ipv6_pinfo *pinfo = inet6_sk(sk);
+ struct hip_lhi lhi;
+ struct sockaddr_eid *sockaddr_eid = (struct sockaddr_eid *) umyaddr;
+
+ HIP_DEBUG("\n");
+
+ err = hip_socket_get_eid_info(sock, &socket_handler, sockaddr_eid,
+ 1, &lhi);
+ if (err) {
+ HIP_ERROR("Failed to get socket eid info.\n");
+ goto out_err;
+ }
+
+ /* Clear out the flowinfo, etc from sockaddr_in6 */
+ memset(&sockaddr_in6, 0, sizeof(struct sockaddr_in6));
+
+ /* XX FIXME: select the IP address based on the mappings or interfaces
+ from db and do not use in6_addr_any. */
+
+ /* Use in6_addr_any (= all zeroes) for bind. Offering a HIT to bind
+ does not work without modifications into the bind code because
+ bind_v6 returns an error when it does address type checks. */
+ memset(&sockaddr_in6, 0, sizeof(struct sockaddr_in6));
+ sockaddr_in6.sin6_family = PF_INET6;
+ sockaddr_in6.sin6_port = sockaddr_eid->eid_port;
+
+ /* XX FIX: check access permissions from eid_owner_info */
+
+ err = socket_handler->bind(sock, (struct sockaddr *) &sockaddr_in6,
+ sizeof(struct sockaddr_in6));
+ if (err) {
+ HIP_ERROR("Socket handler failed (%d).\n", err);
+ goto out_err;
+ }
+
+ memcpy(&pinfo->rcv_saddr, &lhi.hit,
+ sizeof(struct in6_addr));
+ memcpy(&pinfo->saddr, &lhi.hit,
+ sizeof(struct in6_addr));
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags)
+{
+ int err = 0;
+ struct sockaddr_in6 sockaddr_in6;
+ struct proto_ops *socket_handler;
+ struct hip_lhi lhi;
+ struct sockaddr_eid *sockaddr_eid = (struct sockaddr_eid *) uservaddr;
+
+ HIP_DEBUG("\n");
+
+ err = hip_socket_get_eid_info(sock, &socket_handler, sockaddr_eid,
+ 0, &lhi);
+ if (err) {
+ HIP_ERROR("Failed to get socket eid info.\n");
+ goto out_err;
+ }
+
+ memset(&sockaddr_in6, 0, sizeof(struct sockaddr_in6));
+ sockaddr_in6.sin6_family = PF_INET6;
+ memcpy(&sockaddr_in6.sin6_addr, &lhi.hit, sizeof(struct in6_addr));
+ sockaddr_in6.sin6_port = sockaddr_eid->eid_port;
+
+ /* Note: connect calls autobind if the application has not already
+ called bind manually. */
+
+ /* XX CHECK: what about autobind src eid ? */
+
+ /* XX CHECK: check does the autobind actually bind to an IPv6 address
+ or HIT? Or inaddr_any? Should we do the autobind manually here? */
+
+ err = socket_handler->connect(sock, (struct sockaddr *) &sockaddr_in6,
+ sizeof(struct sockaddr_in6), flags);
+ if (err) {
+ HIP_ERROR("Socket handler failed (%d).\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+/*
+ * untested
+ */
+int hip_socket_socketpair(struct socket *sock1, struct socket *sock2)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock1, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->socketpair(sock1, sock2);
+ if (err) {
+ HIP_ERROR("Inet socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_accept(struct socket *sock, struct socket *newsock,
+ int flags)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ HIP_ERROR("Failed to select socket handler.\n");
+ goto out_err;
+ }
+
+ err = socket_handler->accept(sock, newsock, flags);
+ if (err) {
+ /* Can return e.g. ERESTARTSYS */
+ HIP_DEBUG("Socket handler returned (%d)\n", err);
+ goto out_err;
+ }
+
+ /* XX FIXME: do something to the newsock? */
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+ struct hip_lhi lhi;
+ struct hip_eid_owner_info owner_info;
+ struct sock *sk = sock->sk;
+ struct ipv6_pinfo *pinfo = inet6_sk(sk);
+ struct inet_opt *inet = inet_sk(sk);
+ struct sockaddr_in6 sockaddr_in6_tmp;
+ struct sockaddr_eid *sockaddr_eid = (struct sockaddr_eid *) uaddr;
+ int sockaddr_in6_tmp_len;
+
+ HIP_DEBUG("\n");
+
+ /* XX CHECK access perms? */
+
+ HIP_DEBUG("getname for %s called\n", (peer ? "peer" : "local"));
+
+ HIP_HEXDUMP("daddr", &pinfo->daddr,
+ sizeof(struct in6_addr));
+ HIP_HEXDUMP("rcv_saddr", &pinfo->rcv_saddr,
+ sizeof(struct in6_addr));
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ HIP_ERROR("Failed to select a socket handler\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("port: %d\n", ntohs((peer ? inet->dport : inet->sport)));
+
+ err = socket_handler->getname(sock,
+ (struct sockaddr *) &sockaddr_in6_tmp,
+ &sockaddr_in6_tmp_len, peer);
+ if (err) {
+ HIP_ERROR("Socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ HIP_ASSERT(sockaddr_in6_tmp_len == sizeof(struct sockaddr_in6));
+ HIP_DEBUG_IN6ADDR("inet6 getname returned addr",
+ &sockaddr_in6_tmp.sin6_addr);
+
+ owner_info.uid = current->uid;
+ owner_info.gid = current->gid;
+
+ memcpy(&lhi.hit, &pinfo->daddr,
+ sizeof(struct in6_addr));
+ lhi.anonymous = 0; /* XX FIXME: should be really set to -1 */
+
+ err = hip_db_set_eid(sockaddr_eid, &lhi, &owner_info, !peer);
+ if (err) {
+ HIP_ERROR("Setting of %s eid failed\n",
+ (peer ? "peer" : "local"));
+ goto out_err;
+ }
+
+ sockaddr_eid->eid_port = (peer) ? inet->dport : inet->sport;
+
+ *usockaddr_len = sizeof(struct sockaddr_eid);
+
+ out_err:
+
+ return err;
+}
+
+/*
+ * XX TODO: fall back to IPV6 POLL
+ */
+unsigned int hip_socket_poll(struct file *file, struct socket *sock,
+ struct poll_table_struct *wait)
+{
+ int err = 0;
+ int mask = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ mask = POLLERR;
+ goto out_err;
+ }
+
+ mask = socket_handler->poll(file, sock, wait);
+
+ out_err:
+
+ return mask;
+}
+
+int hip_socket_ioctl(struct socket *sock, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->ioctl(sock, cmd, arg);
+ if (err) {
+ HIP_ERROR("Inet socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_listen(struct socket *sock, int backlog)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->listen(sock, backlog);
+ if (err) {
+ HIP_ERROR("Inet socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_shutdown(struct socket *sock, int flags)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->shutdown(sock, flags);
+ if (err) {
+ HIP_ERROR("Inet socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len)
+
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+ struct sock *sk = sock->sk;
+ struct inet_opt *inet = inet_sk(sk);
+ struct ipv6_pinfo *pinfo = inet6_sk(sk);
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ HIP_DEBUG("sport=%d dport=%d\n", ntohs(inet->sport), ntohs(inet->dport));
+
+ HIP_HEXDUMP("daddr", &pinfo->daddr,
+ sizeof(struct in6_addr));
+ HIP_HEXDUMP("rcv_saddr", &pinfo->rcv_saddr,
+ sizeof(struct in6_addr));
+
+ err = socket_handler->sendmsg(iocb, sock, m, total_len);
+ if (err) {
+ /* The socket handler can return EIO or EINTR which are not
+ "real" errors. */
+ HIP_DEBUG("Socket handler returned (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len,
+ int flags)
+{
+ int err = 0;
+ struct sock *sk = sock->sk;
+ struct inet_opt *inet = inet_sk(sk);
+ struct ipv6_pinfo *pinfo = inet6_sk(sk);
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ HIP_DEBUG("sport=%d dport=%d\n", ntohs(inet->sport),
+ ntohs(inet->dport));
+
+ HIP_HEXDUMP("daddr", &pinfo->daddr,
+ sizeof(struct in6_addr));
+ HIP_HEXDUMP("rcv_saddr", &pinfo->rcv_saddr,
+ sizeof(struct in6_addr));
+
+ err = socket_handler->recvmsg(iocb, sock, m, total_len, flags);
+ if (err) {
+ /* The socket handler can return EIO or EINTR which are not
+ "real" errors. */
+ HIP_DEBUG("Socket socket handler returned (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+int hip_socket_mmap(struct file *file, struct socket *sock,
+ struct vm_area_struct *vma)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->mmap(file, sock, vma);
+ if (err) {
+ HIP_ERROR("Inet socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+ssize_t hip_socket_sendpage(struct socket *sock, struct page *page, int offset,
+ size_t size, int flags)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ err = socket_handler->sendpage(sock, page, offset, size, flags);
+ if (err) {
+ HIP_ERROR("Inet socket handler failed (%d)\n", err);
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+/*
+ * note this function is called by two other functions below.
+ */
+int hip_socket_add_local_hi(const struct hip_host_id *host_identity,
+ const struct hip_lhi *lhi)
+{
+ int err = 0;
+
+ err = hip_add_localhost_id(lhi, host_identity);
+ if (err) {
+ HIP_ERROR("adding of local host identity failed\n");
+ goto out_err;
+ }
+
+ /* If adding localhost id failed because there was a duplicate, we
+ won't precreate anything (and void causing dagling memory
+ pointers) */
+#if 0
+ HIP_DEBUG("hip: Generating a new R1 now\n");
+
+ if (!hip_precreate_r1(&lhi->hit)) {
+ HIP_ERROR("Unable to precreate R1s... failing\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+#endif
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_socket_handle_local_add_hi - handle adding of a localhost host identity
+ * @input: contains the hi parameter in fqdn format (includes private key)
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_socket_handle_add_local_hi(const struct hip_common *input)
+{
+ int err = 0;
+ struct hip_host_id *dsa_host_identity, *rsa_host_identity = NULL;
+ struct hip_lhi dsa_lhi, rsa_lhi;
+ struct in6_addr hit_our;
+
+ HIP_DEBUG("\n");
+
+ if ((err = hip_get_msg_err(input)) != 0) {
+ HIP_ERROR("daemon failed (%d)\n", err);
+ goto out_err;
+ }
+
+ _HIP_DUMP_MSG(response);
+
+ dsa_host_identity = hip_get_nth_param(input, HIP_PARAM_HOST_ID, 1);
+ if (!dsa_host_identity) {
+ HIP_ERROR("no dsa host identity pubkey in response\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ rsa_host_identity = hip_get_nth_param(input, HIP_PARAM_HOST_ID, 2);
+ if (!rsa_host_identity) {
+ HIP_ERROR("no rsa host identity pubkey in response\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ _HIP_HEXDUMP("rsa host id\n", rsa_host_identity,
+ hip_get_param_total_len(rsa_host_identity));
+
+ err = hip_private_host_id_to_hit(dsa_host_identity, &dsa_lhi.hit,
+ HIP_HIT_TYPE_HASH126);
+ if (err) {
+ HIP_ERROR("dsa host id to hit conversion failed\n");
+ goto out_err;
+ }
+
+ err = hip_private_host_id_to_hit(rsa_host_identity, &rsa_lhi.hit,
+ HIP_HIT_TYPE_HASH126);
+ if (err) {
+ HIP_ERROR("rsa host id to hit conversion failed\n");
+ goto out_err;
+ }
+
+ /* XX FIX: Note: currently the order of insertion of host ids makes a
+ difference. */
+
+ err = hip_socket_add_local_hi(rsa_host_identity, &rsa_lhi);
+ if (err) {
+ HIP_ERROR("Failed to add HIP localhost identity\n");
+ goto out_err;
+ }
+
+ err = hip_socket_add_local_hi(dsa_host_identity, &dsa_lhi);
+ if (err) {
+ HIP_ERROR("Failed to add HIP localhost identity\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("Adding of HIP localhost identity was successful\n");
+
+ HIP_DEBUG("hip: Generating a new R1 now\n");
+
+ /* XX TODO: precreate R1s for both algorithms, not just the default */
+ if (hip_copy_any_localhost_hit_by_algo(&hit_our, HIP_HI_DEFAULT_ALGO) < 0) {
+ HIP_ERROR("Didn't find HIT for R1 precreation\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ if (!hip_precreate_r1(&hit_our)) {
+ HIP_ERROR("Unable to precreate R1s... failing\n");
+ err = -ENOENT;
+ goto out_err;
+ }
+
+ out_err:
+
+ return err;
+}
+
+/**
+ * hip_socket_handle_del_local_hi - handle deletion of a localhost host identity
+ * @msg: the message containing the lhi to be deleted
+ *
+ * This function is currently unimplemented.
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_socket_handle_del_local_hi(const struct hip_common *input)
+{
+ int err = 0;
+
+ HIP_ERROR("Not implemented\n");
+ err = -ENOSYS;
+
+ return err;
+}
+
+int hip_insert_peer_map_work_order(const struct in6_addr *hit,
+ const struct in6_addr *ip,
+ int insert, int rvs)
+{
+ int err = 0;
+ struct hip_work_order *hwo;
+ struct in6_addr *ip_copy;
+
+ hwo = hip_create_job_with_hit(GFP_ATOMIC, hit);
+ if (!hwo) {
+ HIP_ERROR("No memory for hit <-> ip mapping\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ ip_copy = kmalloc(sizeof(struct in6_addr), GFP_ATOMIC);
+ if (!ip_copy) {
+ HIP_ERROR("No memory to copy IP to work order\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ ipv6_addr_copy(ip_copy,ip);
+ hwo->arg2 = ip_copy;
+ hwo->type = HIP_WO_TYPE_MSG;
+ if (rvs)
+ hwo->subtype = HIP_WO_SUBTYPE_ADDRVS;
+ else {
+ if (insert)
+ hwo->subtype = HIP_WO_SUBTYPE_ADDMAP;
+ else
+ hwo->subtype = HIP_WO_SUBTYPE_DELMAP;
+ }
+
+ hip_insert_work_order(hwo);
+
+ out_err:
+
+ return err;
+}
+
+static int hip_do_work(const struct hip_common *input, int rvs)
+{
+ struct in6_addr *hit, *ip;
+ char buf[46];
+ int err = 0;
+
+
+ hit = (struct in6_addr *)
+ hip_get_param_contents(input, HIP_PARAM_HIT);
+ if (!hit) {
+ HIP_ERROR("handle async map: no hit\n");
+ err = -ENODATA;
+ goto out;
+ }
+
+ ip = (struct in6_addr *)
+ hip_get_param_contents(input, HIP_PARAM_IPV6_ADDR);
+ if (!ip) {
+ HIP_ERROR("handle async map: no ipv6 address\n");
+ err = -ENODATA;
+ goto out;
+ }
+
+ hip_in6_ntop(hit, buf);
+ HIP_DEBUG("map HIT: %s\n", buf);
+ hip_in6_ntop(ip, buf);
+ HIP_DEBUG("map IP: %s\n", buf);
+
+ err = hip_insert_peer_map_work_order(hit, ip, 1, rvs);
+ if (err) {
+ HIP_ERROR("Failed to insert peer map work order (%d)\n", err);
+ }
+
+ out:
+
+ return err;
+
+}
+
+
+/**
+ * hip_socket_handle_rvs - Handle a case where we want our host to register
+ * with rendezvous server.
+ * Use this instead off "add map" functionality since we set the special
+ * flag... (rvs)
+ */
+int hip_socket_handle_rvs(const struct hip_common *input)
+{
+ return hip_do_work(input, 1);
+}
+
+
+/**
+ * hip_socket_handle_add_peer_map_hit_ip - handle adding of a HIT-to-IPv6 mapping
+ * @msg: the message containing the mapping to be added to kernel databases
+ *
+ * Add a HIT-to-IPv6 mapping of peer to the mapping database in the kernel
+ * module.
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_socket_handle_add_peer_map_hit_ip(const struct hip_common *input)
+{
+ return hip_do_work(input, 0);
+}
+
+/**
+ * hipd_handle_async_del_map_hit_ip - handle deletion of a mapping
+ * @msg: the message containing the mapping to be deleted
+ *
+ * Currently this function is unimplemented.
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_socket_handle_del_peer_map_hit_ip(const struct hip_common *input)
+{
+ struct in6_addr *hit, *ip;
+ char buf[46];
+ int err = 0;
+
+
+ hit = (struct in6_addr *)
+ hip_get_param_contents(input, HIP_PARAM_HIT);
+ if (!hit) {
+ HIP_ERROR("handle async map: no hit\n");
+ err = -ENODATA;
+ goto out;
+ }
+
+ ip = (struct in6_addr *)
+ hip_get_param_contents(input, HIP_PARAM_IPV6_ADDR);
+ if (!ip) {
+ HIP_ERROR("handle async map: no ipv6 address\n");
+ err = -ENODATA;
+ goto out;
+ }
+
+ hip_in6_ntop(hit, buf);
+ HIP_INFO("map HIT: %s\n", buf);
+ hip_in6_ntop(ip, buf);
+ HIP_INFO("map IP: %s\n", buf);
+
+ err = hip_insert_peer_map_work_order(hit, ip, 0, 0);
+ if (err) {
+ HIP_ERROR("Failed to insert peer map work order (%d)\n", err);
+ }
+
+ out:
+
+ return err;
+}
+
+
+int hip_socket_handle_rst(const struct hip_common *input)
+{
+ return -ENOSYS;
+}
+
+/* This is the maximum number of source addresses for sending BOS packets */
+#define MAX_SRC_ADDRS 128
+
+/**
+ * hip_socket_send_bos - send a BOS packet
+ * @msg: input message (should be empty)
+ *
+ * Generate a signed HIP BOS packet containing our HIT, and send
+ * the packet out each network device interface. Note that there
+ * is a limit of MAX_SRC_ADDRS (128) total addresses.
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_socket_send_bos(const struct hip_common *msg)
+{
+ int err = 0;
+ struct hip_common *bos = NULL;
+ struct in6_addr hit_our;
+ struct in6_addr daddr;
+ int i, mask;
+ struct hip_host_id *host_id_pub = NULL;
+ struct hip_host_id *host_id_private = NULL;
+ u8 signature[HIP_RSA_SIGNATURE_LEN]; // assert RSA > DSA
+ struct net_device *saddr_dev;
+ struct inet6_dev *idev;
+ struct in6_addr saddr[MAX_SRC_ADDRS];
+ int if_idx[MAX_SRC_ADDRS];
+ int addr_count = 0;
+ struct flowi fl;
+ struct inet6_ifaddr *ifa = NULL;
+
+ HIP_DEBUG("\n");
+
+ /* Extra consistency test */
+ if (hip_get_msg_type(msg) != SO_HIP_BOS) {
+ err = -EINVAL;
+ HIP_ERROR("Bad message type\n");
+ goto out_err;
+ }
+
+ /* allocate space for new BOS */
+ bos = hip_msg_alloc();
+ if (!bos) {
+ HIP_ERROR("Allocation of BOS failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ /* Determine our HIT */
+ if (hip_copy_any_localhost_hit(&hit_our) < 0) {
+ HIP_ERROR("Our HIT not found\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Determine our HOST ID public key */
+ host_id_pub = hip_get_any_localhost_public_key(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_pub) {
+ HIP_ERROR("Could not acquire localhost public key\n");
+ goto out_err;
+ }
+
+ /* Determine our HOST ID private key */
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ err = -EINVAL;
+ HIP_ERROR("No localhost private key found\n");
+ goto out_err;
+ }
+
+ /* Ready to begin building the BOS packet */
+ /*
+ IP ( HIP ( HOST_ID,
+ HIP_SIGNATURE ) )
+ */
+ mask = HIP_CONTROL_NONE;
+
+ hip_build_network_hdr(bos, HIP_BOS, mask, &hit_our, NULL);
+
+ /********** HOST_ID *********/
+
+ _HIP_DEBUG("This HOST ID belongs to: %s\n",
+ hip_get_param_host_id_hostname(host_id_pub));
+ err = hip_build_param(bos, host_id_pub);
+ if (err) {
+ HIP_ERROR("Building of host id failed\n");
+ goto out_err;
+ }
+
+ /********** SIGNATURE **********/
+
+ HIP_ASSERT(host_id_private);
+
+ /* Build a digest of the packet built so far. Signature will
+ be calculated over the digest. */
+
+ if (!hip_create_signature(bos, hip_get_msg_total_len(bos),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not create signature\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* Only DSA supported currently */
+ _HIP_ASSERT(hip_get_host_id_algo(host_id_private) == HIP_HI_DSA);
+
+ err = hip_build_param_signature_contents(bos,
+ signature,
+ ((HIP_SIG_DEFAULT_ALGO == HIP_HI_RSA) ?
+ HIP_RSA_SIGNATURE_LEN : HIP_DSA_SIGNATURE_LEN),
+ HIP_SIG_DEFAULT_ALGO);
+ if (err) {
+ HIP_ERROR("Building of signature failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /************** BOS packet ready ***************/
+ HIP_DEBUG("sending BOS\n");
+ /* Use All Nodes Addresses (link-local) RFC2373
+ FF02:0:0:0:0:0:0:1 as the destination multicast address */
+ ipv6_addr_all_nodes(&daddr);
+
+ /* Iterate through all the network devices, recording source
+ * addresses for BOS packets */
+
+ /* First lock the devices list */
+ read_lock(&dev_base_lock);
+ read_lock(&addrconf_lock);
+
+ /* Now, iterate through the list */
+ for (saddr_dev = dev_base; saddr_dev; saddr_dev = saddr_dev->next) {
+ HIP_DEBUG("Found network interface %d: %s\n",
+ saddr_dev->ifindex, saddr_dev->name);
+
+ /* Skip down devices */
+ if (!(saddr_dev->flags & IFF_UP)) {
+ HIP_DEBUG("Skipping down device\n");
+ continue;
+ }
+
+ /* Skip non-multicast devices */
+ if (!(saddr_dev->flags & IFF_MULTICAST)) {
+ HIP_DEBUG("Skipping non-multicast device\n");
+ continue;
+ }
+
+ /* Skip loopback devices (as long as we do
+ * not have loopback support). TODO: skip tunnels etc. */
+ if (saddr_dev->flags & IFF_LOOPBACK) {
+ HIP_DEBUG("Skipping loopback device\n");
+ continue;
+ }
+
+ /* Skip non-IPv6 devices (as long as we do
+ * not have IPv4 support). TODO: skip tunnels etc. */
+ idev = in6_dev_get(saddr_dev);
+ if (!idev) {
+ HIP_DEBUG("Skipping non-IPv6 device\n");
+ continue;
+ }
+ read_lock(&idev->lock);
+
+ /* test, debug crashing when all IPv6 addresses of
+ * interface were deleted */
+ if (idev->dead) {
+ HIP_DEBUG("dead device\n");
+ goto out_idev_unlock;
+ }
+
+ /* Record the interface's non-link local IPv6 addresses */
+ for (i=0, ifa=idev->addr_list; ifa; i++, ifa = ifa->if_next) {
+ if (addr_count >= MAX_SRC_ADDRS) {
+ HIP_DEBUG("too many source addresses\n");
+ goto out_idev_unlock;
+ }
+ spin_lock_bh(&ifa->lock);
+ HIP_DEBUG_IN6ADDR("addr", &ifa->addr);
+ if (ipv6_addr_type(&ifa->addr) & IPV6_ADDR_LINKLOCAL){
+ HIP_DEBUG("not counting link local address\n");
+ } else {
+ if_idx[addr_count] = saddr_dev->ifindex;
+ ipv6_addr_copy(&(saddr[addr_count]), &ifa->addr);
+ addr_count++;
+ }
+ spin_unlock_bh(&ifa->lock);
+ }
+ HIP_DEBUG("address list count=%d\n", addr_count);
+
+ out_idev_unlock:
+ read_unlock(&idev->lock);
+ in6_dev_put(idev);
+ }
+
+ read_unlock(&addrconf_lock);
+ read_unlock(&dev_base_lock);
+
+ HIP_DEBUG("final address list count=%d\n", addr_count);
+
+ HIP_DEBUG_IN6ADDR("dest mc address", &daddr);
+
+ /* Loop through the saved addresses, sending the BOS packets
+ out the correct interface */
+ for (i = 0; i < addr_count; i++) {
+ /* got a source addresses, send BOS */
+ HIP_DEBUG_IN6ADDR("selected source address", &(saddr[i]));
+
+ /* Set up the routing structure to use the correct
+ interface, source addr, and destination addr */
+ fl.proto = IPPROTO_HIP;
+ fl.oif = if_idx[i];
+ fl.fl6_flowlabel = 0;
+ fl.fl6_dst = daddr;
+ fl.fl6_src = saddr[i];
+
+ HIP_DEBUG("pre csum totlen=%u\n", hip_get_msg_total_len(bos));
+ /* Send it! */
+ err = hip_csum_send_fl(&(saddr[i]), &daddr, bos, &fl);
+ if (err)
+ HIP_ERROR("sending of BOS failed, err=%d\n", err);
+ }
+
+ err = 0;
+
+ out_err:
+ if (host_id_private)
+ kfree(host_id_private);
+ if (host_id_pub)
+ kfree(host_id_pub);
+ if (bos)
+ kfree(bos);
+
+ return err;
+}
+
+/**
+ * hipd_handle_async_unit_test - handle unit test message
+ * @msg: message containing information about which unit tests to execute
+ *
+ * Execute unit tests in the kernelspace and return the number of unit tests
+ * failed.
+ *
+ * Returns: the number of unit tests failed
+ */
+int hip_socket_handle_unit_test(const struct hip_common *msg)
+{
+ int err = 0;
+#if 0 /* XX TODO */
+ uint16_t failed_test_cases;
+ uint16_t suiteid, caseid;
+ struct hip_unit_test *test = NULL;
+ char err_log[HIP_UNIT_ERR_LOG_MSG_MAX_LEN] = "";
+
+ test = (struct hip_unit_test *)
+ hip_get_param(msg, HIP_PARAM_UNIT_TEST);
+ if (!test) {
+ HIP_ERROR("No unit test parameter found\n");
+ err = -ENOMSG;
+ goto out;
+ }
+
+ suiteid = hip_get_unit_test_suite_param_id(test);
+ caseid = hip_get_unit_test_case_param_id(test);
+
+ HIP_DEBUG("Executing suiteid=%d, caseid=%d\n", suiteid, caseid);
+
+ failed_test_cases = hip_run_unit_test_case(&hip_unit_test_suite_list,
+ suiteid, caseid,
+ err_log, sizeof(err_log));
+ if (failed_test_cases)
+ HIP_ERROR("\n===Unit Test Summary===\nTotal %d errors:\n%s\n",
+ failed_test_cases, err_log);
+ else
+ HIP_INFO("\n===Unit Test Summary===\nAll tests passed, no errors!\n");
+
+ out:
+ hip_msg_init(msg); /* reuse the same input msg for results */
+ hip_build_user_hdr(msg, SO_HIP_RUN_UNIT_TEST, failed_test_cases);
+ hip_build_unit_test_log();
+#endif
+
+ return err;
+}
+
+/*
+ * This function is similar to hip_socket_handle_add_local_hi but there are
+ * three major differences:
+ * - this function is used by native HIP sockets (not hipconf)
+ * - HIP sockets require EID handling which is done here
+ * - this function DOES NOT call hip_precreate_r1, so you need launch
+ */
+int hip_socket_handle_set_my_eid(struct hip_common *msg)
+{
+ int err = 0;
+ struct sockaddr_eid eid;
+ struct hip_tlv_common *param = NULL;
+ struct hip_eid_iface *iface;
+ struct hip_eid_endpoint *eid_endpoint;
+ struct hip_lhi lhi;
+ struct hip_eid_owner_info owner_info;
+ struct hip_host_id *host_id;
+
+ HIP_DEBUG("\n");
+
+ /* Extra consistency test */
+ if (hip_get_msg_type(msg) != SO_HIP_SET_MY_EID) {
+ err = -EINVAL;
+ HIP_ERROR("Bad message type\n");
+ goto out_err;
+ }
+
+ eid_endpoint = hip_get_param(msg, HIP_PARAM_EID_ENDPOINT);
+ if (!eid_endpoint) {
+ err = -ENOENT;
+ HIP_ERROR("Could not find eid endpoint\n");
+ goto out_err;
+ }
+
+ if (eid_endpoint->endpoint.flags & HIP_ENDPOINT_FLAG_HIT) {
+ err = -EAFNOSUPPORT;
+ HIP_ERROR("setmyeid does not support HITs, only HIs\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("hi len %d\n",
+ ntohs((eid_endpoint->endpoint.id.host_id.hi_length)));
+
+ HIP_HEXDUMP("eid endpoint", eid_endpoint,
+ hip_get_param_total_len(eid_endpoint));
+
+ host_id = &eid_endpoint->endpoint.id.host_id;
+
+ owner_info.uid = current->uid;
+ owner_info.gid = current->gid;
+
+ if (hip_host_id_contains_private_key(host_id)) {
+ err = hip_private_host_id_to_hit(host_id, &lhi.hit,
+ HIP_HIT_TYPE_HASH126);
+ if (err) {
+ HIP_ERROR("Failed to calculate HIT from HI.");
+ goto out_err;
+ }
+
+ /* XX TODO: check UID/GID permissions before adding */
+ err = hip_socket_add_local_hi(host_id, &lhi);
+ if (err == -EEXIST) {
+ HIP_INFO("Host id exists already, ignoring\n");
+ err = 0;
+ } else if (err) {
+ HIP_ERROR("Adding of localhost id failed");
+ goto out_err;
+ }
+ } else {
+ /* Only public key */
+ err = hip_host_id_to_hit(host_id,
+ &lhi.hit, HIP_HIT_TYPE_HASH126);
+ }
+
+ HIP_DEBUG_HIT("calculated HIT", &lhi.hit);
+
+ /* Iterate through the interfaces */
+ while((param = hip_get_next_param(msg, param)) != NULL) {
+ /* Skip other parameters (only the endpoint should
+ really be there). */
+ if (hip_get_param_type(param) != HIP_PARAM_EID_IFACE)
+ continue;
+ iface = (struct hip_eid_iface *) param;
+ /* XX TODO: convert and store the iface somewhere?? */
+ /* XX TODO: check also the UID permissions for storing
+ the ifaces before actually storing them */
+ }
+
+ /* The eid port information will be filled by the resolver. It is not
+ really meaningful in the eid db. */
+ eid.eid_port = htons(0);
+
+ lhi.anonymous =
+ (eid_endpoint->endpoint.flags & HIP_ENDPOINT_FLAG_ANON) ?
+ 1 : 0;
+
+ /* XX TODO: check UID/GID permissions before adding ? */
+ err = hip_db_set_my_eid(&eid, &lhi, &owner_info);
+ if (err) {
+ HIP_ERROR("Could not set my eid into the db\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("EID value was set to %d\n", ntohs(eid.eid_val));
+
+ /* Clear the msg and reuse it for the result */
+
+ hip_msg_init(msg);
+ hip_build_user_hdr(msg, SO_HIP_SET_MY_EID, err);
+ err = hip_build_param_eid_sockaddr(msg, (struct sockaddr *) &eid,
+ sizeof(struct sockaddr_eid));
+ if (err) {
+ HIP_ERROR("Could not build eid sockaddr\n");
+ goto out_err;
+ }
+
+ out_err:
+ return err;
+}
+
+
+int hip_socket_handle_set_peer_eid(struct hip_common *msg)
+{
+ int err = 0;
+ struct sockaddr_eid eid;
+ struct hip_tlv_common *param = NULL;
+ struct hip_eid_endpoint *eid_endpoint;
+ struct hip_lhi lhi;
+ struct hip_eid_owner_info owner_info;
+
+ HIP_DEBUG("\n");
+
+ /* Extra consistency test */
+ if (hip_get_msg_type(msg) != SO_HIP_SET_PEER_EID) {
+ err = -EINVAL;
+ HIP_ERROR("Bad message type\n");
+ goto out_err;
+ }
+
+ eid_endpoint = hip_get_param(msg, HIP_PARAM_EID_ENDPOINT);
+ if (!eid_endpoint) {
+ err = -ENOENT;
+ HIP_ERROR("Could not find eid endpoint\n");
+ goto out_err;
+ }
+
+ if (eid_endpoint->endpoint.flags & HIP_ENDPOINT_FLAG_HIT) {
+ memcpy(&lhi.hit, &eid_endpoint->endpoint.id.hit,
+ sizeof(struct in6_addr));
+ HIP_DEBUG_HIT("Peer HIT: ", &lhi.hit);
+ } else {
+ HIP_DEBUG("host_id len %d\n",
+ ntohs((eid_endpoint->endpoint.id.host_id.hi_length)));
+ err = hip_host_id_to_hit(&eid_endpoint->endpoint.id.host_id,
+ &lhi.hit, HIP_HIT_TYPE_HASH126);
+ if (err) {
+ HIP_ERROR("Failed to calculate HIT from HI.");
+ goto out_err;
+ }
+ }
+
+ lhi.anonymous =
+ (eid_endpoint->endpoint.flags & HIP_ENDPOINT_FLAG_ANON) ? 1 : 0;
+
+ /* Fill eid owner information in and assign a peer EID */
+
+ owner_info.uid = current->uid;
+ owner_info.gid = current->gid;
+
+ /* The eid port information will be filled by the resolver. It is not
+ really meaningful in the eid db. */
+ eid.eid_port = htons(0);
+
+ err = hip_db_set_peer_eid(&eid, &lhi, &owner_info);
+ if (err) {
+ HIP_ERROR("Could not set my eid into the db\n");
+ goto out_err;
+ }
+
+ /* Iterate through the addresses */
+
+ while((param = hip_get_next_param(msg, param)) != NULL) {
+ struct sockaddr_in6 *sockaddr;
+
+ HIP_DEBUG("Param type=%d\n", hip_get_param_type(param));
+
+ /* Skip other parameters (only the endpoint should
+ really be there). */
+ if (hip_get_param_type(param) != HIP_PARAM_EID_SOCKADDR)
+ continue;
+
+ HIP_DEBUG("EID sockaddr found in the msg\n");
+
+ sockaddr =
+ (struct sockaddr_in6 *) hip_get_param_contents_direct(param);
+ if (sockaddr->sin6_family != AF_INET6) {
+ HIP_INFO("sa_family %d not supported, ignoring\n",
+ sockaddr->sin6_family);
+ continue;
+ }
+
+ HIP_DEBUG_IN6ADDR("Peer IPv6 address", &sockaddr->sin6_addr);
+
+ /* XX FIX: the mapping should be tagged with an uid */
+
+ err = hip_insert_peer_map_work_order(&lhi.hit,
+ &sockaddr->sin6_addr,1,0);
+ if (err) {
+ HIP_ERROR("Failed to insert map work order (%d)\n",
+ err);
+ goto out_err;
+ }
+ }
+
+ /* Finished. Write a return message with the EID (reuse the msg for
+ result). */
+
+ hip_msg_init(msg);
+ hip_build_user_hdr(msg, SO_HIP_SET_PEER_EID, -err);
+ err = hip_build_param_eid_sockaddr(msg,
+ (struct sockaddr *) &eid,
+ sizeof(eid));
+ if (err) {
+ HIP_ERROR("Could not build eid sockaddr\n");
+ goto out_err;
+ }
+
+ out_err:
+ /* XX FIXME: if there were errors, remove eid and hit-ip mappings
+ if necessary */
+
+ return err;
+}
+
+/**
+* hip_list_peers_add - private function to add an entry to the peer list
+* @addr: IPv6 address
+* @entry: peer list entry
+* @last: pointer to pointer to end of peer list linked list
+*
+* Add an IPv6 address (if valid) to the peer list and update the tail
+* pointer.
+*
+* Returns: zero on success, or negative error value on failure
+*/
+static int hip_list_peers_add(struct in6_addr *address,
+ hip_peer_entry_opaque_t *entry,
+ hip_peer_addr_opaque_t **last)
+{
+ hip_peer_addr_opaque_t *addr;
+
+ HIP_DEBUG_IN6ADDR("## SPI is 0, found bex address:", address);
+
+ /* Allocate an entry for the address */
+ addr = kmalloc(sizeof(hip_peer_addr_opaque_t), GFP_ATOMIC);
+ if (!addr) {
+ HIP_ERROR("No memory to create peer addr entry\n");
+ return -ENOMEM;
+ }
+ addr->next = NULL;
+ /* Record the peer addr */
+ ipv6_addr_copy(&addr->addr, address);
+
+ if (*last == NULL) { /* First entry? Add to head and tail */
+ entry->addr_list = addr;
+ } else { /* Otherwise, add to tail */
+ (*last)->next = addr;
+ }
+ *last = addr;
+ entry->count++; /* Increment count in peer entry */
+ return 0;
+}
+
+
+/**
+ * hip_hadb_list_peers_func - private function to process a hadb entry
+ * @entry: hadb table entry
+ * @opaque: private data for the function (contains record keeping structure)
+ *
+ * Process a hadb entry, extracting the HOST ID, HIT, and IPv6 addresses.
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+static int hip_hadb_list_peers_func(hip_ha_t *entry, void *opaque)
+{
+ hip_peer_opaque_t *op = (hip_peer_opaque_t *)opaque;
+ hip_peer_entry_opaque_t *peer_entry = NULL;
+ hip_peer_addr_opaque_t *last = NULL;
+ struct hip_peer_addr_list_item *s;
+ struct hip_spi_out_item *spi_out, *tmp;
+ char buf[46];
+ struct hip_lhi lhi;
+ int err = 0;
+ int found_addrs = 0;
+
+ /* Start by locking the entry */
+ HIP_LOCK_HA(entry);
+
+ /* Extract HIT */
+ hip_in6_ntop(&(entry->hit_peer), buf);
+ HIP_DEBUG("## Got an entry for peer HIT: %s\n", buf);
+ memset(&lhi, 0, sizeof(struct hip_lhi));
+ memcpy(&(lhi.hit),&(entry->hit_peer),sizeof(struct in6_addr));
+
+ /* Create a new peer list entry */
+ peer_entry = kmalloc(sizeof(hip_peer_entry_opaque_t),GFP_ATOMIC);
+ if (!peer_entry) {
+ HIP_ERROR("No memory to create peer list entry\n");
+ err = -ENOMEM;
+ goto error;
+ }
+ peer_entry->count = 0; /* Initialize the number of addrs to 0 */
+ peer_entry->host_id = NULL;
+ /* Record the peer hit */
+ ipv6_addr_copy(&(peer_entry->hit), &(lhi.hit));
+ peer_entry->addr_list = NULL;
+ peer_entry->next = NULL;
+
+ if (!op->head) { /* Save first list entry as head and tail */
+ op->head = peer_entry;
+ op->end = peer_entry;
+ } else { /* Add entry to the end */
+ op->end->next = peer_entry;
+ op->end = peer_entry;
+ }
+
+ /* Record each peer address */
+
+ if (entry->default_spi_out == 0) {
+ if (!ipv6_addr_any(&entry->bex_address)) {
+ err = hip_list_peers_add(&entry->bex_address,
+ peer_entry, &last);
+ if (err != 0)
+ goto error;
+ found_addrs = 1;
+ }
+ goto done;
+ }
+
+ list_for_each_entry_safe(spi_out, tmp, &entry->spis_out, list) {
+ list_for_each_entry(s, &spi_out->peer_addr_list, list) {
+ err = hip_list_peers_add(&(s->address), peer_entry,
+ &last);
+ if (err != 0)
+ goto error;
+ found_addrs = 1;
+ }
+ }
+
+ done:
+
+ /* Increment count of entries and connect the address list to
+ * peer entry only if addresses were copied */
+ if (!found_addrs) {
+ err = -ENOMSG;
+ HIP_DEBUG("entry has no usable addresses\n");
+ }
+
+ op->count++; /* increment count on error also so err handling works */
+
+ error:
+ //HIP_DEBUG("*** TODO: on error, kfree kmalloced addresses here ? ***\n");
+ _HIP_DEBUG("op->end->next=0x%p\n", op->end->next);
+ _HIP_DEBUG("op->end=0x%p\n", op->end);
+
+ HIP_UNLOCK_HA(entry);
+ return err;
+}
+
+/**
+ * hip_socket_handle_get_peer_list - handle creation of list of known peers
+ * @msg: message containing information about which unit tests to execute
+ *
+ * Process a request for the list of known peers
+ *
+ * Returns: zero on success, or negative error value on failure
+ */
+int hip_socket_handle_get_peer_list(struct hip_common *msg)
+{
+ int err = 0;
+ hip_peer_opaque_t pr;
+ int fail;
+ int i, j;
+ struct hip_host_id *peer_host_id = NULL;
+ struct hip_lhi lhi;
+ char buf[46];
+ hip_peer_entry_opaque_t *entry, *next;
+ hip_peer_addr_opaque_t *addr, *anext;
+
+ HIP_DEBUG("\n");
+
+ /* Initialize the data structure for the peer list */
+ memset(&pr, 0, sizeof(hip_peer_opaque_t));
+
+ /* Extra consistency test */
+ if (hip_get_msg_type(msg) != SO_HIP_GET_PEER_LIST) {
+ err = -EINVAL;
+ HIP_ERROR("Bad message type\n");
+ goto out_err;
+ }
+
+ /* Iterate through the hadb db entries, collecting addresses */
+ fail = hip_for_each_ha(hip_hadb_list_peers_func, &pr);
+ if (fail) {
+ err = -EINVAL;
+ HIP_ERROR("Peer list creation failed\n");
+ goto out_err;
+ }
+ HIP_DEBUG("pr.count=%d headp=0x%p end=0x%p\n", pr.count, pr.head, pr.end);
+ if (pr.count <= 0) {
+ HIP_ERROR("No usable entries found\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ /* Complete the list by iterating through the list and
+ recording the peer host id. This is done separately from
+ list creation because it involves calling a function that
+ may sleep (can't be done while holding locks!) */
+ memset(&lhi, 0, sizeof(struct hip_lhi)); /* Zero flags, etc. */
+ for (i = 0, entry = pr.head; i < pr.count; i++, entry = entry->next) {
+ /* Get the HIT */
+ memcpy(&(lhi.hit),&(entry->hit),sizeof(struct in6_addr));
+
+ /* Look up HOST ID */
+ peer_host_id = hip_get_host_id(HIP_DB_PEER_HID, &lhi);
+ if (peer_host_id == NULL) {
+ hip_in6_ntop(&(lhi.hit), buf);
+ HIP_DEBUG("Peer host id for hit (%s) not found!\n",
+ buf);
+ err = -EINVAL;
+ goto out_err;
+ }
+ HIP_DEBUG("## Hostname for HOST ID is: %s\n",
+ hip_get_param_host_id_hostname(peer_host_id));
+
+ /* Save the HOST ID */
+ entry->host_id = peer_host_id;
+ }
+
+ /* Finished. Write a return message with the peer list (reuse the
+ msg for result).
+ Format is:
+ - Number of entries
+ [ - Host identifier
+ - HIT
+ - Number of addresses
+ [ - IPv6 address
+ ...]
+ ...]
+ */
+
+ hip_msg_init(msg);
+ hip_build_user_hdr(msg, SO_HIP_GET_PEER_LIST, -err);
+
+ /********** PEER LIST COUNT *********/
+
+ err = hip_build_param_contents(msg, &(pr.count), HIP_PARAM_UINT,
+ sizeof(unsigned int));
+ if (err) {
+ HIP_ERROR("Could not build peer list count\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ for (i = 0, entry = pr.head; i < pr.count; i++, entry = entry->next) {
+ /********** HOST_ID *********/
+
+ HIP_DEBUG("The HOST ID is: %s\n",
+ hip_get_param_host_id_hostname(entry->host_id));
+ err = hip_build_param(msg, entry->host_id);
+ if (err) {
+ HIP_ERROR("Building of host id failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /********** HIT *********/
+
+ err = hip_build_param_contents(msg, &entry->hit,
+ HIP_PARAM_HIT,
+ sizeof(struct in6_addr));
+ if (err) {
+ HIP_ERROR("Building of hit failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /********** IP ADDR LIST COUNT *********/
+
+ err = hip_build_param_contents(msg, &entry->count,
+ HIP_PARAM_UINT,
+ sizeof(unsigned int));
+ if (err) {
+ HIP_ERROR("Could not build peer addr list count\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ addr = entry->addr_list;
+ for (j = 0; j < entry->count; j++, addr = addr->next) {
+ /********** IP ADDR *********/
+
+ err=hip_build_param_contents(msg, &addr->addr,
+ HIP_PARAM_IPV6_ADDR,
+ sizeof(struct in6_addr));
+ if (err) {
+ HIP_ERROR("Building of IP address failed\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ }
+ }
+
+ out_err:
+ /* Recurse through structure, freeing memory */
+ entry = pr.head;
+ _HIP_DEBUG("free mem, pr.head=0x%p\n", pr.head);
+ while (entry) {
+ _HIP_DEBUG("entry=0x%p\n", entry);
+ next = entry->next;
+ _HIP_DEBUG("next=0x%p\n", next);
+ _HIP_DEBUG("entry->host_id=0x%p\n", entry->host_id);
+ if (entry->host_id)
+ kfree(entry->host_id);
+ addr = entry->addr_list;
+ _HIP_DEBUG("addrlist=0x%p\n", addr);
+ while (addr) {
+ _HIP_DEBUG("addr=0x%p\n", addr);
+ anext = addr->next;
+ kfree(addr);
+ addr = anext;
+ }
+ kfree(entry);
+ entry = next;
+ }
+ _HIP_DEBUG("done freeing mem, err = %d\n", err);
+ return err;
+}
+
+/*
+ * The socket options that do not need a return value.
+ */
+int hip_socket_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+ struct hip_common *msg = (struct hip_common *) optval;
+ int msg_type;
+
+ HIP_DEBUG("\n");
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ /* The message was destined to TCP or IP - forward */
+ if (level != IPPROTO_HIP) {
+ err = socket_handler->setsockopt(sock, level, optname, optval,
+ optlen);
+ goto out_err;
+ }
+
+ if (!(optname == SO_HIP_GLOBAL_OPT || optname == SO_HIP_SOCKET_OPT)) {
+ err = -ESOCKTNOSUPPORT;
+ HIP_ERROR("optname (%d) was incorrect\n", optname);
+ goto out_err;
+ }
+
+ err = hip_check_userspace_msg(msg);
+ if (err) {
+ HIP_ERROR("HIP socket option was invalid\n");
+ goto out_err;
+ }
+
+ msg_type = hip_get_msg_type(msg);
+ switch(msg_type) {
+ case SO_HIP_ADD_LOCAL_HI:
+ err = hip_socket_handle_add_local_hi(msg);
+ break;
+ case SO_HIP_DEL_LOCAL_HI:
+ err = hip_socket_handle_del_local_hi(msg);
+ break;
+ case SO_HIP_ADD_PEER_MAP_HIT_IP:
+ err = hip_socket_handle_add_peer_map_hit_ip(msg);
+ break;
+ case SO_HIP_DEL_PEER_MAP_HIT_IP:
+ err = hip_socket_handle_del_peer_map_hit_ip(msg);
+ break;
+ case SO_HIP_RST:
+ err = hip_socket_handle_rst(msg);
+ break;
+ case SO_HIP_ADD_RVS:
+ err = hip_socket_handle_rvs(msg);
+ break;
+ case SO_HIP_BOS:
+ err = hip_socket_send_bos(msg);
+ break;
+ default:
+ HIP_ERROR("Unknown socket option (%d)\n", msg_type);
+ err = -ESOCKTNOSUPPORT;
+ }
+
+ out_err:
+
+ return err;
+}
+
+/*
+ * The socket options that need a return value.
+ */
+int hip_socket_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen)
+{
+ int err = 0;
+ struct proto_ops *socket_handler;
+ struct hip_common *msg = (struct hip_common *) optval;
+
+ HIP_DEBUG("%d\n", level);
+
+ err = hip_select_socket_handler(sock, &socket_handler);
+ if (err) {
+ goto out_err;
+ }
+
+ /* The message was destined to TCP or IP - forward */
+ if (level != IPPROTO_HIP) {
+ err = socket_handler->getsockopt(sock, level, optname, optval,
+ optlen);
+ goto out_err;
+ }
+
+ if (!(optname == SO_HIP_GLOBAL_OPT || optname == SO_HIP_SOCKET_OPT)) {
+ err = -ESOCKTNOSUPPORT;
+ goto out_err;
+ }
+
+ err = hip_check_userspace_msg(msg);
+ if (err) {
+ HIP_ERROR("HIP socket option was malformed\n");
+ goto out_err;
+ }
+
+ if (hip_get_msg_total_len(msg) != *optlen) {
+ HIP_ERROR("HIP socket option length was incorrect\n");
+ err = -EMSGSIZE;
+ goto out_err;
+ }
+
+ /* XX FIX: we make the assumtion here that the socket option return
+ value has enough space... */
+
+ switch(hip_get_msg_type(msg)) {
+ case SO_HIP_RUN_UNIT_TEST:
+ err = hip_socket_handle_unit_test(msg);
+ break;
+ case SO_HIP_SET_MY_EID:
+ err = hip_socket_handle_set_my_eid(msg);
+ break;
+ case SO_HIP_SET_PEER_EID:
+ err = hip_socket_handle_set_peer_eid(msg);
+ break;
+ case SO_HIP_GET_PEER_LIST:
+ err = hip_socket_handle_get_peer_list(msg);
+ break;
+ default:
+ err = -ESOCKTNOSUPPORT;
+ }
+
+
+ out_err:
+
+ return err;
+}
+
diff -urN linux-2.6.10/net/ipv6/hip/socket.h linux-2.6.10-hip/net/ipv6/hip/socket.h
--- linux-2.6.10/net/ipv6/hip/socket.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/socket.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,51 @@
+#ifndef HIP_SOCKET_H
+#define HIP_SOCKET_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+int hip_init_socket_handler(void);
+void hip_uninit_socket_handler(void);
+
+sa_eid_t hip_create_unique_local_eid(void);
+sa_eid_t hip_create_unique_peer_eid(void);
+
+int hip_create_socket(struct socket *sock, int protocol);
+
+int hip_socket_release(struct socket *sock);
+int hip_socket_bind(struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+int hip_socket_connect(struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags);
+int hip_socket_socketpair(struct socket *sock1, struct socket *sock2);
+int hip_socket_accept(struct socket *sock, struct socket *newsock,
+ int flags);
+int hip_socket_getname(struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer);
+unsigned int hip_socket_poll(struct file *file, struct socket *sock,
+ struct poll_table_struct *wait);
+int hip_socket_ioctl(struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+int hip_socket_listen(struct socket *sock, int len);
+int hip_socket_shutdown(struct socket *sock, int flags);
+int hip_socket_setsockopt(struct socket *sock, int level, int optname,
+ char *optval, int optlen);
+int hip_socket_getsockopt(struct socket *sock, int level, int optname,
+ char *optval, int *optlen);
+int hip_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *m, size_t total_len);
+int hip_socket_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
+ size_t total_len, int flags);
+int hip_socket_mmap(struct file *file, struct socket *sock,
+ struct vm_area_struct * vma);
+ssize_t hip_socket_sendpage(struct socket *sock, struct page *page, int offset,
+ size_t size, int flags);
+int hip_insert_peer_map_work_order(const struct in6_addr *hit,
+ const struct in6_addr *ip,
+ int insert, int rvs);
+
+#endif /* HIP_SOCKET_H */
diff -urN linux-2.6.10/net/ipv6/hip/test.c linux-2.6.10-hip/net/ipv6/hip/test.c
--- linux-2.6.10/net/ipv6/hip/test.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/test.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,54 @@
+/*
+ * Unit tests for HIPL kernel module.
+ *
+ * USAGE:
+ *
+ * - How to add a new test suite in the kernelspace:
+ * - Add a new struct unit_test_suite.
+ * - Insert the name of the struct into unit_test_suite_list
+ * in this file.
+ * - Increase the counter in unit_test_suite_list by one.
+ * - How to add a new test case in the kernelspace:
+ * - Insert a new HIP_UNIT_TEST_CASE(name) macro before the test suite in
+ * which the test case belongs to.
+ * - Insert the name of the macro the test suite.
+ * - Increase the counter in the test suite by one. An empty test suite has
+ * 0 as the counter value.
+ * - How to add a new test into a test case:
+ * - Insert a HIP_UNIT_ASSERT(value) macro call into the test case.
+ *
+ * Author:
+ * - Miika Komu
+ *
+ */
+
+#include "unit.h"
+#include "debug.h"
+
+#if 0 /* Commented until the test suite can be really triggered with hipconf */
+
+/*************** internal test cases ************************************/
+
+HIP_UNIT_TEST_CASE(hip_test_internal) {
+ int i = 1;
+ HIP_UNIT_ASSERT(i);
+ HIP_DEBUG("Unit test environment is working in the kernel\n");
+}
+
+static struct hip_unit_test_suite hip_unit_test_suite_internal = {
+ 1,
+ {
+ hip_test_internal
+ }
+};
+
+/**************** collection of all test suites *************************/
+
+static struct hip_unit_test_suite_list hip_unit_test_suite_list = {
+ 1,
+ {
+ &hip_unit_test_suite_internal
+ }
+};
+
+#endif /* 0 */
diff -urN linux-2.6.10/net/ipv6/hip/unit.c linux-2.6.10-hip/net/ipv6/hip/unit.c
--- linux-2.6.10/net/ipv6/hip/unit.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/unit.c 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,154 @@
+/*
+ * HIPL unit test suite implementation. The actual unit test suite functions
+ * are in separate file.
+ *
+ * NOTE: This file is shared between userspace and kernel!
+ *
+ * Licence: GNU/GPL
+ * Authors:
+ * - Miika Komu
+ *
+ */
+
+#include "unit.h"
+
+/**
+ * hip_run_unit_test_case - run a single or multiple HIP unit test cases.
+ *
+ * @list: pointer to a test suite list
+ * @suiteid: test suite number (zero = all suites in the list)
+ * @caseid: test case nuber (zero = all cases in the suite)
+ * @err_log: a string where the error log is stored
+ * @err_max: the capacity of the string
+ *
+ * Returns: the number of errors found from the runned testcases. Error
+ * log of the test cases will be recorded into err_log.
+ *
+ */
+uint16_t hip_run_unit_test_case(struct hip_unit_test_suite_list *list,
+ uint16_t suiteid, uint16_t caseid,
+ void *err_log, size_t err_max)
+{
+ size_t err_len = 0, space_left = 0;
+ void *err_index = err_log;
+ uint16_t err = 0;
+ /* Suiteid and caseid are indexed in the range [1..n] because zero is
+ reserved for executing all tests. However, xx_suite and xx_case
+ variables below are indexed in the range [0..n-1] for direct
+ access into the corresponding arrays */
+ uint16_t first_suite, current_suite, last_suite;
+ uint16_t first_case, current_case, last_case;
+
+ first_suite = (suiteid == 0) ? 0 : suiteid - 1;
+ last_suite = (suiteid == 0) ?
+ list->nbr - 1 : suiteid - 1;
+ first_case = (caseid == 0) ? 0 : caseid - 1;
+
+ _HIP_DEBUG("for1 index: %d-%d\n", suiteid, first_suite, last_suite);
+
+ if (last_suite > list->nbr - 1) {
+ HIP_ERROR("Trying to access illegal unit test suite (%d/%d)\n",
+ last_suite, list->nbr - 1);
+ err = 1;
+ goto out;
+ }
+
+ /* make sure that the string will null padded even if nothing
+ will actually be written to err_log in the call to test suite */
+ if (err_max > 0)
+ ((char *)err_log)[0] = '\0';
+
+ for (current_suite = first_suite; current_suite <= last_suite;
+ current_suite++) {
+ last_case = (caseid == 0) ?
+ list->test_suite[current_suite]->nbr - 1 :
+ caseid - 1;
+ if (last_case >
+ list->test_suite[current_suite]->nbr - 1) {
+ HIP_ERROR("Trying to access illegal test case (%d/%d)\n", last_case, list->test_suite[current_suite]->nbr - 1);
+ err += 1;
+ goto out;
+ }
+
+ for (current_case = first_case; current_case <= last_case;
+ current_case++) {
+ _HIP_DEBUG("for2 index: %d-%d\n", first_case, last_case);
+ space_left = ((void *)err_log) - err_index + err_max;
+ /* if no space left - ignore error messages,
+ just count errors */
+ if (space_left <= 0)
+ err_len = 0;
+ list->test_suite[current_suite]->test_case[current_case]
+ (&err, err_index, &err_len, space_left);
+
+ /* glibc versions prior to 2.0.6 may return -1
+ if truncated */
+ if (err_len < 0)
+ space_left = 0;
+ err_index += err_len;
+ }
+ }
+ out:
+ return err;
+
+}
+
+/**
+ * hip_run_unit_test_space - select and run tests in the unit test space
+ * @unit_space: pointer to an unit space structure
+ * @spaceid: test space id (zero = all spaces)
+ * @suiteid: test suite number (zero = all suites)
+ * @caseid: test case nuber (zero = all cases)
+ * @err_log: a string where the error log is stored
+ * @err_max: the capacity of the string
+ *
+ * This is only needed in the userspace for selecting and launching the
+ * test cases in the correct testspace (kernelspace or userspace).
+ *
+ * Returns: the number of errors occurred when the test cases were run.
+ */
+uint16_t hip_run_unit_test_space(struct hip_unit_test_space *unit_space,
+ uint16_t spaceid, uint16_t suiteid,
+ uint16_t caseid, void *err_log,
+ size_t err_max)
+{
+ /* Spaceid is indexed in the range [1..n] because zero is reserved for
+ executing in all spaces. However, xx_space variables below are
+ indexed in the range [0..n-1] for direct access into the
+ corresponding array. */
+ uint16_t first_space, current_space, last_space;
+ size_t err_len = 0, space_left = 0;
+ void *err_index = err_log;
+ uint16_t err = 0;
+
+ first_space = (spaceid == 0) ? 0 : spaceid - 1;
+ last_space = (spaceid == 0) ?
+ unit_space->nbr - 1 : spaceid - 1;
+
+ if (last_space > unit_space->nbr - 1) {
+ HIP_ERROR("Trying to access illegal unit test spaceid (%d/%d).", last_space, unit_space->nbr - 1);
+ err = 1;
+ goto out;
+ }
+
+ /* make sure that the string will null padded even if nothing
+ will actually be written to err_log in the call to test suite */
+ if (err_max > 0)
+ ((char *)err_log)[0] = '\0';
+
+ _HIP_DEBUG("for index: %d-%d\n", first_space, last_space);
+
+ for (current_space = first_space; current_space <= last_space;
+ current_space++) {
+ space_left = ((void *)err_log) - err_index + err_max;
+ /* if no space - ignore error messages, just count errors */
+ if (space_left <= 0)
+ err_len = 0;
+ err += hip_run_unit_test_case(unit_space->test_suite_list[current_space], suiteid, caseid, err_index, space_left);
+ err_len = strlen(err_index);
+ err_index += err_len;
+ }
+
+ out:
+ return err;
+}
diff -urN linux-2.6.10/net/ipv6/hip/unit.h linux-2.6.10-hip/net/ipv6/hip/unit.h
--- linux-2.6.10/net/ipv6/hip/unit.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/unit.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,82 @@
+#ifndef HIP_UNIT
+#define HIP_UNIT
+
+#ifdef __KERNEL__
+# include
+# include
+# include
+# include "debug.h"
+#else
+# include
+# include
+# include "libinet6/debug.h"
+#endif /* __KERNEL__ */
+
+
+/*
+ * A maximum amount of test spaces, suites and cases have be fixed because they
+ * are created statically at compile time. A dynamic method can cause
+ * other side effects (dynamic allocation of memory), so it was not used.
+ * Each MAX variable has to include also the NULL element in the contained
+ * structure.
+ */
+#define HIP_UNIT_TEST_SPACE_MAX 3
+#define HIP_UNIT_TEST_SUITE_MAX 10
+#define HIP_UNIT_TEST_CASE_MAX 50
+
+#define HIP_UNIT_TEST_NAME_MAX 15
+
+#define HIP_UNIT_ASSERT(s) \
+ do { \
+ if (!(s)) { \
+ (*__hip_unit_err)++; \
+ *__hip_unit_err_len = \
+ snprintf(__hip_unit_err_msg, __hip_unit_err_msg_max_len, \
+ "HIP unit assertion failed in %s in %s line %d\n", \
+ __FILE__, __FUNCTION__, __LINE__); \
+ return; \
+ } else { \
+ *__hip_unit_err_len = 0; \
+ } \
+ } while (0)
+
+#define HIP_UNIT_TEST_CASE(name) \
+ void name(uint16_t *__hip_unit_err, void *__hip_unit_err_msg, \
+ size_t *__hip_unit_err_len, \
+ const uint16_t __hip_unit_err_msg_max_len)
+
+typedef void (*hip_unit_test_case)(uint16_t *, void *,
+ size_t *, const uint16_t);
+
+struct hip_unit_test_suite {
+ uint16_t nbr;
+ hip_unit_test_case test_case[HIP_UNIT_TEST_CASE_MAX];
+};
+
+struct hip_unit_test_suite_list {
+ uint16_t nbr;
+ struct hip_unit_test_suite *test_suite[HIP_UNIT_TEST_SUITE_MAX];
+};
+
+/*
+ * This structure is used for separating kernel and userspace test suites.
+ * It is require for making a three level hierarchy: testspace / testsuite /
+ * testcase. Kernel does not this structure but it is include here for
+ * consistency.
+ */
+struct hip_unit_test_space {
+ uint16_t nbr;
+ struct hip_unit_test_suite_list *test_suite_list[HIP_UNIT_TEST_SPACE_MAX];
+};
+
+uint16_t hip_run_unit_test_case(struct hip_unit_test_suite_list *list,
+ uint16_t suiteid, uint16_t caseid,
+ void *err_log, size_t err_max);
+
+
+uint16_t hip_run_unit_test_space(struct hip_unit_test_space *unit_space,
+ uint16_t spaceid, uint16_t suiteid,
+ uint16_t caseid, void *err_log,
+ size_t err_max);
+
+#endif /* HIP_UNIT */
diff -urN linux-2.6.10/net/ipv6/hip/update.c linux-2.6.10-hip/net/ipv6/hip/update.c
--- linux-2.6.10/net/ipv6/hip/update.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/update.c 2005-03-14 14:24:10.000000000 +0200
@@ -0,0 +1,2046 @@
+/*
+ * Licence: GNU/GPL
+ * Authors:
+ * - Mika Kousa
+ */
+
+#include
+#include
+#include
+
+#include "update.h"
+#include "hip.h"
+#include "security.h"
+#include "input.h"
+#include "hadb.h"
+#include "db.h"
+#include "keymat.h"
+#include "builder.h"
+#include "misc.h"
+#include "output.h"
+
+
+/* SPI waitlist stuff is not currently used */
+
+
+#if 0
+/* List of SPIs which are waiting for data to come through */
+/* See draft's section "Processing an initial UPDATE packet" */
+LIST_HEAD(hip_update_spi_waitlist);
+spinlock_t hip_update_spi_waitlist_lock = SPIN_LOCK_UNLOCKED;
+
+struct hip_update_spi_waitlist_item {
+ struct list_head list;
+ uint32_t spi;
+ struct in6_addr hit;
+ struct in6_addr v6addr_test;
+ /* REA stuff ? address list */
+};
+#endif
+
+
+#if 0
+/**
+ * hip_update_spi_waitlist_add - add a SPI to SPI waitlist
+ * @spi: the inbound SPI to be added in host byte order
+ * @hit: the HIT for which the @spi is related to
+ */
+void hip_update_spi_waitlist_add(uint32_t spi, struct in6_addr *hit, struct hip_rea *rea)
+{
+ struct hip_update_spi_waitlist_item *s;
+ unsigned long flags = 0;
+ struct list_head *pos, *n;
+ int i = 1;
+
+ HIP_DEBUG("spi=0x%x\n", spi);
+
+ s = kmalloc(sizeof(struct hip_update_spi_waitlist_item), GFP_ATOMIC);
+ if (!s) {
+ HIP_ERROR("kmalloc failed\n");
+ return;
+ }
+
+ s->spi = spi;
+ ipv6_addr_copy(&s->hit, hit);
+
+ spin_lock_irqsave(&hip_update_spi_waitlist_lock, flags);
+ list_add(&s->list, &hip_update_spi_waitlist);
+ HIP_DEBUG("Current SPI waitlist:\n");
+ list_for_each_safe(pos, n, &hip_update_spi_waitlist) {
+ s = list_entry(pos, struct hip_update_spi_waitlist_item, list);
+ HIP_DEBUG("SPI waitlist %d: SPI=0x%x\n", i, s->spi);
+ i++;
+ }
+ spin_unlock_irqrestore(&hip_update_spi_waitlist_lock, flags);
+ HIP_DEBUG("End of SPI waitlist\n");
+ return;
+}
+#endif
+
+#if 0
+/**
+ * hip_update_spi_waitlist_delete - delete a SPI from SPI waitlist
+ * @spi: SPI in host byte order to be deleted
+ */
+void hip_update_spi_waitlist_delete(uint32_t spi)
+{
+ struct list_head *pos, *n;
+ struct hip_update_spi_waitlist_item *s = NULL;
+ int i = 1;
+
+ HIP_DEBUG("deleting spi=0x%x\n", spi);
+ /* hip_update_spi_waitlist_ispending holds the
+ * hip_update_spi_waitlist_lock */
+ list_for_each_safe(pos, n, &hip_update_spi_waitlist) {
+ s = list_entry(pos, struct hip_update_spi_waitlist_item, list);
+ if (s->spi == spi) {
+ HIP_DEBUG("found, delete item %d\n", i);
+ list_del(&s->list);
+ kfree(s);
+ break;
+ }
+ i++;
+ }
+ return;
+}
+#endif
+
+#if 0
+/**
+ * hip_update_spi_waitlist_delete_all - delete all SPIs from the SPI waitlist
+ */
+void hip_update_spi_waitlist_delete_all(void)
+{
+ struct list_head *pos, *n;
+ struct hip_update_spi_waitlist_item *s = NULL;
+ unsigned long flags = 0;
+
+ HIP_DEBUG("\n");
+ spin_lock_irqsave(&hip_update_spi_waitlist_lock, flags);
+ list_for_each_safe(pos, n, &hip_update_spi_waitlist) {
+ s = list_entry(pos, struct hip_update_spi_waitlist_item, list);
+ list_del(&s->list);
+ kfree(s);
+ }
+ spin_unlock_irqrestore(&hip_update_spi_waitlist_lock, flags);
+ return;
+}
+#endif
+
+#if 0
+/**
+ * hip_update_spi_waitlist_ispending - test if SPI is on the SPI waitlist
+ * @spi: SPI in host byte order
+ *
+ * Called from xfrm6_rcv.
+ *
+ * When data is received on the new incoming SA the new outgoing SA is
+ * activated and old SA is deleted.
+ *
+ * Returns 1 if @spi is in the SPI waitlist, otherwise 0.
+ */
+int hip_update_spi_waitlist_ispending(uint32_t spi)
+{
+#if 0
+ int err = 0, found = 0;
+ struct hip_update_spi_waitlist_item *s = NULL;
+ unsigned long flags = 0;
+ struct list_head *pos, *n;
+ int i = 1;
+#endif
+
+ HIP_DEBUG("skipping test, not needed anymore ?\n");
+ return 0;
+#if 0
+ spin_lock_irqsave(&hip_update_spi_waitlist_lock, flags);
+
+ list_for_each_safe(pos, n, &hip_update_spi_waitlist) {
+ s = list_entry(pos, struct hip_update_spi_waitlist_item, list);
+ HIP_DEBUG("SPI waitlist %d: SPI=0x%x\n", i, s->spi);
+ if (s->spi == spi) {
+ found = 1;
+ break;
+ }
+ i++;
+ }
+
+ /* If the SPI was in pending list, switch the NEW_SPI to be
+ * the current active SPI. Delete also the old SA and remove
+ * the SPI from the pending list.*/
+ if (found) {
+ uint32_t old_spi_out;
+ hip_ha_t *entry;
+ HIP_DEBUG("spi=0x%x : pending=yes\n", spi);
+
+ entry = hip_hadb_find_byhit(&s->hit);
+ if (!entry) {
+ HIP_ERROR("Entry not found\n");
+ goto out;
+ }
+ HIP_LOCK_HA(entry);
+
+ HIP_DEBUG("Switching from SPI_OUT=0x%x to NEW_SPI_OUT=0x%x\n",
+ entry->spi_out, entry->new_spi_out);
+ old_spi_out = entry->spi_out;
+// hip_hadb_remove_state_spi(entry);
+ entry->spi_out = entry->new_spi_out;
+ hip_hadb_insert_state(entry);
+
+ /* SETTING OF DEFAULT SPI THIS WAY IS BROKEN */
+ entry->default_spi_out = entry->spi_out;
+ HIP_DEBUG("set default SPI out=0x%x\n", entry->default_spi_out);
+
+ /* test, todo: addr from rea */
+// hip_print_hit("v6addr_test", &s->v6addr_test);
+// hip_hadb_add_addr_to_spi(entry, entry->default_spi_out, &s->v6addr_test, PEER_ADDR_STATE_ACTIVE, 0, 0);
+
+// hip_hadb_insert_state(entry);
+ hip_print_hit("finalizing", &s->hit);
+ hip_finalize_sa(&s->hit, entry->new_spi_out);
+#if 1
+ HIP_DEBUG("Removing old inbound IPsec SA, SPI=0x%x\n", old_spi_out);
+ hip_ifindex2spi_map_del(entry, old_spi_out);
+// err = hip_delete_sa(old_spi_out, &s->hit);
+ hip_hadb_delete_outbound_spi(entry, old_spi_out);
+// if (err)
+// HIP_DEBUG("delete_sa ret err=%d\n", err);
+ entry->new_spi_out = 0;
+#endif
+
+ HIP_UNLOCK_HA(entry);
+ hip_put_ha(entry);
+ hip_update_spi_waitlist_delete(spi);
+ }
+
+ out:
+ spin_unlock_irqrestore(&hip_update_spi_waitlist_lock, flags);
+ return found;
+#endif
+}
+#endif
+
+
+
+/** hip_update_get_sa_keys - Get keys needed by UPDATE
+ * @entry: corresponding hadb entry of the peer
+ * @keymat_offset_new: value-result parameter for keymat index used
+ * @calc_index_new: value-result parameter for the one byte index used
+ * @Kn_out: value-result parameter for keymat
+ * @espkey_gl: HIP-gl encryption key
+ * @authkey_gl: HIP-gl integrity (HMAC)
+ * @espkey_lg: HIP-lg encryption key
+ * @authkey_lg: HIP-lg integrity (HMAC)
+ *
+ * Returns: 0 on success (all encryption and integrity keys are
+ * successfully stored and @keymat_offset_new, @calc_index_new, and
+ * @Kn_out contain updated values). On error < 0 is returned.
+ */
+int hip_update_get_sa_keys(hip_ha_t *entry, uint16_t *keymat_offset_new,
+ uint8_t *calc_index_new, uint8_t *Kn_out,
+ struct hip_crypto_key *espkey_gl, struct hip_crypto_key *authkey_gl,
+ struct hip_crypto_key *espkey_lg, struct hip_crypto_key *authkey_lg)
+{
+ int err = 0;
+ unsigned char Kn[HIP_AH_SHA_LEN];
+ uint16_t k = *keymat_offset_new;
+ uint8_t c = *calc_index_new;
+ int esp_transform;
+ int esp_transf_length = 0;
+ int auth_transf_length = 0;
+ uint16_t Kn_pos;
+
+ _HIP_DEBUG("k=%u c=%u\n", k, c);
+
+ esp_transform = entry->esp_transform;
+ esp_transf_length = hip_enc_key_length(esp_transform);
+ auth_transf_length = hip_auth_key_length_esp(esp_transform);
+ _HIP_DEBUG("enckeylen=%d authkeylen=%d\n", esp_transf_length, auth_transf_length);
+
+ if (*keymat_offset_new + 2*(esp_transf_length+auth_transf_length) > 0xffff) {
+ HIP_ERROR("Can not draw requested amount of new KEYMAT, keymat index=%u, requested amount=%d\n",
+ *keymat_offset_new, 2*(esp_transf_length+auth_transf_length));
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ memcpy(Kn, Kn_out, HIP_AH_SHA_LEN);
+
+ /* SA-gl */
+ Kn_pos = entry->current_keymat_index - (entry->current_keymat_index % HIP_AH_SHA_LEN);
+ err = hip_keymat_get_new(espkey_gl->key, esp_transf_length, entry->dh_shared_key,
+ entry->dh_shared_key_len, &k, &c, Kn, &Kn_pos);
+ _HIP_DEBUG("enckey_gl hip_keymat_get_new ret err=%d k=%u c=%u\n", err, k, c);
+ if (err)
+ goto out_err;
+ _HIP_HEXDUMP("ENC KEY gl", espkey_gl->key, esp_transf_length);
+ k += esp_transf_length;
+
+ err = hip_keymat_get_new(authkey_gl->key, auth_transf_length, entry->dh_shared_key,
+ entry->dh_shared_key_len, &k, &c, Kn, &Kn_pos);
+ _HIP_DEBUG("authkey_gl hip_keymat_get_new ret err=%d k=%u c=%u\n", err, k, c);
+ if (err)
+ goto out_err;
+ _HIP_HEXDUMP("AUTH KEY gl", authkey_gl->key, auth_transf_length);
+ k += auth_transf_length;
+
+ /* SA-lg */
+ err = hip_keymat_get_new(espkey_lg->key, esp_transf_length, entry->dh_shared_key,
+ entry->dh_shared_key_len, &k, &c, Kn, &Kn_pos);
+ _HIP_DEBUG("enckey_lg hip_keymat_get_new ret err=%d k=%u c=%u\n", err, k, c);
+ if (err)
+ goto out_err;
+ _HIP_HEXDUMP("ENC KEY lg", espkey_lg->key, esp_transf_length);
+ k += esp_transf_length;
+ err = hip_keymat_get_new(authkey_lg->key, auth_transf_length, entry->dh_shared_key,
+ entry->dh_shared_key_len, &k, &c, Kn, &Kn_pos);
+ _HIP_DEBUG("authkey_lg hip_keymat_get_new ret err=%d k=%u c=%u\n", err, k, c);
+ if (err)
+ goto out_err;
+ _HIP_HEXDUMP("AUTH KEY lg", authkey_lg->key, auth_transf_length);
+ k += auth_transf_length;
+
+ _HIP_DEBUG("at end: k=%u c=%u\n", k, c);
+ *keymat_offset_new = k;
+ *calc_index_new = c;
+ memcpy(Kn_out, Kn, HIP_AH_SHA_LEN);
+ out_err:
+ return err;
+}
+
+/** hip_update_test_rea_addr - test if IPv6 address is to be added into REA.
+ * @addr: the IPv6 address to be tested
+ *
+ * Currently the following address types are ignored: unspecified
+ * (any), loopback, link local, site local, and other not unicast
+ * addresses.
+ *
+ * Returns 1 if address is ok to be used as a peer address, otherwise 0.
+*/
+int hip_update_test_rea_addr(struct in6_addr *addr)
+{
+ int addr_type = ipv6_addr_type(addr);
+
+ if (addr_type == IPV6_ADDR_ANY) {
+ HIP_DEBUG("skipping IPV6_ADDR_ANY address\n");
+ return 0;
+ }
+ if (addr_type & IPV6_ADDR_LOOPBACK) {
+ HIP_DEBUG("skipping loopback address, not supported\n");
+ return 0;
+ }
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ HIP_DEBUG("skipping link local address, not supported\n");
+ return 0;
+ }
+ if (addr_type & IPV6_ADDR_SITELOCAL) {
+ HIP_DEBUG("skipping site local address, not supported\n");
+ return 0;
+ }
+ if (! (addr_type & IPV6_ADDR_UNICAST) ) {
+ HIP_DEBUG("skipping non-unicast address\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+/** hip_update_handle_rea_parameter - Process REA parameters in the UPDATE
+ * @entry: corresponding hadb entry of the peer
+ * @rea: the REA parameter in the packet
+ *
+ * Returns: 0 if the REA parameter was processed successfully,
+ * otherwise < 0.
+ */
+int hip_update_handle_rea_parameter(hip_ha_t *entry, struct hip_rea *rea)
+{
+ int err = 0; /* set to -Esomething ?*/
+ uint32_t spi;
+ struct hip_rea_info_addr_item *rea_address_item;
+ int i, n_addrs;
+ struct hip_spi_out_item *spi_out;
+ struct hip_peer_addr_list_item *a, *tmp;
+
+ /* assume already locked entry */
+
+ /* ietf-mm-02 7.2 Handling received REAs */
+
+ spi = ntohl(rea->spi);
+ HIP_DEBUG("REA SPI=0x%x\n", spi);
+
+ if ((hip_get_param_total_len(rea) - sizeof(struct hip_rea)) %
+ sizeof(struct hip_rea_info_addr_item))
+ HIP_ERROR("addr item list len modulo not zero, (len=%d)\n",
+ ntohs(rea->length));
+
+ n_addrs = (hip_get_param_total_len(rea) - sizeof(struct hip_rea)) /
+ sizeof(struct hip_rea_info_addr_item);
+ HIP_DEBUG("REA has %d address(es), rea param len=%d\n",
+ n_addrs, hip_get_param_total_len(rea));
+ if (n_addrs < 0) {
+ HIP_DEBUG("BUG: n_addrs=%d < 0\n", n_addrs);
+ goto out_err;
+ }
+
+ /* 1. The host checks if the SPI listed is a new one. If it
+ is a new one, it creates a new SPI that contains no addresses. */
+ spi_out = hip_hadb_get_spi_list(entry, spi);
+ if (!spi_out) {
+ /* bug: outbound SPI must have been already created by the
+ corresponding NES in the same UPDATE packet */
+ HIP_ERROR("bug: outbound SPI 0x%x does not exist\n", spi);
+ goto out_err;
+ }
+
+ _HIP_DEBUG("Clearing old preferred flags of the SPI\n");
+ list_for_each_entry_safe(a, tmp, &spi_out->peer_addr_list, list) {
+ a->is_preferred = 0;
+ }
+
+ rea_address_item = (void *)rea+sizeof(struct hip_rea);
+ for(i = 0; i < n_addrs; i++, rea_address_item++) {
+ struct in6_addr *rea_address = &rea_address_item->address;
+ uint32_t lifetime = ntohl(rea_address_item->lifetime);
+ int is_preferred = ntohl(rea_address_item->reserved) == 1 << 31;
+
+ hip_print_hit("REA address", rea_address);
+ HIP_DEBUG(" addr %d: is_pref=%s reserved=0x%x lifetime=0x%x\n", i+1,
+ is_preferred ? "yes" : "no", ntohl(rea_address_item->reserved),
+ lifetime);
+ /* 2. check that the address is a legal unicast or anycast address */
+ if (!hip_update_test_rea_addr(rea_address))
+ continue;
+
+ if (i > 0) {
+ /* preferred address allowed only for the first address */
+ if (is_preferred)
+ HIP_ERROR("bug, preferred flag set to other than the first address\n");
+ is_preferred = 0;
+ }
+ /* 3. check if the address is already bound to the SPI + add/update address */
+ err = hip_hadb_add_addr_to_spi(entry, spi, rea_address, 0,
+ lifetime, is_preferred);
+ if (err) {
+ HIP_DEBUG("failed to add/update address to the SPI list\n");
+ goto out_err;
+ }
+ }
+
+ /* 4. Mark all addresses on the SPI that were NOT listed in the REA
+ parameter as DEPRECATED. */
+ _HIP_DEBUG("deprecating not listed address from the SPI list\n");
+
+ list_for_each_entry_safe(a, tmp, &spi_out->peer_addr_list, list) {
+ int spi_addr_is_in_rea = 0;
+
+ rea_address_item = (void *)rea+sizeof(struct hip_rea);
+ for(i = 0; i < n_addrs; i++, rea_address_item++) {
+ struct in6_addr *rea_address = &rea_address_item->address;
+
+ if (!ipv6_addr_cmp(&a->address, rea_address)) {
+ spi_addr_is_in_rea = 1;
+ break;
+ }
+
+ }
+ if (!spi_addr_is_in_rea) {
+ /* deprecate the address */
+ hip_print_hit("deprecating address", &a->address);
+ a->address_state = PEER_ADDR_STATE_DEPRECATED;
+ }
+ }
+
+ if (n_addrs == 0) /* our own extension, use some other SPI */
+ (void)hip_hadb_relookup_default_out(entry);
+ /* relookup always ? */
+
+ _HIP_DEBUG("done\n");
+ out_err:
+ return err;
+}
+
+
+/**
+ * hip_handle_update_established - handle incoming UPDATE packet received in ESTABLISHED state
+ * @entry: hadb entry corresponding to the peer
+ * @msg: the HIP packet
+ * @src_ip: source IPv6 address from where the UPDATE was sent
+ * @dst_ip: destination IPv6 address where the UPDATE was received
+ *
+ * This function handles case 7 in section 8.11 Processing UPDATE
+ * packets of the base draft.
+ *
+ * Returns: 0 if successful, otherwise < 0.
+ */
+int hip_handle_update_established(hip_ha_t *entry, struct hip_common *msg,
+ struct in6_addr *src_ip, struct in6_addr *dst_ip)
+{
+ int err = 0;
+ struct in6_addr *hits = &msg->hits, *hitr = &msg->hitr;
+ struct hip_nes *nes;
+ struct hip_seq *seq;
+ struct hip_rea *rea;
+ struct hip_dh_fixed *dh;
+ struct hip_host_id *host_id_private;
+ uint32_t update_id_out = 0;
+ uint32_t prev_spi_in = 0, new_spi_in = 0;
+ uint16_t keymat_index = 0;
+ struct hip_common *update_packet = NULL;
+ //struct in6_addr daddr;
+ u8 signature[HIP_RSA_SIGNATURE_LEN]; /* RSA sig > DSA sig */
+ int need_to_generate_key = 0, dh_key_generated = 0; //, new_keymat_generated;
+ int nes_i = 1;
+
+ /* assume already locked entry */
+ uint16_t mask;
+
+ HIP_DEBUG("\n");
+
+ seq = hip_get_param(msg, HIP_PARAM_SEQ);
+ if (!seq) {
+ HIP_ERROR("No SEQ parameter in packet\n");
+ goto out_err;
+ }
+
+ /* 8.11.1 Processing an UPDATE packet in state ESTABLISHED */
+
+ /* 1. The system consults its policy to see if it needs to generate a
+ new Diffie-Hellman key, and generates a new key if needed. */
+ _HIP_DEBUG("8.11.1 case 1 TODO: need to rekey here ?\n");
+ if (need_to_generate_key) {
+ _HIP_DEBUG("would generate new D-H keys\n");
+ /* generate_dh_key(); */
+ dh_key_generated = 1;
+ /* todo: The system records any newly generated or
+ received Diffie-Hellman keys, for use in KEYMAT generation upon
+ leaving the REKEYING state. */
+ } else {
+ dh_key_generated = 0;
+ }
+
+ _HIP_DEBUG("dh_key_generated=%d\n", dh_key_generated);
+
+ /* 4. The system creates a UPDATE packet, which contains an SEQ
+ parameter (with the current value of Update ID), NES parameter
+ and the optional DIFFIE_HELLMAN parameter. The UPDATE packet also
+ includes the ACK of the Update ID found in the received UPDATE
+ SEQ parameter. */
+ update_packet = hip_msg_alloc();
+ if (!update_packet) {
+ HIP_ERROR("update_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask, hitr, hits);
+
+ /* 3. The system increments its outgoing Update ID by one. */
+ entry->update_id_out++;
+ update_id_out = entry->update_id_out;
+ _HIP_DEBUG("outgoing UPDATE ID=%u\n", update_id_out);
+ if (!update_id_out) { /* todo: handle this case */
+ HIP_ERROR("outgoing UPDATE ID overflowed back to 0, bug ?\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ /* test: handle multiple NES, not tested well yet */
+ handle_nes:
+ nes = hip_get_nth_param(msg, HIP_PARAM_NES, nes_i);
+ if (!nes) {
+ HIP_DEBUG("no more NES params found\n");
+ goto nes_params_handled;
+ }
+ HIP_DEBUG("Found NES parameter [%d]\n", nes_i);
+
+ /* 2. If the system generated new Diffie-Hellman key in the previous
+ step, or it received a DIFFIE_HELLMAN parameter, it sets NES
+ Keymat Index to zero. */
+ dh = hip_get_param(msg, HIP_PARAM_DIFFIE_HELLMAN);
+ if (dh || dh_key_generated) {
+ HIP_DEBUG("would generate new keymat\n");
+ /* todo: generate_new_keymat(); */
+ keymat_index = 0;
+ } else {
+ /* Otherwise, the NES Keymat Index MUST be larger or
+ equal to the index of the next byte to be drawn from the
+ current KEYMAT. */
+ if (ntohs(nes->keymat_index) < entry->current_keymat_index) {
+ HIP_ERROR("NES Keymat Index (%u) < current KEYMAT %u\n",
+ ntohs(nes->keymat_index), entry->current_keymat_index);
+ goto out_err;
+ }
+ /* In this case, it is RECOMMENDED that the host use the
+ Keymat Index requested by the peer in the received NES. */
+
+ /* here we could set the keymat index to use, but we
+ * follow the recommendation */
+ _HIP_DEBUG("Using Keymat Index from NES\n");
+ keymat_index = ntohs(nes->keymat_index);
+ }
+
+ /* Set up new incoming IPsec SA, (Old SPI value to put in NES tlv) */
+ prev_spi_in = hip_get_spi_to_update_in_established(entry, dst_ip);
+ HIP_DEBUG("Old incoming SA selected for update, prev_spi_in=0x%x\n", prev_spi_in);
+ if (!prev_spi_in)
+ goto out_err;
+
+ new_spi_in = hip_acquire_spi(hits, hitr);
+ if (!new_spi_in) {
+ HIP_ERROR("Error while acquiring a SPI\n");
+ goto out_err;
+ }
+ HIP_DEBUG("acquired inbound SPI 0x%x\n", new_spi_in);
+ hip_update_set_new_spi_in(entry, prev_spi_in, new_spi_in, ntohl(nes->old_spi));
+
+ /* draft-hip-mm test */
+ if (nes->old_spi == nes->new_spi) {
+ struct hip_spi_out_item spi_out_data;
+
+ _HIP_DEBUG("peer has a new SA, create a new outbound SA\n");
+ memset(&spi_out_data, 0, sizeof(struct hip_spi_out_item));
+ spi_out_data.spi = ntohl(nes->new_spi);
+ spi_out_data.seq_update_id = ntohl(seq->update_id);
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_OUT, &spi_out_data);
+ if (err) {
+ goto out_err;
+ }
+ HIP_DEBUG("added SPI=0x%x to list of outbound SAs (SA not created yet)\n",
+ ntohl(nes->new_spi));
+ }
+
+ /* testing REA parameters in UPDATE */
+ rea = hip_get_nth_param(msg, HIP_PARAM_REA, nes_i);
+ if (rea) {
+ HIP_DEBUG("Found REA parameter [%d]\n", nes_i);
+ if (rea->spi != nes->new_spi) {
+ HIP_ERROR("SPI 0x%x in REA is not equal to the New SPI 0x%x in NES\n",
+ ntohl(rea->spi), ntohl(nes->new_spi));
+ } else {
+ err = hip_update_handle_rea_parameter(entry, rea);
+ _HIP_DEBUG("rea param handling ret %d\n", err);
+ err = 0;
+ }
+ }
+
+ /* associate Old SPI with Update ID, NES received, store
+ * received NES and proposed keymat index value used in the reply NES */
+ hip_update_set_status(entry, prev_spi_in,
+ 0x1 | 0x2 | 0x4 | 0x8, update_id_out, 0x2,
+ nes, keymat_index);
+
+ nes_i++;
+ goto handle_nes;
+
+ nes_params_handled:
+
+ /* 5. The system sends the UPDATE packet and transitions to state
+ REKEYING. The system stores any received NES and DIFFIE_HELLMAN
+ parameters. */
+
+ err = hip_build_param_nes(update_packet, keymat_index,
+ prev_spi_in, new_spi_in);
+ if (err) {
+ HIP_ERROR("Building of NES failed\n");
+ goto out_err;
+ }
+
+ err = hip_build_param_seq(update_packet, update_id_out);
+ if (err) {
+ HIP_ERROR("Building of SEQ failed\n");
+ goto out_err;
+ }
+
+ /* ACK the received UPDATE SEQ */
+ err = hip_build_param_ack(update_packet, ntohl(seq->update_id));
+ if (err) {
+ HIP_ERROR("Building of ACK failed\n");
+ goto out_err;
+ }
+
+ /* TODO: hmac/signature to common functions */
+ /* Add HMAC */
+ err = hip_build_param_hmac_contents(update_packet, &entry->hip_hmac_out);
+ if (err) {
+ HIP_ERROR("Building of HMAC failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* Add SIGNATURE */
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not get our host identity. Can not sign data\n");
+ goto out_err;
+ }
+
+ if (!hip_create_signature(update_packet, hip_get_msg_total_len(update_packet),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not sign UPDATE. Failing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of SIGNATURE failed (%d)\n", err);
+ goto out_err;
+ }
+ _HIP_DEBUG("SIGNATURE added\n");
+
+#if 0
+ err = hip_hadb_get_peer_addr(entry, &daddr);
+ if (err) {
+ HIP_DEBUG("hip_sdb_get_peer_address err = %d\n", err);
+ goto out_err;
+ }
+#endif
+ /* 5. The system sends the UPDATE packet and transitions to state
+ REKEYING. */
+ entry->state = HIP_STATE_REKEYING;
+ HIP_DEBUG("moved to state REKEYING\n");
+
+ HIP_DEBUG("Sending reply UPDATE packet\n");
+ //err = hip_csum_send(NULL, &daddr, update_packet);
+ err = hip_csum_send(NULL, src_ip, update_packet);
+ if (err) {
+ HIP_DEBUG("hip_csum_send err=%d\n", err);
+ HIP_DEBUG("NOT ignored, or should we..\n");
+ /* fallback to established ? */
+ /* goto out_err; ? */
+ }
+
+ out_err:
+ if (update_packet)
+ kfree(update_packet);
+ if (err) {
+ hip_set_spi_update_status(entry, prev_spi_in, 0);
+ /* SA remove not tested yet */
+ if (new_spi_in) {
+ //hip_delete_sa(new_spi_in, hitr);
+ hip_hadb_delete_inbound_spi(entry, new_spi_in);
+ }
+ }
+
+ return err;
+}
+
+int hip_update_send_addr_verify(hip_ha_t *entry, struct hip_common *msg,
+ struct in6_addr *src_ip, uint32_t spi);
+
+
+/* 8.11.3 Leaving REKEYING state */
+/* @nes is the NES param to be handled in the received UPDATE in host byte order */
+/* @entry is locked when this function is called */
+int hip_update_finish_rekeying(struct hip_common *msg, hip_ha_t *entry,
+ struct hip_nes *nes)
+{
+ int err = 0;
+ struct in6_addr *hits = &msg->hits, *hitr = &msg->hitr;
+ uint8_t calc_index_new;
+ unsigned char Kn[HIP_AH_SHA_LEN];
+ uint16_t keymat_index;
+ struct hip_crypto_key espkey_gl, authkey_gl;
+ struct hip_crypto_key espkey_lg, authkey_lg;
+ uint32_t new_spi_in = 0; /* inbound IPsec SA SPI */
+ uint32_t new_spi_out = 0; /* outbound IPsec SA SPI */
+ uint32_t prev_spi_in = 0, prev_spi_out = 0;
+ int we_are_HITg = 0;
+ int esp_transform = -1;
+ int esp_transf_length = 0;
+ int auth_transf_length = 0;
+ struct hip_spi_in_item spi_in_data;
+ struct hip_ack *ack;
+ uint16_t kmindex_saved;
+
+ /* assume already locked entry */
+
+ HIP_DEBUG("\n");
+ ack = hip_get_param(msg, HIP_PARAM_ACK);
+
+ HIP_DEBUG("handled NES: Old SPI: 0x%x\n", nes->old_spi);
+ HIP_DEBUG("handled NES: New SPI: 0x%x\n", nes->new_spi);
+ HIP_DEBUG("handled NES: Keymat Index: %u\n", nes->keymat_index);
+
+ prev_spi_out = nes->old_spi;
+ if (!prev_spi_out) {
+ HIP_ERROR("bug: stored NES Old SPI is 0\n");
+ goto out_err;
+ }
+
+ new_spi_out = nes->new_spi;
+ if (!new_spi_out) {
+ HIP_ERROR("bug: stored NES New SPI is 0\n");
+ goto out_err;
+ }
+
+ prev_spi_in = hip_update_get_prev_spi_in(entry, ntohl(ack->peer_update_id));
+
+ /* use the new inbound IPsec SA created when rekeying started */
+ new_spi_in = hip_update_get_new_spi_in(entry, ntohl(ack->peer_update_id));
+ if (!new_spi_in) {
+ HIP_ERROR("Did not find related New SPI for peer Update ID %u\n",
+ ntohl(ack->peer_update_id));
+ goto out_err;
+ }
+ HIP_DEBUG("prev_spi_in=0x%x new_spi_in=0x%x prev_spi_out=0x%x new_spi_out=0x%x\n",
+ prev_spi_in, new_spi_in, prev_spi_out, new_spi_out);
+
+ kmindex_saved = hip_update_get_spi_keymat_index(entry, ntohl(ack->peer_update_id));
+ if (!kmindex_saved) {
+ HIP_ERROR("saved kmindex is 0\n");
+ goto out_err;
+ }
+ _HIP_DEBUG("saved kmindex for NES is %u\n", kmindex_saved);
+
+ /* 2. .. If the system did not generate new KEYMAT, it uses
+ the lowest Keymat Index of the two NES parameters. */
+ _HIP_DEBUG("entry keymat index=%u\n", entry->current_keymat_index);
+ if (kmindex_saved < nes->keymat_index)
+ keymat_index = kmindex_saved;
+ else
+ keymat_index = nes->keymat_index;
+ _HIP_DEBUG("lowest keymat_index=%u\n", keymat_index);
+
+ /* 3. The system draws keys for new incoming and outgoing ESP
+ SAs, starting from the Keymat Index, and prepares new incoming
+ and outgoing ESP SAs. */
+ we_are_HITg = hip_hit_is_bigger(hitr, hits);
+ HIP_DEBUG("we are: HIT%c\n", we_are_HITg ? 'g' : 'l');
+
+ esp_transform = entry->esp_transform;
+ esp_transf_length = hip_enc_key_length(esp_transform);
+ auth_transf_length = hip_auth_key_length_esp(esp_transform);
+ _HIP_DEBUG("enckeylen=%d authkeylen=%d\n", esp_transf_length, auth_transf_length);
+ calc_index_new = entry->keymat_calc_index;
+ memcpy(Kn, entry->current_keymat_K, HIP_AH_SHA_LEN);
+ err = hip_update_get_sa_keys(entry, &keymat_index, &calc_index_new, Kn,
+ &espkey_gl, &authkey_gl, &espkey_lg, &authkey_lg);
+ if (err)
+ goto out_err;
+ /* todo: update entry keymat later */
+ hip_update_entry_keymat(entry, keymat_index, calc_index_new, Kn);
+
+ /* set up new outbound IPsec SA */
+ HIP_DEBUG("Setting new outbound SA, SPI=0x%x\n", new_spi_out);
+ err = hip_setup_sa(hitr, hits, &new_spi_out, esp_transform,
+ we_are_HITg ? &espkey_gl.key : &espkey_lg.key,
+ we_are_HITg ? &authkey_gl.key : &authkey_lg.key,
+ 0, HIP_SPI_DIRECTION_OUT);
+ if (err) {
+ HIP_ERROR("Setting up new outbound IPsec failed (%d)\n", err);
+ goto out_err;
+ }
+ HIP_DEBUG("Set up new outbound IPsec SA, SPI=0x%x\n", new_spi_out);
+
+ HIP_DEBUG("Setting new inbound SA, SPI=0x%x\n", new_spi_in);
+ err = hip_setup_sa(hits, hitr, &new_spi_in, entry->esp_transform,
+ we_are_HITg ? &espkey_lg : &espkey_gl,
+ we_are_HITg ? &authkey_lg : &authkey_gl,
+ 1, HIP_SPI_DIRECTION_IN);
+ if (err) {
+ HIP_ERROR("Error while setting up new IPsec SA (err=%d)\n", err);
+ goto out_err;
+ }
+ HIP_DEBUG("New inbound SA created with SPI=0x%x\n", new_spi_in);
+
+ if (prev_spi_in == new_spi_in) {
+ memset(&spi_in_data, 0, sizeof(struct hip_spi_in_item));
+ spi_in_data.spi = new_spi_in;
+ spi_in_data.ifindex = hip_hadb_get_spi_ifindex(entry, prev_spi_in);/* already set ? */
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_IN, &spi_in_data);
+ if (err)
+ goto out_err;
+ } else
+ _HIP_DEBUG("Old SPI <> New SPI, not adding a new inbound SA\n");
+
+ /* activate the new inbound and outbound SAs */
+ _HIP_DEBUG("finalizing the new inbound SA, SPI=0x%x\n", new_spi_in);
+ hip_finalize_sa(hitr, new_spi_in);
+ _HIP_DEBUG("finalizing the new outbound SA, SPI=0x%x\n", new_spi_out);
+ hip_finalize_sa(hits, new_spi_out);
+
+ HIP_DEBUG("switching inbound SPIs: 0x%x -> 0x%x\n", prev_spi_in, new_spi_in);
+ hip_update_switch_spi_in(entry, prev_spi_in);
+
+ hip_update_set_new_spi_out(entry, prev_spi_out, new_spi_out); /* temporary fix */
+ HIP_DEBUG("switching outbound SPIs: 0x%x -> 0x%x\n", prev_spi_out, new_spi_out);
+ hip_update_switch_spi_out(entry, prev_spi_out);
+
+ hip_set_spi_update_status(entry, new_spi_in, 0);
+ hip_update_clear_status(entry, new_spi_in);
+
+ // if (is not mm update) ?
+ hip_hadb_set_default_out_addr(entry, hip_hadb_get_spi_list(entry, new_spi_out), NULL);
+
+ /* 4. The system cancels any timers protecting the UPDATE and
+ transitions to ESTABLISHED. */
+ entry->state = HIP_STATE_ESTABLISHED;
+ HIP_DEBUG("Went back to ESTABLISHED state\n");
+
+ /* delete old SAs */
+ if (prev_spi_out != new_spi_out) {
+ HIP_DEBUG("REMOVING OLD OUTBOUND IPsec SA, SPI=0x%x\n", prev_spi_out);
+ err = hip_delete_sa(prev_spi_out, hits);
+ HIP_DEBUG("TODO: set new spi to 0\n");
+ _HIP_DEBUG("delete_sa out retval=%d\n", err);
+ err = 0;
+ } else
+ HIP_DEBUG("prev SPI_out = new SPI_out, not deleting the outbound SA\n");
+
+ if (prev_spi_in != new_spi_in) {
+ HIP_DEBUG("REMOVING OLD INBOUND IPsec SA, SPI=0x%x\n", prev_spi_in);
+ err = hip_delete_sa(prev_spi_in, hitr);
+ /* remove old HIT-SPI mapping and add a new mapping */
+
+ /* actually should change hip_hadb_delete_inbound_spi
+ * somehow, but we do this or else delete_inbound_spi
+ * would delete both old and new SPIs */
+ hip_hadb_remove_hs(prev_spi_in);
+ err = hip_hadb_insert_state_spi_list(entry, new_spi_in);
+ if (err == -EEXIST) {
+ HIP_DEBUG("HIT-SPI mapping already exists, hmm ..\n");
+ err = 0;
+ } else if (err) {
+ HIP_ERROR("Could not add a HIT-SPI mapping for SPI 0x%x (err=%d)\n",
+ new_spi_in, err);
+ }
+ } else
+ _HIP_DEBUG("prev SPI_in = new SPI_in, not deleting the inbound SA\n");
+
+ /* start verifying addresses */
+ _HIP_DEBUG("start verifing addresses for new spi 0x%x\n", new_spi_out);
+ err = hip_update_send_addr_verify(entry, msg, NULL /* ok ? */, new_spi_out);
+
+ /* hip_update_spi_waitlist_add(new_spi_in, hits, NULL); */
+
+ out_err:
+ return err;
+}
+
+/**
+ * hip_handle_update_rekeying - handle incoming UPDATE packet received in REKEYING state
+ *
+ * @msg: the HIP packet
+ * @src_ip: source IPv6 address from where the UPDATE was sent
+ *
+ * This function handles case 8 in section 8.11 Processing UPDATE
+ * packets of the base draft.
+ *
+ * Returns: 0 if successful, otherwise < 0.
+ */
+int hip_handle_update_rekeying(hip_ha_t *entry, struct hip_common *msg, struct in6_addr *src_ip)
+{
+ int err = 0;
+ struct in6_addr *hits = &msg->hits, *hitr = &msg->hitr;
+ struct hip_common *update_packet = NULL;
+ struct hip_nes *nes = NULL;
+ struct hip_seq *seq = NULL;
+ struct hip_ack *ack = NULL;
+ struct in6_addr daddr;
+ struct hip_host_id *host_id_private;
+ u8 signature[HIP_RSA_SIGNATURE_LEN]; /* RSA sig > DSA sig */
+ uint16_t mask;
+ /* assume already locked entry */
+
+ /* 8.11.2 Processing an UPDATE packet in state REKEYING */
+
+ HIP_DEBUG("\n");
+
+ seq = hip_get_param(msg, HIP_PARAM_SEQ);
+ nes = hip_get_param(msg, HIP_PARAM_NES);
+ ack = hip_get_param(msg, HIP_PARAM_ACK);
+
+ if (seq && nes) {
+ /* 1. If the packet contains a SEQ and NES parameters, then the system
+ generates a new UPDATE packet with an ACK of the peer's Update ID
+ as received in the SEQ parameter. .. */
+ update_packet = hip_msg_alloc();
+ if (!update_packet) {
+ HIP_DEBUG("update_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask, hitr, hits);
+
+ err = hip_build_param_ack(update_packet, ntohl(seq->update_id));
+ if (err) {
+ HIP_ERROR("Building of ACK param failed\n");
+ goto out_err;
+ }
+ }
+
+
+ if (nes && ack) { /* kludge */
+ uint32_t s = hip_update_get_prev_spi_in(entry, ntohl(ack->peer_update_id));
+ _HIP_DEBUG("s=0x%x\n", s);
+ hip_update_set_status(entry, s, 0x4, 0, 0, nes, 0);
+ }
+ /* .. Additionally, if the UPDATE packet contained an ACK of the
+ outstanding Update ID, or if the ACK of the UPDATE packet that
+ contained the NES has already been received, the system stores
+ the received NES and (optional) DIFFIE_HELLMAN parameters and
+ finishes the rekeying procedure as described in Section
+ 8.11.3. If the ACK of the outstanding Update ID has not been
+ received, stay in state REKEYING after storing the recived NES
+ and (optional) DIFFIE_HELLMAN. */
+
+ if (ack) /* breaks if packet has no ack but nes exists ? */
+ hip_update_handle_ack(entry, ack, nes ? 1 : 0, NULL);
+// if (nes)
+// hip_update_handle_nes(entry, puid); /* kludge */
+
+ /* finish SAs if we have received ACK and NES */
+ {
+ struct hip_spi_in_item *item, *tmp;
+
+ list_for_each_entry_safe(item, tmp, &entry->spis_in, list) {
+ _HIP_DEBUG("test item: spi_in=0x%x seq=%u updflags=0x%x\n",
+ item->spi, item->seq_update_id, item->update_state_flags);
+ if (item->update_state_flags == 0x3) {
+ err = hip_update_finish_rekeying(msg, entry, &item->stored_received_nes);
+ _HIP_DEBUG("update_finish handling ret err=%d\n", err);
+ }
+ }
+ err = 0;
+ }
+
+ if (!update_packet) {
+ HIP_DEBUG("not sending ACK\n");
+ goto out;
+ }
+
+ /* send ACK */
+
+ /* TODO: hmac/signature to common functions */
+ /* Add HMAC */
+ err = hip_build_param_hmac_contents(update_packet, &entry->hip_hmac_out);
+ if (err) {
+ HIP_ERROR("Building of HMAC failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* Add SIGNATURE */
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not get our host identity. Can not sign data\n");
+ goto out_err;
+ }
+
+ if (!hip_create_signature(update_packet, hip_get_msg_total_len(update_packet),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not sign UPDATE. Failing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of SIGNATURE failed (%d)\n", err);
+ goto out_err;
+ }
+ _HIP_DEBUG("SIGNATURE added\n");
+
+
+ err = hip_hadb_get_peer_addr(entry, &daddr);
+ if (err) {
+ HIP_DEBUG("hip_sdb_get_peer_address err = %d\n", err);
+ goto out_err;
+ }
+
+ HIP_DEBUG("Sending reply UPDATE packet (only ACK)\n");
+ err = hip_csum_send(NULL, &daddr, update_packet);
+ if (err) {
+ HIP_DEBUG("hip_csum_send err=%d\n", err);
+ HIP_DEBUG("NOT ignored, or should we..\n");
+ /* fallback to established ? */
+ /* goto out_err; ? */
+ }
+
+ out:
+ out_err:
+ /* if (err)
+ TODO: REMOVE IPSEC SAs
+ move to state = ?
+ */
+ if (update_packet)
+ kfree(update_packet);
+
+ return err;
+}
+
+/* src_ip = our addr to use when sending update */
+int hip_update_send_addr_verify(hip_ha_t *entry, struct hip_common *msg,
+ struct in6_addr *src_ip, uint32_t spi)
+{
+ int err = 0;
+ struct in6_addr *hits = &msg->hits, *hitr = &msg->hitr;
+ struct hip_spi_out_item *spi_out;
+ struct hip_peer_addr_list_item *addr, *tmp;
+ struct hip_common *update_packet = NULL;
+ uint16_t mask;
+
+ /* assume already locked entry */
+
+ HIP_DEBUG("SPI=0x%x\n", spi);
+
+ spi_out = hip_hadb_get_spi_list(entry, spi);
+ if (!spi_out) {
+ HIP_DEBUG("bug: outbound SPI 0x%x does not exist\n", spi);
+ goto out_err;
+ }
+
+ /* start checking the addresses */
+
+ update_packet = hip_msg_alloc();
+ if (!update_packet) {
+ HIP_ERROR("update_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask, hitr, hits);
+
+ list_for_each_entry_safe(addr, tmp, &spi_out->peer_addr_list, list) {
+ u8 signature[HIP_RSA_SIGNATURE_LEN]; /* RSA > DSA */
+ struct hip_host_id *host_id_private;
+
+ hip_print_hit("new addr to check", &addr->address);
+
+ if (addr->address_state == PEER_ADDR_STATE_DEPRECATED) {
+ _HIP_DEBUG("addr state is DEPRECATED, not verifying\n");
+ continue;
+ }
+
+ if (addr->address_state == PEER_ADDR_STATE_ACTIVE) {
+ _HIP_DEBUG("not verifying already active address\n");
+ if (addr->is_preferred) {
+ HIP_DEBUG("TEST (maybe should not do this yet?): setting already active address and set as preferred to default addr\n");
+ hip_hadb_set_default_out_addr(entry, spi_out, &addr->address);
+ }
+ continue;
+ }
+
+ hip_msg_init(update_packet);
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask, hitr, hits);
+
+ err = hip_build_param_spi(update_packet, 0x11223344); /* test */
+ if (err) {
+ HIP_ERROR("Building of SPI failed\n");
+ goto out_err;
+ }
+
+ entry->update_id_out++;
+ addr->seq_update_id = entry->update_id_out;
+ _HIP_DEBUG("outgoing UPDATE ID for REA addr check=%u\n", addr->seq_update_id);
+ /* todo: handle overflow if (!update_id_out) */
+ err = hip_build_param_seq(update_packet, addr->seq_update_id);
+ if (err) {
+ HIP_ERROR("Building of SEQ failed\n");
+ continue;
+ }
+
+ /* Add HMAC */
+ err = hip_build_param_hmac_contents(update_packet, &entry->hip_hmac_out);
+ if (err) {
+ HIP_ERROR("Building of HMAC failed (%d)\n", err);
+ continue;
+ }
+
+ /* Add SIGNATURE */
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not get own host identity. Can not sign data\n");
+ continue;
+ }
+
+ if (!hip_create_signature(update_packet, hip_get_msg_total_len(update_packet),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not sign UPDATE. Failing\n");
+ continue;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of SIGNATURE failed (%d)\n", err);
+ continue;
+ }
+
+ get_random_bytes(addr->echo_data, sizeof(addr->echo_data));
+ _HIP_HEXDUMP("ECHO_REQUEST in REA addr check",
+ addr->echo_data, sizeof(addr->echo_data));
+ err = hip_build_param_echo(update_packet, addr->echo_data ,
+ sizeof(addr->echo_data), 0, 1);
+ if (err) {
+ HIP_ERROR("Building of ECHO_REQUEST failed\n");
+ continue;
+ }
+
+
+ HIP_DEBUG("Sending reply UPDATE packet (for REA)\n");
+ /* test: send all addr check from same address */
+ err = hip_csum_send(src_ip, &addr->address, update_packet);
+ if (err) {
+ HIP_DEBUG("hip_csum_send err=%d\n", err);
+ HIP_DEBUG("NOT ignored, or should we..\n");
+ }
+ }
+
+ out_err:
+ if (update_packet)
+ kfree(update_packet);
+
+ _HIP_DEBUG("done, err=%d\n", err);
+ return err;
+}
+
+
+/* handle UPDATE(REA, SEQ) */
+/* reply with ACK and UPDATE(SPI, SEQ, ACK, ECHO_REQUEST) for each addr in REA */
+int hip_handle_update_plain_rea(hip_ha_t *entry, struct hip_common *msg,
+ struct in6_addr *src_ip, struct in6_addr *dst_ip)
+{
+ int err = 0;
+ struct in6_addr *hits = &msg->hits, *hitr = &msg->hitr;
+ struct hip_common *update_packet = NULL;
+ struct hip_seq *seq;
+ struct hip_rea *rea;
+ uint16_t mask;
+
+ /* assume already locked entry */
+
+ HIP_DEBUG("\n");
+
+ update_packet = hip_msg_alloc();
+ if (!update_packet) {
+ HIP_ERROR("update_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err_nolock;
+ }
+
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask, hitr, hits);
+
+ /* ACK the received UPDATE SEQ */
+ seq = hip_get_param(msg, HIP_PARAM_SEQ);
+ err = hip_build_param_ack(update_packet, ntohl(seq->update_id));
+ if (err) {
+ HIP_ERROR("Building of ACK failed\n");
+ goto out_err_nolock;
+ }
+
+ HIP_DEBUG("Sending reply UPDATE packet (for REA)\n");
+ err = hip_csum_send(dst_ip, src_ip, update_packet);
+ if (err) {
+ HIP_DEBUG("hip_csum_send err=%d\n", err);
+ HIP_DEBUG("NOT ignored, or should we..\n");
+ }
+
+
+ rea = hip_get_param(msg, HIP_PARAM_REA);
+ hip_update_handle_rea_parameter(entry, rea);
+ err = hip_update_send_addr_verify(entry, msg, dst_ip, ntohl(rea->spi));
+
+ out_err_nolock:
+ if (update_packet)
+ kfree(update_packet);
+ return err;
+}
+
+
+/* handle UPDATE(SPI, SEQ, ACK, ECHO_REQUEST) */
+/* or ? */
+/* handle UPDATE(SPI, SEQ, ECHO_REQUEST) */
+int hip_handle_update_addr_verify(hip_ha_t *entry, struct hip_common *msg,
+ struct in6_addr *src_ip, struct in6_addr *dst_ip)
+{
+ int err = 0;
+ struct in6_addr *hits = &msg->hits, *hitr = &msg->hitr;
+ struct hip_common *update_packet = NULL;
+ struct hip_seq *seq = NULL;
+ struct hip_echo_request *echo = NULL;
+ u8 signature[HIP_RSA_SIGNATURE_LEN]; /* RSA > DSA */
+ struct hip_host_id *host_id_private;
+ uint16_t mask;
+
+ /* assume already locked entry */
+
+ HIP_DEBUG("\n");
+
+ echo = hip_get_param(msg, HIP_PARAM_ECHO_REQUEST);
+ if (!echo) {
+ HIP_ERROR("ECHO not found\n");
+ goto out_err;
+ }
+
+ seq = hip_get_param(msg, HIP_PARAM_SEQ);
+ if (!seq) {
+ HIP_ERROR("SEQ not found\n");
+ goto out_err;
+ }
+
+ update_packet = hip_msg_alloc();
+ if (!update_packet) {
+ HIP_ERROR("update_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask, hitr, hits);
+
+ /* reply with UPDATE(ACK, ECHO_RESPONSE) */
+ err = hip_build_param_ack(update_packet, ntohl(seq->update_id));
+ if (err) {
+ HIP_ERROR("Building of ACK failed\n");
+ goto out_err;
+ }
+
+ /* Add HMAC */
+ err = hip_build_param_hmac_contents(update_packet, &entry->hip_hmac_out);
+ if (err) {
+ HIP_ERROR("Building of HMAC failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* Add SIGNATURE */
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not get own host identity. Can not sign data\n");
+ goto out_err;
+ }
+
+ if (!hip_create_signature(update_packet,
+ hip_get_msg_total_len(update_packet),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not sign UPDATE. Failing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of SIGNATURE failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* ECHO_RESPONSE (no sign) */
+ HIP_DEBUG("echo opaque data len=%d\n",
+ hip_get_param_contents_len(echo));
+ err = hip_build_param_echo(update_packet,
+ (void *)echo+sizeof(struct hip_tlv_common),
+ hip_get_param_contents_len(echo), 0, 0);
+ if (err) {
+ HIP_ERROR("Building of ECHO_RESPONSE failed\n");
+ goto out_err;
+ }
+
+ HIP_DEBUG("Sending reply UPDATE packet (address check)\n");
+ err = hip_csum_send(dst_ip, src_ip, update_packet);
+ if (err) {
+ HIP_DEBUG("hip_csum_send err=%d\n", err);
+ HIP_DEBUG("NOT ignored, or should we..\n");
+ }
+
+ out_err:
+ if (update_packet)
+ kfree(update_packet);
+ return err;
+}
+
+
+/**
+ * hip_receive_update - receive UPDATE packet
+ * @skb: sk_buff where the HIP packet is in
+ * @hip_common: pointer to HIP header
+ *
+ * This is the initial function which is called when an UPDATE packet
+ * is received. The validity of the packet is checked and then this
+ * function acts according to whether this packet is a reply or not.
+ *
+ * Returns: 0 if successful (HMAC and signature (if needed) are
+ * validated, and the rest of the packet is handled if current state
+ * allows it), otherwise < 0.
+ */
+int hip_receive_update(struct sk_buff *skb)
+{
+ int err = 0;
+ struct hip_common *msg;
+ struct in6_addr *hits;
+ struct hip_nes *nes = NULL;
+ struct hip_seq *seq = NULL;
+ struct hip_ack *ack = NULL;
+ struct hip_rea *rea;
+ struct hip_echo_request *echo = NULL;
+ struct hip_echo_response *echo_response = NULL;
+ struct hip_hmac *hmac = NULL;
+ struct hip_signature *signature = NULL;
+ int state = 0;
+ uint32_t pkt_update_id = 0; /* UPDATE ID in packet */
+ uint32_t update_id_in = 0; /* stored incoming UPDATE ID */
+ int is_retransmission = 0;
+ uint16_t keymat_index = 0;
+ struct hip_dh_fixed *dh;
+ struct in6_addr *src_ip, *dst_ip;
+ struct hip_lhi peer_lhi;
+ struct hip_host_id *peer_id;
+ int handle_upd = 0;
+ hip_ha_t *entry = NULL;
+
+ HIP_DEBUG("\n");
+ msg = (struct hip_common *) skb->h.raw;
+ _HIP_HEXDUMP("msg", msg, hip_get_msg_total_len(msg));
+
+ src_ip = &(skb->nh.ipv6h->saddr);
+ dst_ip = &(skb->nh.ipv6h->daddr);
+ hits = &msg->hits;
+
+ entry = hip_hadb_find_byhit(hits);
+ if (!entry) {
+ HIP_ERROR("Entry not found\n");
+ goto out_err;
+ }
+ HIP_LOCK_HA(entry);
+
+ state = entry->state; /* todo: remove variable state */
+
+ HIP_DEBUG("Received UPDATE in state %s\n", hip_state_str(state));
+
+ /* in state R2-SENT: Receive UPDATE, go to ESTABLISHED and
+ * process from ESTABLISHED state */
+ if (state == HIP_STATE_R2_SENT) {
+ state = entry->state = HIP_STATE_ESTABLISHED;
+ HIP_DEBUG("Moved from R2-SENT to ESTABLISHED\n");
+ }
+
+ if (! (state == HIP_STATE_ESTABLISHED ||
+ state == HIP_STATE_REKEYING) ) {
+ HIP_DEBUG("Received UPDATE in illegal state %s. Dropping\n",
+ hip_state_str(state));
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ nes = hip_get_param(msg, HIP_PARAM_NES);
+ seq = hip_get_param(msg, HIP_PARAM_SEQ);
+ ack = hip_get_param(msg, HIP_PARAM_ACK);
+ rea = hip_get_param(msg, HIP_PARAM_REA);
+ echo = hip_get_param(msg, HIP_PARAM_ECHO_REQUEST);
+ echo_response = hip_get_param(msg, HIP_PARAM_ECHO_RESPONSE);
+
+ if (nes) {
+ HIP_DEBUG("UPDATE contains (at least one) NES parameter\n");
+ keymat_index = ntohs(nes->keymat_index);
+ HIP_DEBUG("NES: Keymaterial Index: %u\n", keymat_index);
+ HIP_DEBUG("NES: Old SPI: 0x%x\n", ntohl(nes->old_spi));
+ HIP_DEBUG("NES: New SPI: 0x%x\n", ntohl(nes->new_spi));
+ }
+ if (seq) {
+ pkt_update_id = ntohl(seq->update_id);
+ HIP_DEBUG("SEQ: UPDATE ID: %u\n", pkt_update_id);
+ }
+ if (ack)
+ HIP_DEBUG("ACK found\n");
+ if (rea)
+ HIP_DEBUG("REA: SPI 0x%x\n", ntohl(rea->spi));
+ if (echo)
+ HIP_DEBUG("ECHO_REQUEST found\n");
+ if (echo_response)
+ HIP_DEBUG("ECHO_RESPONSE found\n");
+
+ /* 8.11 Processing UPDATE packets checks */
+ if (seq && nes) {
+ HIP_DEBUG("UPDATE has both SEQ and NES, peer host is rekeying, MUST process this UPDATE\n");
+ handle_upd = 1;
+ }
+
+ if (!handle_upd && state == HIP_STATE_REKEYING && ack && !echo) {
+ HIP_DEBUG("in REKEYING state and ACK and not ECHO_REQUEST, MUST process this UPDATE\n");
+ handle_upd = 1;
+ }
+
+ /* mm-02 UPDATE tests */
+ if (!handle_upd && rea && seq && !nes) {
+ HIP_DEBUG("have REA and SEQ but no NES, process this UPDATE\n");
+ handle_upd = 2;
+ }
+
+ //if (!handle_upd && /* SPI && */ seq && ack && !nes && echo) {
+ if (!handle_upd && /* SPI && */ seq && !nes && echo) {
+ /* ACK might have been in a separate packet */
+ HIP_DEBUG("have SEQ,ECHO_REQUEST but no NES, process this UPDATE\n");
+ handle_upd = 3;
+ }
+ if (!handle_upd && ack && echo) {
+ HIP_DEBUG("have ACK and ECHO_REQUEST, process this UPDATE\n");
+ handle_upd = 4;
+ }
+
+ if (!handle_upd && ack) {
+ HIP_DEBUG("have only ACK, process this UPDATE\n");
+ handle_upd = 5;
+ }
+
+ if (!handle_upd) {
+ HIP_ERROR("NOT processing UPDATE packet\n");
+ goto out_err;
+ }
+
+ update_id_in = entry->update_id_in;
+ _HIP_DEBUG("previous incoming update id=%u\n", update_id_in);
+ if (seq) {
+ /* 1. If the SEQ parameter is present, and the Update ID in the
+ received SEQ is smaller than the stored Update ID for the host,
+ the packet MUST BE dropped. */
+ if (pkt_update_id < update_id_in) {
+ HIP_DEBUG("SEQ param present and received UPDATE ID (%u) < stored incoming UPDATE ID (%u). Dropping\n",
+ pkt_update_id, update_id_in);
+ err = -EINVAL;
+ goto out_err;
+ } else if (pkt_update_id == update_id_in) {
+ /* 2. If the SEQ parameter is present, and the Update ID in the
+ received SEQ is equal to the stored Update ID for the host, the
+ packet is treated as a retransmission. */
+ is_retransmission = 1;
+ HIP_DEBUG("Retransmitted UPDATE packet (?), continuing\n");
+ /* todo: ignore this packet or process anyway ? */
+ }
+ }
+
+ HIP_DEBUG("handle_upd=%d\n", handle_upd);
+ if (handle_upd > 1) {
+ _HIP_DEBUG("MM-02 UPDATE\n");
+ }
+
+ hmac = hip_get_param(msg, HIP_PARAM_HMAC);
+ if (hmac) {
+ /* 3. The system MUST verify the HMAC in the UPDATE packet.
+ If the verification fails, the packet MUST be dropped. */
+ err = hip_verify_packet_hmac(msg, &entry->hip_hmac_in);
+ if (err) {
+ HIP_ERROR("HMAC validation on UPDATE failed\n");
+ goto out_err;
+ }
+ _HIP_DEBUG("UPDATE HMAC ok\n");
+ } else {
+ HIP_DEBUG("HMAC not found, error ?\n");
+ }
+
+ /* 4. If the received UPDATE contains a Diffie-Hellman
+ parameter, the received Keymat Index MUST be zero. If this
+ test fails, the packet SHOULD be dropped and the system
+ SHOULD log an error message. */
+ dh = hip_get_param(msg, HIP_PARAM_DIFFIE_HELLMAN);
+ if (dh) {
+ HIP_DEBUG("packet contains DH\n");
+ if (!nes) {
+ HIP_ERROR("packet contains DH but not NES\n");
+ goto out_err;
+ }
+ if (keymat_index != 0) {
+ HIP_ERROR("UPDATE contains Diffie-Hellman parameter with non-zero"
+ "keymat value %u in NES. Dropping\n",
+ keymat_index);
+ err = -EINVAL;
+ goto out_err;
+ }
+ }
+
+ /* 5. The system MAY verify the SIGNATURE in the UPDATE
+ packet. If the verification fails, the packet SHOULD be
+ dropped and an error message logged. */
+
+ signature = hip_get_param(msg, HIP_PARAM_HIP_SIGNATURE);
+ if (signature) {
+ peer_lhi.anonymous = 0;
+ memcpy(&peer_lhi.hit, &msg->hits, sizeof(struct in6_addr));
+ peer_id = hip_get_host_id(HIP_DB_PEER_HID, &peer_lhi);
+ if (!peer_id) {
+ HIP_ERROR("Unknown peer (no identity found)\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+ err = hip_verify_packet_signature(msg, peer_id);
+ if (err) {
+ HIP_ERROR("Verification of UPDATE signature failed\n");
+ _HIP_DEBUG("ignoring SIGNATURE fail\n");
+ goto out_err;
+ }
+ _HIP_DEBUG("SIGNATURE ok\n");
+ } else
+ HIP_DEBUG("SIGNATURE not found, error ?\n");
+
+ /* 6. If a new SEQ parameter is being processed, the system MUST record
+ the Update ID in the received SEQ parameter, for replay
+ protection. */
+ if (seq && !is_retransmission) {
+ entry->update_id_in = pkt_update_id;
+ _HIP_DEBUG("Stored peer's incoming UPDATE ID %u\n", pkt_update_id);
+ }
+
+ /* check that Old SPI value exists */
+ if (nes &&
+ (nes->old_spi != nes->new_spi) && /* mm check */
+ !hip_update_exists_spi(entry, ntohl(nes->old_spi), HIP_SPI_DIRECTION_OUT, 0)) {
+ HIP_ERROR("Old SPI value 0x%x in NES parameter does not belong to the current list of outbound SPIs in HA\n",
+ ntohl(nes->old_spi));
+ goto out_err;
+ }
+
+ if (handle_upd == 2) {
+ /* REA, SEQ */
+ err = hip_handle_update_plain_rea(entry, msg, src_ip, dst_ip);
+ } else if (handle_upd == 3) {
+ /* SPI, SEQ, ACK, ECHO_REQUEST */
+ err = hip_handle_update_addr_verify(entry, msg, src_ip, dst_ip);
+ } else if (handle_upd == 5) {
+ /* ACK, ECHO_RESPONSE */
+ hip_update_handle_ack(entry, ack, 0, echo_response);
+ } else {
+ /* base draft cases 7-8: */
+ if (state == HIP_STATE_ESTABLISHED) {
+ if (nes && seq) {
+ HIP_DEBUG("case 7: in ESTABLISHED and has NES and SEQ\n");
+ err = hip_handle_update_established(entry, msg, src_ip, dst_ip);
+ } else {
+ HIP_ERROR("in ESTABLISHED but no both NES and SEQ\n");
+ err = -EINVAL;
+ }
+ } else {
+ HIP_DEBUG("case 8: in REKEYING\n");
+ err = hip_handle_update_rekeying(entry, msg, src_ip);
+ }
+ }
+
+ //hip_hadb_dump_spis_in(entry);
+ //hip_hadb_dump_spis_out(entry);
+ //hip_hadb_dump_hs_ht();
+
+ out_err:
+ if (err)
+ HIP_ERROR("UPDATE handler failed, err=%d\n", err);
+
+ if (entry) {
+ HIP_UNLOCK_HA(entry);
+ hip_put_ha(entry);
+ }
+ return err;
+}
+
+
+#define SEND_UPDATE_NES (1 << 0)
+#define SEND_UPDATE_REA (1 << 1)
+
+/** hip_send_update - send initial UPDATE packet to the peer
+ * @addr_list: if non-NULL, REA parameter is added to the UPDATE
+ * @addr_count: number of addresses in @addr_list
+ * @ifindex: if non-zero, the ifindex value of the interface which caused the event
+ * @flags: TODO comment
+ *
+ * Returns: 0 if UPDATE was sent, otherwise < 0.
+ */
+int hip_send_update(struct hip_hadb_state *entry, struct hip_rea_info_addr_item *addr_list,
+ int addr_count, int ifindex, int flags)
+{
+ int err = 0;
+ uint32_t update_id_out = 0;
+ uint32_t mapped_spi = 0; /* SPI of the SA mapped to the ifindex */
+ uint32_t new_spi_in = 0;
+ struct hip_common *update_packet = NULL;
+ struct in6_addr daddr;
+ struct hip_host_id *host_id_private;
+ u8 signature[HIP_RSA_SIGNATURE_LEN]; /* RSA > DSA */
+ int make_new_sa = 0;
+ int add_nes = 0, add_rea;
+ uint32_t nes_old_spi = 0, nes_new_spi = 0;
+ uint16_t mask;
+
+ add_rea = flags & SEND_UPDATE_REA;
+ HIP_DEBUG("addr_list=0x%p addr_count=%d ifindex=%d flags=0x%x\n",
+ addr_list, addr_count, ifindex, flags);
+ if (!ifindex)
+ _HIP_DEBUG("base draft UPDATE\n");
+
+ if (add_rea)
+ _HIP_DEBUG("mm UPDATE, %d addresses in REA\n", addr_count);
+ else
+ _HIP_DEBUG("Plain UPDATE\n");
+
+ /* start building UPDATE packet */
+ update_packet = hip_msg_alloc();
+ if (!update_packet) {
+ HIP_ERROR("update_packet alloc failed\n");
+ err = -ENOMEM;
+ goto out_err;
+ }
+
+ HIP_LOCK_HA(entry);
+
+ hip_print_hit("sending UPDATE to", &entry->hit_peer);
+ mask = hip_create_control_flags(0, 0, HIP_CONTROL_SHT_TYPE1,
+ HIP_CONTROL_DHT_TYPE1);
+ hip_build_network_hdr(update_packet, HIP_UPDATE, mask,
+ &entry->hit_our, &entry->hit_peer);
+ if (add_rea) {
+ /* mm stuff, per-ifindex SA */
+ /* reuse old SA if we have one, else create a new SA */
+ mapped_spi = hip_hadb_get_spi(entry, ifindex);
+ HIP_DEBUG("mapped_spi=0x%x\n", mapped_spi);
+ if (mapped_spi) {
+ /* NES not needed */
+ add_nes = 0;
+ make_new_sa = 0;
+ _HIP_DEBUG("5.1 Mobility with single SA pair, readdress with no rekeying\n");
+ HIP_DEBUG("Reusing old SA\n");
+ /* 5.1 Mobility with single SA pair */
+ } else {
+ _HIP_DEBUG("5.2 Host multihoming\n");
+ make_new_sa = 1;
+ _HIP_DEBUG("TODO\n");
+ }
+ } else {
+ /* base draft UPDATE, create a new SA anyway */
+ _HIP_DEBUG("base draft UPDATE, create a new SA\n");
+ make_new_sa = 1;
+ }
+
+ /* If this is mm-UPDATE (ifindex should be then != 0) avoid
+ * sending empty REAs to the peer if we have not sent previous
+ * information on this ifindex/SPI yet */
+ if (ifindex != 0 && mapped_spi == 0 && addr_count == 0) {
+ HIP_DEBUG("NETDEV_DOWN and ifindex not advertised yet, returning\n");
+ goto out;
+ }
+
+ if (make_new_sa) {
+ HIP_DEBUG("make_new_sa=1 -> add_nes=1\n");
+ add_nes = 1;
+ }
+
+ HIP_DEBUG("add_nes=%d make_new_sa=%d\n", add_nes, make_new_sa);
+
+ if (make_new_sa) {
+ new_spi_in = hip_acquire_spi(&entry->hit_peer, &entry->hit_our);
+ if (!new_spi_in) {
+ HIP_ERROR("Error while acquiring a SPI\n");
+ goto out_err;
+ }
+ HIP_DEBUG("Got SPI value for the SA 0x%x\n", new_spi_in);
+
+ /* TODO: move to rekeying_finish */
+ if (!mapped_spi) {
+ struct hip_spi_in_item spi_in_data;
+
+ _HIP_DEBUG("previously unknown ifindex, creating a new item to inbound spis_in\n");
+ memset(&spi_in_data, 0, sizeof(struct hip_spi_in_item));
+ spi_in_data.spi = new_spi_in;
+ spi_in_data.ifindex = ifindex;
+ spi_in_data.updating = 1;
+ err = hip_hadb_add_spi(entry, HIP_SPI_DIRECTION_IN, &spi_in_data);
+ if (err) {
+ HIP_ERROR("add_spi failed\n");
+ goto out_err;
+ }
+ }
+ else {
+ _HIP_DEBUG("is previously mapped ifindex\n");
+ }
+ } else
+ _HIP_DEBUG("not creating a new SA\n");
+
+ _HIP_DEBUG("entry->current_keymat_index=%u\n", entry->current_keymat_index);
+
+ if (add_rea) {
+ /* REA is the first parameter of the UPDATE */
+ if (mapped_spi)
+ err = hip_build_param_rea(update_packet, mapped_spi,
+ addr_list, addr_count);
+ else
+ err = hip_build_param_rea(update_packet, new_spi_in,
+ addr_list, addr_count);
+ if (err) {
+ HIP_ERROR("Building of REA param failed\n");
+ goto out_err;
+ }
+ } else
+ HIP_DEBUG("not adding REA\n");
+
+ if (add_nes) {
+ if (addr_list) {
+ if (make_new_sa) {
+ /* mm02 5.2 Host multihoming */
+ HIP_DEBUG("mm-02, adding NES, Old SPI == New SPI\n");
+ /* notify the peer about new interface */
+ nes_old_spi = new_spi_in;
+ nes_new_spi = new_spi_in;
+
+ } else {
+ HIP_DEBUG("mm-02, !makenewsa\n");
+ nes_old_spi = mapped_spi;
+ nes_new_spi = new_spi_in;
+ }
+ } else {
+ HIP_DEBUG("adding NES, Old SPI <> New SPI\n");
+ /* plain UPDATE or readdress with rekeying */
+ /* update the SA of the interface which caused the event */
+ nes_old_spi = hip_hadb_get_spi(entry, ifindex);
+ if (!nes_old_spi) {
+ HIP_ERROR("Could not find SPI to use in Old SPI\n");
+ goto out_err;
+ }
+ hip_set_spi_update_status(entry, nes_old_spi, 1); /* here or later ? */
+ nes_new_spi = new_spi_in;
+ }
+
+ HIP_DEBUG("nes_old_spi=0x%x nes_new_spi=0x%x\n", nes_old_spi, nes_new_spi);
+ err = hip_build_param_nes(update_packet, entry->current_keymat_index,
+ nes_old_spi, nes_new_spi);
+ if (err) {
+ HIP_ERROR("Building of NES param failed\n");
+ goto out_err;
+ }
+ } else {
+ HIP_DEBUG("not adding NES\n");
+ nes_old_spi = nes_new_spi = mapped_spi;
+ }
+
+ hip_update_set_new_spi_in(entry, nes_old_spi, nes_new_spi, 0);
+
+ entry->update_id_out++;
+ update_id_out = entry->update_id_out;
+ _HIP_DEBUG("outgoing UPDATE ID=%u\n", update_id_out);
+ if (!update_id_out) { /* todo: handle this case */
+ HIP_ERROR("outgoing UPDATE ID overflowed back to 0, bug ?\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ err = hip_build_param_seq(update_packet, update_id_out);
+ if (err) {
+ HIP_ERROR("Building of SEQ param failed\n");
+ goto out_err;
+ }
+
+ if (add_nes) {
+ /* remember the update id of this update */
+ hip_update_set_status(entry, nes_old_spi,
+ 0x1 | 0x2 | 0x8, update_id_out, 0, NULL,
+ entry->current_keymat_index);
+ }
+
+ /* Add HMAC */
+ err = hip_build_param_hmac_contents(update_packet, &entry->hip_hmac_out);
+ if (err) {
+ HIP_ERROR("Building of HMAC failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* Add SIGNATURE */
+ host_id_private = hip_get_any_localhost_host_id(HIP_HI_DEFAULT_ALGO);
+ if (!host_id_private) {
+ HIP_ERROR("Could not get own host identity. Can not sign data\n");
+ goto out_err;
+ }
+
+ if (!hip_create_signature(update_packet, hip_get_msg_total_len(update_packet),
+ host_id_private, signature)) {
+ HIP_ERROR("Could not sign UPDATE. Failing\n");
+ err = -EINVAL;
+ goto out_err;
+ }
+
+ if (HIP_HI_DEFAULT_ALGO == HIP_HI_RSA) {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_RSA_SIGNATURE_LEN,
+ HIP_SIG_RSA);
+ } else {
+ err = hip_build_param_signature_contents(update_packet,
+ signature,
+ HIP_DSA_SIGNATURE_LEN,
+ HIP_SIG_DSA);
+ }
+
+ if (err) {
+ HIP_ERROR("Building of SIGNATURE failed (%d)\n", err);
+ goto out_err;
+ }
+
+ /* send UPDATE */
+ err = hip_hadb_get_peer_addr(entry, &daddr);
+ if (err) {
+ HIP_DEBUG("hip_sdb_get_peer_addr err=%d\n", err);
+ goto out_err;
+ }
+
+#if 0
+ /* Store the last UPDATE ID value sent from us */
+ entry->update_id_out = update_id_out;
+ _HIP_DEBUG("Stored peer's outgoing UPDATE ID %u\n", update_id_out);
+#endif
+
+ hip_set_spi_update_status(entry, nes_old_spi, 1);
+
+ /* if UPDATE contains only REA, then do not move state ? */
+ if (add_nes) {
+ entry->state = HIP_STATE_REKEYING;
+ HIP_DEBUG("moved to state REKEYING\n");
+ } else
+ HIP_DEBUG("experimental: staying in ESTABLISHED (NES not added)\n");
+
+
+ HIP_DEBUG("Sending initial UPDATE packet\n");
+ err = hip_csum_send(NULL, &daddr, update_packet);
+ if (err) {
+ HIP_DEBUG("hip_csum_send err=%d\n", err);
+ _HIP_DEBUG("NOT ignored, or should we..\n");
+
+ entry->state = HIP_STATE_ESTABLISHED;
+ HIP_DEBUG("fallbacked to state ESTABLISHED due to error (ok ?)\n");
+ goto out_err;
+ }
+
+ //hip_hadb_dump_spis_in(entry);
+ //hip_hadb_dump_spis_out(entry);
+
+ /* todo: 5. The system SHOULD start a timer whose timeout value should be ..*/
+ goto out;
+
+ out_err:
+ entry->state = HIP_STATE_ESTABLISHED;
+ HIP_DEBUG("fallbacked to state ESTABLISHED (ok ?)\n");
+ hip_set_spi_update_status(entry, nes_old_spi, 0);
+ /* delete IPsec SA on failure */
+ HIP_ERROR("TODO: delete SA\n");
+ out:
+ HIP_UNLOCK_HA(entry);
+ if (update_packet)
+ kfree(update_packet);
+ return err;
+}
+
+/* really ugly hack ripped from rea.c, must convert to list_head asap */
+struct hip_update_kludge {
+ hip_ha_t **array;
+ int count;
+ int length;
+};
+
+/* from rea.c */
+static int hip_update_get_all_valid(hip_ha_t *entry, void *op)
+{
+ struct hip_update_kludge *rk = op;
+
+ if (rk->count >= rk->length)
+ return -1;
+
+ if (entry->hastate == HIP_HASTATE_HITOK && entry->state == HIP_STATE_ESTABLISHED) {
+ rk->array[rk->count] = entry;
+ hip_hold_ha(entry);
+ rk->count++;
+ } else
+ HIP_DEBUG("skipping HA entry 0x%p\n", entry);
+
+ return 0;
+}
+
+/**
+ * hip_send_update_all - send UPDATE packet to every peer
+ * @addr_list: if non-NULL, REA parameter is added to the UPDATE
+ * @addr_count: number of addresses in @addr_list
+ * @ifindex: if non-zero, the ifindex value of the interface which caused the event
+ * @flags: TODO comment
+ *
+ * UPDATE is sent to the peer only if the peer is in established
+ * state.
+ *
+ * Add REA parameter if @addr_list is non-null. @ifindex tells which
+ * device caused the network device event.
+ *
+ * TODO: retransmission timers
+ */
+void hip_send_update_all(struct hip_rea_info_addr_item *addr_list, int addr_count,
+ int ifindex, int flags)
+{
+ int err = 0, i;
+
+ /* code ripped from rea.c */
+ hip_ha_t *entries[HIP_MAX_HAS] = {0};
+ struct hip_update_kludge rk;
+
+ HIP_DEBUG("ifindex=%d\n", ifindex);
+ if (!ifindex) {
+ HIP_DEBUG("test: returning, ifindex=0 (fix this for non-mm UPDATE)\n");
+ return;
+ }
+
+ rk.array = entries;
+ rk.count = 0;
+ rk.length = HIP_MAX_HAS;
+
+ err = hip_for_each_ha(hip_update_get_all_valid, &rk);
+ if (err) {
+ HIP_ERROR("for_each_ha err=%d\n", err);
+ return;
+ }
+
+ for (i = 0; i < rk.count; i++) {
+ if (rk.array[i] != NULL) {
+ hip_send_update(rk.array[i], addr_list, addr_count, ifindex, flags);
+ hip_put_ha(rk.array[i]);
+ }
+ }
+
+ return;
+}
diff -urN linux-2.6.10/net/ipv6/hip/update.h linux-2.6.10-hip/net/ipv6/hip/update.h
--- linux-2.6.10/net/ipv6/hip/update.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/update.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,14 @@
+#ifndef HIP_UPDATE_H
+#define HIP_UPDATE_H
+
+#include
+#include
+
+/* int hip_update_spi_waitlist_ispending(uint32_t spi); */
+/* void hip_update_spi_waitlist_delete_all(void); */
+int hip_receive_update(struct sk_buff *skb);
+int hip_send_update(struct hip_hadb_state *entry, struct hip_rea_info_addr_item *addr_list,
+ int addr_count, int ifindex, int flags);
+void hip_send_update_all(struct hip_rea_info_addr_item *addr_list, int addr_count, int ifindex, int flags);
+
+#endif /* HIP_UPDATE_H */
diff -urN linux-2.6.10/net/ipv6/hip/workqueue.c linux-2.6.10-hip/net/ipv6/hip/workqueue.c
--- linux-2.6.10/net/ipv6/hip/workqueue.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/workqueue.c 2005-02-05 00:01:00.000000000 +0200
@@ -0,0 +1,267 @@
+/*
+ * Work queue functions for HIP
+ *
+ * Licence: GNU/GPL
+ * Authors:
+ * - Kristian Slavov
+ *
+ * Common comments: __get_cpu_var() is used instead of the get_cpu_var() since
+ * each workqueue "listener" is bound to a certain cpu. Workorder is always
+ * inserted into the workqueue of the sender. This is actually the only place
+ * where we would like the adder to be in the same cpu as the workqueue he is
+ * adding to. This is ensured by local_irq_save().
+ *
+ */
+
+#include "workqueue.h"
+#include "debug.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/* HIP Per Cpu WorkQueue */
+struct hip_pc_wq {
+ struct semaphore worklock;
+ struct list_head workqueue;
+};
+
+static DEFINE_PER_CPU(struct hip_pc_wq, hip_workqueue);
+
+/**
+ * hip_get_work_order - Get one work order from workqueue
+ *
+ * HIP kernel daemons call this function when waiting for
+ * work. They will sleep until a work order is received, which
+ * is signalled by up()ing semaphore.
+ * The received work order is removed from the workqueue and
+ * returned to the kernel daemon for processing.
+ *
+ * Returns work order or NULL, if an error occurs.
+ */
+struct hip_work_order *hip_get_work_order(void)
+{
+ unsigned long eflags;
+ struct hip_pc_wq *wq;
+ struct hip_work_order *result;
+ int locked;
+
+ /* get_cpu_var / put_cpu_var ? */
+ wq = &__get_cpu_var(hip_workqueue);
+
+ /* Wait for job */
+ locked = down_interruptible(&wq->worklock);
+ if (locked) {
+ if (locked == -EINTR)
+ HIP_DEBUG("interrupted while trying to get lock\n");
+ return NULL;
+ }
+
+ /* every processor has its own worker thread, so
+ spin lock is not needed. Only local irq disabling */
+ local_irq_save(eflags);
+
+ if (list_empty(&wq->workqueue)) {
+ HIP_ERROR("Work queue empty?\n");
+ result = NULL;
+ goto err;
+ }
+
+ result = list_entry((&wq->workqueue)->next, struct hip_work_order, queue);
+ if (!result) {
+ HIP_ERROR("Couldn't extract the main structure from the list\n");
+ result = NULL;
+ goto err;
+ }
+
+ list_del((&wq->workqueue)->next);
+
+ err:
+ local_irq_restore(eflags);
+ return result;
+
+}
+
+/**
+ * hip_insert_work_order_cpu - Insert a work order on a particular CPU's workqueue
+ * @hwo: Work order to be inserted
+ * @cpu: Cpu number
+ *
+ * Adds the work order into @cpu CPU's HIP workqueue. Mainly useful to send messages
+ * to another kernel daemons from one kernel daemon or user thread (ioctl etc)
+ * This function is static in purpose. Normally this shouldn't be used. Instead use
+ * the variant below.
+ *
+ * Returns 1, if ok. -1 if error
+ */
+static int hip_insert_work_order_cpu(struct hip_work_order *hwo, int cpu)
+{
+ unsigned long eflags;
+ struct hip_pc_wq *wq;
+
+ if (cpu >= NR_CPUS) {
+ HIP_ERROR("Invalid CPU number: %d (max cpus: %d)\n", cpu, NR_CPUS);
+ return -1;
+ }
+
+ if (!hwo) {
+ HIP_ERROR("NULL hwo\n");
+ return -1;
+ }
+
+ _HIP_DEBUG("hwo=0x%p cpu=%d\n", hwo, cpu);
+ local_irq_save(eflags);
+
+ /* get_cpu_var / put_cpu_var ? */
+ wq = &per_cpu(hip_workqueue, cpu);
+ if (wq) {
+ list_add_tail(&hwo->queue, &wq->workqueue);
+ /* what is the correct order of these two, l_i_r and up ? */
+ up(&wq->worklock);
+ } else
+ HIP_ERROR("NULL wq, aieee!\n");
+
+ local_irq_restore(eflags);
+ return 1;
+}
+
+/**
+ * hip_insert_work_order - Insert work order into the HIP working queue of the current CPU.
+ * @hwo: Work order
+ *
+ * Returns 1 if ok, < 0 if error
+ */
+int hip_insert_work_order(struct hip_work_order *hwo)
+{
+ if (!hwo) {
+ HIP_ERROR("NULL hwo\n");
+ return -1;
+ }
+
+ if (hwo->type < 0 || hwo->type > HIP_MAX_WO_TYPES)
+ return -1;
+
+ return hip_insert_work_order_cpu(hwo, smp_processor_id());
+}
+
+
+int hip_init_workqueue()
+{
+ struct hip_pc_wq *wq;
+ unsigned long eflags;
+
+ local_irq_save(eflags);
+
+ wq = &get_cpu_var(hip_workqueue);
+ INIT_LIST_HEAD(&wq->workqueue);
+ init_MUTEX_LOCKED(&wq->worklock);
+ put_cpu_var(hip_workqueue);
+ local_irq_restore(eflags);
+
+ return 0;
+}
+
+void hip_uninit_workqueue()
+{
+ struct list_head *pos,*iter;
+ struct hip_pc_wq *wq;
+ struct hip_work_order *hwo;
+ unsigned long eflags;
+
+ local_irq_save(eflags);
+ //local_bh_disable();
+ /* get_cpu_var / put_cpu_var ? */
+ // wq = &__get_cpu_var(hip_workqueue);
+ wq = &get_cpu_var(hip_workqueue);
+ list_for_each_safe(pos, iter, &wq->workqueue) {
+ hwo = list_entry(pos, struct hip_work_order, queue);
+ hip_free_work_order(hwo);
+ list_del(pos);
+ }
+ put_cpu_var(hip_workqueue); // test
+ local_irq_restore(eflags);
+}
+
+/**
+ * hip_free_work_order - Free work order structure
+ * @hwo: Work order to be freed
+ *
+ * Don't use @hwo after this function. The memory is freed.
+ */
+void hip_free_work_order(struct hip_work_order *hwo)
+{
+ if (hwo) {
+ if (hwo->destructor)
+ hwo->destructor(hwo);
+ kfree(hwo);
+ }
+}
+
+/**
+ * hip_init_job - Allocate and initialize work order
+ * @gfp_mask: Mask for memory allocation
+ *
+ * Returns work order struct, with all fields zeroed. Or %NULL in case
+ * of error.
+ */
+struct hip_work_order *hip_init_job(int gfp_mask)
+{
+ struct hip_work_order *hwo;
+
+ hwo = kmalloc(sizeof(struct hip_work_order), gfp_mask);
+ if (hwo)
+ memset(hwo, 0, sizeof(struct hip_work_order));
+ else
+ HIP_ERROR("No memory for work order\n");
+
+ return hwo;
+}
+
+/**
+ * hip_create_job_with_hit - Create work order and add HIT as a first argument
+ * @gfp_mask: Mask for memory allocation
+ * @hit: HIT to be added
+ *
+ * Allocates and initializes work order with HIT as the first argument.
+ * The memory for HIT is also allocated and the HIT is copied.
+ */
+struct hip_work_order *hip_create_job_with_hit(int gfp_mask,
+ const struct in6_addr *hit)
+{
+ struct hip_work_order *hwo;
+ struct in6_addr *tmp;
+
+ hwo = hip_init_job(gfp_mask);
+ if (!hwo)
+ return NULL;
+
+ tmp = kmalloc(sizeof(struct in6_addr), gfp_mask);
+ if (!tmp) {
+ kfree(hwo);
+ return NULL;
+ }
+
+ ipv6_addr_copy(tmp, hit);
+ hwo->arg1 = tmp;
+ hwo->arg2 = NULL;
+ hwo->destructor = hwo_default_destructor;
+ return hwo;
+}
+
+/**
+ * hwo_default_destructor - Default destructor for work order
+ *
+ * Simple... if you don't understand, then you shouldn't be
+ * dealing with the kernel.
+ */
+void hwo_default_destructor(struct hip_work_order *hwo)
+{
+ if (hwo) {
+ if (hwo->arg1)
+ kfree(hwo->arg1);
+ if (hwo->arg2)
+ kfree(hwo->arg2);
+ }
+}
diff -urN linux-2.6.10/net/ipv6/hip/workqueue.h linux-2.6.10-hip/net/ipv6/hip/workqueue.h
--- linux-2.6.10/net/ipv6/hip/workqueue.h 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/hip/workqueue.h 2005-02-01 17:16:24.000000000 +0200
@@ -0,0 +1,53 @@
+#ifndef HIP_WORKQUEUE
+#define HIP_WORKQUEUE
+
+#include
+#include
+#include
+
+#define HIP_WO_TYPE_INCOMING 1
+#define HIP_WO_TYPE_OUTGOING 2
+#define HIP_WO_TYPE_MSG 3
+#define HIP_MAX_WO_TYPES 3 // this should be equal to the greates type number
+
+/* subtypes from 1 to 100, reserved for HIP_WO_TYPE_INCOMING */
+#define HIP_WO_SUBTYPE_RECV_I1 1
+#define HIP_WO_SUBTYPE_RECV_R1 2
+#define HIP_WO_SUBTYPE_RECV_I2 3
+#define HIP_WO_SUBTYPE_RECV_R2 4
+#define HIP_WO_SUBTYPE_RECV_UPDATE 5
+#define HIP_WO_SUBTYPE_RECV_NOTIFY 6
+#define HIP_WO_SUBTYPE_RECV_BOS 7
+
+/* subtypes from 101 to 200 reserved for HIP_WO_TYPE_OUTGOING */
+
+#define HIP_WO_SUBTYPE_NEW_CONN 101
+#define HIP_WO_SUBTYPE_DEL_CONN 102 // reinitialize state to start
+#define HIP_WO_SUBTYPE_SKWAIT 103
+
+/* subtypes from 201 to 300 reserved for HIP_WO_TYPE_MSG */
+
+#define HIP_WO_SUBTYPE_ADDMAP 202
+#define HIP_WO_SUBTYPE_DELMAP 203
+#define HIP_WO_SUBTYPE_FLUSHMAPS 204 // flush states
+#define HIP_WO_SUBTYPE_ADDHI 205
+#define HIP_WO_SUBTYPE_DELHI 206
+#define HIP_WO_SUBTYPE_FLUSHHIS 207
+#define HIP_WO_SUBTYPE_NEWDH 208 // request new DH-key (implies UPDATE)
+#define HIP_WO_SUBTYPE_IN6_EVENT 209
+#define HIP_WO_SUBTYPE_DEV_EVENT 210
+#define HIP_WO_SUBTYPE_ADDRVS 211
+
+extern struct semaphore hip_work;
+
+int hip_init_workqueue(void);
+void hip_uninit_workqueue(void);
+int hip_insert_work_order(struct hip_work_order *hwo);
+struct hip_work_order *hip_get_work_order(void);
+struct hip_work_order *hip_create_job_with_hit(int mask,
+ const struct in6_addr *hit);
+struct hip_work_order *hip_init_job(int mask);
+void hip_free_work_order(struct hip_work_order *hwo);
+
+void hwo_default_destructor(struct hip_work_order *hwo);
+#endif /* HIP_WORKQUEUE */
diff -urN linux-2.6.10/net/ipv6/ip6_input.c linux-2.6.10-hip/net/ipv6/ip6_input.c
--- linux-2.6.10/net/ipv6/ip6_input.c 2004-12-24 23:35:23.000000000 +0200
+++ linux-2.6.10-hip/net/ipv6/ip6_input.c 2005-02-01 17:16:24.000000000 +0200
@@ -47,7 +47,6 @@
#include