2 * Copyright (C) 2008 Martin Willi
3 * HSR Hochschule fuer Technik Rapperswil
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "load_tester_config.h"
21 #include <attributes/mem_pool.h>
22 #include <collections/hashtable.h>
23 #include <threading/mutex.h>
25 #define UNIQUE_PORT_START 1025
27 typedef struct private_load_tester_config_t private_load_tester_config_t
;
30 * Private data of an load_tester_config_t object
32 struct private_load_tester_config_t
{
37 load_tester_config_t
public;
75 * Authentication method(s) to use/expect from initiator
80 * Authentication method(s) use/expected from responder
85 * Initiator ID to enforce
90 * Initiator ID to to match against as responder
92 char *initiator_match
;
95 * Responder ID to enforce
100 * IPsec mode, tunnel|transport|beet
105 * Traffic Selector on initiator side, as proposed from initiator
110 * Traffic Selector on responder side, as proposed from initiator
115 * Traffic Selector on initiator side, as narrowed by responder
120 * Traffic Selector on responder side, as narrowed by responder
125 * Current port for unique initiator ports
127 uint16_t unique_port
;
130 * IKE_SA rekeying delay
135 * CHILD_SA rekeying delay
145 * DPD timeout (IKEv1 only)
150 * incremental numbering of generated configs
155 * Dynamic source port, if used
160 * IKE version to use for load testing
162 ike_version_t version
;
165 * List of pools to allocate external addresses dynamically, as mem_pool_t
167 linked_list_t
*pools
;
170 * Address prefix to use when installing dynamic addresses
175 * Keep addresses until shutdown?
180 * Hashtable with leases in "pools", host_t => entry_t
185 * Mutex for leases hashtable
194 /** host reference, equal to key */
196 /** associated identity */
197 identification_t
*id
;
203 static void entry_destroy(entry_t
*this)
205 this->host
->destroy(this->host
);
206 this->id
->destroy(this->id
);
211 * Hashtable hash function
213 static u_int
hash(host_t
*key
)
215 return chunk_hash(key
->get_address(key
));
219 * Hashtable equals function
221 static bool equals(host_t
*a
, host_t
*b
)
223 return a
->ip_equals(a
, b
);
227 * Load external addresses to use, if any
229 static void load_addrs(private_load_tester_config_t
*this)
231 enumerator_t
*enumerator
, *tokens
;
234 char *iface
, *token
, *pos
;
237 this->keep
= lib
->settings
->get_bool(lib
->settings
,
238 "%s.plugins.load-tester.addrs_keep", FALSE
, lib
->ns
);
239 this->prefix
= lib
->settings
->get_int(lib
->settings
,
240 "%s.plugins.load-tester.addrs_prefix", 16, lib
->ns
);
241 enumerator
= lib
->settings
->create_key_value_enumerator(lib
->settings
,
242 "%s.plugins.load-tester.addrs", lib
->ns
);
243 while (enumerator
->enumerate(enumerator
, &iface
, &token
))
245 tokens
= enumerator_create_token(token
, ",", " ");
246 while (tokens
->enumerate(tokens
, &token
))
248 pos
= strchr(token
, '-');
252 /* trim whitespace */
257 while (token
[strlen(token
) - 1] == ' ')
259 token
[strlen(token
) - 1] = '\0';
261 from
= host_create_from_string(token
, 0);
262 to
= host_create_from_string(pos
, 0);
265 pool
= mem_pool_create_range(iface
, from
, to
);
268 DBG1(DBG_CFG
, "loaded load-tester address range "
269 "%H-%H on %s", from
, to
, iface
);
270 this->pools
->insert_last(this->pools
, pool
);
277 DBG1(DBG_CFG
, "parsing load-tester address range %s-%s "
278 "failed, skipped", token
, pos
);
285 from
= host_create_from_subnet(token
, &bits
);
288 DBG1(DBG_CFG
, "loaded load-tester address pool %H/%d on %s",
290 pool
= mem_pool_create(iface
, from
, bits
);
292 this->pools
->insert_last(this->pools
, pool
);
296 DBG1(DBG_CFG
, "parsing load-tester address %s failed, "
301 tokens
->destroy(tokens
);
303 enumerator
->destroy(enumerator
);
307 * Generate auth config from string
309 static void generate_auth_cfg(private_load_tester_config_t
*this, char *str
,
310 peer_cfg_t
*peer_cfg
, bool local
, int num
)
312 enumerator_t
*enumerator
;
314 identification_t
*id
;
320 enumerator
= enumerator_create_token(str
, "|", " ");
321 while (enumerator
->enumerate(enumerator
, &str
))
324 auth
= auth_cfg_create();
327 if (this->initiator_id
)
329 if (this->initiator_match
&& (!local
&& !num
))
330 { /* as responder, use the secified identity that matches
331 * all used initiator identities, if given. */
332 snprintf(buf
, sizeof(buf
), this->initiator_match
, rnd
);
333 id
= identification_create_from_string(buf
);
335 else if ((local
&& num
) || (!local
&& !num
))
336 { /* as initiator, create peer specific identities */
337 snprintf(buf
, sizeof(buf
), this->initiator_id
, num
, rnd
);
338 id
= identification_create_from_string(buf
);
341 if (this->responder_id
)
343 if ((local
&& !num
) || (!local
&& num
))
345 snprintf(buf
, sizeof(buf
), this->responder_id
, num
, rnd
);
346 id
= identification_create_from_string(buf
);
350 if (streq(str
, "psk"))
351 { /* PSK authentication, use FQDNs */
352 class = AUTH_CLASS_PSK
;
355 if ((local
&& !num
) || (!local
&& num
))
357 id
= identification_create_from_string("srv.strongswan.org");
361 snprintf(buf
, sizeof(buf
), "c%d-r%d.strongswan.org",
363 id
= identification_create_from_string(buf
);
367 id
= identification_create_from_string("*.strongswan.org");
371 else if (strpfx(str
, "eap"))
372 { /* EAP authentication, use a NAI */
373 class = AUTH_CLASS_EAP
;
374 if (*(str
+ strlen("eap")) == '-')
376 type
= eap_type_from_string(str
+ strlen("eap-"));
379 auth
->add(auth
, AUTH_RULE_EAP_TYPE
, type
);
386 snprintf(buf
, sizeof(buf
), "1%.10d%.4d@strongswan.org",
388 id
= identification_create_from_string(buf
);
392 id
= identification_create_from_encoding(ID_ANY
, chunk_empty
);
396 else if (strpfx(str
, "xauth"))
397 { /* XAuth, use a username */
398 class = AUTH_CLASS_XAUTH
;
399 if (*(str
+ strlen("xauth")) == '-')
401 auth
->add(auth
, AUTH_RULE_XAUTH_BACKEND
, str
+ strlen("xauth-"));
407 snprintf(buf
, sizeof(buf
), "cli-%.6d-%.2d", num
, rnd
);
408 id
= identification_create_from_string(buf
);
412 id
= identification_create_from_encoding(ID_ANY
, chunk_empty
);
415 /* additionally set the ID as XAuth identity */
416 auth
->add(auth
, AUTH_RULE_XAUTH_IDENTITY
, id
->clone(id
));
420 if (!streq(str
, "pubkey"))
422 DBG1(DBG_CFG
, "invalid authentication: '%s', fallback to pubkey",
425 /* certificate authentication, use distinguished names */
426 class = AUTH_CLASS_PUBKEY
;
429 if ((local
&& !num
) || (!local
&& num
))
431 id
= identification_create_from_string(
432 "CN=srv, OU=load-test, O=strongSwan");
436 snprintf(buf
, sizeof(buf
),
437 "CN=c%d-r%d, OU=load-test, O=strongSwan", num
, rnd
);
438 id
= identification_create_from_string(buf
);
442 id
= identification_create_from_string(
443 "CN=*, OU=load-test, O=strongSwan");
447 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, class);
448 auth
->add(auth
, AUTH_RULE_IDENTITY
, id
);
449 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, local
);
451 enumerator
->destroy(enumerator
);
455 * Parse a protoport specifier
457 static bool parse_protoport(char *token
, uint16_t *from_port
,
458 uint16_t *to_port
, uint8_t *protocol
)
460 char *sep
, *port
= "", *endptr
;
461 struct protoent
*proto
;
465 sep
= strrchr(token
, ']');
472 sep
= strchr(token
, '/');
474 { /* protocol/port */
479 if (streq(token
, "%any"))
485 proto
= getprotobyname(token
);
488 *protocol
= proto
->p_proto
;
492 p
= strtol(token
, &endptr
, 0);
493 if ((*token
&& *endptr
) || p
< 0 || p
> 0xff)
497 *protocol
= (uint8_t)p
;
500 if (streq(port
, "%any"))
505 else if (streq(port
, "%opaque"))
510 else if (streq(port
, "%unique"))
512 *from_port
= *to_port
= 0;
516 svc
= getservbyname(port
, NULL
);
519 *from_port
= *to_port
= ntohs(svc
->s_port
);
523 p
= strtol(port
, &endptr
, 0);
524 if (p
< 0 || p
> 0xffff)
532 p
= strtol(port
, &endptr
, 0);
533 if (p
< 0 || p
> 0xffff)
549 * Add a TS from a string to a child_cfg
551 static void add_ts(private_load_tester_config_t
*this,
552 char *string
, child_cfg_t
*cfg
, bool local
, bool initiator
)
554 traffic_selector_t
*ts
;
558 enumerator_t
*enumerator
;
560 uint16_t from_port
, to_port
;
563 enumerator
= enumerator_create_token(string
, ",", " ");
564 while (enumerator
->enumerate(enumerator
, &subnet
))
570 pos
= strchr(subnet
, '[');
574 if (!parse_protoport(pos
, &from_port
, &to_port
, &proto
))
576 DBG1(DBG_CFG
, "invalid proto/port: %s, skipped subnet",
581 if (from_port
== 0 && to_port
== 0)
585 from_port
= this->unique_port
++;
586 from_port
= to_port
= max(from_port
, UNIQUE_PORT_START
);
589 { /* not supported as responder, use %any */
593 if (streq(subnet
, "%dynamic"))
595 ts
= traffic_selector_create_dynamic(proto
,
600 ts
= traffic_selector_create_from_cidr(subnet
, proto
,
605 cfg
->add_traffic_selector(cfg
, local
, ts
);
609 DBG1(DBG_CFG
, "invalid subnet: %s, skipped", subnet
);
612 enumerator
->destroy(enumerator
);
616 ts
= traffic_selector_create_dynamic(0, 0, 65535);
619 cfg
->add_traffic_selector(cfg
, local
, ts
);
625 * Allocate and install a dynamic external address to use
627 static host_t
*allocate_addr(private_load_tester_config_t
*this, uint num
)
629 enumerator_t
*enumerator
;
631 host_t
*found
= NULL
, *requested
;
632 identification_t
*id
;
633 char *iface
= NULL
, buf
[32];
636 requested
= host_create_any(AF_INET
);
637 snprintf(buf
, sizeof(buf
), "ext-%d", num
);
638 id
= identification_create_from_string(buf
);
639 enumerator
= this->pools
->create_enumerator(this->pools
);
640 while (enumerator
->enumerate(enumerator
, &pool
))
642 found
= pool
->acquire_address(pool
, id
, requested
, MEM_POOL_NEW
, NULL
);
645 iface
= (char*)pool
->get_name(pool
);
649 enumerator
->destroy(enumerator
);
650 requested
->destroy(requested
);
654 DBG1(DBG_CFG
, "no address found to install as load-tester external IP");
658 if (charon
->kernel
->add_ip(charon
->kernel
, found
, this->prefix
,
661 DBG1(DBG_CFG
, "installing load-tester IP %H on %s failed", found
, iface
);
662 found
->destroy(found
);
666 DBG1(DBG_CFG
, "installed load-tester IP %H on %s", found
, iface
);
668 .host
= found
->clone(found
),
671 this->mutex
->lock(this->mutex
);
672 entry
= this->leases
->put(this->leases
, entry
->host
, entry
);
673 this->mutex
->unlock(this->mutex
);
675 { /* shouldn't actually happen */
676 entry_destroy(entry
);
682 * Generate a new initiator config, num = 0 for responder config
684 static peer_cfg_t
* generate_config(private_load_tester_config_t
*this, uint num
)
687 child_cfg_t
*child_cfg
;
688 peer_cfg_t
*peer_cfg
;
691 ike_cfg_create_t ike
= {
692 .version
= this->version
,
693 .remote_port
= IKEV2_UDP_PORT
,
695 peer_cfg_create_t peer
= {
696 .cert_policy
= CERT_SEND_IF_ASKED
,
699 .rekey_time
= this->ike_rekey
,
700 .over_time
= this->ike_rekey
,
702 .dpd
= this->dpd_delay
,
703 .dpd_timeout
= this->dpd_timeout
,
705 child_cfg_create_t child
= {
708 .life
= this->child_rekey
* 2,
709 .rekey
= this->child_rekey
,
718 if (this->pools
->get_count(this->pools
))
719 { /* using dynamically installed external addresses */
720 addr
= allocate_addr(this, num
);
723 DBG1(DBG_CFG
, "allocating external address failed");
726 snprintf(local
, sizeof(local
), "%H", addr
);
731 snprintf(local
, sizeof(local
), "%s", this->initiator
);
733 ike
.remote
= this->responder
;
737 snprintf(local
, sizeof(local
), "%s", this->responder
);
738 ike
.remote
= this->initiator
;
742 if (this->port
&& num
)
744 ike
.local_port
= this->port
+ num
- 1;
745 ike
.remote_port
= IKEV2_NATT_PORT
;
749 ike
.local_port
= charon
->socket
->get_port(charon
->socket
, FALSE
);
751 ike_cfg
= ike_cfg_create(&ike
);
752 ike_cfg
->add_proposal(ike_cfg
, this->proposal
->clone(this->proposal
));
753 peer_cfg
= peer_cfg_create("load-test", ike_cfg
, &peer
);
757 peer_cfg
->add_virtual_ip(peer_cfg
, this->vip
->clone(this->vip
));
761 peer_cfg
->add_pool(peer_cfg
, this->pool
);
765 generate_auth_cfg(this, this->initiator_auth
, peer_cfg
, TRUE
, num
);
766 generate_auth_cfg(this, this->responder_auth
, peer_cfg
, FALSE
, num
);
770 generate_auth_cfg(this, this->responder_auth
, peer_cfg
, TRUE
, num
);
771 generate_auth_cfg(this, this->initiator_auth
, peer_cfg
, FALSE
, num
);
776 if (streq(this->mode
, "transport"))
778 child
.mode
= MODE_TRANSPORT
;
780 else if (streq(this->mode
, "beet"))
782 child
.mode
= MODE_BEET
;
786 child_cfg
= child_cfg_create("load-test", &child
);
787 child_cfg
->add_proposal(child_cfg
, this->esp
->clone(this->esp
));
793 add_ts(this, NULL
, child_cfg
, TRUE
, TRUE
);
797 add_ts(this, this->initiator_tsi
, child_cfg
, TRUE
, TRUE
);
799 add_ts(this, this->initiator_tsr
, child_cfg
, FALSE
, TRUE
);
803 add_ts(this, this->responder_tsr
, child_cfg
, TRUE
, FALSE
);
804 add_ts(this, this->responder_tsi
, child_cfg
, FALSE
, FALSE
);
806 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
);
810 METHOD(backend_t
, create_peer_cfg_enumerator
, enumerator_t
*,
811 private_load_tester_config_t
*this,
812 identification_t
*me
, identification_t
*other
)
814 return enumerator_create_single(this->peer_cfg
, NULL
);
817 METHOD(backend_t
, create_ike_cfg_enumerator
, enumerator_t
*,
818 private_load_tester_config_t
*this, host_t
*me
, host_t
*other
)
822 ike_cfg
= this->peer_cfg
->get_ike_cfg(this->peer_cfg
);
823 return enumerator_create_single(ike_cfg
, NULL
);
826 METHOD(backend_t
, get_peer_cfg_by_name
, peer_cfg_t
*,
827 private_load_tester_config_t
*this, char *name
)
829 if (streq(name
, "load-test"))
831 return generate_config(this, (u_int
)ref_get(&this->num
));
836 METHOD(load_tester_config_t
, delete_ip
, void,
837 private_load_tester_config_t
*this, host_t
*ip
)
839 enumerator_t
*enumerator
;
848 this->mutex
->lock(this->mutex
);
849 entry
= this->leases
->remove(this->leases
, ip
);
850 this->mutex
->unlock(this->mutex
);
854 enumerator
= this->pools
->create_enumerator(this->pools
);
855 while (enumerator
->enumerate(enumerator
, &pool
))
857 if (pool
->release_address(pool
, entry
->host
, entry
->id
))
859 charon
->kernel
->del_ip(charon
->kernel
, entry
->host
,
860 this->prefix
, FALSE
);
864 enumerator
->destroy(enumerator
);
865 entry_destroy(entry
);
870 * Clean up leases for allocated external addresses, if have been kept
872 static void cleanup_leases(private_load_tester_config_t
*this)
874 enumerator_t
*pools
, *leases
;
876 identification_t
*id
;
881 pools
= this->pools
->create_enumerator(this->pools
);
882 while (pools
->enumerate(pools
, &pool
))
884 leases
= pool
->create_lease_enumerator(pool
);
885 while (leases
->enumerate(leases
, &id
, &addr
, &online
))
889 charon
->kernel
->del_ip(charon
->kernel
, addr
, this->prefix
,
891 entry
= this->leases
->remove(this->leases
, addr
);
894 entry_destroy(entry
);
898 leases
->destroy(leases
);
900 pools
->destroy(pools
);
903 METHOD(load_tester_config_t
, destroy
, void,
904 private_load_tester_config_t
*this)
908 cleanup_leases(this);
910 this->mutex
->destroy(this->mutex
);
911 this->leases
->destroy(this->leases
);
912 this->pools
->destroy_offset(this->pools
, offsetof(mem_pool_t
, destroy
));
913 this->peer_cfg
->destroy(this->peer_cfg
);
914 DESTROY_IF(this->proposal
);
915 DESTROY_IF(this->esp
);
916 DESTROY_IF(this->vip
);
921 * Described in header.
923 load_tester_config_t
*load_tester_config_create()
925 private_load_tester_config_t
*this;
930 .create_peer_cfg_enumerator
= _create_peer_cfg_enumerator
,
931 .create_ike_cfg_enumerator
= _create_ike_cfg_enumerator
,
932 .get_peer_cfg_by_name
= _get_peer_cfg_by_name
,
934 .delete_ip
= _delete_ip
,
937 .pools
= linked_list_create(),
938 .leases
= hashtable_create((hashtable_hash_t
)hash
,
939 (hashtable_equals_t
)equals
, 256),
940 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
941 .unique_port
= UNIQUE_PORT_START
,
944 if (lib
->settings
->get_bool(lib
->settings
,
945 "%s.plugins.load-tester.request_virtual_ip", FALSE
, lib
->ns
))
947 this->vip
= host_create_from_string("0.0.0.0", 0);
949 this->pool
= lib
->settings
->get_str(lib
->settings
,
950 "%s.plugins.load-tester.pool", NULL
, lib
->ns
);
951 this->initiator
= lib
->settings
->get_str(lib
->settings
,
952 "%s.plugins.load-tester.initiator", "0.0.0.0", lib
->ns
);
953 this->responder
= lib
->settings
->get_str(lib
->settings
,
954 "%s.plugins.load-tester.responder", "127.0.0.1", lib
->ns
);
956 this->proposal
= proposal_create_from_string(PROTO_IKE
,
957 lib
->settings
->get_str(lib
->settings
,
958 "%s.plugins.load-tester.proposal", "aes128-sha1-modp768",
962 this->proposal
= proposal_create_from_string(PROTO_IKE
,
963 "aes128-sha1-modp768");
965 this->esp
= proposal_create_from_string(PROTO_ESP
,
966 lib
->settings
->get_str(lib
->settings
,
967 "%s.plugins.load-tester.esp", "aes128-sha1", lib
->ns
));
970 this->esp
= proposal_create_from_string(PROTO_ESP
, "aes128-sha1");
973 this->ike_rekey
= lib
->settings
->get_int(lib
->settings
,
974 "%s.plugins.load-tester.ike_rekey", 0, lib
->ns
);
975 this->child_rekey
= lib
->settings
->get_int(lib
->settings
,
976 "%s.plugins.load-tester.child_rekey", 600, lib
->ns
);
977 this->dpd_delay
= lib
->settings
->get_int(lib
->settings
,
978 "%s.plugins.load-tester.dpd_delay", 0, lib
->ns
);
979 this->dpd_timeout
= lib
->settings
->get_int(lib
->settings
,
980 "%s.plugins.load-tester.dpd_timeout", 0, lib
->ns
);
982 this->initiator_auth
= lib
->settings
->get_str(lib
->settings
,
983 "%s.plugins.load-tester.initiator_auth", "pubkey", lib
->ns
);
984 this->responder_auth
= lib
->settings
->get_str(lib
->settings
,
985 "%s.plugins.load-tester.responder_auth", "pubkey", lib
->ns
);
986 this->initiator_id
= lib
->settings
->get_str(lib
->settings
,
987 "%s.plugins.load-tester.initiator_id", NULL
, lib
->ns
);
988 this->initiator_match
= lib
->settings
->get_str(lib
->settings
,
989 "%s.plugins.load-tester.initiator_match", NULL
, lib
->ns
);
990 this->responder_id
= lib
->settings
->get_str(lib
->settings
,
991 "%s.plugins.load-tester.responder_id", NULL
, lib
->ns
);
993 this->mode
= lib
->settings
->get_str(lib
->settings
,
994 "%s.plugins.load-tester.mode", NULL
, lib
->ns
);
995 this->initiator_tsi
= lib
->settings
->get_str(lib
->settings
,
996 "%s.plugins.load-tester.initiator_tsi", NULL
, lib
->ns
);
997 this->responder_tsi
=lib
->settings
->get_str(lib
->settings
,
998 "%s.plugins.load-tester.responder_tsi",
999 this->initiator_tsi
, lib
->ns
);
1000 this->initiator_tsr
= lib
->settings
->get_str(lib
->settings
,
1001 "%s.plugins.load-tester.initiator_tsr", NULL
, lib
->ns
);
1002 this->responder_tsr
=lib
->settings
->get_str(lib
->settings
,
1003 "%s.plugins.load-tester.responder_tsr",
1004 this->initiator_tsr
, lib
->ns
);
1006 this->port
= lib
->settings
->get_int(lib
->settings
,
1007 "%s.plugins.load-tester.dynamic_port", 0, lib
->ns
);
1008 this->version
= lib
->settings
->get_int(lib
->settings
,
1009 "%s.plugins.load-tester.version", IKE_ANY
, lib
->ns
);
1013 this->peer_cfg
= generate_config(this, 0);
1015 return &this->public;