merging kernel_klips plugin back into trunk
authorTobias Brunner <tobias@strongswan.org>
Tue, 11 Nov 2008 09:22:00 +0000 (09:22 -0000)
committerTobias Brunner <tobias@strongswan.org>
Tue, 11 Nov 2008 09:22:00 +0000 (09:22 -0000)
30 files changed:
configure.in
src/charon/Makefile.am
src/charon/encoding/payloads/payload.c
src/charon/kernel/kernel_interface.c
src/charon/kernel/kernel_interface.h
src/charon/kernel/kernel_ipsec.h
src/charon/network/socket.c
src/charon/network/socket.h
src/charon/plugins/kernel_klips/Makefile.am [new file with mode: 0644]
src/charon/plugins/kernel_klips/kernel_klips_ipsec.c [new file with mode: 0644]
src/charon/plugins/kernel_klips/kernel_klips_ipsec.h [new file with mode: 0644]
src/charon/plugins/kernel_klips/kernel_klips_plugin.c [new file with mode: 0644]
src/charon/plugins/kernel_klips/kernel_klips_plugin.h [new file with mode: 0644]
src/charon/plugins/kernel_klips/pfkeyv2.h [new file with mode: 0644]
src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c
src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
src/charon/plugins/load_tester/load_tester_ipsec.c
src/charon/plugins/nm/Makefile.am
src/charon/sa/child_sa.c
src/charon/sa/child_sa.h
src/charon/sa/ike_sa.c
src/charon/sa/tasks/child_create.c
src/charon/sa/tasks/ike_mobike.c
src/charon/sa/tasks/task.c
src/libstrongswan/utils/host.h
src/starter/Makefile.am
src/starter/files.h
src/starter/klips.c [new file with mode: 0644]
src/starter/klips.h [new file with mode: 0644]
src/starter/starter.c

index b016451..54ff358 100644 (file)
@@ -469,6 +469,14 @@ AC_ARG_ENABLE(
 )
 
 AC_ARG_ENABLE(
+       [kernel-klips],
+       AS_HELP_STRING([--enable-kernel-klips],[enable the KLIPS kernel interface. (default is NO).]),
+       [if test x$enableval = xyes; then
+               kernel_klips=true
+       fi]
+)
+
+AC_ARG_ENABLE(
        [nat-transport],
        AS_HELP_STRING([--enable-nat-transport],[enable NAT traversal with IPsec transport mode (default is NO).]),
        [if test x$enableval = xyes; then
@@ -901,6 +909,7 @@ AM_CONDITIONAL(USE_EAP_GTC, test x$eap_gtc = xtrue)
 AM_CONDITIONAL(USE_EAP_AKA, test x$eap_aka = xtrue)
 AM_CONDITIONAL(USE_KERNEL_NETLINK, test x$kernel_netlink = xtrue)
 AM_CONDITIONAL(USE_KERNEL_PFKEY, test x$kernel_pfkey = xtrue)
+AM_CONDITIONAL(USE_KERNEL_KLIPS, test x$kernel_klips = xtrue)
 
 dnl other options
 dnl =============
@@ -977,6 +986,7 @@ AC_OUTPUT(
        src/charon/plugins/eap_sim_file/Makefile
        src/charon/plugins/kernel_netlink/Makefile
        src/charon/plugins/kernel_pfkey/Makefile
+       src/charon/plugins/kernel_klips/Makefile
        src/charon/plugins/smp/Makefile
        src/charon/plugins/sql/Makefile
        src/charon/plugins/medsrv/Makefile
index d66eb55..e3d3e8e 100644 (file)
@@ -156,6 +156,11 @@ if USE_KERNEL_PFKEY
   PLUGINS += kernel-pfkey
 endif
 
+if USE_KERNEL_KLIPS
+  SUBDIRS += plugins/kernel_klips
+  PLUGINS += kernel-klips
+endif
+
 if USE_KERNEL_NETLINK
   SUBDIRS += plugins/kernel_netlink
   PLUGINS += kernel-netlink
index 032de6e..418151e 100644 (file)
@@ -62,9 +62,15 @@ ENUM_NEXT(payload_type_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICATION, N
 ENUM_NEXT(payload_type_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION,
        "ID_PEER");
 ENUM_NEXT(payload_type_names, HEADER, UNKNOWN_PAYLOAD, ID_PEER,
+       "HEADER",
+       "PROPOSAL_SUBSTRUCTURE",
+       "TRANSFORM_SUBSTRUCTURE",
+       "TRANSFORM_ATTRIBUTE",
+       "TRAFFIC_SELECTOR_SUBSTRUCTURE",
+       "CONFIGURATION_ATTRIBUTE",
+       "UNKNOWN_PAYLOAD");
 #else
 ENUM_NEXT(payload_type_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION,
-#endif /* ME */
        "HEADER",
        "PROPOSAL_SUBSTRUCTURE",
        "TRANSFORM_SUBSTRUCTURE",
@@ -72,6 +78,7 @@ ENUM_NEXT(payload_type_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION
        "TRAFFIC_SELECTOR_SUBSTRUCTURE",
        "CONFIGURATION_ATTRIBUTE",
        "UNKNOWN_PAYLOAD");
+#endif /* ME */
 ENUM_END(payload_type_names, UNKNOWN_PAYLOAD);
 
 /* short forms of payload names */
@@ -98,9 +105,15 @@ ENUM_NEXT(payload_type_short_names, SECURITY_ASSOCIATION, EXTENSIBLE_AUTHENTICAT
 ENUM_NEXT(payload_type_short_names, ID_PEER, ID_PEER, EXTENSIBLE_AUTHENTICATION,
        "IDp");
 ENUM_NEXT(payload_type_short_names, HEADER, UNKNOWN_PAYLOAD, ID_PEER,
+       "HDR",
+       "PROP",
+       "TRANS",
+       "TRANSATTR",
+       "TSSUB",
+       "CPATTR",
+       "??");
 #else
 ENUM_NEXT(payload_type_short_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTICATION,
-#endif /* ME */
        "HDR",
        "PROP",
        "TRANS",
@@ -108,6 +121,7 @@ ENUM_NEXT(payload_type_short_names, HEADER, UNKNOWN_PAYLOAD, EXTENSIBLE_AUTHENTI
        "TSSUB",
        "CPATTR",
        "??");
+#endif /* ME */
 ENUM_END(payload_type_short_names, UNKNOWN_PAYLOAD);
 
 /*
index f71e3c5..2c515b0 100644 (file)
@@ -87,31 +87,32 @@ static status_t add_sa(private_kernel_interface_t *this, host_t *src, host_t *ds
                                u_int64_t expire_soft, u_int64_t expire_hard,
                                u_int16_t enc_alg, chunk_t enc_key,
                                u_int16_t int_alg, chunk_t int_key,
-                               ipsec_mode_t mode, u_int16_t ipcomp, bool encap, bool update)
+                               ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi, bool encap,
+                               bool inbound)
 {
        return this->ipsec->add_sa(this->ipsec, src, dst, spi, protocol, reqid,
                        expire_soft, expire_hard, enc_alg, enc_key, int_alg, int_key,
-                       mode, ipcomp, encap, update);
+                       mode, ipcomp, cpi, encap, inbound);
 }
 
 /**
  * Implementation of kernel_interface_t.update_sa
  */
 static status_t update_sa(private_kernel_interface_t *this, u_int32_t spi,
-                                  protocol_id_t protocol, host_t *src, host_t *dst, 
-                                  host_t *new_src, host_t *new_dst, bool encap)
+                                  protocol_id_t protocol, u_int16_t cpi, host_t *src, host_t *dst, 
+                                  host_t *new_src, host_t *new_dst, bool encap, bool new_encap)
 {
-       return this->ipsec->update_sa(this->ipsec, spi, protocol, src, dst, new_src,
-                       new_dst, encap);
+       return this->ipsec->update_sa(this->ipsec, spi, protocol, cpi, src, dst,
+                       new_src, new_dst, encap, new_encap);
 }
 
 /**
  * Implementation of kernel_interface_t.del_sa
  */
 static status_t del_sa(private_kernel_interface_t *this, host_t *dst, u_int32_t spi,
-                               protocol_id_t protocol)
+                               protocol_id_t protocol, u_int16_t cpi)
 {
-       return this->ipsec->del_sa(this->ipsec, dst, spi, protocol);
+       return this->ipsec->del_sa(this->ipsec, dst, spi, protocol, cpi);
 }
 
 /**
@@ -119,12 +120,12 @@ static status_t del_sa(private_kernel_interface_t *this, host_t *dst, u_int32_t
  */
 static status_t add_policy(private_kernel_interface_t *this, host_t *src, host_t *dst,
                                        traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
-                                       policy_dir_t direction, protocol_id_t protocol,
-                                       u_int32_t reqid, bool high_prio, ipsec_mode_t mode,
-                                       u_int16_t ipcomp)
+                                       policy_dir_t direction, u_int32_t spi, protocol_id_t protocol,
+                                       u_int32_t reqid, ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                       bool routed)
 {
        return this->ipsec->add_policy(this->ipsec, src, dst, src_ts, dst_ts,
-                       direction, protocol, reqid, high_prio, mode, ipcomp);
+                       direction, spi, protocol, reqid, mode, ipcomp, cpi, routed);
 }
 
 /**
@@ -142,9 +143,9 @@ static status_t query_policy(private_kernel_interface_t *this,
  */
 static status_t del_policy(private_kernel_interface_t *this,
                                        traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
-                                       policy_dir_t direction)
+                                       policy_dir_t direction, bool unrouted)
 {
-       return this->ipsec->del_policy(this->ipsec, src_ts, dst_ts, direction);
+       return this->ipsec->del_policy(this->ipsec, src_ts, dst_ts, direction, unrouted);
 }
 
 /**
@@ -370,12 +371,12 @@ kernel_interface_t *kernel_interface_create()
        
        this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
        this->public.get_cpi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,u_int32_t,u_int16_t*))get_cpi;
-       this->public.add_sa  = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,bool,bool))add_sa;
-       this->public.update_sa = (status_t(*)(kernel_interface_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*,bool))update_sa;
-       this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa;
-       this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,ipsec_mode_t,u_int16_t))add_policy;
+       this->public.add_sa  = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,u_int16_t,bool,bool))add_sa;
+       this->public.update_sa = (status_t(*)(kernel_interface_t*,u_int32_t,protocol_id_t,u_int16_t,host_t*,host_t*,host_t*,host_t*,bool,bool))update_sa;
+       this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int16_t))del_sa;
+       this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t,protocol_id_t,u_int32_t,ipsec_mode_t,u_int16_t,u_int16_t,bool))add_policy;
        this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
-       this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy;
+       this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,bool))del_policy;
        
        this->public.get_source_addr = (host_t*(*)(kernel_interface_t*, host_t *dest, host_t *src))get_source_addr;
        this->public.get_nexthop = (host_t*(*)(kernel_interface_t*, host_t *dest))get_nexthop;
index aec4924..105c572 100644 (file)
@@ -56,10 +56,6 @@ struct kernel_interface_t {
        /**
         * Get a SPI from the kernel.
         *
-        * @warning get_spi() implicitly creates an SA with
-        * the allocated SPI, therefore the replace flag
-        * in add_sa() must be set when installing this SA.
-        * 
         * @param src           source address of SA
         * @param dst           destination address of SA
         * @param protocol      protocol for SA (ESP/AH)
@@ -106,8 +102,9 @@ struct kernel_interface_t {
         * @param int_key               key to use for integrity protection
         * @param mode                  mode of the SA (tunnel, transport)
         * @param ipcomp                IPComp transform to use
+        * @param cpi                   CPI for IPComp
         * @param encap                 enable UDP encapsulation for NAT traversal
-        * @param replace               Should an already installed SA be updated?
+        * @param inbound               TRUE if this is an inbound SA
         * @return                              SUCCESS if operation completed
         */
        status_t (*add_sa) (kernel_interface_t *this,
@@ -116,8 +113,8 @@ struct kernel_interface_t {
                                                u_int64_t expire_soft, u_int64_t expire_hard,
                                            u_int16_t enc_alg, chunk_t enc_key,
                                            u_int16_t int_alg, chunk_t int_key,
-                                               ipsec_mode_t mode, u_int16_t ipcomp, bool encap,
-                                               bool update);
+                                               ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                               bool encap, bool inbound);
        
        /**
         * Update the hosts on an installed SA.
@@ -129,17 +126,21 @@ struct kernel_interface_t {
         *
         * @param spi                   SPI of the SA
         * @param protocol              protocol for this SA (ESP/AH)
+        * @param cpi                   CPI for IPComp, 0 if no IPComp is used
         * @param src                   current source address
         * @param dst                   current destination address
         * @param new_src               new source address
         * @param new_dst               new destination address
-        * @param encap                 use UDP encapsulation
-        * @return                              SUCCESS if operation completed
+        * @param encap                 current use of UDP encapsulation
+        * @param new_encap             new use of UDP encapsulation
+        * @return                              SUCCESS if operation completed, NOT_SUPPORTED if
+        *                      the kernel interface can't update the SA
         */
        status_t (*update_sa)(kernel_interface_t *this,
-                                                 u_int32_t spi, protocol_id_t protocol,
+                                                 u_int32_t spi, protocol_id_t protocol, u_int16_t cpi,
                                                  host_t *src, host_t *dst, 
-                                                 host_t *new_src, host_t *new_dst, bool encap);
+                                                 host_t *new_src, host_t *new_dst,
+                                                 bool encap, bool new_encap);
        
        /**
         * Delete a previously installed SA from the SAD.
@@ -147,10 +148,11 @@ struct kernel_interface_t {
         * @param dst                   destination address for this SA
         * @param spi                   SPI allocated by us or remote peer
         * @param protocol              protocol for this SA (ESP/AH)
+        * @param cpi                   CPI for IPComp or 0
         * @return                              SUCCESS if operation completed
         */
        status_t (*del_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi,
-                                               protocol_id_t protocol);
+                                               protocol_id_t protocol, u_int16_t cpi);
        
        /**
         * Add a policy to the SPD.
@@ -163,20 +165,23 @@ struct kernel_interface_t {
         * @param src_ts                traffic selector to match traffic source
         * @param dst_ts                traffic selector to match traffic dest
         * @param direction             direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD
+        * @param spi                   SPI of SA
         * @param protocol              protocol to use to protect traffic (AH/ESP)
         * @param reqid                 unique ID of an SA to use to enforce policy
-        * @param high_prio             if TRUE, uses a higher priority than any with FALSE
         * @param mode                  mode of SA (tunnel, transport)
         * @param ipcomp                the IPComp transform used
+        * @param cpi                   CPI for IPComp
+        * @param routed                TRUE, if this policy is routed in the kernel
         * @return                              SUCCESS if operation completed
         */
        status_t (*add_policy) (kernel_interface_t *this,
                                                        host_t *src, host_t *dst,
                                                        traffic_selector_t *src_ts,
                                                        traffic_selector_t *dst_ts,
-                                                       policy_dir_t direction, protocol_id_t protocol,
-                                                       u_int32_t reqid, bool high_prio, ipsec_mode_t mode,
-                                                       u_int16_t ipcomp);
+                                                       policy_dir_t direction, u_int32_t spi,
+                                                       protocol_id_t protocol, u_int32_t reqid,
+                                                       ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                                       bool routed);
        
        /**
         * Query the use time of a policy.
@@ -206,12 +211,14 @@ struct kernel_interface_t {
         * @param src_ts                traffic selector to match traffic source
         * @param dst_ts                traffic selector to match traffic dest
         * @param direction             direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD
+        * @param unrouted              TRUE, if this policy is unrouted from the kernel
         * @return                              SUCCESS if operation completed
         */
        status_t (*del_policy) (kernel_interface_t *this,
                                                        traffic_selector_t *src_ts, 
                                                        traffic_selector_t *dst_ts,
-                                                       policy_dir_t direction);
+                                                       policy_dir_t direction,
+                                                       bool unrouted);
        
        /**
         * Get our outgoing source address for a destination.
index bef496a..01fbf6f 100644 (file)
@@ -88,10 +88,6 @@ struct kernel_ipsec_t {
        /**
         * Get a SPI from the kernel.
         *
-        * @warning get_spi() implicitly creates an SA with
-        * the allocated SPI, therefore the replace flag
-        * in add_sa() must be set when installing this SA.
-        * 
         * @param src           source address of SA
         * @param dst           destination address of SA
         * @param protocol      protocol for SA (ESP/AH)
@@ -138,8 +134,9 @@ struct kernel_ipsec_t {
         * @param int_key               key to use for integrity protection
         * @param mode                  mode of the SA (tunnel, transport)
         * @param ipcomp                IPComp transform to use
+        * @param cpi                   CPI for IPComp
         * @param encap                 enable UDP encapsulation for NAT traversal
-        * @param replace               Should an already installed SA be updated?
+        * @param inbound               TRUE if this is an inbound SA
         * @return                              SUCCESS if operation completed
         */
        status_t (*add_sa) (kernel_ipsec_t *this,
@@ -148,8 +145,8 @@ struct kernel_ipsec_t {
                                                u_int64_t expire_soft, u_int64_t expire_hard,
                                            u_int16_t enc_alg, chunk_t enc_key,
                                            u_int16_t int_alg, chunk_t int_key,
-                                               ipsec_mode_t mode, u_int16_t ipcomp, bool encap,
-                                               bool update);
+                                               ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                               bool encap, bool inbound);
        
        /**
         * Update the hosts on an installed SA.
@@ -161,17 +158,21 @@ struct kernel_ipsec_t {
         *
         * @param spi                   SPI of the SA
         * @param protocol              protocol for this SA (ESP/AH)
+        * @param cpi                   CPI for IPComp, 0 if no IPComp is used
         * @param src                   current source address
         * @param dst                   current destination address
         * @param new_src               new source address
         * @param new_dst               new destination address
-        * @param encap                 use UDP encapsulation
-        * @return                              SUCCESS if operation completed
+        * @param encap                 current use of UDP encapsulation
+        * @param new_encap             new use of UDP encapsulation
+        * @return                              SUCCESS if operation completed, NOT_SUPPORTED if
+        *                      the kernel interface can't update the SA
         */
        status_t (*update_sa)(kernel_ipsec_t *this,
-                                                 u_int32_t spi, protocol_id_t protocol,
+                                                 u_int32_t spi, protocol_id_t protocol, u_int16_t cpi,
                                                  host_t *src, host_t *dst, 
-                                                 host_t *new_src, host_t *new_dst, bool encap);
+                                                 host_t *new_src, host_t *new_dst,
+                                                 bool encap, bool new_encap);
        
        /**
         * Delete a previusly installed SA from the SAD.
@@ -179,10 +180,11 @@ struct kernel_ipsec_t {
         * @param dst                   destination address for this SA
         * @param spi                   SPI allocated by us or remote peer
         * @param protocol              protocol for this SA (ESP/AH)
+        * @param cpi                   CPI for IPComp or 0
         * @return                              SUCCESS if operation completed
         */
        status_t (*del_sa) (kernel_ipsec_t *this, host_t *dst, u_int32_t spi,
-                                               protocol_id_t protocol);
+                                               protocol_id_t protocol, u_int16_t cpi);
        
        /**
         * Add a policy to the SPD.
@@ -195,20 +197,23 @@ struct kernel_ipsec_t {
         * @param src_ts                traffic selector to match traffic source
         * @param dst_ts                traffic selector to match traffic dest
         * @param direction             direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD
+        * @param spi                   SPI of SA
         * @param protocol              protocol to use to protect traffic (AH/ESP)
         * @param reqid                 unique ID of an SA to use to enforce policy
-        * @param high_prio             if TRUE, uses a higher priority than any with FALSE
         * @param mode                  mode of SA (tunnel, transport)
         * @param ipcomp                the IPComp transform used
+        * @param cpi                   CPI for IPComp
+        * @param routed                TRUE, if this policy is routed in the kernel
         * @return                              SUCCESS if operation completed
         */
        status_t (*add_policy) (kernel_ipsec_t *this,
                                                        host_t *src, host_t *dst,
                                                        traffic_selector_t *src_ts,
                                                        traffic_selector_t *dst_ts,
-                                                       policy_dir_t direction, protocol_id_t protocol,
-                                                       u_int32_t reqid, bool high_prio, ipsec_mode_t mode,
-                                                       u_int16_t ipcomp);
+                                                       policy_dir_t direction, u_int32_t spi,
+                                                       protocol_id_t protocol, u_int32_t reqid,
+                                                       ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                                       bool routed);
        
        /**
         * Query the use time of a policy.
@@ -238,12 +243,14 @@ struct kernel_ipsec_t {
         * @param src_ts                traffic selector to match traffic source
         * @param dst_ts                traffic selector to match traffic dest
         * @param direction             direction of traffic, POLICY_IN, POLICY_OUT, POLICY_FWD
+        * @param unrouted              TRUE, if this policy is unrouted from the kernel
         * @return                              SUCCESS if operation completed
         */
        status_t (*del_policy) (kernel_ipsec_t *this,
                                                        traffic_selector_t *src_ts, 
                                                        traffic_selector_t *dst_ts,
-                                                       policy_dir_t direction);
+                                                       policy_dir_t direction,
+                                                       bool unrouted);
        
        /**
         * Destroy the implementation.
index 2b854c6..137ac5f 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2006-2008 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
  * Copyright (C) 2005-2007 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -33,7 +34,7 @@
 #include <netinet/ip.h>
 #include <netinet/ip6.h>
 #include <netinet/udp.h>
-#include <linux/ipsec.h>
+#include <linux/types.h>
 #include <linux/filter.h>
 #include <net/if.h>
 
 /* length of non-esp marker */
 #define MARKER_LEN sizeof(u_int32_t)
 
-/* from linux/in.h */
-#ifndef IP_IPSEC_POLICY
-#define IP_IPSEC_POLICY 16
-#endif /*IP_IPSEC_POLICY*/
-
 /* from linux/udp.h */
 #ifndef UDP_ENCAP
 #define UDP_ENCAP 100
@@ -101,6 +97,18 @@ struct private_socket_t {
 };
 
 /**
+ * enumerator for underlying sockets
+ */
+typedef struct {
+       /** implements enumerator_t */
+       enumerator_t public;
+       /** sockets we enumerate */
+       private_socket_t *socket;
+       /** counter */
+       u_int8_t index;
+} socket_enumerator_t;
+
+/**
  * implementation of socket_t.receive
  */
 static status_t receiver(private_socket_t *this, packet_t **packet)
@@ -405,8 +413,7 @@ static int open_socket(private_socket_t *this, int family, u_int16_t port)
        int on = TRUE;
        int type = UDP_ENCAP_ESPINUDP;
        struct sockaddr_storage addr;
-       u_int sol, ipsec_policy, pktinfo;
-       struct sadb_x_policy policy;
+       u_int sol, pktinfo;
        int skt;
        
        memset(&addr, 0, sizeof(addr));
@@ -420,7 +427,6 @@ static int open_socket(private_socket_t *this, int family, u_int16_t port)
                        sin->sin_addr.s_addr = INADDR_ANY;
                        sin->sin_port = htons(port);
                        sol = SOL_IP;
-                       ipsec_policy = IP_IPSEC_POLICY;
                        pktinfo = IP_PKTINFO;
                        break;
                }
@@ -431,7 +437,6 @@ static int open_socket(private_socket_t *this, int family, u_int16_t port)
                        memcpy(&sin6->sin6_addr, &in6addr_any, sizeof(in6addr_any));
                        sin6->sin6_port = htons(port);
                        sol = SOL_IPV6;
-                       ipsec_policy = IPV6_IPSEC_POLICY;
                        pktinfo = IPV6_2292PKTINFO;
                        break;
                }
@@ -452,29 +457,6 @@ static int open_socket(private_socket_t *this, int family, u_int16_t port)
                return 0;
        }
        
-       /* bypass IKE traffic on socket */
-       memset(&policy, 0, sizeof(policy));
-       policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t);
-       policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
-       policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS;
-       
-       policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND;
-       if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
-       {
-               DBG1(DBG_NET, "unable to set IPSEC_POLICY on socket: %s",
-                        strerror(errno));
-               close(skt);
-               return 0;
-       }
-       policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND;
-       if (setsockopt(skt, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
-       {
-               DBG1(DBG_NET, "unable to set IPSEC_POLICY on socket: %s", 
-                        strerror(errno));
-               close(skt);
-               return 0;
-       }
-       
        /* bind the send socket */
        if (bind(skt, (struct sockaddr *)&addr, sizeof(addr)) < 0)
        {
@@ -501,6 +483,53 @@ static int open_socket(private_socket_t *this, int family, u_int16_t port)
 }
 
 /**
+ * enumerate function for socket_enumerator_t
+ */
+static bool enumerate(socket_enumerator_t *this, int *fd, int *family, int *port)
+{
+       static const struct {
+               int fd_offset;
+               int family;
+               int port;
+       } sockets[] = {
+               { 0, 0, 0 },
+               { offsetof(private_socket_t, ipv4), AF_INET, IKEV2_UDP_PORT },
+               { offsetof(private_socket_t, ipv6), AF_INET6, IKEV2_UDP_PORT },
+               { offsetof(private_socket_t, ipv4_natt), AF_INET, IKEV2_NATT_PORT },
+               { offsetof(private_socket_t, ipv6_natt), AF_INET6, IKEV2_NATT_PORT }
+       };
+       
+       while(++this->index <= 4)
+       {
+               int sock = *(int*)((char*)this->socket + sockets[this->index].fd_offset);
+               if (!sock)
+               {
+                       continue;
+               }
+               *fd = sock;
+               *family = sockets[this->index].family;
+               *port = sockets[this->index].port;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+/**
+ * implementation of socket_t.create_enumerator
+ */
+static enumerator_t *create_enumerator(private_socket_t *this)
+{
+       socket_enumerator_t *enumerator;
+       
+       enumerator = malloc_thing(socket_enumerator_t);
+       enumerator->index = 0;
+       enumerator->socket = this;
+       enumerator->public.enumerate = (void*)enumerate;
+       enumerator->public.destroy = (void*)free;
+       return &enumerator->public;
+}
+
+/**
  * implementation of socket_t.destroy
  */
 static void destroy(private_socket_t *this)
@@ -529,12 +558,12 @@ static void destroy(private_socket_t *this)
  */
 socket_t *socket_create()
 {
-       int key;
        private_socket_t *this = malloc_thing(private_socket_t);
 
        /* public functions */
        this->public.send = (status_t(*)(socket_t*, packet_t*))sender;
        this->public.receive = (status_t(*)(socket_t*, packet_t**))receiver;
+       this->public.create_enumerator = (enumerator_t*(*)(socket_t*))create_enumerator;
        this->public.destroy = (void(*)(socket_t*)) destroy;
        
        this->ipv4 = 0;
@@ -542,15 +571,6 @@ socket_t *socket_create()
        this->ipv4_natt = 0;
        this->ipv6_natt = 0;
        
-       /* we open a AF_KEY socket to autoload the af_key module. Otherwise
-        * setsockopt(IPSEC_POLICY) won't work. */
-       key = socket(AF_KEY, SOCK_RAW, PF_KEY_V2);
-       if (key == 0)
-       {
-               charon->kill(charon, "could not open AF_KEY socket");
-       }
-       close(key);
-
        this->ipv4 = open_socket(this, AF_INET, IKEV2_UDP_PORT);
        if (this->ipv4 == 0)
        {
index 7e6623f..2f1f627 100644 (file)
@@ -30,7 +30,7 @@ typedef struct socket_t socket_t;
 #include <library.h>
 #include <network/packet.h>
 #include <utils/host.h>
-#include <utils/linked_list.h>
+#include <utils/enumerator.h>
 
 /**
  * Maximum size of a packet.
@@ -85,6 +85,13 @@ struct socket_t {
        status_t (*send) (socket_t *this, packet_t *packet);
        
        /**
+        * Enumerate the underlying sockets.
+        * 
+        * @return                              enumerator_t object
+        */
+       enumerator_t *(*create_enumerator) (socket_t *this);
+       
+       /**
         * Destroy socket.
         */
        void (*destroy) (socket_t *this);
diff --git a/src/charon/plugins/kernel_klips/Makefile.am b/src/charon/plugins/kernel_klips/Makefile.am
new file mode 100644 (file)
index 0000000..dc02347
--- /dev/null
@@ -0,0 +1,10 @@
+
+INCLUDES = -I${linuxdir} -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
+
+AM_CFLAGS = -rdynamic
+
+plugin_LTLIBRARIES = libstrongswan-kernel-klips.la
+
+libstrongswan_kernel_klips_la_SOURCES = kernel_klips_plugin.h kernel_klips_plugin.c \
+       kernel_klips_ipsec.h kernel_klips_ipsec.c pfkeyv2.h
+libstrongswan_kernel_klips_la_LDFLAGS = -module
diff --git a/src/charon/plugins/kernel_klips/kernel_klips_ipsec.c b/src/charon/plugins/kernel_klips/kernel_klips_ipsec.c
new file mode 100644 (file)
index 0000000..892572a
--- /dev/null
@@ -0,0 +1,2657 @@
+/*
+ * Copyright (C) 2008 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <stdint.h>
+#include "pfkeyv2.h"
+#include <linux/udp.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "kernel_klips_ipsec.h"
+
+#include <daemon.h>
+#include <utils/mutex.h>
+#include <processing/jobs/callback_job.h>
+#include <processing/jobs/acquire_job.h>
+#include <processing/jobs/rekey_child_sa_job.h>
+#include <processing/jobs/delete_child_sa_job.h>
+#include <processing/jobs/update_sa_job.h>
+
+/** default timeout for generated SPIs (in seconds) */
+#define SPI_TIMEOUT 30
+
+/** buffer size for PF_KEY messages */
+#define PFKEY_BUFFER_SIZE 2048
+
+/** PF_KEY messages are 64 bit aligned */
+#define PFKEY_ALIGNMENT 8
+/** aligns len to 64 bits */
+#define PFKEY_ALIGN(len) (((len) + PFKEY_ALIGNMENT - 1) & ~(PFKEY_ALIGNMENT - 1))
+/** calculates the properly padded length in 64 bit chunks */
+#define PFKEY_LEN(len) ((PFKEY_ALIGN(len) / PFKEY_ALIGNMENT))
+/** calculates user mode length i.e. in bytes */
+#define PFKEY_USER_LEN(len) ((len) * PFKEY_ALIGNMENT)
+
+/** given a PF_KEY message header and an extension this updates the length in the header */
+#define PFKEY_EXT_ADD(msg, ext) ((msg)->sadb_msg_len += ((struct sadb_ext*)ext)->sadb_ext_len)
+/** given a PF_KEY message header this returns a pointer to the next extension */
+#define PFKEY_EXT_ADD_NEXT(msg) ((struct sadb_ext*)(((char*)(msg)) + PFKEY_USER_LEN((msg)->sadb_msg_len)))
+/** copy an extension and append it to a PF_KEY message */
+#define PFKEY_EXT_COPY(msg, ext) (PFKEY_EXT_ADD(msg, memcpy(PFKEY_EXT_ADD_NEXT(msg), ext, PFKEY_USER_LEN(((struct sadb_ext*)ext)->sadb_ext_len))))
+/** given a PF_KEY extension this returns a pointer to the next extension */
+#define PFKEY_EXT_NEXT(ext) ((struct sadb_ext*)(((char*)(ext)) + PFKEY_USER_LEN(((struct sadb_ext*)ext)->sadb_ext_len)))
+/** given a PF_KEY extension this returns a pointer to the next extension also updates len (len in 64 bit words) */
+#define PFKEY_EXT_NEXT_LEN(ext,len) ((len) -= (ext)->sadb_ext_len, PFKEY_EXT_NEXT(ext))
+/** true if ext has a valid length and len is large enough to contain ext (assuming len in 64 bit words) */
+#define PFKEY_EXT_OK(ext,len) ((len) >= PFKEY_LEN(sizeof(struct sadb_ext)) && \
+                               (ext)->sadb_ext_len >= PFKEY_LEN(sizeof(struct sadb_ext)) && \
+                               (ext)->sadb_ext_len <= (len))
+
+/** special SPI values used for policies in KLIPS */
+#define SPI_PASS 256
+#define SPI_DROP 257
+#define SPI_REJECT 258
+#define SPI_HOLD 259
+#define SPI_TRAP 260
+#define SPI_TRAPSUBNET 261
+
+/** the prefix of the name of KLIPS ipsec devices */
+#define IPSEC_DEV_PREFIX "ipsec"
+/** this is the default number of ipsec devices */
+#define DEFAULT_IPSEC_DEV_COUNT 4 
+/** TRUE if the given name matches an ipsec device */
+#define IS_IPSEC_DEV(name) (strneq((name), IPSEC_DEV_PREFIX, sizeof(IPSEC_DEV_PREFIX) - 1))
+
+/** the following stuff is from ipsec_tunnel.h */
+struct ipsectunnelconf
+{
+       __u32   cf_cmd;
+       union
+       {
+               char    cfu_name[12];
+       } cf_u;
+#define cf_name cf_u.cfu_name
+};
+
+#define IPSEC_SET_DEV (SIOCDEVPRIVATE)
+#define IPSEC_DEL_DEV (SIOCDEVPRIVATE + 1)
+#define IPSEC_CLR_DEV (SIOCDEVPRIVATE + 2)
+
+typedef struct private_kernel_klips_ipsec_t private_kernel_klips_ipsec_t;
+
+/**
+ * Private variables and functions of kernel_klips class.
+ */
+struct private_kernel_klips_ipsec_t
+{
+       /**
+        * Public part of the kernel_klips_t object.
+        */
+       kernel_klips_ipsec_t public;
+       
+       /**
+        * mutex to lock access to various lists
+        */
+       mutex_t *mutex;
+       
+       /**
+        * List of installed policies (policy_entry_t)
+        */
+       linked_list_t *policies;
+       
+       /**
+        * List of allocated SPIs without installed SA (sa_entry_t)
+        */
+       linked_list_t *allocated_spis;
+       
+       /**
+        * List of installed SAs (sa_entry_t)
+        */
+       linked_list_t *installed_sas;
+       
+       /**
+        * whether to install routes along policies
+        */
+       bool install_routes;
+       
+       /**
+        * List of ipsec devices (ipsec_dev_t)
+        */
+       linked_list_t *ipsec_devices;
+       
+       /**
+        * job receiving PF_KEY events
+        */
+       callback_job_t *job;
+       
+       /**
+        * mutex to lock access to the PF_KEY socket
+        */
+       mutex_t *mutex_pfkey;
+       
+       /**
+        * PF_KEY socket to communicate with the kernel
+        */
+       int socket;
+       
+       /**
+        * PF_KEY socket to receive acquire and expire events
+        */
+       int socket_events;
+       
+       /**
+        * sequence number for messages sent to the kernel
+        */
+       int seq;
+       
+};
+
+
+typedef struct ipsec_dev_t ipsec_dev_t;
+
+/**
+ * ipsec device
+ */
+struct ipsec_dev_t {
+       /** name of the virtual ipsec interface */
+       char name[IFNAMSIZ];
+       
+       /** name of the physical interface */
+       char phys_name[IFNAMSIZ];
+       
+       /** by how many CHILD_SA's this ipsec device is used */
+       u_int refcount;
+};
+
+/**
+ * compare the given name with the virtual device name
+ */
+static inline bool ipsec_dev_match_byname(ipsec_dev_t *current, char *name)
+{
+       return name && streq(current->name, name);
+}
+
+/**
+ * compare the given name with the physical device name
+ */
+static inline bool ipsec_dev_match_byphys(ipsec_dev_t *current, char *name)
+{
+       return name && streq(current->phys_name, name);
+}
+
+/**
+ * matches free ipsec devices
+ */
+static inline bool ipsec_dev_match_free(ipsec_dev_t *current)
+{
+       return current->refcount == 0;
+}
+
+/**
+ * tries to find an ipsec_dev_t object by name
+ */
+static status_t find_ipsec_dev(private_kernel_klips_ipsec_t *this, char *name,
+                                                          ipsec_dev_t **dev)
+{
+       linked_list_match_t match = (linked_list_match_t)(IS_IPSEC_DEV(name) ?
+                                                               ipsec_dev_match_byname : ipsec_dev_match_byphys);
+       return this->ipsec_devices->find_first(this->ipsec_devices, match,
+                                                                                               (void**)dev, name);
+}
+
+/**
+ * attach an ipsec device to a physical interface
+ */
+static status_t attach_ipsec_dev(char* name, char *phys_name)
+{
+       int sock;
+       struct ifreq req;
+       struct ipsectunnelconf *itc = (struct ipsectunnelconf*)&req.ifr_data;
+       short phys_flags;
+       int mtu;
+       
+       DBG2(DBG_KNL, "attaching virtual interface %s to %s", name, phys_name);
+       
+       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) <= 0)
+       {
+               return FAILED;
+       }
+       
+       strncpy(req.ifr_name, phys_name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFFLAGS, &req) < 0)
+       {
+               close(sock);
+               return FAILED;
+       }
+       phys_flags = req.ifr_flags;
+
+       strncpy(req.ifr_name, name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFFLAGS, &req) < 0)
+       {
+               close(sock);
+               return FAILED;
+       }
+       
+       if (req.ifr_flags & IFF_UP)
+       {
+               /* if it's already up, it is already attached, detach it first */
+               ioctl(sock, IPSEC_DEL_DEV, &req);
+       }
+       
+       /* attach it */
+       strncpy(req.ifr_name, name, IFNAMSIZ);
+       strncpy(itc->cf_name, phys_name, sizeof(itc->cf_name));
+       ioctl(sock, IPSEC_SET_DEV, &req);
+       
+       /* copy address from physical to virtual */
+       strncpy(req.ifr_name, phys_name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFADDR, &req) == 0)
+       {
+               strncpy(req.ifr_name, name, IFNAMSIZ);
+               ioctl(sock, SIOCSIFADDR, &req);
+       }
+       
+       /* copy net mask from physical to virtual */
+       strncpy(req.ifr_name, phys_name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFNETMASK, &req) == 0)
+       {
+               strncpy(req.ifr_name, name, IFNAMSIZ);
+               ioctl(sock, SIOCSIFNETMASK, &req);
+       }
+       
+       /* copy other flags and addresses */
+       strncpy(req.ifr_name, name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
+       {
+               if (phys_flags & IFF_POINTOPOINT)
+               {
+                       req.ifr_flags |= IFF_POINTOPOINT;
+                       req.ifr_flags &= ~IFF_BROADCAST;
+                       ioctl(sock, SIOCSIFFLAGS, &req);
+                       
+                       strncpy(req.ifr_name, phys_name, IFNAMSIZ);
+                       if (ioctl(sock, SIOCGIFDSTADDR, &req) == 0)
+                       {
+                               strncpy(req.ifr_name, name, IFNAMSIZ);
+                               ioctl(sock, SIOCSIFDSTADDR, &req);
+                       }
+               }
+               else if (phys_flags & IFF_BROADCAST)
+               {
+                       req.ifr_flags &= ~IFF_POINTOPOINT;
+                       req.ifr_flags |= IFF_BROADCAST;
+                       ioctl(sock, SIOCSIFFLAGS, &req);
+                       
+                       strncpy(req.ifr_name, phys_name, IFNAMSIZ);
+                       if (ioctl(sock, SIOCGIFBRDADDR, &req)==0)
+                       {
+                               strncpy(req.ifr_name, name, IFNAMSIZ);
+                               ioctl(sock, SIOCSIFBRDADDR, &req);
+                       }
+               }
+               else
+               {
+                       req.ifr_flags &= ~IFF_POINTOPOINT;
+                       req.ifr_flags &= ~IFF_BROADCAST;
+                       ioctl(sock, SIOCSIFFLAGS, &req);
+               }
+       }
+
+       mtu = lib->settings->get_int(lib->settings,
+                                               "charon.plugins.kernel_klips.ipsec_dev_mtu", 0);
+       if (mtu <= 0)
+       {
+               /* guess MTU as physical MTU - ESP overhead [- NAT-T overhead]
+                * ESP overhead      : 73 bytes
+                * NAT-T overhead    :  8 bytes ==> 81 bytes
+                * 
+                * assuming tunnel mode with AES encryption and integrity
+                * outer IP header  : 20 bytes
+                * (NAT-T UDP header:  8 bytes)
+                * ESP header       :  8 bytes
+                * IV               : 16 bytes
+                * padding          : 15 bytes (worst-case)
+                * pad len / NH     :  2 bytes
+                * auth data        : 12 bytes
+                */
+               strncpy(req.ifr_name, phys_name, IFNAMSIZ);
+               ioctl(sock, SIOCGIFMTU, &req);
+               mtu = req.ifr_mtu - 81;
+       }
+       
+       /* set MTU */
+       strncpy(req.ifr_name, name, IFNAMSIZ);
+       req.ifr_mtu = mtu;
+       ioctl(sock, SIOCSIFMTU, &req);
+       
+       /* bring ipsec device UP */
+       if (ioctl(sock, SIOCGIFFLAGS, &req) == 0)
+       {
+               req.ifr_flags |= IFF_UP;
+               ioctl(sock, SIOCSIFFLAGS, &req);
+       }
+       
+       close(sock);
+       return SUCCESS;
+}
+
+/**
+ * detach an ipsec device from a physical interface
+ */
+static status_t detach_ipsec_dev(char* name, char *phys_name)
+{
+       int sock;
+       struct ifreq req;
+       
+       DBG2(DBG_KNL, "detaching virtual interface %s from %s", name,
+                       strlen(phys_name) ? phys_name : "any physical interface");
+       
+       if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) <= 0)
+       {
+               return FAILED;
+       }
+       
+       strncpy(req.ifr_name, name, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFFLAGS, &req) < 0)
+       {
+               close(sock);
+               return FAILED;
+       }
+       
+       /* shutting interface down */
+       if (req.ifr_flags & IFF_UP)
+       {
+               req.ifr_flags &= ~IFF_UP;
+               ioctl(sock, SIOCSIFFLAGS, &req);
+       }
+       
+       /* unset address */
+       memset(&req.ifr_addr, 0, sizeof(req.ifr_addr));
+       req.ifr_addr.sa_family = AF_INET;
+       ioctl(sock, SIOCSIFADDR, &req);
+       
+       /* detach interface */
+       ioctl(sock, IPSEC_DEL_DEV, &req);
+       
+       close(sock);
+       return SUCCESS;
+}
+
+/**
+ * destroy an ipsec_dev_t object
+ */
+static void ipsec_dev_destroy(ipsec_dev_t *this)
+{
+       detach_ipsec_dev(this->name, this->phys_name);
+       free(this);
+}
+
+
+typedef struct route_entry_t route_entry_t;
+
+/**
+ * installed routing entry
+ */
+struct route_entry_t {
+       /** Name of the interface the route is bound to */
+       char *if_name;
+       
+       /** Source ip of the route */
+       host_t *src_ip;
+       
+       /** Gateway for this route */
+       host_t *gateway;
+
+       /** Destination net */
+       chunk_t dst_net;
+
+       /** Destination net prefixlen */
+       u_int8_t prefixlen;
+};
+
+/**
+ * destroy an route_entry_t object
+ */
+static void route_entry_destroy(route_entry_t *this)
+{
+       free(this->if_name);
+       this->src_ip->destroy(this->src_ip);
+       this->gateway->destroy(this->gateway);
+       chunk_free(&this->dst_net);
+       free(this);
+}
+
+typedef struct policy_entry_t policy_entry_t;
+
+/**
+ * installed kernel policy.
+ */
+struct policy_entry_t {
+       
+       /** reqid of this policy, if setup as trap */
+       u_int32_t reqid;
+       
+       /** direction of this policy: in, out, forward */
+       u_int8_t direction;
+       
+       /** parameters of installed policy */
+       struct {
+               /** subnet and port */
+               host_t *net;
+               /** subnet mask */
+               u_int8_t mask;
+               /** protocol */
+               u_int8_t proto;
+       } src, dst;
+       
+       /** associated route installed for this policy */
+       route_entry_t *route;
+       
+       /** by how many CHILD_SA's this policy is actively used */
+       u_int activecount;
+       
+       /** by how many CHILD_SA's this policy is trapped */
+       u_int trapcount;
+};
+
+/**
+ * convert a numerical netmask to a host_t
+ */
+static host_t *mask2host(int family, u_int8_t mask)
+{
+       static const u_char bitmask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+       chunk_t chunk = chunk_alloca(family == AF_INET ? 4 : 16);
+       int bytes = mask / 8, bits = mask % 8;
+       memset(chunk.ptr, 0xFF, bytes);
+       memset(chunk.ptr + bytes, 0, chunk.len - bytes);
+       if (bits)
+       {
+               chunk.ptr[bytes] =  bitmask[bits];
+       }
+       return host_create_from_chunk(family, chunk, 0);
+}
+
+/**
+ * check if a host is in a subnet (host with netmask in bits)
+ */
+static bool is_host_in_net(host_t *host, host_t *net, u_int8_t mask)
+{
+       static const u_char bitmask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+       chunk_t host_chunk, net_chunk;
+       int bytes = mask / 8, bits = mask % 8;
+       
+       host_chunk = host->get_address(host);
+       net_chunk = net->get_address(net);
+       
+       if (host_chunk.len != net_chunk.len)
+       {
+               return FALSE;
+       }
+       
+       if (memeq(host_chunk.ptr, net_chunk.ptr, bytes))
+       {
+               return (bits == 0) ||
+                          (host_chunk.ptr[bytes] & bitmask[bits]) == 
+                                  (net_chunk.ptr[bytes] & bitmask[bits]);
+       }
+       
+       return FALSE;
+}
+
+/**
+ * create a policy_entry_t object
+ */
+static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts,
+               traffic_selector_t *dst_ts, policy_dir_t dir)
+{
+       policy_entry_t *policy = malloc_thing(policy_entry_t);
+       policy->reqid = 0;
+       policy->direction = dir;
+       policy->route = NULL;
+       policy->activecount = 0;
+       policy->trapcount = 0;
+       
+       src_ts->to_subnet(src_ts, &policy->src.net, &policy->src.mask);
+       dst_ts->to_subnet(dst_ts, &policy->dst.net, &policy->dst.mask);
+       
+       /* src or dest proto may be "any" (0), use more restrictive one */
+       policy->src.proto = max(src_ts->get_protocol(src_ts), dst_ts->get_protocol(dst_ts));
+       policy->src.proto = policy->src.proto ? policy->src.proto : 0; 
+       policy->dst.proto = policy->src.proto;
+       
+       return policy;
+}
+
+/**
+ * destroy a policy_entry_t object
+ */
+static void policy_entry_destroy(policy_entry_t *this)
+{
+       DESTROY_IF(this->src.net);
+       DESTROY_IF(this->dst.net);
+       if (this->route)
+       {
+               route_entry_destroy(this->route);
+       }
+       free(this);
+}
+
+/**
+ * compares two policy_entry_t
+ */
+static inline bool policy_entry_equals(policy_entry_t *current, policy_entry_t *policy)
+{
+       return current->direction == policy->direction &&
+                  current->src.proto == policy->src.proto &&
+                  current->dst.proto == policy->dst.proto &&
+                  current->src.mask == policy->src.mask &&
+                  current->dst.mask == policy->dst.mask &&
+                  current->src.net->equals(current->src.net, policy->src.net) &&
+                  current->dst.net->equals(current->dst.net, policy->dst.net);
+}
+
+static inline bool policy_entry_match_byaddrs(policy_entry_t *current, host_t *src,
+               host_t *dst)
+{
+       return is_host_in_net(src, current->src.net, current->src.mask) &&
+                       is_host_in_net(dst, current->dst.net, current->dst.mask);
+}
+
+typedef struct sa_entry_t sa_entry_t;
+
+/**
+ * used for two things:
+ * - allocated SPIs that have not yet resulted in an installed SA
+ * - installed inbound SAs with enabled UDP encapsulation
+ */
+struct sa_entry_t {
+       
+       /** protocol of this SA */
+       protocol_id_t protocol;
+       
+       /** reqid of this SA */
+       u_int32_t reqid;
+       
+       /** SPI of this SA */
+       u_int32_t spi;
+       
+       /** src address of this SA */
+       host_t *src;
+       
+       /** dst address of this SA */
+       host_t *dst;
+       
+       /** TRUE if this SA uses UDP encapsulation */
+       bool encap;
+       
+       /** TRUE if this SA is inbound */
+       bool inbound;
+};
+
+/**
+ * create an sa_entry_t object
+ */
+static sa_entry_t *create_sa_entry(protocol_id_t protocol, u_int32_t spi,
+                                                                  u_int32_t reqid, host_t *src, host_t *dst,
+                                                                  bool encap, bool inbound)
+{
+       sa_entry_t *sa = malloc_thing(sa_entry_t);
+       sa->protocol = protocol;
+       sa->reqid = reqid;
+       sa->spi = spi;
+       sa->src = src ? src->clone(src) : NULL;
+       sa->dst = dst ? dst->clone(dst) : NULL;
+       sa->encap = encap;
+       sa->inbound = inbound;
+       return sa;
+}
+
+/**
+ * destroy an sa_entry_t object
+ */
+static void sa_entry_destroy(sa_entry_t *this)
+{
+       DESTROY_IF(this->src);
+       DESTROY_IF(this->dst);
+       free(this);
+}
+
+/**
+ * match an sa_entry_t for an inbound SA that uses UDP encapsulation by spi and src (remote) address
+ */
+static inline bool sa_entry_match_encapbysrc(sa_entry_t *current, u_int32_t *spi,
+               host_t *src)
+{
+       return current->encap && current->inbound &&
+                  current->spi == *spi && src->ip_equals(src, current->src);
+}
+
+/**
+ * match an sa_entry_t by protocol, spi and dst address (as the kernel does it)
+ */
+static inline bool sa_entry_match_bydst(sa_entry_t *current, protocol_id_t *protocol,
+               u_int32_t *spi, host_t *dst)
+{
+       return current->protocol == *protocol && current->spi == *spi && dst->ip_equals(dst, current->dst);
+}
+
+/**
+ * match an sa_entry_t by protocol, reqid and spi
+ */
+static inline bool sa_entry_match_byid(sa_entry_t *current, protocol_id_t *protocol,
+               u_int32_t *spi, u_int32_t *reqid)
+{
+       return current->protocol == *protocol && current->spi == *spi && current->reqid == *reqid;
+}
+
+typedef struct pfkey_msg_t pfkey_msg_t;
+
+struct pfkey_msg_t
+{
+       /**
+        * PF_KEY message base
+        */
+       struct sadb_msg *msg;
+       
+       
+       /**
+        * PF_KEY message extensions
+        */
+       union {
+               struct sadb_ext *ext[SADB_EXT_MAX + 1];
+               struct {
+                       struct sadb_ext *reserved;                              /* SADB_EXT_RESERVED */
+                       struct sadb_sa *sa;                                             /* SADB_EXT_SA */
+                       struct sadb_lifetime *lft_current;              /* SADB_EXT_LIFETIME_CURRENT */
+                       struct sadb_lifetime *lft_hard;                 /* SADB_EXT_LIFETIME_HARD */
+                       struct sadb_lifetime *lft_soft;                 /* SADB_EXT_LIFETIME_SOFT */
+                       struct sadb_address *src;                               /* SADB_EXT_ADDRESS_SRC */
+                       struct sadb_address *dst;                               /* SADB_EXT_ADDRESS_DST */
+                       struct sadb_address *proxy;                             /* SADB_EXT_ADDRESS_PROXY */
+                       struct sadb_key *key_auth;                              /* SADB_EXT_KEY_AUTH */
+                       struct sadb_key *key_encr;                              /* SADB_EXT_KEY_ENCRYPT */
+                       struct sadb_ident *id_src;                              /* SADB_EXT_IDENTITY_SRC */
+                       struct sadb_ident *id_dst;                              /* SADB_EXT_IDENTITY_DST */
+                       struct sadb_sens *sensitivity;                  /* SADB_EXT_SENSITIVITY */
+                       struct sadb_prop *proposal;                             /* SADB_EXT_PROPOSAL */
+                       struct sadb_supported *supported_auth;  /* SADB_EXT_SUPPORTED_AUTH */
+                       struct sadb_supported *supported_encr;  /* SADB_EXT_SUPPORTED_ENCRYPT */
+                       struct sadb_spirange *spirange;                 /* SADB_EXT_SPIRANGE */
+                       struct sadb_x_kmprivate *x_kmprivate;   /* SADB_X_EXT_KMPRIVATE */
+                       struct sadb_ext *x_policy;                              /* SADB_X_EXT_SATYPE2 */
+                       struct sadb_ext *x_sa2;                                 /* SADB_X_EXT_SA2 */
+                       struct sadb_address *x_dst2;                    /* SADB_X_EXT_ADDRESS_DST2 */
+                       struct sadb_address *x_src_flow;                /* SADB_X_EXT_ADDRESS_SRC_FLOW */
+                       struct sadb_address *x_dst_flow;                /* SADB_X_EXT_ADDRESS_DST_FLOW */
+                       struct sadb_address *x_src_mask;                /* SADB_X_EXT_ADDRESS_SRC_MASK */
+                       struct sadb_address *x_dst_mask;                /* SADB_X_EXT_ADDRESS_DST_MASK */
+                       struct sadb_x_debug *x_debug;                   /* SADB_X_EXT_DEBUG */
+                       struct sadb_protocol *x_protocol;               /* SADB_X_EXT_PROTOCOL */
+                       struct sadb_x_nat_t_type *x_natt_type;  /* SADB_X_EXT_NAT_T_TYPE */
+                       struct sadb_x_nat_t_port *x_natt_sport; /* SADB_X_EXT_NAT_T_SPORT */
+                       struct sadb_x_nat_t_port *x_natt_dport; /* SADB_X_EXT_NAT_T_DPORT */
+                       struct sadb_address *x_natt_oa;                 /* SADB_X_EXT_NAT_T_OA */
+               } __attribute__((__packed__));
+       };
+};
+
+/**
+ * convert a IKEv2 specific protocol identifier to the PF_KEY sa type
+ */
+static u_int8_t proto_ike2satype(protocol_id_t proto)
+{
+       switch (proto)
+       {
+               case PROTO_ESP:
+                       return SADB_SATYPE_ESP;
+               case PROTO_AH:
+                       return SADB_SATYPE_AH;
+               case IPPROTO_COMP:
+                       return SADB_X_SATYPE_COMP;
+               default:
+                       return proto;
+       }
+}
+
+/**
+ * convert a PF_KEY sa type to a IKEv2 specific protocol identifier
+ */
+static protocol_id_t proto_satype2ike(u_int8_t proto)
+{
+       switch (proto)
+       {
+               case SADB_SATYPE_ESP:
+                       return PROTO_ESP;
+               case SADB_SATYPE_AH:
+                       return PROTO_AH;
+               case SADB_X_SATYPE_COMP:
+                       return IPPROTO_COMP;
+               default:
+                       return proto;
+       }
+}
+
+typedef struct kernel_algorithm_t kernel_algorithm_t;
+
+/**
+ * Mapping of IKEv2 algorithms to PF_KEY algorithms
+ */
+struct kernel_algorithm_t {
+       /**
+        * Identifier specified in IKEv2
+        */
+       int ikev2;
+       
+       /**
+        * Identifier as defined in pfkeyv2.h
+        */
+       int kernel;
+};
+
+#define END_OF_LIST -1
+
+/**
+ * Algorithms for encryption
+ */
+static kernel_algorithm_t encryption_algs[] = {
+/*     {ENCR_DES_IV64,                         0                                                       }, */
+       {ENCR_DES,                                      SADB_EALG_DESCBC                        },
+       {ENCR_3DES,                             SADB_EALG_3DESCBC                       },
+/*     {ENCR_RC5,                                      0                                                       }, */
+/*     {ENCR_IDEA,                             0                                                       }, */
+/*     {ENCR_CAST,                             0                                                       }, */
+       {ENCR_BLOWFISH,                         SADB_EALG_BFCBC                         },
+/*     {ENCR_3IDEA,                            0                                                       }, */
+/*     {ENCR_DES_IV32,                         0                                                       }, */
+       {ENCR_NULL,                             SADB_EALG_NULL                          },
+       {ENCR_AES_CBC,                          SADB_EALG_AESCBC                        },
+/*     {ENCR_AES_CTR,                          0                                                       }, */
+/*     {ENCR_AES_CCM_ICV8,                     0                                                       }, */
+/*     {ENCR_AES_CCM_ICV12,            0                                                       }, */
+/*     {ENCR_AES_CCM_ICV16,            0                                                       }, */
+/*     {ENCR_AES_GCM_ICV8,                     0                                                       }, */
+/*     {ENCR_AES_GCM_ICV12,            0                                                       }, */
+/*     {ENCR_AES_GCM_ICV16,            0                                                       }, */
+       {END_OF_LIST,                           0                                                       },
+};
+
+/**
+ * Algorithms for integrity protection
+ */
+static kernel_algorithm_t integrity_algs[] = {
+       {AUTH_HMAC_MD5_96,                      SADB_AALG_MD5HMAC                       },
+       {AUTH_HMAC_SHA1_96,                     SADB_AALG_SHA1HMAC                      },
+       {AUTH_HMAC_SHA2_256_128,        SADB_AALG_SHA256_HMAC           },
+       {AUTH_HMAC_SHA2_384_192,        SADB_AALG_SHA384_HMAC           },
+       {AUTH_HMAC_SHA2_512_256,        SADB_AALG_SHA512_HMAC           },
+/*     {AUTH_DES_MAC,                          0,                                                      }, */
+/*     {AUTH_KPDK_MD5,                         0,                                                      }, */
+/*     {AUTH_AES_XCBC_96,                      0,                                                      }, */
+       {END_OF_LIST,                           0,                                                      },
+};
+
+/**
+ * Algorithms for IPComp
+ */
+static kernel_algorithm_t compression_algs[] = {
+/*     {IPCOMP_OUI,                            0                                                       }, */
+       {IPCOMP_DEFLATE,                        SADB_X_CALG_DEFLATE                     },
+       {IPCOMP_LZS,                            SADB_X_CALG_LZS                         },
+/*     {IPCOMP_LZJH,                           0                                                       }, */
+       {END_OF_LIST,                           0                                                       },
+};
+
+/**
+ * Look up a kernel algorithm ID and its key size
+ */
+static int lookup_algorithm(kernel_algorithm_t *list, int ikev2)
+{
+       while (list->ikev2 != END_OF_LIST)
+       {
+               if (ikev2 == list->ikev2)
+               {
+                       return list->kernel;
+               }
+               list++;
+       }
+       return 0;
+}
+
+/**
+ * add a host behind a sadb_address extension
+ */
+static void host2ext(host_t *host, struct sadb_address *ext)
+{
+       sockaddr_t *host_addr = host->get_sockaddr(host);
+       socklen_t *len = host->get_sockaddr_len(host);
+       memcpy((char*)(ext + 1), host_addr, *len);
+       ext->sadb_address_len = PFKEY_LEN(sizeof(*ext) + *len);
+}
+
+/**
+ * add a host behind a sadb_address extension
+ */
+static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type)
+{
+       struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = type;
+       host2ext(host, addr);
+       PFKEY_EXT_ADD(msg, addr);
+}
+
+/**
+ * adds an empty address extension to the given sadb_msg
+ */
+static void add_anyaddr_ext(struct sadb_msg *msg, int family, u_int8_t type)
+{
+       socklen_t len = (family == AF_INET) ? sizeof(struct sockaddr_in) :
+                                                                                 sizeof(struct sockaddr_in6);
+       struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = type;
+       sockaddr_t *saddr = (sockaddr_t*)(addr + 1);
+       saddr->sa_family = family;
+       addr->sadb_address_len = PFKEY_LEN(sizeof(*addr) + len);
+       PFKEY_EXT_ADD(msg, addr);
+}
+
+/**
+ * add udp encap extensions to a sadb_msg
+ */
+static void add_encap_ext(struct sadb_msg *msg, host_t *src, host_t *dst,
+                                                       bool ports_only)
+{
+       struct sadb_x_nat_t_type* nat_type;
+       struct sadb_x_nat_t_port* nat_port;
+       
+       if (!ports_only)
+       {
+               nat_type = (struct sadb_x_nat_t_type*)PFKEY_EXT_ADD_NEXT(msg);
+               nat_type->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+               nat_type->sadb_x_nat_t_type_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_type));
+               nat_type->sadb_x_nat_t_type_type = UDP_ENCAP_ESPINUDP;
+               PFKEY_EXT_ADD(msg, nat_type);
+       }
+       
+       nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg);
+       nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT;
+       nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_port));
+       nat_port->sadb_x_nat_t_port_port = src->get_port(src);
+       PFKEY_EXT_ADD(msg, nat_port);
+       
+       nat_port = (struct sadb_x_nat_t_port*)PFKEY_EXT_ADD_NEXT(msg);
+       nat_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT;
+       nat_port->sadb_x_nat_t_port_len = PFKEY_LEN(sizeof(struct sadb_x_nat_t_port));
+       nat_port->sadb_x_nat_t_port_port = dst->get_port(dst);
+       PFKEY_EXT_ADD(msg, nat_port);
+}
+
+/**
+ * build an SADB_X_ADDFLOW msg
+ */
+static void build_addflow(struct sadb_msg *msg, u_int8_t satype, u_int32_t spi,
+               host_t *src, host_t *dst, host_t *src_net, u_int8_t src_mask,
+               host_t *dst_net, u_int8_t dst_mask, u_int8_t protocol, bool replace)
+{
+       struct sadb_sa *sa;
+       struct sadb_protocol *proto;
+       host_t *host;
+       
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_X_ADDFLOW;
+       msg->sadb_msg_satype = satype;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_EXT_SA;
+       sa->sadb_sa_spi = spi;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_flags = replace ? SADB_X_SAFLAGS_REPLACEFLOW : 0;
+       PFKEY_EXT_ADD(msg, sa);
+       
+       if (!src)
+       {
+               add_anyaddr_ext(msg, src_net->get_family(src_net), SADB_EXT_ADDRESS_SRC);
+       }
+       else
+       {
+               add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC);
+       }
+       
+       if (!dst)
+       {
+               add_anyaddr_ext(msg, dst_net->get_family(dst_net), SADB_EXT_ADDRESS_DST);
+       }
+       else
+       {
+               add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST);
+       }
+       
+       add_addr_ext(msg, src_net, SADB_X_EXT_ADDRESS_SRC_FLOW);
+       add_addr_ext(msg, dst_net, SADB_X_EXT_ADDRESS_DST_FLOW);
+       
+       host = mask2host(src_net->get_family(src_net), src_mask);
+       add_addr_ext(msg, host, SADB_X_EXT_ADDRESS_SRC_MASK);
+       host->destroy(host);
+       
+       host = mask2host(dst_net->get_family(dst_net), dst_mask);
+       add_addr_ext(msg, host, SADB_X_EXT_ADDRESS_DST_MASK);
+       host->destroy(host);
+       
+       proto = (struct sadb_protocol*)PFKEY_EXT_ADD_NEXT(msg);
+       proto->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+       proto->sadb_protocol_len = PFKEY_LEN(sizeof(struct sadb_protocol));
+       proto->sadb_protocol_proto = protocol;
+       PFKEY_EXT_ADD(msg, proto);
+}
+
+/**
+ * build an SADB_X_DELFLOW msg
+ */
+static void build_delflow(struct sadb_msg *msg, u_int8_t satype,
+               host_t *src_net, u_int8_t src_mask, host_t *dst_net, u_int8_t dst_mask,
+               u_int8_t protocol)
+{
+       struct sadb_protocol *proto;
+       host_t *host;
+       
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_X_DELFLOW;
+       msg->sadb_msg_satype = satype;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       add_addr_ext(msg, src_net, SADB_X_EXT_ADDRESS_SRC_FLOW);
+       add_addr_ext(msg, dst_net, SADB_X_EXT_ADDRESS_DST_FLOW);
+       
+       host = mask2host(src_net->get_family(src_net),
+                                        src_mask);
+       add_addr_ext(msg, host, SADB_X_EXT_ADDRESS_SRC_MASK);
+       host->destroy(host);
+       
+       host = mask2host(dst_net->get_family(dst_net),
+                                        dst_mask);
+       add_addr_ext(msg, host, SADB_X_EXT_ADDRESS_DST_MASK);
+       host->destroy(host);
+       
+       proto = (struct sadb_protocol*)PFKEY_EXT_ADD_NEXT(msg);
+       proto->sadb_protocol_exttype = SADB_X_EXT_PROTOCOL;
+       proto->sadb_protocol_len = PFKEY_LEN(sizeof(struct sadb_protocol));
+       proto->sadb_protocol_proto = protocol;
+       PFKEY_EXT_ADD(msg, proto);
+}
+
+/**
+ * Parses a pfkey message received from the kernel
+ */
+static status_t parse_pfkey_message(struct sadb_msg *msg, pfkey_msg_t *out)
+{
+       struct sadb_ext* ext;
+       size_t len;
+       
+       memset(out, 0, sizeof(pfkey_msg_t));
+       out->msg = msg;
+       
+       len = msg->sadb_msg_len;
+       len -= PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       ext = (struct sadb_ext*)(((char*)msg) + sizeof(struct sadb_msg));
+       
+       while (len >= PFKEY_LEN(sizeof(struct sadb_ext)))
+       {
+               if (ext->sadb_ext_len < PFKEY_LEN(sizeof(struct sadb_ext)) ||
+                       ext->sadb_ext_len > len)
+               {
+                       DBG1(DBG_KNL, "length of PF_KEY extension (%d) is invalid", ext->sadb_ext_type);
+                       break;
+               }
+               
+               if ((ext->sadb_ext_type > SADB_EXT_MAX) || (!ext->sadb_ext_type))
+               {
+                       DBG1(DBG_KNL, "type of PF_KEY extension (%d) is invalid", ext->sadb_ext_type);
+                       break;
+               }
+               
+               if (out->ext[ext->sadb_ext_type])
+               {
+                       DBG1(DBG_KNL, "duplicate PF_KEY extension of type (%d)", ext->sadb_ext_type);                   
+                       break;
+               }
+               
+               out->ext[ext->sadb_ext_type] = ext;
+               ext = PFKEY_EXT_NEXT_LEN(ext, len);
+       }
+
+       if (len)
+       {
+               DBG1(DBG_KNL, "PF_KEY message length is invalid");
+               return FAILED;
+       }
+       
+       return SUCCESS;
+}
+
+/**
+ * Send a message to a specific PF_KEY socket and handle the response.
+ */
+static status_t pfkey_send_socket(private_kernel_klips_ipsec_t *this, int socket,
+                                       struct sadb_msg *in, struct sadb_msg **out, size_t *out_len)
+{
+       unsigned char buf[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg;
+       int in_len, len;
+       
+       this->mutex_pfkey->lock(this->mutex_pfkey);
+
+       in->sadb_msg_seq = ++this->seq;
+       in->sadb_msg_pid = getpid();
+
+       in_len = PFKEY_USER_LEN(in->sadb_msg_len);
+
+       while (TRUE)
+       {
+               len = send(socket, in, in_len, 0);
+
+               if (len != in_len)
+               {
+                       switch (errno)
+                       {
+                               case EINTR:
+                                       /* interrupted, try again */
+                                       continue;
+                               case EINVAL:
+                               case EEXIST:
+                               case ESRCH:
+                                       /* we should also get a response for these from KLIPS */
+                                       break;
+                               default:
+                                       this->mutex_pfkey->unlock(this->mutex_pfkey);
+                                       DBG1(DBG_KNL, "error sending to PF_KEY socket: %s (%d)",
+                                                       strerror(errno), errno);
+                                       return FAILED;
+                       }
+               }
+               break;
+       }
+       
+       while (TRUE)
+       {       
+               msg = (struct sadb_msg*)buf;
+               
+               len = recv(socket, buf, sizeof(buf), 0);
+               
+               if (len < 0)
+               {
+                       if (errno == EINTR)
+                       {
+                               DBG1(DBG_KNL, "got interrupted");
+                               /* interrupted, try again */
+                               continue;
+                       }
+                       this->mutex_pfkey->unlock(this->mutex_pfkey);
+                       DBG1(DBG_KNL, "error reading from PF_KEY socket: %s", strerror(errno));
+                       return FAILED;
+               }
+               if (len < sizeof(struct sadb_msg) ||
+                       msg->sadb_msg_len < PFKEY_LEN(sizeof(struct sadb_msg)))
+               {
+                       this->mutex_pfkey->unlock(this->mutex_pfkey);
+                       DBG1(DBG_KNL, "received corrupted PF_KEY message");
+                       return FAILED;
+               }
+               if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT)
+               {
+                       this->mutex_pfkey->unlock(this->mutex_pfkey);
+                       DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message");
+                       return FAILED;
+               }
+               if (msg->sadb_msg_pid != in->sadb_msg_pid)
+               {
+                       DBG2(DBG_KNL, "received PF_KEY message is not intended for us");
+                       continue;
+               }
+               if (msg->sadb_msg_seq != this->seq)
+               {
+                       DBG1(DBG_KNL, "received PF_KEY message with invalid sequence number,"
+                                       " was %d expected %d", msg->sadb_msg_seq, this->seq);
+                       if (msg->sadb_msg_seq < this->seq)
+                       {
+                               continue;
+                       }
+                       this->mutex_pfkey->unlock(this->mutex_pfkey);
+                       return FAILED;
+               }
+               if (msg->sadb_msg_type != in->sadb_msg_type)
+               {
+                       DBG2(DBG_KNL, "received PF_KEY message of wrong type,"
+                                       " was %d expected %d, ignoring",
+                                       msg->sadb_msg_type, in->sadb_msg_type);
+               }
+               break;
+       }
+       
+       *out_len = len;
+       *out = (struct sadb_msg*)malloc(len);
+       memcpy(*out, buf, len);
+               
+       this->mutex_pfkey->unlock(this->mutex_pfkey);
+       
+       return SUCCESS;
+}
+
+/**
+ * Send a message to the default PF_KEY socket.
+ */
+static status_t pfkey_send(private_kernel_klips_ipsec_t *this,
+                                       struct sadb_msg *in, struct sadb_msg **out, size_t *out_len)
+{
+       return pfkey_send_socket(this, this->socket, in, out, out_len);
+}
+
+/**
+ * Send a message to the default PF_KEY socket and handle the response.
+ */
+static status_t pfkey_send_ack(private_kernel_klips_ipsec_t *this, struct sadb_msg *in)
+{
+       struct sadb_msg *out;
+       size_t len;
+       
+       if (pfkey_send(this, in, &out, &len) != SUCCESS)
+       {
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "PF_KEY error: %s (%d)",
+                                          strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       free(out);
+       return SUCCESS;
+}
+
+/**
+ * Add an eroute to KLIPS
+ */
+static status_t add_eroute(private_kernel_klips_ipsec_t *this, u_int8_t satype,
+               u_int32_t spi, host_t *src, host_t *dst, host_t *src_net, u_int8_t src_mask,
+               host_t *dst_net, u_int8_t dst_mask, u_int8_t protocol, bool replace)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg = (struct sadb_msg*)request;
+       
+       memset(&request, 0, sizeof(request));
+       
+       build_addflow(msg, satype, spi, src, dst, src_net, src_mask,
+                       dst_net, dst_mask, protocol, replace);
+       
+       return pfkey_send_ack(this, msg);
+}
+
+/**
+ * Delete an eroute fom KLIPS
+ */
+static status_t del_eroute(private_kernel_klips_ipsec_t *this, u_int8_t satype,
+               host_t *src_net, u_int8_t src_mask, host_t *dst_net, u_int8_t dst_mask,
+               u_int8_t protocol)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg = (struct sadb_msg*)request;
+       
+       memset(&request, 0, sizeof(request));
+       
+       build_delflow(msg, satype, src_net, src_mask, dst_net, dst_mask, protocol);
+       
+       return pfkey_send_ack(this, msg);
+}
+
+/**
+ * Process a SADB_ACQUIRE message from the kernel
+ */
+static void process_acquire(private_kernel_klips_ipsec_t *this, struct sadb_msg* msg)
+{
+       pfkey_msg_t response;
+       host_t *src, *dst;
+       u_int32_t reqid;
+       u_int8_t proto;
+       policy_entry_t *policy;
+       job_t *job;
+       
+       switch (msg->sadb_msg_satype)
+       {
+               case SADB_SATYPE_UNSPEC:
+               case SADB_SATYPE_ESP:
+               case SADB_SATYPE_AH:
+                       break;
+               default:
+                       /* acquire for AH/ESP only */
+                       return;
+       }
+       
+       if (parse_pfkey_message(msg, &response) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "parsing SADB_ACQUIRE from kernel failed");
+               return;
+       }
+       
+       /* KLIPS provides us only with the source and destination address,
+        * and the transport protocol of the packet that triggered the policy.
+        * we use this information to find a matching policy in our cache.
+        * because KLIPS installs a narrow %hold eroute covering only this information,
+        * we replace both the %trap and this %hold eroutes with a broader %hold
+        * eroute covering the whole policy */
+       src = host_create_from_sockaddr((sockaddr_t*)(response.src + 1));
+       dst = host_create_from_sockaddr((sockaddr_t*)(response.dst + 1));
+       proto = response.src->sadb_address_proto;
+       if (!src || !dst || src->get_family(src) != dst->get_family(dst))
+       {
+               DBG1(DBG_KNL, "received an SADB_ACQUIRE with invalid hosts");
+               return;
+       }
+       
+       DBG2(DBG_KNL, "received an SADB_ACQUIRE for %H == %H : %d", src, dst, proto);
+       this->mutex->lock(this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_match_byaddrs,
+                               (void**)&policy, src, dst) != SUCCESS)
+       {
+               this->mutex->unlock(this->mutex);
+               DBG1(DBG_KNL, "received an SADB_ACQUIRE, but found no matching policy");
+               return;
+       }
+       if ((reqid = policy->reqid) == 0)
+       {
+               this->mutex->unlock(this->mutex);
+               DBG1(DBG_KNL, "received an SADB_ACQUIRE, but policy is not routed anymore");
+               return;
+       }
+       
+       /* add a broad %hold eroute that replaces the %trap eroute */
+       add_eroute(this, SADB_X_SATYPE_INT, htonl(SPI_HOLD), NULL, NULL,
+                       policy->src.net, policy->src.mask, policy->dst.net, policy->dst.mask,
+                       policy->src.proto, TRUE);
+       
+       /* remove the narrow %hold eroute installed by KLIPS */
+       del_eroute(this, SADB_X_SATYPE_INT, src, 32, dst, 32, proto);
+       
+       this->mutex->unlock(this->mutex);
+       
+       DBG2(DBG_KNL, "received an SADB_ACQUIRE");
+       DBG1(DBG_KNL, "creating acquire job for CHILD_SA with reqid {%d}", reqid);
+       job = (job_t*)acquire_job_create(reqid, NULL, NULL);
+       charon->processor->queue_job(charon->processor, job);
+}
+
+/**
+ * Process a SADB_X_NAT_T_NEW_MAPPING message from the kernel
+ */
+static void process_mapping(private_kernel_klips_ipsec_t *this, struct sadb_msg* msg)
+{
+       pfkey_msg_t response;
+       u_int32_t spi, reqid;
+       host_t *old_src, *new_src;
+       job_t *job;
+       
+       DBG2(DBG_KNL, "received an SADB_X_NAT_T_NEW_MAPPING");
+       
+       if (parse_pfkey_message(msg, &response) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "parsing SADB_X_NAT_T_NEW_MAPPING from kernel failed");
+               return;
+       }
+       
+       spi = response.sa->sadb_sa_spi;
+       
+       if (proto_satype2ike(msg->sadb_msg_satype) == PROTO_ESP)
+       {
+               sa_entry_t *sa;
+               sockaddr_t *addr = (sockaddr_t*)(response.src + 1);
+               old_src = host_create_from_sockaddr(addr);
+               
+               this->mutex->lock(this->mutex);
+               if (!old_src || this->installed_sas->find_first(this->installed_sas,
+                               (linked_list_match_t)sa_entry_match_encapbysrc,
+                                       (void**)&sa, &spi, old_src) != SUCCESS)
+               {
+                       this->mutex->unlock(this->mutex);
+                       DBG1(DBG_KNL, "received an SADB_X_NAT_T_NEW_MAPPING, but found no matching SA");
+                       return;
+               }
+               reqid = sa->reqid;
+               this->mutex->unlock(this->mutex);
+               
+               addr = (sockaddr_t*)(response.dst + 1);
+               switch (addr->sa_family)
+               {
+                       case AF_INET:
+                       {
+                               struct sockaddr_in *sin = (struct sockaddr_in*)addr;
+                               sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
+                       }
+                       case AF_INET6:
+                       {
+                               struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)addr;
+                               sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);                         
+                       }
+                       default:
+                               break;
+               }
+               new_src = host_create_from_sockaddr(addr);
+               if (new_src)
+               {
+                       DBG1(DBG_KNL, "NAT mappings of ESP CHILD_SA with SPI %.8x and"
+                               " reqid {%d} changed, queuing update job", ntohl(spi), reqid);
+                       job = (job_t*)update_sa_job_create(reqid, new_src);
+                       charon->processor->queue_job(charon->processor, job);
+               }
+       }
+}
+
+/**
+ * Receives events from kernel
+ */
+static job_requeue_t receive_events(private_kernel_klips_ipsec_t *this)
+{
+       unsigned char buf[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg = (struct sadb_msg*)buf;
+       int len, oldstate;
+       
+       pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+       len = recv(this->socket_events, buf, sizeof(buf), 0);
+       pthread_setcancelstate(oldstate, NULL);
+       
+       if (len < 0)
+       {
+               switch (errno)
+               {
+                       case EINTR:
+                               /* interrupted, try again */
+                               return JOB_REQUEUE_DIRECT;
+                       case EAGAIN:
+                               /* no data ready, select again */
+                               return JOB_REQUEUE_DIRECT;
+                       default:
+                               DBG1(DBG_KNL, "unable to receive from PF_KEY event socket");
+                               sleep(1);
+                               return JOB_REQUEUE_FAIR;
+               }
+       }
+       
+       if (len < sizeof(struct sadb_msg) ||
+               msg->sadb_msg_len < PFKEY_LEN(sizeof(struct sadb_msg)))
+       {
+               DBG2(DBG_KNL, "received corrupted PF_KEY message");
+               return JOB_REQUEUE_DIRECT;
+       }
+       if (msg->sadb_msg_pid != 0)
+       {       /* not from kernel. not interested, try another one */
+               return JOB_REQUEUE_DIRECT;
+       }
+       if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT)
+       {
+               DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message");
+               return JOB_REQUEUE_DIRECT;
+       }
+       
+       switch (msg->sadb_msg_type)
+       {
+               case SADB_ACQUIRE:
+                       process_acquire(this, msg);
+                       break;
+               case SADB_EXPIRE:
+                       /* SADB_EXPIRE events in KLIPS are only triggered by traffic (even for
+                        * the time based limits). So if there is no traffic for a longer
+                        * period than configured as hard limit, we wouldn't be able to rekey
+                        * the SA and just receive the hard expire and thus delete the SA.
+                        * To avoid this behavior and to make charon behave as with the other
+                        * kernel plugins, we implement the expiration of SAs ourselves. */
+                       break;
+               case SADB_X_NAT_T_NEW_MAPPING:
+                       process_mapping(this, msg);
+                       break;
+               default:
+                       break;
+       }
+       
+       return JOB_REQUEUE_DIRECT;
+}
+
+typedef enum {
+       /** an SPI has expired */
+       EXPIRE_TYPE_SPI,
+       /** a CHILD_SA has to be rekeyed */
+       EXPIRE_TYPE_SOFT,
+       /** a CHILD_SA has to be deleted */
+       EXPIRE_TYPE_HARD
+} expire_type_t;
+
+typedef struct sa_expire_t sa_expire_t;
+
+struct sa_expire_t {
+       /** kernel interface */
+       private_kernel_klips_ipsec_t *this;
+       /** the SPI of the expiring SA */
+       u_int32_t spi;
+       /** the protocol of the expiring SA */
+       protocol_id_t protocol;
+       /** the reqid of the expiring SA*/
+       u_int32_t reqid;
+       /** what type of expire this is */
+       expire_type_t type;
+};
+
+/**
+ * Called when an SA expires
+ */
+static job_requeue_t sa_expires(sa_expire_t *expire)
+{
+       private_kernel_klips_ipsec_t *this = expire->this;
+       protocol_id_t protocol = expire->protocol;
+       u_int32_t spi = expire->spi, reqid = expire->reqid;
+       bool hard = expire->type != EXPIRE_TYPE_SOFT;
+       sa_entry_t *cached_sa;
+       linked_list_t *list;
+       job_t *job;
+       
+       /* for an expired SPI we first check whether the CHILD_SA got installed
+        * in the meantime, for expired SAs we check whether they are still installed */
+       list = expire->type == EXPIRE_TYPE_SPI ? this->allocated_spis : this->installed_sas;
+       
+       this->mutex->lock(this->mutex);
+       if (list->find_first(list, (linked_list_match_t)sa_entry_match_byid,
+                       (void**)&cached_sa, &protocol, &spi, &reqid) != SUCCESS)
+       {
+               /* we found no entry:
+                * - for SPIs, a CHILD_SA has been installed
+                * - for SAs, the CHILD_SA has already been deleted */
+               this->mutex->unlock(this->mutex);
+               return JOB_REQUEUE_NONE;
+       }
+       else
+       {
+               list->remove(list, cached_sa, NULL);
+               sa_entry_destroy(cached_sa);
+       }
+       this->mutex->unlock(this->mutex);
+       
+       DBG2(DBG_KNL, "%N CHILD_SA with SPI %.8x and reqid {%d} expired",
+                       protocol_id_names, protocol, ntohl(spi), reqid);
+       
+       DBG1(DBG_KNL, "creating %s job for %N CHILD_SA with SPI %.8x and reqid {%d}",
+                hard ? "delete" : "rekey",  protocol_id_names,
+                protocol, ntohl(spi), reqid);
+       if (hard)
+       {
+               job = (job_t*)delete_child_sa_job_create(reqid, protocol, spi);
+       }
+       else
+       {
+               job = (job_t*)rekey_child_sa_job_create(reqid, protocol, spi);
+       }
+       charon->processor->queue_job(charon->processor, job);
+       return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Schedule an expire job for an SA. Time is in seconds. 
+ */
+static void schedule_expire(private_kernel_klips_ipsec_t *this,
+                                                       protocol_id_t protocol, u_int32_t spi,
+                                                       u_int32_t reqid, expire_type_t type, u_int32_t time)
+{
+       callback_job_t *job;
+       sa_expire_t *expire = malloc_thing(sa_expire_t);
+       expire->this = this;
+       expire->protocol = protocol;
+       expire->spi = spi;
+       expire->reqid = reqid;
+       expire->type = type;
+       job = callback_job_create((callback_job_cb_t)sa_expires, expire, free, NULL);
+       charon->scheduler->schedule_job(charon->scheduler, (job_t*)job, time * 1000);
+}
+
+/**
+ * Implementation of kernel_interface_t.get_spi.
+ */
+static status_t get_spi(private_kernel_klips_ipsec_t *this, 
+                                               host_t *src, host_t *dst, 
+                                               protocol_id_t protocol, u_int32_t reqid,
+                                               u_int32_t *spi)
+{
+       /* we cannot use SADB_GETSPI because KLIPS does not allow us to set the
+        * NAT-T type in an SADB_UPDATE which we would have to use to update the
+        * implicitly created SA.
+        */
+       rng_t *rng;
+       u_int32_t spi_gen;
+       
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng)
+       {
+               DBG1(DBG_KNL, "allocating SPI failed: no RNG");
+               return FAILED;
+       }
+       rng->get_bytes(rng, sizeof(spi_gen), (void*)&spi_gen);
+       rng->destroy(rng);
+       
+       /* charon's SPIs lie within the range from 0xc0000000 to 0xcFFFFFFF */
+       spi_gen = 0xc0000000 | (spi_gen & 0x0FFFFFFF);
+       
+       DBG2(DBG_KNL, "allocated SPI %.8x for %N SA between %#H..%#H",
+                       spi_gen, protocol_id_names, protocol, src, dst);
+       
+       *spi = htonl(spi_gen);
+       
+       this->mutex->lock(this->mutex);
+       this->allocated_spis->insert_last(this->allocated_spis,
+                       create_sa_entry(protocol, *spi, reqid, NULL, NULL, FALSE, TRUE));
+       this->mutex->unlock(this->mutex);
+       schedule_expire(this, protocol, *spi, reqid, EXPIRE_TYPE_SPI, SPI_TIMEOUT);
+       
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.get_cpi.
+ */
+static status_t get_cpi(private_kernel_klips_ipsec_t *this, 
+                                               host_t *src, host_t *dst, 
+                                               u_int32_t reqid, u_int16_t *cpi)
+{
+       return FAILED;
+}
+
+/**
+ * Add a pseudo IPIP SA for tunnel mode with KLIPS.
+ */
+static status_t add_ipip_sa(private_kernel_klips_ipsec_t *this,
+                                          host_t *src, host_t *dst, u_int32_t spi, u_int32_t reqid)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       size_t len;
+       
+       memset(&request, 0, sizeof(request));
+       
+       DBG2(DBG_KNL, "adding pseudo IPIP SA with SPI %.8x and reqid {%d}", ntohl(spi), reqid);
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_ADD;
+       msg->sadb_msg_satype = SADB_X_SATYPE_IPIP;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_EXT_SA;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_spi = spi;
+       sa->sadb_sa_state = SADB_SASTATE_MATURE;
+       PFKEY_EXT_ADD(msg, sa);
+       
+       add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC);
+       add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to add pseudo IPIP SA with SPI %.8x", ntohl(spi));
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to add pseudo IPIP SA with SPI %.8x: %s (%d)",
+                               ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       
+       free(out);
+       return SUCCESS;
+}
+
+/**
+ * group the IPIP SA required for tunnel mode with the outer SA
+ */
+static status_t group_ipip_sa(private_kernel_klips_ipsec_t *this,
+                                          host_t *src, host_t *dst, u_int32_t spi,
+                                          protocol_id_t protocol, u_int32_t reqid)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       struct sadb_x_satype *satype;
+       size_t len;
+       
+       memset(&request, 0, sizeof(request));
+       
+       DBG2(DBG_KNL, "grouping SAs with SPI %.8x and reqid {%d}", ntohl(spi), reqid);
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_X_GRPSA;
+       msg->sadb_msg_satype = SADB_X_SATYPE_IPIP;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_EXT_SA;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_spi = spi;
+       sa->sadb_sa_state = SADB_SASTATE_MATURE;
+       PFKEY_EXT_ADD(msg, sa);
+       
+       add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST);
+       
+       satype = (struct sadb_x_satype*)PFKEY_EXT_ADD_NEXT(msg);
+       satype->sadb_x_satype_exttype = SADB_X_EXT_SATYPE2;
+       satype->sadb_x_satype_len = PFKEY_LEN(sizeof(struct sadb_x_satype));
+       satype->sadb_x_satype_satype = proto_ike2satype(protocol);
+       PFKEY_EXT_ADD(msg, satype);
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_X_EXT_SA2;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_spi = spi;
+       sa->sadb_sa_state = SADB_SASTATE_MATURE;
+       PFKEY_EXT_ADD(msg, sa);
+       
+       add_addr_ext(msg, dst, SADB_X_EXT_ADDRESS_DST2);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to group SAs with SPI %.8x", ntohl(spi));
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to group SAs with SPI %.8x: %s (%d)",
+                               ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       
+       free(out);
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.add_sa.
+ */
+static status_t add_sa(private_kernel_klips_ipsec_t *this,
+                                          host_t *src, host_t *dst, u_int32_t spi,
+                                          protocol_id_t protocol, u_int32_t reqid,
+                                          u_int64_t expire_soft, u_int64_t expire_hard,
+                                          u_int16_t enc_alg, chunk_t enc_key,
+                                          u_int16_t int_alg, chunk_t int_key,
+                                          ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                          bool encap, bool inbound)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       struct sadb_key *key;
+       size_t len;
+       
+       if (inbound)
+       {
+               /* for inbound SAs we allocated an SPI via get_spi, so we first check
+                * whether that SPI has already expired (race condition) */
+               sa_entry_t *alloc_spi;
+               this->mutex->lock(this->mutex);
+               if (this->allocated_spis->find_first(this->allocated_spis,
+                               (linked_list_match_t)sa_entry_match_byid, (void**)&alloc_spi,
+                                       &protocol, &spi, &reqid) != SUCCESS)
+               {
+                       this->mutex->unlock(this->mutex);
+                       DBG1(DBG_KNL, "allocated SPI %.8x has already expired", ntohl(spi));
+                       return FAILED;
+               }
+               else
+               {
+                       this->allocated_spis->remove(this->allocated_spis, alloc_spi, NULL);
+                       sa_entry_destroy(alloc_spi);
+               }
+               this->mutex->unlock(this->mutex);
+       }
+       
+       memset(&request, 0, sizeof(request));
+       
+       DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%d}", ntohl(spi), reqid);
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_ADD;
+       msg->sadb_msg_satype = proto_ike2satype(protocol);
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_EXT_SA;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_spi = spi;
+       sa->sadb_sa_state = SADB_SASTATE_MATURE;
+       sa->sadb_sa_replay = (protocol == IPPROTO_COMP) ? 0 : 32;
+       sa->sadb_sa_auth = lookup_algorithm(integrity_algs, int_alg);
+       sa->sadb_sa_encrypt = lookup_algorithm(encryption_algs, enc_alg);
+       PFKEY_EXT_ADD(msg, sa);
+       
+       add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC);
+       add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST);
+       
+       if (enc_alg != ENCR_UNDEFINED)
+       {
+               if (!sa->sadb_sa_encrypt)
+               {
+                       DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
+                                encryption_algorithm_names, enc_alg);
+                       return FAILED;
+               }
+               DBG2(DBG_KNL, "  using encryption algorithm %N with key size %d",
+                        encryption_algorithm_names, enc_alg, enc_key.len * 8);
+               
+               key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg);
+               key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+               key->sadb_key_bits = enc_key.len * 8;
+               key->sadb_key_len = PFKEY_LEN(sizeof(struct sadb_key) + enc_key.len);
+               memcpy(key + 1, enc_key.ptr, enc_key.len);
+               
+               PFKEY_EXT_ADD(msg, key);
+       }
+       
+       if (int_alg != AUTH_UNDEFINED)
+       {
+               if (!sa->sadb_sa_auth)
+               {
+                       DBG1(DBG_KNL, "algorithm %N not supported by kernel!",
+                                        integrity_algorithm_names, int_alg);
+                       return FAILED;
+               }
+               DBG2(DBG_KNL, "  using integrity algorithm %N with key size %d",
+                        integrity_algorithm_names, int_alg, int_key.len * 8);
+               
+               key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg);
+               key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
+               key->sadb_key_bits = int_key.len * 8;
+               key->sadb_key_len = PFKEY_LEN(sizeof(struct sadb_key) + int_key.len);
+               memcpy(key + 1, int_key.ptr, int_key.len);
+               
+               PFKEY_EXT_ADD(msg, key);
+       }
+       
+       if (ipcomp != IPCOMP_NONE)
+       {
+               /*TODO*/
+       }
+       
+       if (encap)
+       {
+               add_encap_ext(msg, src, dst, FALSE);
+       }
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x", ntohl(spi));
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x: %s (%d)",
+                               ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       free(out);
+       
+       /* for tunnel mode SAs we have to install an additional IPIP SA and
+        * group the two SAs together */
+       if (mode == MODE_TUNNEL)
+       {
+               if (add_ipip_sa(this, src, dst, spi, reqid) != SUCCESS ||
+                       group_ipip_sa(this, src, dst, spi, protocol, reqid) != SUCCESS)
+               {
+                       DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x", ntohl(spi));
+                       return FAILED;
+               }
+       }
+       
+       this->mutex->lock(this->mutex);
+       /* we cache this SA for two reasons:
+        * - in case an SADB_X_NAT_T_MAPPING_NEW event occurs (we need to find the reqid then)
+        * - to decide if an expired SA is still installed */
+       this->installed_sas->insert_last(this->installed_sas,
+                               create_sa_entry(protocol, spi, reqid, src, dst, encap, inbound));
+       this->mutex->unlock(this->mutex);
+       
+       /* Although KLIPS supports SADB_EXT_LIFETIME_SOFT/HARD, we handle the lifetime
+        * of SAs manually in the plugin. Refer to the comments in receive_events()
+        * for details. */
+       if (expire_soft)
+       {
+               schedule_expire(this, protocol, spi, reqid, EXPIRE_TYPE_SOFT, expire_soft);
+       }
+       
+       if (expire_hard)
+       {
+               schedule_expire(this, protocol, spi, reqid, EXPIRE_TYPE_HARD, expire_hard);
+       }
+               
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.update_sa.
+ */
+static status_t update_sa(private_kernel_klips_ipsec_t *this,
+                                                 u_int32_t spi, protocol_id_t protocol, u_int16_t cpi,
+                                                 host_t *src, host_t *dst,
+                                                 host_t *new_src, host_t *new_dst,
+                                                 bool encap, bool new_encap)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       size_t len;
+       
+       /* we can't update the SA if any of the ip addresses have changed.
+        * that's because we can't use SADB_UPDATE and by deleting and readding the
+        * SA the sequence numbers would get lost */
+       if (!src->ip_equals(src, new_src) ||
+               !dst->ip_equals(dst, new_dst))
+       {
+               DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address changes"
+                               " are not supported", ntohl(spi));
+               return NOT_SUPPORTED;
+       }
+       
+       /* because KLIPS does not allow us to change the NAT-T type in an SADB_UPDATE,
+        * we can't update the SA if the encap flag has changed since installing it */
+       if (encap != new_encap)
+       {
+               DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: change of UDP"
+                               " encapsulation is not supported", ntohl(spi));
+               return NOT_SUPPORTED;
+       }
+       
+       DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
+                ntohl(spi), src, dst, new_src, new_dst);
+       
+       memset(&request, 0, sizeof(request));
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_UPDATE;
+       msg->sadb_msg_satype = proto_ike2satype(protocol);
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_EXT_SA;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_spi = spi;
+       sa->sadb_sa_encrypt = SADB_EALG_AESCBC; /* ignored */
+       sa->sadb_sa_auth = SADB_AALG_SHA1HMAC; /* ignored */
+       sa->sadb_sa_state = SADB_SASTATE_MATURE;
+       PFKEY_EXT_ADD(msg, sa);
+       
+       add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC);
+       add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST);
+                       
+       add_encap_ext(msg, new_src, new_dst, TRUE);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi));
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: %s (%d)",
+                               ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       free(out);
+       
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.del_sa.
+ */
+static status_t del_sa(private_kernel_klips_ipsec_t *this, host_t *dst,
+                                          u_int32_t spi, protocol_id_t protocol, u_int16_t cpi)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       sa_entry_t *cached_sa;
+       size_t len;
+       
+       memset(&request, 0, sizeof(request));
+       
+       /* all grouped SAs are automatically deleted by KLIPS as soon as
+        * one of them is deleted, therefore we delete only the main one */
+       DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi));
+       
+       this->mutex->lock(this->mutex);
+       /* this should not fail, but we don't care if it does, let the kernel decide
+        * whether this SA exists or not */
+       if (this->installed_sas->find_first(this->installed_sas,
+                       (linked_list_match_t)sa_entry_match_bydst, (void**)&cached_sa,
+                       &protocol, &spi, dst) == SUCCESS)
+       {
+               this->installed_sas->remove(this->installed_sas, cached_sa, NULL);
+               sa_entry_destroy(cached_sa);
+       }
+       this->mutex->unlock(this->mutex);
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_DELETE;
+       msg->sadb_msg_satype = proto_ike2satype(protocol);
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa = (struct sadb_sa*)PFKEY_EXT_ADD_NEXT(msg);
+       sa->sadb_sa_exttype = SADB_EXT_SA;
+       sa->sadb_sa_len = PFKEY_LEN(sizeof(struct sadb_sa));
+       sa->sadb_sa_spi = spi;
+       PFKEY_EXT_ADD(msg, sa);
+       
+       /* the kernel wants an SADB_EXT_ADDRESS_SRC to be present even though
+        * it is not used for anything. */
+       add_anyaddr_ext(msg, dst->get_family(dst), SADB_EXT_ADDRESS_SRC);
+       add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", ntohl(spi));
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x: %s (%d)",
+                               ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       
+       DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x", ntohl(spi));
+       free(out);
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.add_policy.
+ */
+static status_t add_policy(private_kernel_klips_ipsec_t *this, 
+                                                  host_t *src, host_t *dst,
+                                                  traffic_selector_t *src_ts,
+                                                  traffic_selector_t *dst_ts,
+                                                  policy_dir_t direction, u_int32_t spi,
+                                                  protocol_id_t protocol, u_int32_t reqid,
+                                                  ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                                  bool routed)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       policy_entry_t *policy, *found = NULL;
+       u_int8_t satype;
+       size_t len;     
+       
+       if (direction == POLICY_FWD)
+       {
+               /* no forward policies for KLIPS */
+               return SUCCESS;
+       }
+       
+       /* tunnel mode policies direct the packets into the pseudo IPIP SA */
+       satype = (mode == MODE_TUNNEL) ? SADB_X_SATYPE_IPIP :
+                                                                        proto_ike2satype(protocol);
+       
+       /* create a policy */
+       policy = create_policy_entry(src_ts, dst_ts, direction);
+       
+       /* find a matching policy */
+       this->mutex->lock(this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_equals, (void**)&found, policy) == SUCCESS)
+       {
+               /* use existing policy */
+               DBG2(DBG_KNL, "policy %R === %R %N already exists, increasing"
+                                         " refcount", src_ts, dst_ts,
+                                          policy_dir_names, direction);
+               policy_entry_destroy(policy);
+               policy = found;
+       }
+       else
+       {
+               /* apply the new one, if we have no such policy */
+               this->policies->insert_last(this->policies, policy);
+       }
+               
+       if (routed)
+       {
+               /* we install this as a %trap eroute in the kernel, later to be
+                * triggered by packets matching the policy (-> ACQUIRE). */
+               spi = htonl(SPI_TRAP);
+               satype = SADB_X_SATYPE_INT;
+               
+               /* the reqid is always set to the latest child SA that trapped this
+                * policy. we will need this reqid upon receiving an acquire. */
+               policy->reqid = reqid;
+               
+               /* increase the trap counter */
+               policy->trapcount++;
+               
+               if (policy->activecount)
+               {
+                       /* we do not replace the current policy in the kernel while a
+                        * policy is actively used */
+                       this->mutex->unlock(this->mutex);
+                       return SUCCESS;
+               }
+       }
+       else
+       {
+               /* increase the reference counter */
+               policy->activecount++;
+       }
+       
+       DBG2(DBG_KNL, "adding policy %R === %R %N", src_ts, dst_ts,
+                                  policy_dir_names, direction);
+       
+       memset(&request, 0, sizeof(request));
+       
+       msg = (struct sadb_msg*)request;
+       
+       /* FIXME: SADB_X_SAFLAGS_INFLOW may be required, if we add an inbound policy for an IPIP SA */
+       build_addflow(msg, satype, spi, routed ? NULL : src, routed ? NULL : dst,
+                       policy->src.net, policy->src.mask, policy->dst.net, policy->dst.mask,
+                       policy->src.proto, found != NULL);
+       
+       this->mutex->unlock(this->mutex);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to add policy %R === %R %N", src_ts, dst_ts,
+                                          policy_dir_names, direction);
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to add policy %R === %R %N: %s (%d)", src_ts, dst_ts,
+                                          policy_dir_names, direction,
+                                          strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       free(out);
+       
+       this->mutex->lock(this->mutex);
+       
+       /* we try to find the policy again and install the route if needed */
+       if (this->policies->find_last(this->policies, NULL, (void**)&policy) != SUCCESS)
+       {
+               this->mutex->unlock(this->mutex);
+               DBG2(DBG_KNL, "the policy %R === %R %N is already gone, ignoring",
+                               src_ts, dst_ts, policy_dir_names, direction);
+               return SUCCESS;
+       }
+       
+       /* KLIPS requires a special route that directs traffic that matches this
+        * policy to one of the virtual ipsec interfaces. The virtual interface
+        * has to be attached to the physical one the traffic runs over.
+        * This is a special case of the source route we install in other kernel
+        * interfaces.
+        * In the following cases we do NOT install a source route (but just a
+        * regular route):
+        * - we are not in tunnel mode
+        * - we are using IPv6 (does not work correctly yet!)
+        * - routing is disabled via strongswan.conf
+        */
+       if (policy->route == NULL && direction == POLICY_OUT)
+       {
+               char *iface;
+               ipsec_dev_t *dev;
+               route_entry_t *route = malloc_thing(route_entry_t);
+               route->src_ip = NULL;
+               
+               if (mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6 &&
+                       this->install_routes)
+               {
+                       charon->kernel_interface->get_address_by_ts(charon->kernel_interface,
+                                               src_ts, &route->src_ip);
+               }
+               
+               if (!route->src_ip)
+               {
+                       route->src_ip = host_create_any(src->get_family(src));
+               }
+               
+               /* find the virtual interface */
+               iface = charon->kernel_interface->get_interface(charon->kernel_interface,
+                                                                                                               src);
+               if (find_ipsec_dev(this, iface, &dev) == SUCCESS)
+               {
+                       /* above, we got either the name of a virtual or a physical
+                        * interface. for both cases it means we already have the devices
+                        * properly attached (assuming that we are exclusively attaching
+                        * ipsec devices). */
+                       dev->refcount++;
+               }
+               else
+               {
+                       /* there is no record of a mapping with the returned interface.
+                        * thus, we attach the first free virtual interface we find to
+                        * it. As above we assume we are the only client fiddling with
+                        * ipsec devices. */
+                       if (this->ipsec_devices->find_first(this->ipsec_devices,
+                                       (linked_list_match_t)ipsec_dev_match_free,
+                                               (void**)&dev) == SUCCESS)
+                       {
+                               if (attach_ipsec_dev(dev->name, iface) == SUCCESS)
+                               {
+                                       strncpy(dev->phys_name, iface, IFNAMSIZ);
+                                       dev->refcount = 1;
+                               }
+                               else
+                               {
+                                       DBG1(DBG_KNL, "failed to attach virtual interface %s"
+                                                       " to %s", dev->name, iface);
+                                       this->mutex->unlock(this->mutex);
+                                       free(iface);
+                                       return FAILED;
+                               }
+                       }
+                       else
+                       {
+                               this->mutex->unlock(this->mutex);
+                               DBG1(DBG_KNL, "failed to attach a virtual interface to %s: no"
+                                               " virtual interfaces left", iface);
+                               free(iface);
+                               return FAILED;
+                       }
+               }
+               free(iface);
+               route->if_name = strdup(dev->name);
+               
+               /* get the nexthop to dst */
+               route->gateway = charon->kernel_interface->get_nexthop(
+                                                                               charon->kernel_interface, dst);
+               route->dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net));
+               route->prefixlen = policy->dst.mask;
+               
+               switch (charon->kernel_interface->add_route(charon->kernel_interface,
+                               route->dst_net, route->prefixlen, route->gateway,
+                               route->src_ip, route->if_name))
+               {
+                       default:
+                               DBG1(DBG_KNL, "unable to install route for policy %R === %R",
+                                        src_ts, dst_ts);
+                               /* FALL */
+                       case ALREADY_DONE:
+                               /* route exists, do not uninstall */
+                               route_entry_destroy(route);
+                               break;
+                       case SUCCESS:
+                               /* cache the installed route */
+                               policy->route = route;
+                               break;
+               }
+       }       
+       
+       this->mutex->unlock(this->mutex);       
+       
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.query_policy.
+ */
+static status_t query_policy(private_kernel_klips_ipsec_t *this,
+                                                        traffic_selector_t *src_ts, 
+                                                        traffic_selector_t *dst_ts,
+                                                        policy_dir_t direction, u_int32_t *use_time)
+{
+       #define IDLE_PREFIX "idle="
+       static const char *path_eroute = "/proc/net/ipsec_eroute";
+       static const char *path_spi = "/proc/net/ipsec_spi";
+       FILE *file;
+       char line[1024], src[INET6_ADDRSTRLEN + 9], dst[INET6_ADDRSTRLEN + 9];
+       char *said = NULL, *pos;
+       policy_entry_t *policy, *found = NULL;
+       status_t status = FAILED;
+       
+       if (direction == POLICY_FWD)
+       {
+               /* we do not install forward policies */
+               return FAILED;
+       }
+       
+       DBG2(DBG_KNL, "querying policy %R === %R %N", src_ts, dst_ts,
+                                  policy_dir_names, direction);
+       
+       /* create a policy */
+       policy = create_policy_entry(src_ts, dst_ts, direction);
+       
+       /* find a matching policy */
+       this->mutex->lock(this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_equals, (void**)&found, policy) != SUCCESS)
+       {
+               this->mutex->unlock(this->mutex);
+               DBG1(DBG_KNL, "querying policy %R === %R %N failed, not found", src_ts,
+                                          dst_ts, policy_dir_names, direction);
+               policy_entry_destroy(policy);
+               return NOT_FOUND;
+       }
+       policy_entry_destroy(policy);
+       policy = found;
+       
+       /* src and dst selectors in KLIPS are of the form NET_ADDR/NETBITS:PROTO */
+       snprintf(src, sizeof(src), "%H/%d:%d", policy->src.net, policy->src.mask,
+                       policy->src.proto);
+       src[sizeof(src) - 1] = '\0';
+       snprintf(dst, sizeof(dst), "%H/%d:%d", policy->dst.net, policy->dst.mask,
+                       policy->dst.proto);
+       dst[sizeof(dst) - 1] = '\0';
+       
+       this->mutex->unlock(this->mutex);
+       
+       /* we try to find the matching eroute first */
+       file = fopen(path_eroute, "r");
+       if (file == NULL)
+       {
+               DBG1(DBG_KNL, "unable to query policy %R === %R %N: %s (%d)", src_ts,
+                               dst_ts, policy_dir_names, direction, strerror(errno), errno);
+               return FAILED;
+       }
+       
+       /* read line by line where each line looks like:
+        * packets  src  ->  dst  =>  said */
+       while (fgets(line, sizeof(line), file))
+       {
+               enumerator_t *enumerator;
+               char *token;
+               int i = 0;
+               
+               enumerator = enumerator_create_token(line, " \t", " \t\n");
+               while (enumerator->enumerate(enumerator, &token))
+               {
+                       switch (i++)
+                       {
+                               case 0: /* packets */
+                                       continue;
+                               case 1: /* src */
+                                       if (streq(token, src))
+                                       {
+                                               continue;
+                                       }
+                                       break;
+                               case 2: /* -> */
+                                       continue;
+                               case 3: /* dst */
+                                       if (streq(token, dst))
+                                       {
+                                               continue;
+                                       }
+                                       break;
+                               case 4: /* => */
+                                       continue;
+                               case 5: /* said */
+                                       said = strdup(token);
+                                       break;
+                       }
+                       break;
+               }
+               enumerator->destroy(enumerator);
+               
+               if (i == 5)
+               {
+                       /* eroute matched */
+                       break;
+               }
+       }
+       fclose(file);
+       
+       if (said == NULL)
+       {
+               DBG1(DBG_KNL, "unable to query policy %R === %R %N: found no matching"
+                               " eroute", src_ts, dst_ts, policy_dir_names, direction);
+               return FAILED;
+       }
+       
+       /* compared with the one in the spi entry the SA ID from the eroute entry
+        * has an additional ":PROTO" appended, which we need to cut off */
+       pos = strrchr(said, ':');
+       *pos = '\0';
+       
+       /* now we try to find the matching spi entry */
+       file = fopen(path_spi, "r");
+       if (file == NULL)
+       {
+               DBG1(DBG_KNL, "unable to query policy %R === %R %N: %s (%d)", src_ts,
+                               dst_ts, policy_dir_names, direction, strerror(errno), errno);
+               return FAILED;
+       }
+       
+       while (fgets(line, sizeof(line), file))
+       {
+               if (strneq(line, said, strlen(said)))
+               {
+                       /* fine we found the correct line, now find the idle time */
+                       u_int32_t idle_time;
+                       pos = strstr(line, IDLE_PREFIX);
+                       if (pos == NULL)
+                       {
+                               /* no idle time, i.e. this SA has not been used yet */
+                               break;
+                       }
+                       if (sscanf(pos, IDLE_PREFIX"%u", &idle_time) <= 0)
+                       {
+                               /* idle time not valid */
+                               break;
+                       }
+                       
+                       *use_time = time(NULL) - idle_time;
+                       status = SUCCESS;
+                       break;
+               }
+       }
+       fclose(file);
+       free(said);
+       
+       return status;
+}
+
+/**
+ * Implementation of kernel_interface_t.del_policy.
+ */
+static status_t del_policy(private_kernel_klips_ipsec_t *this,
+                                                  traffic_selector_t *src_ts, 
+                                                  traffic_selector_t *dst_ts,
+                                                  policy_dir_t direction, bool unrouted)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg = (struct sadb_msg*)request, *out;
+       policy_entry_t *policy, *found = NULL;
+       route_entry_t *route;
+       size_t len;
+       
+       if (direction == POLICY_FWD)
+       {
+               /* no forward policies for KLIPS */
+               return SUCCESS;
+       }
+       
+       DBG2(DBG_KNL, "deleting policy %R === %R %N", src_ts, dst_ts,
+                                  policy_dir_names, direction);
+       
+       /* create a policy */
+       policy = create_policy_entry(src_ts, dst_ts, direction);
+       
+       /* find a matching policy */
+       this->mutex->lock(this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_equals, (void**)&found, policy) != SUCCESS)
+       {
+               this->mutex->unlock(this->mutex);
+               DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", src_ts,
+                                          dst_ts, policy_dir_names, direction);
+               policy_entry_destroy(policy);
+               return NOT_FOUND;
+       }
+       policy_entry_destroy(policy);
+       
+       /* decrease appropriate counter */
+       unrouted ? found->trapcount-- : found->activecount--;
+       
+       if (found->trapcount == 0)
+       {
+               /* if this policy is finally unrouted, we reset the reqid because it
+                * may still be actively used and there might be a pending acquire for
+                * this policy. */
+               found->reqid = 0;
+       }
+       
+       if (found->activecount > 0)
+       {
+               /* is still used by SAs, keep in kernel */
+               this->mutex->unlock(this->mutex);
+               DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed");
+               return SUCCESS;
+       }
+       else if (found->activecount == 0 && found->trapcount > 0)
+       {
+               /* for a policy that is not used actively anymore, but is still trapped
+                * by another child SA we replace the current eroute with a %trap eroute */
+               DBG2(DBG_KNL, "policy still routed by another CHILD_SA, not removed");
+               memset(&request, 0, sizeof(request));
+               build_addflow(msg, SADB_X_SATYPE_INT, htonl(SPI_TRAP), NULL, NULL,
+                               found->src.net, found->src.mask, found->dst.net,
+                               found->dst.mask, found->src.proto, TRUE);
+               this->mutex->unlock(this->mutex);
+               return pfkey_send_ack(this, msg);
+       }
+       
+       /* remove if last reference */
+       this->policies->remove(this->policies, found, NULL);
+       policy = found;
+       
+       this->mutex->unlock(this->mutex);
+               
+       memset(&request, 0, sizeof(request));
+       
+       build_delflow(msg, 0, policy->src.net, policy->src.mask, policy->dst.net,
+                       policy->dst.mask, policy->src.proto);
+       
+       route = policy->route;
+       policy->route = NULL;
+       policy_entry_destroy(policy);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to delete policy %R === %R %N", src_ts, dst_ts,
+                                          policy_dir_names, direction);
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to delete policy %R === %R %N: %s (%d)", src_ts,
+                                          dst_ts, policy_dir_names, direction,
+                                          strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       free(out);
+       
+       if (route)
+       {
+               ipsec_dev_t *dev;
+               
+               if (charon->kernel_interface->del_route(charon->kernel_interface,
+                               route->dst_net, route->prefixlen, route->gateway,
+                               route->src_ip, route->if_name) != SUCCESS)
+               {
+                       DBG1(DBG_KNL, "error uninstalling route installed with"
+                                                 " policy %R === %R %N", src_ts, dst_ts,
+                                                  policy_dir_names, direction);
+               }
+               
+               /* we have to detach the ipsec interface from the physical one over which
+                * this SA ran (if it is not used by any other) */
+               this->mutex->lock(this->mutex);
+               
+               if (find_ipsec_dev(this, route->if_name, &dev) == SUCCESS)
+               {
+                       /* fine, we found a matching device object, let's check if we have
+                        * to detach it. */
+                       if (--dev->refcount == 0)
+                       {
+                               if (detach_ipsec_dev(dev->name, dev->phys_name) != SUCCESS)
+                               {
+                                       DBG1(DBG_KNL, "failed to detach virtual interface %s"
+                                                       " from %s", dev->name, dev->phys_name);
+                               }
+                               dev->phys_name[0] = '\0';
+                       }
+               }
+               
+               this->mutex->unlock(this->mutex);
+               
+               route_entry_destroy(route);
+       }
+       
+       return SUCCESS;
+}
+
+/**
+ * Initialize the list of ipsec devices
+ */
+static void init_ipsec_devices(private_kernel_klips_ipsec_t *this)
+{
+       int i, count = lib->settings->get_int(lib->settings,
+                                               "charon.plugins.kernel_klips.ipsec_dev_count",
+                                               DEFAULT_IPSEC_DEV_COUNT);
+       
+       for (i = 0; i < count; ++i)
+       {
+               ipsec_dev_t *dev = malloc_thing(ipsec_dev_t);
+               snprintf(dev->name, IFNAMSIZ, IPSEC_DEV_PREFIX"%d", i);
+               dev->name[IFNAMSIZ - 1] = '\0';
+               dev->phys_name[0] = '\0';
+               dev->refcount = 0;
+               this->ipsec_devices->insert_last(this->ipsec_devices, dev);
+               
+               /* detach any previously attached ipsec device */
+               detach_ipsec_dev(dev->name, dev->phys_name);
+       }
+}
+
+/**
+ * Register a socket for AQUIRE/EXPIRE messages
+ */
+static status_t register_pfkey_socket(private_kernel_klips_ipsec_t *this, u_int8_t satype)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       size_t len;
+       
+       memset(&request, 0, sizeof(request));
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_REGISTER;
+       msg->sadb_msg_satype = satype;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       if (pfkey_send_socket(this, this->socket_events, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to register PF_KEY socket");
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to register PF_KEY socket: %s (%d)",
+                                          strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       free(out);
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.destroy.
+ */
+static void destroy(private_kernel_klips_ipsec_t *this)
+{
+       this->job->cancel(this->job);
+       close(this->socket);
+       close(this->socket_events);
+       this->mutex_pfkey->destroy(this->mutex_pfkey);
+       this->mutex->destroy(this->mutex);
+       this->ipsec_devices->destroy_function(this->ipsec_devices, (void*)ipsec_dev_destroy);
+       this->installed_sas->destroy_function(this->installed_sas, (void*)sa_entry_destroy);
+       this->allocated_spis->destroy_function(this->allocated_spis, (void*)sa_entry_destroy);
+       this->policies->destroy_function(this->policies, (void*)policy_entry_destroy);
+       free(this);
+}
+
+/*
+ * Described in header.
+ */
+kernel_klips_ipsec_t *kernel_klips_ipsec_create()
+{
+       private_kernel_klips_ipsec_t *this = malloc_thing(private_kernel_klips_ipsec_t);
+       
+       /* public functions */
+       this->public.interface.get_spi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
+       this->public.interface.get_cpi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,u_int32_t,u_int16_t*))get_cpi;
+       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,u_int16_t,bool,bool))add_sa;
+       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,u_int16_t,host_t*,host_t*,host_t*,host_t*,bool,bool))update_sa;
+       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t,u_int16_t))del_sa;
+       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t,protocol_id_t,u_int32_t,ipsec_mode_t,u_int16_t,u_int16_t,bool))add_policy;
+       this->public.interface.query_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
+       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,bool))del_policy;
+       
+       this->public.interface.destroy = (void(*)(kernel_ipsec_t*)) destroy;
+
+       /* private members */
+       this->policies = linked_list_create();
+       this->allocated_spis = linked_list_create();
+       this->installed_sas = linked_list_create();
+       this->ipsec_devices = linked_list_create();
+       this->mutex = mutex_create(MUTEX_DEFAULT);
+       this->mutex_pfkey = mutex_create(MUTEX_DEFAULT);
+       this->install_routes = lib->settings->get_bool(lib->settings, "charon.install_routes", TRUE);
+       this->seq = 0;
+       
+       /* initialize ipsec devices */
+       init_ipsec_devices(this);
+       
+       /* create a PF_KEY socket to communicate with the kernel */
+       this->socket = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+       if (this->socket <= 0)
+       {
+               charon->kill(charon, "unable to create PF_KEY socket");
+       }
+       
+       /* create a PF_KEY socket for ACQUIRE & EXPIRE */
+       this->socket_events = socket(PF_KEY, SOCK_RAW, PF_KEY_V2);
+       if (this->socket_events <= 0)
+       {
+               charon->kill(charon, "unable to create PF_KEY event socket");
+       }
+       
+       /* register the event socket */
+       if (register_pfkey_socket(this, SADB_SATYPE_ESP) != SUCCESS ||
+               register_pfkey_socket(this, SADB_SATYPE_AH) != SUCCESS)
+       {
+               charon->kill(charon, "unable to register PF_KEY event socket");
+       }
+       
+       this->job = callback_job_create((callback_job_cb_t)receive_events,
+                                                                       this, NULL, NULL);
+       charon->processor->queue_job(charon->processor, (job_t*)this->job);
+       
+       return &this->public;
+}
diff --git a/src/charon/plugins/kernel_klips/kernel_klips_ipsec.h b/src/charon/plugins/kernel_klips/kernel_klips_ipsec.h
new file mode 100644 (file)
index 0000000..cd810c6
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup kernel_klips_ipsec_i kernel_klips_ipsec
+ * @{ @ingroup kernel_klips
+ */
+
+#ifndef KERNEL_KLIPS_IPSEC_H_
+#define KERNEL_KLIPS_IPSEC_H_
+
+#include <kernel/kernel_ipsec.h>
+
+typedef struct kernel_klips_ipsec_t kernel_klips_ipsec_t;
+
+/**
+ * Implementation of the kernel ipsec interface using PF_KEY.
+ */
+struct kernel_klips_ipsec_t {
+
+       /**
+        * Implements kernel_ipsec_t interface
+        */
+       kernel_ipsec_t interface;
+};
+
+/**
+ * Create a PF_KEY kernel ipsec interface instance.
+ *
+ * @return                     kernel_klips_ipsec_t instance
+ */
+kernel_klips_ipsec_t *kernel_klips_ipsec_create();
+
+#endif /* KERNEL_KLIPS_IPSEC_H_ @} */
diff --git a/src/charon/plugins/kernel_klips/kernel_klips_plugin.c b/src/charon/plugins/kernel_klips/kernel_klips_plugin.c
new file mode 100644 (file)
index 0000000..7dbea7e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+
+#include "kernel_klips_plugin.h"
+
+#include "kernel_klips_ipsec.h"
+
+#include <daemon.h>
+
+typedef struct private_kernel_klips_plugin_t private_kernel_klips_plugin_t;
+
+/**
+ * private data of kernel PF_KEY plugin
+ */
+struct private_kernel_klips_plugin_t {
+       /**
+        * implements plugin interface
+        */
+       kernel_klips_plugin_t public;
+};
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(private_kernel_klips_plugin_t *this)
+{
+       charon->kernel_interface->remove_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_klips_ipsec_create);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+       private_kernel_klips_plugin_t *this = malloc_thing(private_kernel_klips_plugin_t);
+       
+       this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+       
+       charon->kernel_interface->add_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_klips_ipsec_create);
+       
+       return &this->public.plugin;
+}
diff --git a/src/charon/plugins/kernel_klips/kernel_klips_plugin.h b/src/charon/plugins/kernel_klips/kernel_klips_plugin.h
new file mode 100644 (file)
index 0000000..2e1acc2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2008 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * $Id$
+ */
+
+/**
+ * @defgroup kernel_klips kernel_klips
+ * @ingroup cplugins
+ *
+ * @defgroup kernel_klips_plugin kernel_klips_plugin
+ * @{ @ingroup kernel_klips
+ */
+
+#ifndef KERNEL_KLIPS_PLUGIN_H_
+#define KERNEL_KLIPS_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct kernel_klips_plugin_t kernel_klips_plugin_t;
+
+/**
+ * PF_KEY kernel interface plugin
+ */
+struct kernel_klips_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+/**
+ * Create a kernel_klips_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* KERNEL_KLIPS_PLUGIN_H_ @} */
diff --git a/src/charon/plugins/kernel_klips/pfkeyv2.h b/src/charon/plugins/kernel_klips/pfkeyv2.h
new file mode 100644 (file)
index 0000000..78d3dfa
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+RFC 2367               PF_KEY Key Management API               July 1998
+
+
+Appendix D: Sample Header File
+
+This file defines structures and symbols for the PF_KEY Version 2
+key management interface. It was written at the U.S. Naval Research
+Laboratory. This file is in the public domain. The authors ask that
+you leave this credit intact on any copies of this file.
+*/
+#ifndef __PFKEY_V2_H
+#define __PFKEY_V2_H 1
+
+#define PF_KEY_V2 2
+#define PFKEYV2_REVISION        199806L
+
+#define SADB_RESERVED    0
+#define SADB_GETSPI      1
+#define SADB_UPDATE      2
+#define SADB_ADD         3
+#define SADB_DELETE      4
+#define SADB_GET         5
+#define SADB_ACQUIRE     6
+#define SADB_REGISTER    7
+#define SADB_EXPIRE      8
+#define SADB_FLUSH       9
+#define SADB_DUMP       10
+#define SADB_X_PROMISC  11
+#define SADB_X_PCHANGE  12
+#define SADB_X_GRPSA    13
+#define SADB_X_ADDFLOW 14
+#define SADB_X_DELFLOW 15
+#define SADB_X_DEBUG   16
+#define SADB_X_NAT_T_NEW_MAPPING  17
+#define SADB_MAX                  17
+
+struct sadb_msg {
+  uint8_t sadb_msg_version;
+  uint8_t sadb_msg_type;
+  uint8_t sadb_msg_errno;
+  uint8_t sadb_msg_satype;
+  uint16_t sadb_msg_len;
+  uint16_t sadb_msg_reserved;
+  uint32_t sadb_msg_seq;
+  uint32_t sadb_msg_pid;
+};
+
+struct sadb_ext {
+  uint16_t sadb_ext_len;
+  uint16_t sadb_ext_type;
+};
+
+struct sadb_sa {
+  uint16_t sadb_sa_len;
+  uint16_t sadb_sa_exttype;
+  uint32_t sadb_sa_spi;
+  uint8_t sadb_sa_replay;
+  uint8_t sadb_sa_state;
+  uint8_t sadb_sa_auth;
+  uint8_t sadb_sa_encrypt;
+  uint32_t sadb_sa_flags;
+};
+
+struct sadb_lifetime {
+  uint16_t sadb_lifetime_len;
+  uint16_t sadb_lifetime_exttype;
+  uint32_t sadb_lifetime_allocations;
+  uint64_t sadb_lifetime_bytes;
+  uint64_t sadb_lifetime_addtime;
+  uint64_t sadb_lifetime_usetime;
+  uint32_t sadb_x_lifetime_packets;
+  uint32_t sadb_x_lifetime_reserved;
+};
+
+struct sadb_address {
+  uint16_t sadb_address_len;
+  uint16_t sadb_address_exttype;
+  uint8_t sadb_address_proto;
+  uint8_t sadb_address_prefixlen;
+  uint16_t sadb_address_reserved;
+};
+
+struct sadb_key {
+  uint16_t sadb_key_len;
+  uint16_t sadb_key_exttype;
+  uint16_t sadb_key_bits;
+  uint16_t sadb_key_reserved;
+};
+
+struct sadb_ident {
+  uint16_t sadb_ident_len;
+  uint16_t sadb_ident_exttype;
+  uint16_t sadb_ident_type;
+  uint16_t sadb_ident_reserved;
+  uint64_t sadb_ident_id;
+};
+
+struct sadb_sens {
+  uint16_t sadb_sens_len;
+  uint16_t sadb_sens_exttype;
+  uint32_t sadb_sens_dpd;
+  uint8_t sadb_sens_sens_level;
+  uint8_t sadb_sens_sens_len;
+  uint8_t sadb_sens_integ_level;
+  uint8_t sadb_sens_integ_len;
+  uint32_t sadb_sens_reserved;
+};
+
+struct sadb_prop {
+  uint16_t sadb_prop_len;
+  uint16_t sadb_prop_exttype;
+  uint8_t sadb_prop_replay;
+  uint8_t sadb_prop_reserved[3];
+};
+
+struct sadb_comb {
+  uint8_t sadb_comb_auth;
+  uint8_t sadb_comb_encrypt;
+  uint16_t sadb_comb_flags;
+  uint16_t sadb_comb_auth_minbits;
+  uint16_t sadb_comb_auth_maxbits;
+  uint16_t sadb_comb_encrypt_minbits;
+  uint16_t sadb_comb_encrypt_maxbits;
+  uint32_t sadb_comb_reserved;
+  uint32_t sadb_comb_soft_allocations;
+  uint32_t sadb_comb_hard_allocations;
+  uint64_t sadb_comb_soft_bytes;
+  uint64_t sadb_comb_hard_bytes;
+  uint64_t sadb_comb_soft_addtime;
+  uint64_t sadb_comb_hard_addtime;
+  uint64_t sadb_comb_soft_usetime;
+  uint64_t sadb_comb_hard_usetime;
+  uint32_t sadb_x_comb_soft_packets;
+  uint32_t sadb_x_comb_hard_packets;
+};
+
+struct sadb_supported {
+  uint16_t sadb_supported_len;
+  uint16_t sadb_supported_exttype;
+  uint32_t sadb_supported_reserved;
+};
+
+struct sadb_alg {
+  uint8_t sadb_alg_id;
+  uint8_t sadb_alg_ivlen;
+  uint16_t sadb_alg_minbits;
+  uint16_t sadb_alg_maxbits;
+  uint16_t sadb_alg_reserved;
+};
+
+struct sadb_spirange {
+  uint16_t sadb_spirange_len;
+  uint16_t sadb_spirange_exttype;
+  uint32_t sadb_spirange_min;
+  uint32_t sadb_spirange_max;
+  uint32_t sadb_spirange_reserved;
+};
+
+struct sadb_x_kmprivate {
+  uint16_t sadb_x_kmprivate_len;
+  uint16_t sadb_x_kmprivate_exttype;
+  uint32_t sadb_x_kmprivate_reserved;
+};
+
+struct sadb_x_satype {
+  uint16_t sadb_x_satype_len;
+  uint16_t sadb_x_satype_exttype;
+  uint8_t sadb_x_satype_satype;
+  uint8_t sadb_x_satype_reserved[3];
+};
+  
+struct sadb_x_debug {
+  uint16_t sadb_x_debug_len;
+  uint16_t sadb_x_debug_exttype;
+  uint32_t sadb_x_debug_tunnel;
+  uint32_t sadb_x_debug_netlink;
+  uint32_t sadb_x_debug_xform;
+  uint32_t sadb_x_debug_eroute;
+  uint32_t sadb_x_debug_spi;
+  uint32_t sadb_x_debug_radij;
+  uint32_t sadb_x_debug_esp;
+  uint32_t sadb_x_debug_ah;
+  uint32_t sadb_x_debug_rcv;
+  uint32_t sadb_x_debug_pfkey;
+  uint32_t sadb_x_debug_ipcomp;
+  uint32_t sadb_x_debug_verbose;
+  uint8_t sadb_x_debug_reserved[4];
+};
+
+struct sadb_x_nat_t_type {
+  uint16_t sadb_x_nat_t_type_len;
+  uint16_t sadb_x_nat_t_type_exttype;
+  uint8_t sadb_x_nat_t_type_type;
+  uint8_t sadb_x_nat_t_type_reserved[3];
+};
+struct sadb_x_nat_t_port {
+  uint16_t sadb_x_nat_t_port_len;
+  uint16_t sadb_x_nat_t_port_exttype;
+  uint16_t sadb_x_nat_t_port_port;
+  uint16_t sadb_x_nat_t_port_reserved;
+};
+  
+/*
+ * A protocol structure for passing through the transport level
+ * protocol.  It contains more fields than are actually used/needed
+ * but it is this way to be compatible with the structure used in
+ * OpenBSD (http://www.openbsd.org/cgi-bin/cvsweb/src/sys/net/pfkeyv2.h)
+ */
+struct sadb_protocol {
+  uint16_t sadb_protocol_len;
+  uint16_t sadb_protocol_exttype;
+  uint8_t  sadb_protocol_proto;
+  uint8_t  sadb_protocol_direction;
+  uint8_t  sadb_protocol_flags;
+  uint8_t  sadb_protocol_reserved2;
+};
+
+#define SADB_EXT_RESERVED             0
+#define SADB_EXT_SA                   1
+#define SADB_EXT_LIFETIME_CURRENT     2
+#define SADB_EXT_LIFETIME_HARD        3
+#define SADB_EXT_LIFETIME_SOFT        4
+#define SADB_EXT_ADDRESS_SRC          5
+#define SADB_EXT_ADDRESS_DST          6
+#define SADB_EXT_ADDRESS_PROXY        7
+#define SADB_EXT_KEY_AUTH             8
+#define SADB_EXT_KEY_ENCRYPT          9
+#define SADB_EXT_IDENTITY_SRC         10
+#define SADB_EXT_IDENTITY_DST         11
+#define SADB_EXT_SENSITIVITY          12
+#define SADB_EXT_PROPOSAL             13
+#define SADB_EXT_SUPPORTED_AUTH       14
+#define SADB_EXT_SUPPORTED_ENCRYPT    15
+#define SADB_EXT_SPIRANGE             16
+#define SADB_X_EXT_KMPRIVATE          17
+#define SADB_X_EXT_SATYPE2            18
+#define SADB_X_EXT_SA2                19
+#define SADB_X_EXT_ADDRESS_DST2       20
+#define SADB_X_EXT_ADDRESS_SRC_FLOW   21
+#define SADB_X_EXT_ADDRESS_DST_FLOW   22
+#define SADB_X_EXT_ADDRESS_SRC_MASK   23
+#define SADB_X_EXT_ADDRESS_DST_MASK   24
+#define SADB_X_EXT_DEBUG              25
+#define SADB_X_EXT_PROTOCOL           26
+#define SADB_X_EXT_NAT_T_TYPE         27
+#define SADB_X_EXT_NAT_T_SPORT        28
+#define SADB_X_EXT_NAT_T_DPORT        29
+#define SADB_X_EXT_NAT_T_OA           30
+#define SADB_EXT_MAX                  30
+
+/* SADB_X_DELFLOW required over and above SADB_X_SAFLAGS_CLEARFLOW */
+#define SADB_X_EXT_ADDRESS_DELFLOW \
+       ( (1<<SADB_X_EXT_ADDRESS_SRC_FLOW) \
+       | (1<<SADB_X_EXT_ADDRESS_DST_FLOW) \
+       | (1<<SADB_X_EXT_ADDRESS_SRC_MASK) \
+       | (1<<SADB_X_EXT_ADDRESS_DST_MASK))
+
+#define SADB_SATYPE_UNSPEC    0
+#define SADB_SATYPE_AH        2
+#define SADB_SATYPE_ESP       3
+#define SADB_SATYPE_RSVP      5
+#define SADB_SATYPE_OSPFV2    6
+#define SADB_SATYPE_RIPV2     7
+#define SADB_SATYPE_MIP       8
+#define SADB_X_SATYPE_IPIP    9
+#define SADB_X_SATYPE_COMP    10
+#define SADB_X_SATYPE_INT     11
+#define SADB_SATYPE_MAX       11
+
+#define SADB_SASTATE_LARVAL   0
+#define SADB_SASTATE_MATURE   1
+#define SADB_SASTATE_DYING    2
+#define SADB_SASTATE_DEAD     3
+#define SADB_SASTATE_MAX      3
+
+#define SADB_SAFLAGS_PFS               1
+#define SADB_X_SAFLAGS_REPLACEFLOW     2
+#define SADB_X_SAFLAGS_CLEARFLOW       4
+#define SADB_X_SAFLAGS_INFLOW          8
+
+#define SADB_AALG_NONE        0
+#define SADB_AALG_MD5HMAC     2
+#define SADB_AALG_SHA1HMAC    3
+#define        SADB_AALG_SHA256_HMAC    5
+#define        SADB_AALG_SHA384_HMAC    6
+#define        SADB_AALG_SHA512_HMAC    7
+#define        SADB_AALG_RIPEMD160HMAC      8
+#define        SADB_AALG_MAX         15
+
+#define SADB_EALG_NONE        0
+#define SADB_EALG_DESCBC      2
+#define SADB_EALG_3DESCBC     3
+#define SADB_EALG_BFCBC              7
+#define SADB_EALG_NULL        11
+#define SADB_EALG_AESCBC      12
+#define SADB_EALG_MAX         255
+
+#define SADB_X_CALG_NONE          0
+#define SADB_X_CALG_OUI           1
+#define SADB_X_CALG_DEFLATE       2
+#define SADB_X_CALG_LZS           3
+#define SADB_X_CALG_V42BIS        4
+#define SADB_X_CALG_MAX           4
+
+#define SADB_X_TALG_NONE          0
+#define SADB_X_TALG_IPv4_in_IPv4  1
+#define SADB_X_TALG_IPv6_in_IPv4  2
+#define SADB_X_TALG_IPv4_in_IPv6  3
+#define SADB_X_TALG_IPv6_in_IPv6  4
+#define SADB_X_TALG_MAX           4
+
+
+#define SADB_IDENTTYPE_RESERVED   0
+#define SADB_IDENTTYPE_PREFIX     1
+#define SADB_IDENTTYPE_FQDN       2
+#define SADB_IDENTTYPE_USERFQDN   3
+#define SADB_X_IDENTTYPE_CONNECTION 4
+#define SADB_IDENTTYPE_MAX        4
+
+#define SADB_KEY_FLAGS_MAX     0
+#endif /* __PFKEY_V2_H */
index 6f1d93f..d0c9b72 100644 (file)
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/time.h>
+#include <stdint.h>
+#include <linux/ipsec.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 #include <linux/xfrm.h>
 #include <linux/udp.h>
-#include <netinet/in.h>
 #include <pthread.h>
 #include <unistd.h>
 #include <errno.h>
 #define XFRM_STATE_AF_UNSPEC   32
 #endif
 
+/** from linux/in.h */
+#ifndef IP_IPSEC_POLICY
+#define IP_IPSEC_POLICY 16
+#endif
+
 /** default priority of installed policies */
 #define PRIO_LOW 3000
 #define PRIO_HIGH 2000
@@ -849,8 +855,8 @@ static status_t add_sa(private_kernel_netlink_ipsec_t *this,
                                           u_int64_t expire_soft, u_int64_t expire_hard,
                                           u_int16_t enc_alg, chunk_t enc_key,
                                           u_int16_t int_alg, chunk_t int_key,
-                                          ipsec_mode_t mode, u_int16_t ipcomp, bool encap,
-                                          bool replace)
+                                          ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                          bool encap, bool inbound)
 {
        unsigned char request[NETLINK_BUFFER_SIZE];
        char *alg_name;
@@ -858,6 +864,17 @@ static status_t add_sa(private_kernel_netlink_ipsec_t *this,
        struct xfrm_usersa_info *sa;
        u_int16_t icv_size = 64;        
        
+       /* if IPComp is used, we install an additional IPComp SA. if the cpi is 0
+        * we are in the recursive call below */
+       if (ipcomp != IPCOMP_NONE && cpi != 0)
+       {
+               this->public.interface.add_sa(&this->public.interface,
+                               src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, 0, 0,
+                               ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, chunk_empty,
+                               mode, ipcomp, 0, FALSE, inbound);
+               ipcomp = IPCOMP_NONE;
+       }
+       
        memset(&request, 0, sizeof(request));
        
        DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}",
@@ -865,7 +882,7 @@ static status_t add_sa(private_kernel_netlink_ipsec_t *this,
        
        hdr = (struct nlmsghdr*)request;
        hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
-       hdr->nlmsg_type = replace ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA;
+       hdr->nlmsg_type = inbound ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA;
        hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
        
        sa = (struct xfrm_usersa_info*)NLMSG_DATA(hdr);
@@ -1149,9 +1166,10 @@ static status_t get_replay_state(private_kernel_netlink_ipsec_t *this,
  * Implementation of kernel_interface_t.update_sa.
  */
 static status_t update_sa(private_kernel_netlink_ipsec_t *this,
-                                                 u_int32_t spi, protocol_id_t protocol,
+                                                 u_int32_t spi, protocol_id_t protocol, u_int16_t cpi,
                                                  host_t *src, host_t *dst,
-                                                 host_t *new_src, host_t *new_dst, bool encap)
+                                                 host_t *new_src, host_t *new_dst,
+                                                 bool encap, bool new_encap)
 {
        unsigned char request[NETLINK_BUFFER_SIZE], *pos;
        struct nlmsghdr *hdr, *out = NULL;
@@ -1164,6 +1182,14 @@ static status_t update_sa(private_kernel_netlink_ipsec_t *this,
        bool got_replay_state;
        struct xfrm_replay_state replay;
        
+       /* if IPComp is used, we first update the IPComp SA */
+       if (cpi)
+       {
+               this->public.interface.update_sa(&this->public.interface, 
+                               htonl(ntohs(cpi)), IPPROTO_COMP, 0,
+                               src, dst, new_src, new_dst, FALSE, FALSE);
+       }
+       
        memset(&request, 0, sizeof(request));
        
        DBG2(DBG_KNL, "querying SAD entry with SPI %.8x for update", ntohl(spi));
@@ -1219,8 +1245,9 @@ static status_t update_sa(private_kernel_netlink_ipsec_t *this,
        got_replay_state = (get_replay_state(
                                                this, spi, protocol, dst, &replay) == SUCCESS);
        
-       /* delete the old SA */
-       if (this->public.interface.del_sa(&this->public.interface, dst, spi, protocol) != SUCCESS)
+       /* delete the old SA (without affecting the IPComp SA) */
+       if (this->public.interface.del_sa(&this->public.interface, dst, spi,
+                       protocol, 0) != SUCCESS)
        {
                DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", ntohl(spi));
                free(out);
@@ -1320,12 +1347,19 @@ static status_t update_sa(private_kernel_netlink_ipsec_t *this,
  * Implementation of kernel_interface_t.del_sa.
  */
 static status_t del_sa(private_kernel_netlink_ipsec_t *this, host_t *dst,
-                                          u_int32_t spi, protocol_id_t protocol)
+                                          u_int32_t spi, protocol_id_t protocol, u_int16_t cpi)
 {
        unsigned char request[NETLINK_BUFFER_SIZE];
        struct nlmsghdr *hdr;
        struct xfrm_usersa_id *sa_id;
        
+       /* if IPComp was used, we first delete the additional IPComp SA */
+       if (cpi)
+       {
+               this->public.interface.del_sa(&this->public.interface, dst,
+                               htonl(ntohs(cpi)), IPPROTO_COMP, 0);
+       }
+       
        memset(&request, 0, sizeof(request));
        
        DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi));
@@ -1357,9 +1391,10 @@ static status_t add_policy(private_kernel_netlink_ipsec_t *this,
                                                   host_t *src, host_t *dst,
                                                   traffic_selector_t *src_ts,
                                                   traffic_selector_t *dst_ts,
-                                                  policy_dir_t direction, protocol_id_t protocol,
-                                                  u_int32_t reqid, bool high_prio, ipsec_mode_t mode,
-                                                  u_int16_t ipcomp)
+                                                  policy_dir_t direction, u_int32_t spi,
+                                                  protocol_id_t protocol, u_int32_t reqid,
+                                                  ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                                  bool routed)
 {
        iterator_t *iterator;
        policy_entry_t *current, *policy;
@@ -1413,7 +1448,7 @@ static status_t add_policy(private_kernel_netlink_ipsec_t *this,
        policy_info->sel = policy->sel;
        policy_info->dir = policy->direction;
        /* calculate priority based on source selector size, small size = high prio */
-       policy_info->priority = high_prio ? PRIO_HIGH : PRIO_LOW;
+       policy_info->priority = routed ? PRIO_LOW : PRIO_HIGH;
        policy_info->priority -= policy->sel.prefixlen_s * 10;
        policy_info->priority -= policy->sel.proto ? 2 : 0;
        policy_info->priority -= policy->sel.sport_mask ? 1 : 0;
@@ -1617,7 +1652,7 @@ static status_t query_policy(private_kernel_netlink_ipsec_t *this,
 static status_t del_policy(private_kernel_netlink_ipsec_t *this,
                                                   traffic_selector_t *src_ts, 
                                                   traffic_selector_t *dst_ts,
-                                                  policy_dir_t direction)
+                                                  policy_dir_t direction, bool unrouted)
 {
        policy_entry_t *current, policy, *to_delete = NULL;
        route_entry_t *route;
@@ -1714,6 +1749,67 @@ static void destroy(private_kernel_netlink_ipsec_t *this)
        free(this);
 }
 
+/**
+ * Add bypass policies for IKE on the sockets used by charon
+ */
+static bool add_bypass_policies()
+{
+       int fd, family, port;
+       enumerator_t *sockets;
+       
+       /* we open an AF_KEY socket to autoload the af_key module. Otherwise
+        * setsockopt(IPSEC_POLICY) won't work. */
+       fd = socket(AF_KEY, SOCK_RAW, PF_KEY_V2);
+       if (fd == 0)
+       {
+               DBG1(DBG_KNL, "could not open AF_KEY socket");
+               return FALSE;
+       }
+       close(fd);
+       
+       sockets = charon->socket->create_enumerator(charon->socket);
+       while (sockets->enumerate(sockets, &fd, &family, &port))
+       {
+               struct sadb_x_policy policy;
+               u_int sol, ipsec_policy;
+               
+               switch (family)
+               {
+                       case AF_INET:
+                               sol = SOL_IP;
+                               ipsec_policy = IP_IPSEC_POLICY;
+                               break;
+                       case AF_INET6:
+                       {
+                               sol = SOL_IPV6;
+                               ipsec_policy = IPV6_IPSEC_POLICY;
+                               break;
+                       }
+               }
+               
+               memset(&policy, 0, sizeof(policy));
+               policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t);
+               policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+               policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS;
+       
+               policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND;
+               if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
+               {
+                       DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s",
+                                strerror(errno));
+                       return FALSE;
+               }
+               policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND;
+               if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
+               {
+                       DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", 
+                                strerror(errno));
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
 /*
  * Described in header.
  */
@@ -1725,12 +1821,12 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
        /* public functions */
        this->public.interface.get_spi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
        this->public.interface.get_cpi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,u_int32_t,u_int16_t*))get_cpi;
-       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,bool,bool))add_sa;
-       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*,bool))update_sa;
-       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t))del_sa;
-       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,ipsec_mode_t,u_int16_t))add_policy;
+       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,u_int16_t,bool,bool))add_sa;
+       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,u_int16_t,host_t*,host_t*,host_t*,host_t*,bool,bool))update_sa;
+       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t,u_int16_t))del_sa;
+       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t,protocol_id_t,u_int32_t,ipsec_mode_t,u_int16_t,u_int16_t,bool))add_policy;
        this->public.interface.query_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
-       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy;
+       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,bool))del_policy;
        this->public.interface.destroy = (void(*)(kernel_ipsec_t*)) destroy;
 
        /* private members */
@@ -1739,6 +1835,12 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
        this->install_routes = lib->settings->get_bool(lib->settings,
                                        "charon.install_routes", TRUE);
        
+       /* add bypass policies on the sockets used by charon */
+       if (!add_bypass_policies())
+       {
+               charon->kill(charon, "unable to add bypass policies on sockets");
+       }
+       
        this->socket_xfrm = netlink_socket_create(NETLINK_XFRM);
        
        memset(&addr, 0, sizeof(addr));
index 68f777c..1f2c651 100644 (file)
 #include <processing/jobs/delete_child_sa_job.h>
 #include <processing/jobs/update_sa_job.h>
 
+/** from linux/in.h */
+#ifndef IP_IPSEC_POLICY
+#define IP_IPSEC_POLICY 16
+#endif
+
 /** default priority of installed policies */
 #define PRIO_LOW 3000
 #define PRIO_HIGH 2000
@@ -920,12 +925,12 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg*
                        case AF_INET:
                        {
                                struct sockaddr_in *sin = (struct sockaddr_in*)sa;
-                               sin->sin_port = response.x_natt_dport->sadb_x_nat_t_port_port;
+                               sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
                        }
                        case AF_INET6:
                        {
                                struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa;
-                               sin6->sin6_port = response.x_natt_dport->sadb_x_nat_t_port_port;                                
+                               sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port);
                        }
                        default:
                                break;
@@ -1098,8 +1103,8 @@ static status_t add_sa(private_kernel_pfkey_ipsec_t *this,
                                           u_int64_t expire_soft, u_int64_t expire_hard,
                                           u_int16_t enc_alg, chunk_t enc_key,
                                           u_int16_t int_alg, chunk_t int_key,
-                                          ipsec_mode_t mode, u_int16_t ipcomp, bool encap,
-                                          bool replace)
+                                          ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                          bool encap, bool inbound)
 {
        unsigned char request[PFKEY_BUFFER_SIZE];
        struct sadb_msg *msg, *out;
@@ -1116,7 +1121,7 @@ static status_t add_sa(private_kernel_pfkey_ipsec_t *this,
        
        msg = (struct sadb_msg*)request;
        msg->sadb_msg_version = PF_KEY_V2;
-       msg->sadb_msg_type = replace ? SADB_UPDATE : SADB_ADD;
+       msg->sadb_msg_type = inbound ? SADB_UPDATE : SADB_ADD;
        msg->sadb_msg_satype = proto_ike2satype(protocol);
        msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
        
@@ -1229,9 +1234,10 @@ static status_t add_sa(private_kernel_pfkey_ipsec_t *this,
  * Implementation of kernel_interface_t.update_sa.
  */
 static status_t update_sa(private_kernel_pfkey_ipsec_t *this,
-                                                 u_int32_t spi, protocol_id_t protocol,
+                                                 u_int32_t spi, protocol_id_t protocol, u_int16_t cpi,
                                                  host_t *src, host_t *dst,
-                                                 host_t *new_src, host_t *new_dst, bool encap)
+                                                 host_t *new_src, host_t *new_dst,
+                                                 bool encap, bool new_encap)
 {
        unsigned char request[PFKEY_BUFFER_SIZE];
        struct sadb_msg *msg, *out;
@@ -1240,6 +1246,17 @@ static status_t update_sa(private_kernel_pfkey_ipsec_t *this,
        pfkey_msg_t response;
        size_t len;
        
+       /* we can't update the SA if any of the ip addresses have changed.
+        * that's because we can't use SADB_UPDATE and by deleting and readding the
+        * SA the sequence numbers would get lost */
+       if (!src->ip_equals(src, new_src) ||
+               !dst->ip_equals(dst, new_dst))
+       {
+               DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: address changes"
+                               " are not supported", ntohl(spi));
+               return NOT_SUPPORTED;
+       }
+       
        memset(&request, 0, sizeof(request));
        
        DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi));
@@ -1289,14 +1306,6 @@ static status_t update_sa(private_kernel_pfkey_ipsec_t *this,
                return FAILED;
        }
        
-       /* delete the old SA */
-       if (this->public.interface.del_sa(&this->public.interface, dst, spi, protocol) != SUCCESS)
-       {
-               DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", ntohl(spi));
-               free(out);
-               return FAILED;
-       }
-       
        DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
                 ntohl(spi), src, dst, new_src, new_dst);
        
@@ -1304,22 +1313,15 @@ static status_t update_sa(private_kernel_pfkey_ipsec_t *this,
        
        msg = (struct sadb_msg*)request;
        msg->sadb_msg_version = PF_KEY_V2;
-       msg->sadb_msg_type = SADB_ADD;
+       msg->sadb_msg_type = SADB_UPDATE;
        msg->sadb_msg_satype = proto_ike2satype(protocol);
        msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
        
        PFKEY_EXT_COPY(msg, response.sa);
        PFKEY_EXT_COPY(msg, response.x_sa2);
        
-       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
-       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
-       host2ext(new_src, addr);
-       PFKEY_EXT_ADD(msg, addr);
-       
-       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
-       addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
-       host2ext(new_dst, addr);
-       PFKEY_EXT_ADD(msg, addr);
+       PFKEY_EXT_COPY(msg, response.src);
+       PFKEY_EXT_COPY(msg, response.dst);
        
        PFKEY_EXT_COPY(msg, response.lft_soft);
        PFKEY_EXT_COPY(msg, response.lft_hard);
@@ -1362,7 +1364,7 @@ static status_t update_sa(private_kernel_pfkey_ipsec_t *this,
  * Implementation of kernel_interface_t.del_sa.
  */
 static status_t del_sa(private_kernel_pfkey_ipsec_t *this, host_t *dst,
-                                          u_int32_t spi, protocol_id_t protocol)
+                                          u_int32_t spi, protocol_id_t protocol, u_int16_t cpi)
 {
        unsigned char request[PFKEY_BUFFER_SIZE];
        struct sadb_msg *msg, *out;
@@ -1423,9 +1425,10 @@ static status_t add_policy(private_kernel_pfkey_ipsec_t *this,
                                                   host_t *src, host_t *dst,
                                                   traffic_selector_t *src_ts,
                                                   traffic_selector_t *dst_ts,
-                                                  policy_dir_t direction, protocol_id_t protocol,
-                                                  u_int32_t reqid, bool high_prio, ipsec_mode_t mode,
-                                                  u_int16_t ipcomp)
+                                                  policy_dir_t direction, u_int32_t spi,
+                                                  protocol_id_t protocol, u_int32_t reqid,
+                                                  ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                                  bool routed)
 {
        unsigned char request[PFKEY_BUFFER_SIZE];
        struct sadb_msg *msg, *out;
@@ -1476,7 +1479,7 @@ static status_t add_policy(private_kernel_pfkey_ipsec_t *this,
        pol->sadb_x_policy_id = 0;
        pol->sadb_x_policy_dir = dir2kernel(direction);
        /* calculate priority based on source selector size, small size = high prio */
-       pol->sadb_x_policy_priority = high_prio ? PRIO_HIGH : PRIO_LOW;
+       pol->sadb_x_policy_priority = routed ? PRIO_LOW : PRIO_HIGH;
        pol->sadb_x_policy_priority -= policy->src.mask * 10;
        pol->sadb_x_policy_priority -= policy->src.proto != IPSEC_PROTO_ANY ? 2 : 0;
        pol->sadb_x_policy_priority -= policy->src.net->get_port(policy->src.net) ? 1 : 0;
@@ -1713,7 +1716,7 @@ static status_t query_policy(private_kernel_pfkey_ipsec_t *this,
 static status_t del_policy(private_kernel_pfkey_ipsec_t *this,
                                                   traffic_selector_t *src_ts, 
                                                   traffic_selector_t *dst_ts,
-                                                  policy_dir_t direction)
+                                                  policy_dir_t direction, bool unrouted)
 {
        unsigned char request[PFKEY_BUFFER_SIZE];
        struct sadb_msg *msg, *out;
@@ -1869,6 +1872,57 @@ static void destroy(private_kernel_pfkey_ipsec_t *this)
        free(this);
 }
 
+/**
+ * Add bypass policies for IKE on the sockets of charon
+ */
+static bool add_bypass_policies(private_kernel_pfkey_ipsec_t *this)
+{
+       int fd, family, port;
+       enumerator_t *sockets;
+       
+       sockets = charon->socket->create_enumerator(charon->socket);
+       while (sockets->enumerate(sockets, &fd, &family, &port))
+       {
+               struct sadb_x_policy policy;
+               u_int sol, ipsec_policy;
+               
+               switch (family)
+               {
+                       case AF_INET:
+                               sol = SOL_IP;
+                               ipsec_policy = IP_IPSEC_POLICY;
+                               break;
+                       case AF_INET6:
+                       {
+                               sol = SOL_IPV6;
+                               ipsec_policy = IPV6_IPSEC_POLICY;
+                               break;
+                       }
+               }
+               
+               memset(&policy, 0, sizeof(policy));
+               policy.sadb_x_policy_len = sizeof(policy) / sizeof(u_int64_t);
+               policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+               policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS;
+       
+               policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND;
+               if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
+               {
+                       DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s",
+                                strerror(errno));
+                       return FALSE;
+               }
+               policy.sadb_x_policy_dir = IPSEC_DIR_INBOUND;
+               if (setsockopt(fd, sol, ipsec_policy, &policy, sizeof(policy)) < 0)
+               {
+                       DBG1(DBG_KNL, "unable to set IPSEC_POLICY on socket: %s", 
+                                strerror(errno));
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
 /*
  * Described in header.
  */
@@ -1879,12 +1933,12 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
        /* public functions */
        this->public.interface.get_spi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
        this->public.interface.get_cpi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,u_int32_t,u_int16_t*))get_cpi;
-       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,bool,bool))add_sa;
-       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*,bool))update_sa;
-       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t))del_sa;
-       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,ipsec_mode_t,u_int16_t))add_policy;
+       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,u_int16_t,bool,bool))add_sa;
+       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,u_int16_t,host_t*,host_t*,host_t*,host_t*,bool,bool))update_sa;
+       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t,u_int16_t))del_sa;
+       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t,protocol_id_t,u_int32_t,ipsec_mode_t,u_int16_t,u_int16_t,bool))add_policy;
        this->public.interface.query_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
-       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy;
+       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,bool))del_policy;
        
        this->public.interface.destroy = (void(*)(kernel_ipsec_t*)) destroy;
 
@@ -1910,6 +1964,12 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
                charon->kill(charon, "unable to create PF_KEY event socket");
        }
        
+       /* add bypass policies on the sockets used by charon */
+       if (!add_bypass_policies(this))
+       {
+               charon->kill(charon, "unable to add bypass policies on sockets");
+       }
+       
        /* register the event socket */
        if (register_pfkey_socket(this, SADB_SATYPE_ESP) != SUCCESS ||
                register_pfkey_socket(this, SADB_SATYPE_AH) != SUCCESS)
index ddb72ac..f057416 100644 (file)
@@ -67,8 +67,8 @@ static status_t add_sa(private_load_tester_ipsec_t *this,
                                           u_int64_t expire_soft, u_int64_t expire_hard,
                                           u_int16_t enc_alg, chunk_t enc_key,
                                           u_int16_t int_alg, chunk_t int_key,
-                                          ipsec_mode_t mode, u_int16_t ipcomp, bool encap,
-                                          bool replace)
+                                          ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+                                          bool encap, bool inbound)
 {
        return SUCCESS;
 }
@@ -77,9 +77,10 @@ static status_t add_sa(private_load_tester_ipsec_t *this,
  * Implementation of kernel_interface_t.update_sa.
  */
 static status_t update_sa(private_load_tester_ipsec_t *this,
-                                                 u_int32_t spi, protocol_id_t protocol,
+                                                 u_int32_t spi, protocol_id_t protocol, u_int16_t cpi,
                                                  host_t *src, host_t *dst,
-                                                 host_t *new_src, host_t *new_dst, bool encap)
+                                                 host_t *new_src, host_t *new_dst,
+                                                 bool encap, bool new_encap)
 {
        return SUCCESS;
 }
@@ -88,7 +89,7 @@ static status_t update_sa(private_load_tester_ipsec_t *this,
  * Implementation of kernel_interface_t.del_sa.
  */
 static status_t del_sa(private_load_tester_ipsec_t *this, host_t *dst,
-                                          u_int32_t spi, protocol_id_t protocol)
+                                          u_int32_t spi, protocol_id_t protocol, u_int16_t cpi)
 {
        return SUCCESS;
 }
@@ -101,8 +102,8 @@ static status_t add_policy(private_load_tester_ipsec_t *this,
                                                   traffic_selector_t *src_ts,
                                                   traffic_selector_t *dst_ts,
                                                   policy_dir_t direction, protocol_id_t protocol,
-                                                  u_int32_t reqid, bool high_prio, ipsec_mode_t mode,
-                                                  u_int16_t ipcomp)
+                                                  u_int32_t reqid, ipsec_mode_t mode, u_int16_t ipcomp,
+                                                  u_int16_t cpi, bool routed)
 {
        return SUCCESS;
 }
@@ -125,7 +126,7 @@ static status_t query_policy(private_load_tester_ipsec_t *this,
 static status_t del_policy(private_load_tester_ipsec_t *this,
                                                   traffic_selector_t *src_ts, 
                                                   traffic_selector_t *dst_ts,
-                                                  policy_dir_t direction)
+                                                  policy_dir_t direction, bool unrouted)
 {
        return SUCCESS;
 }
@@ -148,12 +149,12 @@ load_tester_ipsec_t *load_tester_ipsec_create()
        /* public functions */
        this->public.interface.get_spi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
        this->public.interface.get_cpi = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,u_int32_t,u_int16_t*))get_cpi;
-       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,bool,bool))add_sa;
-       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*,bool))update_sa;
-       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t))del_sa;
-       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,ipsec_mode_t,u_int16_t))add_policy;
+       this->public.interface.add_sa  = (status_t(*)(kernel_ipsec_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,u_int16_t,chunk_t,u_int16_t,chunk_t,ipsec_mode_t,u_int16_t,u_int16_t,bool,bool))add_sa;
+       this->public.interface.update_sa = (status_t(*)(kernel_ipsec_t*,u_int32_t,protocol_id_t,u_int16_t,host_t*,host_t*,host_t*,host_t*,bool,bool))update_sa;
+       this->public.interface.del_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t,u_int16_t))del_sa;
+       this->public.interface.add_policy = (status_t(*)(kernel_ipsec_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,ipsec_mode_t,u_int16_t,u_int16_t,bool))add_policy;
        this->public.interface.query_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
-       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy;
+       this->public.interface.del_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,bool))del_policy;
        this->public.interface.destroy = (void(*)(kernel_ipsec_t*)) destroy;
        
        this->spi = 0;
index 107ca1a..bb54364 100644 (file)
@@ -25,4 +25,4 @@ EXTRA_DIST = gnome/configure gnome/po/LINGUAS gnome/po/POTFILES.in gnome/po/Make
        gnome/config.sub gnome/missing
 
 gnome/configure :      gnome/configure.in
-                                       cd gnome && ./autogen.sh; cd ..
+                                       (cd `dirname $<` && ./autogen.sh)
index 24d91a5..95c7735 100644 (file)
@@ -30,6 +30,7 @@
 ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
        "CREATED",
        "ROUTED",
+       "INSTALLING",
        "INSTALLED",
        "UPDATING",
        "REKEYING",
@@ -444,7 +445,7 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
                                                ipsec_mode_t mode, chunk_t integ, chunk_t encr, bool in)
 {
        u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
-       u_int32_t spi, cpi, soft, hard, now;
+       u_int32_t spi, soft, hard, now;
        host_t *src, *dst;
        status_t status;
        
@@ -461,7 +462,7 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
                        if (this->alloc_ah_spi)
                        {
                                charon->kernel_interface->del_sa(charon->kernel_interface,
-                                                               this->my_addr, this->alloc_ah_spi, PROTO_AH);
+                                                               this->my_addr, this->alloc_ah_spi, 0, PROTO_AH);
                        }
                }
                else
@@ -470,7 +471,7 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
                        if (this->alloc_esp_spi)
                        {
                                charon->kernel_interface->del_sa(charon->kernel_interface,
-                                                               this->my_addr, this->alloc_esp_spi, PROTO_ESP);
+                                                               this->my_addr, this->alloc_esp_spi, 0, PROTO_ESP);
                        }
                }
                spi = this->my_spi;
@@ -491,16 +492,6 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
        /* send SA down to the kernel */
        DBG2(DBG_CHD, "  SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
        
-       if (this->ipcomp != IPCOMP_NONE)
-       {
-               /* we install an additional IPComp SA */
-               cpi = htonl(ntohs(in ? this->my_cpi : this->other_cpi));
-               charon->kernel_interface->add_sa(charon->kernel_interface,
-                               src, dst, cpi, IPPROTO_COMP, this->reqid, 0, 0,
-                               ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED, chunk_empty,
-                               mode, this->ipcomp, FALSE, in);
-       }
-       
        proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &enc_alg, &size);
        proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &int_alg, &size);
        
@@ -509,7 +500,8 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
        status = charon->kernel_interface->add_sa(charon->kernel_interface,
                                src, dst, spi, this->protocol, this->reqid,
                                in ? soft : 0, hard, enc_alg, encr, int_alg, integ,
-                               mode, IPCOMP_NONE, this->encap, in);
+                               mode, this->ipcomp, in ? this->my_cpi : this->other_cpi,
+                               this->encap, in);
        
        now = time(NULL);
        this->rekey_time = now + soft;
@@ -588,12 +580,8 @@ static status_t add_policies(private_child_sa_t *this,
        enumerator_t *enumerator;
        traffic_selector_t *my_ts, *other_ts;
        status_t status = SUCCESS;
-       bool high_prio = TRUE;
+       bool routed = (this->state == CHILD_CREATED);
        
-       if (this->state == CHILD_CREATED)
-       {       /* use low prio for ROUTED policies */
-               high_prio = FALSE;
-       }
        if (this->protocol == PROTO_NONE)
        {       /* update if not set yet */
                this->protocol = proto;
@@ -622,17 +610,20 @@ static status_t add_policies(private_child_sa_t *this,
                        /* install 3 policies: out, in and forward */
                        status |= charon->kernel_interface->add_policy(charon->kernel_interface,
                                        this->my_addr, this->other_addr, my_ts, other_ts, POLICY_OUT,
-                                       this->protocol, this->reqid, high_prio, mode, this->ipcomp);
-               
+                                       this->other_spi, this->protocol, this->reqid, mode, this->ipcomp,
+                                       this->other_cpi, routed);
+                       
                        status |= charon->kernel_interface->add_policy(charon->kernel_interface,
                                        this->other_addr, this->my_addr, other_ts, my_ts, POLICY_IN,
-                                       this->protocol, this->reqid, high_prio, mode, this->ipcomp);
+                                       this->my_spi, this->protocol, this->reqid, mode, this->ipcomp,
+                                       this->my_cpi, routed);
                
                        if (mode == MODE_TUNNEL)
                        {
                                status |= charon->kernel_interface->add_policy(charon->kernel_interface,
-                               this->other_addr, this->my_addr, other_ts, my_ts, POLICY_FWD,
-                               this->protocol, this->reqid, high_prio, mode, this->ipcomp);
+                                       this->other_addr, this->my_addr, other_ts, my_ts, POLICY_FWD,
+                                       this->my_spi, this->protocol, this->reqid, mode, this->ipcomp,
+                                       this->my_cpi, routed);
                        }
                
                        if (status != SUCCESS)
@@ -682,26 +673,23 @@ static status_t update_hosts(private_child_sa_t *this,
        old = this->state;
        set_state(this, CHILD_UPDATING);
        
-       this->encap = encap;
-       
-       if (this->ipcomp != IPCOMP_NONE)
+       /* update our (initator) SA */
+       if (charon->kernel_interface->update_sa(charon->kernel_interface, this->my_spi,
+                       this->protocol, this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
+                       this->other_addr, this->my_addr, other, me,
+                       this->encap, encap) == NOT_SUPPORTED)
        {
-               /* update our (initator) IPComp SA */
-               charon->kernel_interface->update_sa(charon->kernel_interface, 
-                                                       htonl(ntohs(this->my_cpi)),     IPPROTO_COMP,
-                                                       this->other_addr, this->my_addr, other, me, FALSE);
-               /* update his (responder) IPComp SA */
-               charon->kernel_interface->update_sa(charon->kernel_interface,
-                                                       htonl(ntohs(this->other_cpi)), IPPROTO_COMP,
-                                                       this->my_addr, this->other_addr, me, other, FALSE);
+               return NOT_SUPPORTED;
        }
        
-       /* update our (initator) SA */
-       charon->kernel_interface->update_sa(charon->kernel_interface, this->my_spi,
-                       this->protocol, this->other_addr, this->my_addr, other, me, encap);
        /* update his (responder) SA */
-       charon->kernel_interface->update_sa(charon->kernel_interface, this->other_spi, 
-                       this->protocol, this->my_addr, this->other_addr, me, other, encap);
+       if (charon->kernel_interface->update_sa(charon->kernel_interface, this->other_spi, 
+                       this->protocol, this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
+                       this->my_addr, this->other_addr, me, other,
+                       this->encap, encap) == NOT_SUPPORTED)
+       {
+               return NOT_SUPPORTED;
+       }
        
        if (this->config->install_policy(this->config))
        {
@@ -718,13 +706,13 @@ static status_t update_hosts(private_child_sa_t *this,
                        {
                                /* remove old policies first */
                                charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                                                                my_ts, other_ts, POLICY_OUT);
+                                                                                                my_ts, other_ts, POLICY_OUT, FALSE);
                                charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                                                                other_ts, my_ts,  POLICY_IN);
+                                                                                                other_ts, my_ts,  POLICY_IN, FALSE);
                                if (this->mode == MODE_TUNNEL)
                                {
                                        charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                                                                other_ts, my_ts, POLICY_FWD);
+                                                                                                other_ts, my_ts, POLICY_FWD, FALSE);
                                }
 
                                /* check whether we have to update a "dynamic" traffic selector */
@@ -749,16 +737,19 @@ static status_t update_hosts(private_child_sa_t *this,
                
                                /* reinstall updated policies */
                                charon->kernel_interface->add_policy(charon->kernel_interface,
-                                               me, other, my_ts, other_ts, POLICY_OUT, this->protocol,
-                                               this->reqid, TRUE, this->mode, this->ipcomp);
+                                               me, other, my_ts, other_ts, POLICY_OUT, this->other_spi,
+                                               this->protocol, this->reqid, this->mode, this->ipcomp,
+                                               this->other_cpi, FALSE);
                                charon->kernel_interface->add_policy(charon->kernel_interface, 
-                                               other, me, other_ts, my_ts, POLICY_IN, this->protocol,
-                                               this->reqid, TRUE, this->mode, this->ipcomp);
+                                               other, me, other_ts, my_ts, POLICY_IN, this->my_spi,
+                                               this->protocol, this->reqid, this->mode, this->ipcomp,
+                                               this->my_cpi, FALSE);
                                if (this->mode == MODE_TUNNEL)
                                {
                                        charon->kernel_interface->add_policy(charon->kernel_interface,
-                                               other, me, other_ts, my_ts, POLICY_FWD, this->protocol,
-                                               this->reqid, TRUE, this->mode, this->ipcomp);
+                                               other, me, other_ts, my_ts, POLICY_FWD, this->my_spi,
+                                               this->protocol, this->reqid, this->mode, this->ipcomp,
+                                               this->my_cpi, FALSE);
                                }
                        }
                        enumerator->destroy(enumerator);
@@ -779,6 +770,8 @@ static status_t update_hosts(private_child_sa_t *this,
                        this->other_addr = other->clone(other);
                }
        }
+       this->encap = encap;
+       
        set_state(this, old);
        
        return SUCCESS;
@@ -815,6 +808,7 @@ static void destroy(private_child_sa_t *this)
 {
        enumerator_t *enumerator;
        traffic_selector_t *my_ts, *other_ts;
+       bool unrouted = (this->state == CHILD_ROUTED);
        
        set_state(this, CHILD_DESTROYING);
        
@@ -822,32 +816,24 @@ static void destroy(private_child_sa_t *this)
        if (this->my_spi)
        {
                charon->kernel_interface->del_sa(charon->kernel_interface,
-                                       this->my_addr, this->my_spi, this->protocol);
+                                       this->my_addr, this->my_spi, this->protocol,
+                                       this->my_cpi);
        }
        if (this->alloc_esp_spi && this->alloc_esp_spi != this->my_spi)
        {
                charon->kernel_interface->del_sa(charon->kernel_interface,
-                                       this->my_addr, this->alloc_esp_spi, PROTO_ESP);
+                                       this->my_addr, this->alloc_esp_spi, PROTO_ESP, 0);
        }
        if (this->alloc_ah_spi && this->alloc_ah_spi != this->my_spi)
        {
                charon->kernel_interface->del_sa(charon->kernel_interface,
-                                       this->my_addr, this->alloc_ah_spi, PROTO_AH);
+                                       this->my_addr, this->alloc_ah_spi, PROTO_AH, 0);
        }
        if (this->other_spi)
        {
                charon->kernel_interface->del_sa(charon->kernel_interface,
-                                       this->other_addr, this->other_spi, this->protocol);
-       }
-       if (this->my_cpi)
-       {
-               charon->kernel_interface->del_sa(charon->kernel_interface,
-                                       this->my_addr, htonl(ntohs(this->my_cpi)), IPPROTO_COMP);
-       }
-       if (this->other_cpi)
-       {
-               charon->kernel_interface->del_sa(charon->kernel_interface,
-                                       this->other_addr, htonl(ntohs(this->other_cpi)), IPPROTO_COMP);
+                                       this->other_addr, this->other_spi, this->protocol,
+                                       this->other_cpi);
        }
        
        if (this->config->install_policy(this->config))
@@ -857,13 +843,13 @@ static void destroy(private_child_sa_t *this)
                while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
                {
                        charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                                                                my_ts, other_ts, POLICY_OUT);
+                                                                                                my_ts, other_ts, POLICY_OUT, unrouted);
                        charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                                                                other_ts, my_ts, POLICY_IN);
+                                                                                                other_ts, my_ts, POLICY_IN, unrouted);
                        if (this->mode == MODE_TUNNEL)
                        {
                                charon->kernel_interface->del_policy(charon->kernel_interface,
-                                                                                                other_ts, my_ts, POLICY_FWD);
+                                                                                                other_ts, my_ts, POLICY_FWD, unrouted);
                        }
                }
                enumerator->destroy(enumerator);
index e9ad4ac..202573a 100644 (file)
@@ -50,6 +50,11 @@ enum child_sa_state_t {
        CHILD_ROUTED,
        
        /**
+        * Installing an in-use CHILD_SA
+        */
+       CHILD_INSTALLING,
+       
+       /**
         * Installed an in-use CHILD_SA
         */
        CHILD_INSTALLED,
index ab2a789..80e42d9 100644 (file)
@@ -898,8 +898,14 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
                iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
                while (iterator->iterate(iterator, (void**)&child_sa))
                {
-                       child_sa->update_hosts(child_sa, this->my_host, this->other_host,
-                                               this->my_virtual_ip, has_condition(this, COND_NAT_ANY));
+                       if (child_sa->update_hosts(child_sa, this->my_host,
+                                               this->other_host, this->my_virtual_ip,
+                                               has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED)
+                       {
+                               this->public.rekey_child_sa(&this->public,
+                                               child_sa->get_protocol(child_sa),
+                                               child_sa->get_spi(child_sa, TRUE));
+                       }
                }
                iterator->destroy(iterator);
        }
@@ -1314,6 +1320,7 @@ static status_t route(private_ike_sa_t *this, child_cfg_t *child_cfg)
        }
        else
        {
+               child_sa->destroy(child_sa);
                DBG1(DBG_IKE, "routing CHILD_SA failed");
        }
        return status;
index 628d139..0266433 100644 (file)
@@ -327,20 +327,14 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
                }
        }
        
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
+       
        if (this->ipcomp != IPCOMP_NONE)
        {
                this->child_sa->activate_ipcomp(this->child_sa, this->ipcomp,
                                                                                this->other_cpi);
        }
        
-       status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
-                                       this->mode, this->proposal->get_protocol(this->proposal));
-       if (status != SUCCESS)
-       {       
-               DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
-               return NOT_FOUND;
-       }
-       
        status = FAILED;
        if (this->keymat->derive_child_keys(this->keymat, this->proposal,
                        this->dh, nonce_i, nonce_r,     &encr_i, &integ_i, &encr_r, &integ_r))
@@ -367,6 +361,14 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh)
                return FAILED;
        }
        
+       status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
+                                       this->mode, this->proposal->get_protocol(this->proposal));
+       if (status != SUCCESS)
+       {       
+               DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
+               return NOT_FOUND;
+       }
+       
        charon->bus->child_keys(charon->bus, this->child_sa, this->dh,
                                                        nonce_i, nonce_r);
        
index b3bffd6..026190b 100644 (file)
@@ -251,11 +251,16 @@ static void update_children(private_ike_mobike_t *this)
        iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
        while (iterator->iterate(iterator, (void**)&child_sa))
        {
-               child_sa->update_hosts(child_sa,
-                                               this->ike_sa->get_my_host(this->ike_sa), 
-                                               this->ike_sa->get_other_host(this->ike_sa),
-                                               this->ike_sa->get_virtual_ip(this->ike_sa, TRUE),
-                                               this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
+               if (child_sa->update_hosts(child_sa,
+                               this->ike_sa->get_my_host(this->ike_sa), 
+                               this->ike_sa->get_other_host(this->ike_sa),
+                               this->ike_sa->get_virtual_ip(this->ike_sa, TRUE),
+                               this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED)
+               {
+                       this->ike_sa->rekey_child_sa(this->ike_sa,
+                                       child_sa->get_protocol(child_sa),
+                                       child_sa->get_spi(child_sa, TRUE));
+               }
        }
        iterator->destroy(iterator);
 }
index 4ff50a6..0a5e753 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "task.h"
 
+#ifdef ME
 ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
        "IKE_INIT",
        "IKE_NATD",
@@ -31,11 +32,27 @@ ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
        "IKE_REAUTH",
        "IKE_DELETE",
        "IKE_DPD",
-#ifdef ME
        "IKE_ME",
-#endif /* ME */
        "CHILD_CREATE",
        "CHILD_DELETE",
        "CHILD_REKEY",
 );
-
+#else
+ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
+       "IKE_INIT",
+       "IKE_NATD",
+       "IKE_MOBIKE",
+       "IKE_AUTHENTICATE",
+       "IKE_AUTH_LIFETIME",
+       "IKE_CERT_PRE",
+       "IKE_CERT_POST",
+       "IKE_CONFIG",
+       "IKE_REKEY",
+       "IKE_REAUTH",
+       "IKE_DELETE",
+       "IKE_DPD",
+       "CHILD_CREATE",
+       "CHILD_DELETE",
+       "CHILD_REKEY",
+);
+#endif /* ME */
index 6a1d824..02bfed8 100644 (file)
@@ -173,7 +173,7 @@ host_t *host_create_from_dns(char *string, int family, u_int16_t port);
  * Constructor to create a host_t object from an address chunk
  *
  * @param family               Address family, such as AF_INET or AF_INET6
- * @param address              address as chunk_t in networ order
+ * @param address              address as chunk_t in network order
  * @param port                 port number
  * @return                             host_t, NULL if family not supported/chunk invalid
  */
index 1ba3e47..5ee614f 100644 (file)
@@ -1,9 +1,9 @@
 ipsec_PROGRAMS = starter
 starter_SOURCES = y.tab.c netkey.c y.tab.h parser.h args.h netkey.h \
 starterwhack.c starterwhack.h starterstroke.c invokepluto.c confread.c \
-starterstroke.h interfaces.c invokepluto.h confread.h  interfaces.h args.c \
+starterstroke.h interfaces.c invokepluto.h confread.h interfaces.h args.c \
 keywords.c files.h keywords.h cmp.c starter.c cmp.h exec.c invokecharon.c \
-exec.h invokecharon.h lex.yy.c loglite.c
+exec.h invokecharon.h lex.yy.c loglite.c klips.c klips.h
 
 INCLUDES = -I$(top_srcdir)/src/libfreeswan -I$(top_srcdir)/src/pluto -I$(top_srcdir)/src/whack -I$(top_srcdir)/src/stroke
 AM_CFLAGS = -DIPSEC_DIR=\"${ipsecdir}\" -DIPSEC_CONFDIR=\"${confdir}\" -DIPSEC_PIDDIR=\"${piddir}\" -DIPSEC_EAPDIR=\"${eapdir}\" -DDEBUG
index b2c9145..05ab2cb 100644 (file)
@@ -19,7 +19,8 @@
 
 #define STARTER_PID_FILE IPSEC_PIDDIR "/starter.pid"
 
-#define PROC_NETKEY    "/proc/net/pfkey"
+#define PROC_NETKEY            "/proc/net/pfkey"
+#define PROC_KLIPS             "/proc/net/pf_key"
 #define PROC_MODULES   "/proc/modules"
 
 #define CONFIG_FILE     IPSEC_CONFDIR "/ipsec.conf"
diff --git a/src/starter/klips.c b/src/starter/klips.c
new file mode 100644 (file)
index 0000000..bed1674
--- /dev/null
@@ -0,0 +1,82 @@
+/* strongSwan KLIPS starter
+ * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+
+#include <freeswan.h>
+
+#include "../pluto/constants.h"
+#include "../pluto/defs.h"
+#include "../pluto/log.h"
+
+#include "files.h"
+
+bool
+starter_klips_init(void)
+{
+    struct stat stb;
+
+    if (stat(PROC_KLIPS, &stb) != 0)
+    {
+       /* ipsec module makes the pf_key proc interface visible */
+       if (stat(PROC_MODULES, &stb) == 0)
+       {
+           system("modprobe -qv ipsec");
+       }
+
+       /* now test again */
+       if (stat(PROC_KLIPS, &stb) != 0)
+       {
+           DBG(DBG_CONTROL,
+               DBG_log("kernel appears to lack the KLIPS IPsec stack")
+           )
+           return FALSE;
+       }
+    }
+    
+    /* load crypto algorithm modules */
+    system("modprobe -qv ipsec_aes");
+    system("modprobe -qv ipsec_blowfish");
+       system("modprobe -qv ipsec_sha2");
+
+    DBG(DBG_CONTROL,
+       DBG_log("Found KLIPS IPsec stack")
+    )
+    
+    return TRUE;
+}
+
+void
+starter_klips_cleanup(void)
+{
+       if (system("type eroute > /dev/null 2>&1") == 0)
+       {
+               system("spi --clear");
+        system("eroute --clear");
+       }
+       else if (system("type setkey > /dev/null 2>&1") == 0)
+    {
+       system("setkey -F");
+        system("setkey -FP");
+    }
+    else
+    {
+        plog("WARNING: cannot flush IPsec state/policy database");
+    }
+}
+
diff --git a/src/starter/klips.h b/src/starter/klips.h
new file mode 100644 (file)
index 0000000..96e89bb
--- /dev/null
@@ -0,0 +1,24 @@
+/* strongSwan KLIPS initialization and cleanup 
+ * Copyright (C) 2001-2002 Mathieu Lafon - Arkoon Network Security
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * RCSID $Id$
+ */
+
+#ifndef _STARTER_KLIPS_H_
+#define _STARTER_KLIPS_H_
+
+extern bool starter_klips_init (void);
+extern void starter_klips_cleanup (void);
+
+#endif /* _STARTER_KLIPS_H_ */
+
index ad78d14..0166f1b 100644 (file)
@@ -42,6 +42,7 @@
 #include "invokepluto.h"
 #include "invokecharon.h"
 #include "netkey.h"
+#include "klips.h"
 #include "cmp.h"
 #include "interfaces.h"
 
@@ -324,7 +325,11 @@ int main (int argc, char **argv)
     if (!starter_netkey_init())
     {
        plog("no netkey IPSec stack detected");
-       exit(LSB_RC_FAILURE);
+       if (!starter_klips_init())
+       {
+           plog("no KLIPS IPSec stack detected");
+           exit(LSB_RC_FAILURE);
+       }
     }
 
     last_reload = time(NULL);