If load-tester requests a virtual IP, use a dynamic local traffic selector
[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;
209 host_t *net;
210 int bits;
211 char *iface, *cidr;
212 mem_pool_t *pool;
213
214
215 this->prefix = lib->settings->get_int(lib->settings,
216 "%s.plugins.load-tester.addrs_prefix", 16, charon->name);
217 enumerator = lib->settings->create_key_value_enumerator(lib->settings,
218 "%s.plugins.load-tester.addrs", charon->name);
219 while (enumerator->enumerate(enumerator, &iface, &cidr))
220 {
221 net = host_create_from_subnet(cidr, &bits);
222 if (net)
223 {
224 DBG1(DBG_CFG, "loaded load-tester addresses %s", cidr);
225 pool = mem_pool_create(iface, net, bits);
226 net->destroy(net);
227 this->pools->insert_last(this->pools, pool);
228 }
229 else
230 {
231 DBG1(DBG_CFG, "parsing load-tester addresses %s failed", cidr);
232 }
233 }
234 enumerator->destroy(enumerator);
235 }
236
237 /**
238 * Generate auth config from string
239 */
240 static void generate_auth_cfg(private_load_tester_config_t *this, char *str,
241 peer_cfg_t *peer_cfg, bool local, int num)
242 {
243 enumerator_t *enumerator;
244 auth_cfg_t *auth;
245 identification_t *id;
246 auth_class_t class;
247 eap_type_t type;
248 char buf[128];
249 int rnd = 0;
250
251 enumerator = enumerator_create_token(str, "|", " ");
252 while (enumerator->enumerate(enumerator, &str))
253 {
254 id = NULL;
255 auth = auth_cfg_create();
256 rnd++;
257
258 if (this->initiator_id)
259 {
260 if (this->initiator_match && (!local && !num))
261 { /* as responder, use the secified identity that matches
262 * all used initiator identities, if given. */
263 snprintf(buf, sizeof(buf), this->initiator_match, rnd);
264 id = identification_create_from_string(buf);
265 }
266 else if ((local && num) || (!local && !num))
267 { /* as initiator, create peer specific identities */
268 snprintf(buf, sizeof(buf), this->initiator_id, num, rnd);
269 id = identification_create_from_string(buf);
270 }
271 }
272 if (this->responder_id)
273 {
274 if ((local && !num) || (!local && num))
275 {
276 snprintf(buf, sizeof(buf), this->responder_id, num, rnd);
277 id = identification_create_from_string(buf);
278 }
279 }
280
281 if (streq(str, "psk"))
282 { /* PSK authentication, use FQDNs */
283 class = AUTH_CLASS_PSK;
284 if (!id)
285 {
286 if ((local && !num) || (!local && num))
287 {
288 id = identification_create_from_string("srv.strongswan.org");
289 }
290 else if (local)
291 {
292 snprintf(buf, sizeof(buf), "c%d-r%d.strongswan.org",
293 num, rnd);
294 id = identification_create_from_string(buf);
295 }
296 else
297 {
298 id = identification_create_from_string("*.strongswan.org");
299 }
300 }
301 }
302 else if (strneq(str, "eap", strlen("eap")))
303 { /* EAP authentication, use a NAI */
304 class = AUTH_CLASS_EAP;
305 if (*(str + strlen("eap")) == '-')
306 {
307 type = eap_type_from_string(str + strlen("eap-"));
308 if (type)
309 {
310 auth->add(auth, AUTH_RULE_EAP_TYPE, type);
311 }
312 }
313 if (!id)
314 {
315 if (local && num)
316 {
317 snprintf(buf, sizeof(buf), "1%.10d%.4d@strongswan.org",
318 num, rnd);
319 id = identification_create_from_string(buf);
320 }
321 else
322 {
323 id = identification_create_from_encoding(ID_ANY, chunk_empty);
324 }
325 }
326 }
327 else
328 {
329 if (!streq(str, "pubkey"))
330 {
331 DBG1(DBG_CFG, "invalid authentication: '%s', fallback to pubkey",
332 str);
333 }
334 /* certificate authentication, use distinguished names */
335 class = AUTH_CLASS_PUBKEY;
336 if (!id)
337 {
338 if ((local && !num) || (!local && num))
339 {
340 id = identification_create_from_string(
341 "CN=srv, OU=load-test, O=strongSwan");
342 }
343 else if (local)
344 {
345 snprintf(buf, sizeof(buf),
346 "CN=c%d-r%d, OU=load-test, O=strongSwan", num, rnd);
347 id = identification_create_from_string(buf);
348 }
349 else
350 {
351 id = identification_create_from_string(
352 "CN=*, OU=load-test, O=strongSwan");
353 }
354 }
355 }
356 auth->add(auth, AUTH_RULE_AUTH_CLASS, class);
357 auth->add(auth, AUTH_RULE_IDENTITY, id);
358 peer_cfg->add_auth_cfg(peer_cfg, auth, local);
359 }
360 enumerator->destroy(enumerator);
361 }
362
363 /**
364 * Add a TS from a string to a child_cfg
365 */
366 static void add_ts(char *string, child_cfg_t *cfg, bool local)
367 {
368 traffic_selector_t *ts;
369
370 if (string)
371 {
372 ts = traffic_selector_create_from_cidr(string, 0, 0);
373 if (!ts)
374 {
375 DBG1(DBG_CFG, "parsing TS string '%s' failed", string);
376 }
377 }
378 else
379 {
380 ts = traffic_selector_create_dynamic(0, 0, 65535);
381 }
382 if (ts)
383 {
384 cfg->add_traffic_selector(cfg, local, ts);
385 }
386 }
387
388 /**
389 * Allocate and install a dynamic external address to use
390 */
391 static host_t *allocate_addr(private_load_tester_config_t *this, uint num)
392 {
393 enumerator_t *enumerator;
394 mem_pool_t *pool;
395 host_t *found = NULL, *requested;
396 identification_t *id;
397 char *iface = NULL, buf[32];
398 entry_t *entry;
399
400 requested = host_create_any(AF_INET);
401 snprintf(buf, sizeof(buf), "ext-%d", num);
402 id = identification_create_from_string(buf);
403 enumerator = this->pools->create_enumerator(this->pools);
404 while (enumerator->enumerate(enumerator, &pool))
405 {
406 found = pool->acquire_address(pool, id, requested, MEM_POOL_NEW);
407 if (found)
408 {
409 iface = (char*)pool->get_name(pool);
410 break;
411 }
412 }
413 enumerator->destroy(enumerator);
414 requested->destroy(requested);
415
416 if (!found)
417 {
418 DBG1(DBG_CFG, "no address found to install as load-tester external IP");
419 id->destroy(id);
420 return NULL;
421 }
422 if (hydra->kernel_interface->add_ip(hydra->kernel_interface,
423 found, this->prefix, iface) != SUCCESS)
424 {
425 DBG1(DBG_CFG, "installing load-tester IP %H on %s failed", found, iface);
426 found->destroy(found);
427 id->destroy(id);
428 return NULL;
429 }
430 DBG1(DBG_CFG, "installed load-tester IP %H on %s", found, iface);
431 INIT(entry,
432 .host = found->clone(found),
433 .id = id,
434 );
435 this->mutex->lock(this->mutex);
436 entry = this->leases->put(this->leases, entry->host, entry);
437 this->mutex->unlock(this->mutex);
438 if (entry)
439 { /* shouldn't actually happen */
440 entry_destroy(entry);
441 }
442 return found;
443 }
444
445 /**
446 * Generate a new initiator config, num = 0 for responder config
447 */
448 static peer_cfg_t* generate_config(private_load_tester_config_t *this, uint num)
449 {
450 ike_cfg_t *ike_cfg;
451 child_cfg_t *child_cfg;
452 peer_cfg_t *peer_cfg;
453 proposal_t *proposal;
454 char local[32], *remote;
455 host_t *addr;
456 lifetime_cfg_t lifetime = {
457 .time = {
458 .life = this->child_rekey * 2,
459 .rekey = this->child_rekey,
460 .jitter = 0
461 }
462 };
463
464 if (num)
465 { /* initiator */
466 if (this->pools->get_count(this->pools))
467 { /* using dynamically installed external addresses */
468 addr = allocate_addr(this, num);
469 if (!addr)
470 {
471 DBG1(DBG_CFG, "allocating external address failed");
472 return NULL;
473 }
474 snprintf(local, sizeof(local), "%H", addr);
475 addr->destroy(addr);
476 }
477 else
478 {
479 snprintf(local, sizeof(local), "%s", this->initiator);
480 }
481 remote = this->responder;
482 }
483 else
484 {
485 snprintf(local, sizeof(local), "%s", this->responder);
486 remote = this->initiator;
487 }
488
489 if (this->port && num)
490 {
491 ike_cfg = ike_cfg_create(this->version, TRUE, FALSE,
492 local, FALSE, this->port + num - 1,
493 remote, FALSE, IKEV2_NATT_PORT);
494 }
495 else
496 {
497 ike_cfg = ike_cfg_create(this->version, TRUE, FALSE,
498 local, FALSE,
499 charon->socket->get_port(charon->socket, FALSE),
500 remote, FALSE, IKEV2_UDP_PORT);
501 }
502 ike_cfg->add_proposal(ike_cfg, this->proposal->clone(this->proposal));
503 peer_cfg = peer_cfg_create("load-test", ike_cfg,
504 CERT_SEND_IF_ASKED, UNIQUE_NO, 1, /* keytries */
505 this->ike_rekey, 0, /* rekey, reauth */
506 0, this->ike_rekey, /* jitter, overtime */
507 FALSE, FALSE, /* mobike, aggressive mode */
508 this->dpd_delay, /* dpd_delay */
509 this->dpd_timeout, /* dpd_timeout */
510 FALSE, NULL, NULL);
511 if (this->vip)
512 {
513 peer_cfg->add_virtual_ip(peer_cfg, this->vip->clone(this->vip));
514 }
515 if (this->pool)
516 {
517 peer_cfg->add_pool(peer_cfg, this->pool);
518 }
519 if (num)
520 { /* initiator */
521 generate_auth_cfg(this, this->initiator_auth, peer_cfg, TRUE, num);
522 generate_auth_cfg(this, this->responder_auth, peer_cfg, FALSE, num);
523 }
524 else
525 { /* responder */
526 generate_auth_cfg(this, this->responder_auth, peer_cfg, TRUE, num);
527 generate_auth_cfg(this, this->initiator_auth, peer_cfg, FALSE, num);
528 }
529
530 child_cfg = child_cfg_create("load-test", &lifetime, NULL, TRUE, MODE_TUNNEL,
531 ACTION_NONE, ACTION_NONE, ACTION_NONE, FALSE,
532 0, 0, NULL, NULL, 0);
533 proposal = proposal_create_from_string(PROTO_ESP, "aes128-sha1");
534 child_cfg->add_proposal(child_cfg, proposal);
535
536 if (num)
537 { /* initiator */
538 if (this->vip)
539 {
540 add_ts(NULL, child_cfg, TRUE);
541 }
542 else
543 {
544 add_ts(this->initiator_tsi, child_cfg, TRUE);
545 }
546 add_ts(this->initiator_tsr, child_cfg, FALSE);
547 }
548 else
549 { /* responder */
550 add_ts(this->responder_tsr, child_cfg, TRUE);
551 add_ts(this->responder_tsi, child_cfg, FALSE);
552 }
553 peer_cfg->add_child_cfg(peer_cfg, child_cfg);
554 return peer_cfg;
555 }
556
557 METHOD(backend_t, create_peer_cfg_enumerator, enumerator_t*,
558 private_load_tester_config_t *this,
559 identification_t *me, identification_t *other)
560 {
561 return enumerator_create_single(this->peer_cfg, NULL);
562 }
563
564 METHOD(backend_t, create_ike_cfg_enumerator, enumerator_t*,
565 private_load_tester_config_t *this, host_t *me, host_t *other)
566 {
567 ike_cfg_t *ike_cfg;
568
569 ike_cfg = this->peer_cfg->get_ike_cfg(this->peer_cfg);
570 return enumerator_create_single(ike_cfg, NULL);
571 }
572
573 METHOD(backend_t, get_peer_cfg_by_name, peer_cfg_t*,
574 private_load_tester_config_t *this, char *name)
575 {
576 if (streq(name, "load-test"))
577 {
578 return generate_config(this, this->num++);
579 }
580 return NULL;
581 }
582
583 METHOD(load_tester_config_t, delete_ip, void,
584 private_load_tester_config_t *this, host_t *ip)
585 {
586 enumerator_t *enumerator;
587 mem_pool_t *pool;
588 entry_t *entry;
589
590 this->mutex->lock(this->mutex);
591 entry = this->leases->remove(this->leases, ip);
592 this->mutex->unlock(this->mutex);
593
594 if (entry)
595 {
596 enumerator = this->pools->create_enumerator(this->pools);
597 while (enumerator->enumerate(enumerator, &pool))
598 {
599 if (pool->release_address(pool, entry->host, entry->id))
600 {
601 hydra->kernel_interface->del_ip(hydra->kernel_interface,
602 entry->host, this->prefix, FALSE);
603 break;
604 }
605 }
606 enumerator->destroy(enumerator);
607 entry_destroy(entry);
608 }
609 }
610
611 METHOD(load_tester_config_t, destroy, void,
612 private_load_tester_config_t *this)
613 {
614 this->mutex->destroy(this->mutex);
615 this->leases->destroy(this->leases);
616 this->pools->destroy_offset(this->pools, offsetof(mem_pool_t, destroy));
617 this->peer_cfg->destroy(this->peer_cfg);
618 DESTROY_IF(this->proposal);
619 DESTROY_IF(this->vip);
620 free(this);
621 }
622
623 /**
624 * Described in header.
625 */
626 load_tester_config_t *load_tester_config_create()
627 {
628 private_load_tester_config_t *this;
629
630 INIT(this,
631 .public = {
632 .backend = {
633 .create_peer_cfg_enumerator = _create_peer_cfg_enumerator,
634 .create_ike_cfg_enumerator = _create_ike_cfg_enumerator,
635 .get_peer_cfg_by_name = _get_peer_cfg_by_name,
636 },
637 .delete_ip = _delete_ip,
638 .destroy = _destroy,
639 },
640 .pools = linked_list_create(),
641 .leases = hashtable_create((hashtable_hash_t)hash,
642 (hashtable_equals_t)equals, 256),
643 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
644 .num = 1,
645 );
646
647 if (lib->settings->get_bool(lib->settings,
648 "%s.plugins.load-tester.request_virtual_ip", FALSE, charon->name))
649 {
650 this->vip = host_create_from_string("0.0.0.0", 0);
651 }
652 this->pool = lib->settings->get_str(lib->settings,
653 "%s.plugins.load-tester.pool", NULL, charon->name);
654 this->initiator = lib->settings->get_str(lib->settings,
655 "%s.plugins.load-tester.initiator", "0.0.0.0", charon->name);
656 this->responder = lib->settings->get_str(lib->settings,
657 "%s.plugins.load-tester.responder", "127.0.0.1", charon->name);
658
659 this->proposal = proposal_create_from_string(PROTO_IKE,
660 lib->settings->get_str(lib->settings,
661 "%s.plugins.load-tester.proposal", "aes128-sha1-modp768",
662 charon->name));
663 if (!this->proposal)
664 { /* fallback */
665 this->proposal = proposal_create_from_string(PROTO_IKE,
666 "aes128-sha1-modp768");
667 }
668 this->ike_rekey = lib->settings->get_int(lib->settings,
669 "%s.plugins.load-tester.ike_rekey", 0, charon->name);
670 this->child_rekey = lib->settings->get_int(lib->settings,
671 "%s.plugins.load-tester.child_rekey", 600, charon->name);
672 this->dpd_delay = lib->settings->get_int(lib->settings,
673 "%s.plugins.load-tester.dpd_delay", 0, charon->name);
674 this->dpd_timeout = lib->settings->get_int(lib->settings,
675 "%s.plugins.load-tester.dpd_timeout", 0, charon->name);
676
677 this->initiator_auth = lib->settings->get_str(lib->settings,
678 "%s.plugins.load-tester.initiator_auth", "pubkey", charon->name);
679 this->responder_auth = lib->settings->get_str(lib->settings,
680 "%s.plugins.load-tester.responder_auth", "pubkey", charon->name);
681 this->initiator_id = lib->settings->get_str(lib->settings,
682 "%s.plugins.load-tester.initiator_id", NULL, charon->name);
683 this->initiator_match = lib->settings->get_str(lib->settings,
684 "%s.plugins.load-tester.initiator_match", NULL, charon->name);
685 this->responder_id = lib->settings->get_str(lib->settings,
686 "%s.plugins.load-tester.responder_id", NULL, charon->name);
687
688 this->initiator_tsi = lib->settings->get_str(lib->settings,
689 "%s.plugins.load-tester.initiator_tsi", NULL, charon->name);
690 this->responder_tsi =lib->settings->get_str(lib->settings,
691 "%s.plugins.load-tester.responder_tsi",
692 this->initiator_tsi, charon->name);
693 this->initiator_tsr = lib->settings->get_str(lib->settings,
694 "%s.plugins.load-tester.initiator_tsr", NULL, charon->name);
695 this->responder_tsr =lib->settings->get_str(lib->settings,
696 "%s.plugins.load-tester.responder_tsr",
697 this->initiator_tsr, charon->name);
698
699 this->port = lib->settings->get_int(lib->settings,
700 "%s.plugins.load-tester.dynamic_port", 0, charon->name);
701 this->version = lib->settings->get_int(lib->settings,
702 "%s.plugins.load-tester.version", IKE_ANY, charon->name);
703
704 load_addrs(this);
705
706 this->peer_cfg = generate_config(this, 0);
707
708 return &this->public;
709 }