2 * Copyright (C) 2008 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
16 #include "ha_dispatcher.h"
19 #include <processing/jobs/callback_job.h>
21 typedef struct private_ha_dispatcher_t private_ha_dispatcher_t
;
24 * Private data of an ha_dispatcher_t object.
26 struct private_ha_dispatcher_t
{
29 * Public ha_dispatcher_t interface.
31 ha_dispatcher_t
public;
34 * socket to pull messages from
41 ha_segments_t
*segments
;
65 * Quick and dirty hack implementation of diffie_hellman_t.get_shared_secret
67 static status_t
get_shared_secret(diffie_hellman_t
*this, chunk_t
*secret
)
69 *secret
= chunk_clone((*(chunk_t
*)this->destroy
));
74 * Process messages of type IKE_ADD
76 static void process_ike_add(private_ha_dispatcher_t
*this, ha_message_t
*message
)
78 ha_message_attribute_t attribute
;
79 ha_message_value_t value
;
80 enumerator_t
*enumerator
;
81 ike_sa_t
*ike_sa
= NULL
, *old_sa
= NULL
;
82 u_int16_t encr
= 0, len
= 0, integ
= 0, prf
= 0, old_prf
= PRF_UNDEFINED
;
83 chunk_t nonce_i
= chunk_empty
, nonce_r
= chunk_empty
;
84 chunk_t secret
= chunk_empty
, old_skd
= chunk_empty
;
86 enumerator
= message
->create_attribute_enumerator(message
);
87 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
92 ike_sa
= ike_sa_create(value
.ike_sa_id
);
95 old_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
,
99 nonce_i
= value
.chunk
;
102 nonce_r
= value
.chunk
;
105 secret
= value
.chunk
;
108 old_skd
= value
.chunk
;
113 case HA_ALG_ENCR_LEN
:
129 enumerator
->destroy(enumerator
);
133 proposal_t
*proposal
;
135 /* quick and dirty hack of a DH implementation ;-) */
136 diffie_hellman_t dh
= { .get_shared_secret
= get_shared_secret
,
137 .destroy
= (void*)&secret
};
139 proposal
= proposal_create(PROTO_IKE
, 0);
140 keymat
= ike_sa
->get_keymat(ike_sa
);
143 proposal
->add_algorithm(proposal
, INTEGRITY_ALGORITHM
, integ
, 0);
147 proposal
->add_algorithm(proposal
, ENCRYPTION_ALGORITHM
, encr
, len
);
151 proposal
->add_algorithm(proposal
, PSEUDO_RANDOM_FUNCTION
, prf
, 0);
153 charon
->bus
->set_sa(charon
->bus
, ike_sa
);
154 if (keymat
->derive_ike_keys(keymat
, proposal
, &dh
, nonce_i
, nonce_r
,
155 ike_sa
->get_id(ike_sa
), old_prf
, old_skd
))
159 peer_cfg_t
*peer_cfg
= old_sa
->get_peer_cfg(old_sa
);
163 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
164 ike_sa
->inherit(ike_sa
, old_sa
);
166 charon
->ike_sa_manager
->checkin_and_destroy(
167 charon
->ike_sa_manager
, old_sa
);
170 ike_sa
->set_state(ike_sa
, IKE_CONNECTING
);
171 this->cache
->cache(this->cache
, ike_sa
, message
);
173 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
177 DBG1(DBG_IKE
, "HA keymat derivation failed");
178 ike_sa
->destroy(ike_sa
);
180 charon
->bus
->set_sa(charon
->bus
, NULL
);
181 proposal
->destroy(proposal
);
185 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, old_sa
);
191 * Apply a condition flag to the IKE_SA if it is in set
193 static void set_condition(ike_sa_t
*ike_sa
, ike_condition_t set
,
194 ike_condition_t flag
)
196 ike_sa
->set_condition(ike_sa
, flag
, flag
& set
);
200 * Apply a extension flag to the IKE_SA if it is in set
202 static void set_extension(ike_sa_t
*ike_sa
, ike_extension_t set
,
203 ike_extension_t flag
)
207 ike_sa
->enable_extension(ike_sa
, flag
);
212 * Process messages of type IKE_UPDATE
214 static void process_ike_update(private_ha_dispatcher_t
*this,
215 ha_message_t
*message
)
217 ha_message_attribute_t attribute
;
218 ha_message_value_t value
;
219 enumerator_t
*enumerator
;
220 ike_sa_t
*ike_sa
= NULL
;
221 peer_cfg_t
*peer_cfg
= NULL
;
223 bool received_vip
= FALSE
;
225 enumerator
= message
->create_attribute_enumerator(message
);
226 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
228 if (attribute
!= HA_IKE_ID
&& ike_sa
== NULL
)
230 /* must be first attribute */
236 ike_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
,
240 ike_sa
->set_my_id(ike_sa
, value
.id
->clone(value
.id
));
243 ike_sa
->set_other_id(ike_sa
, value
.id
->clone(value
.id
));
245 case HA_REMOTE_EAP_ID
:
246 auth
= auth_cfg_create();
247 auth
->add(auth
, AUTH_RULE_EAP_IDENTITY
, value
.id
->clone(value
.id
));
248 ike_sa
->add_auth_cfg(ike_sa
, FALSE
, auth
);
251 ike_sa
->set_my_host(ike_sa
, value
.host
->clone(value
.host
));
254 ike_sa
->set_other_host(ike_sa
, value
.host
->clone(value
.host
));
257 ike_sa
->set_virtual_ip(ike_sa
, TRUE
, value
.host
);
260 ike_sa
->set_virtual_ip(ike_sa
, FALSE
, value
.host
);
263 case HA_ADDITIONAL_ADDR
:
264 ike_sa
->add_additional_address(ike_sa
,
265 value
.host
->clone(value
.host
));
268 peer_cfg
= charon
->backends
->get_peer_cfg_by_name(
269 charon
->backends
, value
.str
);
272 ike_sa
->set_peer_cfg(ike_sa
, peer_cfg
);
273 peer_cfg
->destroy(peer_cfg
);
277 DBG1(DBG_IKE
, "HA is missing nodes peer configuration");
281 set_extension(ike_sa
, value
.u32
, EXT_NATT
);
282 set_extension(ike_sa
, value
.u32
, EXT_MOBIKE
);
283 set_extension(ike_sa
, value
.u32
, EXT_HASH_AND_URL
);
286 set_condition(ike_sa
, value
.u32
, COND_NAT_ANY
);
287 set_condition(ike_sa
, value
.u32
, COND_NAT_HERE
);
288 set_condition(ike_sa
, value
.u32
, COND_NAT_THERE
);
289 set_condition(ike_sa
, value
.u32
, COND_NAT_FAKE
);
290 set_condition(ike_sa
, value
.u32
, COND_EAP_AUTHENTICATED
);
291 set_condition(ike_sa
, value
.u32
, COND_CERTREQ_SEEN
);
292 set_condition(ike_sa
, value
.u32
, COND_ORIGINAL_INITIATOR
);
298 enumerator
->destroy(enumerator
);
302 if (ike_sa
->get_state(ike_sa
) == IKE_CONNECTING
&&
303 ike_sa
->get_peer_cfg(ike_sa
))
305 DBG1(DBG_CFG
, "installed HA passive IKE_SA '%s' %H[%Y]...%H[%Y]",
306 ike_sa
->get_name(ike_sa
),
307 ike_sa
->get_my_host(ike_sa
), ike_sa
->get_my_id(ike_sa
),
308 ike_sa
->get_other_host(ike_sa
), ike_sa
->get_other_id(ike_sa
));
309 ike_sa
->set_state(ike_sa
, IKE_PASSIVE
);
316 peer_cfg
= ike_sa
->get_peer_cfg(ike_sa
);
317 vip
= ike_sa
->get_virtual_ip(ike_sa
, FALSE
);
320 pool
= peer_cfg
->get_pool(peer_cfg
);
323 this->attr
->reserve(this->attr
, pool
, vip
);
327 this->cache
->cache(this->cache
, ike_sa
, message
);
328 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
332 DBG1(DBG_CFG
, "passive HA IKE_SA to update not found");
333 message
->destroy(message
);
338 * Process messages of type IKE_MID_INITIATOR/RESPONDER
340 static void process_ike_mid(private_ha_dispatcher_t
*this,
341 ha_message_t
*message
, bool initiator
)
343 ha_message_attribute_t attribute
;
344 ha_message_value_t value
;
345 enumerator_t
*enumerator
;
346 ike_sa_t
*ike_sa
= NULL
;
349 enumerator
= message
->create_attribute_enumerator(message
);
350 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
355 ike_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
,
365 enumerator
->destroy(enumerator
);
371 ike_sa
->set_message_id(ike_sa
, initiator
, mid
);
373 this->cache
->cache(this->cache
, ike_sa
, message
);
374 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
378 message
->destroy(message
);
383 * Process messages of type IKE_DELETE
385 static void process_ike_delete(private_ha_dispatcher_t
*this,
386 ha_message_t
*message
)
388 ha_message_attribute_t attribute
;
389 ha_message_value_t value
;
390 enumerator_t
*enumerator
;
391 ike_sa_t
*ike_sa
= NULL
;
393 enumerator
= message
->create_attribute_enumerator(message
);
394 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
399 ike_sa
= charon
->ike_sa_manager
->checkout(
400 charon
->ike_sa_manager
, value
.ike_sa_id
);
406 enumerator
->destroy(enumerator
);
409 this->cache
->cache(this->cache
, ike_sa
, message
);
410 charon
->ike_sa_manager
->checkin_and_destroy(
411 charon
->ike_sa_manager
, ike_sa
);
415 message
->destroy(message
);
420 * Lookup a child cfg from the peer cfg by name
422 static child_cfg_t
* find_child_cfg(ike_sa_t
*ike_sa
, char *name
)
424 peer_cfg_t
*peer_cfg
;
425 child_cfg_t
*current
, *found
= NULL
;
426 enumerator_t
*enumerator
;
428 peer_cfg
= ike_sa
->get_peer_cfg(ike_sa
);
431 enumerator
= peer_cfg
->create_child_cfg_enumerator(peer_cfg
);
432 while (enumerator
->enumerate(enumerator
, ¤t
))
434 if (streq(current
->get_name(current
), name
))
440 enumerator
->destroy(enumerator
);
446 * Process messages of type CHILD_ADD
448 static void process_child_add(private_ha_dispatcher_t
*this,
449 ha_message_t
*message
)
451 ha_message_attribute_t attribute
;
452 ha_message_value_t value
;
453 enumerator_t
*enumerator
;
454 ike_sa_t
*ike_sa
= NULL
;
455 char *config_name
= "";
456 child_cfg_t
*config
= NULL
;
457 child_sa_t
*child_sa
;
458 proposal_t
*proposal
;
460 bool initiator
= FALSE
, failed
= FALSE
;
461 u_int32_t inbound_spi
= 0, outbound_spi
= 0;
462 u_int16_t inbound_cpi
= 0, outbound_cpi
= 0;
463 u_int8_t mode
= MODE_TUNNEL
, ipcomp
= 0;
464 u_int16_t encr
= ENCR_UNDEFINED
, integ
= AUTH_UNDEFINED
, len
= 0;
465 u_int16_t esn
= NO_EXT_SEQ_NUMBERS
;
467 chunk_t nonce_i
= chunk_empty
, nonce_r
= chunk_empty
, secret
= chunk_empty
;
468 chunk_t encr_i
, integ_i
, encr_r
, integ_r
;
469 linked_list_t
*local_ts
, *remote_ts
;
470 /* quick and dirty hack of a DH implementation */
471 diffie_hellman_t dh
= { .get_shared_secret
= get_shared_secret
,
472 .destroy
= (void*)&secret
};
474 enumerator
= message
->create_attribute_enumerator(message
);
475 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
480 ike_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
,
484 config_name
= value
.str
;
487 initiator
= value
.u8
;
490 inbound_spi
= value
.u32
;
492 case HA_OUTBOUND_SPI
:
493 outbound_spi
= value
.u32
;
496 inbound_cpi
= value
.u32
;
498 case HA_OUTBOUND_CPI
:
499 outbound_cpi
= value
.u32
;
510 case HA_ALG_ENCR_LEN
:
520 nonce_i
= value
.chunk
;
523 nonce_r
= value
.chunk
;
526 secret
= value
.chunk
;
532 enumerator
->destroy(enumerator
);
536 DBG1(DBG_CHD
, "IKE_SA for HA CHILD_SA not found");
537 message
->destroy(message
);
540 config
= find_child_cfg(ike_sa
, config_name
);
543 DBG1(DBG_CHD
, "HA is missing nodes child configuration");
544 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
545 message
->destroy(message
);
549 child_sa
= child_sa_create(ike_sa
->get_my_host(ike_sa
),
550 ike_sa
->get_other_host(ike_sa
), config
, 0,
551 ike_sa
->has_condition(ike_sa
, COND_NAT_ANY
));
552 child_sa
->set_mode(child_sa
, mode
);
553 child_sa
->set_protocol(child_sa
, PROTO_ESP
);
554 child_sa
->set_ipcomp(child_sa
, ipcomp
);
556 proposal
= proposal_create(PROTO_ESP
, 0);
559 proposal
->add_algorithm(proposal
, INTEGRITY_ALGORITHM
, integ
, 0);
563 proposal
->add_algorithm(proposal
, ENCRYPTION_ALGORITHM
, encr
, len
);
565 proposal
->add_algorithm(proposal
, EXTENDED_SEQUENCE_NUMBERS
, esn
, 0);
566 keymat
= ike_sa
->get_keymat(ike_sa
);
568 if (!keymat
->derive_child_keys(keymat
, proposal
, secret
.ptr ?
&dh
: NULL
,
569 nonce_i
, nonce_r
, &encr_i
, &integ_i
, &encr_r
, &integ_r
))
571 DBG1(DBG_CHD
, "HA CHILD_SA key derivation failed");
572 child_sa
->destroy(child_sa
);
573 proposal
->destroy(proposal
);
574 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
577 child_sa
->set_proposal(child_sa
, proposal
);
578 child_sa
->set_state(child_sa
, CHILD_INSTALLING
);
579 proposal
->destroy(proposal
);
581 /* TODO: Change CHILD_SA API to avoid cloning twice */
582 local_ts
= linked_list_create();
583 remote_ts
= linked_list_create();
584 enumerator
= message
->create_attribute_enumerator(message
);
585 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
590 local_ts
->insert_last(local_ts
, value
.ts
->clone(value
.ts
));
593 remote_ts
->insert_last(remote_ts
, value
.ts
->clone(value
.ts
));
599 enumerator
->destroy(enumerator
);
603 if (child_sa
->install(child_sa
, encr_r
, integ_r
, inbound_spi
,
604 inbound_cpi
, TRUE
, TRUE
, local_ts
, remote_ts
) != SUCCESS
||
605 child_sa
->install(child_sa
, encr_i
, integ_i
, outbound_spi
,
606 outbound_cpi
, FALSE
, TRUE
, local_ts
, remote_ts
) != SUCCESS
)
613 if (child_sa
->install(child_sa
, encr_i
, integ_i
, inbound_spi
,
614 inbound_cpi
, TRUE
, TRUE
, local_ts
, remote_ts
) != SUCCESS
||
615 child_sa
->install(child_sa
, encr_r
, integ_r
, outbound_spi
,
616 outbound_cpi
, FALSE
, TRUE
, local_ts
, remote_ts
) != SUCCESS
)
621 chunk_clear(&encr_i
);
622 chunk_clear(&integ_i
);
623 chunk_clear(&encr_r
);
624 chunk_clear(&integ_r
);
628 DBG1(DBG_CHD
, "HA CHILD_SA installation failed");
629 child_sa
->destroy(child_sa
);
630 local_ts
->destroy_offset(local_ts
, offsetof(traffic_selector_t
, destroy
));
631 remote_ts
->destroy_offset(remote_ts
, offsetof(traffic_selector_t
, destroy
));
632 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
633 message
->destroy(message
);
637 seg_i
= this->kernel
->get_segment_spi(this->kernel
,
638 ike_sa
->get_my_host(ike_sa
), inbound_spi
);
639 seg_o
= this->kernel
->get_segment_spi(this->kernel
,
640 ike_sa
->get_other_host(ike_sa
), outbound_spi
);
642 DBG1(DBG_CFG
, "installed HA CHILD_SA %s{%d} %#R=== %#R "
643 "(segment in: %d%s, out: %d%s)", child_sa
->get_name(child_sa
),
644 child_sa
->get_reqid(child_sa
), local_ts
, remote_ts
,
645 seg_i
, this->segments
->is_active(this->segments
, seg_i
) ?
"*" : "",
646 seg_o
, this->segments
->is_active(this->segments
, seg_o
) ?
"*" : "");
647 child_sa
->add_policies(child_sa
, local_ts
, remote_ts
);
648 local_ts
->destroy_offset(local_ts
, offsetof(traffic_selector_t
, destroy
));
649 remote_ts
->destroy_offset(remote_ts
, offsetof(traffic_selector_t
, destroy
));
651 child_sa
->set_state(child_sa
, CHILD_INSTALLED
);
652 ike_sa
->add_child_sa(ike_sa
, child_sa
);
653 message
->destroy(message
);
654 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
658 * Process messages of type CHILD_DELETE
660 static void process_child_delete(private_ha_dispatcher_t
*this,
661 ha_message_t
*message
)
663 ha_message_attribute_t attribute
;
664 ha_message_value_t value
;
665 enumerator_t
*enumerator
;
666 ike_sa_t
*ike_sa
= NULL
;
667 child_sa_t
*child_sa
;
670 enumerator
= message
->create_attribute_enumerator(message
);
671 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
676 ike_sa
= charon
->ike_sa_manager
->checkout(charon
->ike_sa_manager
,
686 enumerator
->destroy(enumerator
);
690 child_sa
= ike_sa
->get_child_sa(ike_sa
, PROTO_ESP
, spi
, TRUE
);
693 ike_sa
->destroy_child_sa(ike_sa
, PROTO_ESP
, spi
);
695 charon
->ike_sa_manager
->checkin(charon
->ike_sa_manager
, ike_sa
);
697 message
->destroy(message
);
701 * Process messages of type SEGMENT_TAKE/DROP
703 static void process_segment(private_ha_dispatcher_t
*this,
704 ha_message_t
*message
, bool take
)
706 ha_message_attribute_t attribute
;
707 ha_message_value_t value
;
708 enumerator_t
*enumerator
;
710 enumerator
= message
->create_attribute_enumerator(message
);
711 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
718 DBG1(DBG_CFG
, "remote node takes segment %d", value
.u16
);
719 this->segments
->deactivate(this->segments
, value
.u16
, FALSE
);
723 DBG1(DBG_CFG
, "remote node drops segment %d", value
.u16
);
724 this->segments
->activate(this->segments
, value
.u16
, FALSE
);
731 enumerator
->destroy(enumerator
);
732 message
->destroy(message
);
736 * Process messages of type STATUS
738 static void process_status(private_ha_dispatcher_t
*this,
739 ha_message_t
*message
)
741 ha_message_attribute_t attribute
;
742 ha_message_value_t value
;
743 enumerator_t
*enumerator
;
744 segment_mask_t mask
= 0;
746 enumerator
= message
->create_attribute_enumerator(message
);
747 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
752 mask
|= SEGMENTS_BIT(value
.u16
);
758 enumerator
->destroy(enumerator
);
760 this->segments
->handle_status(this->segments
, mask
);
761 message
->destroy(message
);
765 * Process messages of type RESYNC
767 static void process_resync(private_ha_dispatcher_t
*this,
768 ha_message_t
*message
)
770 ha_message_attribute_t attribute
;
771 ha_message_value_t value
;
772 enumerator_t
*enumerator
;
774 enumerator
= message
->create_attribute_enumerator(message
);
775 while (enumerator
->enumerate(enumerator
, &attribute
, &value
))
780 this->cache
->resync(this->cache
, value
.u16
);
786 enumerator
->destroy(enumerator
);
787 message
->destroy(message
);
791 * Dispatcher job function
793 static job_requeue_t
dispatch(private_ha_dispatcher_t
*this)
795 ha_message_t
*message
;
796 ha_message_type_t type
;
798 message
= this->socket
->pull(this->socket
);
799 type
= message
->get_type(message
);
800 if (type
!= HA_STATUS
)
802 DBG2(DBG_CFG
, "received HA %N message", ha_message_type_names
,
803 message
->get_type(message
));
808 process_ike_add(this, message
);
811 process_ike_update(this, message
);
813 case HA_IKE_MID_INITIATOR
:
814 process_ike_mid(this, message
, TRUE
);
816 case HA_IKE_MID_RESPONDER
:
817 process_ike_mid(this, message
, FALSE
);
820 process_ike_delete(this, message
);
823 process_child_add(this, message
);
825 case HA_CHILD_DELETE
:
826 process_child_delete(this, message
);
828 case HA_SEGMENT_DROP
:
829 process_segment(this, message
, FALSE
);
831 case HA_SEGMENT_TAKE
:
832 process_segment(this, message
, TRUE
);
835 process_status(this, message
);
838 process_resync(this, message
);
841 DBG1(DBG_CFG
, "received unknown HA message type %d", type
);
842 message
->destroy(message
);
845 return JOB_REQUEUE_DIRECT
;
848 METHOD(ha_dispatcher_t
, destroy
, void,
849 private_ha_dispatcher_t
*this)
851 this->job
->cancel(this->job
);
858 ha_dispatcher_t
*ha_dispatcher_create(ha_socket_t
*socket
,
859 ha_segments_t
*segments
, ha_cache_t
*cache
,
860 ha_kernel_t
*kernel
, ha_attribute_t
*attr
)
862 private_ha_dispatcher_t
*this;
870 .segments
= segments
,
875 this->job
= callback_job_create_with_prio((callback_job_cb_t
)dispatch
,
876 this, NULL
, NULL
, JOB_PRIO_CRITICAL
);
877 lib
->processor
->queue_job(lib
->processor
, (job_t
*)this->job
);
879 return &this->public;