2 * Copyright (C) 2008 Martin Willi
3 * 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"
22 #include <attributes/mem_pool.h>
23 #include <collections/hashtable.h>
24 #include <threading/mutex.h>
26 #define UNIQUE_PORT_START 1025
28 typedef struct private_load_tester_config_t private_load_tester_config_t
;
31 * Private data of an load_tester_config_t object
33 struct private_load_tester_config_t
{
38 load_tester_config_t
public;
76 * Authentication method(s) to use/expect from initiator
81 * Authentication method(s) use/expected from responder
86 * Initiator ID to enforce
91 * Initiator ID to to match against as responder
93 char *initiator_match
;
96 * Responder ID to enforce
101 * IPsec mode, tunnel|transport|beet
106 * Traffic Selector on initiator side, as proposed from initiator
111 * Traffic Selector on responder side, as proposed from initiator
116 * Traffic Selector on initiator side, as narrowed by responder
121 * Traffic Selector on responder side, as narrowed by responder
126 * Current port for unique initiator ports
128 u_int16_t unique_port
;
131 * IKE_SA rekeying delay
136 * CHILD_SA rekeying delay
146 * DPD timeout (IKEv1 only)
151 * incremental numbering of generated configs
156 * Dynamic source port, if used
161 * IKE version to use for load testing
163 ike_version_t version
;
166 * List of pools to allocate external addresses dynamically, as mem_pool_t
168 linked_list_t
*pools
;
171 * Address prefix to use when installing dynamic addresses
176 * Keep addresses until shutdown?
181 * Hashtable with leases in "pools", host_t => entry_t
186 * Mutex for leases hashtable
195 /** host reference, equal to key */
197 /** associated identity */
198 identification_t
*id
;
204 static void entry_destroy(entry_t
*this)
206 this->host
->destroy(this->host
);
207 this->id
->destroy(this->id
);
212 * Hashtable hash function
214 static u_int
hash(host_t
*key
)
216 return chunk_hash(key
->get_address(key
));
220 * Hashtable equals function
222 static bool equals(host_t
*a
, host_t
*b
)
224 return a
->ip_equals(a
, b
);
228 * Load external addresses to use, if any
230 static void load_addrs(private_load_tester_config_t
*this)
232 enumerator_t
*enumerator
, *tokens
;
235 char *iface
, *token
, *pos
;
238 this->keep
= lib
->settings
->get_bool(lib
->settings
,
239 "%s.plugins.load-tester.addrs_keep", FALSE
, lib
->ns
);
240 this->prefix
= lib
->settings
->get_int(lib
->settings
,
241 "%s.plugins.load-tester.addrs_prefix", 16, lib
->ns
);
242 enumerator
= lib
->settings
->create_key_value_enumerator(lib
->settings
,
243 "%s.plugins.load-tester.addrs", lib
->ns
);
244 while (enumerator
->enumerate(enumerator
, &iface
, &token
))
246 tokens
= enumerator_create_token(token
, ",", " ");
247 while (tokens
->enumerate(tokens
, &token
))
249 pos
= strchr(token
, '-');
253 /* trim whitespace */
258 while (token
[strlen(token
) - 1] == ' ')
260 token
[strlen(token
) - 1] = '\0';
262 from
= host_create_from_string(token
, 0);
263 to
= host_create_from_string(pos
, 0);
266 pool
= mem_pool_create_range(iface
, from
, to
);
269 DBG1(DBG_CFG
, "loaded load-tester address range "
270 "%H-%H on %s", from
, to
, iface
);
271 this->pools
->insert_last(this->pools
, pool
);
278 DBG1(DBG_CFG
, "parsing load-tester address range %s-%s "
279 "failed, skipped", token
, pos
);
286 from
= host_create_from_subnet(token
, &bits
);
289 DBG1(DBG_CFG
, "loaded load-tester address pool %H/%d on %s",
291 pool
= mem_pool_create(iface
, from
, bits
);
293 this->pools
->insert_last(this->pools
, pool
);
297 DBG1(DBG_CFG
, "parsing load-tester address %s failed, "
302 tokens
->destroy(tokens
);
304 enumerator
->destroy(enumerator
);
308 * Generate auth config from string
310 static void generate_auth_cfg(private_load_tester_config_t
*this, char *str
,
311 peer_cfg_t
*peer_cfg
, bool local
, int num
)
313 enumerator_t
*enumerator
;
315 identification_t
*id
;
321 enumerator
= enumerator_create_token(str
, "|", " ");
322 while (enumerator
->enumerate(enumerator
, &str
))
325 auth
= auth_cfg_create();
328 if (this->initiator_id
)
330 if (this->initiator_match
&& (!local
&& !num
))
331 { /* as responder, use the secified identity that matches
332 * all used initiator identities, if given. */
333 snprintf(buf
, sizeof(buf
), this->initiator_match
, rnd
);
334 id
= identification_create_from_string(buf
);
336 else if ((local
&& num
) || (!local
&& !num
))
337 { /* as initiator, create peer specific identities */
338 snprintf(buf
, sizeof(buf
), this->initiator_id
, num
, rnd
);
339 id
= identification_create_from_string(buf
);
342 if (this->responder_id
)
344 if ((local
&& !num
) || (!local
&& num
))
346 snprintf(buf
, sizeof(buf
), this->responder_id
, num
, rnd
);
347 id
= identification_create_from_string(buf
);
351 if (streq(str
, "psk"))
352 { /* PSK authentication, use FQDNs */
353 class = AUTH_CLASS_PSK
;
356 if ((local
&& !num
) || (!local
&& num
))
358 id
= identification_create_from_string("srv.strongswan.org");
362 snprintf(buf
, sizeof(buf
), "c%d-r%d.strongswan.org",
364 id
= identification_create_from_string(buf
);
368 id
= identification_create_from_string("*.strongswan.org");
372 else if (strpfx(str
, "eap"))
373 { /* EAP authentication, use a NAI */
374 class = AUTH_CLASS_EAP
;
375 if (*(str
+ strlen("eap")) == '-')
377 type
= eap_type_from_string(str
+ strlen("eap-"));
380 auth
->add(auth
, AUTH_RULE_EAP_TYPE
, type
);
387 snprintf(buf
, sizeof(buf
), "1%.10d%.4d@strongswan.org",
389 id
= identification_create_from_string(buf
);
393 id
= identification_create_from_encoding(ID_ANY
, chunk_empty
);
399 if (!streq(str
, "pubkey"))
401 DBG1(DBG_CFG
, "invalid authentication: '%s', fallback to pubkey",
404 /* certificate authentication, use distinguished names */
405 class = AUTH_CLASS_PUBKEY
;
408 if ((local
&& !num
) || (!local
&& num
))
410 id
= identification_create_from_string(
411 "CN=srv, OU=load-test, O=strongSwan");
415 snprintf(buf
, sizeof(buf
),
416 "CN=c%d-r%d, OU=load-test, O=strongSwan", num
, rnd
);
417 id
= identification_create_from_string(buf
);
421 id
= identification_create_from_string(
422 "CN=*, OU=load-test, O=strongSwan");
426 auth
->add(auth
, AUTH_RULE_AUTH_CLASS
, class);
427 auth
->add(auth
, AUTH_RULE_IDENTITY
, id
);
428 peer_cfg
->add_auth_cfg(peer_cfg
, auth
, local
);
430 enumerator
->destroy(enumerator
);
434 * Parse a protoport specifier
436 static bool parse_protoport(char *token
, u_int16_t
*from_port
,
437 u_int16_t
*to_port
, u_int8_t
*protocol
)
439 char *sep
, *port
= "", *endptr
;
440 struct protoent
*proto
;
444 sep
= strrchr(token
, ']');
451 sep
= strchr(token
, '/');
453 { /* protocol/port */
458 if (streq(token
, "%any"))
464 proto
= getprotobyname(token
);
467 *protocol
= proto
->p_proto
;
471 p
= strtol(token
, &endptr
, 0);
472 if ((*token
&& *endptr
) || p
< 0 || p
> 0xff)
476 *protocol
= (u_int8_t
)p
;
479 if (streq(port
, "%any"))
484 else if (streq(port
, "%opaque"))
489 else if (streq(port
, "%unique"))
491 *from_port
= *to_port
= 0;
495 svc
= getservbyname(port
, NULL
);
498 *from_port
= *to_port
= ntohs(svc
->s_port
);
502 p
= strtol(port
, &endptr
, 0);
503 if (p
< 0 || p
> 0xffff)
511 p
= strtol(port
, &endptr
, 0);
512 if (p
< 0 || p
> 0xffff)
528 * Add a TS from a string to a child_cfg
530 static void add_ts(private_load_tester_config_t
*this,
531 char *string
, child_cfg_t
*cfg
, bool local
, bool initiator
)
533 traffic_selector_t
*ts
;
537 enumerator_t
*enumerator
;
539 u_int16_t from_port
, to_port
;
542 enumerator
= enumerator_create_token(string
, ",", " ");
543 while (enumerator
->enumerate(enumerator
, &subnet
))
549 pos
= strchr(subnet
, '[');
553 if (!parse_protoport(pos
, &from_port
, &to_port
, &proto
))
555 DBG1(DBG_CFG
, "invalid proto/port: %s, skipped subnet",
560 if (from_port
== 0 && to_port
== 0)
564 from_port
= this->unique_port
++;
565 from_port
= to_port
= max(from_port
, UNIQUE_PORT_START
);
568 { /* not supported as responder, use %any */
572 if (streq(subnet
, "%dynamic"))
574 ts
= traffic_selector_create_dynamic(proto
,
579 ts
= traffic_selector_create_from_cidr(subnet
, proto
,
584 cfg
->add_traffic_selector(cfg
, local
, ts
);
588 DBG1(DBG_CFG
, "invalid subnet: %s, skipped", subnet
);
591 enumerator
->destroy(enumerator
);
595 ts
= traffic_selector_create_dynamic(0, 0, 65535);
598 cfg
->add_traffic_selector(cfg
, local
, ts
);
604 * Allocate and install a dynamic external address to use
606 static host_t
*allocate_addr(private_load_tester_config_t
*this, uint num
)
608 enumerator_t
*enumerator
;
610 host_t
*found
= NULL
, *requested
;
611 identification_t
*id
;
612 char *iface
= NULL
, buf
[32];
615 requested
= host_create_any(AF_INET
);
616 snprintf(buf
, sizeof(buf
), "ext-%d", num
);
617 id
= identification_create_from_string(buf
);
618 enumerator
= this->pools
->create_enumerator(this->pools
);
619 while (enumerator
->enumerate(enumerator
, &pool
))
621 found
= pool
->acquire_address(pool
, id
, requested
, MEM_POOL_NEW
);
624 iface
= (char*)pool
->get_name(pool
);
628 enumerator
->destroy(enumerator
);
629 requested
->destroy(requested
);
633 DBG1(DBG_CFG
, "no address found to install as load-tester external IP");
637 if (hydra
->kernel_interface
->add_ip(hydra
->kernel_interface
,
638 found
, this->prefix
, iface
) != SUCCESS
)
640 DBG1(DBG_CFG
, "installing load-tester IP %H on %s failed", found
, iface
);
641 found
->destroy(found
);
645 DBG1(DBG_CFG
, "installed load-tester IP %H on %s", found
, iface
);
647 .host
= found
->clone(found
),
650 this->mutex
->lock(this->mutex
);
651 entry
= this->leases
->put(this->leases
, entry
->host
, entry
);
652 this->mutex
->unlock(this->mutex
);
654 { /* shouldn't actually happen */
655 entry_destroy(entry
);
661 * Generate a new initiator config, num = 0 for responder config
663 static peer_cfg_t
* generate_config(private_load_tester_config_t
*this, uint num
)
666 child_cfg_t
*child_cfg
;
667 peer_cfg_t
*peer_cfg
;
668 char local
[32], *remote
;
670 ipsec_mode_t mode
= MODE_TUNNEL
;
671 lifetime_cfg_t lifetime
= {
673 .life
= this->child_rekey
* 2,
674 .rekey
= this->child_rekey
,
681 if (this->pools
->get_count(this->pools
))
682 { /* using dynamically installed external addresses */
683 addr
= allocate_addr(this, num
);
686 DBG1(DBG_CFG
, "allocating external address failed");
689 snprintf(local
, sizeof(local
), "%H", addr
);
694 snprintf(local
, sizeof(local
), "%s", this->initiator
);
696 remote
= this->responder
;
700 snprintf(local
, sizeof(local
), "%s", this->responder
);
701 remote
= this->initiator
;
704 if (this->port
&& num
)
706 ike_cfg
= ike_cfg_create(this->version
, TRUE
, FALSE
,
707 local
, this->port
+ num
- 1,
708 remote
, IKEV2_NATT_PORT
,
709 FRAGMENTATION_NO
, 0);
713 ike_cfg
= ike_cfg_create(this->version
, TRUE
, FALSE
, local
,
714 charon
->socket
->get_port(charon
->socket
, FALSE
),
715 remote
, IKEV2_UDP_PORT
,
716 FRAGMENTATION_NO
, 0);
718 ike_cfg
->add_proposal(ike_cfg
, this->proposal
->clone(this->proposal
));
719 peer_cfg
= peer_cfg_create("load-test", ike_cfg
,
720 CERT_SEND_IF_ASKED
, UNIQUE_NO
, 1, /* keytries */
721 this->ike_rekey
, 0, /* rekey, reauth */
722 0, this->ike_rekey
, /* jitter, overtime */
723 FALSE
, FALSE
, TRUE
, /* mobike, aggressive, pull */
724 this->dpd_delay
, /* dpd_delay */
725 this->dpd_timeout
, /* dpd_timeout */
729 peer_cfg
->add_virtual_ip(peer_cfg
, this->vip
->clone(this->vip
));
733 peer_cfg
->add_pool(peer_cfg
, this->pool
);
737 generate_auth_cfg(this, this->initiator_auth
, peer_cfg
, TRUE
, num
);
738 generate_auth_cfg(this, this->responder_auth
, peer_cfg
, FALSE
, num
);
742 generate_auth_cfg(this, this->responder_auth
, peer_cfg
, TRUE
, num
);
743 generate_auth_cfg(this, this->initiator_auth
, peer_cfg
, FALSE
, num
);
748 if (streq(this->mode
, "transport"))
750 mode
= MODE_TRANSPORT
;
752 else if (streq(this->mode
, "beet"))
758 child_cfg
= child_cfg_create("load-test", &lifetime
, NULL
, TRUE
, mode
,
759 ACTION_NONE
, ACTION_NONE
, ACTION_NONE
, FALSE
,
760 0, 0, NULL
, NULL
, 0);
761 child_cfg
->add_proposal(child_cfg
, this->esp
->clone(this->esp
));
767 add_ts(this, NULL
, child_cfg
, TRUE
, TRUE
);
771 add_ts(this, this->initiator_tsi
, child_cfg
, TRUE
, TRUE
);
773 add_ts(this, this->initiator_tsr
, child_cfg
, FALSE
, TRUE
);
777 add_ts(this, this->responder_tsr
, child_cfg
, TRUE
, FALSE
);
778 add_ts(this, this->responder_tsi
, child_cfg
, FALSE
, FALSE
);
780 peer_cfg
->add_child_cfg(peer_cfg
, child_cfg
);
784 METHOD(backend_t
, create_peer_cfg_enumerator
, enumerator_t
*,
785 private_load_tester_config_t
*this,
786 identification_t
*me
, identification_t
*other
)
788 return enumerator_create_single(this->peer_cfg
, NULL
);
791 METHOD(backend_t
, create_ike_cfg_enumerator
, enumerator_t
*,
792 private_load_tester_config_t
*this, host_t
*me
, host_t
*other
)
796 ike_cfg
= this->peer_cfg
->get_ike_cfg(this->peer_cfg
);
797 return enumerator_create_single(ike_cfg
, NULL
);
800 METHOD(backend_t
, get_peer_cfg_by_name
, peer_cfg_t
*,
801 private_load_tester_config_t
*this, char *name
)
803 if (streq(name
, "load-test"))
805 return generate_config(this, this->num
++);
810 METHOD(load_tester_config_t
, delete_ip
, void,
811 private_load_tester_config_t
*this, host_t
*ip
)
813 enumerator_t
*enumerator
;
822 this->mutex
->lock(this->mutex
);
823 entry
= this->leases
->remove(this->leases
, ip
);
824 this->mutex
->unlock(this->mutex
);
828 enumerator
= this->pools
->create_enumerator(this->pools
);
829 while (enumerator
->enumerate(enumerator
, &pool
))
831 if (pool
->release_address(pool
, entry
->host
, entry
->id
))
833 hydra
->kernel_interface
->del_ip(hydra
->kernel_interface
,
834 entry
->host
, this->prefix
, FALSE
);
838 enumerator
->destroy(enumerator
);
839 entry_destroy(entry
);
844 * Clean up leases for allocated external addresses, if have been kept
846 static void cleanup_leases(private_load_tester_config_t
*this)
848 enumerator_t
*pools
, *leases
;
850 identification_t
*id
;
855 pools
= this->pools
->create_enumerator(this->pools
);
856 while (pools
->enumerate(pools
, &pool
))
858 leases
= pool
->create_lease_enumerator(pool
);
859 while (leases
->enumerate(leases
, &id
, &addr
, &online
))
863 hydra
->kernel_interface
->del_ip(hydra
->kernel_interface
,
864 addr
, this->prefix
, FALSE
);
865 entry
= this->leases
->remove(this->leases
, addr
);
868 entry_destroy(entry
);
872 leases
->destroy(leases
);
874 pools
->destroy(pools
);
877 METHOD(load_tester_config_t
, destroy
, void,
878 private_load_tester_config_t
*this)
882 cleanup_leases(this);
884 this->mutex
->destroy(this->mutex
);
885 this->leases
->destroy(this->leases
);
886 this->pools
->destroy_offset(this->pools
, offsetof(mem_pool_t
, destroy
));
887 this->peer_cfg
->destroy(this->peer_cfg
);
888 DESTROY_IF(this->proposal
);
889 DESTROY_IF(this->esp
);
890 DESTROY_IF(this->vip
);
895 * Described in header.
897 load_tester_config_t
*load_tester_config_create()
899 private_load_tester_config_t
*this;
904 .create_peer_cfg_enumerator
= _create_peer_cfg_enumerator
,
905 .create_ike_cfg_enumerator
= _create_ike_cfg_enumerator
,
906 .get_peer_cfg_by_name
= _get_peer_cfg_by_name
,
908 .delete_ip
= _delete_ip
,
911 .pools
= linked_list_create(),
912 .leases
= hashtable_create((hashtable_hash_t
)hash
,
913 (hashtable_equals_t
)equals
, 256),
914 .mutex
= mutex_create(MUTEX_TYPE_DEFAULT
),
916 .unique_port
= UNIQUE_PORT_START
,
919 if (lib
->settings
->get_bool(lib
->settings
,
920 "%s.plugins.load-tester.request_virtual_ip", FALSE
, lib
->ns
))
922 this->vip
= host_create_from_string("0.0.0.0", 0);
924 this->pool
= lib
->settings
->get_str(lib
->settings
,
925 "%s.plugins.load-tester.pool", NULL
, lib
->ns
);
926 this->initiator
= lib
->settings
->get_str(lib
->settings
,
927 "%s.plugins.load-tester.initiator", "0.0.0.0", lib
->ns
);
928 this->responder
= lib
->settings
->get_str(lib
->settings
,
929 "%s.plugins.load-tester.responder", "127.0.0.1", lib
->ns
);
931 this->proposal
= proposal_create_from_string(PROTO_IKE
,
932 lib
->settings
->get_str(lib
->settings
,
933 "%s.plugins.load-tester.proposal", "aes128-sha1-modp768",
937 this->proposal
= proposal_create_from_string(PROTO_IKE
,
938 "aes128-sha1-modp768");
940 this->esp
= proposal_create_from_string(PROTO_ESP
,
941 lib
->settings
->get_str(lib
->settings
,
942 "%s.plugins.load-tester.esp", "aes128-sha1", lib
->ns
));
945 this->esp
= proposal_create_from_string(PROTO_ESP
, "aes128-sha1");
948 this->ike_rekey
= lib
->settings
->get_int(lib
->settings
,
949 "%s.plugins.load-tester.ike_rekey", 0, lib
->ns
);
950 this->child_rekey
= lib
->settings
->get_int(lib
->settings
,
951 "%s.plugins.load-tester.child_rekey", 600, lib
->ns
);
952 this->dpd_delay
= lib
->settings
->get_int(lib
->settings
,
953 "%s.plugins.load-tester.dpd_delay", 0, lib
->ns
);
954 this->dpd_timeout
= lib
->settings
->get_int(lib
->settings
,
955 "%s.plugins.load-tester.dpd_timeout", 0, lib
->ns
);
957 this->initiator_auth
= lib
->settings
->get_str(lib
->settings
,
958 "%s.plugins.load-tester.initiator_auth", "pubkey", lib
->ns
);
959 this->responder_auth
= lib
->settings
->get_str(lib
->settings
,
960 "%s.plugins.load-tester.responder_auth", "pubkey", lib
->ns
);
961 this->initiator_id
= lib
->settings
->get_str(lib
->settings
,
962 "%s.plugins.load-tester.initiator_id", NULL
, lib
->ns
);
963 this->initiator_match
= lib
->settings
->get_str(lib
->settings
,
964 "%s.plugins.load-tester.initiator_match", NULL
, lib
->ns
);
965 this->responder_id
= lib
->settings
->get_str(lib
->settings
,
966 "%s.plugins.load-tester.responder_id", NULL
, lib
->ns
);
968 this->mode
= lib
->settings
->get_str(lib
->settings
,
969 "%s.plugins.load-tester.mode", NULL
, lib
->ns
);
970 this->initiator_tsi
= lib
->settings
->get_str(lib
->settings
,
971 "%s.plugins.load-tester.initiator_tsi", NULL
, lib
->ns
);
972 this->responder_tsi
=lib
->settings
->get_str(lib
->settings
,
973 "%s.plugins.load-tester.responder_tsi",
974 this->initiator_tsi
, lib
->ns
);
975 this->initiator_tsr
= lib
->settings
->get_str(lib
->settings
,
976 "%s.plugins.load-tester.initiator_tsr", NULL
, lib
->ns
);
977 this->responder_tsr
=lib
->settings
->get_str(lib
->settings
,
978 "%s.plugins.load-tester.responder_tsr",
979 this->initiator_tsr
, lib
->ns
);
981 this->port
= lib
->settings
->get_int(lib
->settings
,
982 "%s.plugins.load-tester.dynamic_port", 0, lib
->ns
);
983 this->version
= lib
->settings
->get_int(lib
->settings
,
984 "%s.plugins.load-tester.version", IKE_ANY
, lib
->ns
);
988 this->peer_cfg
= generate_config(this, 0);
990 return &this->public;