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