diff options
Diffstat (limited to 'src/libhydra/plugins')
17 files changed, 1992 insertions, 1031 deletions
diff --git a/src/libhydra/plugins/attr/Makefile.in b/src/libhydra/plugins/attr/Makefile.in index 1ceb93ef3..831adf9d6 100644 --- a/src/libhydra/plugins/attr/Makefile.in +++ b/src/libhydra/plugins/attr/Makefile.in @@ -49,6 +49,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -82,7 +83,7 @@ libstrongswan_attr_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ $(libstrongswan_attr_la_LDFLAGS) $(LDFLAGS) -o $@ @MONOLITHIC_FALSE@am_libstrongswan_attr_la_rpath = -rpath $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_attr_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -108,6 +109,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -202,11 +204,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -223,11 +228,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -243,6 +249,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -252,7 +259,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/attr/attr_provider.c b/src/libhydra/plugins/attr/attr_provider.c index 44242c259..c1c3cd895 100644 --- a/src/libhydra/plugins/attr/attr_provider.c +++ b/src/libhydra/plugins/attr/attr_provider.c @@ -77,10 +77,10 @@ static bool attr_enum_filter(void *null, attribute_entry_t **in, } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, - private_attr_provider_t *this, char *pool, - identification_t *id, host_t *vip) + private_attr_provider_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) { - if (vip) + if (vips->get_count(vips)) { this->lock->read_lock(this->lock); return enumerator_create_filter( @@ -145,18 +145,22 @@ static void add_legacy_entry(private_attr_provider_t *this, char *key, int nr, /** * Key to attribute type mappings, for v4 and v6 attributes */ -static struct { +typedef struct { char *name; configuration_attribute_type_t v4; configuration_attribute_type_t v6; -} keys[] = { - {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS}, - {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS}, - {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS}, - {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP}, - {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK}, - {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER}, - {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET}, +} attribute_type_key_t; + +static attribute_type_key_t keys[] = { + {"address", INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS}, + {"dns", INTERNAL_IP4_DNS, INTERNAL_IP6_DNS}, + {"nbns", INTERNAL_IP4_NBNS, INTERNAL_IP6_NBNS}, + {"dhcp", INTERNAL_IP4_DHCP, INTERNAL_IP6_DHCP}, + {"netmask", INTERNAL_IP4_NETMASK, INTERNAL_IP6_NETMASK}, + {"server", INTERNAL_IP4_SERVER, INTERNAL_IP6_SERVER}, + {"subnet", INTERNAL_IP4_SUBNET, INTERNAL_IP6_SUBNET}, + {"split-include", UNITY_SPLIT_INCLUDE, UNITY_SPLIT_INCLUDE}, + {"split-exclude", UNITY_LOCAL_LAN, UNITY_LOCAL_LAN}, }; /** @@ -179,12 +183,29 @@ static void load_entries(private_attr_provider_t *this) while (enumerator->enumerate(enumerator, &key, &value)) { configuration_attribute_type_t type; + attribute_type_key_t *mapped = NULL; attribute_entry_t *entry; host_t *host; char *pos; - int i, mask = -1; + int i, mask = -1, family; type = atoi(key); + if (!type) + { + for (i = 0; i < countof(keys); i++) + { + if (streq(key, keys[i].name)) + { + mapped = &keys[i]; + break; + } + } + if (!mapped) + { + DBG1(DBG_CFG, "mapping attribute type %s failed", key); + continue; + } + } tokens = enumerator_create_token(value, ",", " "); while (tokens->enumerate(tokens, &token)) { @@ -200,37 +221,16 @@ static void load_entries(private_attr_provider_t *this) DBG1(DBG_CFG, "invalid host in key %s: %s", key, token); continue; } - if (!type) - { - for (i = 0; i < countof(keys); i++) - { - if (streq(key, keys[i].name)) - { - if (host->get_family(host) == AF_INET) - { - type = keys[i].v4; - } - else - { - type = keys[i].v6; - } - } - } - if (!type) - { - DBG1(DBG_CFG, "mapping attribute type %s failed", key); - break; - } - } + family = host->get_family(host); entry = malloc_thing(attribute_entry_t); - entry->type = type; + entry->type = type ?: (family == AF_INET ? mapped->v4 : mapped->v6); if (mask == -1) { entry->value = chunk_clone(host->get_address(host)); } else { - if (host->get_family(host) == AF_INET) + if (family == AF_INET) { /* IPv4 attributes contain a subnet mask */ u_int32_t netmask; diff --git a/src/libhydra/plugins/attr_sql/Makefile.in b/src/libhydra/plugins/attr_sql/Makefile.in index 4fe577f3b..71810ae5e 100644 --- a/src/libhydra/plugins/attr_sql/Makefile.in +++ b/src/libhydra/plugins/attr_sql/Makefile.in @@ -51,6 +51,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -95,7 +96,7 @@ pool_OBJECTS = $(am_pool_OBJECTS) pool_DEPENDENCIES = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ $(top_builddir)/src/libhydra/libhydra.la -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -121,6 +122,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -215,11 +217,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -236,11 +241,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -256,6 +262,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -265,7 +272,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/attr_sql/pool_attributes.c b/src/libhydra/plugins/attr_sql/pool_attributes.c index 5c7397476..d3fc06eeb 100644 --- a/src/libhydra/plugins/attr_sql/pool_attributes.c +++ b/src/libhydra/plugins/attr_sql/pool_attributes.c @@ -492,7 +492,7 @@ void del_attr(char *name, char *pool, char *identity, { fprintf(stderr, "deleting %s attribute (%N) with value '%.*s'%s failed.\n", name, configuration_attribute_type_names, type, - blob_db.len, blob_db.ptr, id_pool_str); + (int)blob_db.len, blob_db.ptr, id_pool_str); } else @@ -514,7 +514,7 @@ void del_attr(char *name, char *pool, char *identity, { printf("deleted %s attribute (%N) with value '%.*s'%s.\n", name, configuration_attribute_type_names, type, - blob_db.len, blob_db.ptr, id_pool_str); + (int)blob_db.len, blob_db.ptr, id_pool_str); } else { @@ -555,7 +555,7 @@ void del_attr(char *name, char *pool, char *identity, fprintf(stderr, "the %s attribute (%N) with value '%.*s'%s " "was not found.\n", name, configuration_attribute_type_names, type, - blob.len, blob.ptr, id_pool_str); + (int)blob.len, blob.ptr, id_pool_str); } } } diff --git a/src/libhydra/plugins/attr_sql/sql_attribute.c b/src/libhydra/plugins/attr_sql/sql_attribute.c index 714bbcd72..a7d90e728 100644 --- a/src/libhydra/plugins/attr_sql/sql_attribute.c +++ b/src/libhydra/plugins/attr_sql/sql_attribute.c @@ -233,54 +233,37 @@ static host_t* get_lease(private_sql_attribute_t *this, char *name, } METHOD(attribute_provider_t, acquire_address, host_t*, - private_sql_attribute_t *this, char *names, identification_t *id, + private_sql_attribute_t *this, linked_list_t *pools, identification_t *id, host_t *requested) { + enumerator_t *enumerator; host_t *address = NULL; u_int identity, pool, timeout; + char *name; identity = get_identity(this, id); if (identity) { - /* check for a single pool first (no concatenation and enumeration) */ - if (strchr(names, ',') == NULL) + /* check for an existing lease in all pools */ + enumerator = pools->create_enumerator(pools); + while (enumerator->enumerate(enumerator, &name)) { - pool = get_pool(this, names, &timeout); + pool = get_pool(this, name, &timeout); if (pool) { - /* check for an existing lease */ - address = check_lease(this, names, pool, identity); - if (address == NULL) + address = check_lease(this, name, pool, identity); + if (address) { - /* get an unallocated address or expired lease */ - address = get_lease(this, names, pool, timeout, identity); + break; } } } - else - { - enumerator_t *enumerator; - char *name; + enumerator->destroy(enumerator); - /* in a first step check for an existing lease over all pools */ - enumerator = enumerator_create_token(names, ",", " "); - while (enumerator->enumerate(enumerator, &name)) - { - pool = get_pool(this, name, &timeout); - if (pool) - { - address = check_lease(this, name, pool, identity); - if (address) - { - enumerator->destroy(enumerator); - return address; - } - } - } - enumerator->destroy(enumerator); - - /* in a second step get an unallocated address or expired lease */ - enumerator = enumerator_create_token(names, ",", " "); + if (!address) + { + /* get an unallocated address or expired lease */ + enumerator = pools->create_enumerator(pools); while (enumerator->enumerate(enumerator, &name)) { pool = get_pool(this, name, &timeout); @@ -300,20 +283,27 @@ METHOD(attribute_provider_t, acquire_address, host_t*, } METHOD(attribute_provider_t, release_address, bool, - private_sql_attribute_t *this, char *name, host_t *address, + private_sql_attribute_t *this, linked_list_t *pools, host_t *address, identification_t *id) { enumerator_t *enumerator; - bool found = FALSE; + u_int pool, timeout; time_t now = time(NULL); + bool found = FALSE; + char *name; - enumerator = enumerator_create_token(name, ",", " "); + enumerator = pools->create_enumerator(pools); while (enumerator->enumerate(enumerator, &name)) { - u_int pool, timeout; - pool = get_pool(this, name, &timeout); - if (pool) + if (!pool) + { + continue; + } + if (this->db->execute(this->db, NULL, + "UPDATE addresses SET released = ? WHERE " + "pool = ? AND address = ?", DB_UINT, time(NULL), + DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0) { if (this->history) { @@ -324,29 +314,24 @@ METHOD(attribute_provider_t, release_address, bool, DB_UINT, now, DB_UINT, pool, DB_BLOB, address->get_address(address)); } - if (this->db->execute(this->db, NULL, - "UPDATE addresses SET released = ? WHERE " - "pool = ? AND address = ?", DB_UINT, time(NULL), - DB_UINT, pool, DB_BLOB, address->get_address(address)) > 0) - { - found = TRUE; - break; - } + found = TRUE; + break; } } enumerator->destroy(enumerator); + return found; } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, - private_sql_attribute_t *this, char *names, identification_t *id, - host_t *vip) + private_sql_attribute_t *this, linked_list_t *pools, identification_t *id, + linked_list_t *vips) { enumerator_t *attr_enumerator = NULL; - if (vip) + if (vips->get_count(vips)) { - enumerator_t *names_enumerator; + enumerator_t *pool_enumerator; u_int count; char *name; @@ -357,8 +342,8 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, { u_int identity = get_identity(this, id); - names_enumerator = enumerator_create_token(names, ",", " "); - while (names_enumerator->enumerate(names_enumerator, &name)) + pool_enumerator = pools->create_enumerator(pools); + while (pool_enumerator->enumerate(pool_enumerator, &name)) { u_int attr_pool = get_attr_pool(this, name); if (!attr_pool) @@ -385,14 +370,14 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, DESTROY_IF(attr_enumerator); attr_enumerator = NULL; } - names_enumerator->destroy(names_enumerator); + pool_enumerator->destroy(pool_enumerator); } /* in a second step check for attributes that match name */ if (!attr_enumerator) { - names_enumerator = enumerator_create_token(names, ",", " "); - while (names_enumerator->enumerate(names_enumerator, &name)) + pool_enumerator = pools->create_enumerator(pools); + while (pool_enumerator->enumerate(pool_enumerator, &name)) { u_int attr_pool = get_attr_pool(this, name); if (!attr_pool) @@ -419,7 +404,7 @@ METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, DESTROY_IF(attr_enumerator); attr_enumerator = NULL; } - names_enumerator->destroy(names_enumerator); + pool_enumerator->destroy(pool_enumerator); } this->db->execute(this->db, NULL, "END TRANSACTION"); diff --git a/src/libhydra/plugins/kernel_klips/Makefile.in b/src/libhydra/plugins/kernel_klips/Makefile.in index 63f3e045b..1dd633ee2 100644 --- a/src/libhydra/plugins/kernel_klips/Makefile.in +++ b/src/libhydra/plugins/kernel_klips/Makefile.in @@ -49,6 +49,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -86,7 +87,7 @@ libstrongswan_kernel_klips_la_LINK = $(LIBTOOL) --tag=CC \ @MONOLITHIC_FALSE@am_libstrongswan_kernel_klips_la_rpath = -rpath \ @MONOLITHIC_FALSE@ $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_kernel_klips_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -112,6 +113,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -206,11 +208,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -227,11 +232,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -247,6 +253,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -256,7 +263,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c index ceff8cdc9..d875dab04 100644 --- a/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c +++ b/src/libhydra/plugins/kernel_klips/kernel_klips_ipsec.c @@ -138,11 +138,6 @@ struct private_kernel_klips_ipsec_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; @@ -825,8 +820,22 @@ static kernel_algorithm_t compression_algs[] = { /** * Look up a kernel algorithm ID and its key size */ -static int lookup_algorithm(kernel_algorithm_t *list, int ikev2) +static int lookup_algorithm(transform_type_t type, int ikev2) { + kernel_algorithm_t *list; + int alg = 0; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = encryption_algs; + break; + case INTEGRITY_ALGORITHM: + list = integrity_algs; + break; + default: + return 0; + } while (list->ikev2 != END_OF_LIST) { if (ikev2 == list->ikev2) @@ -835,7 +844,9 @@ static int lookup_algorithm(kernel_algorithm_t *list, int ikev2) } list++; } - return 0; + hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, ikev2, + type, &alg, NULL); + return alg; } /** @@ -1525,12 +1536,12 @@ METHOD(kernel_ipsec_t, get_spi, status_t, u_int32_t spi_gen; rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); - if (!rng) + if (!rng || !rng->get_bytes(rng, sizeof(spi_gen), (void*)&spi_gen)) { - DBG1(DBG_KNL, "allocating SPI failed: no RNG"); + DBG1(DBG_KNL, "allocating SPI failed"); + DESTROY_IF(rng); return FAILED; } - rng->get_bytes(rng, sizeof(spi_gen), (void*)&spi_gen); rng->destroy(rng); /* allocated SPIs lie within the range from 0xc0000000 to 0xcFFFFFFF */ @@ -1718,8 +1729,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, 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); + sa->sadb_sa_auth = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); + sa->sadb_sa_encrypt = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); PFKEY_EXT_ADD(msg, sa); add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC); @@ -2097,7 +2108,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, */ if (policy->route == NULL && direction == POLICY_OUT) { - char *iface; + char *iface = NULL; ipsec_dev_t *dev; route_entry_t *route = malloc_thing(route_entry_t); route->src_ip = NULL; @@ -2115,8 +2126,8 @@ METHOD(kernel_ipsec_t, add_policy, status_t, } /* find the virtual interface */ - iface = hydra->kernel_interface->get_interface(hydra->kernel_interface, - src); + hydra->kernel_interface->get_interface(hydra->kernel_interface, + src, &iface); if (find_ipsec_dev(this, iface, &dev) == SUCCESS) { /* above, we got either the name of a virtual or a physical @@ -2163,7 +2174,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t, /* get the nexthop to dst */ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, dst); + hydra->kernel_interface, dst, route->src_ip); route->dst_net = chunk_clone(policy->dst.net->get_address(policy->dst.net)); route->prefixlen = policy->dst.mask; @@ -2542,20 +2553,9 @@ static status_t register_pfkey_socket(private_kernel_klips_ipsec_t *this, u_int8 return SUCCESS; } -METHOD(kernel_ipsec_t, bypass_socket, bool, - private_kernel_klips_ipsec_t *this, int fd, int family) -{ - /* KLIPS does not need a bypass policy for IKE */ - return TRUE; -} - METHOD(kernel_ipsec_t, destroy, void, private_kernel_klips_ipsec_t *this) { - if (this->job) - { - this->job->cancel(this->job); - } if (this->socket > 0) { close(this->socket); @@ -2594,7 +2594,10 @@ kernel_klips_ipsec_t *kernel_klips_ipsec_create() .query_policy = _query_policy, .del_policy = _del_policy, .flush_policies = (void*)return_failed, - .bypass_socket = _bypass_socket, + /* KLIPS does not need a bypass policy for IKE */ + .bypass_socket = (void*)return_true, + /* KLIPS does not need enabling UDP decap explicitly */ + .enable_udp_decap = (void*)return_true, .destroy = _destroy, }, }, @@ -2639,9 +2642,9 @@ kernel_klips_ipsec_t *kernel_klips_ipsec_create() return NULL; } - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive_events, + this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); return &this->public; } diff --git a/src/libhydra/plugins/kernel_netlink/Makefile.in b/src/libhydra/plugins/kernel_netlink/Makefile.in index 73dbdd0e3..d0adb3b1e 100644 --- a/src/libhydra/plugins/kernel_netlink/Makefile.in +++ b/src/libhydra/plugins/kernel_netlink/Makefile.in @@ -49,6 +49,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -87,7 +88,7 @@ libstrongswan_kernel_netlink_la_LINK = $(LIBTOOL) --tag=CC \ @MONOLITHIC_FALSE@am_libstrongswan_kernel_netlink_la_rpath = -rpath \ @MONOLITHIC_FALSE@ $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_kernel_netlink_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -113,6 +114,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -207,11 +209,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -228,11 +233,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -248,6 +254,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -257,7 +264,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c index b2cf778be..4f5b6600d 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2011 Tobias Brunner + * Copyright (C) 2006-2012 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2008 Andreas Steffen * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser @@ -58,6 +58,20 @@ #define IPV6_XFRM_POLICY 34 #endif /*IPV6_XFRM_POLICY*/ +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif + +/* this is not defined on some platforms */ +#ifndef SOL_UDP +#define SOL_UDP IPPROTO_UDP +#endif + /** Default priority of installed policies */ #define PRIO_BASE 512 @@ -229,8 +243,25 @@ static kernel_algorithm_t compression_algs[] = { /** * Look up a kernel algorithm name and its key size */ -static char* lookup_algorithm(kernel_algorithm_t *list, int ikev2) +static char* lookup_algorithm(transform_type_t type, int ikev2) { + kernel_algorithm_t *list; + char *name = NULL; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = encryption_algs; + break; + case INTEGRITY_ALGORITHM: + list = integrity_algs; + break; + case COMPRESSION_ALGORITHM: + list = compression_algs; + break; + default: + return NULL; + } while (list->ikev2 != END_OF_LIST) { if (list->ikev2 == ikev2) @@ -239,7 +270,9 @@ static char* lookup_algorithm(kernel_algorithm_t *list, int ikev2) } list++; } - return NULL; + hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, ikev2, + type, NULL, &name); + return name; } typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t; @@ -269,11 +302,6 @@ struct private_kernel_netlink_ipsec_t { hashtable_t *sas; /** - * Job receiving netlink events - */ - callback_job_t *job; - - /** * Netlink xfrm socket (IPsec) */ netlink_socket_t *socket_xfrm; @@ -294,12 +322,12 @@ struct private_kernel_netlink_ipsec_t { bool policy_history; /** - * Size of the replay window, in packets + * Size of the replay window, in packets (= bits) */ u_int32_t replay_window; /** - * Size of the replay window bitmap, in bytes + * Size of the replay window bitmap, in number of __u32 blocks */ u_int32_t replay_bmp; }; @@ -344,8 +372,8 @@ static void route_entry_destroy(route_entry_t *this) static bool route_entry_equals(route_entry_t *a, route_entry_t *b) { return a->if_name && b->if_name && streq(a->if_name, b->if_name) && - a->src_ip->equals(a->src_ip, b->src_ip) && - a->gateway->equals(a->gateway, b->gateway) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway->ip_equals(a->gateway, b->gateway) && chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; } @@ -562,9 +590,8 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, */ static u_int policy_hash(policy_entry_t *key) { - chunk_t chunk = chunk_create((void*)&key->sel, - sizeof(struct xfrm_selector) + sizeof(u_int32_t)); - return chunk_hash(chunk); + chunk_t chunk = chunk_from_thing(key->sel); + return chunk_hash_inc(chunk, chunk_hash(chunk_from_thing(key->mark))); } /** @@ -572,8 +599,8 @@ static u_int policy_hash(policy_entry_t *key) */ static bool policy_equals(policy_entry_t *key, policy_entry_t *other_key) { - return memeq(&key->sel, &other_key->sel, - sizeof(struct xfrm_selector) + sizeof(u_int32_t)) && + return memeq(&key->sel, &other_key->sel, sizeof(struct xfrm_selector)) && + key->mark == other_key->mark && key->direction == other_key->direction; } @@ -1147,16 +1174,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u} (mark " - "%u/0x%8x)", ntohl(spi), reqid, mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u}", - ntohl(spi), reqid); - } + DBG2(DBG_KNL, "adding SAD entry with SPI %.8x and reqid {%u} (mark " + "%u/0x%08x)", ntohl(spi), reqid, mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = inbound ? XFRM_MSG_UPDSA : XFRM_MSG_NEWSA; @@ -1220,12 +1240,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t, { struct xfrm_algo_aead *algo; - alg_name = lookup_algorithm(encryption_algs, enc_alg); + alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", - encryption_algorithm_names, enc_alg); - goto failed; + encryption_algorithm_names, enc_alg); + goto failed; } DBG2(DBG_KNL, " using encryption algorithm %N with key size %d", encryption_algorithm_names, enc_alg, enc_key.len * 8); @@ -1242,7 +1262,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr); algo->alg_key_len = enc_key.len * 8; algo->alg_icv_len = icv_size; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, enc_key.ptr, enc_key.len); rthdr = XFRM_RTA_NEXT(rthdr); @@ -1252,7 +1273,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, { struct xfrm_algo *algo; - alg_name = lookup_algorithm(encryption_algs, enc_alg); + alg_name = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", @@ -1272,7 +1293,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = enc_key.len * 8; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, enc_key.ptr, enc_key.len); rthdr = XFRM_RTA_NEXT(rthdr); @@ -1283,7 +1305,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, { u_int trunc_len = 0; - alg_name = lookup_algorithm(integrity_algs, int_alg); + alg_name = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", @@ -1326,7 +1348,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, algo = (struct xfrm_algo_auth*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; algo->alg_trunc_len = trunc_len; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, int_key.ptr, int_key.len); } else @@ -1344,7 +1367,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = int_key.len * 8; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; memcpy(algo->alg_key, int_key.ptr, int_key.len); } rthdr = XFRM_RTA_NEXT(rthdr); @@ -1353,7 +1377,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (ipcomp != IPCOMP_NONE) { rthdr->rta_type = XFRMA_ALG_COMP; - alg_name = lookup_algorithm(compression_algs, ipcomp); + alg_name = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp); if (alg_name == NULL) { DBG1(DBG_KNL, "algorithm %N not supported by kernel!", @@ -1372,7 +1396,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr); algo->alg_key_len = 0; - strcpy(algo->alg_name, alg_name); + strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name)); + algo->alg_name[sizeof(algo->alg_name) - 1] = '\0'; rthdr = XFRM_RTA_NEXT(rthdr); } @@ -1467,7 +1492,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, /* bmp_len contains number uf __u32's */ replay->bmp_len = this->replay_bmp; replay->replay_window = this->replay_window; - DBG2(DBG_KNL, " using replay window of %u bytes", + DBG2(DBG_KNL, " using replay window of %u packets", this->replay_window); rthdr = XFRM_RTA_NEXT(rthdr); @@ -1479,7 +1504,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t, } else { - sa->replay_window = DEFAULT_REPLAY_WINDOW; + DBG2(DBG_KNL, " using replay window of %u packets", + this->replay_window); + sa->replay_window = this->replay_window; } } @@ -1488,7 +1515,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t, if (mark.value) { DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x " - "(mark %u/0x%8x)", ntohl(spi), mark.value, mark.mask); + "(mark %u/0x%08x)", ntohl(spi), mark.value, mark.mask); } else { @@ -1608,15 +1635,9 @@ METHOD(kernel_ipsec_t, query_sa, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "querying SAD entry with SPI %.8x (mark %u/0x%8x)", - ntohl(spi), mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "querying SAD entry with SPI %.8x", ntohl(spi)); - } + DBG2(DBG_KNL, "querying SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_GETSA; @@ -1665,7 +1686,7 @@ METHOD(kernel_ipsec_t, query_sa, status_t, if (mark.value) { DBG1(DBG_KNL, "querying SAD entry with SPI %.8x " - "(mark %u/0x%8x) failed: %s (%d)", + "(mark %u/0x%08x) failed: %s (%d)", ntohl(spi), mark.value, mark.mask, strerror(-err->error), -err->error); } @@ -1717,15 +1738,9 @@ METHOD(kernel_ipsec_t, del_sa, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x (mark %u/0x%8x)", - ntohl(spi), mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x", ntohl(spi)); - } + DBG2(DBG_KNL, "deleting SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_DELSA; @@ -1755,30 +1770,27 @@ METHOD(kernel_ipsec_t, del_sa, status_t, mrk->m = mark.mask; } - if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) - { - if (mark.value) - { - DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x " - "(mark %u/0x%8x)", ntohl(spi), mark.value, mark.mask); - } - else - { - DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", - ntohl(spi)); - } - return FAILED; - } - if (mark.value) + switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr)) { - DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x (mark %u/0x%8x)", - ntohl(spi), mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x", ntohl(spi)); + case SUCCESS: + DBG2(DBG_KNL, "deleted SAD entry with SPI %.8x (mark %u/0x%08x)", + ntohl(spi), mark.value, mark.mask); + return SUCCESS; + case NOT_FOUND: + return NOT_FOUND; + default: + if (mark.value) + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x " + "(mark %u/0x%08x)", ntohl(spi), mark.value, mark.mask); + } + else + { + DBG1(DBG_KNL, "unable to delete SAD entry with SPI %.8x", + ntohl(spi)); + } + return FAILED; } - return SUCCESS; } METHOD(kernel_ipsec_t, update_sa, status_t, @@ -1954,7 +1966,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, { goto failed; } - memcpy(RTA_DATA(rta), replay, sizeof(replay)); + memcpy(RTA_DATA(rta), replay, sizeof(struct xfrm_replay_state)); rta = XFRM_RTA_NEXT(rta); } @@ -2153,23 +2165,26 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, if (policy->direction == POLICY_FWD && ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { - route_entry_t *route = malloc_thing(route_entry_t); policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; + route_entry_t *route; + + INIT(route, + .prefixlen = policy->sel.prefixlen_s, + ); if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, fwd->dst_ts, &route->src_ip) == SUCCESS) { /* get the nexthop to src (src as we are in POLICY_FWD) */ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, ipsec->src); - /* install route via outgoing interface */ - route->if_name = hydra->kernel_interface->get_interface( - hydra->kernel_interface, ipsec->dst); + hydra->kernel_interface, ipsec->src, + ipsec->dst); route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16); memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len); - route->prefixlen = policy->sel.prefixlen_s; - if (!route->if_name) + /* install route via outgoing interface */ + if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, + ipsec->dst, &route->if_name)) { this->mutex->unlock(this->mutex); route_entry_destroy(route); @@ -2180,12 +2195,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this, { route_entry_t *old = policy->route; if (route_entry_equals(old, route)) - { /* keep previously installed route. since it might have - * still been removed by an address change, we install it - * again but ignore the result */ - hydra->kernel_interface->add_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name); + { this->mutex->unlock(this->mutex); route_entry_destroy(route); return SUCCESS; @@ -2258,19 +2268,10 @@ METHOD(kernel_ipsec_t, add_policy, status_t, if (current) { /* use existing policy */ - if (mark.value) - { - DBG2(DBG_KNL, "policy %R === %R %N (mark %u/0x%8x) " - "already exists, increasing refcount", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "policy %R === %R %N " - "already exists, increasing refcount", - src_ts, dst_ts, policy_dir_names, direction); - } + DBG2(DBG_KNL, "policy %R === %R %N (mark %u/0x%08x) " + "already exists, increasing refcount", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); policy_entry_destroy(this, policy); policy = current; found = TRUE; @@ -2314,18 +2315,9 @@ METHOD(kernel_ipsec_t, add_policy, status_t, return SUCCESS; } - if (mark.value) - { - DBG2(DBG_KNL, "%s policy %R === %R %N (mark %u/0x%8x)", - found ? "updating" : "adding", src_ts, dst_ts, - policy_dir_names, direction, mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "%s policy %R === %R %N", - found ? "updating" : "adding", src_ts, dst_ts, - policy_dir_names, direction); - } + DBG2(DBG_KNL, "%s policy %R === %R %N (mark %u/0x%08x)", + found ? "updating" : "adding", src_ts, dst_ts, + policy_dir_names, direction, mark.value, mark.mask); if (add_policy_internal(this, policy, assigned_sa, found) != SUCCESS) { @@ -2350,17 +2342,10 @@ METHOD(kernel_ipsec_t, query_policy, status_t, memset(&request, 0, sizeof(request)); - if (mark.value) - { - DBG2(DBG_KNL, "querying policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "querying policy %R === %R %N", src_ts, dst_ts, - policy_dir_names, direction); - } + DBG2(DBG_KNL, "querying policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); + hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; hdr->nlmsg_type = XFRM_MSG_GETPOLICY; @@ -2454,17 +2439,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, bool is_installed = TRUE; u_int32_t priority; - if (mark.value) - { - DBG2(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "deleting policy %R === %R %N", - src_ts, dst_ts, policy_dir_names, direction); - } + DBG2(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); /* create a policy */ memset(&policy, 0, sizeof(policy_entry_t)); @@ -2479,7 +2456,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, { if (mark.value) { - DBG1(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%8x) " + DBG1(DBG_KNL, "deleting policy %R === %R %N (mark %u/0x%08x) " "failed, not found", src_ts, dst_ts, policy_dir_names, direction, mark.value, mark.mask); } @@ -2525,17 +2502,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, return SUCCESS; } - if (mark.value) - { - DBG2(DBG_KNL, "updating policy %R === %R %N (mark %u/0x%8x)", - src_ts, dst_ts, policy_dir_names, direction, - mark.value, mark.mask); - } - else - { - DBG2(DBG_KNL, "updating policy %R === %R %N", - src_ts, dst_ts, policy_dir_names, direction); - } + DBG2(DBG_KNL, "updating policy %R === %R %N (mark %u/0x%08x)", + src_ts, dst_ts, policy_dir_names, direction, + mark.value, mark.mask); current->used_by->get_first(current->used_by, (void**)&mapping); if (add_policy_internal(this, current, mapping, TRUE) != SUCCESS) @@ -2599,7 +2568,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, if (mark.value) { DBG1(DBG_KNL, "unable to delete policy %R === %R %N " - "(mark %u/0x%8x)", src_ts, dst_ts, policy_dir_names, + "(mark %u/0x%08x)", src_ts, dst_ts, policy_dir_names, direction, mark.value, mark.mask); } else @@ -2680,16 +2649,25 @@ METHOD(kernel_ipsec_t, bypass_socket, bool, return TRUE; } +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_kernel_netlink_ipsec_t *this, int fd, int family, u_int16_t port) +{ + int type = UDP_ENCAP_ESPINUDP; + + if (setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) + { + DBG1(DBG_KNL, "unable to set UDP_ENCAP: %s", strerror(errno)); + return FALSE; + } + return TRUE; +} + METHOD(kernel_ipsec_t, destroy, void, private_kernel_netlink_ipsec_t *this) { enumerator_t *enumerator; policy_entry_t *policy; - if (this->job) - { - this->job->cancel(this->job); - } if (this->socket_xfrm_events > 0) { close(this->socket_xfrm_events); @@ -2713,7 +2691,7 @@ METHOD(kernel_ipsec_t, destroy, void, kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() { private_kernel_netlink_ipsec_t *this; - struct sockaddr_nl addr; + bool register_for_events = TRUE; int fd; INIT(this, @@ -2731,6 +2709,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() .del_policy = _del_policy, .flush_policies = _flush_policies, .bypass_socket = _bypass_socket, + .enable_udp_decap = _enable_udp_decap, .destroy = _destroy, }, }, @@ -2755,10 +2734,14 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() /* no policy history for pluto */ this->policy_history = FALSE; } + else if (streq(hydra->daemon, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } /* disable lifetimes for allocated SPIs in kernel */ fd = open("/proc/sys/net/core/xfrm_acq_expires", O_WRONLY); - if (fd) + if (fd > 0) { ignore_result(write(fd, "165", 3)); close(fd); @@ -2771,28 +2754,34 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() return NULL; } - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - /* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */ - this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); - if (this->socket_xfrm_events <= 0) + if (register_for_events) { - DBG1(DBG_KNL, "unable to create XFRM event socket"); - destroy(this); - return NULL; - } - addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) | - XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING); - if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) - { - DBG1(DBG_KNL, "unable to bind XFRM event socket"); - destroy(this); - return NULL; + struct sockaddr_nl addr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + + /* create and bind XFRM socket for ACQUIRE, EXPIRE, MIGRATE & MAPPING */ + this->socket_xfrm_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_XFRM); + if (this->socket_xfrm_events <= 0) + { + DBG1(DBG_KNL, "unable to create XFRM event socket"); + destroy(this); + return NULL; + } + addr.nl_groups = XFRMNLGRP(ACQUIRE) | XFRMNLGRP(EXPIRE) | + XFRMNLGRP(MIGRATE) | XFRMNLGRP(MAPPING); + if (bind(this->socket_xfrm_events, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind XFRM event socket"); + destroy(this); + return NULL; + } + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)receive_events, this, NULL, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); } - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); return &this->public; } diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c index cce0ff402..3f63a8496 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Tobias Brunner + * Copyright (C) 2008-2012 Tobias Brunner * Copyright (C) 2005-2008 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -38,6 +38,7 @@ */ #include <sys/socket.h> +#include <sys/utsname.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> #include <unistd.h> @@ -50,32 +51,38 @@ #include <hydra.h> #include <debug.h> #include <threading/thread.h> -#include <threading/condvar.h> #include <threading/mutex.h> +#include <threading/rwlock.h> +#include <threading/rwlock_condvar.h> +#include <threading/spinlock.h> +#include <utils/hashtable.h> #include <utils/linked_list.h> #include <processing/jobs/callback_job.h> /** delay before firing roam events (ms) */ #define ROAM_DELAY 100 +/** delay before reinstalling routes (ms) */ +#define ROUTE_DELAY 100 + typedef struct addr_entry_t addr_entry_t; /** - * IP address in an inface_entry_t + * IP address in an iface_entry_t */ struct addr_entry_t { - /** The ip address */ + /** the ip address */ host_t *ip; - /** virtual IP managed by us */ - bool virtual; - /** scope of the address */ u_char scope; - /** Number of times this IP is used, if virtual */ + /** number of times this IP is used, if virtual (i.e. managed by us) */ u_int refcount; + + /** TRUE once it is installed, if virtual */ + bool installed; }; /** @@ -105,6 +112,9 @@ struct iface_entry_t { /** list of addresses as host_t */ linked_list_t *addrs; + + /** TRUE if usable by config */ + bool usable; }; /** @@ -116,6 +126,208 @@ static void iface_entry_destroy(iface_entry_t *this) free(this); } +/** + * find an interface entry by index + */ +static bool iface_entry_by_index(iface_entry_t *this, int *ifindex) +{ + return this->ifindex == *ifindex; +} + +/** + * find an interface entry by name + */ +static bool iface_entry_by_name(iface_entry_t *this, char *ifname) +{ + return streq(this->ifname, ifname); +} + +/** + * check if an interface is up + */ +static inline bool iface_entry_up(iface_entry_t *iface) +{ + return (iface->flags & IFF_UP) == IFF_UP; +} + +/** + * check if an interface is up and usable + */ +static inline bool iface_entry_up_and_usable(iface_entry_t *iface) +{ + return iface->usable && iface_entry_up(iface); +} + +typedef struct addr_map_entry_t addr_map_entry_t; + +/** + * Entry that maps an IP address to an interface entry + */ +struct addr_map_entry_t { + /** The IP address */ + host_t *ip; + + /** The address entry for this IP address */ + addr_entry_t *addr; + + /** The interface this address is installed on */ + iface_entry_t *iface; +}; + +/** + * Hash a addr_map_entry_t object, all entries with the same IP address + * are stored in the same bucket + */ +static u_int addr_map_entry_hash(addr_map_entry_t *this) +{ + return chunk_hash(this->ip->get_address(this->ip)); +} + +/** + * Compare two addr_map_entry_t objects, two entries are equal if they are + * installed on the same interface + */ +static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->iface->ifindex == b->iface->ifindex && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * an up and usable interface + */ +static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a, + addr_map_entry_t *b) +{ + return iface_entry_up_and_usable(b->iface) && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any active local interface + */ +static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any local interface + */ +static bool addr_map_entry_match(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->ip->ip_equals(a->ip, b->ip); +} + +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; +}; + +/** + * Clone a route_entry_t object. + */ +static route_entry_t *route_entry_clone(route_entry_t *this) +{ + route_entry_t *route; + + INIT(route, + .if_name = strdup(this->if_name), + .src_ip = this->src_ip->clone(this->src_ip), + .gateway = this->gateway->clone(this->gateway), + .dst_net = chunk_clone(this->dst_net), + .prefixlen = this->prefixlen, + ); + return route; +} + +/** + * Destroy a route_entry_t object + */ +static void route_entry_destroy(route_entry_t *this) +{ + free(this->if_name); + DESTROY_IF(this->src_ip); + DESTROY_IF(this->gateway); + chunk_free(&this->dst_net); + free(this); +} + +/** + * Hash a route_entry_t object + */ +static u_int route_entry_hash(route_entry_t *this) +{ + return chunk_hash_inc(chunk_from_thing(this->prefixlen), + chunk_hash(this->dst_net)); +} + +/** + * Compare two route_entry_t objects + */ +static bool route_entry_equals(route_entry_t *a, route_entry_t *b) +{ + return a->if_name && b->if_name && streq(a->if_name, b->if_name) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway->ip_equals(a->gateway, b->gateway) && + chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; +} + +typedef struct net_change_t net_change_t; + +/** + * Queued network changes + */ +struct net_change_t { + /** Name of the interface that got activated (or an IP appeared on) */ + char *if_name; +}; + +/** + * Destroy a net_change_t object + */ +static void net_change_destroy(net_change_t *this) +{ + free(this->if_name); + free(this); +} + +/** + * Hash a net_change_t object + */ +static u_int net_change_hash(net_change_t *this) +{ + return chunk_hash(chunk_create(this->if_name, strlen(this->if_name))); +} + +/** + * Compare two net_change_t objects + */ +static bool net_change_equals(net_change_t *a, net_change_t *b) +{ + return streq(a->if_name, b->if_name); +} + typedef struct private_kernel_netlink_net_t private_kernel_netlink_net_t; /** @@ -128,14 +340,14 @@ struct private_kernel_netlink_net_t { kernel_netlink_net_t public; /** - * mutex to lock access to various lists + * lock to access various lists and maps */ - mutex_t *mutex; + rwlock_t *lock; /** * condition variable to signal virtual IP add/removal */ - condvar_t *condvar; + rwlock_condvar_t *condvar; /** * Cached list of interfaces and its addresses (iface_entry_t) @@ -143,9 +355,14 @@ struct private_kernel_netlink_net_t { linked_list_t *ifaces; /** - * job receiving netlink events + * Map for IP addresses to iface_entry_t objects (addr_map_entry_t) */ - callback_job_t *job; + hashtable_t *addrs; + + /** + * Map for virtual IP addresses to iface_entry_t objects (addr_map_entry_t) + */ + hashtable_t *vips; /** * netlink rt socket (routing) @@ -158,9 +375,14 @@ struct private_kernel_netlink_net_t { int socket_events; /** - * time of the last roam event + * earliest time of the next roam event + */ + timeval_t next_roam; + + /** + * lock to check and update roam event time */ - timeval_t last_roam; + spinlock_t *roam_lock; /** * routing table to install routes @@ -173,6 +395,31 @@ struct private_kernel_netlink_net_t { int routing_table_prio; /** + * installed routes + */ + hashtable_t *routes; + + /** + * mutex for routes + */ + mutex_t *routes_lock; + + /** + * interface changes which may trigger route reinstallation + */ + hashtable_t *net_changes; + + /** + * mutex for route reinstallation triggers + */ + mutex_t *net_changes_lock; + + /** + * time of last route reinstallation + */ + timeval_t last_route_reinstall; + + /** * whether to react to RTM_NEWROUTE or RTM_DELROUTE events */ bool process_route; @@ -183,79 +430,253 @@ struct private_kernel_netlink_net_t { bool install_virtual_ip; /** + * the name of the interface virtual IP addresses are installed on + */ + char *install_virtual_ip_on; + + /** + * whether preferred source addresses can be specified for IPv6 routes + */ + bool rta_prefsrc_for_ipv6; + + /** * list with routing tables to be excluded from route lookup */ linked_list_t *rt_exclude; }; /** - * get the refcount of a virtual ip + * Forward declaration + */ +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name); + +/** + * Clear the queued network changes. */ -static int get_vip_refcount(private_kernel_netlink_net_t *this, host_t* ip) +static void net_changes_clear(private_kernel_netlink_net_t *this) { - enumerator_t *ifaces, *addrs; - iface_entry_t *iface; - addr_entry_t *addr; - int refcount = 0; + enumerator_t *enumerator; + net_change_t *change; - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, (void**)&iface)) + enumerator = this->net_changes->create_enumerator(this->net_changes); + while (enumerator->enumerate(enumerator, NULL, (void**)&change)) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, (void**)&addr)) - { - if (addr->virtual && (iface->flags & IFF_UP) && - ip->ip_equals(ip, addr->ip)) + this->net_changes->remove_at(this->net_changes, enumerator); + net_change_destroy(change); + } + enumerator->destroy(enumerator); +} + +/** + * Act upon queued network changes. + */ +static job_requeue_t reinstall_routes(private_kernel_netlink_net_t *this) +{ + enumerator_t *enumerator; + route_entry_t *route; + + this->net_changes_lock->lock(this->net_changes_lock); + this->routes_lock->lock(this->routes_lock); + + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + net_change_t *change, lookup = { + .if_name = route->if_name, + }; + /* check if a change for the outgoing interface is queued */ + change = this->net_changes->get(this->net_changes, &lookup); + if (!change) + { /* in case src_ip is not on the outgoing interface */ + if (this->public.interface.get_interface(&this->public.interface, + route->src_ip, &lookup.if_name)) { - refcount = addr->refcount; - break; + if (!streq(lookup.if_name, route->if_name)) + { + change = this->net_changes->get(this->net_changes, &lookup); + } + free(lookup.if_name); } } - addrs->destroy(addrs); - if (refcount) + if (change) { - break; + manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + route->dst_net, route->prefixlen, route->gateway, + route->src_ip, route->if_name); } } - ifaces->destroy(ifaces); + enumerator->destroy(enumerator); + this->routes_lock->unlock(this->routes_lock); + + net_changes_clear(this); + this->net_changes_lock->unlock(this->net_changes_lock); + return JOB_REQUEUE_NONE; +} + +/** + * Queue route reinstallation caused by network changes for a given interface. + * + * The route reinstallation is delayed for a while and only done once for + * several calls during this delay, in order to avoid doing it too often. + * The interface name is freed. + */ +static void queue_route_reinstall(private_kernel_netlink_net_t *this, + char *if_name) +{ + net_change_t *update, *found; + timeval_t now; + job_t *job; + + INIT(update, + .if_name = if_name + ); + + this->net_changes_lock->lock(this->net_changes_lock); + found = this->net_changes->put(this->net_changes, update, update); + if (found) + { + net_change_destroy(found); + } + time_monotonic(&now); + if (timercmp(&now, &this->last_route_reinstall, >)) + { + now.tv_usec += ROUTE_DELAY * 1000; + while (now.tv_usec > 1000000) + { + now.tv_sec++; + now.tv_usec -= 1000000; + } + this->last_route_reinstall = now; + + job = (job_t*)callback_job_create((callback_job_cb_t)reinstall_routes, + this, NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROUTE_DELAY); + } + this->net_changes_lock->unlock(this->net_changes_lock); +} - return refcount; +/** + * check if the given IP is known as virtual IP and currently installed + * + * this function will also return TRUE if the virtual IP entry disappeared. + * in that case the returned entry will be NULL. + * + * this->lock must be held when calling this function + */ +static bool is_vip_installed_or_gone(private_kernel_netlink_net_t *this, + host_t *ip, addr_map_entry_t **entry) +{ + addr_map_entry_t lookup = { + .ip = ip, + }; + + *entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (*entry == NULL) + { /* the virtual IP disappeared */ + return TRUE; + } + return (*entry)->addr->installed; +} + +/** + * check if the given IP is known as virtual IP + * + * this->lock must be held when calling this function + */ +static bool is_known_vip(private_kernel_netlink_net_t *this, host_t *ip) +{ + addr_map_entry_t lookup = { + .ip = ip, + }; + + return this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match) != NULL; +} + +/** + * Add an address map entry + */ +static void addr_map_entry_add(hashtable_t *map, addr_entry_t *addr, + iface_entry_t *iface) +{ + addr_map_entry_t *entry; + + INIT(entry, + .ip = addr->ip, + .addr = addr, + .iface = iface, + ); + entry = map->put(map, entry, entry); + free(entry); +} + +/** + * Remove an address map entry + */ +static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr, + iface_entry_t *iface) +{ + addr_map_entry_t *entry, lookup = { + .ip = addr->ip, + .addr = addr, + .iface = iface, + }; + + entry = map->remove(map, &lookup); + free(entry); } /** * get the first non-virtual ip address on the given interface. + * if a candidate address is given, we first search for that address and if not + * found return the address as above. * returned host is a clone, has to be freed by caller. + * + * this->lock must be held when calling this function */ static host_t *get_interface_address(private_kernel_netlink_net_t *this, - int ifindex, int family) + int ifindex, int family, host_t *candidate) { - enumerator_t *ifaces, *addrs; iface_entry_t *iface; + enumerator_t *addrs; addr_entry_t *addr; host_t *ip = NULL; - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &ifindex) == SUCCESS) { - if (iface->ifindex == ifindex) - { + if (iface->usable) + { /* only use interfaces not excluded by config */ addrs = iface->addrs->create_enumerator(iface->addrs); while (addrs->enumerate(addrs, &addr)) { - if (!addr->virtual && addr->ip->get_family(addr->ip) == family) + if (addr->refcount) + { /* ignore virtual IP addresses */ + continue; + } + if (addr->ip->get_family(addr->ip) == family) { - ip = addr->ip->clone(addr->ip); - break; + if (!candidate || candidate->ip_equals(candidate, addr->ip)) + { /* stop at the first address if we don't search for a + * candidate or if the candidate matches */ + ip = addr->ip; + break; + } + else if (!ip) + { /* store the first address as fallback if candidate is + * not found */ + ip = addr->ip; + } } } addrs->destroy(addrs); - break; } } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return ip; + return ip ? ip->clone(ip) : NULL; } /** @@ -277,21 +698,60 @@ static void fire_roam_event(private_kernel_netlink_net_t *this, bool address) job_t *job; time_monotonic(&now); - if (timercmp(&now, &this->last_roam, >)) + this->roam_lock->lock(this->roam_lock); + if (!timercmp(&now, &this->next_roam, >)) { - now.tv_usec += ROAM_DELAY * 1000; - while (now.tv_usec > 1000000) - { - now.tv_sec++; - now.tv_usec -= 1000000; - } - this->last_roam = now; + this->roam_lock->unlock(this->roam_lock); + return; + } + now.tv_usec += ROAM_DELAY * 1000; + while (now.tv_usec > 1000000) + { + now.tv_sec++; + now.tv_usec -= 1000000; + } + this->next_roam = now; + this->roam_lock->unlock(this->roam_lock); + + job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, + (void*)(uintptr_t)(address ? 1 : 0), + NULL, NULL); + lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); +} - job = (job_t*)callback_job_create((callback_job_cb_t)roam_event, - (void*)(uintptr_t)(address ? 1 : 0), - NULL, NULL); - lib->scheduler->schedule_job_ms(lib->scheduler, job, ROAM_DELAY); +/** + * check if an interface with a given index is up and usable + * + * this->lock must be locked when calling this function + */ +static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this, + int index) +{ + iface_entry_t *iface; + + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &index) == SUCCESS) + { + return iface_entry_up_and_usable(iface); + } + return FALSE; +} + +/** + * unregister the current addr_entry_t from the hashtable it is stored in + * + * this->lock must be locked when calling this function + */ +static void addr_entry_unregister(addr_entry_t *addr, iface_entry_t *iface, + private_kernel_netlink_net_t *this) +{ + if (addr->refcount) + { + addr_map_entry_remove(this->vips, addr, iface); + this->condvar->broadcast(this->condvar); + return; } + addr_map_entry_remove(this->addrs, addr, iface); } /** @@ -306,9 +766,9 @@ static void process_link(private_kernel_netlink_net_t *this, enumerator_t *enumerator; iface_entry_t *current, *entry = NULL; char *name = NULL; - bool update = FALSE; + bool update = FALSE, update_routes = FALSE; - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { @@ -323,40 +783,30 @@ static void process_link(private_kernel_netlink_net_t *this, name = "(unknown)"; } - this->mutex->lock(this->mutex); + this->lock->write_lock(this->lock); switch (hdr->nlmsg_type) { case RTM_NEWLINK: { - if (msg->ifi_flags & IFF_LOOPBACK) - { /* ignore loopback interfaces */ - break; - } - enumerator = this->ifaces->create_enumerator(this->ifaces); - while (enumerator->enumerate(enumerator, ¤t)) - { - if (current->ifindex == msg->ifi_index) - { - entry = current; - break; - } - } - enumerator->destroy(enumerator); - if (!entry) + if (this->ifaces->find_first(this->ifaces, + (void*)iface_entry_by_index, (void**)&entry, + &msg->ifi_index) != SUCCESS) { - entry = malloc_thing(iface_entry_t); - entry->ifindex = msg->ifi_index; - entry->flags = 0; - entry->addrs = linked_list_create(); + INIT(entry, + .ifindex = msg->ifi_index, + .addrs = linked_list_create(), + .usable = hydra->kernel_interface->is_interface_usable( + hydra->kernel_interface, name), + ); this->ifaces->insert_last(this->ifaces, entry); } strncpy(entry->ifname, name, IFNAMSIZ); entry->ifname[IFNAMSIZ-1] = '\0'; - if (event) + if (event && entry->usable) { if (!(entry->flags & IFF_UP) && (msg->ifi_flags & IFF_UP)) { - update = TRUE; + update = update_routes = TRUE; DBG1(DBG_KNL, "interface %s activated", name); } if ((entry->flags & IFF_UP) && !(msg->ifi_flags & IFF_UP)) @@ -375,12 +825,16 @@ static void process_link(private_kernel_netlink_net_t *this, { if (current->ifindex == msg->ifi_index) { - if (event) + if (event && current->usable) { update = TRUE; DBG1(DBG_KNL, "interface %s deleted", current->ifname); } + /* TODO: move virtual IPs installed on this interface to + * another interface? */ this->ifaces->remove_at(this->ifaces, enumerator); + current->addrs->invoke_function(current->addrs, + (void*)addr_entry_unregister, current, this); iface_entry_destroy(current); break; } @@ -389,9 +843,13 @@ static void process_link(private_kernel_netlink_net_t *this, break; } } - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); + + if (update_routes && event) + { + queue_route_reinstall(this, strdup(name)); + } - /* send an update to all IKE_SAs */ if (update && event) { fire_roam_event(this, TRUE); @@ -408,13 +866,12 @@ static void process_addr(private_kernel_netlink_net_t *this, struct rtattr *rta = IFA_RTA(msg); size_t rtasize = IFA_PAYLOAD (hdr); host_t *host = NULL; - enumerator_t *ifaces, *addrs; iface_entry_t *iface; - addr_entry_t *addr; chunk_t local = chunk_empty, address = chunk_empty; + char *route_ifname = NULL; bool update = FALSE, found = FALSE, changed = FALSE; - while(RTA_OK(rta, rtasize)) + while (RTA_OK(rta, rtasize)) { switch (rta->rta_type) { @@ -447,65 +904,92 @@ static void process_addr(private_kernel_netlink_net_t *this, return; } - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + this->lock->write_lock(this->lock); + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, + (void**)&iface, &msg->ifa_index) == SUCCESS) { - if (iface->ifindex == msg->ifa_index) + addr_map_entry_t *entry, lookup = { + .ip = host, + .iface = iface, + }; + addr_entry_t *addr; + + entry = this->vips->get(this->vips, &lookup); + if (entry) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) + if (hdr->nlmsg_type == RTM_NEWADDR) + { /* mark as installed and signal waiting threads */ + entry->addr->installed = TRUE; + } + else + { /* the address was already marked as uninstalled */ + addr = entry->addr; + iface->addrs->remove(iface->addrs, addr, NULL); + addr_map_entry_remove(this->vips, addr, iface); + addr_entry_destroy(addr); + } + /* no roam events etc. for virtual IPs */ + this->condvar->broadcast(this->condvar); + this->lock->unlock(this->lock); + host->destroy(host); + return; + } + entry = this->addrs->get(this->addrs, &lookup); + if (entry) + { + if (hdr->nlmsg_type == RTM_DELADDR) { - if (host->ip_equals(host, addr->ip)) + found = TRUE; + addr = entry->addr; + iface->addrs->remove(iface->addrs, addr, NULL); + if (iface->usable) { - found = TRUE; - if (hdr->nlmsg_type == RTM_DELADDR) - { - iface->addrs->remove_at(iface->addrs, addrs); - if (!addr->virtual) - { - changed = TRUE; - DBG1(DBG_KNL, "%H disappeared from %s", - host, iface->ifname); - } - addr_entry_destroy(addr); - } - else if (hdr->nlmsg_type == RTM_NEWADDR && addr->virtual) - { - addr->refcount = 1; - } + changed = TRUE; + DBG1(DBG_KNL, "%H disappeared from %s", host, + iface->ifname); } + addr_map_entry_remove(this->addrs, addr, iface); + addr_entry_destroy(addr); } - addrs->destroy(addrs); - + } + else + { if (hdr->nlmsg_type == RTM_NEWADDR) { - if (!found) + found = TRUE; + changed = TRUE; + route_ifname = strdup(iface->ifname); + INIT(addr, + .ip = host->clone(host), + .scope = msg->ifa_scope, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this->addrs, addr, iface); + if (event && iface->usable) { - found = TRUE; - changed = TRUE; - addr = malloc_thing(addr_entry_t); - addr->ip = host->clone(host); - addr->virtual = FALSE; - addr->refcount = 1; - addr->scope = msg->ifa_scope; - - iface->addrs->insert_last(iface->addrs, addr); - if (event) - { - DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); - } + DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); } } - if (found && (iface->flags & IFF_UP)) - { - update = TRUE; - } - break; + } + if (found && (iface->flags & IFF_UP)) + { + update = TRUE; + } + if (!iface->usable) + { /* ignore events for interfaces excluded by config */ + update = changed = FALSE; } } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); + + if (update && event && route_ifname) + { + queue_route_reinstall(this, route_ifname); + } + else + { + free(route_ifname); + } host->destroy(host); /* send an update to all IKE_SAs */ @@ -532,6 +1016,10 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h { return; } + else if (msg->rtm_flags & RTM_F_CLONED) + { /* ignore cached routes, seem to be created a lot for IPv6 */ + return; + } while (RTA_OK(rta, rtasize)) { @@ -551,20 +1039,26 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h } rta = RTA_NEXT(rta, rtasize); } + this->lock->read_lock(this->lock); + if (rta_oif && !is_interface_up_and_usable(this, rta_oif)) + { /* ignore route changes for interfaces that are ignored or down */ + this->lock->unlock(this->lock); + DESTROY_IF(host); + return; + } if (!host && rta_oif) { - host = get_interface_address(this, rta_oif, msg->rtm_family); + host = get_interface_address(this, rta_oif, msg->rtm_family, NULL); } - if (host) - { - this->mutex->lock(this->mutex); - if (!get_vip_refcount(this, host)) - { /* ignore routes added for virtual IPs */ - fire_roam_event(this, FALSE); - } - this->mutex->unlock(this->mutex); - host->destroy(host); + if (!host || is_known_vip(this, host)) + { /* ignore routes added for virtual IPs */ + this->lock->unlock(this->lock); + DESTROY_IF(host); + return; } + this->lock->unlock(this->lock); + fire_roam_event(this, FALSE); + host->destroy(host); } /** @@ -614,12 +1108,10 @@ static job_requeue_t receive_events(private_kernel_netlink_net_t *this) case RTM_NEWADDR: case RTM_DELADDR: process_addr(this, hdr, TRUE); - this->condvar->broadcast(this->condvar); break; case RTM_NEWLINK: case RTM_DELLINK: process_link(this, hdr, TRUE); - this->condvar->broadcast(this->condvar); break; case RTM_NEWROUTE: case RTM_DELROUTE: @@ -639,10 +1131,8 @@ static job_requeue_t receive_events(private_kernel_netlink_net_t *this) /** enumerator over addresses */ typedef struct { private_kernel_netlink_net_t* this; - /** whether to enumerate down interfaces */ - bool include_down_ifaces; - /** whether to enumerate virtual ip addresses */ - bool include_virtual_ips; + /** which addresses to enumerate */ + kernel_address_type_t which; } address_enumerator_t; /** @@ -650,7 +1140,7 @@ typedef struct { */ static void address_enumerator_destroy(address_enumerator_t *data) { - data->this->mutex->unlock(data->this->mutex); + data->this->lock->unlock(data->this->lock); free(data); } @@ -660,7 +1150,7 @@ static void address_enumerator_destroy(address_enumerator_t *data) static bool filter_addresses(address_enumerator_t *data, addr_entry_t** in, host_t** out) { - if (!data->include_virtual_ips && (*in)->virtual) + if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->refcount) { /* skip virtual interfaces added by us */ return FALSE; } @@ -689,7 +1179,15 @@ static enumerator_t *create_iface_enumerator(iface_entry_t *iface, static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, iface_entry_t** out) { - if (!data->include_down_ifaces && !((*in)->flags & IFF_UP)) + if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable) + { /* skip interfaces excluded by config */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK)) + { /* ignore loopback devices */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP)) { /* skip interfaces not up */ return FALSE; } @@ -698,15 +1196,13 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, } METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, - private_kernel_netlink_net_t *this, - bool include_down_ifaces, bool include_virtual_ips) + private_kernel_netlink_net_t *this, kernel_address_type_t which) { address_enumerator_t *data = malloc_thing(address_enumerator_t); data->this = this; - data->include_down_ifaces = include_down_ifaces; - data->include_virtual_ips = include_virtual_ips; + data->which = which; - this->mutex->lock(this->mutex); + this->lock->read_lock(this->lock); return enumerator_create_nested( enumerator_create_filter( this->ifaces->create_enumerator(this->ifaces), @@ -715,47 +1211,40 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, (void*)address_enumerator_destroy); } -METHOD(kernel_net_t, get_interface_name, char*, - private_kernel_netlink_net_t *this, host_t* ip) +METHOD(kernel_net_t, get_interface_name, bool, + private_kernel_netlink_net_t *this, host_t* ip, char **name) { - enumerator_t *ifaces, *addrs; - iface_entry_t *iface; - addr_entry_t *addr; - char *name = NULL; + addr_map_entry_t *entry, lookup = { + .ip = ip, + }; - DBG2(DBG_KNL, "getting interface name for %H", ip); - - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + if (ip->is_anyaddr(ip)) + { + return FALSE; + } + this->lock->read_lock(this->lock); + /* first try to find it on an up and usable interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) - { - if (ip->ip_equals(ip, addr->ip)) - { - name = strdup(iface->ifname); - break; - } - } - addrs->destroy(addrs); if (name) { - break; + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "%H is on interface %s", ip, *name); } + this->lock->unlock(this->lock); + return TRUE; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - if (name) - { - DBG2(DBG_KNL, "%H is on interface %s", ip, name); - } - else + /* maybe it is installed on an ignored interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up); + if (!entry) { - DBG2(DBG_KNL, "%H is not a local address", ip); + DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip); } - return name; + this->lock->unlock(this->lock); + return FALSE; } /** @@ -763,24 +1252,18 @@ METHOD(kernel_net_t, get_interface_name, char*, */ static int get_interface_index(private_kernel_netlink_net_t *this, char* name) { - enumerator_t *ifaces; iface_entry_t *iface; int ifindex = 0; DBG2(DBG_KNL, "getting iface index for %s", name); - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + this->lock->read_lock(this->lock); + if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, name) == SUCCESS) { - if (streq(name, iface->ifname)) - { - ifindex = iface->ifindex; - break; - } + ifindex = iface->ifindex; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); if (ifindex == 0) { @@ -790,29 +1273,6 @@ static int get_interface_index(private_kernel_netlink_net_t *this, char* name) } /** - * Check if an interface with a given index is up - */ -static bool is_interface_up(private_kernel_netlink_net_t *this, int index) -{ - enumerator_t *ifaces; - iface_entry_t *iface; - /* default to TRUE for interface we do not monitor (e.g. lo) */ - bool up = TRUE; - - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) - { - if (iface->ifindex == index) - { - up = iface->flags & IFF_UP; - break; - } - } - ifaces->destroy(ifaces); - return up; -} - -/** * check if an address (chunk) addr is in subnet (net with net_len net bits) */ static bool addr_in_subnet(chunk_t addr, chunk_t net, int net_len) @@ -849,6 +1309,94 @@ static bool addr_in_subnet(chunk_t addr, chunk_t net, int net_len) } /** + * Store information about a route retrieved via RTNETLINK + */ +typedef struct { + chunk_t gtw; + chunk_t src; + chunk_t dst; + host_t *src_host; + u_int8_t dst_len; + u_int32_t table; + u_int32_t oif; +} rt_entry_t; + +/** + * Free a route entry + */ +static void rt_entry_destroy(rt_entry_t *this) +{ + DESTROY_IF(this->src_host); + free(this); +} + +/** + * Parse route received with RTM_NEWROUTE. The given rt_entry_t object will be + * reused if not NULL. + * + * Returned chunks point to internal data of the Netlink message. + */ +static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route) +{ + struct rtattr *rta; + struct rtmsg *msg; + size_t rtasize; + + msg = (struct rtmsg*)(NLMSG_DATA(hdr)); + rta = RTM_RTA(msg); + rtasize = RTM_PAYLOAD(hdr); + + if (route) + { + route->gtw = chunk_empty; + route->src = chunk_empty; + route->dst = chunk_empty; + route->dst_len = msg->rtm_dst_len; + route->table = msg->rtm_table; + route->oif = 0; + } + else + { + INIT(route, + .dst_len = msg->rtm_dst_len, + .table = msg->rtm_table, + ); + } + + while (RTA_OK(rta, rtasize)) + { + switch (rta->rta_type) + { + case RTA_PREFSRC: + route->src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_GATEWAY: + route->gtw = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_DST: + route->dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); + break; + case RTA_OIF: + if (RTA_PAYLOAD(rta) == sizeof(route->oif)) + { + route->oif = *(u_int32_t*)RTA_DATA(rta); + } + break; +#ifdef HAVE_RTA_TABLE + case RTA_TABLE: + if (RTA_PAYLOAD(rta) == sizeof(route->table)) + { + route->table = *(u_int32_t*)RTA_DATA(rta); + } + break; +#endif /* HAVE_RTA_TABLE*/ + } + rta = RTA_NEXT(rta, rtasize); + } + return route; +} + +/** * Get a route: If "nexthop", the nexthop is returned. source addr otherwise. */ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, @@ -859,22 +1407,21 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, struct rtmsg *msg; chunk_t chunk; size_t len; - int best = -1; + linked_list_t *routes; + rt_entry_t *route = NULL, *best = NULL; enumerator_t *enumerator; - host_t *src = NULL, *gtw = NULL; - - DBG2(DBG_KNL, "getting address to reach %H", dest); + host_t *addr = NULL; memset(&request, 0, sizeof(request)); hdr = (struct nlmsghdr*)request; hdr->nlmsg_flags = NLM_F_REQUEST; - if (dest->get_family(dest) == AF_INET) - { - /* We dump all addresses for IPv4, as we want to ignore IPsec specific - * routes installed by us. But the kernel does not return source - * addresses in a IPv6 dump, so fall back to get() for v6 routes. */ - hdr->nlmsg_flags |= NLM_F_ROOT | NLM_F_DUMP; + if (dest->get_family(dest) == AF_INET || this->rta_prefsrc_for_ipv6 || + this->routing_table) + { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes. + * as we want to ignore routes with virtual IPs we cannot use DUMP + * if these routes are not installed in a separate table */ + hdr->nlmsg_flags |= NLM_F_DUMP; } hdr->nlmsg_type = RTM_GETROUTE; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); @@ -891,10 +1438,12 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, if (this->socket->send(this->socket, hdr, &out, &len) != SUCCESS) { - DBG1(DBG_KNL, "getting address to %H failed", dest); + DBG2(DBG_KNL, "getting %s to reach %H failed", + nexthop ? "nexthop" : "address", dest); return NULL; } - this->mutex->lock(this->mutex); + routes = linked_list_create(); + this->lock->read_lock(this->lock); for (current = out; NLMSG_OK(current, len); current = NLMSG_NEXT(current, len)) @@ -905,132 +1454,53 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, break; case RTM_NEWROUTE: { - struct rtattr *rta; - size_t rtasize; - chunk_t rta_gtw, rta_src, rta_dst; - u_int32_t rta_oif = 0, rta_table; - host_t *new_src, *new_gtw; - bool cont = FALSE; + rt_entry_t *other; uintptr_t table; - rta_gtw = rta_src = rta_dst = chunk_empty; - msg = (struct rtmsg*)(NLMSG_DATA(current)); - rta = RTM_RTA(msg); - rtasize = RTM_PAYLOAD(current); - rta_table = msg->rtm_table; - while (RTA_OK(rta, rtasize)) - { - switch (rta->rta_type) - { - case RTA_PREFSRC: - rta_src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); - break; - case RTA_GATEWAY: - rta_gtw = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); - break; - case RTA_DST: - rta_dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)); - break; - case RTA_OIF: - if (RTA_PAYLOAD(rta) == sizeof(rta_oif)) - { - rta_oif = *(u_int32_t*)RTA_DATA(rta); - } - break; -#ifdef HAVE_RTA_TABLE - case RTA_TABLE: - if (RTA_PAYLOAD(rta) == sizeof(rta_table)) - { - rta_table = *(u_int32_t*)RTA_DATA(rta); - } - break; -#endif /* HAVE_RTA_TABLE*/ - } - rta = RTA_NEXT(rta, rtasize); - } - if (msg->rtm_dst_len <= best) - { /* not better than a previous one */ - continue; - } - enumerator = this->rt_exclude->create_enumerator(this->rt_exclude); - while (enumerator->enumerate(enumerator, &table)) - { - if (table == rta_table) - { - cont = TRUE; - break; - } - } - enumerator->destroy(enumerator); - if (cont) - { + route = parse_route(current, route); + + table = (uintptr_t)route->table; + if (this->rt_exclude->find_first(this->rt_exclude, NULL, + (void**)&table) == SUCCESS) + { /* route is from an excluded routing table */ continue; } if (this->routing_table != 0 && - rta_table == this->routing_table) + route->table == this->routing_table) { /* route is from our own ipsec routing table */ continue; } - if (rta_oif && !is_interface_up(this, rta_oif)) + if (route->oif && !is_interface_up_and_usable(this, route->oif)) { /* interface is down */ continue; } - if (!addr_in_subnet(chunk, rta_dst, msg->rtm_dst_len)) + if (!addr_in_subnet(chunk, route->dst, route->dst_len)) { /* route destination does not contain dest */ continue; } - - if (nexthop) - { - /* nexthop lookup, return gateway if any */ - DESTROY_IF(gtw); - gtw = host_create_from_chunk(msg->rtm_family, rta_gtw, 0); - best = msg->rtm_dst_len; - continue; - } - if (rta_src.ptr) - { /* got a source address */ - new_src = host_create_from_chunk(msg->rtm_family, rta_src, 0); - if (new_src) - { - if (get_vip_refcount(this, new_src)) - { /* skip source address if it is installed by us */ - new_src->destroy(new_src); - } - else - { - DESTROY_IF(src); - src = new_src; - best = msg->rtm_dst_len; - } - } - continue; - } - if (rta_oif) - { /* no src or gtw, but an interface. Get address from it. */ - new_src = get_interface_address(this, rta_oif, - msg->rtm_family); - if (new_src) - { - DESTROY_IF(src); - src = new_src; - best = msg->rtm_dst_len; + if (route->src.ptr) + { /* verify source address, if any */ + host_t *src = host_create_from_chunk(msg->rtm_family, + route->src, 0); + if (src && is_known_vip(this, src)) + { /* ignore routes installed by us */ + src->destroy(src); + continue; } - continue; + route->src_host = src; } - if (rta_gtw.ptr) - { /* no source, but a gateway. Lookup source to reach gtw. */ - new_gtw = host_create_from_chunk(msg->rtm_family, rta_gtw, 0); - new_src = get_route(this, new_gtw, FALSE, candidate); - new_gtw->destroy(new_gtw); - if (new_src) + /* insert route, sorted by decreasing network prefix */ + enumerator = routes->create_enumerator(routes); + while (enumerator->enumerate(enumerator, &other)) + { + if (route->dst_len > other->dst_len) { - DESTROY_IF(src); - src = new_src; - best = msg->rtm_dst_len; + break; } - continue; } + routes->insert_before(routes, enumerator, route); + enumerator->destroy(enumerator); + route = NULL; continue; } default: @@ -1038,18 +1508,111 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, } break; } - free(out); - this->mutex->unlock(this->mutex); + if (route) + { + rt_entry_destroy(route); + } + + /* now we have a list of routes matching dest, sorted by net prefix. + * we will look for source addresses for these routes and select the one + * with the preferred source address, if possible */ + enumerator = routes->create_enumerator(routes); + while (enumerator->enumerate(enumerator, &route)) + { + if (route->src_host) + { /* got a source address with the route, if no preferred source + * is given or it matches we are done, as this is the best route */ + if (!candidate || candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + else if (route->oif) + { /* no match yet, maybe it is assigned to the same interface */ + host_t *src = get_interface_address(this, route->oif, + msg->rtm_family, candidate); + if (src && src->ip_equals(src, candidate)) + { + route->src_host->destroy(route->src_host); + route->src_host = src; + best = route; + break; + } + DESTROY_IF(src); + } + /* no luck yet with the source address. if this is the best (first) + * route we store it as fallback in case we don't find a route with + * the preferred source */ + best = best ?: route; + continue; + } + if (route->oif) + { /* no src, but an interface - get address from it */ + route->src_host = get_interface_address(this, route->oif, + msg->rtm_family, candidate); + if (route->src_host) + { /* we handle this address the same as the one above */ + if (!candidate || + candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + best = best ?: route; + continue; + } + } + if (route->gtw.ptr) + { /* no src, no iface, but a gateway - lookup src to reach gtw */ + host_t *gtw; + + gtw = host_create_from_chunk(msg->rtm_family, route->gtw, 0); + route->src_host = get_route(this, gtw, FALSE, candidate); + gtw->destroy(gtw); + if (route->src_host) + { /* more of the same */ + if (!candidate || + candidate->ip_equals(candidate, route->src_host)) + { + best = route; + break; + } + best = best ?: route; + } + } + } + enumerator->destroy(enumerator); if (nexthop) + { /* nexthop lookup, return gateway if any */ + if (best || routes->get_first(routes, (void**)&best) == SUCCESS) + { + addr = host_create_from_chunk(msg->rtm_family, best->gtw, 0); + } + addr = addr ?: dest->clone(dest); + } + else { - if (gtw) + if (best) { - return gtw; + addr = best->src_host->clone(best->src_host); } - return dest->clone(dest); } - return src; + this->lock->unlock(this->lock); + routes->destroy_function(routes, (void*)rt_entry_destroy); + free(out); + + if (addr) + { + DBG2(DBG_KNL, "using %H as %s to reach %H", addr, + nexthop ? "nexthop" : "address", dest); + } + else + { + DBG2(DBG_KNL, "no %s found to reach %H", + nexthop ? "nexthop" : "address", dest); + } + return addr; } METHOD(kernel_net_t, get_source_addr, host_t*, @@ -1059,9 +1622,9 @@ METHOD(kernel_net_t, get_source_addr, host_t*, } METHOD(kernel_net_t, get_nexthop, host_t*, - private_kernel_netlink_net_t *this, host_t *dest) + private_kernel_netlink_net_t *this, host_t *dest, host_t *src) { - return get_route(this, dest, TRUE, NULL); + return get_route(this, dest, TRUE, src); } /** @@ -1100,87 +1663,109 @@ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type METHOD(kernel_net_t, add_ip, status_t, private_kernel_netlink_net_t *this, host_t *virtual_ip, host_t *iface_ip) { - iface_entry_t *iface; - addr_entry_t *addr; - enumerator_t *addrs, *ifaces; - int ifindex; + addr_map_entry_t *entry, lookup = { + .ip = virtual_ip, + }; + iface_entry_t *iface = NULL; if (!this->install_virtual_ip) { /* disabled by config */ return SUCCESS; } - DBG2(DBG_KNL, "adding virtual IP %H", virtual_ip); - - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) - { - bool iface_found = FALSE; - - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) - { - if (iface_ip->ip_equals(iface_ip, addr->ip)) + this->lock->write_lock(this->lock); + /* the virtual IP might actually be installed as regular IP, in which case + * we don't track it as virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* otherwise it might already be installed as virtual IP */ + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (entry) + { /* the vip we found can be in one of three states: 1) installed and + * ready, 2) just added by another thread, but not yet confirmed to + * be installed by the kernel, 3) just deleted, but not yet gone. + * Then while we wait below, several things could happen (as we + * release the lock). For instance, the interface could disappear, + * or the IP is finally deleted, and it reappears on a different + * interface. All these cases are handled by the call below. */ + while (!is_vip_installed_or_gone(this, virtual_ip, &entry)) { - iface_found = TRUE; + this->condvar->wait(this->condvar, this->lock); } - else if (virtual_ip->ip_equals(virtual_ip, addr->ip)) + if (entry) { - addr->refcount++; - DBG2(DBG_KNL, "virtual IP %H already installed on %s", - virtual_ip, iface->ifname); - addrs->destroy(addrs); - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return SUCCESS; + entry->addr->refcount++; } } - addrs->destroy(addrs); - - if (iface_found) + } + if (entry) + { + DBG2(DBG_KNL, "virtual IP %H is already installed on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + return SUCCESS; + } + /* try to find the target interface, either by config or via src ip */ + if (!this->install_virtual_ip_on || + this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, + (void**)&iface, this->install_virtual_ip_on) != SUCCESS) + { + lookup.ip = iface_ip; + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* if we don't find the requested interface we just use the first */ + this->ifaces->get_first(this->ifaces, (void**)&iface); + } + else { - ifindex = iface->ifindex; - addr = malloc_thing(addr_entry_t); - addr->ip = virtual_ip->clone(virtual_ip); - addr->refcount = 0; - addr->virtual = TRUE; - addr->scope = RT_SCOPE_UNIVERSE; - iface->addrs->insert_last(iface->addrs, addr); - - if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, - ifindex, virtual_ip) == SUCCESS) - { - while (get_vip_refcount(this, virtual_ip) == 0) - { /* wait until address appears */ - this->condvar->wait(this->condvar, this->mutex); - } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + iface = entry->iface; + } + } + if (iface) + { + addr_entry_t *addr; + + INIT(addr, + .ip = virtual_ip->clone(virtual_ip), + .refcount = 1, + .scope = RT_SCOPE_UNIVERSE, + ); + iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this->vips, addr, iface); + if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL, + iface->ifindex, virtual_ip) == SUCCESS) + { + while (!is_vip_installed_or_gone(this, virtual_ip, &entry)) + { /* wait until address appears */ + this->condvar->wait(this->condvar, this->lock); + } + if (entry) + { /* we fail if the interface got deleted in the meantime */ + DBG2(DBG_KNL, "virtual IP %H installed on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); return SUCCESS; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip); - return FAILED; } + this->lock->unlock(this->lock); + DBG1(DBG_KNL, "adding virtual IP %H failed", virtual_ip); + return FAILED; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - DBG1(DBG_KNL, "interface address %H not found, unable to install" - "virtual IP %H", iface_ip, virtual_ip); + this->lock->unlock(this->lock); + DBG1(DBG_KNL, "no interface available, unable to install virtual IP %H", + virtual_ip); return FAILED; } METHOD(kernel_net_t, del_ip, status_t, private_kernel_netlink_net_t *this, host_t *virtual_ip) { - iface_entry_t *iface; - addr_entry_t *addr; - enumerator_t *addrs, *ifaces; - status_t status; - int ifindex; + addr_map_entry_t *entry, lookup = { + .ip = virtual_ip, + }; if (!this->install_virtual_ip) { /* disabled by config */ @@ -1189,60 +1774,61 @@ METHOD(kernel_net_t, del_ip, status_t, DBG2(DBG_KNL, "deleting virtual IP %H", virtual_ip); - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) - { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) + this->lock->write_lock(this->lock); + entry = this->vips->get_match(this->vips, &lookup, + (void*)addr_map_entry_match); + if (!entry) + { /* we didn't install this IP as virtual IP */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match); + if (entry) { - if (virtual_ip->ip_equals(virtual_ip, addr->ip)) + DBG2(DBG_KNL, "not deleting existing IP %H on %s", virtual_ip, + entry->iface->ifname); + this->lock->unlock(this->lock); + return SUCCESS; + } + DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); + this->lock->unlock(this->lock); + return FAILED; + } + if (entry->addr->refcount == 1) + { + status_t status; + + /* we set this flag so that threads calling add_ip will block and wait + * until the entry is gone, also so we can wait below */ + entry->addr->installed = FALSE; + status = manage_ipaddr(this, RTM_DELADDR, 0, entry->iface->ifindex, + virtual_ip); + if (status == SUCCESS) + { /* wait until the address is really gone */ + while (is_known_vip(this, virtual_ip)) { - ifindex = iface->ifindex; - if (addr->refcount == 1) - { - status = manage_ipaddr(this, RTM_DELADDR, 0, - ifindex, virtual_ip); - if (status == SUCCESS) - { /* wait until the address is really gone */ - while (get_vip_refcount(this, virtual_ip) > 0) - { - this->condvar->wait(this->condvar, this->mutex); - } - } - addrs->destroy(addrs); - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return status; - } - else - { - addr->refcount--; - } - DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", - virtual_ip); - addrs->destroy(addrs); - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - return SUCCESS; + this->condvar->wait(this->condvar, this->lock); } } - addrs->destroy(addrs); + this->lock->unlock(this->lock); + return status; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - DBG2(DBG_KNL, "virtual IP %H not cached, unable to delete", virtual_ip); - return FAILED; + else + { + entry->addr->refcount--; + } + DBG2(DBG_KNL, "virtual IP %H used by other SAs, not deleting", + virtual_ip); + this->lock->unlock(this->lock); + return SUCCESS; } /** * Manages source routes in the routing table. * By setting the appropriate nlmsg_type, the route gets added or removed. */ -static status_t manage_srcroute(private_kernel_netlink_net_t *this, int nlmsg_type, - int flags, chunk_t dst_net, u_int8_t prefixlen, - host_t *gateway, host_t *src_ip, char *if_name) +static status_t manage_srcroute(private_kernel_netlink_net_t *this, + int nlmsg_type, int flags, chunk_t dst_net, + u_int8_t prefixlen, host_t *gateway, + host_t *src_ip, char *if_name) { netlink_buf_t request; struct nlmsghdr *hdr; @@ -1306,16 +1892,56 @@ METHOD(kernel_net_t, add_route, status_t, private_kernel_netlink_net_t *this, chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) { - return manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, - dst_net, prefixlen, gateway, src_ip, if_name); + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .src_ip = src_ip, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (found) + { + this->routes_lock->unlock(this->routes_lock); + return ALREADY_DONE; + } + found = route_entry_clone(&route); + this->routes->put(this->routes, found, found); + status = manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, + dst_net, prefixlen, gateway, src_ip, if_name); + this->routes_lock->unlock(this->routes_lock); + return status; } METHOD(kernel_net_t, del_route, status_t, private_kernel_netlink_net_t *this, chunk_t dst_net, u_int8_t prefixlen, host_t *gateway, host_t *src_ip, char *if_name) { - return manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen, - gateway, src_ip, if_name); + status_t status; + route_entry_t *found, route = { + .dst_net = dst_net, + .prefixlen = prefixlen, + .gateway = gateway, + .src_ip = src_ip, + .if_name = if_name, + }; + + this->routes_lock->lock(this->routes_lock); + found = this->routes->get(this->routes, &route); + if (!found) + { + this->routes_lock->unlock(this->routes_lock); + return NOT_FOUND; + } + this->routes->remove(this->routes, found); + route_entry_destroy(found); + status = manage_srcroute(this, RTM_DELROUTE, 0, dst_net, prefixlen, + gateway, src_ip, if_name); + this->routes_lock->unlock(this->routes_lock); + return status; } /** @@ -1331,7 +1957,7 @@ static status_t init_address_list(private_kernel_netlink_net_t *this) iface_entry_t *iface; addr_entry_t *addr; - DBG1(DBG_KNL, "listening on interfaces:"); + DBG2(DBG_KNL, "known interfaces and IP addresses:"); memset(&request, 0, sizeof(request)); @@ -1389,23 +2015,23 @@ static status_t init_address_list(private_kernel_netlink_net_t *this) } free(out); - this->mutex->lock(this->mutex); + this->lock->read_lock(this->lock); ifaces = this->ifaces->create_enumerator(this->ifaces); while (ifaces->enumerate(ifaces, &iface)) { - if (iface->flags & IFF_UP) + if (iface_entry_up_and_usable(iface)) { - DBG1(DBG_KNL, " %s", iface->ifname); + DBG2(DBG_KNL, " %s", iface->ifname); addrs = iface->addrs->create_enumerator(iface->addrs); while (addrs->enumerate(addrs, (void**)&addr)) { - DBG1(DBG_KNL, " %H", addr->ip); + DBG2(DBG_KNL, " %H", addr->ip); } addrs->destroy(addrs); } } ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); return SUCCESS; } @@ -1443,9 +2069,59 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type, return this->socket->send_ack(this->socket, hdr); } +/** + * check for kernel features (currently only via version number) + */ +static void check_kernel_features(private_kernel_netlink_net_t *this) +{ + struct utsname utsname; + int a, b, c; + + if (uname(&utsname) == 0) + { + switch(sscanf(utsname.release, "%d.%d.%d", &a, &b, &c)) + { + case 3: + if (a == 2) + { + DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for " + "RTA_PREFSRC for IPv6 routes", a, b, c); + break; + } + /* fall-through */ + case 2: + /* only 3.x+ uses two part version numbers */ + this->rta_prefsrc_for_ipv6 = TRUE; + break; + default: + break; + } + } +} + +/** + * Destroy an address to iface map + */ +static void addr_map_destroy(hashtable_t *map) +{ + enumerator_t *enumerator; + addr_map_entry_t *addr; + + enumerator = map->create_enumerator(map); + while (enumerator->enumerate(enumerator, NULL, (void**)&addr)) + { + free(addr); + } + enumerator->destroy(enumerator); + map->destroy(map); +} + METHOD(kernel_net_t, destroy, void, private_kernel_netlink_net_t *this) { + enumerator_t *enumerator; + route_entry_t *route; + if (this->routing_table) { manage_rule(this, RTM_DELRULE, AF_INET, this->routing_table, @@ -1453,19 +2129,34 @@ METHOD(kernel_net_t, destroy, void, manage_rule(this, RTM_DELRULE, AF_INET6, this->routing_table, this->routing_table_prio); } - if (this->job) - { - this->job->cancel(this->job); - } if (this->socket_events > 0) { close(this->socket_events); } + enumerator = this->routes->create_enumerator(this->routes); + while (enumerator->enumerate(enumerator, NULL, (void**)&route)) + { + manage_srcroute(this, RTM_DELROUTE, 0, route->dst_net, route->prefixlen, + route->gateway, route->src_ip, route->if_name); + route_entry_destroy(route); + } + enumerator->destroy(enumerator); + this->routes->destroy(this->routes); + this->routes_lock->destroy(this->routes_lock); DESTROY_IF(this->socket); + + net_changes_clear(this); + this->net_changes->destroy(this->net_changes); + this->net_changes_lock->destroy(this->net_changes_lock); + + addr_map_destroy(this->addrs); + addr_map_destroy(this->vips); + this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); this->rt_exclude->destroy(this->rt_exclude); + this->roam_lock->destroy(this->roam_lock); this->condvar->destroy(this->condvar); - this->mutex->destroy(this->mutex); + this->lock->destroy(this->lock); free(this); } @@ -1475,8 +2166,8 @@ METHOD(kernel_net_t, destroy, void, kernel_netlink_net_t *kernel_netlink_net_create() { private_kernel_netlink_net_t *this; - struct sockaddr_nl addr; enumerator_t *enumerator; + bool register_for_events = TRUE; char *exclude; INIT(this, @@ -1495,9 +2186,22 @@ kernel_netlink_net_t *kernel_netlink_net_create() }, .socket = netlink_socket_create(NETLINK_ROUTE), .rt_exclude = linked_list_create(), + .routes = hashtable_create((hashtable_hash_t)route_entry_hash, + (hashtable_equals_t)route_entry_equals, 16), + .net_changes = hashtable_create( + (hashtable_hash_t)net_change_hash, + (hashtable_equals_t)net_change_equals, 16), + .addrs = hashtable_create( + (hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .vips = hashtable_create((hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .routes_lock = mutex_create(MUTEX_TYPE_DEFAULT), + .net_changes_lock = mutex_create(MUTEX_TYPE_DEFAULT), .ifaces = linked_list_create(), - .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), - .condvar = condvar_create(CONDVAR_TYPE_DEFAULT), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + .condvar = rwlock_condvar_create(), + .roam_lock = spinlock_create(), .routing_table = lib->settings->get_int(lib->settings, "%s.routing_table", ROUTING_TABLE, hydra->daemon), .routing_table_prio = lib->settings->get_int(lib->settings, @@ -1506,8 +2210,18 @@ kernel_netlink_net_t *kernel_netlink_net_create() "%s.process_route", TRUE, hydra->daemon), .install_virtual_ip = lib->settings->get_bool(lib->settings, "%s.install_virtual_ip", TRUE, hydra->daemon), + .install_virtual_ip_on = lib->settings->get_str(lib->settings, + "%s.install_virtual_ip_on", NULL, hydra->daemon), ); - timerclear(&this->last_roam); + timerclear(&this->last_route_reinstall); + timerclear(&this->next_roam); + + check_kernel_features(this); + + if (streq(hydra->daemon, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } exclude = lib->settings->get_str(lib->settings, "%s.ignore_routing_tables", NULL, hydra->daemon); @@ -1530,29 +2244,35 @@ kernel_netlink_net_t *kernel_netlink_net_create() enumerator->destroy(enumerator); } - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - /* create and bind RT socket for events (address/interface/route changes) */ - this->socket_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (this->socket_events < 0) + if (register_for_events) { - DBG1(DBG_KNL, "unable to create RT event socket"); - destroy(this); - return NULL; - } - addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; - if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) - { - DBG1(DBG_KNL, "unable to bind RT event socket"); - destroy(this); - return NULL; - } + struct sockaddr_nl addr; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); + /* create and bind RT socket for events (address/interface/route changes) */ + this->socket_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (this->socket_events < 0) + { + DBG1(DBG_KNL, "unable to create RT event socket"); + destroy(this); + return NULL; + } + addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | + RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_LINK; + if (bind(this->socket_events, (struct sockaddr*)&addr, sizeof(addr))) + { + DBG1(DBG_KNL, "unable to bind RT event socket"); + destroy(this); + return NULL; + } + + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)receive_events, this, NULL, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + } if (init_address_list(this) != SUCCESS) { diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c index dad3fb68e..285f6c8b2 100644 --- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c @@ -206,6 +206,11 @@ METHOD(netlink_socket_t, netlink_send_ack, status_t, free(out); return ALREADY_DONE; } + if (-err->error == ESRCH) + { /* do not report missing entries */ + free(out); + return NOT_FOUND; + } DBG1(DBG_KNL, "received netlink error: %s (%d)", strerror(-err->error), -err->error); free(out); diff --git a/src/libhydra/plugins/kernel_pfkey/Makefile.in b/src/libhydra/plugins/kernel_pfkey/Makefile.in index 14c924b6f..aac85a4e6 100644 --- a/src/libhydra/plugins/kernel_pfkey/Makefile.in +++ b/src/libhydra/plugins/kernel_pfkey/Makefile.in @@ -49,6 +49,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -86,7 +87,7 @@ libstrongswan_kernel_pfkey_la_LINK = $(LIBTOOL) --tag=CC \ @MONOLITHIC_FALSE@am_libstrongswan_kernel_pfkey_la_rpath = -rpath \ @MONOLITHIC_FALSE@ $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_kernel_pfkey_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -112,6 +113,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -206,11 +208,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -227,11 +232,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -247,6 +253,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -256,7 +263,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c index da10edffe..b099bc714 100644 --- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c +++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008-2011 Tobias Brunner + * Copyright (C) 2008-2012 Tobias Brunner * Copyright (C) 2008 Andreas Steffen * Hochschule fuer Technik Rapperswil * @@ -51,6 +51,9 @@ #include <unistd.h> #include <time.h> #include <errno.h> +#ifdef __APPLE__ +#include <sys/sysctl.h> +#endif #include "kernel_pfkey_ipsec.h" @@ -99,6 +102,20 @@ #define IPV6_IPSEC_POLICY 34 #endif +/* from linux/udp.h */ +#ifndef UDP_ENCAP +#define UDP_ENCAP 100 +#endif + +#ifndef UDP_ENCAP_ESPINUDP +#define UDP_ENCAP_ESPINUDP 2 +#endif + +/* this is not defined on some platforms */ +#ifndef SOL_UDP +#define SOL_UDP IPPROTO_UDP +#endif + /** default priority of installed policies */ #define PRIO_BASE 512 @@ -173,11 +190,6 @@ struct private_kernel_pfkey_ipsec_t bool install_routes; /** - * job receiving PF_KEY events - */ - callback_job_t *job; - - /** * mutex to lock access to the PF_KEY socket */ mutex_t *mutex_pfkey; @@ -238,8 +250,8 @@ static void route_entry_destroy(route_entry_t *this) static bool route_entry_equals(route_entry_t *a, route_entry_t *b) { return a->if_name && b->if_name && streq(a->if_name, b->if_name) && - a->src_ip->equals(a->src_ip, b->src_ip) && - a->gateway->equals(a->gateway, b->gateway) && + a->src_ip->ip_equals(a->src_ip, b->src_ip) && + a->gateway->ip_equals(a->gateway, b->gateway) && chunk_equals(a->dst_net, b->dst_net) && a->prefixlen == b->prefixlen; } @@ -795,8 +807,22 @@ static kernel_algorithm_t compression_algs[] = { /** * Look up a kernel algorithm ID and its key size */ -static int lookup_algorithm(kernel_algorithm_t *list, int ikev2) +static int lookup_algorithm(transform_type_t type, int ikev2) { + kernel_algorithm_t *list; + int alg = 0; + + switch (type) + { + case ENCRYPTION_ALGORITHM: + list = encryption_algs; + break; + case INTEGRITY_ALGORITHM: + list = integrity_algs; + break; + default: + return 0; + } while (list->ikev2 != END_OF_LIST) { if (ikev2 == list->ikev2) @@ -805,18 +831,21 @@ static int lookup_algorithm(kernel_algorithm_t *list, int ikev2) } list++; } - return 0; + hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, ikev2, + type, &alg, NULL); + return alg; } /** - * Copy a host_t as sockaddr_t to the given memory location. Ports are - * reset to zero as per RFC 2367. + * Copy a host_t as sockaddr_t to the given memory location. * @return the number of bytes copied */ -static size_t hostcpy(void *dest, host_t *host) +static size_t hostcpy(void *dest, host_t *host, bool include_port) { sockaddr_t *addr = host->get_sockaddr(host), *dest_addr = dest; socklen_t *len = host->get_sockaddr_len(host); + u_int16_t port = htons(host->get_port(host)); + memcpy(dest, addr, *len); #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN dest_addr->sa_len = *len; @@ -826,13 +855,13 @@ static size_t hostcpy(void *dest, host_t *host) case AF_INET: { struct sockaddr_in *sin = dest; - sin->sin_port = 0; + sin->sin_port = include_port ? port : 0; break; } case AF_INET6: { struct sockaddr_in6 *sin6 = dest; - sin6->sin6_port = 0; + sin6->sin6_port = include_port ? port : 0; break; } } @@ -842,9 +871,9 @@ static size_t hostcpy(void *dest, host_t *host) /** * add a host behind an sadb_address extension */ -static void host2ext(host_t *host, struct sadb_address *ext) +static void host2ext(host_t *host, struct sadb_address *ext, bool include_port) { - size_t len = hostcpy(ext + 1, host); + size_t len = hostcpy(ext + 1, host, include_port); ext->sadb_address_len = PFKEY_LEN(sizeof(*ext) + len); } @@ -852,13 +881,13 @@ static void host2ext(host_t *host, struct sadb_address *ext) * add a host to the given sadb_msg */ static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type, - u_int8_t proto, u_int8_t prefixlen) + u_int8_t proto, u_int8_t prefixlen, bool include_port) { struct sadb_address *addr = (struct sadb_address*)PFKEY_EXT_ADD_NEXT(msg); addr->sadb_address_exttype = type; addr->sadb_address_proto = proto; addr->sadb_address_prefixlen = prefixlen; - host2ext(host, addr); + host2ext(host, addr, include_port); PFKEY_EXT_ADD(msg, addr); } @@ -1292,11 +1321,13 @@ static void process_mapping(private_kernel_pfkey_ipsec_t *this, { struct sockaddr_in *sin = (struct sockaddr_in*)sa; sin->sin_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); + break; } case AF_INET6: { struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)sa; sin6->sin6_port = htons(response.x_natt_dport->sadb_x_nat_t_port_port); + break; } default: break; @@ -1410,8 +1441,8 @@ METHOD(kernel_ipsec_t, get_spi, status_t, sa2->sadb_x_sa2_reqid = reqid; PFKEY_EXT_ADD(msg, sa2); - add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0); - add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0); + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); range = (struct sadb_spirange*)PFKEY_EXT_ADD_NEXT(msg); range->sadb_spirange_exttype = SADB_EXT_SPIRANGE; @@ -1497,8 +1528,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, sa->sadb_sa_len = PFKEY_LEN(len); sa->sadb_sa_spi = spi; 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); + sa->sadb_sa_auth = lookup_algorithm(INTEGRITY_ALGORITHM, int_alg); + sa->sadb_sa_encrypt = lookup_algorithm(ENCRYPTION_ALGORITHM, enc_alg); PFKEY_EXT_ADD(msg, sa); sa2 = (struct sadb_x_sa2*)PFKEY_EXT_ADD_NEXT(msg); @@ -1508,8 +1539,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t, sa2->sadb_x_sa2_reqid = reqid; PFKEY_EXT_ADD(msg, sa2); - add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0); - add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0); + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); lft = (struct sadb_lifetime*)PFKEY_EXT_ADD_NEXT(msg); lft->sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; @@ -1639,7 +1670,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, /* the kernel wants a 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, 0, 0); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); if (pfkey_send(this, msg, &out, &len) != SUCCESS) { @@ -1762,8 +1793,8 @@ METHOD(kernel_ipsec_t, query_sa, status_t, /* the Linux Kernel doesn't care for the src address, but other systems do * (e.g. FreeBSD) */ - add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0); - add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0); + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); if (pfkey_send(this, msg, &out, &len) != SUCCESS) { @@ -1818,8 +1849,8 @@ METHOD(kernel_ipsec_t, del_sa, status_t, /* the Linux Kernel doesn't care for the src address, but other systems do * (e.g. FreeBSD) */ - add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0); - add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0); + add_addr_ext(msg, src, SADB_EXT_ADDRESS_SRC, 0, 0, FALSE); + add_addr_ext(msg, dst, SADB_EXT_ADDRESS_DST, 0, 0, FALSE); if (pfkey_send(this, msg, &out, &len) != SUCCESS) { @@ -1919,9 +1950,9 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, req->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE; if (ipsec->cfg.mode == MODE_TUNNEL) { - len = hostcpy(req + 1, ipsec->src); + len = hostcpy(req + 1, ipsec->src, FALSE); req->sadb_x_ipsecrequest_len += len; - len = hostcpy((char*)(req + 1) + len, ipsec->dst); + len = hostcpy((char*)(req + 1) + len, ipsec->dst, FALSE); req->sadb_x_ipsecrequest_len += len; } @@ -1929,9 +1960,9 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, PFKEY_EXT_ADD(msg, pol); add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, - policy->src.mask); + policy->src.mask, TRUE); add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, - policy->dst.mask); + policy->dst.mask, TRUE); #ifdef __FreeBSD__ { /* on FreeBSD a lifetime has to be defined to be able to later query @@ -1989,23 +2020,26 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, if (policy->direction == POLICY_FWD && ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes) { - route_entry_t *route = malloc_thing(route_entry_t); policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping; + route_entry_t *route; + + INIT(route, + .prefixlen = policy->src.mask, + ); if (hydra->kernel_interface->get_address_by_ts(hydra->kernel_interface, fwd->dst_ts, &route->src_ip) == SUCCESS) { /* get the nexthop to src (src as we are in POLICY_FWD).*/ route->gateway = hydra->kernel_interface->get_nexthop( - hydra->kernel_interface, ipsec->src); - /* install route via outgoing interface */ - route->if_name = hydra->kernel_interface->get_interface( - hydra->kernel_interface, ipsec->dst); + hydra->kernel_interface, ipsec->src, + ipsec->dst); route->dst_net = chunk_clone(policy->src.net->get_address( policy->src.net)); - route->prefixlen = policy->src.mask; - if (!route->if_name) + /* install route via outgoing interface */ + if (!hydra->kernel_interface->get_interface(hydra->kernel_interface, + ipsec->dst, &route->if_name)) { this->mutex->unlock(this->mutex); route_entry_destroy(route); @@ -2016,12 +2050,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this, { route_entry_t *old = policy->route; if (route_entry_equals(old, route)) - { /* keep previously installed route. since it might have - * still been removed by an address change, we install it - * again but ignore the result */ - hydra->kernel_interface->add_route(hydra->kernel_interface, - route->dst_net, route->prefixlen, route->gateway, - route->src_ip, route->if_name); + { this->mutex->unlock(this->mutex); route_entry_destroy(route); return SUCCESS; @@ -2200,9 +2229,9 @@ METHOD(kernel_ipsec_t, query_policy, status_t, PFKEY_EXT_ADD(msg, pol); add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, - policy->src.mask); + policy->src.mask, TRUE); add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, - policy->dst.mask); + policy->dst.mask, TRUE); this->mutex->unlock(this->mutex); @@ -2344,9 +2373,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t, PFKEY_EXT_ADD(msg, pol); add_addr_ext(msg, policy->src.net, SADB_EXT_ADDRESS_SRC, policy->src.proto, - policy->src.mask); + policy->src.mask, TRUE); add_addr_ext(msg, policy->dst.net, SADB_EXT_ADDRESS_DST, policy->dst.proto, - policy->dst.mask); + policy->dst.mask, TRUE); if (policy->route) { @@ -2497,13 +2526,33 @@ METHOD(kernel_ipsec_t, bypass_socket, bool, return TRUE; } -METHOD(kernel_ipsec_t, destroy, void, - private_kernel_pfkey_ipsec_t *this) +METHOD(kernel_ipsec_t, enable_udp_decap, bool, + private_kernel_pfkey_ipsec_t *this, int fd, int family, u_int16_t port) { - if (this->job) +#ifndef __APPLE__ + int type = UDP_ENCAP_ESPINUDP; + + if (setsockopt(fd, SOL_UDP, UDP_ENCAP, &type, sizeof(type)) < 0) { - this->job->cancel(this->job); + DBG1(DBG_KNL, "unable to set UDP_ENCAP: %s", strerror(errno)); + return FALSE; } +#else /* __APPLE__ */ + if (sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &port, + sizeof(port)) != 0) + { + DBG1(DBG_KNL, "could not set net.inet.ipsec.esp_port to %d: %s", + port, strerror(errno)); + return FALSE; + } +#endif /* __APPLE__ */ + + return TRUE; +} + +METHOD(kernel_ipsec_t, destroy, void, + private_kernel_pfkey_ipsec_t *this) +{ if (this->socket > 0) { close(this->socket); @@ -2528,6 +2577,7 @@ METHOD(kernel_ipsec_t, destroy, void, kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() { private_kernel_pfkey_ipsec_t *this; + bool register_for_events = TRUE; INIT(this, .public = { @@ -2544,6 +2594,7 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() .del_policy = _del_policy, .flush_policies = _flush_policies, .bypass_socket = _bypass_socket, + .enable_udp_decap = _enable_udp_decap, .destroy = _destroy, }, }, @@ -2561,6 +2612,10 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() { /* no routes for pluto, they are installed via updown script */ this->install_routes = FALSE; } + else if (streq(hydra->daemon, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } /* create a PF_KEY socket to communicate with the kernel */ this->socket = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); @@ -2571,27 +2626,31 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create() return NULL; } - /* create a PF_KEY socket for ACQUIRE & EXPIRE */ - this->socket_events = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); - if (this->socket_events <= 0) + if (register_for_events) { - DBG1(DBG_KNL, "unable to create PF_KEY event socket"); - destroy(this); - return NULL; - } + /* create a PF_KEY socket for ACQUIRE & EXPIRE */ + this->socket_events = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (this->socket_events <= 0) + { + DBG1(DBG_KNL, "unable to create PF_KEY event socket"); + destroy(this); + return NULL; + } - /* register the event socket */ - if (register_pfkey_socket(this, SADB_SATYPE_ESP) != SUCCESS || - register_pfkey_socket(this, SADB_SATYPE_AH) != SUCCESS) - { - DBG1(DBG_KNL, "unable to register PF_KEY event socket"); - destroy(this); - return NULL; - } + /* register the event socket */ + if (register_pfkey_socket(this, SADB_SATYPE_ESP) != SUCCESS || + register_pfkey_socket(this, SADB_SATYPE_AH) != SUCCESS) + { + DBG1(DBG_KNL, "unable to register PF_KEY event socket"); + destroy(this); + return NULL; + } - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)receive_events, this, NULL, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + } return &this->public; } diff --git a/src/libhydra/plugins/kernel_pfroute/Makefile.in b/src/libhydra/plugins/kernel_pfroute/Makefile.in index 1412db0ec..6fa2fc9c1 100644 --- a/src/libhydra/plugins/kernel_pfroute/Makefile.in +++ b/src/libhydra/plugins/kernel_pfroute/Makefile.in @@ -49,6 +49,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -86,7 +87,7 @@ libstrongswan_kernel_pfroute_la_LINK = $(LIBTOOL) --tag=CC \ @MONOLITHIC_FALSE@am_libstrongswan_kernel_pfroute_la_rpath = -rpath \ @MONOLITHIC_FALSE@ $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_kernel_pfroute_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -112,6 +113,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -206,11 +208,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -227,11 +232,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -247,6 +253,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -256,7 +263,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c index 5464568df..16a46bb56 100644 --- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c +++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Tobias Brunner + * Copyright (C) 2009-2012 Tobias Brunner * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it @@ -28,6 +28,8 @@ #include <utils/host.h> #include <threading/thread.h> #include <threading/mutex.h> +#include <threading/rwlock.h> +#include <utils/hashtable.h> #include <utils/linked_list.h> #include <processing/jobs/callback_job.h> @@ -85,6 +87,9 @@ struct iface_entry_t { /** list of addresses as host_t */ linked_list_t *addrs; + + /** TRUE if usable by config */ + bool usable; }; /** @@ -96,6 +101,73 @@ static void iface_entry_destroy(iface_entry_t *this) free(this); } +/** + * check if an interface is up + */ +static inline bool iface_entry_up(iface_entry_t *iface) +{ + return (iface->flags & IFF_UP) == IFF_UP; +} + +/** + * check if an interface is up and usable + */ +static inline bool iface_entry_up_and_usable(iface_entry_t *iface) +{ + return iface->usable && iface_entry_up(iface); +} + +typedef struct addr_map_entry_t addr_map_entry_t; + +/** + * Entry that maps an IP address to an interface entry + */ +struct addr_map_entry_t { + /** The IP address */ + host_t *ip; + + /** The interface this address is installed on */ + iface_entry_t *iface; +}; + +/** + * Hash a addr_map_entry_t object, all entries with the same IP address + * are stored in the same bucket + */ +static u_int addr_map_entry_hash(addr_map_entry_t *this) +{ + return chunk_hash(this->ip->get_address(this->ip)); +} + +/** + * Compare two addr_map_entry_t objects, two entries are equal if they are + * installed on the same interface + */ +static bool addr_map_entry_equals(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return a->iface->ifindex == b->iface->ifindex && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * an up and usable interface + */ +static bool addr_map_entry_match_up_and_usable(addr_map_entry_t *a, + addr_map_entry_t *b) +{ + return iface_entry_up_and_usable(b->iface) && + a->ip->ip_equals(a->ip, b->ip); +} + +/** + * Used with get_match this finds an address entry if it is installed on + * any active local interface + */ +static bool addr_map_entry_match_up(addr_map_entry_t *a, addr_map_entry_t *b) +{ + return iface_entry_up(b->iface) && a->ip->ip_equals(a->ip, b->ip); +} typedef struct private_kernel_pfroute_net_t private_kernel_pfroute_net_t; @@ -110,9 +182,9 @@ struct private_kernel_pfroute_net_t kernel_pfroute_net_t public; /** - * mutex to lock access to various lists + * lock to access lists and maps */ - mutex_t *mutex; + rwlock_t *lock; /** * Cached list of interfaces and their addresses (iface_entry_t) @@ -120,9 +192,9 @@ struct private_kernel_pfroute_net_t linked_list_t *ifaces; /** - * job receiving PF_ROUTE events + * Map for IP addresses to iface_entry_t objects (addr_map_entry_t) */ - callback_job_t *job; + hashtable_t *addrs; /** * mutex to lock access to the PF_ROUTE socket @@ -151,6 +223,48 @@ struct private_kernel_pfroute_net_t }; /** + * Add an address map entry + */ +static void addr_map_entry_add(private_kernel_pfroute_net_t *this, + addr_entry_t *addr, iface_entry_t *iface) +{ + addr_map_entry_t *entry; + + if (addr->virtual) + { /* don't map virtual IPs */ + return; + } + + INIT(entry, + .ip = addr->ip, + .iface = iface, + ); + entry = this->addrs->put(this->addrs, entry, entry); + free(entry); +} + +/** + * Remove an address map entry (the argument order is a bit strange because + * it is also used with linked_list_t.invoke_function) + */ +static void addr_map_entry_remove(addr_entry_t *addr, iface_entry_t *iface, + private_kernel_pfroute_net_t *this) +{ + addr_map_entry_t *entry, lookup = { + .ip = addr->ip, + .iface = iface, + }; + + if (addr->virtual) + { /* these are never mapped, but this check avoid problems if a virtual IP + * equals a regular one */ + return; + } + entry = this->addrs->remove(this->addrs, &lookup); + free(entry); +} + +/** * callback function that raises the delayed roam event */ static job_requeue_t roam_event(uintptr_t address) @@ -219,7 +333,7 @@ static void process_addr(private_kernel_pfroute_net_t *this, return; } - this->mutex->lock(this->mutex); + this->lock->write_lock(this->lock); ifaces = this->ifaces->create_enumerator(this->ifaces); while (ifaces->enumerate(ifaces, &iface)) { @@ -234,12 +348,13 @@ static void process_addr(private_kernel_pfroute_net_t *this, if (ifa->ifam_type == RTM_DELADDR) { iface->addrs->remove_at(iface->addrs, addrs); - if (!addr->virtual) + if (!addr->virtual && iface->usable) { changed = TRUE; DBG1(DBG_KNL, "%H disappeared from %s", host, iface->ifname); } + addr_map_entry_remove(addr, iface, this); addr_entry_destroy(addr); } else if (ifa->ifam_type == RTM_NEWADDR && addr->virtual) @@ -258,10 +373,14 @@ static void process_addr(private_kernel_pfroute_net_t *this, addr->virtual = FALSE; addr->refcount = 1; iface->addrs->insert_last(iface->addrs, addr); - DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); + addr_map_entry_add(this, addr, iface); + if (iface->usable) + { + DBG1(DBG_KNL, "%H appeared on %s", host, iface->ifname); + } } - if (changed && (iface->flags & IFF_UP)) + if (changed && iface_entry_up_and_usable(iface)) { roam = TRUE; } @@ -269,7 +388,7 @@ static void process_addr(private_kernel_pfroute_net_t *this, } } ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); host->destroy(host); if (roam) @@ -289,33 +408,31 @@ static void process_link(private_kernel_pfroute_net_t *this, iface_entry_t *iface; bool roam = FALSE; - if (msg->ifm_flags & IFF_LOOPBACK) - { /* ignore loopback interfaces */ - return; - } - - this->mutex->lock(this->mutex); + this->lock->write_lock(this->lock); enumerator = this->ifaces->create_enumerator(this->ifaces); while (enumerator->enumerate(enumerator, &iface)) { if (iface->ifindex == msg->ifm_index) { - if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP)) - { - roam = TRUE; - DBG1(DBG_KNL, "interface %s activated", iface->ifname); - } - else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP)) + if (iface->usable) { - roam = TRUE; - DBG1(DBG_KNL, "interface %s deactivated", iface->ifname); + if (!(iface->flags & IFF_UP) && (msg->ifm_flags & IFF_UP)) + { + roam = TRUE; + DBG1(DBG_KNL, "interface %s activated", iface->ifname); + } + else if ((iface->flags & IFF_UP) && !(msg->ifm_flags & IFF_UP)) + { + roam = TRUE; + DBG1(DBG_KNL, "interface %s deactivated", iface->ifname); + } } iface->flags = msg->ifm_flags; break; } } enumerator->destroy(enumerator); - this->mutex->unlock(this->mutex); + this->lock->unlock(this->lock); if (roam) { @@ -394,10 +511,8 @@ static job_requeue_t receive_events(private_kernel_pfroute_net_t *this) /** enumerator over addresses */ typedef struct { private_kernel_pfroute_net_t* this; - /** whether to enumerate down interfaces */ - bool include_down_ifaces; - /** whether to enumerate virtual ip addresses */ - bool include_virtual_ips; + /** which addresses to enumerate */ + kernel_address_type_t which; } address_enumerator_t; /** @@ -405,7 +520,7 @@ typedef struct { */ static void address_enumerator_destroy(address_enumerator_t *data) { - data->this->mutex->unlock(data->this->mutex); + data->this->lock->unlock(data->this->lock); free(data); } @@ -416,7 +531,7 @@ static bool filter_addresses(address_enumerator_t *data, addr_entry_t** in, host_t** out) { host_t *ip; - if (!data->include_virtual_ips && (*in)->virtual) + if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->virtual) { /* skip virtual interfaces added by us */ return FALSE; } @@ -449,8 +564,16 @@ static enumerator_t *create_iface_enumerator(iface_entry_t *iface, static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, iface_entry_t** out) { - if (!data->include_down_ifaces && !((*in)->flags & IFF_UP)) - { /* skip interfaces not up */ + if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable) + { /* skip interfaces excluded by config */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK)) + { /* ignore loopback devices */ + return FALSE; + } + if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP)) + { /* skip interfaces not up */ return FALSE; } *out = *in; @@ -458,15 +581,13 @@ static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, } METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, - private_kernel_pfroute_net_t *this, - bool include_down_ifaces, bool include_virtual_ips) + private_kernel_pfroute_net_t *this, kernel_address_type_t which) { address_enumerator_t *data = malloc_thing(address_enumerator_t); data->this = this; - data->include_down_ifaces = include_down_ifaces; - data->include_virtual_ips = include_virtual_ips; + data->which = which; - this->mutex->lock(this->mutex); + this->lock->read_lock(this->lock); return enumerator_create_nested( enumerator_create_filter( this->ifaces->create_enumerator(this->ifaces), @@ -475,47 +596,40 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, (void*)address_enumerator_destroy); } -METHOD(kernel_net_t, get_interface_name, char*, - private_kernel_pfroute_net_t *this, host_t* ip) +METHOD(kernel_net_t, get_interface_name, bool, + private_kernel_pfroute_net_t *this, host_t* ip, char **name) { - enumerator_t *ifaces, *addrs; - iface_entry_t *iface; - addr_entry_t *addr; - char *name = NULL; - - DBG2(DBG_KNL, "getting interface name for %H", ip); + addr_map_entry_t *entry, lookup = { + .ip = ip, + }; - this->mutex->lock(this->mutex); - ifaces = this->ifaces->create_enumerator(this->ifaces); - while (ifaces->enumerate(ifaces, &iface)) + if (ip->is_anyaddr(ip)) + { + return FALSE; + } + this->lock->read_lock(this->lock); + /* first try to find it on an up and usable interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up_and_usable); + if (entry) { - addrs = iface->addrs->create_enumerator(iface->addrs); - while (addrs->enumerate(addrs, &addr)) - { - if (ip->ip_equals(ip, addr->ip)) - { - name = strdup(iface->ifname); - break; - } - } - addrs->destroy(addrs); if (name) { - break; + *name = strdup(entry->iface->ifname); + DBG2(DBG_KNL, "%H is on interface %s", ip, *name); } + this->lock->unlock(this->lock); + return TRUE; } - ifaces->destroy(ifaces); - this->mutex->unlock(this->mutex); - - if (name) - { - DBG2(DBG_KNL, "%H is on interface %s", ip, name); - } - else - { - DBG2(DBG_KNL, "%H is not a local address", ip); + /* maybe it is installed on an ignored interface */ + entry = this->addrs->get_match(this->addrs, &lookup, + (void*)addr_map_entry_match_up); + if (!entry) + { /* the address does not exist, is on a down interface */ + DBG2(DBG_KNL, "%H is not a local address or the interface is down", ip); } - return name; + this->lock->unlock(this->lock); + return FALSE; } METHOD(kernel_net_t, get_source_addr, host_t*, @@ -525,7 +639,7 @@ METHOD(kernel_net_t, get_source_addr, host_t*, } METHOD(kernel_net_t, get_nexthop, host_t*, - private_kernel_pfroute_net_t *this, host_t *dest) + private_kernel_pfroute_net_t *this, host_t *dest, host_t *src) { return NULL; } @@ -566,7 +680,7 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) addr_entry_t *addr; enumerator_t *ifaces, *addrs; - DBG1(DBG_KNL, "listening on interfaces:"); + DBG2(DBG_KNL, "known interfaces and IP addresses:"); if (getifaddrs(&ifap) < 0) { @@ -586,11 +700,6 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) case AF_INET: case AF_INET6: { - if (ifa->ifa_flags & IFF_LOOPBACK) - { /* ignore loopback interfaces */ - continue; - } - iface = NULL; ifaces = this->ifaces->create_enumerator(this->ifaces); while (ifaces->enumerate(ifaces, ¤t)) @@ -610,6 +719,8 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) iface->ifindex = if_nametoindex(ifa->ifa_name); iface->flags = ifa->ifa_flags; iface->addrs = linked_list_create(); + iface->usable = hydra->kernel_interface->is_interface_usable( + hydra->kernel_interface, ifa->ifa_name); this->ifaces->insert_last(this->ifaces, iface); } @@ -620,6 +731,7 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) addr->virtual = FALSE; addr->refcount = 1; iface->addrs->insert_last(iface->addrs, addr); + addr_map_entry_add(this, addr, iface); } } } @@ -629,13 +741,13 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) ifaces = this->ifaces->create_enumerator(this->ifaces); while (ifaces->enumerate(ifaces, &iface)) { - if (iface->flags & IFF_UP) + if (iface->usable && iface->flags & IFF_UP) { - DBG1(DBG_KNL, " %s", iface->ifname); + DBG2(DBG_KNL, " %s", iface->ifname); addrs = iface->addrs->create_enumerator(iface->addrs); while (addrs->enumerate(addrs, (void**)&addr)) { - DBG1(DBG_KNL, " %H", addr->ip); + DBG2(DBG_KNL, " %H", addr->ip); } addrs->destroy(addrs); } @@ -648,10 +760,9 @@ static status_t init_address_list(private_kernel_pfroute_net_t *this) METHOD(kernel_net_t, destroy, void, private_kernel_pfroute_net_t *this) { - if (this->job) - { - this->job->cancel(this->job); - } + enumerator_t *enumerator; + addr_entry_t *addr; + if (this->socket > 0) { close(this->socket); @@ -660,8 +771,15 @@ METHOD(kernel_net_t, destroy, void, { close(this->socket_events); } + enumerator = this->addrs->create_enumerator(this->addrs); + while (enumerator->enumerate(enumerator, NULL, (void**)&addr)) + { + free(addr); + } + enumerator->destroy(enumerator); + this->addrs->destroy(this->addrs); this->ifaces->destroy_function(this->ifaces, (void*)iface_entry_destroy); - this->mutex->destroy(this->mutex); + this->lock->destroy(this->lock); this->mutex_pfroute->destroy(this->mutex_pfroute); free(this); } @@ -672,6 +790,7 @@ METHOD(kernel_net_t, destroy, void, kernel_pfroute_net_t *kernel_pfroute_net_create() { private_kernel_pfroute_net_t *this; + bool register_for_events = TRUE; INIT(this, .public = { @@ -688,10 +807,18 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() }, }, .ifaces = linked_list_create(), - .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .addrs = hashtable_create( + (hashtable_hash_t)addr_map_entry_hash, + (hashtable_equals_t)addr_map_entry_equals, 16), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), .mutex_pfroute = mutex_create(MUTEX_TYPE_DEFAULT), ); + if (streq(hydra->daemon, "starter")) + { /* starter has no threads, so we do not register for kernel events */ + register_for_events = FALSE; + } + /* create a PF_ROUTE socket to communicate with the kernel */ this->socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); if (this->socket < 0) @@ -701,18 +828,22 @@ kernel_pfroute_net_t *kernel_pfroute_net_create() return NULL; } - /* create a PF_ROUTE socket to receive events */ - this->socket_events = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); - if (this->socket_events < 0) + if (register_for_events) { - DBG1(DBG_KNL, "unable to create PF_ROUTE event socket"); - destroy(this); - return NULL; - } + /* create a PF_ROUTE socket to receive events */ + this->socket_events = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (this->socket_events < 0) + { + DBG1(DBG_KNL, "unable to create PF_ROUTE event socket"); + destroy(this); + return NULL; + } - this->job = callback_job_create_with_prio((callback_job_cb_t)receive_events, - this, NULL, NULL, JOB_PRIO_CRITICAL); - lib->processor->queue_job(lib->processor, (job_t*)this->job); + lib->processor->queue_job(lib->processor, + (job_t*)callback_job_create_with_prio( + (callback_job_cb_t)receive_events, this, NULL, + (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL)); + } if (init_address_list(this) != SUCCESS) { diff --git a/src/libhydra/plugins/resolve/Makefile.in b/src/libhydra/plugins/resolve/Makefile.in index 41846ffe0..aed5ee19d 100644 --- a/src/libhydra/plugins/resolve/Makefile.in +++ b/src/libhydra/plugins/resolve/Makefile.in @@ -49,6 +49,7 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -85,7 +86,7 @@ libstrongswan_resolve_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ @MONOLITHIC_FALSE@am_libstrongswan_resolve_la_rpath = -rpath \ @MONOLITHIC_FALSE@ $(plugindir) @MONOLITHIC_TRUE@am_libstrongswan_resolve_la_rpath = -DEFAULT_INCLUDES = -I.@am__isrc@ +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = $(SHELL) $(top_srcdir)/depcomp am__depfiles_maybe = depfiles am__mv = mv -f @@ -111,6 +112,7 @@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ +BFDLIB = @BFDLIB@ BTLIB = @BTLIB@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ @@ -205,11 +207,14 @@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ c_plugins = @c_plugins@ +charon_natt_port = @charon_natt_port@ +charon_plugins = @charon_plugins@ +charon_udp_port = @charon_udp_port@ clearsilver_LIBS = @clearsilver_LIBS@ datadir = @datadir@ datarootdir = @datarootdir@ dbusservicedir = @dbusservicedir@ -default_pkcs11 = @default_pkcs11@ +dev_headers = @dev_headers@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ @@ -226,11 +231,12 @@ imcvdir = @imcvdir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ +ipsec_script = @ipsec_script@ +ipsec_script_upper = @ipsec_script_upper@ ipsecdir = @ipsecdir@ ipsecgroup = @ipsecgroup@ ipseclibdir = @ipseclibdir@ ipsecuser = @ipsecuser@ -libcharon_plugins = @libcharon_plugins@ libdir = @libdir@ libexecdir = @libexecdir@ linux_headers = @linux_headers@ @@ -246,6 +252,7 @@ mkdir_p = @mkdir_p@ nm_CFLAGS = @nm_CFLAGS@ nm_LIBS = @nm_LIBS@ nm_ca_dir = @nm_ca_dir@ +nm_plugins = @nm_plugins@ oldincludedir = @oldincludedir@ openac_plugins = @openac_plugins@ p_plugins = @p_plugins@ @@ -255,7 +262,6 @@ pdfdir = @pdfdir@ piddir = @piddir@ pki_plugins = @pki_plugins@ plugindir = @plugindir@ -pluto_plugins = @pluto_plugins@ pool_plugins = @pool_plugins@ prefix = @prefix@ program_transform_name = @program_transform_name@ diff --git a/src/libhydra/plugins/resolve/resolve_handler.c b/src/libhydra/plugins/resolve/resolve_handler.c index 011ebbaaf..0a3094fd7 100644 --- a/src/libhydra/plugins/resolve/resolve_handler.c +++ b/src/libhydra/plugins/resolve/resolve_handler.c @@ -150,6 +150,7 @@ static bool invoke_resolvconf(private_resolve_handler_t *this, bool install) { char cmd[128]; + bool success = TRUE; /* we use the nameserver's IP address as part of the interface name to * make them unique */ @@ -171,7 +172,8 @@ static bool invoke_resolvconf(private_resolve_handler_t *this, DBG1(DBG_IKE, "installing DNS server %H via resolvconf", addr); fprintf(out, "nameserver %H # by strongSwan, from %Y\n", addr, server); - if (ferror(out) || pclose(out)) + success = !ferror(out); + if (pclose(out)) { return FALSE; } @@ -180,7 +182,7 @@ static bool invoke_resolvconf(private_resolve_handler_t *this, { ignore_result(system(cmd)); } - return TRUE; + return success; } METHOD(attribute_handler_t, handle, bool, @@ -267,46 +269,71 @@ METHOD(attribute_handler_t, release, void, typedef struct { /** implements enumerator_t interface */ enumerator_t public; - /** virtual IP we are requesting */ - host_t *vip; + /** request IPv4 DNS? */ + bool v4; + /** request IPv6 DNS? */ + bool v6; } attribute_enumerator_t; static bool attribute_enumerate(attribute_enumerator_t *this, configuration_attribute_type_t *type, chunk_t *data) { - switch (this->vip->get_family(this->vip)) + if (this->v4) { - case AF_INET: - *type = INTERNAL_IP4_DNS; - break; - case AF_INET6: - *type = INTERNAL_IP6_DNS; - break; - default: - return FALSE; + *type = INTERNAL_IP4_DNS; + *data = chunk_empty; + this->v4 = FALSE; + return TRUE; + } + if (this->v6) + { + *type = INTERNAL_IP6_DNS; + *data = chunk_empty; + this->v6 = FALSE; + return TRUE; } - *data = chunk_empty; - /* enumerate only once */ - this->public.enumerate = (void*)return_false; - return TRUE; + return FALSE; } -METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, - private_resolve_handler_t *this, identification_t *server, host_t *vip) +/** + * Check if a list has a host of given family + */ +static bool has_host_family(linked_list_t *list, int family) { - if (vip) + enumerator_t *enumerator; + host_t *host; + bool found = FALSE; + + enumerator = list->create_enumerator(list); + while (enumerator->enumerate(enumerator, &host)) { - attribute_enumerator_t *enumerator; + if (host->get_family(host) == family) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); - enumerator = malloc_thing(attribute_enumerator_t); - enumerator->public.enumerate = (void*)attribute_enumerate; - enumerator->public.destroy = (void*)free; - enumerator->vip = vip; + return found; +} - return &enumerator->public; - } - return enumerator_create_empty(); +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_resolve_handler_t *this, identification_t *server, + linked_list_t *vips) +{ + attribute_enumerator_t *enumerator; + + INIT(enumerator, + .public = { + .enumerate = (void*)attribute_enumerate, + .destroy = (void*)free, + }, + .v4 = has_host_family(vips, AF_INET), + .v6 = has_host_family(vips, AF_INET6), + ); + return &enumerator->public; } METHOD(resolve_handler_t, destroy, void, |