diff options
71 files changed, 1977 insertions, 733 deletions
@@ -29,6 +29,20 @@ strongswan-5.0.1 The plugin initially requests the first registered method or the first method configured with charon.plugins.eap-dynamic.preferred. +- The new left/rightdns options specify connection specific DNS servers to + request/respond in IKEv2 configuration payloads or IKEv2 mode config. leftdns + can be any (comma separated) combination of %config4 and %config6 to request + multiple servers, both for IPv4 and IPv6. rightdns takes a list of DNS server + IP addresses to return. + +- The left/rightsourceip options now accept multiple addresses or pools. + leftsourceip can be any (comma separated) combination of %config4, %config6 + or fixed IP addresses to request. rightsourceip accepts multiple explicitly + specified or referenced named pools. + +- Multiple connections can now share a single address pool when they use the + same definition in one of the rightsourceip pools. + - The autotools build has been migrated to use a config.h header. strongSwan development headers will get installed during "make install" if --with-dev-headers has been passed to ./configure. diff --git a/man/ipsec.conf.5.in b/man/ipsec.conf.5.in index 837a2055a..7c336c451 100644 --- a/man/ipsec.conf.5.in +++ b/man/ipsec.conf.5.in @@ -586,6 +586,16 @@ Comma separated list of certificate policy OIDs the peer's certificate must have. OIDs are specified using the numerical dotted representation. .TP +.BR leftdns " = <servers>" +Comma separated list of DNS server addresses to exchange as configuration +attributes. On the initiator, a server is a fixed IPv4 / IPv6 address, or +.B %config4 +/ +.B %config6 +to request attributes without an address. On the responder, +only fixed IPv4 /IPv6 addresses are allowed and define DNS servers assigned +to the client. +.TP .BR leftfirewall " = yes | " no whether the left participant is doing forwarding-firewalling (including masquerading) using iptables for traffic from \fIleftsubnet\fR, @@ -691,19 +701,19 @@ and the latter meaning that the peer must send a certificate request payload in order to get a certificate in return. .TP -.BR leftsourceip " = %config | %cfg | %modeconfig | %modecfg | <ip address>" -The internal source IP to use in a tunnel, also known as virtual IP. If the -value is one of the synonyms +.BR leftsourceip " = %config4 | %config6 | <ip address>" +Comma separated list of internal source IPs to use in a tunnel, also known as +virtual IP. If the value is one of the synonyms .BR %config , .BR %cfg , .BR %modeconfig , or .BR %modecfg , -an address is requested from the peer. +an address (from the tunnel address family) is requested from the peer. .TP .BR rightsourceip " = %config | <network>/<netmask> | %poolname" -The internal source IP to use in a tunnel for the remote peer. If the -value is +Comma separated list of internal source IPs to use in a tunnel for the remote +peer. If the value is .B %config on the responder side, the initiator must propose an address which is then echoed back. Also supported are address pools expressed as diff --git a/src/_updown/_updown.in b/src/_updown/_updown.in index 2c742c010..3a40e2110 100644 --- a/src/_updown/_updown.in +++ b/src/_updown/_updown.in @@ -73,8 +73,12 @@ # just the host, this will be 255.255.255.255. # # PLUTO_MY_SOURCEIP -# if non-empty, then the source address for the route will be -# set to this IP address. +# PLUTO_MY_SOURCEIP4_$i +# PLUTO_MY_SOURCEIP6_$i +# contains IPv4/IPv6 virtual IP received from a responder, +# $i enumerates from 1 to the number of IP per address family. +# PLUTO_MY_SOURCEIP is a legacy variable and equals to the first +# virtual IP, IPv4 or IPv6. # # PLUTO_MY_PROTOCOL # is the IP protocol that will be transported. @@ -128,6 +132,12 @@ # contains the remote UDP port in the case of ESP_IN_UDP # encapsulation # +# PLUTO_DNS4_$i +# PLUTO_DNS6_$i +# contains IPv4/IPv6 DNS server attribute received from a +# responder, $i enumerates from 1 to the number of servers per +# address family. +# # define a minimum PATH environment in case it is not set PATH="/sbin:/bin:/usr/sbin:/usr/bin:@sbindir@" diff --git a/src/charon-nm/nm/nm_handler.c b/src/charon-nm/nm/nm_handler.c index 408129ebe..28aa04b31 100644 --- a/src/charon-nm/nm/nm_handler.c +++ b/src/charon-nm/nm/nm_handler.c @@ -92,15 +92,17 @@ static bool enumerate_dns(enumerator_t *this, } METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, - private_nm_handler_t *this, identification_t *server, host_t *vip) + private_nm_handler_t *this, identification_t *server, linked_list_t *vips) { - if (vip && vip->get_family(vip) == AF_INET) - { /* no IPv6 attributes yet */ - enumerator_t *enumerator = malloc_thing(enumerator_t); - /* enumerate DNS attribute first ... */ - enumerator->enumerate = (void*)enumerate_dns; - enumerator->destroy = (void*)free; + if (vips->get_count(vips)) + { + enumerator_t *enumerator; + INIT(enumerator, + /* enumerate DNS attribute first ... */ + .enumerate = (void*)enumerate_dns, + .destroy = (void*)free, + ); return enumerator; } return enumerator_create_empty(); diff --git a/src/charon-nm/nm/nm_service.c b/src/charon-nm/nm/nm_service.c index ccfa210a0..461837c0c 100644 --- a/src/charon-nm/nm/nm_service.c +++ b/src/charon-nm/nm/nm_service.c @@ -507,8 +507,11 @@ static gboolean connect_(NMVPNPlugin *plugin, NMConnection *connection, 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - virtual ? host_create_from_string("0.0.0.0", 0) : NULL, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + if (virtual) + { + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); + } auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_class); auth->add(auth, AUTH_RULE_IDENTITY, user); diff --git a/src/conftest/config.c b/src/conftest/config.c index a3f23f3a4..cbc6ac05f 100644 --- a/src/conftest/config.c +++ b/src/conftest/config.c @@ -253,7 +253,7 @@ static peer_cfg_t *load_peer_config(private_config_t *this, ike_cfg = load_ike_config(this, settings, config); peer_cfg = peer_cfg_create(config, IKEV2, ike_cfg, CERT_ALWAYS_SEND, UNIQUE_NO, 1, 0, 0, 0, 0, FALSE, FALSE, 0, 0, - NULL, NULL, FALSE, NULL, NULL); + FALSE, NULL, NULL); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY); diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_attr.c b/src/frontends/android/jni/libandroidbridge/backend/android_attr.c index e8c506950..f9e96a76c 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_attr.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_attr.c @@ -80,7 +80,7 @@ METHOD(enumerator_t, enumerate_dns, bool, } METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, - private_android_attr_t *this, identification_t *server, host_t *vip) + private_android_attr_t *this, identification_t *server, linked_list_t *vips) { enumerator_t *enumerator; diff --git a/src/frontends/android/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/jni/libandroidbridge/backend/android_service.c index dfc0d2342..d1769a99a 100644 --- a/src/frontends/android/jni/libandroidbridge/backend/android_service.c +++ b/src/frontends/android/jni/libandroidbridge/backend/android_service.c @@ -235,21 +235,36 @@ static bool setup_tun_device(private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa) { vpnservice_builder_t *builder; + enumerator_t *enumerator; + bool vip_found = FALSE; host_t *vip; int tunfd; DBG1(DBG_DMN, "setting up TUN device for CHILD_SA %s{%u}", child_sa->get_name(child_sa), child_sa->get_reqid(child_sa)); - vip = ike_sa->get_virtual_ip(ike_sa, TRUE); - if (!vip || vip->is_anyaddr(vip)) + + builder = charonservice->get_vpnservice_builder(charonservice); + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &vip)) + { + if (!vip->is_anyaddr(vip)) + { + if (!builder->add_address(builder, vip)) + { + break; + } + vip_found = TRUE; + } + } + enumerator->destroy(enumerator); + + if (!vip_found) { DBG1(DBG_DMN, "setting up TUN device failed, no virtual IP found"); return FALSE; } - - builder = charonservice->get_vpnservice_builder(charonservice); - if (!builder->add_address(builder, vip) || - !add_routes(builder, child_sa) || + if (!add_routes(builder, child_sa) || !builder->set_mtu(builder, TUN_DEFAULT_MTU)) { return FALSE; @@ -427,9 +442,8 @@ static job_requeue_t initiate(private_android_service_t *this) 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - host_create_from_string("0.0.0.0", 0) /* virt */, - NULL, FALSE, NULL, NULL); /* pool, mediation */ - + FALSE, NULL, NULL); /* mediation */ + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); diff --git a/src/libcharon/config/peer_cfg.c b/src/libcharon/config/peer_cfg.c index d16aedc79..59869eab2 100644 --- a/src/libcharon/config/peer_cfg.c +++ b/src/libcharon/config/peer_cfg.c @@ -141,14 +141,14 @@ struct private_peer_cfg_t { u_int32_t dpd_timeout; /** - * virtual IP to use locally + * List of virtual IPs (host_t*) to request */ - host_t *virtual_ip; + linked_list_t *vips; /** - * pool to acquire configuration attributes from + * List of pool names to use for virtual IP lookup */ - char *pool; + linked_list_t *pools; /** * local authentication configs (rulesets) @@ -409,16 +409,28 @@ METHOD(peer_cfg_t, get_dpd_timeout, u_int32_t, return this->dpd_timeout; } -METHOD(peer_cfg_t, get_virtual_ip, host_t*, +METHOD(peer_cfg_t, add_virtual_ip, void, + private_peer_cfg_t *this, host_t *vip) +{ + this->vips->insert_last(this->vips, vip); +} + +METHOD(peer_cfg_t, create_virtual_ip_enumerator, enumerator_t*, private_peer_cfg_t *this) { - return this->virtual_ip; + return this->vips->create_enumerator(this->vips); } -METHOD(peer_cfg_t, get_pool, char*, +METHOD(peer_cfg_t, add_pool, void, + private_peer_cfg_t *this, char *name) +{ + this->pools->insert_last(this->pools, strdup(name)); +} + +METHOD(peer_cfg_t, create_pool_enumerator, enumerator_t*, private_peer_cfg_t *this) { - return this->pool; + return this->pools->create_enumerator(this->pools); } METHOD(peer_cfg_t, add_auth_cfg, void, @@ -521,6 +533,10 @@ static bool auth_cfg_equal(private_peer_cfg_t *this, private_peer_cfg_t *other) METHOD(peer_cfg_t, equals, bool, private_peer_cfg_t *this, private_peer_cfg_t *other) { + enumerator_t *e1, *e2; + host_t *vip1, *vip2; + char *pool1, *pool2; + if (this == other) { return TRUE; @@ -530,6 +546,43 @@ METHOD(peer_cfg_t, equals, bool, return FALSE; } + if (this->vips->get_count(this->vips) != other->vips->get_count(other->vips)) + { + return FALSE; + } + e1 = create_virtual_ip_enumerator(this); + e2 = create_virtual_ip_enumerator(other); + if (e1->enumerate(e1, &vip1) && e2->enumerate(e2, &vip2)) + { + if (!vip1->ip_equals(vip1, vip2)) + { + e1->destroy(e1); + e2->destroy(e2); + return FALSE; + } + } + e1->destroy(e1); + e2->destroy(e2); + + if (this->pools->get_count(this->pools) != + other->pools->get_count(other->pools)) + { + return FALSE; + } + e1 = create_pool_enumerator(this); + e2 = create_pool_enumerator(other); + if (e1->enumerate(e1, &pool1) && e2->enumerate(e2, &pool2)) + { + if (!streq(pool1, pool2)) + { + e1->destroy(e1); + e2->destroy(e2); + return FALSE; + } + } + e1->destroy(e1); + e2->destroy(e2); + return ( this->ike_version == other->ike_version && this->cert_policy == other->cert_policy && @@ -541,11 +594,6 @@ METHOD(peer_cfg_t, equals, bool, this->jitter_time == other->jitter_time && this->over_time == other->over_time && this->dpd == other->dpd && - (this->virtual_ip == other->virtual_ip || - (this->virtual_ip && other->virtual_ip && - this->virtual_ip->equals(this->virtual_ip, other->virtual_ip))) && - (this->pool == other->pool || - (this->pool && other->pool && streq(this->pool, other->pool))) && auth_cfg_equal(this, other) #ifdef ME && this->mediation == other->mediation && @@ -572,18 +620,18 @@ METHOD(peer_cfg_t, destroy, void, this->ike_cfg->destroy(this->ike_cfg); this->child_cfgs->destroy_offset(this->child_cfgs, offsetof(child_cfg_t, destroy)); - DESTROY_IF(this->virtual_ip); this->local_auth->destroy_offset(this->local_auth, offsetof(auth_cfg_t, destroy)); this->remote_auth->destroy_offset(this->remote_auth, offsetof(auth_cfg_t, destroy)); + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); + this->pools->destroy_function(this->pools, free); #ifdef ME DESTROY_IF(this->mediated_by); DESTROY_IF(this->peer_id); #endif /* ME */ this->mutex->destroy(this->mutex); free(this->name); - free(this->pool); free(this); } } @@ -597,8 +645,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, u_int32_t rekey_time, u_int32_t reauth_time, u_int32_t jitter_time, u_int32_t over_time, bool mobike, bool aggressive, u_int32_t dpd, - u_int32_t dpd_timeout, host_t *virtual_ip, - char *pool, bool mediation, peer_cfg_t *mediated_by, + u_int32_t dpd_timeout, + bool mediation, peer_cfg_t *mediated_by, identification_t *peer_id) { private_peer_cfg_t *this; @@ -631,8 +679,10 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, .use_aggressive = _use_aggressive, .get_dpd = _get_dpd, .get_dpd_timeout = _get_dpd_timeout, - .get_virtual_ip = _get_virtual_ip, - .get_pool = _get_pool, + .add_virtual_ip = _add_virtual_ip, + .create_virtual_ip_enumerator = _create_virtual_ip_enumerator, + .add_pool = _add_pool, + .create_pool_enumerator = _create_pool_enumerator, .add_auth_cfg = _add_auth_cfg, .create_auth_cfg_enumerator = _create_auth_cfg_enumerator, .equals = (void*)_equals, @@ -660,8 +710,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, .aggressive = aggressive, .dpd = dpd, .dpd_timeout = dpd_timeout, - .virtual_ip = virtual_ip, - .pool = strdupnull(pool), + .vips = linked_list_create(), + .pools = linked_list_create(), .local_auth = linked_list_create(), .remote_auth = linked_list_create(), .refcount = 1, diff --git a/src/libcharon/config/peer_cfg.h b/src/libcharon/config/peer_cfg.h index 572153505..f65b91258 100644 --- a/src/libcharon/config/peer_cfg.h +++ b/src/libcharon/config/peer_cfg.h @@ -278,23 +278,34 @@ struct peer_cfg_t { u_int32_t (*get_dpd_timeout) (peer_cfg_t *this); /** - * Get a virtual IP for the local peer. + * Add a virtual IP to request as initiator. * - * If no virtual IP should be used, NULL is returned. %any means to request - * a virtual IP using configuration payloads. A specific address is also - * used for a request and may be changed by the server. + * @param vip virtual IP to request, may be %any or %any6 + */ + void (*add_virtual_ip)(peer_cfg_t *this, host_t *vip); + + /** + * Create an enumerator over virtual IPs to request. + * + * The returned enumerator enumerates over IPs added with add_virtual_ip(). + * + * @return enumerator over host_t* + */ + enumerator_t* (*create_virtual_ip_enumerator)(peer_cfg_t *this); + + /** + * Add a pool name this configuration uses to select virtual IPs. * - * @param suggestion NULL, %any or specific - * @return virtual IP, %any or NULL + * @param name pool name to use for virtual IP lookup */ - host_t* (*get_virtual_ip) (peer_cfg_t *this); + void (*add_pool)(peer_cfg_t *this, char *name); /** - * Get the name of the pool to acquire configuration attributes from. + * Create an enumerator over pool names of this config. * - * @return pool name, NULL if none defined + * @return enumerator over char* */ - char* (*get_pool)(peer_cfg_t *this); + enumerator_t* (*create_pool_enumerator)(peer_cfg_t *this); #ifdef ME /** @@ -374,8 +385,6 @@ struct peer_cfg_t { * @param aggressive use/accept aggressive mode with IKEv1 * @param dpd DPD check interval, 0 to disable * @param dpd_timeout DPD timeout interval (IKEv1 only), if 0 default applies - * @param virtual_ip virtual IP for local host, or NULL - * @param pool pool name to get configuration attributes from, or NULL * @param mediation TRUE if this is a mediation connection * @param mediated_by peer_cfg_t of the mediation connection to mediate through * @param peer_id ID that identifies our peer at the mediation server @@ -387,8 +396,8 @@ peer_cfg_t *peer_cfg_create(char *name, ike_version_t ike_version, u_int32_t rekey_time, u_int32_t reauth_time, u_int32_t jitter_time, u_int32_t over_time, bool mobike, bool aggressive, u_int32_t dpd, - u_int32_t dpd_timeout, host_t *virtual_ip, - char *pool, bool mediation, peer_cfg_t *mediated_by, + u_int32_t dpd_timeout, + bool mediation, peer_cfg_t *mediated_by, identification_t *peer_id); #endif /** PEER_CFG_H_ @}*/ diff --git a/src/libcharon/plugins/android/android_handler.c b/src/libcharon/plugins/android/android_handler.c index a53962f16..f1d3045ca 100644 --- a/src/libcharon/plugins/android/android_handler.c +++ b/src/libcharon/plugins/android/android_handler.c @@ -196,7 +196,7 @@ METHOD(enumerator_t, enumerate_dns, bool, } METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t *, - android_handler_t *this, identification_t *id, host_t *vip) + android_handler_t *this, identification_t *id, linked_list_t *vips) { enumerator_t *enumerator; diff --git a/src/libcharon/plugins/android/android_service.c b/src/libcharon/plugins/android/android_service.c index f2d8ddce9..81628b80a 100644 --- a/src/libcharon/plugins/android/android_service.c +++ b/src/libcharon/plugins/android/android_service.c @@ -275,8 +275,8 @@ static job_requeue_t initiate(private_android_service_t *this) 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - host_create_from_string("0.0.0.0", 0) /* virt */, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); diff --git a/src/libcharon/plugins/dhcp/dhcp_provider.c b/src/libcharon/plugins/dhcp/dhcp_provider.c index a6a887780..e46cc4d90 100644 --- a/src/libcharon/plugins/dhcp/dhcp_provider.c +++ b/src/libcharon/plugins/dhcp/dhcp_provider.c @@ -84,7 +84,7 @@ METHOD(attribute_provider_t, acquire_address, host_t*, private_dhcp_provider_t *this, char *pool, identification_t *id, host_t *requested) { - if (streq(pool, "dhcp")) + if (streq(pool, "dhcp") && requested->get_family(requested) == AF_INET) { dhcp_transaction_t *transaction, *old; host_t *vip; @@ -110,7 +110,7 @@ METHOD(attribute_provider_t, release_address, bool, private_dhcp_provider_t *this, char *pool, host_t *address, identification_t *id) { - if (streq(pool, "dhcp")) + if (streq(pool, "dhcp") && address->get_family(address) == AF_INET) { dhcp_transaction_t *transaction; @@ -129,18 +129,25 @@ METHOD(attribute_provider_t, release_address, bool, } METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, - private_dhcp_provider_t *this, char *pool, identification_t *id, - host_t *vip) + private_dhcp_provider_t *this, linked_list_t *pools, identification_t *id, + linked_list_t *vips) { - dhcp_transaction_t *transaction; + dhcp_transaction_t *transaction = NULL; + enumerator_t *enumerator; + host_t *vip; - if (!vip) + this->mutex->lock(this->mutex); + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &vip)) { - return NULL; + transaction = this->transactions->get(this->transactions, + (void*)hash_id_host(id, vip)); + if (transaction) + { + break; + } } - this->mutex->lock(this->mutex); - transaction = this->transactions->get(this->transactions, - (void*)hash_id_host(id, vip)); + enumerator->destroy(enumerator); if (!transaction) { this->mutex->unlock(this->mutex); diff --git a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c index 243c76304..7690cf9f8 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_accounting.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_accounting.c @@ -149,6 +149,7 @@ static bool send_message(private_eap_radius_accounting_t *this, */ static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) { + enumerator_t *enumerator; host_t *vip; char buf[64]; chunk_t data; @@ -157,17 +158,25 @@ static void add_ike_sa_parameters(radius_message_t *message, ike_sa_t *ike_sa) message->add(message, RAT_USER_NAME, chunk_create(buf, strlen(buf))); snprintf(buf, sizeof(buf), "%#H", ike_sa->get_other_host(ike_sa)); message->add(message, RAT_CALLING_STATION_ID, chunk_create(buf, strlen(buf))); - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (vip && vip->get_family(vip) == AF_INET) - { - message->add(message, RAT_FRAMED_IP_ADDRESS, vip->get_address(vip)); - } - if (vip && vip->get_family(vip) == AF_INET6) + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (enumerator->enumerate(enumerator, &vip)) { - /* we currently assign /128 prefixes, only (reserved, length) */ - data = chunk_from_chars(0, 128); - data = chunk_cata("cc", data, vip->get_address(vip)); - message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + switch (vip->get_family(vip)) + { + case AF_INET: + message->add(message, RAT_FRAMED_IP_ADDRESS, + vip->get_address(vip)); + break; + case AF_INET6: + /* we currently assign /128 prefixes, only (reserved, length) */ + data = chunk_from_chars(0, 128); + data = chunk_cata("cc", data, vip->get_address(vip)); + message->add(message, RAT_FRAMED_IPV6_PREFIX, data); + break; + default: + break; + } } } diff --git a/src/libcharon/plugins/ha/ha_attribute.c b/src/libcharon/plugins/ha/ha_attribute.c index 64bd2b8b7..f18c58b6a 100644 --- a/src/libcharon/plugins/ha/ha_attribute.c +++ b/src/libcharon/plugins/ha/ha_attribute.c @@ -181,6 +181,12 @@ METHOD(attribute_provider_t, acquire_address, host_t*, pool = get_pool(this, name); if (pool) { + if (pool->base->get_family(pool->base) != + requested->get_family(requested)) + { + this->mutex->unlock(this->mutex); + return NULL; + } for (byte = 0; byte < pool->size / 8; byte++) { if (pool->mask[byte] != 0xFF) diff --git a/src/libcharon/plugins/ha/ha_dispatcher.c b/src/libcharon/plugins/ha/ha_dispatcher.c index 8b6ec9180..f07b4ea21 100644 --- a/src/libcharon/plugins/ha/ha_dispatcher.c +++ b/src/libcharon/plugins/ha/ha_dispatcher.c @@ -344,10 +344,10 @@ static void process_ike_update(private_ha_dispatcher_t *this, ike_sa->set_other_host(ike_sa, value.host->clone(value.host)); break; case HA_LOCAL_VIP: - ike_sa->set_virtual_ip(ike_sa, TRUE, value.host); + ike_sa->add_virtual_ip(ike_sa, TRUE, value.host); break; case HA_REMOTE_VIP: - ike_sa->set_virtual_ip(ike_sa, FALSE, value.host); + ike_sa->add_virtual_ip(ike_sa, FALSE, value.host); received_vip = TRUE; break; case HA_PEER_ADDR: @@ -413,18 +413,24 @@ static void process_ike_update(private_ha_dispatcher_t *this, } if (received_vip) { + enumerator_t *pools, *vips; host_t *vip; char *pool; peer_cfg = ike_sa->get_peer_cfg(ike_sa); - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (peer_cfg && vip) + if (peer_cfg) { - pool = peer_cfg->get_pool(peer_cfg); - if (pool) + pools = peer_cfg->create_pool_enumerator(peer_cfg); + while (pools->enumerate(pools, &pool)) { - this->attr->reserve(this->attr, pool, vip); + vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (vips->enumerate(vips, &vip)) + { + this->attr->reserve(this->attr, pool, vip); + } + vips->destroy(vips); } + pools->destroy(pools); } } if (ike_sa->get_version(ike_sa) == IKEV1) diff --git a/src/libcharon/plugins/ha/ha_ike.c b/src/libcharon/plugins/ha/ha_ike.c index d523e8919..442a3a23d 100644 --- a/src/libcharon/plugins/ha/ha_ike.c +++ b/src/libcharon/plugins/ha/ha_ike.c @@ -241,6 +241,34 @@ METHOD(listener_t, ike_state_change, bool, return TRUE; } +/** + * Send a virtual IP sync message for remote VIPs + */ +static void sync_vips(private_ha_ike_t *this, ike_sa_t *ike_sa) +{ + ha_message_t *m = NULL; + enumerator_t *enumerator; + host_t *vip; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (enumerator->enumerate(enumerator, &vip)) + { + if (!m) + { + m = ha_message_create(HA_IKE_UPDATE); + m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); + } + m->add_attribute(m, HA_REMOTE_VIP, vip); + } + enumerator->destroy(enumerator); + + if (m) + { + this->socket->push(this->socket, m); + this->cache->cache(this->cache, ike_sa, m); + } +} + METHOD(listener_t, message_hook, bool, private_ha_ike_t *this, ike_sa_t *ike_sa, message_t *message, bool incoming, bool plain) @@ -276,18 +304,7 @@ METHOD(listener_t, message_hook, bool, { /* After IKE_SA has been established, sync peers virtual IP. * We cannot sync it in the state_change hook, it is installed later. * TODO: where to sync local VIP? */ - ha_message_t *m; - host_t *vip; - - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (vip) - { - m = ha_message_create(HA_IKE_UPDATE); - m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); - m->add_attribute(m, HA_REMOTE_VIP, vip); - this->socket->push(this->socket, m); - this->cache->cache(this->cache, ike_sa, m); - } + sync_vips(this, ike_sa); } } if (!plain && ike_sa->get_version(ike_sa) == IKEV1) @@ -296,7 +313,6 @@ METHOD(listener_t, message_hook, bool, keymat_v1_t *keymat; u_int32_t mid; chunk_t iv; - host_t *vip; mid = message->get_message_id(message); if (mid == 0) @@ -313,15 +329,7 @@ METHOD(listener_t, message_hook, bool, } if (!incoming && message->get_exchange_type(message) == TRANSACTION) { - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (vip) - { - m = ha_message_create(HA_IKE_UPDATE); - m->add_attribute(m, HA_IKE_ID, ike_sa->get_id(ike_sa)); - m->add_attribute(m, HA_REMOTE_VIP, vip); - this->socket->push(this->socket, m); - this->cache->cache(this->cache, ike_sa, m); - } + sync_vips(this, ike_sa); } } if (plain && ike_sa->get_version(ike_sa) == IKEV1 && diff --git a/src/libcharon/plugins/ha/ha_tunnel.c b/src/libcharon/plugins/ha/ha_tunnel.c index ad458caad..541dd9313 100644 --- a/src/libcharon/plugins/ha/ha_tunnel.c +++ b/src/libcharon/plugins/ha/ha_tunnel.c @@ -209,7 +209,7 @@ static void setup_tunnel(private_ha_tunnel_t *this, ike_cfg->add_proposal(ike_cfg, proposal_create_default(PROTO_IKE)); peer_cfg = peer_cfg_create("ha", IKEV2, ike_cfg, CERT_NEVER_SEND, UNIQUE_KEEP, 0, 86400, 0, 7200, 3600, FALSE, FALSE, 30, - 0, NULL, NULL, FALSE, NULL, NULL); + 0, FALSE, NULL, NULL); auth_cfg = auth_cfg_create(); auth_cfg->add(auth_cfg, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); diff --git a/src/libcharon/plugins/load_tester/load_tester_config.c b/src/libcharon/plugins/load_tester/load_tester_config.c index f5da8b892..735f17985 100644 --- a/src/libcharon/plugins/load_tester/load_tester_config.c +++ b/src/libcharon/plugins/load_tester/load_tester_config.c @@ -268,8 +268,15 @@ static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num) FALSE, FALSE, /* mobike, aggressive mode */ this->dpd_delay, /* dpd_delay */ this->dpd_timeout, /* dpd_timeout */ - this->vip ? this->vip->clone(this->vip) : NULL, - this->pool, FALSE, NULL, NULL); + FALSE, NULL, NULL); + if (this->vip) + { + peer_cfg->add_virtual_ip(peer_cfg, this->vip->clone(this->vip)); + } + if (this->pool) + { + peer_cfg->add_pool(peer_cfg, this->pool); + } if (num) { /* initiator */ generate_auth_cfg(this, this->initiator_auth, peer_cfg, TRUE, num); diff --git a/src/libcharon/plugins/maemo/maemo_service.c b/src/libcharon/plugins/maemo/maemo_service.c index f638a81b7..cb2fc9ebb 100644 --- a/src/libcharon/plugins/maemo/maemo_service.c +++ b/src/libcharon/plugins/maemo/maemo_service.c @@ -335,8 +335,8 @@ static gboolean initiate_connection(private_maemo_service_t *this, 600, 600, /* jitter, over 10min */ TRUE, FALSE, /* mobike, aggressive */ 0, 0, /* DPD delay, timeout */ - host_create_from_string("0.0.0.0", 0) /* virt */, - NULL, FALSE, NULL, NULL); /* pool, mediation */ + FALSE, NULL, NULL); /* mediation */ + peer_cfg->add_virtual_ip(peer_cfg, host_create_from_string("0.0.0.0", 0)); auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_EAP); diff --git a/src/libcharon/plugins/medcli/medcli_config.c b/src/libcharon/plugins/medcli/medcli_config.c index 829c8f05e..a1825effc 100644 --- a/src/libcharon/plugins/medcli/medcli_config.c +++ b/src/libcharon/plugins/medcli/medcli_config.c @@ -129,7 +129,6 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ TRUE, NULL, NULL); /* mediation, med by, peer id */ e->destroy(e); @@ -167,7 +166,6 @@ METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ FALSE, med_cfg, /* mediation, med by */ identification_create_from_encoding(ID_KEY_ID, other)); @@ -243,7 +241,6 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ FALSE, NULL, NULL); /* mediation, med by, peer id */ auth = auth_cfg_create(); diff --git a/src/libcharon/plugins/medsrv/medsrv_config.c b/src/libcharon/plugins/medsrv/medsrv_config.c index ebc142de2..ff33c53e1 100644 --- a/src/libcharon/plugins/medsrv/medsrv_config.c +++ b/src/libcharon/plugins/medsrv/medsrv_config.c @@ -94,7 +94,6 @@ METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, this->rekey*5, this->rekey*3, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressiv */ this->dpd, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ TRUE, NULL, NULL); /* mediation, med by, peer id */ e->destroy(e); diff --git a/src/libcharon/plugins/sql/sql_config.c b/src/libcharon/plugins/sql/sql_config.c index 890077157..c614c679e 100644 --- a/src/libcharon/plugins/sql/sql_config.c +++ b/src/libcharon/plugins/sql/sql_config.c @@ -333,6 +333,7 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, mediation, mediated_by, p_type; chunk_t l_data, r_data, p_data; char *name, *virtual, *pool; + enumerator_t *enumerator; while (e->enumerate(e, &id, &name, &ike_cfg, &l_type, &l_data, &r_type, &r_data, @@ -371,8 +372,23 @@ static peer_cfg_t *build_peer_cfg(private_sql_config_t *this, enumerator_t *e, peer_cfg = peer_cfg_create( name, IKEV2, ike, cert_policy, uniqueid, keyingtries, rekeytime, reauthtime, jitter, overtime, - mobike, FALSE, dpd_delay, 0, vip, pool, + mobike, FALSE, dpd_delay, 0, mediation, mediated_cfg, peer_id); + if (vip) + { + peer_cfg->add_virtual_ip(peer_cfg, vip); + } + if (pool) + { + /* attr-sql used comma separated pools, but we now completely + * support multiple pools directly. Support old SQL configs: */ + enumerator = enumerator_create_token(pool, ",", " "); + while (enumerator->enumerate(enumerator, &pool)) + { + peer_cfg->add_pool(peer_cfg, pool); + } + enumerator->destroy(enumerator); + } auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, auth_method); auth->add(auth, AUTH_RULE_IDENTITY, local_id); diff --git a/src/libcharon/plugins/stroke/Makefile.am b/src/libcharon/plugins/stroke/Makefile.am index e561224e9..cebcd984f 100644 --- a/src/libcharon/plugins/stroke/Makefile.am +++ b/src/libcharon/plugins/stroke/Makefile.am @@ -21,6 +21,7 @@ libstrongswan_stroke_la_SOURCES = \ stroke_cred.h stroke_cred.c \ stroke_ca.h stroke_ca.c \ stroke_attribute.h stroke_attribute.c \ + stroke_handler.h stroke_handler.c \ stroke_list.h stroke_list.c libstrongswan_stroke_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/stroke/stroke_attribute.c b/src/libcharon/plugins/stroke/stroke_attribute.c index 1e4615e12..3e012230a 100644 --- a/src/libcharon/plugins/stroke/stroke_attribute.c +++ b/src/libcharon/plugins/stroke/stroke_attribute.c @@ -17,7 +17,6 @@ #include "stroke_attribute.h" #include <daemon.h> -#include <attributes/mem_pool.h> #include <utils/linked_list.h> #include <threading/rwlock.h> @@ -39,12 +38,37 @@ struct private_stroke_attribute_t { linked_list_t *pools; /** + * List of connection specific attributes, as attributes_t + */ + linked_list_t *attrs; + + /** * rwlock to lock access to pools */ rwlock_t *lock; }; /** + * Attributes assigned to a connection + */ +typedef struct { + /** name of the connection */ + char *name; + /** list of DNS attributes, as host_t */ + linked_list_t *dns; +} attributes_t; + +/** + * Destroy an attributes_t entry + */ +static void attributes_destroy(attributes_t *this) +{ + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this->name); + free(this); +} + +/** * find a pool by name */ static mem_pool_t *find_pool(private_stroke_attribute_t *this, char *name) @@ -66,11 +90,12 @@ static mem_pool_t *find_pool(private_stroke_attribute_t *this, char *name) } METHOD(attribute_provider_t, acquire_address, host_t*, - private_stroke_attribute_t *this, char *name, identification_t *id, - host_t *requested) + private_stroke_attribute_t *this, char *name, identification_t *id, + host_t *requested) { mem_pool_t *pool; host_t *addr = NULL; + this->lock->read_lock(this->lock); pool = find_pool(this, name); if (pool) @@ -82,11 +107,12 @@ METHOD(attribute_provider_t, acquire_address, host_t*, } METHOD(attribute_provider_t, release_address, bool, - private_stroke_attribute_t *this, char *name, host_t *address, - identification_t *id) + private_stroke_attribute_t *this, char *name, host_t *address, + identification_t *id) { mem_pool_t *pool; bool found = FALSE; + this->lock->read_lock(this->lock); pool = find_pool(this, name); if (pool) @@ -97,56 +123,162 @@ METHOD(attribute_provider_t, release_address, bool, return found; } -METHOD(stroke_attribute_t, add_pool, void, - private_stroke_attribute_t *this, stroke_msg_t *msg) +/** + * Filter function to convert host to DNS configuration attributes + */ +static bool attr_filter(void *lock, host_t **in, + configuration_attribute_type_t *type, + void *dummy, chunk_t *data) { - if (msg->add_conn.other.sourceip_mask) + host_t *host = *in; + + switch (host->get_family(host)) { - mem_pool_t *pool; - host_t *base = NULL; - u_int32_t bits = 0; + case AF_INET: + *type = INTERNAL_IP4_DNS; + break; + case AF_INET6: + *type = INTERNAL_IP6_DNS; + break; + default: + return FALSE; + } + *data = host->get_address(host); + return TRUE; +} - /* if %config, add an empty pool, otherwise */ - if (msg->add_conn.other.sourceip) +METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*, + private_stroke_attribute_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) +{ + ike_sa_t *ike_sa; + peer_cfg_t *peer_cfg; + enumerator_t *enumerator; + attributes_t *attr; + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + this->lock->read_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) { - DBG1(DBG_CFG, "adding virtual IP address pool '%s': %s/%d", - msg->add_conn.name, msg->add_conn.other.sourceip, - msg->add_conn.other.sourceip_mask); - base = host_create_from_string(msg->add_conn.other.sourceip, 0); - if (!base) + if (streq(attr->name, peer_cfg->get_name(peer_cfg))) { - DBG1(DBG_CFG, "virtual IP address invalid, discarded"); - return; + enumerator->destroy(enumerator); + return enumerator_create_filter( + attr->dns->create_enumerator(attr->dns), + (void*)attr_filter, this->lock, + (void*)this->lock->unlock); } - bits = msg->add_conn.other.sourceip_mask; } - pool = mem_pool_create(msg->add_conn.name, base, bits); - DESTROY_IF(base); - - this->lock->write_lock(this->lock); - this->pools->insert_last(this->pools, pool); + enumerator->destroy(enumerator); this->lock->unlock(this->lock); } + return enumerator_create_empty(); } -METHOD(stroke_attribute_t, del_pool, void, - private_stroke_attribute_t *this, stroke_msg_t *msg) +METHOD(stroke_attribute_t, add_pool, void, + private_stroke_attribute_t *this, mem_pool_t *pool) { enumerator_t *enumerator; - mem_pool_t *pool; + mem_pool_t *current; + host_t *base; + int size; + + base = pool->get_base(pool); + size = pool->get_size(pool); this->lock->write_lock(this->lock); + enumerator = this->pools->create_enumerator(this->pools); - while (enumerator->enumerate(enumerator, &pool)) + while (enumerator->enumerate(enumerator, ¤t)) { - if (streq(msg->del_conn.name, pool->get_name(pool))) + if (base && current->get_base(current) && + base->ip_equals(base, current->get_base(current)) && + size == current->get_size(current)) { - this->pools->remove_at(this->pools, enumerator); pool->destroy(pool); + pool = NULL; + DBG1(DBG_CFG, "reusing virtual IP address pool %H/%d", base, size); + break; + } + } + enumerator->destroy(enumerator); + + if (pool) + { + if (base) + { + DBG1(DBG_CFG, "adding virtual IP address pool %H/%d", base, size); + } + this->pools->insert_last(this->pools, pool); + } + + this->lock->unlock(this->lock); +} + +METHOD(stroke_attribute_t, add_dns, void, + private_stroke_attribute_t *this, stroke_msg_t *msg) +{ + if (msg->add_conn.other.dns) + { + enumerator_t *enumerator; + attributes_t *attr = NULL; + host_t *host; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.other.dns, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + host = host_create_from_string(token, 0); + if (host) + { + if (!attr) + { + INIT(attr, + .name = strdup(msg->add_conn.name), + .dns = linked_list_create(), + ); + } + attr->dns->insert_last(attr->dns, host); + } + else + { + DBG1(DBG_CFG, "ignoring invalid DNS address '%s'", token); + } + } + enumerator->destroy(enumerator); + if (attr) + { + this->lock->write_lock(this->lock); + this->attrs->insert_last(this->attrs, attr); + this->lock->unlock(this->lock); + } + } +} + +METHOD(stroke_attribute_t, del_dns, void, + private_stroke_attribute_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator; + attributes_t *attr; + + this->lock->write_lock(this->lock); + + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (streq(msg->del_conn.name, attr->name)) + { + this->attrs->remove_at(this->attrs, enumerator); + attributes_destroy(attr); break; } } enumerator->destroy(enumerator); + this->lock->unlock(this->lock); } @@ -158,6 +290,11 @@ static bool pool_filter(void *lock, mem_pool_t **poolp, const char **name, void *d3, u_int *offline) { mem_pool_t *pool = *poolp; + + if (pool->get_size(pool) == 0) + { + return FALSE; + } *name = pool->get_name(pool); *size = pool->get_size(pool); *online = pool->get_online(pool); @@ -166,7 +303,7 @@ static bool pool_filter(void *lock, mem_pool_t **poolp, const char **name, } METHOD(stroke_attribute_t, create_pool_enumerator, enumerator_t*, - private_stroke_attribute_t *this) + private_stroke_attribute_t *this) { this->lock->read_lock(this->lock); return enumerator_create_filter(this->pools->create_enumerator(this->pools), @@ -175,7 +312,7 @@ METHOD(stroke_attribute_t, create_pool_enumerator, enumerator_t*, } METHOD(stroke_attribute_t, create_lease_enumerator, enumerator_t*, - private_stroke_attribute_t *this, char *name) + private_stroke_attribute_t *this, char *name) { mem_pool_t *pool; this->lock->read_lock(this->lock); @@ -190,10 +327,11 @@ METHOD(stroke_attribute_t, create_lease_enumerator, enumerator_t*, } METHOD(stroke_attribute_t, destroy, void, - private_stroke_attribute_t *this) + private_stroke_attribute_t *this) { this->lock->destroy(this->lock); this->pools->destroy_offset(this->pools, offsetof(mem_pool_t, destroy)); + this->attrs->destroy_function(this->attrs, (void*)attributes_destroy); free(this); } @@ -209,15 +347,17 @@ stroke_attribute_t *stroke_attribute_create() .provider = { .acquire_address = _acquire_address, .release_address = _release_address, - .create_attribute_enumerator = enumerator_create_empty, + .create_attribute_enumerator = _create_attribute_enumerator, }, .add_pool = _add_pool, - .del_pool = _del_pool, + .add_dns = _add_dns, + .del_dns = _del_dns, .create_pool_enumerator = _create_pool_enumerator, .create_lease_enumerator = _create_lease_enumerator, .destroy = _destroy, }, .pools = linked_list_create(), + .attrs = linked_list_create(), .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), ); diff --git a/src/libcharon/plugins/stroke/stroke_attribute.h b/src/libcharon/plugins/stroke/stroke_attribute.h index 249a9899b..f1b9d135b 100644 --- a/src/libcharon/plugins/stroke/stroke_attribute.h +++ b/src/libcharon/plugins/stroke/stroke_attribute.h @@ -23,6 +23,7 @@ #include <stroke_msg.h> #include <attributes/attribute_provider.h> +#include <attributes/mem_pool.h> typedef struct stroke_attribute_t stroke_attribute_t; @@ -37,18 +38,28 @@ struct stroke_attribute_t { attribute_provider_t provider; /** - * Add a virtual IP address pool. + * Add a memory pool to this virtual IP backend. * - * @param msg stroke message + * The pool gets owned by the provider, or destroyed if such a pool + * is already registered. + * + * @param pool virtual IP pool to add + */ + void (*add_pool)(stroke_attribute_t *this, mem_pool_t *pool); + + /** + * Add connection specific DNS servers. + * + * @param msg stroke add message */ - void (*add_pool)(stroke_attribute_t *this, stroke_msg_t *msg); + void (*add_dns)(stroke_attribute_t *this, stroke_msg_t *msg); /** - * Remove a virtual IP address pool. + * Remove connection specific DNS servers. * - * @param msg stroke message + * @param msg stroke del message */ - void (*del_pool)(stroke_attribute_t *this, stroke_msg_t *msg); + void (*del_dns)(stroke_attribute_t *this, stroke_msg_t *msg); /** * Create an enumerator over installed pools. diff --git a/src/libcharon/plugins/stroke/stroke_config.c b/src/libcharon/plugins/stroke/stroke_config.c index 9dcb9ba0c..e3c78f750 100644 --- a/src/libcharon/plugins/stroke/stroke_config.c +++ b/src/libcharon/plugins/stroke/stroke_config.c @@ -52,6 +52,11 @@ struct private_stroke_config_t { * credentials */ stroke_cred_t *cred; + + /** + * Virtual IP pool / DNS backend + */ + stroke_attribute_t *attributes; }; METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*, @@ -618,7 +623,6 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, { identification_t *peer_id = NULL; peer_cfg_t *mediated_by = NULL; - host_t *vip = NULL; unique_policy_t unique; u_int32_t rekey = 0, reauth = 0, over, jitter; peer_cfg_t *peer_cfg; @@ -677,49 +681,6 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, { rekey = msg->add_conn.rekey.ike_lifetime - over; } - if (msg->add_conn.me.sourceip_mask) - { - if (msg->add_conn.me.sourceip) - { - vip = host_create_from_string(msg->add_conn.me.sourceip, 0); - } - if (!vip) - { /* if it is set to something like %poolname, request an address */ - if (msg->add_conn.me.subnets) - { /* use the same family as in local subnet, if any */ - if (strchr(msg->add_conn.me.subnets, '.')) - { - vip = host_create_any(AF_INET); - } - else - { - vip = host_create_any(AF_INET6); - } - } - else if (msg->add_conn.other.subnets) - { /* use the same family as in remote subnet, if any */ - if (strchr(msg->add_conn.other.subnets, '.')) - { - vip = host_create_any(AF_INET); - } - else - { - vip = host_create_any(AF_INET6); - } - } - else - { - if (strchr(ike_cfg->get_my_addr(ike_cfg, NULL), ':')) - { - vip = host_create_any(AF_INET6); - } - else - { - vip = host_create_any(AF_INET); - } - } - } - } switch (msg->add_conn.unique) { case 1: /* yes */ @@ -747,10 +708,126 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this, msg->add_conn.rekey.tries, rekey, reauth, jitter, over, msg->add_conn.mobike, msg->add_conn.aggressive, msg->add_conn.dpd.delay, msg->add_conn.dpd.timeout, - vip, msg->add_conn.other.sourceip_mask ? - msg->add_conn.name : msg->add_conn.other.sourceip, msg->add_conn.ikeme.mediation, mediated_by, peer_id); + if (msg->add_conn.other.sourceip) + { + enumerator_t *enumerator; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.other.sourceip, + ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (streq(token, "%modeconfig") || streq(token, "%modecfg") || + streq(token, "%config") || streq(token, "%cfg") || + streq(token, "%config4") || streq(token, "%config6")) + { + /* empty pool, uses connection name */ + this->attributes->add_pool(this->attributes, + mem_pool_create(msg->add_conn.name, NULL, 0)); + peer_cfg->add_pool(peer_cfg, msg->add_conn.name); + } + else if (*token == '%') + { + /* external named pool */ + peer_cfg->add_pool(peer_cfg, token + 1); + } + else + { + /* in-memory pool, named using CIDR notation */ + host_t *base; + int bits; + + base = host_create_from_subnet(token, &bits); + if (base) + { + this->attributes->add_pool(this->attributes, + mem_pool_create(token, base, bits)); + peer_cfg->add_pool(peer_cfg, token); + base->destroy(base); + } + else + { + DBG1(DBG_CFG, "IP pool %s invalid, ignored", token); + } + } + } + enumerator->destroy(enumerator); + } + + if (msg->add_conn.me.sourceip) + { + enumerator_t *enumerator; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.me.sourceip, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + host_t *vip = NULL; + + if (streq(token, "%modeconfig") || streq(token, "%modecfg") || + streq(token, "%config") || streq(token, "%cfg")) + { /* try to deduce an address family */ + if (msg->add_conn.me.subnets) + { /* use the same family as in local subnet, if any */ + if (strchr(msg->add_conn.me.subnets, '.')) + { + vip = host_create_any(AF_INET); + } + else + { + vip = host_create_any(AF_INET6); + } + } + else if (msg->add_conn.other.subnets) + { /* use the same family as in remote subnet, if any */ + if (strchr(msg->add_conn.other.subnets, '.')) + { + vip = host_create_any(AF_INET); + } + else + { + vip = host_create_any(AF_INET6); + } + } + else + { + if (strchr(ike_cfg->get_my_addr(ike_cfg, NULL), ':')) + { + vip = host_create_any(AF_INET6); + } + else + { + vip = host_create_any(AF_INET); + } + } + } + else if (streq(token, "%config4")) + { + vip = host_create_any(AF_INET); + } + else if (streq(token, "%config6")) + { + vip = host_create_any(AF_INET6); + } + else + { + vip = host_create_from_string(token, 0); + if (vip) + { + DBG1(DBG_CFG, "ignored invalid subnet token: %s", token); + } + } + + if (vip) + { + peer_cfg->add_virtual_ip(peer_cfg, vip); + } + } + enumerator->destroy(enumerator); + } + /* build leftauth= */ auth_cfg = build_auth_cfg(this, msg, TRUE, TRUE); if (auth_cfg) @@ -1209,7 +1286,8 @@ METHOD(stroke_config_t, destroy, void, /* * see header file */ -stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred) +stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred, + stroke_attribute_t *attributes) { private_stroke_config_t *this; @@ -1229,6 +1307,7 @@ stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred) .mutex = mutex_create(MUTEX_TYPE_RECURSIVE), .ca = ca, .cred = cred, + .attributes = attributes, ); return &this->public; diff --git a/src/libcharon/plugins/stroke/stroke_config.h b/src/libcharon/plugins/stroke/stroke_config.h index 450d517f3..894e03ce4 100644 --- a/src/libcharon/plugins/stroke/stroke_config.h +++ b/src/libcharon/plugins/stroke/stroke_config.h @@ -26,6 +26,7 @@ #include <stroke_msg.h> #include "stroke_ca.h" #include "stroke_cred.h" +#include "stroke_attribute.h" typedef struct stroke_config_t stroke_config_t; @@ -71,6 +72,7 @@ struct stroke_config_t { /** * Create a stroke_config instance. */ -stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred); +stroke_config_t *stroke_config_create(stroke_ca_t *ca, stroke_cred_t *cred, + stroke_attribute_t *attributes); #endif /** STROKE_CONFIG_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_control.c b/src/libcharon/plugins/stroke/stroke_control.c index 163a2a750..233d4088f 100644 --- a/src/libcharon/plugins/stroke/stroke_control.c +++ b/src/libcharon/plugins/stroke/stroke_control.c @@ -407,10 +407,10 @@ METHOD(stroke_control_t, rekey, void, METHOD(stroke_control_t, terminate_srcip, void, private_stroke_control_t *this, stroke_msg_t *msg, FILE *out) { - enumerator_t *enumerator; + enumerator_t *enumerator, *vips; ike_sa_t *ike_sa; host_t *start = NULL, *end = NULL, *vip; - chunk_t chunk_start, chunk_end = chunk_empty, chunk_vip; + chunk_t chunk_start, chunk_end = chunk_empty, chunk; if (msg->terminate_srcip.start) { @@ -438,33 +438,40 @@ METHOD(stroke_control_t, terminate_srcip, void, charon->controller, TRUE); while (enumerator->enumerate(enumerator, &ike_sa)) { - vip = ike_sa->get_virtual_ip(ike_sa, FALSE); - if (!vip) - { - continue; - } - if (!end) + bool match = FALSE; + + vips = ike_sa->create_virtual_ip_enumerator(ike_sa, FALSE); + while (vips->enumerate(vips, &vip)) { - if (!vip->ip_equals(vip, start)) + if (!end) { - continue; + if (vip->ip_equals(vip, start)) + { + match = TRUE; + break; + } } - } - else - { - chunk_vip = vip->get_address(vip); - if (chunk_vip.len != chunk_start.len || - chunk_vip.len != chunk_end.len || - memcmp(chunk_vip.ptr, chunk_start.ptr, chunk_vip.len) < 0 || - memcmp(chunk_vip.ptr, chunk_end.ptr, chunk_vip.len) > 0) + else { - continue; + chunk = vip->get_address(vip); + if (chunk.len == chunk_start.len && + chunk.len == chunk_end.len && + memcmp(chunk.ptr, chunk_start.ptr, chunk.len) >= 0 && + memcmp(chunk.ptr, chunk_end.ptr, chunk.len) <= 0) + { + match = TRUE; + break; + } } } + vips->destroy(vips); - /* schedule delete asynchronously */ - lib->processor->queue_job(lib->processor, (job_t*) + if (match) + { + /* schedule delete asynchronously */ + lib->processor->queue_job(lib->processor, (job_t*) delete_ike_sa_job_create(ike_sa->get_id(ike_sa), TRUE)); + } } enumerator->destroy(enumerator); start->destroy(start); diff --git a/src/libcharon/plugins/stroke/stroke_handler.c b/src/libcharon/plugins/stroke/stroke_handler.c new file mode 100644 index 000000000..523151efb --- /dev/null +++ b/src/libcharon/plugins/stroke/stroke_handler.c @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "stroke_handler.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <threading/rwlock.h> + +typedef struct private_stroke_handler_t private_stroke_handler_t; + +/** + * Private data of an stroke_handler_t object. + */ +struct private_stroke_handler_t { + + /** + * Public stroke_handler_t interface. + */ + stroke_handler_t public; + + /** + * List of connection specific attributes, as attributes_t + */ + linked_list_t *attrs; + + /** + * rwlock to lock access to pools + */ + rwlock_t *lock; +}; + +/** + * Attributes assigned to a connection + */ +typedef struct { + /** name of the connection */ + char *name; + /** list of DNS attributes, as host_t */ + linked_list_t *dns; +} attributes_t; + +/** + * Destroy an attributes_t entry + */ +static void attributes_destroy(attributes_t *this) +{ + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this->name); + free(this); +} + +/** + * Filter function to convert host to DNS configuration attributes + */ +static bool attr_filter(void *lock, host_t **in, + configuration_attribute_type_t *type, + void *dummy, chunk_t *data) +{ + host_t *host = *in; + + switch (host->get_family(host)) + { + case AF_INET: + *type = INTERNAL_IP4_DNS; + break; + case AF_INET6: + *type = INTERNAL_IP6_DNS; + break; + default: + return FALSE; + } + if (host->is_anyaddr(host)) + { + *data = chunk_empty; + } + else + { + *data = host->get_address(host); + } + return TRUE; +} + +METHOD(attribute_handler_t, create_attribute_enumerator, enumerator_t*, + private_stroke_handler_t *this, identification_t *server, + linked_list_t *vips) +{ + ike_sa_t *ike_sa; + peer_cfg_t *peer_cfg; + enumerator_t *enumerator; + attributes_t *attr; + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + peer_cfg = ike_sa->get_peer_cfg(ike_sa); + this->lock->read_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (streq(attr->name, peer_cfg->get_name(peer_cfg))) + { + enumerator->destroy(enumerator); + return enumerator_create_filter( + attr->dns->create_enumerator(attr->dns), + (void*)attr_filter, this->lock, + (void*)this->lock->unlock); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + } + return enumerator_create_empty(); +} + +METHOD(stroke_handler_t, add_attributes, void, + private_stroke_handler_t *this, stroke_msg_t *msg) +{ + if (msg->add_conn.me.dns) + { + enumerator_t *enumerator; + attributes_t *attr = NULL; + host_t *host; + char *token; + + enumerator = enumerator_create_token(msg->add_conn.me.dns, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (streq(token, "%config") || streq(token, "%config4")) + { + host = host_create_any(AF_INET); + } + else if (streq(token, "%config6")) + { + host = host_create_any(AF_INET6); + } + else + { + host = host_create_from_string(token, 0); + } + if (host) + { + if (!attr) + { + INIT(attr, + .name = strdup(msg->add_conn.name), + .dns = linked_list_create(), + ); + } + attr->dns->insert_last(attr->dns, host); + } + else + { + DBG1(DBG_CFG, "ignoring invalid DNS address '%s'", token); + } + } + enumerator->destroy(enumerator); + if (attr) + { + this->lock->write_lock(this->lock); + this->attrs->insert_last(this->attrs, attr); + this->lock->unlock(this->lock); + } + } +} + +METHOD(stroke_handler_t, del_attributes, void, + private_stroke_handler_t *this, stroke_msg_t *msg) +{ + enumerator_t *enumerator; + attributes_t *attr; + + this->lock->write_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (streq(msg->del_conn.name, attr->name)) + { + this->attrs->remove_at(this->attrs, enumerator); + attributes_destroy(attr); + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); +} + +METHOD(stroke_handler_t, destroy, void, + private_stroke_handler_t *this) +{ + this->lock->destroy(this->lock); + this->attrs->destroy_function(this->attrs, (void*)attributes_destroy); + free(this); +} + +/** + * See header + */ +stroke_handler_t *stroke_handler_create() +{ + private_stroke_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = (void*)return_false, + .release = (void*)return_false, + .create_attribute_enumerator = _create_attribute_enumerator, + }, + .add_attributes = _add_attributes, + .del_attributes = _del_attributes, + .destroy = _destroy, + }, + .attrs = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/stroke/stroke_handler.h b/src/libcharon/plugins/stroke/stroke_handler.h new file mode 100644 index 000000000..ab76f80b0 --- /dev/null +++ b/src/libcharon/plugins/stroke/stroke_handler.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup stroke_handler stroke_handler + * @{ @ingroup stroke + */ + +#ifndef STROKE_HANDLER_H_ +#define STROKE_HANDLER_H_ + +#include <stroke_msg.h> +#include <attributes/attribute_handler.h> + +typedef struct stroke_handler_t stroke_handler_t; + +/** + * Handler requesting DNS attributes as defined with leftdns option. + */ +struct stroke_handler_t { + + /** + * Implements the attribute_handler_t interface + */ + attribute_handler_t handler; + + /** + * Add connection specific configuration attributes. + * + * @param msg stroke message + */ + void (*add_attributes)(stroke_handler_t *this, stroke_msg_t *msg); + + /** + * Remove connection specific configuration attributes. + * + * @param msg stroke message + */ + void (*del_attributes)(stroke_handler_t *this, stroke_msg_t *msg); + + /** + * Destroy a stroke_handler_t. + */ + void (*destroy)(stroke_handler_t *this); +}; + +/** + * Create a stroke_handler instance. + */ +stroke_handler_t *stroke_handler_create(); + +#endif /** STROKE_HANDLER_H_ @}*/ diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 6bf65cc7e..241f0fbf6 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -37,6 +37,7 @@ #include "stroke_cred.h" #include "stroke_ca.h" #include "stroke_attribute.h" +#include "stroke_handler.h" #include "stroke_list.h" /** @@ -99,6 +100,11 @@ struct private_stroke_socket_t { stroke_attribute_t *attribute; /** + * attribute handler (requests only) + */ + stroke_handler_t *handler; + + /** * controller to control daemon */ stroke_control_t *control; @@ -171,6 +177,7 @@ static void pop_end(stroke_msg_t *msg, const char* label, stroke_end_t *end) pop_string(msg, &end->address); pop_string(msg, &end->subnets); pop_string(msg, &end->sourceip); + pop_string(msg, &end->dns); pop_string(msg, &end->auth); pop_string(msg, &end->auth2); pop_string(msg, &end->id); @@ -188,6 +195,7 @@ static void pop_end(stroke_msg_t *msg, const char* label, stroke_end_t *end) DBG2(DBG_CFG, " %s=%s", label, end->address); DBG2(DBG_CFG, " %ssubnet=%s", label, end->subnets); DBG2(DBG_CFG, " %ssourceip=%s", label, end->sourceip); + DBG2(DBG_CFG, " %sdns=%s", label, end->dns); DBG2(DBG_CFG, " %sauth=%s", label, end->auth); DBG2(DBG_CFG, " %sauth2=%s", label, end->auth2); DBG2(DBG_CFG, " %sid=%s", label, end->id); @@ -235,7 +243,8 @@ static void stroke_add_conn(private_stroke_socket_t *this, stroke_msg_t *msg) DBG2(DBG_CFG, " keyexchange=ikev%u", msg->add_conn.version); this->config->add(this->config, msg); - this->attribute->add_pool(this->attribute, msg); + this->attribute->add_dns(this->attribute, msg); + this->handler->add_attributes(this->handler, msg); } /** @@ -247,7 +256,8 @@ static void stroke_del_conn(private_stroke_socket_t *this, stroke_msg_t *msg) DBG1(DBG_CFG, "received stroke: delete connection '%s'", msg->del_conn.name); this->config->del(this->config, msg); - this->attribute->del_pool(this->attribute, msg); + this->attribute->del_dns(this->attribute, msg); + this->handler->del_attributes(this->handler, msg); } /** @@ -787,10 +797,12 @@ METHOD(stroke_socket_t, destroy, void, lib->credmgr->remove_set(lib->credmgr, &this->cred->set); charon->backends->remove_backend(charon->backends, &this->config->backend); hydra->attributes->remove_provider(hydra->attributes, &this->attribute->provider); + hydra->attributes->remove_handler(hydra->attributes, &this->handler->handler); this->cred->destroy(this->cred); this->ca->destroy(this->ca); this->config->destroy(this->config); this->attribute->destroy(this->attribute); + this->handler->destroy(this->handler); this->control->destroy(this->control); this->list->destroy(this->list); free(this); @@ -817,8 +829,9 @@ stroke_socket_t *stroke_socket_create() this->cred = stroke_cred_create(); this->attribute = stroke_attribute_create(); + this->handler = stroke_handler_create(); this->ca = stroke_ca_create(this->cred); - this->config = stroke_config_create(this->ca, this->cred); + this->config = stroke_config_create(this->ca, this->cred, this->attribute); this->control = stroke_control_create(); this->list = stroke_list_create(this->attribute); @@ -833,6 +846,7 @@ stroke_socket_t *stroke_socket_create() lib->credmgr->add_set(lib->credmgr, &this->cred->set); charon->backends->add_backend(charon->backends, &this->config->backend); hydra->attributes->add_provider(hydra->attributes, &this->attribute->provider); + hydra->attributes->add_handler(hydra->attributes, &this->handler->handler); lib->processor->queue_job(lib->processor, (job_t*)callback_job_create_with_prio((callback_job_cb_t)receive, this, diff --git a/src/libcharon/plugins/uci/uci_config.c b/src/libcharon/plugins/uci/uci_config.c index 5b698b8b2..1201f568e 100644 --- a/src/libcharon/plugins/uci/uci_config.c +++ b/src/libcharon/plugins/uci/uci_config.c @@ -178,7 +178,6 @@ METHOD(enumerator_t, peer_enumerator_enumerate, bool, 1800, 900, /* jitter, overtime */ TRUE, FALSE, /* mobike, aggressive */ 60, 0, /* DPD delay, timeout */ - NULL, NULL, /* vip, pool */ FALSE, NULL, NULL); /* mediation, med by, peer id */ auth = auth_cfg_create(); auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PSK); diff --git a/src/libcharon/plugins/updown/Makefile.am b/src/libcharon/plugins/updown/Makefile.am index 312c8d7e8..30683d83e 100644 --- a/src/libcharon/plugins/updown/Makefile.am +++ b/src/libcharon/plugins/updown/Makefile.am @@ -12,6 +12,7 @@ endif libstrongswan_updown_la_SOURCES = \ updown_plugin.h updown_plugin.c \ + updown_handler.h updown_handler.c \ updown_listener.h updown_listener.c libstrongswan_updown_la_LDFLAGS = -module -avoid-version diff --git a/src/libcharon/plugins/updown/updown_handler.c b/src/libcharon/plugins/updown/updown_handler.c new file mode 100644 index 000000000..b2ac02e85 --- /dev/null +++ b/src/libcharon/plugins/updown/updown_handler.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "updown_handler.h" + +#include <daemon.h> +#include <utils/linked_list.h> +#include <threading/rwlock.h> + +typedef struct private_updown_handler_t private_updown_handler_t; + +/** + * Private data of an updown_handler_t object. + */ +struct private_updown_handler_t { + + /** + * Public updown_handler_t interface. + */ + updown_handler_t public; + + /** + * List of connection specific attributes, as attributes_t + */ + linked_list_t *attrs; + + /** + * rwlock to lock access to pools + */ + rwlock_t *lock; +}; + +/** + * Attributes assigned to an IKE_SA + */ +typedef struct { + /** unique IKE_SA identifier */ + u_int id; + /** list of DNS attributes, as host_t */ + linked_list_t *dns; +} attributes_t; + +/** + * Destroy an attributes_t entry + */ +static void attributes_destroy(attributes_t *this) +{ + this->dns->destroy_offset(this->dns, offsetof(host_t, destroy)); + free(this); +} + +METHOD(attribute_handler_t, handle, bool, + private_updown_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + attributes_t *current, *attr = NULL; + enumerator_t *enumerator; + ike_sa_t *ike_sa; + host_t *host; + + ike_sa = charon->bus->get_sa(charon->bus); + if (!ike_sa) + { + return FALSE; + } + switch (type) + { + case INTERNAL_IP4_DNS: + host = host_create_from_chunk(AF_INET, data, 0); + break; + case INTERNAL_IP6_DNS: + host = host_create_from_chunk(AF_INET6, data, 0); + break; + default: + return FALSE; + } + if (!host) + { + return FALSE; + } + + this->lock->write_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, ¤t)) + { + if (current->id == ike_sa->get_unique_id(ike_sa)) + { + attr = current; + } + } + enumerator->destroy(enumerator); + + if (!attr) + { + INIT(attr, + .id = ike_sa->get_unique_id(ike_sa), + .dns = linked_list_create(), + ); + this->attrs->insert_last(this->attrs, attr); + } + attr->dns->insert_last(attr->dns, host); + this->lock->unlock(this->lock); + + return TRUE; +} + +METHOD(attribute_handler_t, release, void, + private_updown_handler_t *this, identification_t *server, + configuration_attribute_type_t type, chunk_t data) +{ + attributes_t *attr; + enumerator_t *enumerator, *servers; + ike_sa_t *ike_sa; + host_t *host; + bool found = FALSE; + int family; + + switch (type) + { + case INTERNAL_IP4_DNS: + family = AF_INET; + break; + case INTERNAL_IP6_DNS: + family = AF_INET6; + break; + default: + return; + } + + ike_sa = charon->bus->get_sa(charon->bus); + if (ike_sa) + { + this->lock->write_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->id == ike_sa->get_unique_id(ike_sa)) + { + servers = attr->dns->create_enumerator(attr->dns); + while (servers->enumerate(servers, &host)) + { + if (host->get_family(host) == family && + chunk_equals(data, host->get_address(host))) + { + attr->dns->remove_at(attr->dns, servers); + host->destroy(host); + found = TRUE; + break; + } + } + servers->destroy(servers); + if (attr->dns->get_count(attr->dns) == 0) + { + this->attrs->remove_at(this->attrs, enumerator); + attributes_destroy(attr); + break; + } + } + if (found) + { + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + } +} + +METHOD(updown_handler_t, create_dns_enumerator, enumerator_t*, + private_updown_handler_t *this, u_int id) +{ + attributes_t *attr; + enumerator_t *enumerator; + ike_sa_t *ike_sa; + + ike_sa = charon->bus->get_sa(charon->bus); + if (!ike_sa) + { + return FALSE; + } + + this->lock->read_lock(this->lock); + enumerator = this->attrs->create_enumerator(this->attrs); + while (enumerator->enumerate(enumerator, &attr)) + { + if (attr->id == ike_sa->get_unique_id(ike_sa)) + { + enumerator->destroy(enumerator); + return enumerator_create_cleaner( + attr->dns->create_enumerator(attr->dns), + (void*)this->lock->unlock, this->lock); + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + + return enumerator_create_empty(); +} + + +METHOD(updown_handler_t, destroy, void, + private_updown_handler_t *this) +{ + this->lock->destroy(this->lock); + this->attrs->destroy_function(this->attrs, (void*)attributes_destroy); + free(this); +} + +/** + * See header + */ +updown_handler_t *updown_handler_create() +{ + private_updown_handler_t *this; + + INIT(this, + .public = { + .handler = { + .handle = _handle, + .release = _release, + .create_attribute_enumerator = enumerator_create_empty, + }, + .create_dns_enumerator = _create_dns_enumerator, + .destroy = _destroy, + }, + .attrs = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/plugins/updown/updown_handler.h b/src/libcharon/plugins/updown/updown_handler.h new file mode 100644 index 000000000..d4de880b8 --- /dev/null +++ b/src/libcharon/plugins/updown/updown_handler.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 Martin Willi + * Copyright (C) 2012 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup updown_handler updown_handler + * @{ @ingroup updown + */ + +#ifndef UPDOWN_HANDLER_H_ +#define UPDOWN_HANDLER_H_ + +#include <attributes/attribute_handler.h> + +typedef struct updown_handler_t updown_handler_t; + +/** + * Handler storing configuration attributes to pass to updown script. + */ +struct updown_handler_t { + + /** + * Implements the attribute_handler_t interface + */ + attribute_handler_t handler; + + /** + * Create an enumerator over received DNS servers. + * + * @param id unique IKE_SA identifier to get attributes for + * @return enumerator over host_t* + */ + enumerator_t* (*create_dns_enumerator)(updown_handler_t *this, u_int id); + + /** + * Destroy a updown_handler_t. + */ + void (*destroy)(updown_handler_t *this); +}; + +/** + * Create a updown_handler instance. + */ +updown_handler_t *updown_handler_create(); + +#endif /** UPDOWN_HANDLER_H_ @}*/ diff --git a/src/libcharon/plugins/updown/updown_listener.c b/src/libcharon/plugins/updown/updown_listener.c index 2bd757ec7..2f9f2ef91 100644 --- a/src/libcharon/plugins/updown/updown_listener.c +++ b/src/libcharon/plugins/updown/updown_listener.c @@ -38,6 +38,11 @@ struct private_updown_listener_t { * List of cached interface names */ linked_list_t *iface_cache; + + /** + * DNS attribute handler + */ + updown_handler_t *handler; }; typedef struct cache_entry_t cache_entry_t; @@ -90,6 +95,85 @@ static char* uncache_iface(private_updown_listener_t *this, u_int32_t reqid) return iface; } +/** + * Create variables for handled DNS attributes + */ +static char *make_dns_vars(private_updown_listener_t *this, ike_sa_t *ike_sa) +{ + enumerator_t *enumerator; + host_t *host; + int v4 = 0, v6 = 0; + char total[512] = "", current[64]; + + if (!this->handler) + { + return strdup(""); + } + + enumerator = this->handler->create_dns_enumerator(this->handler, + ike_sa->get_unique_id(ike_sa)); + while (enumerator->enumerate(enumerator, &host)) + { + switch (host->get_family(host)) + { + case AF_INET: + snprintf(current, sizeof(current), + "PLUTO_DNS4_%d='%H' ", ++v4, host); + break; + case AF_INET6: + snprintf(current, sizeof(current), + "PLUTO_DNS6_%d='%H' ", ++v6, host); + break; + default: + continue; + } + strncat(total, current, sizeof(total) - strlen(total) - 1); + } + enumerator->destroy(enumerator); + + return strdup(total); +} + +/** + * Create variables for local virtual IPs + */ +static char *make_vip_vars(private_updown_listener_t *this, ike_sa_t *ike_sa) +{ + enumerator_t *enumerator; + host_t *host; + int v4 = 0, v6 = 0; + char total[512] = "", current[64]; + bool first = TRUE; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + if (first) + { /* legacy variable for first VIP */ + snprintf(current, sizeof(current), + "PLUTO_MY_SOURCEIP='%H' ", host); + strncat(total, current, sizeof(total) - strlen(total) - 1); + } + switch (host->get_family(host)) + { + case AF_INET: + snprintf(current, sizeof(current), + "PLUTO_MY_SOURCEIP4_%d='%H' ", ++v4, host); + break; + case AF_INET6: + snprintf(current, sizeof(current), + "PLUTO_MY_SOURCEIP6_%d='%H' ", ++v6, host); + break; + default: + continue; + } + strncat(total, current, sizeof(total) - strlen(total) - 1); + } + enumerator->destroy(enumerator); + + return strdup(total); +} + METHOD(listener_t, child_updown, bool, private_updown_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, bool up) @@ -97,11 +181,10 @@ METHOD(listener_t, child_updown, bool, traffic_selector_t *my_ts, *other_ts; enumerator_t *enumerator; child_cfg_t *config; - host_t *vip, *me, *other; + host_t *me, *other; char *script; config = child_sa->get_config(child_sa); - vip = ike_sa->get_virtual_ip(ike_sa, TRUE); script = config->get_updown(config); me = ike_sa->get_my_host(ike_sa); other = ike_sa->get_other_host(ike_sa); @@ -117,7 +200,7 @@ METHOD(listener_t, child_updown, bool, char command[1024]; host_t *my_client, *other_client; u_int8_t my_client_mask, other_client_mask; - char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc; + char *virtual_ip, *iface, *mark_in, *mark_out, *udp_enc, *dns; mark_t mark; bool is_host, is_ipv6; FILE *shell; @@ -125,20 +208,7 @@ METHOD(listener_t, child_updown, bool, my_ts->to_subnet(my_ts, &my_client, &my_client_mask); other_ts->to_subnet(other_ts, &other_client, &other_client_mask); - if (vip) - { - if (asprintf(&virtual_ip, "PLUTO_MY_SOURCEIP='%H' ", vip) < 0) - { - virtual_ip = NULL; - } - } - else - { - if (asprintf(&virtual_ip, "") < 0) - { - virtual_ip = NULL; - } - } + virtual_ip = make_vip_vars(this, ike_sa); /* check for the presence of an inbound mark */ mark = config->get_mark(config, TRUE); @@ -209,6 +279,8 @@ METHOD(listener_t, child_updown, bool, iface = uncache_iface(this, child_sa->get_reqid(child_sa)); } + dns = make_dns_vars(this, ike_sa); + /* determine IPv4/IPv6 and client/host situation */ is_host = my_ts->is_host(my_ts, me); is_ipv6 = is_host ? (me->get_family(me) == AF_INET6) : @@ -239,6 +311,7 @@ METHOD(listener_t, child_updown, bool, "%s" "%s" "%s" + "%s" "%s", up ? "up" : "down", is_host ? "-host" : "-client", @@ -259,6 +332,7 @@ METHOD(listener_t, child_updown, bool, mark_out, udp_enc, config->get_hostaccess(config) ? "PLUTO_HOST_ACCESS='1' " : "", + dns, script); my_client->destroy(my_client); other_client->destroy(other_client); @@ -266,6 +340,7 @@ METHOD(listener_t, child_updown, bool, free(mark_in); free(mark_out); free(udp_enc); + free(dns); free(iface); DBG3(DBG_CHD, "running updown script: %s", command); @@ -315,7 +390,7 @@ METHOD(updown_listener_t, destroy, void, /** * See header */ -updown_listener_t *updown_listener_create() +updown_listener_t *updown_listener_create(updown_handler_t *handler) { private_updown_listener_t *this; @@ -327,6 +402,7 @@ updown_listener_t *updown_listener_create() .destroy = _destroy, }, .iface_cache = linked_list_create(), + .handler = handler, ); return &this->public; diff --git a/src/libcharon/plugins/updown/updown_listener.h b/src/libcharon/plugins/updown/updown_listener.h index 5b866c4e5..2d9b56ade 100644 --- a/src/libcharon/plugins/updown/updown_listener.h +++ b/src/libcharon/plugins/updown/updown_listener.h @@ -23,6 +23,8 @@ #include <bus/bus.h> +#include "updown_handler.h" + typedef struct updown_listener_t updown_listener_t; /** @@ -44,6 +46,6 @@ struct updown_listener_t { /** * Create a updown_listener instance. */ -updown_listener_t *updown_listener_create(); +updown_listener_t *updown_listener_create(updown_handler_t *handler); #endif /** UPDOWN_LISTENER_H_ @}*/ diff --git a/src/libcharon/plugins/updown/updown_plugin.c b/src/libcharon/plugins/updown/updown_plugin.c index 2ce2d3257..e1f0d1380 100644 --- a/src/libcharon/plugins/updown/updown_plugin.c +++ b/src/libcharon/plugins/updown/updown_plugin.c @@ -15,8 +15,10 @@ #include "updown_plugin.h" #include "updown_listener.h" +#include "updown_handler.h" #include <daemon.h> +#include <hydra.h> typedef struct private_updown_plugin_t private_updown_plugin_t; @@ -34,6 +36,11 @@ struct private_updown_plugin_t { * Listener interface, listens to CHILD_SA state changes */ updown_listener_t *listener; + + /** + * Attribute handler, to pass DNS servers to updown + */ + updown_handler_t *handler; }; METHOD(plugin_t, get_name, char*, @@ -47,6 +54,12 @@ METHOD(plugin_t, destroy, void, { charon->bus->remove_listener(charon->bus, &this->listener->listener); this->listener->destroy(this->listener); + if (this->handler) + { + hydra->attributes->remove_handler(hydra->attributes, + &this->handler->handler); + this->handler->destroy(this->handler); + } free(this); } @@ -65,9 +78,16 @@ plugin_t *updown_plugin_create() .destroy = _destroy, }, }, - .listener = updown_listener_create(), ); + if (lib->settings->get_bool(lib->settings, + "charon.plugins.updown.dns_handler", FALSE)) + { + this->handler = updown_handler_create(); + hydra->attributes->add_handler(hydra->attributes, + &this->handler->handler); + } + this->listener = updown_listener_create(this->handler); charon->bus->add_listener(charon->bus, &this->listener->listener); return &this->public.plugin; diff --git a/src/libcharon/processing/jobs/migrate_job.c b/src/libcharon/processing/jobs/migrate_job.c index 57f344980..2ebfc6714 100644 --- a/src/libcharon/processing/jobs/migrate_job.c +++ b/src/libcharon/processing/jobs/migrate_job.c @@ -79,9 +79,10 @@ METHOD(job_t, execute, job_requeue_t, } if (ike_sa) { - enumerator_t *children; + enumerator_t *children, *enumerator; child_sa_t *child_sa; host_t *host; + linked_list_t *vips; children = ike_sa->create_child_sa_enumerator(ike_sa); while (children->enumerate(children, (void**)&child_sa)) @@ -104,14 +105,22 @@ METHOD(job_t, execute, job_requeue_t, host->set_port(host, IKEV2_UDP_PORT); ike_sa->set_other_host(ike_sa, host); - if (child_sa->update(child_sa, this->local, this->remote, - ike_sa->get_virtual_ip(ike_sa, TRUE), + vips = linked_list_create(); + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); + + if (child_sa->update(child_sa, this->local, this->remote, vips, ike_sa->has_condition(ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED) { ike_sa->rekey_child_sa(ike_sa, child_sa->get_protocol(child_sa), child_sa->get_spi(child_sa, TRUE)); } charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + vips->destroy(vips); } else { diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index d5adb2527..c3fbd8b53 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -819,9 +819,18 @@ METHOD(child_sa_t, add_policies, status_t, return status; } +/** + * Callback to reinstall a virtual IP + */ +static void reinstall_vip(host_t *vip, host_t *me) +{ + hydra->kernel_interface->del_ip(hydra->kernel_interface, vip); + hydra->kernel_interface->add_ip(hydra->kernel_interface, vip, me); +} + METHOD(child_sa_t, update, status_t, - private_child_sa_t *this, host_t *me, host_t *other, host_t *vip, - bool encap) + private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips, + bool encap) { child_sa_state_t old; bool transport_proxy_mode; @@ -927,13 +936,7 @@ METHOD(child_sa_t, update, status_t, /* we reinstall the virtual IP to handle interface roaming * correctly */ - if (vip) - { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - vip); - hydra->kernel_interface->add_ip(hydra->kernel_interface, - vip, me); - } + vips->invoke_function(vips, (void*)reinstall_vip, me); /* reinstall updated policies */ install_policies_internal(this, me, other, my_ts, other_ts, diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index 712a4765c..dae3f2c18 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -346,12 +346,12 @@ struct child_sa_t { * * @param me the new local host * @param other the new remote host - * @param vip virtual IP, if any + * @param vips list of local virtual IPs * @param TRUE to use UDP encapsulation for NAT traversal * @return SUCCESS or FAILED */ status_t (*update)(child_sa_t *this, host_t *me, host_t *other, - host_t *vip, bool encap); + linked_list_t *vips, bool encap); /** * Destroys a child_sa. */ diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 1e3d00f02..7672accc4 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -182,14 +182,14 @@ struct private_ike_sa_t { keymat_t *keymat; /** - * Virtual IP on local host, if any + * Virtual IPs on local host */ - host_t *my_virtual_ip; + linked_list_t *my_vips; /** - * Virtual IP on remote host, if any + * Virtual IPs on remote host */ - host_t *other_virtual_ip; + linked_list_t *other_vips; /** * List of configuration attributes (attribute_entry_t) @@ -735,7 +735,7 @@ METHOD(ike_sa_t, get_keymat, keymat_t*, return this->keymat; } -METHOD(ike_sa_t, set_virtual_ip, void, +METHOD(ike_sa_t, add_virtual_ip, void, private_ike_sa_t *this, bool local, host_t *ip) { if (local) @@ -744,39 +744,27 @@ METHOD(ike_sa_t, set_virtual_ip, void, if (hydra->kernel_interface->add_ip(hydra->kernel_interface, ip, this->my_host) == SUCCESS) { - if (this->my_virtual_ip) - { - DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip); - hydra->kernel_interface->del_ip(hydra->kernel_interface, - this->my_virtual_ip); - } - DESTROY_IF(this->my_virtual_ip); - this->my_virtual_ip = ip->clone(ip); + this->my_vips->insert_last(this->my_vips, ip->clone(ip)); } else { DBG1(DBG_IKE, "installing virtual IP %H failed", ip); - this->my_virtual_ip = NULL; } } else { - DESTROY_IF(this->other_virtual_ip); - this->other_virtual_ip = ip->clone(ip); + this->other_vips->insert_last(this->other_vips, ip->clone(ip)); } } -METHOD(ike_sa_t, get_virtual_ip, host_t*, +METHOD(ike_sa_t, create_virtual_ip_enumerator, enumerator_t*, private_ike_sa_t *this, bool local) { if (local) { - return this->my_virtual_ip; - } - else - { - return this->other_virtual_ip; + return this->my_vips->create_enumerator(this->my_vips); } + return this->other_vips->create_enumerator(this->other_vips); } METHOD(ike_sa_t, add_peer_address, void, @@ -909,7 +897,7 @@ METHOD(ike_sa_t, update_hosts, void, while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (child_sa->update(child_sa, this->my_host, - this->other_host, this->my_virtual_ip, + this->other_host, this->my_vips, has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED) { this->public.rekey_child_sa(&this->public, @@ -1458,7 +1446,7 @@ METHOD(ike_sa_t, reauth, status_t, if (!has_condition(this, COND_ORIGINAL_INITIATOR)) { DBG1(DBG_IKE, "initiator did not reauthenticate as requested"); - if (this->other_virtual_ip != NULL || + if (this->other_vips->get_count(this->other_vips) != 0 || has_condition(this, COND_XAUTH_AUTHENTICATED) || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME @@ -1562,7 +1550,7 @@ METHOD(ike_sa_t, reestablish, status_t, /* check if we are able to reestablish this IKE_SA */ if (!has_condition(this, COND_ORIGINAL_INITIATOR) && - (this->other_virtual_ip != NULL || + (this->other_vips->get_count(this->other_vips) != 0 || has_condition(this, COND_EAP_AUTHENTICATED) #ifdef ME || this->is_mediation_server @@ -1585,11 +1573,12 @@ METHOD(ike_sa_t, reestablish, status_t, host = this->my_host; new->set_my_host(new, host->clone(host)); /* if we already have a virtual IP, we reuse it */ - host = this->my_virtual_ip; - if (host) + enumerator = this->my_vips->create_enumerator(this->my_vips); + while (enumerator->enumerate(enumerator, &host)) { - new->set_virtual_ip(new, TRUE, host); + new->add_virtual_ip(new, TRUE, host); } + enumerator->destroy(enumerator); #ifdef ME if (this->peer_cfg->is_mediation(this->peer_cfg)) @@ -1732,7 +1721,7 @@ METHOD(ike_sa_t, set_auth_lifetime, status_t, * We send the notify in IKE_AUTH if not yet ESTABLISHED. */ send_update = this->state == IKE_ESTABLISHED && this->version == IKEV2 && !has_condition(this, COND_ORIGINAL_INITIATOR) && - (this->other_virtual_ip != NULL || + (this->other_vips->get_count(this->other_vips) != 0 || has_condition(this, COND_EAP_AUTHENTICATED)); if (lifetime < diff) @@ -1937,6 +1926,7 @@ METHOD(ike_sa_t, inherit, void, attribute_entry_t *entry; enumerator_t *enumerator; auth_cfg_t *cfg; + host_t *vip; /* apply hosts and ids */ this->my_host->destroy(this->my_host); @@ -1948,16 +1938,15 @@ METHOD(ike_sa_t, inherit, void, this->my_id = other->my_id->clone(other->my_id); this->other_id = other->other_id->clone(other->other_id); - /* apply virtual assigned IPs... */ - if (other->my_virtual_ip) + /* apply assigned virtual IPs... */ + while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS) { - this->my_virtual_ip = other->my_virtual_ip; - other->my_virtual_ip = NULL; + other->my_vips->insert_first(other->my_vips, vip); } - if (other->other_virtual_ip) + while (this->other_vips->remove_last(this->other_vips, + (void**)&vip) == SUCCESS) { - this->other_virtual_ip = other->other_virtual_ip; - other->other_virtual_ip = NULL; + other->other_vips->insert_first(other->other_vips, vip); } /* authentication information */ @@ -2032,6 +2021,7 @@ METHOD(ike_sa_t, destroy, void, private_ike_sa_t *this) { attribute_entry_t *entry; + host_t *vip; charon->bus->set_sa(charon->bus, &this->public); @@ -2056,22 +2046,34 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->keymat); - if (this->my_virtual_ip) + while (this->my_vips->remove_last(this->my_vips, (void**)&vip) == SUCCESS) { - hydra->kernel_interface->del_ip(hydra->kernel_interface, - this->my_virtual_ip); - this->my_virtual_ip->destroy(this->my_virtual_ip); + hydra->kernel_interface->del_ip(hydra->kernel_interface, vip); + vip->destroy(vip); } - if (this->other_virtual_ip) + this->my_vips->destroy(this->my_vips); + while (this->other_vips->remove_last(this->other_vips, + (void**)&vip) == SUCCESS) { - if (this->peer_cfg && this->peer_cfg->get_pool(this->peer_cfg)) + if (this->peer_cfg) { - hydra->attributes->release_address(hydra->attributes, - this->peer_cfg->get_pool(this->peer_cfg), - this->other_virtual_ip, get_other_eap_id(this)); + enumerator_t *enumerator; + char *pool; + + enumerator = this->peer_cfg->create_pool_enumerator(this->peer_cfg); + while (enumerator->enumerate(enumerator, &pool)) + { + if (hydra->attributes->release_address(hydra->attributes, pool, + vip, get_other_eap_id(this))) + { + break; + } + } + enumerator->destroy(enumerator); } - this->other_virtual_ip->destroy(this->other_virtual_ip); + vip->destroy(vip); } + this->other_vips->destroy(this->other_vips); this->peer_addresses->destroy_offset(this->peer_addresses, offsetof(host_t, destroy)); #ifdef ME @@ -2190,8 +2192,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .generate_message = _generate_message, .reset = _reset, .get_unique_id = _get_unique_id, - .set_virtual_ip = _set_virtual_ip, - .get_virtual_ip = _get_virtual_ip, + .add_virtual_ip = _add_virtual_ip, + .create_virtual_ip_enumerator = _create_virtual_ip_enumerator, .add_configuration_attribute = _add_configuration_attribute, .set_kmaddress = _set_kmaddress, .create_task_enumerator = _create_task_enumerator, @@ -2226,6 +2228,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .other_auths = linked_list_create(), .unique_id = ++unique_id, .peer_addresses = linked_list_create(), + .my_vips = linked_list_create(), + .other_vips = linked_list_create(), .attributes = linked_list_create(), .keepalive_interval = lib->settings->get_time(lib->settings, "%s.keep_alive", KEEPALIVE_INTERVAL, charon->name), diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index de9e0ede4..d2a394b6c 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -931,7 +931,7 @@ struct ike_sa_t { status_t (*set_auth_lifetime)(ike_sa_t *this, u_int32_t lifetime); /** - * Set the virtual IP to use for this IKE_SA and its children. + * Add a virtual IP to use for this IKE_SA and its children. * * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same * lifetime as the IKE_SA. @@ -939,15 +939,15 @@ struct ike_sa_t { * @param local TRUE to set local address, FALSE for remote * @param ip IP to set as virtual IP */ - void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); + void (*add_virtual_ip) (ike_sa_t *this, bool local, host_t *ip); /** - * Get the virtual IP configured. + * Create an enumerator over virtual IPs. * * @param local TRUE to get local virtual IP, FALSE for remote - * @return host_t *virtual IP + * @return enumerator over host_t* */ - host_t* (*get_virtual_ip) (ike_sa_t *this, bool local); + enumerator_t* (*create_virtual_ip_enumerator) (ike_sa_t *this, bool local); /** * Register a configuration attribute to the IKE_SA. diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c index e5a48ec49..4096141ec 100644 --- a/src/libcharon/sa/ikev1/phase1.c +++ b/src/libcharon/sa/ikev1/phase1.c @@ -609,6 +609,34 @@ METHOD(phase1_t, get_id, identification_t*, return id; } +METHOD(phase1_t, has_virtual_ip, bool, + private_phase1_t *this, peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + bool found = FALSE; + host_t *host; + + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + found = enumerator->enumerate(enumerator, &host); + enumerator->destroy(enumerator); + + return found; +} + +METHOD(phase1_t, has_pool, bool, + private_phase1_t *this, peer_cfg_t *peer_cfg) +{ + enumerator_t *enumerator; + bool found = FALSE; + char *pool; + + enumerator = peer_cfg->create_pool_enumerator(peer_cfg); + found = enumerator->enumerate(enumerator, &pool); + enumerator->destroy(enumerator); + + return found; +} + METHOD(phase1_t, save_sa_payload, bool, private_phase1_t *this, message_t *message) { @@ -748,6 +776,8 @@ phase1_t *phase1_create(ike_sa_t *ike_sa, bool initiator) .get_auth_method = _get_auth_method, .get_id = _get_id, .select_config = _select_config, + .has_virtual_ip = _has_virtual_ip, + .has_pool = _has_pool, .verify_auth = _verify_auth, .build_auth = _build_auth, .save_sa_payload = _save_sa_payload, diff --git a/src/libcharon/sa/ikev1/phase1.h b/src/libcharon/sa/ikev1/phase1.h index f1e95d88c..eaf8908e7 100644 --- a/src/libcharon/sa/ikev1/phase1.h +++ b/src/libcharon/sa/ikev1/phase1.h @@ -109,6 +109,22 @@ struct phase1_t { identification_t* (*get_id)(phase1_t *this, peer_cfg_t *peer_cfg, bool local); /** + * Check if peer config has virtual IPs pool assigned. + * + * @param peer_cfg peer_config to check + * @return TRUE if peer config contains at least one pool + */ + bool (*has_pool)(phase1_t *this, peer_cfg_t *peer_cfg); + + /** + * Check if peer config has virtual IPs to request + * + * @param peer_cfg peer_config to check + * @return TRUE if peer config contains at least one virtual IP + */ + bool (*has_virtual_ip)(phase1_t *this, peer_cfg_t *peer_cfg); + + /** * Extract and store SA payload bytes from encoded message. * * @param message message to extract SA payload bytes from diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 3c84b5114..ea836b76e 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -346,15 +346,30 @@ METHOD(task_manager_t, retransmit, status_t, */ static bool mode_config_expected(private_task_manager_t *this) { + enumerator_t *enumerator; peer_cfg_t *peer_cfg; + char *pool; + host_t *host; peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - if (peer_cfg && peer_cfg->get_pool(peer_cfg)) + if (peer_cfg) { - if (!this->ike_sa->get_virtual_ip(this->ike_sa, FALSE)) - { + enumerator = peer_cfg->create_pool_enumerator(peer_cfg); + if (!enumerator->enumerate(enumerator, &pool)) + { /* no pool configured */ + enumerator->destroy(enumerator); + return FALSE; + } + enumerator->destroy(enumerator); + + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, + FALSE); + if (!enumerator->enumerate(enumerator, &host)) + { /* have a pool, but no VIP assigned yet */ + enumerator->destroy(enumerator); return TRUE; } + enumerator->destroy(enumerator); } return FALSE; } @@ -1309,11 +1324,12 @@ METHOD(task_manager_t, queue_ike_reauth, void, new->set_other_host(new, host->clone(host)); host = this->ike_sa->get_my_host(this->ike_sa); new->set_my_host(new, host->clone(host)); - host = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (host) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) { - new->set_virtual_ip(new, TRUE, host); + new->add_virtual_ip(new, TRUE, host); } + enumerator->destroy(enumerator); enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); while (enumerator->enumerate(enumerator, &child_sa)) diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c index 38962fc60..954dea880 100644 --- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c +++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c @@ -320,7 +320,7 @@ METHOD(task_t, build_i, status_t, } break; } - if (this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); @@ -497,8 +497,8 @@ METHOD(task_t, process_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (this->peer_cfg->get_pool(this->peer_cfg) == NULL && - this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && + this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c index 539286938..9ccf9abf5 100644 --- a/src/libcharon/sa/ikev1/tasks/main_mode.c +++ b/src/libcharon/sa/ikev1/tasks/main_mode.c @@ -524,8 +524,8 @@ METHOD(task_t, build_r, status_t, this->ike_sa->get_id(this->ike_sa))); break; } - if (this->peer_cfg->get_pool(this->peer_cfg) == NULL && - this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (!this->ph1->has_pool(this->ph1, this->peer_cfg) && + this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); @@ -658,7 +658,7 @@ METHOD(task_t, process_i, status_t, } break; } - if (this->peer_cfg->get_virtual_ip(this->peer_cfg)) + if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg)) { this->ike_sa->queue_task(this->ike_sa, (task_t*)mode_config_create(this->ike_sa, TRUE)); diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c index ab3a81386..ef1b35c17 100644 --- a/src/libcharon/sa/ikev1/tasks/mode_config.c +++ b/src/libcharon/sa/ikev1/tasks/mode_config.c @@ -42,9 +42,9 @@ struct private_mode_config_t { bool initiator; /** - * virtual ip + * Received list of virtual IPs, host_t* */ - host_t *virtual_ip; + linked_list_t *vips; /** * list of attributes requested and its handler, entry_t @@ -174,8 +174,7 @@ static void process_attribute(private_mode_config_t *this, } if (ip) { - DESTROY_IF(this->virtual_ip); - this->virtual_ip = ip; + this->vips->insert_last(this->vips, ip); } break; } @@ -239,23 +238,44 @@ METHOD(task_t, build_i, status_t, peer_cfg_t *config; configuration_attribute_type_t type; chunk_t data; - host_t *vip; + linked_list_t *vips; + host_t *host; + + vips = linked_list_create(); /* reuse virtual IP if we already have one */ - vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (!vip) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); + + if (vips->get_count(vips) == 0) { config = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = config->get_virtual_ip(config); + enumerator = config->create_virtual_ip_enumerator(config); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); } - if (vip) + + if (vips->get_count(vips)) { cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REQUEST); - cp->add_attribute(cp, build_vip(vip)); + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &host)) + { + cp->add_attribute(cp, build_vip(host)); + } + enumerator->destroy(enumerator); } - enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes, - this->ike_sa->get_other_id(this->ike_sa), vip); + enumerator = hydra->attributes->create_initiator_enumerator( + hydra->attributes, + this->ike_sa->get_other_id(this->ike_sa), vips); while (enumerator->enumerate(enumerator, &handler, &type, &data)) { configuration_attribute_t *ca; @@ -279,6 +299,8 @@ METHOD(task_t, build_i, status_t, } enumerator->destroy(enumerator); + vips->destroy(vips); + if (cp) { message->add_payload(message, (payload_t*)cp); @@ -299,37 +321,64 @@ METHOD(task_t, build_r, status_t, enumerator_t *enumerator; configuration_attribute_type_t type; chunk_t value; - host_t *vip = NULL; cp_payload_t *cp = NULL; peer_cfg_t *config; identification_t *id; + linked_list_t *vips, *pools; + host_t *requested; id = this->ike_sa->get_other_eap_id(this->ike_sa); - config = this->ike_sa->get_peer_cfg(this->ike_sa); - if (this->virtual_ip) + vips = linked_list_create(); + pools = linked_list_create(); + + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &requested)) { - DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); - if (config->get_pool(config)) - { - vip = hydra->attributes->acquire_address(hydra->attributes, - config->get_pool(config), id, this->virtual_ip); - } - cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY); - if (vip) + enumerator_t *poolenum; + char *pool; + host_t *found = NULL; + + /* query all pools until we get an address */ + DBG1(DBG_IKE, "peer requested virtual IP %H", requested); + + poolenum = config->create_pool_enumerator(config); + while (poolenum->enumerate(poolenum, &pool)) { - DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id); - this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); - cp->add_attribute(cp, build_vip(vip)); + found = hydra->attributes->acquire_address(hydra->attributes, + pool, id, requested); + if (!found) + { + continue; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id); + this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found); + if (!cp) + { + cp = cp_payload_create_type(CONFIGURATION_V1, CFG_REPLY); + } + cp->add_attribute(cp, build_vip(found)); + /* use pool to request other attributes */ + if (pools->find_first(pools, NULL, (void**)&pool) == NOT_FOUND) + { + pools->insert_last(pools, pool); + } + vips->insert_last(vips, found); + break; } - else + poolenum->destroy(poolenum); + + if (!found) { - DBG1(DBG_IKE, "no virtual IP found, sending empty config payload"); + DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'", + requested, id); } } + enumerator->destroy(enumerator); + /* query registered providers for additional attributes to include */ enumerator = hydra->attributes->create_responder_enumerator( - hydra->attributes, config->get_pool(config), id, vip); + hydra->attributes, pools, id, vips); while (enumerator->enumerate(enumerator, &type, &value)) { if (!cp) @@ -343,25 +392,35 @@ METHOD(task_t, build_r, status_t, type, value)); } enumerator->destroy(enumerator); + vips->destroy_offset(vips, offsetof(host_t, destroy)); + vips->destroy(vips); if (cp) { cp->set_identifier(cp, this->identifier); message->add_payload(message, (payload_t*)cp); } - DESTROY_IF(vip); return SUCCESS; } METHOD(task_t, process_i, status_t, private_mode_config_t *this, message_t *message) { + enumerator_t *enumerator; + host_t *host; + process_payloads(this, message); - if (this->virtual_ip) + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &host)) { - this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + if (!host->is_anyaddr(host)) + { + this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + } } + enumerator->destroy(enumerator); + return SUCCESS; } @@ -374,10 +433,9 @@ METHOD(task_t, get_type, task_type_t, METHOD(task_t, migrate, void, private_mode_config_t *this, ike_sa_t *ike_sa) { - DESTROY_IF(this->virtual_ip); - this->ike_sa = ike_sa; - this->virtual_ip = NULL; + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); + this->vips = linked_list_create(); this->requested->destroy_function(this->requested, free); this->requested = linked_list_create(); } @@ -385,7 +443,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_mode_config_t *this) { - DESTROY_IF(this->virtual_ip); + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->requested->destroy_function(this->requested, free); free(this); } @@ -408,6 +466,7 @@ mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator) .initiator = initiator, .ike_sa = ike_sa, .requested = linked_list_create(), + .vips = linked_list_create(), ); if (initiator) diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 67d1b45f1..73a23f4ea 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -398,11 +398,12 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, linked_list_t *supplied) { traffic_selector_t *ts; + enumerator_t *enumerator; linked_list_t *list; host_t *host; - host = this->ike_sa->get_virtual_ip(this->ike_sa, local); - if (!host) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, local); + if (!enumerator->enumerate(enumerator, &host)) { if (local) { @@ -413,6 +414,7 @@ static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local, host = this->ike_sa->get_other_host(this->ike_sa); } } + enumerator->destroy(enumerator); list = this->config->get_traffic_selectors(this->config, local, supplied, host); if (list->get_first(list, (void**)&ts) == SUCCESS) @@ -831,6 +833,30 @@ static void check_for_rekeyed_child(private_quick_mode_t *this) enumerator->destroy(enumerator); } +/** + * Get host to use for dynamic traffic selectors + */ +static host_t *get_dynamic_host(ike_sa_t *ike_sa, bool local) +{ + enumerator_t *enumerator; + host_t *host; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); + if (!enumerator->enumerate(enumerator, &host)) + { + if (local) + { + host = ike_sa->get_my_host(ike_sa); + } + else + { + host = ike_sa->get_other_host(ike_sa); + } + } + enumerator->destroy(enumerator); + return host; +} + METHOD(task_t, process_r, status_t, private_quick_mode_t *this, message_t *message) { @@ -841,7 +867,6 @@ METHOD(task_t, process_r, status_t, sa_payload_t *sa_payload; linked_list_t *tsi, *tsr, *list = NULL; peer_cfg_t *peer_cfg; - host_t *me, *other; u_int16_t group; bool private; @@ -849,16 +874,6 @@ METHOD(task_t, process_r, status_t, { return FAILED; } - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (!me) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (!other) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); tsi = linked_list_create(); tsr = linked_list_create(); @@ -866,7 +881,8 @@ METHOD(task_t, process_r, status_t, tsr->insert_last(tsr, this->tsr); this->tsi = this->tsr = NULL; this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi, - me, other); + get_dynamic_host(this->ike_sa, TRUE), + get_dynamic_host(this->ike_sa, FALSE)); if (this->config) { this->tsi = select_ts(this, FALSE, tsi); diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 5c5468dab..d7d912fdc 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -285,6 +285,30 @@ static void schedule_inactivity_timeout(private_child_create_t *this) } /** + * Get host to use for dynamic traffic selectors + */ +static host_t *get_dynamic_host(ike_sa_t *ike_sa, bool local) +{ + enumerator_t *enumerator; + host_t *host; + + enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local); + if (!enumerator->enumerate(enumerator, &host)) + { + if (local) + { + host = ike_sa->get_my_host(ike_sa); + } + else + { + host = ike_sa->get_other_host(ike_sa); + } + } + enumerator->destroy(enumerator); + return host; +} + +/** * Install a CHILD_SA for usage, return value: * - FAILED: no acceptable proposal * - INVALID_ARG: diffie hellman group inacceptable @@ -298,7 +322,7 @@ static status_t select_and_install(private_child_create_t *this, chunk_t encr_i = chunk_empty, encr_r = chunk_empty; chunk_t integ_i = chunk_empty, integ_r = chunk_empty; linked_list_t *my_ts, *other_ts; - host_t *me, *other, *other_vip, *my_vip; + host_t *me, *other; bool private; if (this->proposals == NULL) @@ -314,8 +338,6 @@ static status_t select_and_install(private_child_create_t *this, me = this->ike_sa->get_my_host(this->ike_sa); other = this->ike_sa->get_other_host(this->ike_sa); - my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); private = this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN); this->proposal = this->config->select_proposal(this->config, @@ -354,15 +376,6 @@ static status_t select_and_install(private_child_create_t *this, this->dh_group = MODP_NONE; } - if (my_vip == NULL) - { - my_vip = me; - } - if (other_vip == NULL) - { - other_vip = other; - } - if (this->initiator) { nonce_i = this->my_nonce; @@ -378,9 +391,9 @@ static status_t select_and_install(private_child_create_t *this, other_ts = this->tsi; } my_ts = this->config->get_traffic_selectors(this->config, TRUE, my_ts, - my_vip); + get_dynamic_host(this->ike_sa, TRUE)); other_ts = this->config->get_traffic_selectors(this->config, FALSE, other_ts, - other_vip); + get_dynamic_host(this->ike_sa, FALSE)); if (this->initiator) { @@ -723,7 +736,8 @@ static void process_payloads(private_child_create_t *this, message_t *message) METHOD(task_t, build_i, status_t, private_child_create_t *this, message_t *message) { - host_t *me, *other, *vip; + enumerator_t *enumerator; + host_t *vip; peer_cfg_t *peer_cfg; switch (message->get_exchange_type(message)) @@ -763,22 +777,10 @@ METHOD(task_t, build_i, status_t, this->config->get_name(this->config)); } - /* reuse virtual IP if we already have one */ - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (me == NULL) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (other == NULL) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } - /* check if we want a virtual IP, but don't have one */ peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = peer_cfg->get_virtual_ip(peer_cfg); - if (!this->reqid && vip) + enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg); + if (!this->reqid && enumerator->enumerate(enumerator, &vip)) { /* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */ vip = host_create_any(vip->get_family(vip)); @@ -788,11 +790,12 @@ METHOD(task_t, build_i, status_t, } else { /* but narrow it for host2host / if we already have a vip */ - this->tsi = this->config->get_traffic_selectors(this->config, TRUE, - NULL, me); + this->tsi = this->config->get_traffic_selectors(this->config, TRUE, NULL, + get_dynamic_host(this->ike_sa, TRUE)); } - this->tsr = this->config->get_traffic_selectors(this->config, FALSE, - NULL, other); + enumerator->destroy(enumerator); + this->tsr = this->config->get_traffic_selectors(this->config, FALSE, NULL, + get_dynamic_host(this->ike_sa, FALSE)); if (this->packet_tsi) { @@ -948,20 +951,10 @@ METHOD(task_t, build_r, status_t, peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); if (!this->config && peer_cfg && this->tsi && this->tsr) { - host_t *me, *other; - - me = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (me == NULL) - { - me = this->ike_sa->get_my_host(this->ike_sa); - } - other = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE); - if (other == NULL) - { - other = this->ike_sa->get_other_host(this->ike_sa); - } - this->config = peer_cfg->select_child_cfg(peer_cfg, this->tsr, - this->tsi, me, other); + this->config = peer_cfg->select_child_cfg(peer_cfg, + this->tsr, this->tsi, + get_dynamic_host(this->ike_sa, TRUE), + get_dynamic_host(this->ike_sa, FALSE)); } if (this->config == NULL) diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c index 932b02d42..e788bb5f9 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_config.c +++ b/src/libcharon/sa/ikev2/tasks/ike_config.c @@ -43,9 +43,9 @@ struct private_ike_config_t { bool initiator; /** - * virtual ip + * Received list of virtual IPs, host_t* */ - host_t *virtual_ip; + linked_list_t *vips; /** * list of attributes requested and its handler, entry_t @@ -170,8 +170,7 @@ static void process_attribute(private_ike_config_t *this, } if (ip) { - DESTROY_IF(this->virtual_ip); - this->virtual_ip = ip; + this->vips->insert_last(this->vips, ip); } break; } @@ -242,23 +241,45 @@ METHOD(task_t, build_i, status_t, peer_cfg_t *config; configuration_attribute_type_t type; chunk_t data; - host_t *vip; + linked_list_t *vips; + host_t *host; + + vips = linked_list_create(); /* reuse virtual IP if we already have one */ - vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE); - if (!vip) + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, + TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); + + if (vips->get_count(vips) == 0) { config = this->ike_sa->get_peer_cfg(this->ike_sa); - vip = config->get_virtual_ip(config); + enumerator = config->create_virtual_ip_enumerator(config); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); } - if (vip) + + if (vips->get_count(vips)) { cp = cp_payload_create_type(CONFIGURATION, CFG_REQUEST); - cp->add_attribute(cp, build_vip(vip)); + enumerator = vips->create_enumerator(vips); + while (enumerator->enumerate(enumerator, &host)) + { + cp->add_attribute(cp, build_vip(host)); + } + enumerator->destroy(enumerator); } - enumerator = hydra->attributes->create_initiator_enumerator(hydra->attributes, - this->ike_sa->get_other_id(this->ike_sa), vip); + enumerator = hydra->attributes->create_initiator_enumerator( + hydra->attributes, + this->ike_sa->get_other_id(this->ike_sa), vips); while (enumerator->enumerate(enumerator, &handler, &type, &data)) { configuration_attribute_t *ca; @@ -284,6 +305,8 @@ METHOD(task_t, build_i, status_t, } enumerator->destroy(enumerator); + vips->destroy(vips); + if (cp) { message->add_payload(message, (payload_t*)cp); @@ -310,40 +333,75 @@ METHOD(task_t, build_r, status_t, enumerator_t *enumerator; configuration_attribute_type_t type; chunk_t value; - host_t *vip = NULL; cp_payload_t *cp = NULL; peer_cfg_t *config; identification_t *id; + linked_list_t *vips, *pools; + host_t *requested; id = this->ike_sa->get_other_eap_id(this->ike_sa); - config = this->ike_sa->get_peer_cfg(this->ike_sa); - if (this->virtual_ip) + vips = linked_list_create(); + pools = linked_list_create(); + + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &requested)) { - DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip); - if (config->get_pool(config)) + enumerator_t *poolenum; + char *pool; + host_t *found = NULL; + + /* query all pools until we get an address */ + DBG1(DBG_IKE, "peer requested virtual IP %H", requested); + + poolenum = config->create_pool_enumerator(config); + while (poolenum->enumerate(poolenum, &pool)) { - vip = hydra->attributes->acquire_address(hydra->attributes, - config->get_pool(config), id, this->virtual_ip); + found = hydra->attributes->acquire_address(hydra->attributes, + pool, id, requested); + if (!found) + { + continue; + } + DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id); + this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found); + if (!cp) + { + cp = cp_payload_create_type(CONFIGURATION, CFG_REPLY); + } + cp->add_attribute(cp, build_vip(found)); + /* use pool to request other attributes */ + if (pools->find_first(pools, NULL, (void**)&pool) == NOT_FOUND) + { + pools->insert_last(pools, pool); + } + vips->insert_last(vips, found); + break; } - if (vip == NULL) + poolenum->destroy(poolenum); + + if (!found) { - DBG1(DBG_IKE, "no virtual IP found, sending %N", - notify_type_names, INTERNAL_ADDRESS_FAILURE); - message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, - chunk_empty); - return SUCCESS; + DBG1(DBG_IKE, "no virtual IP found for %H requested by '%Y'", + requested, id); } - DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", vip, id); - this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, vip); + } + enumerator->destroy(enumerator); - cp = cp_payload_create_type(CONFIGURATION, CFG_REPLY); - cp->add_attribute(cp, build_vip(vip)); + if (this->vips->get_count(this->vips) && !vips->get_count(vips)) + { + DBG1(DBG_IKE, "no virtual IP found, sending %N", + notify_type_names, INTERNAL_ADDRESS_FAILURE); + message->add_notify(message, FALSE, INTERNAL_ADDRESS_FAILURE, + chunk_empty); + vips->destroy_offset(vips, offsetof(host_t, destroy)); + pools->destroy(pools); + return SUCCESS; } /* query registered providers for additional attributes to include */ enumerator = hydra->attributes->create_responder_enumerator( - hydra->attributes, config->get_pool(config), id, vip); + hydra->attributes, pools, id, vips); while (enumerator->enumerate(enumerator, &type, &value)) { if (!cp) @@ -357,12 +415,13 @@ METHOD(task_t, build_r, status_t, type, value)); } enumerator->destroy(enumerator); + vips->destroy_offset(vips, offsetof(host_t, destroy)); + pools->destroy(pools); if (cp) { message->add_payload(message, (payload_t*)cp); } - DESTROY_IF(vip); return SUCCESS; } return NEED_MORE; @@ -373,14 +432,20 @@ METHOD(task_t, process_i, status_t, { if (this->ike_sa->get_state(this->ike_sa) == IKE_ESTABLISHED) { /* in last IKE_AUTH exchange */ + enumerator_t *enumerator; + host_t *host; process_payloads(this, message); - if (this->virtual_ip && - !this->virtual_ip->is_anyaddr(this->virtual_ip)) + enumerator = this->vips->create_enumerator(this->vips); + while (enumerator->enumerate(enumerator, &host)) { - this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip); + if (!host->is_anyaddr(host)) + { + this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host); + } } + enumerator->destroy(enumerator); return SUCCESS; } return NEED_MORE; @@ -395,10 +460,9 @@ METHOD(task_t, get_type, task_type_t, METHOD(task_t, migrate, void, private_ike_config_t *this, ike_sa_t *ike_sa) { - DESTROY_IF(this->virtual_ip); - this->ike_sa = ike_sa; - this->virtual_ip = NULL; + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); + this->vips = linked_list_create(); this->requested->destroy_function(this->requested, free); this->requested = linked_list_create(); } @@ -406,7 +470,7 @@ METHOD(task_t, migrate, void, METHOD(task_t, destroy, void, private_ike_config_t *this) { - DESTROY_IF(this->virtual_ip); + this->vips->destroy_offset(this->vips, offsetof(host_t, destroy)); this->requested->destroy_function(this->requested, free); free(this); } @@ -428,6 +492,7 @@ ike_config_t *ike_config_create(ike_sa_t *ike_sa, bool initiator) }, .initiator = initiator, .ike_sa = ike_sa, + .vips = linked_list_create(), .requested = linked_list_create(), ); diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c index 133bc296e..8c30ed96c 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c +++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c @@ -250,15 +250,26 @@ static void update_children(private_ike_mobike_t *this) { enumerator_t *enumerator; child_sa_t *child_sa; + linked_list_t *vips; + host_t *host; + + vips = linked_list_create(); + + enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE); + while (enumerator->enumerate(enumerator, &host)) + { + vips->insert_last(vips, host); + } + enumerator->destroy(enumerator); enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa); while (enumerator->enumerate(enumerator, (void**)&child_sa)) { if (child_sa->update(child_sa, this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_virtual_ip(this->ike_sa, TRUE), - this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) == NOT_SUPPORTED) + this->ike_sa->get_other_host(this->ike_sa), vips, + this->ike_sa->has_condition(this->ike_sa, + COND_NAT_ANY)) == NOT_SUPPORTED) { this->ike_sa->rekey_child_sa(this->ike_sa, child_sa->get_protocol(child_sa), @@ -266,6 +277,8 @@ static void update_children(private_ike_mobike_t *this) } } enumerator->destroy(enumerator); + + vips->destroy(vips); } /** diff --git a/src/libhydra/attributes/attribute_handler.h b/src/libhydra/attributes/attribute_handler.h index d042f47ef..6014ef0fa 100644 --- a/src/libhydra/attributes/attribute_handler.h +++ b/src/libhydra/attributes/attribute_handler.h @@ -22,8 +22,8 @@ #define ATTRIBUTE_HANDLER_H_ #include <chunk.h> -#include <utils/host.h> #include <utils/identification.h> +#include <utils/linked_list.h> #include "attributes.h" @@ -62,11 +62,11 @@ struct attribute_handler_t { * Enumerate attributes to request from a server. * * @param server server identity to request attributes from - * @param vip virtual IP we are requesting, if any + * @param vips list of virtual IPs (host_t*) we are requesting * @return enumerator (configuration_attribute_type_t, chunk_t) */ enumerator_t* (*create_attribute_enumerator)(attribute_handler_t *this, - identification_t *server, host_t *vip); + identification_t *server, linked_list_t *vips); }; #endif /** ATTRIBUTE_HANDLER_H_ @}*/ diff --git a/src/libhydra/attributes/attribute_manager.c b/src/libhydra/attributes/attribute_manager.c index 95520531e..64dc9c7c9 100644 --- a/src/libhydra/attributes/attribute_manager.c +++ b/src/libhydra/attributes/attribute_manager.c @@ -51,12 +51,12 @@ struct private_attribute_manager_t { * Data to pass to enumerator filters */ typedef struct { - /** attribute group pool */ - char *pool; + /** attribute group pools */ + linked_list_t *pools; /** server/peer identity */ identification_t *id; - /** requesting/assigned virtual IP */ - host_t *vip; + /** requesting/assigned virtual IPs */ + linked_list_t *vips; } enum_data_t; METHOD(attribute_manager_t, acquire_address, host_t*, @@ -80,14 +80,10 @@ METHOD(attribute_manager_t, acquire_address, host_t*, enumerator->destroy(enumerator); this->lock->unlock(this->lock); - if (!host) - { - DBG1(DBG_CFG, "acquiring address from pool '%s' failed", pool); - } return host; } -METHOD(attribute_manager_t, release_address, void, +METHOD(attribute_manager_t, release_address, bool, private_attribute_manager_t *this, char *pool, host_t *address, identification_t *id) { @@ -108,10 +104,7 @@ METHOD(attribute_manager_t, release_address, void, enumerator->destroy(enumerator); this->lock->unlock(this->lock); - if (!found) - { - DBG1(DBG_CFG, "releasing address to pool '%s' failed", pool); - } + return found; } /** @@ -120,19 +113,21 @@ METHOD(attribute_manager_t, release_address, void, static enumerator_t *responder_enum_create(attribute_provider_t *provider, enum_data_t *data) { - return provider->create_attribute_enumerator(provider, data->pool, - data->id, data->vip); + return provider->create_attribute_enumerator(provider, data->pools, + data->id, data->vips); } METHOD(attribute_manager_t, create_responder_enumerator, enumerator_t*, - private_attribute_manager_t *this, char *pool, identification_t *id, - host_t *vip) + private_attribute_manager_t *this, linked_list_t *pools, + identification_t *id, linked_list_t *vips) { - enum_data_t *data = malloc_thing(enum_data_t); + enum_data_t *data; - data->pool = pool; - data->id = id; - data->vip = vip; + INIT(data, + .pools = pools, + .id = id, + .vips = vips, + ); this->lock->read_lock(this->lock); return enumerator_create_cleaner( enumerator_create_nested( @@ -238,8 +233,8 @@ typedef struct { enumerator_t *inner; /** server ID we want attributes for */ identification_t *id; - /** virtual IP we are requesting along with attriubutes */ - host_t *vip; + /** virtual IPs we are requesting along with attriubutes */ + linked_list_t *vips; } initiator_enumerator_t; /** @@ -259,7 +254,7 @@ static bool initiator_enumerate(initiator_enumerator_t *this, } DESTROY_IF(this->inner); this->inner = this->handler->create_attribute_enumerator(this->handler, - this->id, this->vip); + this->id, this->vips); } /* inject the handler as additional attribute */ *handler = this->handler; @@ -278,20 +273,22 @@ static void initiator_destroy(initiator_enumerator_t *this) } METHOD(attribute_manager_t, create_initiator_enumerator, enumerator_t*, - private_attribute_manager_t *this, identification_t *id, host_t *vip) + private_attribute_manager_t *this, identification_t *id, linked_list_t *vips) { - initiator_enumerator_t *enumerator = malloc_thing(initiator_enumerator_t); + initiator_enumerator_t *enumerator; this->lock->read_lock(this->lock); - enumerator->public.enumerate = (void*)initiator_enumerate; - enumerator->public.destroy = (void*)initiator_destroy; - enumerator->this = this; - enumerator->id = id; - enumerator->vip = vip; - enumerator->outer = this->handlers->create_enumerator(this->handlers); - enumerator->inner = NULL; - enumerator->handler = NULL; + INIT(enumerator, + .public = { + .enumerate = (void*)initiator_enumerate, + .destroy = (void*)initiator_destroy, + }, + .this = this, + .id = id, + .vips = vips, + .outer = this->handlers->create_enumerator(this->handlers), + ); return &enumerator->public; } diff --git a/src/libhydra/attributes/attribute_manager.h b/src/libhydra/attributes/attribute_manager.h index 56afef7c6..8bc80ca11 100644 --- a/src/libhydra/attributes/attribute_manager.h +++ b/src/libhydra/attributes/attribute_manager.h @@ -54,20 +54,22 @@ struct attribute_manager_t { * @param pool pool name from which the address was acquired * @param address address to release * @param id peer identity to get address for + * @return TRUE if address released to pool */ - void (*release_address)(attribute_manager_t *this, + bool (*release_address)(attribute_manager_t *this, char *pool, host_t *address, identification_t *id); /** * Create an enumerator over attributes to hand out to a peer. * - * @param pool pool name to get attributes from + * @param pool list of pools names (char*) to query attributes from * @param id peer identity to hand out attributes to - * @param vip virtual IP to assign to peer, if any + * @param vip list of virtual IPs (host_t*) to assign to peer * @return enumerator (configuration_attribute_type_t, chunk_t) */ enumerator_t* (*create_responder_enumerator)(attribute_manager_t *this, - char *pool, identification_t *id, host_t *vip); + linked_list_t *pool, identification_t *id, + linked_list_t *vips); /** * Register an attribute provider to the manager. @@ -114,11 +116,11 @@ struct attribute_manager_t { * Create an enumerator over attributes to request from server. * * @param id server identity to hand out attributes to - * @param vip virtual IP going to request, if any + * @param vip list of virtual IPs (host_t*) going to request * @return enumerator (attribute_handler_t, ca_type_t, chunk_t) */ enumerator_t* (*create_initiator_enumerator)(attribute_manager_t *this, - identification_t *id, host_t *vip); + identification_t *id, linked_list_t *vips); /** * Register an attribute handler to the manager. diff --git a/src/libhydra/attributes/attribute_provider.h b/src/libhydra/attributes/attribute_provider.h index e4b4e13f3..327135ffe 100644 --- a/src/libhydra/attributes/attribute_provider.h +++ b/src/libhydra/attributes/attribute_provider.h @@ -23,6 +23,7 @@ #include <utils/host.h> #include <utils/identification.h> +#include <utils/linked_list.h> typedef struct attribute_provider_t attribute_provider_t; @@ -56,13 +57,14 @@ struct attribute_provider_t { /** * Create an enumerator over attributes to hand out to a peer. * - * @param pool pool name to get attributes from + * @param pool list of pools names (char*) to query attributes from * @param id peer ID - * @param vip virtual IP to assign to peer, if any + * @param vip list of virtual IPs (host_t*) to assign to peer * @return enumerator (configuration_attribute_type_t, chunk_t) */ enumerator_t* (*create_attribute_enumerator)(attribute_provider_t *this, - char *pool, identification_t *id, host_t *vip); + linked_list_t *pools, identification_t *id, + linked_list_t *vips); }; #endif /** ATTRIBUTE_PROVIDER_H_ @}*/ diff --git a/src/libhydra/attributes/mem_pool.c b/src/libhydra/attributes/mem_pool.c index f55b3a7d1..b2fed2703 100644 --- a/src/libhydra/attributes/mem_pool.c +++ b/src/libhydra/attributes/mem_pool.c @@ -162,6 +162,12 @@ METHOD(mem_pool_t, get_name, const char*, return this->name; } +METHOD(mem_pool_t, get_base, host_t*, + private_mem_pool_t *this) +{ + return this->base; +} + METHOD(mem_pool_t, get_size, u_int, private_mem_pool_t *this) { @@ -220,11 +226,9 @@ METHOD(mem_pool_t, acquire_address, host_t*, return requested->clone(requested); } - if (!requested->is_anyaddr(requested) && - requested->get_family(requested) != + if (requested->get_family(requested) != this->base->get_family(this->base)) { - DBG1(DBG_CFG, "IP pool address family mismatch"); return NULL; } @@ -463,6 +467,7 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits) INIT(this, .public = { .get_name = _get_name, + .get_base = _get_base, .get_size = _get_size, .get_online = _get_online, .get_offline = _get_offline, diff --git a/src/libhydra/attributes/mem_pool.h b/src/libhydra/attributes/mem_pool.h index bb963de93..7b7e58af7 100644 --- a/src/libhydra/attributes/mem_pool.h +++ b/src/libhydra/attributes/mem_pool.h @@ -39,6 +39,13 @@ struct mem_pool_t { const char* (*get_name)(mem_pool_t *this); /** + * Get the base (first) address of this pool. + * + * @return base address, internal host + */ + host_t* (*get_base)(mem_pool_t *this); + + /** * Get the size (i.e. number of addresses) of this pool. * * @return the size of this pool diff --git a/src/libhydra/plugins/attr/attr_provider.c b/src/libhydra/plugins/attr/attr_provider.c index 6af8b473b..673c72116 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( diff --git a/src/libhydra/plugins/attr_sql/sql_attribute.c b/src/libhydra/plugins/attr_sql/sql_attribute.c index 714bbcd72..8055be71c 100644 --- a/src/libhydra/plugins/attr_sql/sql_attribute.c +++ b/src/libhydra/plugins/attr_sql/sql_attribute.c @@ -233,7 +233,7 @@ 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, char *name, identification_t *id, host_t *requested) { host_t *address = NULL; @@ -242,59 +242,17 @@ METHOD(attribute_provider_t, acquire_address, host_t*, identity = get_identity(this, id); if (identity) { - /* check for a single pool first (no concatenation and enumeration) */ - if (strchr(names, ',') == NULL) + pool = get_pool(this, name, &timeout); + if (pool) { - pool = get_pool(this, names, &timeout); - if (pool) + /* check for an existing lease */ + address = check_lease(this, name, pool, identity); + if (address == NULL) { - /* check for an existing lease */ - address = check_lease(this, names, pool, identity); - if (address == NULL) - { - /* get an unallocated address or expired lease */ - address = get_lease(this, names, pool, timeout, identity); - } + /* get an unallocated address or expired lease */ + address = get_lease(this, name, pool, timeout, identity); } } - else - { - enumerator_t *enumerator; - char *name; - - /* 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, ",", " "); - while (enumerator->enumerate(enumerator, &name)) - { - pool = get_pool(this, name, &timeout); - if (pool) - { - address = get_lease(this, name, pool, timeout, identity); - if (address) - { - break; - } - } - } - enumerator->destroy(enumerator); - } } return address; } @@ -303,50 +261,41 @@ METHOD(attribute_provider_t, release_address, bool, private_sql_attribute_t *this, char *name, host_t *address, identification_t *id) { - enumerator_t *enumerator; - bool found = FALSE; + u_int pool, timeout; time_t now = time(NULL); - enumerator = enumerator_create_token(name, ",", " "); - while (enumerator->enumerate(enumerator, &name)) + pool = get_pool(this, name, &timeout); + if (pool) { - u_int pool, timeout; - - pool = get_pool(this, name, &timeout); - if (pool) + if (this->history) { - if (this->history) - { - this->db->execute(this->db, NULL, - "INSERT INTO leases (address, identity, acquired, released)" - " SELECT id, identity, acquired, ? FROM addresses " - " WHERE pool = ? AND address = ?", - 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; - } + this->db->execute(this->db, NULL, + "INSERT INTO leases (address, identity, acquired, released)" + " SELECT id, identity, acquired, ? FROM addresses " + " WHERE pool = ? AND address = ?", + 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) + { + return TRUE; } } - enumerator->destroy(enumerator); - return found; + return FALSE; } 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 +306,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 +334,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 +368,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/resolve/resolve_handler.c b/src/libhydra/plugins/resolve/resolve_handler.c index 011ebbaaf..2bee45d0d 100644 --- a/src/libhydra/plugins/resolve/resolve_handler.c +++ b/src/libhydra/plugins/resolve/resolve_handler.c @@ -267,46 +267,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; } - *data = chunk_empty; - /* enumerate only once */ - this->public.enumerate = (void*)return_false; - return TRUE; + if (this->v6) + { + *type = INTERNAL_IP6_DNS; + *data = chunk_empty; + this->v6 = 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, diff --git a/src/starter/args.c b/src/starter/args.c index 640cc43d6..eb25b2abb 100644 --- a/src/starter/args.c +++ b/src/starter/args.c @@ -186,7 +186,7 @@ static const token_info_t token_info[] = { ARG_STR, offsetof(starter_end_t, subnet), NULL }, { ARG_MISC, 0, NULL /* KW_PROTOPORT */ }, { ARG_STR, offsetof(starter_end_t, sourceip), NULL }, - { ARG_MISC, 0, NULL /* KW_NATIP */ }, + { ARG_STR, offsetof(starter_end_t, dns), NULL }, { ARG_ENUM, offsetof(starter_end_t, firewall), LST_bool }, { ARG_ENUM, offsetof(starter_end_t, hostaccess), LST_bool }, { ARG_ENUM, offsetof(starter_end_t, allow_any), LST_bool }, diff --git a/src/starter/cmp.c b/src/starter/cmp.c index 724dc7804..aaba7b11d 100644 --- a/src/starter/cmp.c +++ b/src/starter/cmp.c @@ -26,7 +26,6 @@ static bool starter_cmp_end(starter_end_t *c1, starter_end_t *c2) if ((c1 == NULL) || (c2 == NULL)) return FALSE; - VARCMP(has_natip); VARCMP(modecfg); VARCMP(port); VARCMP(protocol); diff --git a/src/starter/confread.c b/src/starter/confread.c index 642fc551b..f30f2188c 100644 --- a/src/starter/confread.c +++ b/src/starter/confread.c @@ -231,64 +231,6 @@ static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token, end->host = strdupnull(value); break; case KW_SOURCEIP: - if (end->has_natip) - { - DBG1(DBG_APP, "# natip and sourceip cannot be defined at the same time"); - goto err; - } - if (value[0] == '%') - { - if (streq(value, "%modeconfig") || streq(value, "%modecfg") || - streq(value, "%config") || streq(value, "%cfg")) - { - /* request ip via config payload */ - free(end->sourceip); - end->sourceip = NULL; - end->sourceip_mask = 1; - } - else - { /* %poolname, strip %, serve ip requests */ - free(end->sourceip); - end->sourceip = strdupnull(value+1); - end->sourceip_mask = 0; - } - end->modecfg = TRUE; - } - else - { - host_t *host; - char *sep; - - sep = strchr(value, '/'); - if (sep) - { /* CIDR notation, address pool */ - *sep = '\0'; - host = host_create_from_string(value, 0); - if (!host) - { - DBG1(DBG_APP, "# bad subnet: %s=%s", name, value); - goto err; - } - host->destroy(host); - free(end->sourceip); - end->sourceip = strdupnull(value); - end->sourceip_mask = atoi(sep + 1); - /* restore the original text in case also= is used */ - *sep = '/'; - } - else - { /* fixed srcip */ - host = host_create_from_string(value, 0); - if (!host) - { - DBG1(DBG_APP, "# bad addr: %s=%s", name, value); - goto err; - } - end->sourceip_mask = (host->get_family(host) == AF_INET) ? - 32 : 128; - host->destroy(host); - } - } conn->mode = MODE_TUNNEL; conn->proxy_mode = FALSE; break; @@ -370,27 +312,6 @@ static void kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token, } break; } - case KW_NATIP: - { - host_t *host; - if (end->sourceip) - { - DBG1(DBG_APP, "# natip and sourceip cannot be defined at the same time"); - goto err; - } - host = host_create_from_string(value, 0); - if (!host) - { - DBG1(DBG_APP, "# bad addr: %s=%s", name, value); - goto err; - } - host->destroy(host); - end->sourceip = strdupnull(value); - end->has_natip = TRUE; - conn->mode = MODE_TUNNEL; - conn->proxy_mode = FALSE; - break; - } default: break; } diff --git a/src/starter/confread.h b/src/starter/confread.h index 0c284229b..3f2079883 100644 --- a/src/starter/confread.h +++ b/src/starter/confread.h @@ -102,7 +102,6 @@ struct starter_end { char *host; u_int ikeport; char *subnet; - bool has_natip; bool modecfg; certpolicy_t sendcert; bool firewall; @@ -112,7 +111,7 @@ struct starter_end { u_int16_t port; u_int8_t protocol; char *sourceip; - int sourceip_mask; + char *dns; }; typedef struct also also_t; diff --git a/src/starter/keywords.h b/src/starter/keywords.h index bd9affe6c..537bceb07 100644 --- a/src/starter/keywords.h +++ b/src/starter/keywords.h @@ -97,7 +97,7 @@ typedef enum { KW_SUBNET, KW_PROTOPORT, KW_SOURCEIP, - KW_NATIP, + KW_DNS, KW_FIREWALL, KW_HOSTACCESS, KW_ALLOWANY, @@ -126,7 +126,7 @@ typedef enum { KW_LEFTSUBNET, KW_LEFTPROTOPORT, KW_LEFTSOURCEIP, - KW_LEFTNATIP, + KW_LEFTDNS, KW_LEFTFIREWALL, KW_LEFTHOSTACCESS, KW_LEFTALLOWANY, @@ -155,7 +155,7 @@ typedef enum { KW_RIGHTSUBNET, KW_RIGHTPROTOPORT, KW_RIGHTSOURCEIP, - KW_RIGHTNATIP, + KW_RIGHTDNS, KW_RIGHTFIREWALL, KW_RIGHTHOSTACCESS, KW_RIGHTALLOWANY, diff --git a/src/starter/keywords.txt b/src/starter/keywords.txt index 30d89f924..8366f5209 100644 --- a/src/starter/keywords.txt +++ b/src/starter/keywords.txt @@ -85,7 +85,7 @@ leftsubnet, KW_LEFTSUBNET leftsubnetwithin, KW_LEFTSUBNET leftprotoport, KW_LEFTPROTOPORT leftsourceip, KW_LEFTSOURCEIP -leftnatip, KW_LEFTNATIP +leftdns, KW_LEFTDNS leftfirewall, KW_LEFTFIREWALL lefthostaccess, KW_LEFTHOSTACCESS leftallowany, KW_LEFTALLOWANY @@ -109,7 +109,7 @@ rightsubnet, KW_RIGHTSUBNET rightsubnetwithin, KW_RIGHTSUBNET rightprotoport, KW_RIGHTPROTOPORT rightsourceip, KW_RIGHTSOURCEIP -rightnatip, KW_RIGHTNATIP +rightdns, KW_RIGHTDNS rightfirewall, KW_RIGHTFIREWALL righthostaccess, KW_RIGHTHOSTACCESS rightallowany, KW_RIGHTALLOWANY diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c index 0fbc5d245..d6ad3eb89 100644 --- a/src/starter/starterstroke.c +++ b/src/starter/starterstroke.c @@ -140,7 +140,7 @@ static void starter_stroke_add_end(stroke_msg_t *msg, stroke_end_t *msg_end, sta msg_end->ikeport = conn_end->ikeport; msg_end->subnets = push_string(msg, conn_end->subnet); msg_end->sourceip = push_string(msg, conn_end->sourceip); - msg_end->sourceip_mask = conn_end->sourceip_mask; + msg_end->dns = push_string(msg, conn_end->dns); msg_end->sendcert = conn_end->sendcert; msg_end->hostaccess = conn_end->hostaccess; msg_end->tohost = !conn_end->subnet; diff --git a/src/stroke/stroke_msg.h b/src/stroke/stroke_msg.h index 95de7d69b..662feed69 100644 --- a/src/stroke/stroke_msg.h +++ b/src/stroke/stroke_msg.h @@ -158,7 +158,7 @@ struct stroke_end_t { char *address; u_int16_t ikeport; char *sourceip; - int sourceip_mask; + char *dns; char *subnets; int sendcert; int hostaccess; |