merging kernel_pfkey plugin back from kernel-interface branch
authorTobias Brunner <tobias@strongswan.org>
Tue, 14 Oct 2008 08:46:31 +0000 (08:46 -0000)
committerTobias Brunner <tobias@strongswan.org>
Tue, 14 Oct 2008 08:46:31 +0000 (08:46 -0000)
23 files changed:
configure.in
src/_copyright/Makefile.am
src/charon/Makefile.am
src/charon/config/traffic_selector.c
src/charon/config/traffic_selector.h
src/charon/kernel/kernel_interface.c
src/charon/kernel/kernel_interface.h
src/charon/kernel/kernel_ipsec.c
src/charon/kernel/kernel_ipsec.h
src/charon/plugins/kernel_netlink/kernel_netlink_ipsec.c
src/charon/plugins/kernel_pfkey/Makefile.am [new file with mode: 0644]
src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c [new file with mode: 0644]
src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.h [new file with mode: 0644]
src/charon/plugins/kernel_pfkey/kernel_pfkey_plugin.c [new file with mode: 0644]
src/charon/plugins/kernel_pfkey/kernel_pfkey_plugin.h [new file with mode: 0644]
src/charon/plugins/nm/nm_service.c
src/dumm/Makefile.am
src/include/linux/ipsec.h
src/libstrongswan/Makefile.am
src/libstrongswan/utils/linked_list.c
src/libstrongswan/utils/linked_list.h
src/starter/Makefile.am
src/stroke/Makefile.am

index 8f3b7fa..117dcf3 100644 (file)
@@ -445,6 +445,14 @@ AC_ARG_ENABLE(
 )
 
 AC_ARG_ENABLE(
+       [kernel-pfkey],
+       AS_HELP_STRING([--enable-kernel-pfkey],[enable the PF_KEY kernel interface. (default is NO).]),
+       [if test x$enableval = xyes; then
+               kernel_pfkey=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
@@ -874,6 +882,7 @@ AM_CONDITIONAL(USE_EAP_MD5, test x$eap_md5 = xtrue)
 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)
 
 dnl other options
 dnl =============
@@ -949,6 +958,7 @@ AC_OUTPUT(
        src/charon/plugins/eap_sim/Makefile
        src/charon/plugins/eap_sim_file/Makefile
        src/charon/plugins/kernel_netlink/Makefile
+       src/charon/plugins/kernel_pfkey/Makefile
        src/charon/plugins/smp/Makefile
        src/charon/plugins/sql/Makefile
        src/charon/plugins/medsrv/Makefile
index d8dcfb3..00d5fb2 100644 (file)
@@ -3,4 +3,4 @@ _copyright_SOURCES = _copyright.c
 dist_man8_MANS = _copyright.8
 
 INCLUDES = -I$(top_srcdir)/src/libfreeswan
-_copyright_LDADD = $(top_srcdir)/src/libfreeswan/libfreeswan.a
+_copyright_LDADD = $(top_builddir)/src/libfreeswan/libfreeswan.a
index c9f63ac..85111af 100644 (file)
@@ -144,6 +144,11 @@ endif
 SUBDIRS = . 
 PLUGINS = ${libstrongswan_plugins}
 
+if USE_KERNEL_PFKEY
+  SUBDIRS += plugins/kernel_pfkey
+  PLUGINS += kernel-pfkey
+endif
+
 if USE_KERNEL_NETLINK
   SUBDIRS += plugins/kernel_netlink
   PLUGINS += kernel-netlink
index a12e750..b94003c 100644 (file)
@@ -134,8 +134,9 @@ static u_int8_t calc_netbits(private_traffic_selector_t *this)
        int byte, bit;
        size_t size = (this->type == TS_IPV4_ADDR_RANGE) ? 4 : 16;
        
-       /* go trough all bits of the addresses, begging in the front. 
-        * As longer as they equal, the subnet gets larger */
+       /* go trough all bits of the addresses, beginning in the front.
+        * as long as they are equal, the subnet gets larger
+        */
        for (byte = 0; byte < size; byte++)
        {
                for (bit = 7; bit >= 0; bit--)
@@ -583,6 +584,55 @@ static bool includes(private_traffic_selector_t *this, host_t *host)
 }
 
 /**
+ * Implements traffic_selector_t.to_subnet.
+ */
+static void to_subnet(private_traffic_selector_t *this, host_t **net, u_int8_t *mask)
+{
+       /* there is no way to do this cleanly, as the address range may
+        * be anything else but a subnet. We use from_addr as subnet 
+        * and try to calculate a usable subnet mask.
+        */
+       int family, byte;
+       u_int16_t port = 0;
+       chunk_t net_chunk;
+       
+       *mask = calc_netbits(this);
+       
+       switch (this->type)
+       {
+               case TS_IPV4_ADDR_RANGE:
+               {
+                       family = AF_INET;
+                       net_chunk.len = sizeof(this->from4);
+                       break;
+               }
+               case TS_IPV6_ADDR_RANGE:
+               {
+                       family = AF_INET6;
+                       net_chunk.len = sizeof(this->from6);
+                       break;
+               }
+       }
+       
+       net_chunk.ptr = malloc(net_chunk.len);
+       memcpy(net_chunk.ptr, this->from, net_chunk.len);
+       
+       for (byte = net_chunk.len - 1; byte >= (*mask / 8); --byte)
+       {
+               int shift = (byte + 1) * 8 - *mask;
+               net_chunk.ptr[byte] = net_chunk.ptr[byte] & (0xFF << shift);
+       }
+       
+       if (this->to_port == this->from_port)
+       {
+               port = this->to_port;
+       }
+       
+       *net = host_create_from_chunk(family, net_chunk, port); 
+       chunk_free(&net_chunk);
+}
+
+/**
  * Implements traffic_selector_t.clone.
  */
 static traffic_selector_t *clone_(private_traffic_selector_t *this)
@@ -817,6 +867,7 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol,
        this->public.is_contained_in = (bool(*)(traffic_selector_t*,traffic_selector_t*))is_contained_in;
        this->public.includes = (bool(*)(traffic_selector_t*,host_t*))includes;
        this->public.set_address = (void(*)(traffic_selector_t*,host_t*))set_address;
+       this->public.to_subnet = (void(*)(traffic_selector_t*,host_t**,u_int8_t*))to_subnet;
        this->public.clone = (traffic_selector_t*(*)(traffic_selector_t*))clone_;
        this->public.destroy = (void(*)(traffic_selector_t*))destroy;
        
index a9fa9d7..2217962 100644 (file)
@@ -191,6 +191,17 @@ struct traffic_selector_t {
        bool (*includes) (traffic_selector_t *this, host_t *host);
        
        /**
+        * Convert a traffic selector address range to a subnet
+        * and its net mask.
+        * If from and to ports of this traffic selector are equal,
+        * the port of the returned host_t is set to that port.
+        * 
+        * @param net           converted subnet (has to be freed)
+        * @param mask          converted net mask
+        */
+       void (*to_subnet) (traffic_selector_t *this, host_t **net, u_int8_t *mask);
+       
+       /**
         * Destroys the ts object
         */
        void (*destroy) (traffic_selector_t *this);
index 7a0ffa1..e2d508d 100644 (file)
@@ -107,15 +107,6 @@ static status_t update_sa(private_kernel_interface_t *this, u_int32_t spi,
 }
 
 /**
- * Implementation of kernel_interface_t.query_sa
- */
-static status_t query_sa(private_kernel_interface_t *this, host_t *dst, u_int32_t spi, 
-                                 protocol_id_t protocol, u_int32_t *use_time)
-{
-       return this->ipsec->query_sa(this->ipsec, dst, spi, protocol, use_time);
-}
-
-/**
  * Implementation of kernel_interface_t.del_sa
  */
 static status_t del_sa(private_kernel_interface_t *this, host_t *dst, u_int32_t spi,
@@ -231,6 +222,64 @@ static status_t del_route(private_kernel_interface_t *this, chunk_t dst_net,
 
 
 /**
+ * Implementation of kernel_interface_t.get_address_by_ts
+ */
+static status_t get_address_by_ts(private_kernel_interface_t *this,
+                                                                 traffic_selector_t *ts, host_t **ip)
+{
+       enumerator_t *addrs;
+       host_t *host;
+       int family;
+       bool found = FALSE;
+       
+       DBG2(DBG_KNL, "getting a local address in traffic selector %R", ts);
+       
+       /* if we have a family which includes localhost, we do not
+        * search for an IP, we use the default */
+       family = ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
+       
+       if (family == AF_INET)
+       {
+               host = host_create_from_string("127.0.0.1", 0);
+       }
+       else
+       {
+               host = host_create_from_string("::1", 0);
+       }
+       
+       if (ts->includes(ts, host))
+       {
+               *ip = host_create_any(family);
+               host->destroy(host);
+               DBG2(DBG_KNL, "using host %H", *ip);
+               return SUCCESS;
+       }
+       host->destroy(host);
+       
+       addrs = this->public.create_address_enumerator(&this->public, TRUE, TRUE);
+       while (addrs->enumerate(addrs, (void**)&host))
+       {
+               if (ts->includes(ts, host))
+               {
+                       found = TRUE;
+                       *ip = host->clone(host);
+                       break;
+               }
+       }
+       addrs->destroy(addrs);
+       
+       if (!found)
+       {
+               DBG1(DBG_KNL, "no local address found in traffic selector %R", ts);
+               return FAILED;
+       }
+       
+       DBG2(DBG_KNL, "using host %H", *ip);
+       return SUCCESS;
+}
+
+
+/**
  * Implementation of kernel_interface_t.add_ipsec_interface.
  */
 static void add_ipsec_interface(private_kernel_interface_t *this,
@@ -253,7 +302,7 @@ static void remove_ipsec_interface(private_kernel_interface_t *this,
 }
 
 /**
- * Implementation of kernel_interface_t.add_ipsec_interface.
+ * Implementation of kernel_interface_t.add_net_interface.
  */
 static void add_net_interface(private_kernel_interface_t *this,
                kernel_net_constructor_t *create)
@@ -264,7 +313,7 @@ static void add_net_interface(private_kernel_interface_t *this,
 }
 
 /**
- * Implementation of kernel_interface_t.remove_ipsec_interface.
+ * Implementation of kernel_interface_t.remove_net_interface.
  */
 static void remove_net_interface(private_kernel_interface_t *this,
                kernel_net_constructor_t *create)
@@ -324,7 +373,6 @@ kernel_interface_t *kernel_interface_create()
        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,u_int16_t,u_int16_t,u_int16_t,prf_plus_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.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_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.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
@@ -339,6 +387,8 @@ kernel_interface_t *kernel_interface_create()
        this->public.add_route = (status_t(*)(kernel_interface_t*,chunk_t,u_int8_t,host_t*,host_t*,char*)) add_route;
        this->public.del_route = (status_t(*)(kernel_interface_t*,chunk_t,u_int8_t,host_t*,host_t*,char*)) del_route;
        
+       this->public.get_address_by_ts = (status_t(*)(kernel_interface_t*,traffic_selector_t*,host_t**))get_address_by_ts;
+       
        this->public.add_ipsec_interface = (void(*)(kernel_interface_t*, kernel_ipsec_constructor_t))add_ipsec_interface;
        this->public.remove_ipsec_interface = (void(*)(kernel_interface_t*, kernel_ipsec_constructor_t))remove_ipsec_interface;
        this->public.add_net_interface = (void(*)(kernel_interface_t*, kernel_net_constructor_t))add_net_interface;
index c87e825..a2a83b6 100644 (file)
@@ -144,21 +144,6 @@ struct kernel_interface_t {
                                                  host_t *new_src, host_t *new_dst, bool encap);
        
        /**
-        * Query the use time of an SA.
-        *
-        * The use time of an SA is not the time of the last usage, but 
-        * the time of the first usage of the SA.
-        * 
-        * @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 use_time              pointer receives the time of this SA's last use
-        * @return                              SUCCESS if operation completed
-        */
-       status_t (*query_sa) (kernel_interface_t *this, host_t *dst, u_int32_t spi, 
-                                                 protocol_id_t protocol, u_int32_t *use_time);
-       
-       /**
         * Delete a previously installed SA from the SAD.
         * 
         * @param dst                   destination address for this SA
@@ -335,6 +320,17 @@ struct kernel_interface_t {
         */
        
        /**
+        * Tries to find an ip address of a local interface that is included in the
+        * supplied traffic selector.
+        * 
+        * @param ts                    traffic selector
+        * @param ip                    returned ip (has to be destroyed)
+        * @return                              SUCCESS if address found
+        */
+       status_t (*get_address_by_ts) (kernel_interface_t *this,
+                                                                               traffic_selector_t *ts, host_t **ip);
+       
+       /**
         * Register an ipsec kernel interface constructor on the manager.
         *
         * @param create                        constructor to register
index e12e009..579ac72 100644 (file)
@@ -24,3 +24,10 @@ ENUM(ipsec_mode_names, MODE_TRANSPORT, MODE_BEET,
        "3",
        "BEET",
 );
+
+ENUM(policy_dir_names, POLICY_IN, POLICY_FWD,
+       "in",
+       "out",
+       "fwd"
+);
+
index 342d6ce..8fa5fb0 100644 (file)
@@ -68,6 +68,11 @@ enum policy_dir_t {
 };
 
 /**
+ * enum names for policy_dir_t.
+ */
+extern enum_name_t *policy_dir_names;
+
+/**
  * Interface to the ipsec subsystem of the kernel.
  * 
  * The kernel ipsec interface handles the communication with the kernel
@@ -171,21 +176,6 @@ struct kernel_ipsec_t {
                                                  host_t *new_src, host_t *new_dst, bool encap);
        
        /**
-        * Query the use time of an SA.
-        *
-        * The use time of an SA is not the time of the last usage, but 
-        * the time of the first usage of the SA.
-        * 
-        * @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 use_time              pointer receives the time of this SA's last use
-        * @return                              SUCCESS if operation completed
-        */
-       status_t (*query_sa) (kernel_ipsec_t *this, host_t *dst, u_int32_t spi, 
-                                                 protocol_id_t protocol, u_int32_t *use_time);
-       
-       /**
         * Delete a previusly installed SA from the SAD.
         * 
         * @param dst                   destination address for this SA
index 3fe6823..7fb6a26 100644 (file)
@@ -96,12 +96,6 @@ struct kernel_algorithm_t {
        u_int key_size;
 };
 
-ENUM(policy_dir_names, POLICY_IN, POLICY_FWD,
-       "in",
-       "out",
-       "fwd"
-);
-
 #define END_OF_LIST -1
 
 /**
@@ -221,9 +215,6 @@ struct policy_entry_t {
        /** direction of this policy: in, out, forward */
        u_int8_t direction;
        
-       /** reqid of the policy */
-       u_int32_t reqid;
-       
        /** parameters of installed policy */
        struct xfrm_selector sel;
        
@@ -344,41 +335,13 @@ static host_t* xfrm2host(int family, xfrm_address_t *xfrm, u_int16_t port)
 static void ts2subnet(traffic_selector_t* ts, 
                                          xfrm_address_t *net, u_int8_t *mask)
 {
-       /* there is no way to do this cleanly, as the address range may
-        * be anything else but a subnet. We use from_addr as subnet 
-        * and try to calculate a usable subnet mask.
-        */
-       int byte, bit;
-       bool found = FALSE;
-       chunk_t from, to;
-       size_t size = (ts->get_type(ts) == TS_IPV4_ADDR_RANGE) ? 4 : 16;
+       host_t *net_host;
+       chunk_t net_chunk;
        
-       from = ts->get_from_address(ts);
-       to = ts->get_to_address(ts);
-       
-       *mask = (size * 8);
-       /* go trough all bits of the addresses, beginning in the front.
-        * as long as they are equal, the subnet gets larger
-        */
-       for (byte = 0; byte < size; byte++)
-       {
-               for (bit = 7; bit >= 0; bit--)
-               {
-                       if ((1<<bit & from.ptr[byte]) != (1<<bit & to.ptr[byte]))
-                       {
-                               *mask = ((7 - bit) + (byte * 8));
-                               found = TRUE;
-                               break;
-                       }
-               }
-               if (found)
-               {
-                       break;
-               }
-       }
-       memcpy(net, from.ptr, from.len);
-       chunk_free(&from);
-       chunk_free(&to);
+       ts->to_subnet(ts, &net_host, mask);
+       net_chunk = net_host->get_address(net_host);
+       memcpy(net, net_chunk.ptr, net_chunk.len);
+       net_host->destroy(net_host);
 }
 
 /**
@@ -534,7 +497,7 @@ static void process_mapping(private_kernel_netlink_ipsec_t *this,
                if (host)
                {
                        DBG1(DBG_KNL, "NAT mappings of ESP CHILD_SA with SPI %.8x and "
-                               "reqid {%d} changed, queueing update job", ntohl(spi), reqid);
+                               "reqid {%d} changed, queuing update job", ntohl(spi), reqid);
                        job = (job_t*)update_sa_job_create(reqid, host);
                        charon->processor->queue_job(charon->processor, job);
                }
@@ -601,64 +564,6 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this)
 }
 
 /**
- * Tries to find an ip address of a local interface that is included in the
- * supplied traffic selector.
- */
-static status_t get_address_by_ts(private_kernel_netlink_ipsec_t *this,
-                                                                 traffic_selector_t *ts, host_t **ip)
-{
-       enumerator_t *addrs;
-       host_t *host;
-       int family;
-       bool found = FALSE;
-       
-       DBG2(DBG_KNL, "getting a local address in traffic selector %R", ts);
-       
-       /* if we have a family which includes localhost, we do not
-        * search for an IP, we use the default */
-       family = ts->get_type(ts) == TS_IPV4_ADDR_RANGE ? AF_INET : AF_INET6;
-       
-       if (family == AF_INET)
-       {
-               host = host_create_from_string("127.0.0.1", 0);
-       }
-       else
-       {
-               host = host_create_from_string("::1", 0);
-       }
-       
-       if (ts->includes(ts, host))
-       {
-               *ip = host_create_any(family);
-               host->destroy(host);
-               DBG2(DBG_KNL, "using host %H", *ip);
-               return SUCCESS;
-       }
-       host->destroy(host);
-       
-       addrs = charon->kernel_interface->create_address_enumerator(
-                               charon->kernel_interface, TRUE, TRUE);
-       while (addrs->enumerate(addrs, (void**)&host))
-       {
-               if (ts->includes(ts, host))
-               {
-                       found = TRUE;
-                       *ip = host->clone(host);
-                       break;
-               }
-       }
-       addrs->destroy(addrs);
-       
-       if (!found)
-       {
-               DBG1(DBG_KNL, "no local address found in traffic selector %R", ts);
-               return FAILED;
-       }
-       DBG2(DBG_KNL, "using host %H", *ip);
-       return SUCCESS;
-}
-
-/**
  * Get an SPI for a specific protocol from the kernel.
  */
 static status_t get_spi_internal(private_kernel_netlink_ipsec_t *this,
@@ -1257,74 +1162,6 @@ static status_t update_sa(private_kernel_netlink_ipsec_t *this,
 }
 
 /**
- * Implementation of kernel_interface_t.query_sa.
- */
-static status_t query_sa(private_kernel_netlink_ipsec_t *this, host_t *dst,
-                                                u_int32_t spi, protocol_id_t protocol,
-                                                u_int32_t *use_time)
-{
-       unsigned char request[NETLINK_BUFFER_SIZE];
-       struct nlmsghdr *out = NULL, *hdr;
-       struct xfrm_usersa_id *sa_id;
-       struct xfrm_usersa_info *sa = NULL;
-       size_t len;
-       
-       DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi));
-       memset(&request, 0, sizeof(request));
-       
-       hdr = (struct nlmsghdr*)request;
-       hdr->nlmsg_flags = NLM_F_REQUEST;
-       hdr->nlmsg_type = XFRM_MSG_GETSA;
-       hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
-
-       sa_id = (struct xfrm_usersa_id*)NLMSG_DATA(hdr);
-       host2xfrm(dst, &sa_id->daddr);
-       sa_id->spi = spi;
-       sa_id->proto = proto_ike2kernel(protocol);
-       sa_id->family = dst->get_family(dst);
-       
-       if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
-       {
-               hdr = out;
-               while (NLMSG_OK(hdr, len))
-               {
-                       switch (hdr->nlmsg_type)
-                       {
-                               case XFRM_MSG_NEWSA:
-                               {
-                                       sa = NLMSG_DATA(hdr);
-                                       break;
-                               }
-                               case NLMSG_ERROR:
-                               {
-                                       struct nlmsgerr *err = NLMSG_DATA(hdr);
-                                       DBG1(DBG_KNL, "querying SAD entry failed: %s (%d)",
-                                                strerror(-err->error), -err->error);
-                                       break;
-                               }
-                               default:
-                                       hdr = NLMSG_NEXT(hdr, len);
-                                       continue;
-                               case NLMSG_DONE:
-                                       break;
-                       }
-                       break;
-               }
-       }
-       
-       if (sa == NULL)
-       {
-               DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x", ntohl(spi));
-               free(out);
-               return FAILED;
-       }
-       
-       *use_time = sa->curlft.use_time;
-       free (out);
-       return SUCCESS;
-}
-
-/**
  * Implementation of kernel_interface_t.del_sa.
  */
 static status_t del_sa(private_kernel_netlink_ipsec_t *this, host_t *dst,
@@ -1503,7 +1340,8 @@ static status_t add_policy(private_kernel_netlink_ipsec_t *this,
        {
                route_entry_t *route = malloc_thing(route_entry_t);
                
-               if (get_address_by_ts(this, dst_ts, &route->src_ip) == SUCCESS)
+               if (charon->kernel_interface->get_address_by_ts(charon->kernel_interface,
+                               dst_ts, &route->src_ip) == SUCCESS)
                {
                        /* get the nexthop to src (src as we are in POLICY_FWD).*/
                        route->gateway = charon->kernel_interface->get_nexthop(
@@ -1638,7 +1476,7 @@ static status_t del_policy(private_kernel_netlink_ipsec_t *this,
        iterator = this->policies->create_iterator_locked(this->policies, &this->mutex);
        while (iterator->iterate(iterator, (void**)&current))
        {
-               if (memcmp(&current->sel, &policy.sel, sizeof(struct xfrm_selector)) == 0 &&
+               if (memeq(&current->sel, &policy.sel, sizeof(struct xfrm_selector)) &&
                        policy.direction == current->direction)
                {
                        to_delete = current;
@@ -1723,7 +1561,6 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
        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,u_int16_t,u_int16_t,u_int16_t,prf_plus_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.query_sa = (status_t(*)(kernel_ipsec_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_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.query_policy = (status_t(*)(kernel_ipsec_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
diff --git a/src/charon/plugins/kernel_pfkey/Makefile.am b/src/charon/plugins/kernel_pfkey/Makefile.am
new file mode 100644 (file)
index 0000000..c9d66b5
--- /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-pfkey.la
+
+libstrongswan_kernel_pfkey_la_SOURCES = kernel_pfkey_plugin.h kernel_pfkey_plugin.c \
+       kernel_pfkey_ipsec.h kernel_pfkey_ipsec.c
+libstrongswan_kernel_pfkey_la_LDFLAGS = -module
diff --git a/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
new file mode 100644 (file)
index 0000000..dec43e2
--- /dev/null
@@ -0,0 +1,1810 @@
+/*
+ * 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 <stdint.h>
+#include <linux/ipsec.h>
+#include <linux/pfkeyv2.h>
+#include <linux/udp.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "kernel_pfkey_ipsec.h"
+
+#include <daemon.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 priority of installed policies */
+#define PRIO_LOW 3000
+#define PRIO_HIGH 2000
+
+/** 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))
+
+typedef struct private_kernel_pfkey_ipsec_t private_kernel_pfkey_ipsec_t;
+
+/**
+ * Private variables and functions of kernel_pfkey class.
+ */
+struct private_kernel_pfkey_ipsec_t
+{
+       /**
+        * Public part of the kernel_pfkey_t object.
+        */
+       kernel_pfkey_ipsec_t public;
+       
+       /**
+        * mutex to lock access to various lists
+        */
+       pthread_mutex_t mutex;
+       
+       /**
+        * List of installed policies (policy_entry_t)
+        */
+       linked_list_t *policies;
+       
+       /**
+        * whether to install routes along policies
+        */
+       bool install_routes;
+       
+       /**
+        * job receiving PF_KEY events
+        */
+       callback_job_t *job;
+       
+       /**
+        * mutex to lock access to the PF_KEY socket
+        */
+       pthread_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 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 */
+       u_int32_t reqid;
+       
+       /** index assigned by the kernel */
+       u_int32_t index;
+       
+       /** 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 used */
+       u_int refcount;
+};
+
+/**
+ * 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, u_int32_t reqid)
+{
+       policy_entry_t *policy = malloc_thing(policy_entry_t);
+       policy->reqid = reqid;
+       policy->index = 0;
+       policy->direction = dir;
+       policy->route = NULL;
+       policy->refcount = 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 : IPSEC_PROTO_ANY; 
+       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);
+}
+
+/**
+ * compare the given kernel index with that of a policy
+ */
+static inline bool policy_entry_match_byindex(policy_entry_t *current, u_int32_t *index)
+{
+       return current->index == *index;
+}
+
+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_x_policy *x_policy;                 /* SADB_X_EXT_POLICY */
+                       struct sadb_x_sa2 *x_sa2;                               /* SADB_X_EXT_SA2 */
+                       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 */
+                       struct sadb_x_sec_ctx *x_sec_ctx;               /* SADB_X_EXT_SEC_CTX */
+               } __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_IPCOMP;
+               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_IPCOMP:
+                       return IPPROTO_COMP;
+               default:
+                       return proto;
+       }
+}
+
+/**
+ * convert a IKEv2 specific protocol identifier to the IP protocol identifier
+ */
+static u_int8_t proto_ike2ip(protocol_id_t proto)
+{
+       switch (proto)
+       {
+               case PROTO_ESP:
+                       return IPPROTO_ESP;
+               case PROTO_AH:
+                       return IPPROTO_AH;
+               default:
+                       return proto;
+       }
+}
+
+/**
+ * convert the general ipsec mode to the one defined in ipsec.h
+ */
+static u_int8_t mode2kernel(ipsec_mode_t mode)
+{
+       switch (mode)
+       {
+               case MODE_TRANSPORT:
+                       return IPSEC_MODE_TRANSPORT;
+               case MODE_TUNNEL:
+                       return IPSEC_MODE_TUNNEL;
+               case MODE_BEET:
+                       return IPSEC_MODE_BEET;
+               default:
+                       return mode;
+       }
+}
+
+/**
+ * convert the general policy direction to the one defined in ipsec.h
+ */
+static u_int8_t dir2kernel(policy_dir_t dir)
+{
+       switch (dir)
+       {
+               case POLICY_IN:
+                       return IPSEC_DIR_INBOUND;
+               case POLICY_OUT:
+                       return IPSEC_DIR_OUTBOUND;
+               case POLICY_FWD:
+                       return IPSEC_DIR_FWD;
+               default:
+                       return dir;
+       }
+}
+
+typedef struct kernel_algorithm_t kernel_algorithm_t;
+
+/**
+ * Mapping from the algorithms defined in IKEv2 to
+ * kernel level algorithm identifiers and their key length
+ */
+struct kernel_algorithm_t {
+       /**
+        * Identifier specified in IKEv2
+        */
+       int ikev2_id;
+       
+       /**
+        * Identifier as defined in pfkeyv2.h
+        */
+       int kernel_id;
+       
+       /**
+        * Key length in bits, if fixed size
+        */
+       u_int key_size;
+};
+
+#define END_OF_LIST -1
+
+/**
+ * Algorithms for encryption
+ */
+static kernel_algorithm_t encryption_algs[] = {
+/*     {ENCR_DES_IV64,                 0,                                                      0},  */
+       {ENCR_DES,                              SADB_EALG_DESCBC,                       64},
+       {ENCR_3DES,                     SADB_EALG_3DESCBC,                      192},
+/*     {ENCR_RC5,                              0,                                                      0},  */
+/*     {ENCR_IDEA,                     0,                                                      0},  */
+       {ENCR_CAST,                     SADB_X_EALG_CASTCBC,            0},
+       {ENCR_BLOWFISH,                 SADB_X_EALG_BLOWFISHCBC,        0},
+/*     {ENCR_3IDEA,                    0,                                                      0},  */
+/*     {ENCR_DES_IV32,                 0,                                                      0},  */
+       {ENCR_NULL,                     SADB_EALG_NULL,                         0},
+       {ENCR_AES_CBC,                  SADB_X_EALG_AESCBC,                     0},
+/*     {ENCR_AES_CTR,                  0,                                                      0},  */
+/*     {ENCR_AES_CCM_ICV8,             0,                                                      64}, */ /* key_size = ICV size */
+/*     {ENCR_AES_CCM_ICV12,    0,                                                      96}, */ /* key_size = ICV size */
+/*     {ENCR_AES_CCM_ICV16,    0,                                                      128},*/ /* key_size = ICV size */
+/*     {ENCR_AES_GCM_ICV8,             0,                                                      64}, */ /* key_size = ICV size */
+/*     {ENCR_AES_GCM_ICV12,    0,                                                      96}, */ /* key_size = ICV size */
+/*     {ENCR_AES_GCM_ICV16,    0,                                                      128},*/ /* key_size = ICV size */
+       {END_OF_LIST,                   0,                                                      0},
+};
+
+/**
+ * Algorithms for integrity protection
+ */
+static kernel_algorithm_t integrity_algs[] = {
+       {AUTH_HMAC_MD5_96,                      SADB_AALG_MD5HMAC,                      128},
+       {AUTH_HMAC_SHA1_96,                     SADB_AALG_SHA1HMAC,                     160},
+       {AUTH_HMAC_SHA2_256_128,        SADB_X_AALG_SHA2_256HMAC,       256},
+       {AUTH_HMAC_SHA2_384_192,        SADB_X_AALG_SHA2_384HMAC,       384},
+       {AUTH_HMAC_SHA2_512_256,        SADB_X_AALG_SHA2_512HMAC,       512},
+/*     {AUTH_DES_MAC,                          0,                                                      0}, */
+/*     {AUTH_KPDK_MD5,                         0,                                                      0}, */
+       {AUTH_AES_XCBC_96,                      SADB_X_AALG_AES_XCBC_MAC,       128},
+       {END_OF_LIST,                           0,                                                      0},
+};
+
+/**
+ * Algorithms for IPComp
+ */
+static kernel_algorithm_t compression_algs[] = {
+/*     {IPCOMP_OUI,                    0,                                                      0}, */
+       {IPCOMP_DEFLATE,                SADB_X_CALG_DEFLATE,            0},
+       {IPCOMP_LZS,                    SADB_X_CALG_LZS,                        0},
+       {IPCOMP_LZJH,                   SADB_X_CALG_LZJH,                       0},
+       {END_OF_LIST,                   0,                                                      0},
+};
+
+/**
+ * Look up a kernel algorithm ID and its key size
+ */
+static int lookup_algorithm(kernel_algorithm_t *kernel_algo, 
+                                          u_int16_t ikev2_algo, u_int16_t *key_size)
+{
+       while (kernel_algo->ikev2_id != END_OF_LIST)
+       {
+               if (ikev2_algo == kernel_algo->ikev2_id)
+               {
+                       /* match, evaluate key length */
+                       if (key_size && *key_size == 0)
+                       {       /* update key size if not set */
+                               *key_size = kernel_algo->key_size;
+                       }
+                       return kernel_algo->kernel_id;
+               }
+               kernel_algo++;
+       }
+       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 udp encap extensions to a sadb_msg
+ */
+static void add_encap_ext(struct sadb_msg *msg, host_t *src, host_t *dst)
+{
+       struct sadb_x_nat_t_type* nat_type;
+       struct sadb_x_nat_t_port* nat_port;
+       
+       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 = htons(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 = htons(dst->get_port(dst));
+       PFKEY_EXT_ADD(msg, nat_port);
+}
+
+/**
+ * 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_pfkey_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;
+       
+       pthread_mutex_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)
+               {
+                       if (errno == EINTR)
+                       {
+                               /* interrupted, try again */
+                               continue;
+                       }
+                       pthread_mutex_unlock(&this->mutex_pfkey);
+                       DBG1(DBG_KNL, "error sending to PF_KEY socket: %s", strerror(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;
+                       }
+                       DBG1(DBG_KNL, "error reading from PF_KEY socket: %s", strerror(errno));
+                       pthread_mutex_unlock(&this->mutex_pfkey);
+                       return FAILED;
+               }
+               if (len < sizeof(struct sadb_msg) ||
+                       msg->sadb_msg_len < PFKEY_LEN(sizeof(struct sadb_msg)))
+               {
+                       DBG1(DBG_KNL, "received corrupted PF_KEY message");
+                       pthread_mutex_unlock(&this->mutex_pfkey);
+                       return FAILED;
+               }
+               if (msg->sadb_msg_len > len / PFKEY_ALIGNMENT)
+               {
+                       DBG1(DBG_KNL, "buffer was too small to receive the complete PF_KEY message");
+                       pthread_mutex_unlock(&this->mutex_pfkey);
+                       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;
+                       }
+                       pthread_mutex_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);
+               
+       pthread_mutex_unlock(&this->mutex_pfkey);
+       
+       return SUCCESS;
+}
+
+/**
+ * Send a message to the default PF_KEY socket and handle the response.
+ */
+static status_t pfkey_send(private_kernel_pfkey_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);
+}
+
+/**
+ * Process a SADB_ACQUIRE message from the kernel
+ */
+static void process_acquire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+{
+       pfkey_msg_t response;
+       u_int32_t index, reqid;
+       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;
+       }
+       
+       index = response.x_policy->sadb_x_policy_id;
+       DBG2(DBG_KNL, "received an SADB_ACQUIRE, %d", index);
+       pthread_mutex_lock(&this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_match_byindex, (void**)&policy, &index) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "received an SADB_ACQUIRE, but found no matching policy");
+               pthread_mutex_unlock(&this->mutex);
+               return;
+       }
+       reqid = policy->reqid;
+       DBG2(DBG_KNL, "received an SADB_ACQUIRE, %d", reqid);
+       pthread_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);
+       charon->processor->queue_job(charon->processor, job);
+}
+
+/**
+ * Process a SADB_EXPIRE message from the kernel
+ */
+static void process_expire(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+{
+       pfkey_msg_t response;
+       protocol_id_t protocol;
+       u_int32_t spi, reqid;
+       bool hard;
+       job_t *job;
+       
+       DBG2(DBG_KNL, "received an SADB_EXPIRE");
+       
+       if (parse_pfkey_message(msg, &response) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "parsing SADB_EXPIRE from kernel failed");
+               return;
+       }
+       
+       protocol = proto_satype2ike(msg->sadb_msg_satype);
+       spi = response.sa->sadb_sa_spi;
+       reqid = response.x_sa2->sadb_x_sa2_reqid;
+       hard = response.lft_hard != NULL;
+       
+       if (protocol != PROTO_ESP && protocol != PROTO_AH)
+       {
+               DBG2(DBG_KNL, "ignoring SADB_EXPIRE for SA with SPI %.8x and reqid {%d} "
+                                         "which is not a CHILD_SA", ntohl(spi), reqid);
+               return;
+       }
+       
+       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);
+}
+
+/**
+ * Process a SADB_X_NAT_T_NEW_MAPPING message from the kernel
+ */
+static void process_mapping(private_kernel_pfkey_ipsec_t *this, struct sadb_msg* msg)
+{
+       pfkey_msg_t response;
+       u_int32_t spi, reqid;
+       host_t *host;
+       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;
+       }
+       
+       if (!response.x_sa2)
+       {
+               DBG1(DBG_KNL, "received SADB_X_NAT_T_NEW_MAPPING is missing required information");
+               return;
+       }
+       
+       spi = response.sa->sadb_sa_spi;
+       reqid = response.x_sa2->sadb_x_sa2_reqid;
+       
+       if (proto_satype2ike(msg->sadb_msg_satype) == PROTO_ESP)
+       {
+               sockaddr_t *sa = (sockaddr_t*)(response.dst + 1);
+               switch (sa->sa_family)
+               {
+                       case AF_INET:
+                       {
+                               struct sockaddr_in *sin = (struct sockaddr_in*)sa;
+                               sin->sin_port = 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;                                
+                       }
+                       default:
+                               break;
+               }
+               host = host_create_from_sockaddr(sa);
+               if (host)
+               {
+                       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, host);
+                       charon->processor->queue_job(charon->processor, job);
+               }
+       }
+}
+
+/**
+ * Receives events from kernel
+ */
+static job_requeue_t receive_events(private_kernel_pfkey_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:
+                       process_expire(this, msg);
+                       break;
+               case SADB_X_NAT_T_NEW_MAPPING:
+                       process_mapping(this, msg);
+                       break;
+               default:
+                       break;
+       }
+       
+       return JOB_REQUEUE_DIRECT;
+}
+
+/**
+ * Implementation of kernel_interface_t.get_spi.
+ */
+static status_t get_spi(private_kernel_pfkey_ipsec_t *this, 
+                                               host_t *src, host_t *dst, 
+                                               protocol_id_t protocol, u_int32_t reqid,
+                                               u_int32_t *spi)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       struct sadb_x_sa2 *sa2;
+       struct sadb_address *addr;
+       struct sadb_spirange *range;
+       pfkey_msg_t response;
+       u_int32_t received_spi = 0;
+       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_GETSPI;
+       msg->sadb_msg_satype = proto_ike2satype(protocol);
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg);
+       sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+       sa2->sadb_x_sa2_len = PFKEY_LEN(sizeof(struct sadb_spirange));
+       sa2->sadb_x_sa2_reqid = reqid;
+       PFKEY_EXT_ADD(msg, sa2);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       host2ext(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(dst, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       range = (struct sadb_spirange*)PFKEY_EXT_ADD_NEXT(msg);
+       range->sadb_spirange_exttype = SADB_EXT_SPIRANGE;
+       range->sadb_spirange_len = PFKEY_LEN(sizeof(struct sadb_spirange));
+       range->sadb_spirange_min = 0xc0000000;
+       range->sadb_spirange_max = 0xcFFFFFFF;
+       PFKEY_EXT_ADD(msg, range);
+       
+       if (pfkey_send(this, msg, &out, &len) == SUCCESS)
+       {
+               if (out->sadb_msg_errno)
+               {
+                       DBG1(DBG_KNL, "allocating SPI failed: %s (%d)",
+                                       strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               }
+               else if (parse_pfkey_message(out, &response) == SUCCESS)
+               {
+                       received_spi = response.sa->sadb_sa_spi;
+               }
+               free(out);      
+       }
+       
+       if (received_spi == 0)
+       {
+               return FAILED;
+       }
+       
+       *spi = received_spi;
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.get_cpi.
+ */
+static status_t get_cpi(private_kernel_pfkey_ipsec_t *this, 
+                                               host_t *src, host_t *dst, 
+                                               u_int32_t reqid, u_int16_t *cpi)
+{
+       return FAILED;
+}
+
+/**
+ * Implementation of kernel_interface_t.add_sa.
+ */
+static status_t add_sa(private_kernel_pfkey_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, u_int16_t enc_size,
+                                          u_int16_t int_alg, u_int16_t int_size,
+                                          prf_plus_t *prf_plus, ipsec_mode_t mode,
+                                          u_int16_t ipcomp, bool encap,
+                                          bool replace)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       struct sadb_x_sa2 *sa2;
+       struct sadb_address *addr;
+       struct sadb_lifetime *lft;
+       struct sadb_key *key;
+       size_t len;
+       
+       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 = replace ? SADB_UPDATE : 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_replay = (protocol == IPPROTO_COMP) ? 0 : 32;
+       sa->sadb_sa_auth = lookup_algorithm(integrity_algs, 
+                                                               int_alg, &int_size);
+       sa->sadb_sa_encrypt = lookup_algorithm(encryption_algs, 
+                                                               enc_alg, &enc_size);
+       PFKEY_EXT_ADD(msg, sa);
+       
+       sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg);
+       sa2->sadb_x_sa2_exttype = SADB_X_EXT_SA2;
+       sa2->sadb_x_sa2_len = PFKEY_LEN(sizeof(struct sadb_spirange));
+       sa2->sadb_x_sa2_mode = mode2kernel(mode);
+       sa2->sadb_x_sa2_reqid = reqid;
+       PFKEY_EXT_ADD(msg, sa2);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       host2ext(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(dst, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       lft = (struct sadb_lifetime*)PFKEY_EXT_ADD_NEXT(msg);
+       lft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT;
+       lft->sadb_lifetime_len = PFKEY_LEN(sizeof(struct sadb_lifetime));
+       lft->sadb_lifetime_addtime = expire_soft;
+       PFKEY_EXT_ADD(msg, lft);
+       
+       lft = (struct sadb_lifetime*)PFKEY_EXT_ADD_NEXT(msg);
+       lft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD;
+       lft->sadb_lifetime_len = PFKEY_LEN(sizeof(struct sadb_lifetime));
+       lft->sadb_lifetime_addtime = expire_hard;
+       PFKEY_EXT_ADD(msg, lft);
+       
+       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_size);
+               
+               key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg);
+               key->sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
+               key->sadb_key_bits = enc_size;
+               key->sadb_key_len = PFKEY_LEN(sizeof(struct sadb_key) + enc_size / 8);
+               prf_plus->get_bytes(prf_plus, enc_size / 8, (void*)(key + 1));
+               
+               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_size);
+               
+               key = (struct sadb_key*)PFKEY_EXT_ADD_NEXT(msg);
+               key->sadb_key_exttype = SADB_EXT_KEY_AUTH;
+               key->sadb_key_bits = int_size;
+               key->sadb_key_len = PFKEY_LEN(sizeof(struct sadb_key) + int_size / 8);
+               prf_plus->get_bytes(prf_plus, int_size / 8, (void*)(key + 1));
+               
+               PFKEY_EXT_ADD(msg, key);
+       }
+       
+       if (ipcomp != IPCOMP_NONE)
+       {
+               /*TODO*/
+       }
+       
+       if (encap)
+       {
+               add_encap_ext(msg, src, dst);
+       }
+       
+       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);
+       return SUCCESS;
+}
+
+/**
+ * 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,
+                                                 host_t *src, host_t *dst,
+                                                 host_t *new_src, host_t *new_dst, bool encap)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       struct sadb_address *addr;
+       pfkey_msg_t response;
+       size_t len;
+       
+       memset(&request, 0, sizeof(request));
+       
+       DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi));
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_GET;
+       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 a SADB_EXT_ADDRESS_SRC to be present even though
+        * it is not used for anything, so we just send dst twice */
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       host2ext(dst, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+       host2ext(dst, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x",
+                               ntohl(spi));
+               return FAILED;
+       }
+       else if (out->sadb_msg_errno)
+       {
+               DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: %s (%d)",
+                       ntohl(spi), strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+               free(out);
+               return FAILED;
+       }
+       else if (parse_pfkey_message(out, &response) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to query SAD entry with SPI %.8x: parsing response "
+                               "from kernel failed", ntohl(spi));
+               free(out);
+               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);
+       
+       memset(&request, 0, sizeof(request));
+       
+       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));
+       
+       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.lft_soft);
+       PFKEY_EXT_COPY(msg, response.lft_hard);
+       
+       if (response.key_encr)
+       {
+               PFKEY_EXT_COPY(msg, response.key_encr);
+       }
+       
+       if (response.key_auth)
+       {
+               PFKEY_EXT_COPY(msg, response.key_auth);
+       }
+       
+       if (encap)
+       {
+               add_encap_ext(msg, new_src, new_dst);
+       }
+       
+       free(out);
+       
+       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_pfkey_ipsec_t *this, host_t *dst,
+                                          u_int32_t spi, protocol_id_t protocol)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_sa *sa;
+       struct sadb_address *addr;
+       size_t len;
+       
+       memset(&request, 0, sizeof(request));
+       
+       DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi));
+       
+       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 a SADB_EXT_ADDRESS_SRC to be present even though
+        * it is not used for anything, so we just send dst twice */
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       host2ext(dst, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+       host2ext(dst, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       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_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)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_x_policy *pol;
+       struct sadb_address *addr;
+       struct sadb_x_ipsecrequest *req;
+       policy_entry_t *policy, *found = NULL;
+       pfkey_msg_t response;
+       size_t len;
+       
+       /* create a policy */
+       policy = create_policy_entry(src_ts, dst_ts, direction, reqid);
+       
+       /* find a matching policy */
+       pthread_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 */
+               found->refcount++;
+               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);
+               policy->refcount = 1;
+       }
+               
+       memset(&request, 0, sizeof(request));
+       
+       DBG2(DBG_KNL, "adding policy %R === %R %N", src_ts, dst_ts,
+                                  policy_dir_names, direction);
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = found ? SADB_X_SPDUPDATE : SADB_X_SPDADD;
+       msg->sadb_msg_satype = 0;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       pol = (struct sadb_x_policy*)PFKEY_EXT_ADD_NEXT(msg);
+       pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+       pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy));
+       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 -= 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;
+       pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+       
+       /* one or more sadb_x_ipsecrequest extensions are added to the sadb_x_policy extension */
+       req = (struct sadb_x_ipsecrequest*)(pol + 1);
+       req->sadb_x_ipsecrequest_proto = proto_ike2ip(protocol);
+       /* !!! the length of this struct MUST be in octets instead of 64 bit words */
+       req->sadb_x_ipsecrequest_len = sizeof(struct sadb_x_ipsecrequest);
+       req->sadb_x_ipsecrequest_mode = mode2kernel(mode);
+       req->sadb_x_ipsecrequest_reqid = reqid;
+       req->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
+       if (mode == MODE_TUNNEL)
+       {
+               sockaddr_t *sa;
+               socklen_t sl;
+               sa = src->get_sockaddr(src);
+               sl = *src->get_sockaddr_len(src);
+               memcpy(req + 1, sa, sl);
+               sa = dst->get_sockaddr(dst);
+               memcpy((u_int8_t*)(req + 1) + sl, sa, sl);
+               req->sadb_x_ipsecrequest_len += sl * 2;
+       }
+       
+       pol->sadb_x_policy_len += PFKEY_LEN(req->sadb_x_ipsecrequest_len);
+       PFKEY_EXT_ADD(msg, pol);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       addr->sadb_address_proto = policy->src.proto;
+       addr->sadb_address_prefixlen = policy->src.mask;
+       host2ext(policy->src.net, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+       addr->sadb_address_proto = policy->dst.proto;
+       addr->sadb_address_prefixlen = policy->dst.mask;
+       host2ext(policy->dst.net, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       pthread_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;
+       }
+       else if (parse_pfkey_message(out, &response) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to add policy %R === %R %N: parsing response "
+                               "from kernel failed", src_ts, dst_ts, policy_dir_names, direction);
+               free(out);
+               return FAILED;
+       }
+       
+       pthread_mutex_lock(&this->mutex);
+       
+       /* we try to find the policy again and update the kernel index */
+       if (this->policies->find_last(this->policies, NULL, (void**)&policy) != SUCCESS)
+       {
+               DBG2(DBG_KNL, "unable to update index, the policy %R === %R %N is "
+                               "already gone, ignoring", src_ts, dst_ts, policy_dir_names, direction);
+               pthread_mutex_unlock(&this->mutex);
+               free(out);
+               return SUCCESS;
+       }
+       policy->index = response.x_policy->sadb_x_policy_id;
+       free(out);
+       
+       /* install a route, if:
+        * - we are NOT updating a policy
+        * - this is a forward policy (to just get one for each child)
+        * - we are in tunnel mode
+        * - we are not using IPv6 (does not work correctly yet!)
+        * - routing is not disabled via strongswan.conf
+        */
+       if (policy->route == NULL && direction == POLICY_FWD &&
+               mode != MODE_TRANSPORT && src->get_family(src) != AF_INET6 &&
+               this->install_routes)
+       {
+               route_entry_t *route = malloc_thing(route_entry_t);
+               
+               if (charon->kernel_interface->get_address_by_ts(charon->kernel_interface,
+                               dst_ts, &route->src_ip) == SUCCESS)
+               {
+                       /* get the nexthop to src (src as we are in POLICY_FWD).*/
+                       route->gateway = charon->kernel_interface->get_nexthop(
+                                                                       charon->kernel_interface, src);
+                       route->if_name = charon->kernel_interface->get_interface(
+                                                                       charon->kernel_interface, dst);
+                       route->dst_net = chunk_clone(policy->src.net->get_address(policy->src.net));
+                       route->prefixlen = policy->src.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 source route for %H",
+                                                route->src_ip);
+                                       /* FALL */
+                               case ALREADY_DONE:
+                                       /* route exists, do not uninstall */
+                                       route_entry_destroy(route);
+                                       break;
+                               case SUCCESS:
+                                       /* cache the installed route */
+                                       policy->route = route;
+                                       break;
+                       }
+               }
+               else
+               {
+                       free(route);
+               }
+       }       
+       
+       pthread_mutex_unlock(&this->mutex);     
+       
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.query_policy.
+ */
+static status_t query_policy(private_kernel_pfkey_ipsec_t *this,
+                                                        traffic_selector_t *src_ts, 
+                                                        traffic_selector_t *dst_ts,
+                                                        policy_dir_t direction, u_int32_t *use_time)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_x_policy *pol;
+       struct sadb_address *addr;
+       policy_entry_t *policy, *found = NULL;
+       pfkey_msg_t response;
+       size_t len;
+       
+       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, 0);
+       
+       /* find a matching policy */
+       pthread_mutex_lock(&this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_equals, (void**)&found, policy) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "querying policy %R === %R %N failed, not found", src_ts,
+                                          dst_ts, policy_dir_names, direction);
+               policy_entry_destroy(policy);
+               pthread_mutex_unlock(&this->mutex);
+               return NOT_FOUND;
+       }
+       policy_entry_destroy(policy);
+       policy = found;
+       
+       memset(&request, 0, sizeof(request));
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_X_SPDGET;
+       msg->sadb_msg_satype = 0;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       pol = (struct sadb_x_policy*)PFKEY_EXT_ADD_NEXT(msg);
+       pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+       pol->sadb_x_policy_id = policy->index;
+       pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy));
+       pol->sadb_x_policy_dir = dir2kernel(direction);
+       pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+       PFKEY_EXT_ADD(msg, pol);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       addr->sadb_address_proto = policy->src.proto;
+       addr->sadb_address_prefixlen = policy->src.mask;
+       host2ext(policy->src.net, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+       addr->sadb_address_proto = policy->dst.proto;
+       addr->sadb_address_prefixlen = policy->dst.mask;
+       host2ext(policy->dst.net, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       pthread_mutex_unlock(&this->mutex);
+       
+       if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to query 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 query 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;
+       }
+       else if (parse_pfkey_message(out, &response) != SUCCESS)
+       {
+               DBG1(DBG_KNL, "unable to query policy %R === %R %N: parsing response "
+                               "from kernel failed", src_ts, dst_ts, policy_dir_names, direction);
+               free(out);
+               return FAILED;
+       }
+       
+       *use_time = response.lft_current->sadb_lifetime_usetime;
+       
+       free(out);
+                       
+       return SUCCESS;
+}
+
+/**
+ * Implementation of kernel_interface_t.del_policy.
+ */
+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)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_x_policy *pol;
+       struct sadb_address *addr;
+       policy_entry_t *policy, *found = NULL;
+       route_entry_t *route;
+       size_t len;
+       
+       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, 0);
+       
+       /* find a matching policy */
+       pthread_mutex_lock(&this->mutex);
+       if (this->policies->find_first(this->policies,
+                       (linked_list_match_t)policy_entry_equals, (void**)&found, policy) == SUCCESS)
+       {
+               if (--found->refcount > 0)
+               {
+                       /* is used by more SAs, keep in kernel */
+                       DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed");    
+                       policy_entry_destroy(policy);
+                       pthread_mutex_unlock(&this->mutex);
+                       return SUCCESS;
+               }
+               /* remove if last reference */
+               this->policies->remove(this->policies, found, NULL);
+               policy_entry_destroy(policy);
+               policy = found;
+       }
+       else
+       {
+               DBG1(DBG_KNL, "deleting policy %R === %R %N failed, not found", src_ts,
+                                          dst_ts, policy_dir_names, direction);
+               policy_entry_destroy(policy);
+               pthread_mutex_unlock(&this->mutex);
+               return NOT_FOUND;
+       }
+       pthread_mutex_unlock(&this->mutex);
+               
+       memset(&request, 0, sizeof(request));
+       
+       msg = (struct sadb_msg*)request;
+       msg->sadb_msg_version = PF_KEY_V2;
+       msg->sadb_msg_type = SADB_X_SPDDELETE;
+       msg->sadb_msg_satype = 0;
+       msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
+       
+       pol = (struct sadb_x_policy*)PFKEY_EXT_ADD_NEXT(msg);
+       pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
+       pol->sadb_x_policy_len = PFKEY_LEN(sizeof(struct sadb_x_policy));
+       pol->sadb_x_policy_dir = dir2kernel(direction);
+       pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
+       PFKEY_EXT_ADD(msg, pol);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
+       addr->sadb_address_proto = policy->src.proto;
+       addr->sadb_address_prefixlen = policy->src.mask;
+       host2ext(policy->src.net, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg);
+       addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
+       addr->sadb_address_proto = policy->dst.proto;
+       addr->sadb_address_prefixlen = policy->dst.mask;
+       host2ext(policy->dst.net, addr);
+       PFKEY_EXT_ADD(msg, addr);
+       
+       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)
+       {
+               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);
+               }               
+               route_entry_destroy(route);
+       }
+       
+       return SUCCESS;
+}
+
+/**
+ * Register a socket for AQUIRE/EXPIRE messages
+ */
+static status_t register_pfkey_socket(private_kernel_pfkey_ipsec_t *this, u_int8_t satype)
+{
+       unsigned char request[PFKEY_BUFFER_SIZE];
+       struct sadb_msg *msg, *out;
+       struct sadb_x_policy *pol;
+       struct sadb_address *addr;
+       policy_entry_t *policy, *found = NULL;
+       pfkey_msg_t response;
+       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_pfkey_ipsec_t *this)
+{
+       this->job->cancel(this->job);
+       close(this->socket);
+       close(this->socket_events);
+       this->policies->destroy_function(this->policies, (void*)policy_entry_destroy);
+       free(this);
+}
+
+/*
+ * Described in header.
+ */
+kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
+{
+       private_kernel_pfkey_ipsec_t *this = malloc_thing(private_kernel_pfkey_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,u_int16_t,u_int16_t,u_int16_t,prf_plus_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.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.destroy = (void(*)(kernel_ipsec_t*)) destroy;
+
+       /* private members */
+       this->policies = linked_list_create();
+       pthread_mutex_init(&this->mutex, NULL);
+       this->install_routes = lib->settings->get_bool(lib->settings, "charon.install_routes", TRUE);
+       pthread_mutex_init(&this->mutex_pfkey, NULL);
+       this->seq = 0;
+       
+       /* 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_pfkey/kernel_pfkey_ipsec.h b/src/charon/plugins/kernel_pfkey/kernel_pfkey_ipsec.h
new file mode 100644 (file)
index 0000000..5e6953b
--- /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_pfkey_ipsec_i kernel_pfkey_ipsec
+ * @{ @ingroup kernel_pfkey
+ */
+
+#ifndef KERNEL_PFKEY_IPSEC_H_
+#define KERNEL_PFKEY_IPSEC_H_
+
+#include <kernel/kernel_ipsec.h>
+
+typedef struct kernel_pfkey_ipsec_t kernel_pfkey_ipsec_t;
+
+/**
+ * Implementation of the kernel ipsec interface using PF_KEY.
+ */
+struct kernel_pfkey_ipsec_t {
+
+       /**
+        * Implements kernel_ipsec_t interface
+        */
+       kernel_ipsec_t interface;
+};
+
+/**
+ * Create a PF_KEY kernel ipsec interface instance.
+ *
+ * @return                     kernel_pfkey_ipsec_t instance
+ */
+kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create();
+
+#endif /* KERNEL_PFKEY_IPSEC_H_ @} */
diff --git a/src/charon/plugins/kernel_pfkey/kernel_pfkey_plugin.c b/src/charon/plugins/kernel_pfkey/kernel_pfkey_plugin.c
new file mode 100644 (file)
index 0000000..1714de7
--- /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_pfkey_plugin.h"
+
+#include "kernel_pfkey_ipsec.h"
+
+#include <daemon.h>
+
+typedef struct private_kernel_pfkey_plugin_t private_kernel_pfkey_plugin_t;
+
+/**
+ * private data of kernel PF_KEY plugin
+ */
+struct private_kernel_pfkey_plugin_t {
+       /**
+        * implements plugin interface
+        */
+       kernel_pfkey_plugin_t public;
+};
+
+/**
+ * Implementation of plugin_t.destroy
+ */
+static void destroy(private_kernel_pfkey_plugin_t *this)
+{
+       charon->kernel_interface->remove_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_pfkey_ipsec_create);
+       free(this);
+}
+
+/*
+ * see header file
+ */
+plugin_t *plugin_create()
+{
+       private_kernel_pfkey_plugin_t *this = malloc_thing(private_kernel_pfkey_plugin_t);
+       
+       this->public.plugin.destroy = (void(*)(plugin_t*))destroy;
+       
+       charon->kernel_interface->add_ipsec_interface(charon->kernel_interface, (kernel_ipsec_constructor_t)kernel_pfkey_ipsec_create);
+       
+       return &this->public.plugin;
+}
diff --git a/src/charon/plugins/kernel_pfkey/kernel_pfkey_plugin.h b/src/charon/plugins/kernel_pfkey/kernel_pfkey_plugin.h
new file mode 100644 (file)
index 0000000..57e6b9b
--- /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_pfkey kernel_pfkey
+ * @ingroup cplugins
+ *
+ * @defgroup kernel_pfkey_plugin kernel_pfkey_plugin
+ * @{ @ingroup kernel_pfkey
+ */
+
+#ifndef KERNEL_PFKEY_PLUGIN_H_
+#define KERNEL_PFKEY_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct kernel_pfkey_plugin_t kernel_pfkey_plugin_t;
+
+/**
+ * PF_KEY kernel interface plugin
+ */
+struct kernel_pfkey_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+/**
+ * Create a kernel_pfkey_plugin instance.
+ */
+plugin_t *plugin_create();
+
+#endif /* KERNEL_PFKEY_PLUGIN_H_ @} */
index fbc094a..22cef47 100644 (file)
@@ -49,41 +49,14 @@ typedef struct {
  */
 static u_int ts2subnet(traffic_selector_t* ts, u_int8_t *mask)
 {
-       /* there is no way to do this cleanly, as the address range may
-        * be anything else but a subnet. We use from_addr as subnet 
-        * and try to calculate a usable subnet mask.
-        */
-       int byte, bit, net;
-       bool found = FALSE;
-       chunk_t from, to;
-       size_t size = (ts->get_type(ts) == TS_IPV4_ADDR_RANGE) ? 4 : 16;
-       
-       from = ts->get_from_address(ts);
-       to = ts->get_to_address(ts);
+       u_int net;
+       host_t *net_host;
+       chunk_t net_chunk;
        
-       *mask = (size * 8);
-       /* go trough all bits of the addresses, beginning in the front.
-        * as long as they are equal, the subnet gets larger
-        */
-       for (byte = 0; byte < size; byte++)
-       {
-               for (bit = 7; bit >= 0; bit--)
-               {
-                       if ((1<<bit & from.ptr[byte]) != (1<<bit & to.ptr[byte]))
-                       {
-                               *mask = ((7 - bit) + (byte * 8));
-                               found = TRUE;
-                               break;
-                       }
-               }
-               if (found)
-               {
-                       break;
-               }
-       }
-       net = *(u_int32_t*)from.ptr;
-       chunk_free(&from);
-       chunk_free(&to);
+       ts->to_subnet(ts, &net_host, mask);
+       net_chunk = net_host->get_address(net_host);
+       net = *(u_int32_t*)net_chunk.ptr;
+       net_host->destroy(net_host);
        return net;
 }
 
index 11d65bb..029290f 100644 (file)
@@ -11,8 +11,8 @@ irdumm_SOURCES = irdumm.c
 
 libdumm_la_LIBADD = $(top_builddir)/src/libstrongswan/libstrongswan.la \
   -lbridge -lfuse -lutil
-dumm_LDADD = -ldumm ${gtk_LIBS}
-irdumm_LDADD = -ldumm -lruby1.8
+dumm_LDADD = libdumm.la ${gtk_LIBS}
+irdumm_LDADD = libdumm.la -lruby1.8
 
 INCLUDES = -I$(top_srcdir)/src/libstrongswan ${gtk_CFLAGS} \
   -I/usr/lib/ruby/1.8/i486-linux/
index 81ac63a..d17a630 100644 (file)
@@ -3,7 +3,7 @@
 
 /* The definitions, required to talk to KAME racoon IKE. */
 
-#include "pfkeyv2.h"
+#include <linux/pfkeyv2.h>
 
 #define IPSEC_PORT_ANY         0
 #define IPSEC_ULPROTO_ANY      255
@@ -12,7 +12,8 @@
 enum {
        IPSEC_MODE_ANY          = 0,    /* We do not support this for SA */
        IPSEC_MODE_TRANSPORT    = 1,
-       IPSEC_MODE_TUNNEL       = 2
+       IPSEC_MODE_TUNNEL       = 2,
+       IPSEC_MODE_BEET         = 3
 };
 
 enum {
index 08409d5..014509b 100644 (file)
@@ -72,11 +72,11 @@ EXTRA_DIST = asn1/oid.txt asn1/oid.pl
 BUILT_SOURCES = asn1/oid.c asn1/oid.h
 MAINTAINERCLEANFILES = asn1/oid.c asn1/oid.h
 
-asn1/oid.c :   asn1/oid.txt asn1/oid.pl
-               cd asn1 && $(PERL) oid.pl
+asn1/oid.c :   asn1/oid.pl asn1/oid.txt
+               (cd `dirname $<` && $(PERL) $<)
 
-asn1/oid.h :   asn1/oid.txt asn1/oid.pl
-               cd asn1 && $(PERL) oid.pl
+asn1/oid.h :   asn1/oid.pl asn1/oid.txt
+               (cd `dirname $<` && $(PERL) $<)
 
 
 # build plugins with their own Makefile
index cd755c4..9508e9a 100644 (file)
@@ -632,7 +632,8 @@ static status_t find_first(private_linked_list_t *this, linked_list_match_t matc
        
        while (current)
        {
-               if (match(current->value, d1, d2, d3, d4, d5))
+               if ((match && match(current->value, d1, d2, d3, d4, d5)) ||
+                       (!match && item && current->value == *item))
                {
                        if (item != NULL)
                        {
@@ -655,7 +656,8 @@ static status_t find_last(private_linked_list_t *this, linked_list_match_t match
        
        while (current)
        {
-               if (match(current->value, d1, d2, d3, d4, d5))
+               if ((match && match(current->value, d1, d2, d3, d4, d5)) ||
+                       (!match && item && current->value == *item))
                {
                        if (item != NULL)
                        {
index 214558c..183f93f 100644 (file)
@@ -130,7 +130,7 @@ struct linked_list_t {
         * If a compare function is given, it is called for each item, where
         * the first parameter is the current list item and the second parameter
         * is the supplied item parameter.
-        * If compare is NULL, compare is is done by pointer.
+        * If compare is NULL, compare is done by pointer.
         *
         * @param item          item to remove/pass to comparator
         * @param compare       compare function, or NULL
@@ -179,10 +179,12 @@ struct linked_list_t {
         * If the supplied function returns TRUE this function returns SUCCESS, and
         * the current object is returned in the third parameter, otherwise,
         * the next item is checked.
+        *
+        * If match is NULL, *item and the current object are compared.
         * 
         * @warning Only use pointers as user supplied data.
         *
-        * @param match                 comparison function to call on each object
+        * @param match                 comparison function to call on each object, or NULL
         * @param item                  the list item, if found
         * @param ...                   user data to supply to match function (limited to 5 arguments)
         * @return                              SUCCESS if found, NOT_FOUND otherwise
@@ -198,9 +200,11 @@ struct linked_list_t {
         * the current object is returned in the third parameter, otherwise,
         * the next item is checked.
         * 
+        * If match is NULL, *item and the current object are compared.
+        * 
         * @warning Only use pointers as user supplied data.
         *
-        * @param match                 comparison function to call on each object
+        * @param match                 comparison function to call on each object, or NULL
         * @param item                  the list item, if found
         * @param ...                   user data to supply to match function (limited to 5 arguments)
         * @return                              SUCCESS if found, NOT_FOUND otherwise
index e6346a5..1ba3e47 100644 (file)
@@ -7,7 +7,7 @@ exec.h invokecharon.h lex.yy.c loglite.c
 
 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
-starter_LDADD = defs.o $(top_srcdir)/src/libfreeswan/libfreeswan.a
+starter_LDADD = defs.o $(top_builddir)/src/libfreeswan/libfreeswan.a
 EXTRA_DIST = parser.l parser.y keywords.txt ipsec.conf
 dist_man_MANS = ipsec.conf.5 starter.8
 MAINTAINERCLEANFILES = lex.yy.c y.tab.c y.tab.h keywords.c
@@ -15,17 +15,17 @@ MAINTAINERCLEANFILES = lex.yy.c y.tab.c y.tab.h keywords.c
 PLUTODIR=$(top_srcdir)/src/pluto
 SCEPCLIENTDIR=$(top_srcdir)/src/scepclient
 
-lex.yy.c:      y.tab.c parser.l parser.y parser.h
-               $(LEX) --nounput parser.l 
+lex.yy.c:      parser.l parser.y parser.h y.tab.c
+               $(LEX) --nounput $< 
 
-y.tab.c:       parser.l parser.y parser.h
-               $(YACC) -v -d parser.y
+y.tab.c:       parser.y parser.l parser.h
+               $(YACC) -v -d $<
 
-y.tab.h:       parser.l parser.y parser.h
-               $(YACC) -v -d parser.y
+y.tab.h:       parser.y parser.l parser.h
+               $(YACC) -v -d $<
 
 keywords.c:    keywords.txt keywords.h
-               $(GPERF) -C -G -t < keywords.txt > keywords.c
+               $(GPERF) -C -G -t < $< > $@
 
 defs.o:                $(PLUTODIR)/defs.c $(PLUTODIR)/defs.h
                $(COMPILE) -c -o $@ $<
index aaedfc7..df20252 100644 (file)
@@ -7,4 +7,4 @@ MAINTAINERCLEANFILES = stroke_keywords.c
 AM_CFLAGS = -DIPSEC_PIDDIR=\"${piddir}\"
 
 stroke_keywords.c:     stroke_keywords.txt stroke_keywords.h
-               $(GPERF) -C -G -t < stroke_keywords.txt > stroke_keywords.c
+               $(GPERF) -C -G -t < $< > $@