added HA resync option to (re-)integrate nodes to a cluster
[strongswan.git] / src / charon / plugins / ha_sync / ha_sync_dispatcher.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 * $Id$
16 */
17
18 #include "ha_sync_dispatcher.h"
19
20 #include <daemon.h>
21 #include <processing/jobs/callback_job.h>
22
23 typedef struct private_ha_sync_dispatcher_t private_ha_sync_dispatcher_t;
24
25 /**
26 * Private data of an ha_sync_dispatcher_t object.
27 */
28 struct private_ha_sync_dispatcher_t {
29
30 /**
31 * Public ha_sync_dispatcher_t interface.
32 */
33 ha_sync_dispatcher_t public;
34
35 /**
36 * socket to pull messages from
37 */
38 ha_sync_socket_t *socket;
39
40 /**
41 * Dispatcher job
42 */
43 callback_job_t *job;
44 };
45
46 /**
47 * Quick and dirty hack implementation of diffie_hellman_t.get_shared_secret
48 */
49 static status_t get_shared_secret(diffie_hellman_t *this, chunk_t *secret)
50 {
51 *secret = chunk_clone((*(chunk_t*)this->destroy));
52 return SUCCESS;
53 }
54
55 /**
56 * Process messages of type IKE_ADD
57 */
58 static void process_ike_add(private_ha_sync_dispatcher_t *this,
59 ha_sync_message_t *message)
60 {
61 ha_sync_message_attribute_t attribute;
62 ha_sync_message_value_t value;
63 enumerator_t *enumerator;
64 ike_sa_t *ike_sa = NULL, *old_sa = NULL;
65 u_int16_t encr = 0, len = 0, integ = 0, prf = 0, old_prf = PRF_UNDEFINED;
66 chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty;
67 chunk_t secret = chunk_empty, old_skd = chunk_empty;
68
69 enumerator = message->create_attribute_enumerator(message);
70 while (enumerator->enumerate(enumerator, &attribute, &value))
71 {
72 switch (attribute)
73 {
74 case HA_SYNC_IKE_ID:
75 ike_sa = ike_sa_create(value.ike_sa_id);
76 break;
77 case HA_SYNC_IKE_REKEY_ID:
78 old_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
79 value.ike_sa_id);
80 break;
81 case HA_SYNC_NONCE_I:
82 nonce_i = value.chunk;
83 break;
84 case HA_SYNC_NONCE_R:
85 nonce_r = value.chunk;
86 break;
87 case HA_SYNC_SECRET:
88 secret = value.chunk;
89 break;
90 case HA_SYNC_OLD_SKD:
91 old_skd = value.chunk;
92 break;
93 case HA_SYNC_ALG_ENCR:
94 encr = value.u16;
95 break;
96 case HA_SYNC_ALG_ENCR_LEN:
97 len = value.u16;
98 break;
99 case HA_SYNC_ALG_INTEG:
100 integ = value.u16;
101 break;
102 case HA_SYNC_ALG_PRF:
103 prf = value.u16;
104 break;
105 case HA_SYNC_ALG_OLD_PRF:
106 old_prf = value.u16;
107 break;
108 default:
109 break;
110 }
111 }
112 enumerator->destroy(enumerator);
113
114 if (ike_sa)
115 {
116 proposal_t *proposal;
117 keymat_t *keymat;
118 /* quick and dirty hack of a DH implementation ;-) */
119 diffie_hellman_t dh = { .get_shared_secret = get_shared_secret,
120 .destroy = (void*)&secret };
121
122 proposal = proposal_create(PROTO_IKE);
123 keymat = ike_sa->get_keymat(ike_sa);
124 if (integ)
125 {
126 proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
127 }
128 if (encr)
129 {
130 proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
131 }
132 if (prf)
133 {
134 proposal->add_algorithm(proposal, PSEUDO_RANDOM_FUNCTION, prf, 0);
135 }
136 charon->bus->set_sa(charon->bus, ike_sa);
137 if (keymat->derive_ike_keys(keymat, proposal, &dh, nonce_i, nonce_r,
138 ike_sa->get_id(ike_sa), old_prf, old_skd))
139 {
140 if (old_sa)
141 {
142 peer_cfg_t *peer_cfg = old_sa->get_peer_cfg(old_sa);
143
144 if (peer_cfg)
145 {
146 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
147 ike_sa->inherit(ike_sa, old_sa);
148 }
149 charon->ike_sa_manager->checkin_and_destroy(
150 charon->ike_sa_manager, old_sa);
151 old_sa = NULL;
152 }
153 ike_sa->set_state(ike_sa, IKE_CONNECTING);
154 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
155 }
156 else
157 {
158 DBG1(DBG_IKE, "HA sync keymat derivation failed");
159 ike_sa->destroy(ike_sa);
160 }
161 charon->bus->set_sa(charon->bus, NULL);
162 proposal->destroy(proposal);
163 }
164 if (old_sa)
165 {
166 charon->ike_sa_manager->checkin(charon->ike_sa_manager, old_sa);
167 }
168 }
169
170 /**
171 * Apply a condition flag to the IKE_SA if it is in set
172 */
173 static void set_condition(ike_sa_t *ike_sa, ike_condition_t set,
174 ike_condition_t flag)
175 {
176 ike_sa->set_condition(ike_sa, flag, flag & set);
177 }
178
179 /**
180 * Apply a extension flag to the IKE_SA if it is in set
181 */
182 static void set_extension(ike_sa_t *ike_sa, ike_extension_t set,
183 ike_extension_t flag)
184 {
185 if (flag & set)
186 {
187 ike_sa->enable_extension(ike_sa, flag);
188 }
189 }
190
191 /**
192 * Process messages of type IKE_UPDATE
193 */
194 static void process_ike_update(private_ha_sync_dispatcher_t *this,
195 ha_sync_message_t *message)
196 {
197 ha_sync_message_attribute_t attribute;
198 ha_sync_message_value_t value;
199 enumerator_t *enumerator;
200 ike_sa_t *ike_sa = NULL;
201 peer_cfg_t *peer_cfg = NULL;
202
203 enumerator = message->create_attribute_enumerator(message);
204 while (enumerator->enumerate(enumerator, &attribute, &value))
205 {
206 if (attribute != HA_SYNC_IKE_ID && ike_sa == NULL)
207 {
208 /* must be first attribute */
209 break;
210 }
211 switch (attribute)
212 {
213 case HA_SYNC_IKE_ID:
214 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
215 value.ike_sa_id);
216 break;
217 case HA_SYNC_LOCAL_ID:
218 ike_sa->set_my_id(ike_sa, value.id->clone(value.id));
219 break;
220 case HA_SYNC_REMOTE_ID:
221 ike_sa->set_other_id(ike_sa, value.id->clone(value.id));
222 break;
223 case HA_SYNC_EAP_ID:
224 ike_sa->set_eap_identity(ike_sa, value.id->clone(value.id));
225 break;
226 case HA_SYNC_LOCAL_ADDR:
227 ike_sa->set_my_host(ike_sa, value.host->clone(value.host));
228 break;
229 case HA_SYNC_REMOTE_ADDR:
230 ike_sa->set_other_host(ike_sa, value.host->clone(value.host));
231 break;
232 case HA_SYNC_LOCAL_VIP:
233 case HA_SYNC_REMOTE_VIP:
234 ike_sa->set_virtual_ip(ike_sa, attribute == HA_SYNC_LOCAL_VIP,
235 value.host->clone(value.host));
236 break;
237 case HA_SYNC_ADDITIONAL_ADDR:
238 ike_sa->add_additional_address(ike_sa,
239 value.host->clone(value.host));
240 break;
241 case HA_SYNC_CONFIG_NAME:
242 peer_cfg = charon->backends->get_peer_cfg_by_name(
243 charon->backends, value.str);
244 if (peer_cfg)
245 {
246 ike_sa->set_peer_cfg(ike_sa, peer_cfg);
247 peer_cfg->destroy(peer_cfg);
248 }
249 else
250 {
251 DBG1(DBG_IKE, "HA sync is missing nodes peer configuration");
252 }
253 break;
254 case HA_SYNC_CONDITIONS:
255 set_condition(ike_sa, value.u32, EXT_NATT);
256 set_condition(ike_sa, value.u32, EXT_MOBIKE);
257 set_condition(ike_sa, value.u32, EXT_HASH_AND_URL);
258 break;
259 case HA_SYNC_EXTENSIONS:
260 set_extension(ike_sa, value.u32, COND_NAT_ANY);
261 set_extension(ike_sa, value.u32, COND_NAT_HERE);
262 set_extension(ike_sa, value.u32, COND_NAT_THERE);
263 set_extension(ike_sa, value.u32, COND_NAT_FAKE);
264 set_extension(ike_sa, value.u32, COND_EAP_AUTHENTICATED);
265 set_extension(ike_sa, value.u32, COND_CERTREQ_SEEN);
266 set_extension(ike_sa, value.u32, COND_ORIGINAL_INITIATOR);
267 break;
268 case HA_SYNC_INITIATE_MID:
269 ike_sa->set_message_id(ike_sa, TRUE, value.u32);
270 break;
271 case HA_SYNC_RESPOND_MID:
272 ike_sa->set_message_id(ike_sa, FALSE, value.u32);
273 break;
274 default:
275 break;
276 }
277 }
278 enumerator->destroy(enumerator);
279
280 if (ike_sa)
281 {
282 if (ike_sa->get_state(ike_sa) == IKE_CONNECTING &&
283 ike_sa->get_peer_cfg(ike_sa))
284 {
285 ike_sa->set_state(ike_sa, IKE_PASSIVE);
286 }
287 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
288 }
289 }
290
291 /**
292 * Process messages of type IKE_DELETE
293 */
294 static void process_ike_delete(private_ha_sync_dispatcher_t *this,
295 ha_sync_message_t *message)
296 {
297 ha_sync_message_attribute_t attribute;
298 ha_sync_message_value_t value;
299 enumerator_t *enumerator;
300 ike_sa_t *ike_sa;
301
302 enumerator = message->create_attribute_enumerator(message);
303 while (enumerator->enumerate(enumerator, &attribute, &value))
304 {
305 switch (attribute)
306 {
307 case HA_SYNC_IKE_ID:
308 ike_sa = charon->ike_sa_manager->checkout(
309 charon->ike_sa_manager, value.ike_sa_id);
310 if (ike_sa)
311 {
312 charon->ike_sa_manager->checkin_and_destroy(
313 charon->ike_sa_manager, ike_sa);
314 }
315 break;
316 default:
317 break;
318 }
319 }
320 enumerator->destroy(enumerator);
321 }
322
323 /**
324 * Lookup a child cfg from the peer cfg by name
325 */
326 static child_cfg_t* find_child_cfg(ike_sa_t *ike_sa, char *name)
327 {
328 peer_cfg_t *peer_cfg;
329 child_cfg_t *current, *found = NULL;
330 enumerator_t *enumerator;
331
332 peer_cfg = ike_sa->get_peer_cfg(ike_sa);
333 if (peer_cfg)
334 {
335 enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
336 while (enumerator->enumerate(enumerator, &current))
337 {
338 if (streq(current->get_name(current), name))
339 {
340 found = current;
341 break;
342 }
343 }
344 enumerator->destroy(enumerator);
345 }
346 return found;
347 }
348
349 /**
350 * Process messages of type CHILD_ADD
351 */
352 static void process_child_add(private_ha_sync_dispatcher_t *this,
353 ha_sync_message_t *message)
354 {
355 ha_sync_message_attribute_t attribute;
356 ha_sync_message_value_t value;
357 enumerator_t *enumerator;
358 ike_sa_t *ike_sa = NULL;
359 char *config_name;
360 child_cfg_t *config = NULL;
361 child_sa_t *child_sa;
362 proposal_t *proposal;
363 keymat_t *keymat;
364 bool initiator, failed = FALSE;
365 u_int32_t inbound_spi = 0, outbound_spi = 0;
366 u_int16_t inbound_cpi = 0, outbound_cpi = 0;
367 u_int8_t mode = MODE_TUNNEL, ipcomp = 0;
368 u_int16_t encr = ENCR_UNDEFINED, integ = AUTH_UNDEFINED, len = 0;
369 chunk_t nonce_i = chunk_empty, nonce_r = chunk_empty, secret = chunk_empty;
370 chunk_t encr_i, integ_i, encr_r, integ_r;
371 linked_list_t *local_ts, *remote_ts;
372 /* quick and dirty hack of a DH implementation */
373 diffie_hellman_t dh = { .get_shared_secret = get_shared_secret,
374 .destroy = (void*)&secret };
375
376 enumerator = message->create_attribute_enumerator(message);
377 while (enumerator->enumerate(enumerator, &attribute, &value))
378 {
379 switch (attribute)
380 {
381 case HA_SYNC_IKE_ID:
382 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
383 value.ike_sa_id);
384 initiator = value.ike_sa_id->is_initiator(value.ike_sa_id);
385 break;
386 case HA_SYNC_CONFIG_NAME:
387 config_name = value.str;
388 break;
389 case HA_SYNC_INBOUND_SPI:
390 inbound_spi = value.u32;
391 break;
392 case HA_SYNC_OUTBOUND_SPI:
393 outbound_spi = value.u32;
394 break;
395 case HA_SYNC_INBOUND_CPI:
396 inbound_cpi = value.u32;
397 break;
398 case HA_SYNC_OUTBOUND_CPI:
399 outbound_cpi = value.u32;
400 break;
401 case HA_SYNC_IPSEC_MODE:
402 mode = value.u8;
403 break;
404 case HA_SYNC_IPCOMP:
405 ipcomp = value.u8;
406 break;
407 case HA_SYNC_ALG_ENCR:
408 encr = value.u16;
409 break;
410 case HA_SYNC_ALG_ENCR_LEN:
411 len = value.u16;
412 break;
413 case HA_SYNC_ALG_INTEG:
414 integ = value.u16;
415 break;
416 case HA_SYNC_NONCE_I:
417 nonce_i = value.chunk;
418 break;
419 case HA_SYNC_NONCE_R:
420 nonce_r = value.chunk;
421 break;
422 case HA_SYNC_SECRET:
423 secret = value.chunk;
424 break;
425 default:
426 break;
427 }
428 }
429 enumerator->destroy(enumerator);
430
431 if (!ike_sa)
432 {
433 DBG1(DBG_CHD, "IKE_SA for HA sync CHILD_SA not found");
434 return;
435 }
436 config = find_child_cfg(ike_sa, config_name);
437 if (!config)
438 {
439 DBG1(DBG_CHD, "HA sync is missing nodes child configuration");
440 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
441 return;
442 }
443
444 child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
445 ike_sa->get_other_host(ike_sa), config, 0,
446 ike_sa->has_condition(ike_sa, COND_NAT_ANY));
447 child_sa->set_mode(child_sa, mode);
448 child_sa->set_protocol(child_sa, PROTO_ESP);
449 child_sa->set_ipcomp(child_sa, ipcomp);
450
451 proposal = proposal_create(PROTO_ESP);
452 if (integ)
453 {
454 proposal->add_algorithm(proposal, INTEGRITY_ALGORITHM, integ, 0);
455 }
456 if (encr)
457 {
458 proposal->add_algorithm(proposal, ENCRYPTION_ALGORITHM, encr, len);
459 }
460 keymat = ike_sa->get_keymat(ike_sa);
461
462 if (!keymat->derive_child_keys(keymat, proposal, secret.ptr ? &dh : NULL,
463 nonce_i, nonce_r, &encr_i, &integ_i, &encr_r, &integ_r))
464 {
465 DBG1(DBG_CHD, "HA sync CHILD_SA key derivation failed");
466 child_sa->destroy(child_sa);
467 proposal->destroy(proposal);
468 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
469 return;
470 }
471 child_sa->set_proposal(child_sa, proposal);
472 child_sa->set_state(child_sa, CHILD_INSTALLING);
473 proposal->destroy(proposal);
474
475 if (initiator)
476 {
477 if (child_sa->install(child_sa, encr_r, integ_r,
478 inbound_spi, inbound_cpi, TRUE) != SUCCESS ||
479 child_sa->install(child_sa, encr_i, integ_i,
480 outbound_spi, outbound_cpi, FALSE) != SUCCESS)
481 {
482 failed = TRUE;
483 }
484 }
485 else
486 {
487 if (child_sa->install(child_sa, encr_i, integ_i,
488 inbound_spi, inbound_cpi, TRUE) != SUCCESS ||
489 child_sa->install(child_sa, encr_r, integ_r,
490 outbound_spi, outbound_cpi, FALSE) != SUCCESS)
491 {
492 failed = TRUE;
493 }
494 }
495 chunk_clear(&encr_i);
496 chunk_clear(&integ_i);
497 chunk_clear(&encr_r);
498 chunk_clear(&integ_r);
499
500 if (failed)
501 {
502 DBG1(DBG_CHD, "HA sync CHILD_SA installation failed");
503 child_sa->destroy(child_sa);
504 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
505 return;
506 }
507
508 /* TODO: Change CHILD_SA API to avoid cloning twice */
509 local_ts = linked_list_create();
510 remote_ts = linked_list_create();
511 enumerator = message->create_attribute_enumerator(message);
512 while (enumerator->enumerate(enumerator, &attribute, &value))
513 {
514 switch (attribute)
515 {
516 case HA_SYNC_LOCAL_TS:
517 local_ts->insert_last(local_ts, value.ts->clone(value.ts));
518 break;
519 case HA_SYNC_REMOTE_TS:
520 remote_ts->insert_last(remote_ts, value.ts->clone(value.ts));
521 break;
522 default:
523 break;
524 }
525 }
526 enumerator->destroy(enumerator);
527 child_sa->add_policies(child_sa, local_ts, remote_ts);
528 local_ts->destroy_offset(local_ts, offsetof(traffic_selector_t, destroy));
529 remote_ts->destroy_offset(remote_ts, offsetof(traffic_selector_t, destroy));
530
531 child_sa->set_state(child_sa, CHILD_INSTALLED);
532 ike_sa->add_child_sa(ike_sa, child_sa);
533 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
534 }
535
536 /**
537 * Process messages of type CHILD_DELETE
538 */
539 static void process_child_delete(private_ha_sync_dispatcher_t *this,
540 ha_sync_message_t *message)
541 {
542 ha_sync_message_attribute_t attribute;
543 ha_sync_message_value_t value;
544 enumerator_t *enumerator;
545 ike_sa_t *ike_sa = NULL;
546
547 enumerator = message->create_attribute_enumerator(message);
548 while (enumerator->enumerate(enumerator, &attribute, &value))
549 {
550 switch (attribute)
551 {
552 case HA_SYNC_IKE_ID:
553 ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
554 value.ike_sa_id);
555 break;
556 case HA_SYNC_INBOUND_SPI:
557 if (!ike_sa || ike_sa->destroy_child_sa(ike_sa, PROTO_ESP,
558 value.u32) != SUCCESS)
559 {
560 DBG1(DBG_CHD, "HA sync CHILD_SA 0x%lx delete failed (%p)",
561 value.u32, ike_sa);
562 }
563 break;
564 default:
565 break;
566 }
567 break;
568 }
569 if (ike_sa)
570 {
571 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
572 }
573 enumerator->destroy(enumerator);
574 }
575
576 /**
577 * Dispatcher job function
578 */
579 static job_requeue_t dispatch(private_ha_sync_dispatcher_t *this)
580 {
581 ha_sync_message_t *message;
582
583 message = this->socket->pull(this->socket);
584 switch (message->get_type(message))
585 {
586 case HA_SYNC_IKE_ADD:
587 process_ike_add(this, message);
588 break;
589 case HA_SYNC_IKE_UPDATE:
590 process_ike_update(this, message);
591 break;
592 case HA_SYNC_IKE_DELETE:
593 process_ike_delete(this, message);
594 break;
595 case HA_SYNC_CHILD_ADD:
596 process_child_add(this, message);
597 break;
598 case HA_SYNC_CHILD_DELETE:
599 process_child_delete(this, message);
600 break;
601 default:
602 DBG1(DBG_CFG, "received unknown HA sync message type %d",
603 message->get_type(message));
604 break;
605 }
606 message->destroy(message);
607
608 return JOB_REQUEUE_DIRECT;
609 }
610
611 /**
612 * Implementation of ha_sync_dispatcher_t.destroy.
613 */
614 static void destroy(private_ha_sync_dispatcher_t *this)
615 {
616 this->job->cancel(this->job);
617 free(this);
618 }
619
620 /**
621 * See header
622 */
623 ha_sync_dispatcher_t *ha_sync_dispatcher_create(ha_sync_socket_t *socket)
624 {
625 private_ha_sync_dispatcher_t *this = malloc_thing(private_ha_sync_dispatcher_t);
626
627 this->public.destroy = (void(*)(ha_sync_dispatcher_t*))destroy;
628
629 this->socket = socket;
630 this->job = callback_job_create((callback_job_cb_t)dispatch,
631 this, NULL, NULL);
632 charon->processor->queue_job(charon->processor, (job_t*)this->job);
633
634 return &this->public;
635 }