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