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