744eda58fb5b5979eac6c91f44a173a6ae03c9f2
[strongswan.git] / src / libcharon / plugins / load_tester / load_tester_config.c
1 /*
2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
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>.
9 *
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
13 * for more details.
14 */
15
16 #include "load_tester_config.h"
17
18 #include <daemon.h>
19 #include <hydra.h>
20 #include <attributes/mem_pool.h>
21 #include <collections/hashtable.h>
22 #include <threading/mutex.h>
23
24 typedef struct private_load_tester_config_t private_load_tester_config_t;
25
26 /**
27 * Private data of an load_tester_config_t object
28 */
29 struct private_load_tester_config_t {
30
31 /**
32 * Public part
33 */
34 load_tester_config_t public;
35
36 /**
37 * peer config
38 */
39 peer_cfg_t *peer_cfg;
40
41 /**
42 * virtual IP, if any
43 */
44 host_t *vip;
45
46 /**
47 * Initiator address
48 */
49 char *initiator;
50
51 /**
52 * Responder address
53 */
54 char *responder;
55
56 /**
57 * IP address pool
58 */
59 char *pool;
60
61 /**
62 * IKE proposal
63 */
64 proposal_t *proposal;
65
66 /**
67 * Authentication method(s) to use/expect from initiator
68 */
69 char *initiator_auth;
70
71 /**
72 * Authentication method(s) use/expected from responder
73 */
74 char *responder_auth;
75
76 /**
77 * Initiator ID to enforce
78 */
79 char *initiator_id;
80
81 /**
82 * Initiator ID to to match against as responder
83 */
84 char *initiator_match;
85
86 /**
87 * Responder ID to enforce
88 */
89 char *responder_id;
90
91 /**
92 * Traffic Selector on initiator side, as proposed from initiator
93 */
94 char *initiator_tsi;
95
96 /**
97 * Traffic Selector on responder side, as proposed from initiator
98 */
99 char *initiator_tsr;
100
101 /**
102 * Traffic Selector on initiator side, as narrowed by responder
103 */
104 char *responder_tsi;
105
106 /**
107 * Traffic Selector on responder side, as narrowed by responder
108 */
109 char *responder_tsr;
110
111 /**
112 * IKE_SA rekeying delay
113 */
114 u_int ike_rekey;
115
116 /**
117 * CHILD_SA rekeying delay
118 */
119 u_int child_rekey;
120
121 /**
122 * DPD check delay
123 */
124 u_int dpd_delay;
125
126 /**
127 * DPD timeout (IKEv1 only)
128 */
129 u_int dpd_timeout;
130
131 /**
132 * incremental numbering of generated configs
133 */
134 u_int num;
135
136 /**
137 * Dynamic source port, if used
138 */
139 u_int16_t port;
140
141 /**
142 * IKE version to use for load testing
143 */
144 ike_version_t version;
145
146 /**
147 * List of pools to allocate external addresses dynamically, as mem_pool_t
148 */
149 linked_list_t *pools;
150
151 /**
152 * Address prefix to use when installing dynamic addresses
153 */
154 int prefix;
155
156 /**
157 * Hashtable with leases in "pools", host_t => entry_t
158 */
159 hashtable_t *leases;
160
161 /**
162 * Mutex for leases hashtable
163 */
164 mutex_t *mutex;
165 };
166
167 /**
168 * Lease entry
169 */
170 typedef struct {
171 /** host reference, equal to key */
172 host_t *host;
173 /** associated identity */
174 identification_t *id;
175 } entry_t;
176
177 /**
178 * Destroy an entry_t
179 */
180 static void entry_destroy(entry_t *this)
181 {
182 this->host->destroy(this->host);
183 this->id->destroy(this->id);
184 free(this);
185 }
186
187 /**
188 * Hashtable hash function
189 */
190 static u_int hash(host_t *key)
191 {
192 return chunk_hash(key->get_address(key));
193 }
194
195 /**
196 * Hashtable equals function
197 */
198 static bool equals(host_t *a, host_t *b)
199 {
200 return a->ip_equals(a, b);
201 }
202
203 /**
204 * Load external addresses to use, if any
205 */
206 static void load_addrs(private_load_tester_config_t *this)
207 {
208 enumerator_t *enumerator, *tokens;
209 host_t *from, *to;
210 int bits;
211 char *iface, *token, *pos;
212 mem_pool_t *pool;
213
214 this->prefix = lib->settings->get_int(lib->settings,
215 "%s.plugins.load-tester.addrs_prefix", 16, charon->name);
216 enumerator = lib->settings->create_key_value_enumerator(lib->settings,
217 "%s.plugins.load-tester.addrs", charon->name);
218 while (enumerator->enumerate(enumerator, &iface, &token))
219 {
220 tokens = enumerator_create_token(token, ",", " ");
221 while (tokens->enumerate(tokens, &token))
222 {
223 pos = strchr(token, '-');
224 if (pos)
225 { /* range */
226 *(pos++) = '\0';
227 /* trim whitespace */
228 while (*pos == ' ')
229 {
230 pos++;
231 }
232 while (token[strlen(token) - 1] == ' ')
233 {
234 token[strlen(token) - 1] = '\0';
235 }
236 from = host_create_from_string(token, 0);
237 to = host_create_from_string(pos, 0);
238 if (from && to)
239 {
240 pool = mem_pool_create_range(iface, from, to);
241 if (pool)
242 {
243 DBG1(DBG_CFG, "loaded load-tester address range "
244 "%H-%H on %s", from, to, iface);
245 this->pools->insert_last(this->pools, pool);
246 }
247 from->destroy(from);
248 to->destroy(to);
249 }
250 else
251 {
252 DBG1(DBG_CFG, "parsing load-tester address range %s-%s "
253 "failed, skipped", token, pos);
254 DESTROY_IF(from);
255 DESTROY_IF(to);
256 }
257 }
258 else
259 { /* subnet */
260 from = host_create_from_subnet(token, &bits);
261 if (from)
262 {
263 DBG1(DBG_CFG, "loaded load-tester address pool %H/%d on %s",
264 from, bits, iface);
265 pool = mem_pool_create(iface, from, bits);
266 from->destroy(from);
267 this->pools->insert_last(this->pools, pool);
268 }
269 else
270 {
271 DBG1(DBG_CFG, "parsing load-tester address %s failed, "
272 "skipped", token);
273 }
274 }
275 }
276 tokens->destroy(tokens);
277 }
278 enumerator->destroy(enumerator);
279 }
280
281 /**
282 * Generate auth config from string
283 */
284 static void generate_auth_cfg(private_load_tester_config_t *this, char *str,
285 peer_cfg_t *peer_cfg, bool local, int num)
286 {
287 enumerator_t *enumerator;
288 auth_cfg_t *auth;
289 identification_t *id;
290 auth_class_t class;
291 eap_type_t type;
292 char buf[128];
293 int rnd = 0;
294
295 enumerator = enumerator_create_token(str, "|", " ");
296 while (enumerator->enumerate(enumerator, &str))
297 {
298 id = NULL;
299 auth = auth_cfg_create();
300 rnd++;
301
302 if (this->initiator_id)
303 {
304 if (this->initiator_match && (!local && !num))
305 { /* as responder, use the secified identity that matches
306 * all used initiator identities, if given. */
307 snprintf(buf, sizeof(buf), this->initiator_match, rnd);
308 id = identification_create_from_string(buf);
309 }
310 else if ((local && num) || (!local && !num))
311 { /* as initiator, create peer specific identities */
312 snprintf(buf, sizeof(buf), this->initiator_id, num, rnd);
313 id = identification_create_from_string(buf);
314 }
315 }
316 if (this->responder_id)
317 {
318 if ((local && !num) || (!local && num))
319 {
320 snprintf(buf, sizeof(buf), this->responder_id, num, rnd);
321 id = identification_create_from_string(buf);
322 }
323 }
324
325 if (streq(str, "psk"))
326 { /* PSK authentication, use FQDNs */
327 class = AUTH_CLASS_PSK;
328 if (!id)
329 {
330 if ((local && !num) || (!local && num))
331 {
332 id = identification_create_from_string("srv.strongswan.org");
333 }
334 else if (local)
335 {
336 snprintf(buf, sizeof(buf), "c%d-r%d.strongswan.org",
337 num, rnd);
338 id = identification_create_from_string(buf);
339 }
340 else
341 {
342 id = identification_create_from_string("*.strongswan.org");
343 }
344 }
345 }
346 else if (strneq(str, "eap", strlen("eap")))
347 { /* EAP authentication, use a NAI */
348 class = AUTH_CLASS_EAP;
349 if (*(str + strlen("eap")) == '-')
350 {
351 type = eap_type_from_string(str + strlen("eap-"));
352 if (type)
353 {
354 auth->add(auth, AUTH_RULE_EAP_TYPE, type);
355 }
356 }
357 if (!id)
358 {
359 if (local && num)
360 {
361 snprintf(buf, sizeof(buf), "1%.10d%.4d@strongswan.org",
362 num, rnd);
363 id = identification_create_from_string(buf);
364 }
365 else
366 {
367 id = identification_create_from_encoding(ID_ANY, chunk_empty);
368 }
369 }
370 }
371 else
372 {
373 if (!streq(str, "pubkey"))
374 {
375 DBG1(DBG_CFG, "invalid authentication: '%s', fallback to pubkey",
376 str);
377 }
378 /* certificate authentication, use distinguished names */
379 class = AUTH_CLASS_PUBKEY;
380 if (!id)
381 {
382 if ((local && !num) || (!local && num))
383 {
384 id = identification_create_from_string(
385 "CN=srv, OU=load-test, O=strongSwan");
386 }
387 else if (local)
388 {
389 snprintf(buf, sizeof(buf),
390 "CN=c%d-r%d, OU=load-test, O=strongSwan", num, rnd);
391 id = identification_create_from_string(buf);
392 }
393 else
394 {
395 id = identification_create_from_string(
396 "CN=*, OU=load-test, O=strongSwan");
397 }
398 }
399 }
400 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
401 auth->add(auth, AUTH_RULE_IDENTITY, id);
402 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
403 }
404 enumerator->destroy(enumerator);
405 }
406
407 /**
408 * Add a TS from a string to a child_cfg
409 */
410 static void add_ts(char *string, child_cfg_t *cfg, bool local)
411 {
412 traffic_selector_t *ts;
413
414 if (string)
415 {
416 ts = traffic_selector_create_from_cidr(string, 0, 0, 65535);
417 if (!ts)
418 {
419 DBG1(DBG_CFG, "parsing TS string '%s' failed", string);
420 }
421 }
422 else
423 {
424 ts = traffic_selector_create_dynamic(0, 0, 65535);
425 }
426 if (ts)
427 {
428 cfg->add_traffic_selector(cfg, local, ts);
429 }
430 }
431
432 /**
433 * Allocate and install a dynamic external address to use
434 */
435 static host_t *allocate_addr(private_load_tester_config_t *this, uint num)
436 {
437 enumerator_t *enumerator;
438 mem_pool_t *pool;
439 host_t *found = NULL, *requested;
440 identification_t *id;
441 char *iface = NULL, buf[32];
442 entry_t *entry;
443
444 requested = host_create_any(AF_INET);
445 snprintf(buf, sizeof(buf), "ext-%d", num);
446 id = identification_create_from_string(buf);
447 enumerator = this->pools->create_enumerator(this->pools);
448 while (enumerator->enumerate(enumerator, &pool))
449 {
450 found = pool->acquire_address(pool, id, requested, MEM_POOL_NEW);
451 if (found)
452 {
453 iface = (char*)pool->get_name(pool);
454 break;
455 }
456 }
457 enumerator->destroy(enumerator);
458 requested->destroy(requested);
459
460 if (!found)
461 {
462 DBG1(DBG_CFG, "no address found to install as load-tester external IP");
463 id->destroy(id);
464 return NULL;
465 }
466 if (hydra->kernel_interface->add_ip(hydra->kernel_interface,
467 found, this->prefix, iface) != SUCCESS)
468 {
469 DBG1(DBG_CFG, "installing load-tester IP %H on %s failed", found, iface);
470 found->destroy(found);
471 id->destroy(id);
472 return NULL;
473 }
474 DBG1(DBG_CFG, "installed load-tester IP %H on %s", found, iface);
475 INIT(entry,
476 .host = found->clone(found),
477 .id = id,
478 );
479 this->mutex->lock(this->mutex);
480 entry = this->leases->put(this->leases, entry->host, entry);
481 this->mutex->unlock(this->mutex);
482 if (entry)
483 { /* shouldn't actually happen */
484 entry_destroy(entry);
485 }
486 return found;
487 }
488
489 /**
490 * Generate a new initiator config, num = 0 for responder config
491 */
492 static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num)
493 {
494 ike_cfg_t *ike_cfg;
495 child_cfg_t *child_cfg;
496 peer_cfg_t *peer_cfg;
497 proposal_t *proposal;
498 char local[32], *remote;
499 host_t *addr;
500 lifetime_cfg_t lifetime = {
501 .time = {
502 .life = this->child_rekey * 2,
503 .rekey = this->child_rekey,
504 .jitter = 0
505 }
506 };
507
508 if (num)
509 { /* initiator */
510 if (this->pools->get_count(this->pools))
511 { /* using dynamically installed external addresses */
512 addr = allocate_addr(this, num);
513 if (!addr)
514 {
515 DBG1(DBG_CFG, "allocating external address failed");
516 return NULL;
517 }
518 snprintf(local, sizeof(local), "%H", addr);
519 addr->destroy(addr);
520 }
521 else
522 {
523 snprintf(local, sizeof(local), "%s", this->initiator);
524 }
525 remote = this->responder;
526 }
527 else
528 {
529 snprintf(local, sizeof(local), "%s", this->responder);
530 remote = this->initiator;
531 }
532
533 if (this->port && num)
534 {
535 ike_cfg = ike_cfg_create(this->version, TRUE, FALSE,
536 local, FALSE, this->port + num - 1,
537 remote, FALSE, IKEV2_NATT_PORT,
538 FRAGMENTATION_NO, 0);
539 }
540 else
541 {
542 ike_cfg = ike_cfg_create(this->version, TRUE, FALSE,
543 local, FALSE,
544 charon->socket->get_port(charon->socket, FALSE),
545 remote, FALSE, IKEV2_UDP_PORT,
546 FRAGMENTATION_NO, 0);
547 }
548 ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal));
549 peer_cfg = peer_cfg_create("load-test", ike_cfg,
550 CERT_SEND_IF_ASKED, UNIQUE_NO, 1, /* keytries */
551 this->ike_rekey, 0, /* rekey, reauth */
552 0, this->ike_rekey, /* jitter, overtime */
553 FALSE, FALSE, /* mobike, aggressive mode */
554 this->dpd_delay, /* dpd_delay */
555 this->dpd_timeout, /* dpd_timeout */
556 FALSE, NULL, NULL);
557 if (this->vip)
558 {
559 peer_cfg->add_virtual_ip(peer_cfg, this->vip->clone(this->vip));
560 }
561 if (this->pool)
562 {
563 peer_cfg->add_pool(peer_cfg, this->pool);
564 }
565 if (num)
566 { /* initiator */
567 generate_auth_cfg(this, this->initiator_auth, peer_cfg, TRUE, num);
568 generate_auth_cfg(this, this->responder_auth, peer_cfg, FALSE, num);
569 }
570 else
571 { /* responder */
572 generate_auth_cfg(this, this->responder_auth, peer_cfg, TRUE, num);
573 generate_auth_cfg(this, this->initiator_auth, peer_cfg, FALSE, num);
574 }
575
576 child_cfg = child_cfg_create("load-test", &lifetime, NULL, TRUE, MODE_TUNNEL,
577 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
578 0, 0, NULL, NULL, 0);
579 proposal = proposal_create_from_string(PROTO_ESP, "aes128-sha1");
580 child_cfg->add_proposal(child_cfg, proposal);
581
582 if (num)
583 { /* initiator */
584 if (this->vip)
585 {
586 add_ts(NULL, child_cfg, TRUE);
587 }
588 else
589 {
590 add_ts(this->initiator_tsi, child_cfg, TRUE);
591 }
592 add_ts(this->initiator_tsr, child_cfg, FALSE);
593 }
594 else
595 { /* responder */
596 add_ts(this->responder_tsr, child_cfg, TRUE);
597 add_ts(this->responder_tsi, child_cfg, FALSE);
598 }
599 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
600 return peer_cfg;
601 }
602
603 METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
604 private_load_tester_config_t *this,
605 identification_t *me, identification_t *other)
606 {
607 return enumerator_create_single(this->peer_cfg, NULL);
608 }
609
610 METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*,
611 private_load_tester_config_t *this, host_t *me, host_t *other)
612 {
613 ike_cfg_t *ike_cfg;
614
615 ike_cfg = this->peer_cfg->get_ike_cfg(this->peer_cfg);
616 return enumerator_create_single(ike_cfg, NULL);
617 }
618
619 METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*,
620 private_load_tester_config_t *this, char *name)
621 {
622 if (streq(name, "load-test"))
623 {
624 return generate_config(this, this->num++);
625 }
626 return NULL;
627 }
628
629 METHOD(load_tester_config_t, delete_ip, void,
630 private_load_tester_config_t *this, host_t *ip)
631 {
632 enumerator_t *enumerator;
633 mem_pool_t *pool;
634 entry_t *entry;
635
636 this->mutex->lock(this->mutex);
637 entry = this->leases->remove(this->leases, ip);
638 this->mutex->unlock(this->mutex);
639
640 if (entry)
641 {
642 enumerator = this->pools->create_enumerator(this->pools);
643 while (enumerator->enumerate(enumerator, &pool))
644 {
645 if (pool->release_address(pool, entry->host, entry->id))
646 {
647 hydra->kernel_interface->del_ip(hydra->kernel_interface,
648 entry->host, this->prefix, FALSE);
649 break;
650 }
651 }
652 enumerator->destroy(enumerator);
653 entry_destroy(entry);
654 }
655 }
656
657 METHOD(load_tester_config_t, destroy, void,
658 private_load_tester_config_t *this)
659 {
660 this->mutex->destroy(this->mutex);
661 this->leases->destroy(this->leases);
662 this->pools->destroy_offset(this->pools, offsetof(mem_pool_t, destroy));
663 this->peer_cfg->destroy(this->peer_cfg);
664 DESTROY_IF(this->proposal);
665 DESTROY_IF(this->vip);
666 free(this);
667 }
668
669 /**
670 * Described in header.
671 */
672 load_tester_config_t *load_tester_config_create()
673 {
674 private_load_tester_config_t *this;
675
676 INIT(this,
677 .public = {
678 .backend = {
679 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
680 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
681 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
682 },
683 .delete_ip = _delete_ip,
684 .destroy = _destroy,
685 },
686 .pools = linked_list_create(),
687 .leases = hashtable_create((hashtable_hash_t)hash,
688 (hashtable_equals_t)equals, 256),
689 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
690 .num = 1,
691 );
692
693 if (lib->settings->get_bool(lib->settings,
694 "%s.plugins.load-tester.request_virtual_ip", FALSE, charon->name))
695 {
696 this->vip = host_create_from_string("0.0.0.0", 0);
697 }
698 this->pool = lib->settings->get_str(lib->settings,
699 "%s.plugins.load-tester.pool", NULL, charon->name);
700 this->initiator = lib->settings->get_str(lib->settings,
701 "%s.plugins.load-tester.initiator", "0.0.0.0", charon->name);
702 this->responder = lib->settings->get_str(lib->settings,
703 "%s.plugins.load-tester.responder", "127.0.0.1", charon->name);
704
705 this->proposal = proposal_create_from_string(PROTO_IKE,
706 lib->settings->get_str(lib->settings,
707 "%s.plugins.load-tester.proposal", "aes128-sha1-modp768",
708 charon->name));
709 if (!this->proposal)
710 { /* fallback */
711 this->proposal = proposal_create_from_string(PROTO_IKE,
712 "aes128-sha1-modp768");
713 }
714 this->ike_rekey = lib->settings->get_int(lib->settings,
715 "%s.plugins.load-tester.ike_rekey", 0, charon->name);
716 this->child_rekey = lib->settings->get_int(lib->settings,
717 "%s.plugins.load-tester.child_rekey", 600, charon->name);
718 this->dpd_delay = lib->settings->get_int(lib->settings,
719 "%s.plugins.load-tester.dpd_delay", 0, charon->name);
720 this->dpd_timeout = lib->settings->get_int(lib->settings,
721 "%s.plugins.load-tester.dpd_timeout", 0, charon->name);
722
723 this->initiator_auth = lib->settings->get_str(lib->settings,
724 "%s.plugins.load-tester.initiator_auth", "pubkey", charon->name);
725 this->responder_auth = lib->settings->get_str(lib->settings,
726 "%s.plugins.load-tester.responder_auth", "pubkey", charon->name);
727 this->initiator_id = lib->settings->get_str(lib->settings,
728 "%s.plugins.load-tester.initiator_id", NULL, charon->name);
729 this->initiator_match = lib->settings->get_str(lib->settings,
730 "%s.plugins.load-tester.initiator_match", NULL, charon->name);
731 this->responder_id = lib->settings->get_str(lib->settings,
732 "%s.plugins.load-tester.responder_id", NULL, charon->name);
733
734 this->initiator_tsi = lib->settings->get_str(lib->settings,
735 "%s.plugins.load-tester.initiator_tsi", NULL, charon->name);
736 this->responder_tsi =lib->settings->get_str(lib->settings,
737 "%s.plugins.load-tester.responder_tsi",
738 this->initiator_tsi, charon->name);
739 this->initiator_tsr = lib->settings->get_str(lib->settings,
740 "%s.plugins.load-tester.initiator_tsr", NULL, charon->name);
741 this->responder_tsr =lib->settings->get_str(lib->settings,
742 "%s.plugins.load-tester.responder_tsr",
743 this->initiator_tsr, charon->name);
744
745 this->port = lib->settings->get_int(lib->settings,
746 "%s.plugins.load-tester.dynamic_port", 0, charon->name);
747 this->version = lib->settings->get_int(lib->settings,
748 "%s.plugins.load-tester.version", IKE_ANY, charon->name);
749
750 load_addrs(this);
751
752 this->peer_cfg = generate_config(this, 0);
753
754 return &this->public;
755 }