aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Willi <martin@strongswan.org>2007-02-28 14:04:36 +0000
committerMartin Willi <martin@strongswan.org>2007-02-28 14:04:36 +0000
commitc60c7694d2d8925c5d93ff33d132f561ad89e071 (patch)
tree9c7957b0749139c5e7c9b008c927e79d69f8e500
parenta7a5e834e318d0582b6db979b63a5739c0a8244f (diff)
downloadstrongswan-c60c7694d2d8925c5d93ff33d132f561ad89e071.tar.bz2
strongswan-c60c7694d2d8925c5d93ff33d132f561ad89e071.tar.xz
merged tasking branch into trunk
-rw-r--r--configure.in6
-rw-r--r--src/charon/Makefile.am32
-rw-r--r--src/charon/bus/bus.c9
-rw-r--r--src/charon/bus/bus.h5
-rw-r--r--src/charon/bus/listeners/file_logger.c6
-rw-r--r--src/charon/bus/listeners/sys_logger.c6
-rwxr-xr-xsrc/charon/config/configuration.c50
-rwxr-xr-xsrc/charon/config/configuration.h12
-rw-r--r--src/charon/config/policies/policy.c63
-rw-r--r--src/charon/config/policies/policy.h24
-rw-r--r--src/charon/config/traffic_selector.c25
-rw-r--r--src/charon/config/traffic_selector.h30
-rw-r--r--src/charon/encoding/message.c58
-rw-r--r--src/charon/encoding/message.h26
-rw-r--r--src/charon/encoding/payloads/cert_payload.c2
-rw-r--r--src/charon/encoding/payloads/certreq_payload.c3
-rw-r--r--src/charon/encoding/payloads/configuration_attribute.c72
-rw-r--r--src/charon/encoding/payloads/configuration_attribute.h6
-rw-r--r--src/charon/encoding/payloads/cp_payload.c6
-rw-r--r--src/charon/encoding/payloads/cp_payload.h14
-rw-r--r--src/charon/encoding/payloads/notify_payload.c8
-rw-r--r--src/charon/network/packet.c10
-rw-r--r--src/charon/queues/jobs/acquire_job.c4
-rw-r--r--src/charon/queues/jobs/delete_child_sa_job.c4
-rw-r--r--src/charon/queues/jobs/delete_ike_sa_job.c43
-rw-r--r--src/charon/queues/jobs/incoming_packet_job.c34
-rw-r--r--src/charon/queues/jobs/initiate_job.c21
-rw-r--r--src/charon/queues/jobs/initiate_job.h4
-rw-r--r--src/charon/queues/jobs/job.c2
-rw-r--r--src/charon/queues/jobs/job.h4
-rw-r--r--src/charon/queues/jobs/rekey_child_sa_job.c4
-rw-r--r--src/charon/queues/jobs/rekey_ike_sa_job.c4
-rw-r--r--src/charon/queues/jobs/retransmit_job.c (renamed from src/charon/queues/jobs/retransmit_request_job.c)59
-rw-r--r--src/charon/queues/jobs/retransmit_job.h (renamed from src/charon/queues/jobs/retransmit_request_job.h)30
-rw-r--r--src/charon/queues/jobs/route_job.c2
-rw-r--r--src/charon/sa/child_sa.c147
-rw-r--r--src/charon/sa/child_sa.h49
-rw-r--r--src/charon/sa/ike_sa.c1546
-rw-r--r--src/charon/sa/ike_sa.h229
-rw-r--r--src/charon/sa/ike_sa_manager.c475
-rw-r--r--src/charon/sa/ike_sa_manager.h86
-rw-r--r--src/charon/sa/task_manager.c780
-rw-r--r--src/charon/sa/task_manager.h144
-rw-r--r--src/charon/sa/tasks/child_create.c718
-rw-r--r--src/charon/sa/tasks/child_create.h72
-rw-r--r--src/charon/sa/tasks/child_delete.c264
-rw-r--r--src/charon/sa/tasks/child_delete.h58
-rw-r--r--src/charon/sa/tasks/child_rekey.c353
-rw-r--r--src/charon/sa/tasks/child_rekey.h58
-rw-r--r--src/charon/sa/tasks/ike_auth.c513
-rw-r--r--src/charon/sa/tasks/ike_auth.h60
-rw-r--r--src/charon/sa/tasks/ike_cert.c361
-rw-r--r--src/charon/sa/tasks/ike_cert.h61
-rw-r--r--src/charon/sa/tasks/ike_config.c421
-rw-r--r--src/charon/sa/tasks/ike_config.h59
-rw-r--r--src/charon/sa/tasks/ike_delete.c169
-rw-r--r--src/charon/sa/tasks/ike_delete.h57
-rw-r--r--src/charon/sa/tasks/ike_dpd.c106
-rw-r--r--src/charon/sa/tasks/ike_dpd.h58
-rw-r--r--src/charon/sa/tasks/ike_init.c536
-rw-r--r--src/charon/sa/tasks/ike_init.h60
-rw-r--r--src/charon/sa/tasks/ike_natd.c378
-rw-r--r--src/charon/sa/tasks/ike_natd.h57
-rw-r--r--src/charon/sa/tasks/ike_rekey.c232
-rw-r--r--src/charon/sa/tasks/ike_rekey.h57
-rw-r--r--src/charon/sa/tasks/task.c38
-rw-r--r--src/charon/sa/tasks/task.h151
-rw-r--r--src/charon/sa/transactions/create_child_sa.c1121
-rw-r--r--src/charon/sa/transactions/create_child_sa.h123
-rw-r--r--src/charon/sa/transactions/dead_peer_detection.c187
-rw-r--r--src/charon/sa/transactions/dead_peer_detection.h58
-rw-r--r--src/charon/sa/transactions/delete_child_sa.c356
-rw-r--r--src/charon/sa/transactions/delete_child_sa.h68
-rw-r--r--src/charon/sa/transactions/delete_ike_sa.c284
-rw-r--r--src/charon/sa/transactions/delete_ike_sa.h81
-rw-r--r--src/charon/sa/transactions/ike_auth.c1562
-rw-r--r--src/charon/sa/transactions/ike_auth.h110
-rw-r--r--src/charon/sa/transactions/ike_sa_init.c1121
-rw-r--r--src/charon/sa/transactions/ike_sa_init.h97
-rw-r--r--src/charon/sa/transactions/rekey_ike_sa.c889
-rw-r--r--src/charon/sa/transactions/rekey_ike_sa.h81
-rw-r--r--src/charon/sa/transactions/transaction.c167
-rw-r--r--src/charon/sa/transactions/transaction.h180
-rw-r--r--src/charon/threads/kernel_interface.c821
-rw-r--r--src/charon/threads/kernel_interface.h34
-rwxr-xr-xsrc/charon/threads/stroke_interface.c120
-rw-r--r--src/libstrongswan/utils/host.c33
-rw-r--r--src/libstrongswan/utils/host.h15
-rw-r--r--src/libstrongswan/utils/identification.c50
-rw-r--r--src/libstrongswan/utils/identification.h2
-rw-r--r--src/libstrongswan/utils/iterator.h17
-rw-r--r--src/libstrongswan/utils/linked_list.c43
-rw-r--r--src/starter/confread.c3
-rw-r--r--src/starter/starterstroke.c3
-rw-r--r--src/stroke/stroke.c4
-rw-r--r--src/stroke/stroke.h2
96 files changed, 8375 insertions, 8298 deletions
diff --git a/configure.in b/configure.in
index 89ab54fff..aa8b624fa 100644
--- a/configure.in
+++ b/configure.in
@@ -45,6 +45,12 @@ AC_ARG_WITH(
[AC_DEFINE_UNQUOTED(DEV_RANDOM, "$withval")],
[AC_DEFINE_UNQUOTED(DEV_RANDOM, "/dev/random")]
)
+AC_ARG_WITH(
+ [resolv-conf],
+ AS_HELP_STRING([--with-resolv-conf=file],[set the file to store DNS server information other than "sysconfdir/resolv.conf"]),
+ [AC_DEFINE_UNQUOTED(RESOLV_CONF, "$withval")],
+ [AC_DEFINE_UNQUOTED(RESOLV_CONF, "${sysconfdir}/resolv.conf")]
+)
AC_ARG_WITH(
[urandom-device],
diff --git a/src/charon/Makefile.am b/src/charon/Makefile.am
index c4a6afa5f..e37087486 100644
--- a/src/charon/Makefile.am
+++ b/src/charon/Makefile.am
@@ -17,21 +17,25 @@ config/policies/local_policy_store.c config/policies/policy_store.h config/polic
config/credentials/local_credential_store.c config/credentials/local_credential_store.h \
config/credentials/credential_store.h config/traffic_selector.c config/traffic_selector.h \
config/proposal.c config/proposal.h config/configuration.c config/configuration.h \
-sa/transactions/transaction.h sa/transactions/transaction.c \
-sa/transactions/ike_sa_init.h sa/transactions/ike_sa_init.c \
-sa/transactions/ike_auth.h sa/transactions/ike_auth.c \
-sa/transactions/create_child_sa.h sa/transactions/create_child_sa.c \
-sa/transactions/delete_child_sa.h sa/transactions/delete_child_sa.c \
-sa/transactions/dead_peer_detection.h sa/transactions/dead_peer_detection.c \
-sa/transactions/delete_ike_sa.h sa/transactions/delete_ike_sa.c \
-sa/transactions/rekey_ike_sa.h sa/transactions/rekey_ike_sa.c \
-sa/authenticators/authenticator.h sa/authenticators/authenticator.c \
-sa/authenticators/rsa_authenticator.h sa/authenticators/rsa_authenticator.c \
-sa/authenticators/psk_authenticator.h sa/authenticators/psk_authenticator.c \
sa/authenticators/eap_authenticator.h sa/authenticators/eap_authenticator.c \
sa/authenticators/eap/eap_method.h sa/authenticators/eap/eap_method.c \
sa/child_sa.c sa/child_sa.h sa/ike_sa.c sa/ike_sa.h sa/ike_sa_manager.c sa/ike_sa_manager.h \
-sa/ike_sa_id.c sa/ike_sa_id.h encoding/payloads/encryption_payload.c \
+sa/ike_sa_id.c sa/ike_sa_id.h sa/tasks/task.c sa/tasks/task.h \
+sa/tasks/ike_init.c sa/tasks/ike_init.h \
+sa/tasks/ike_natd.c sa/tasks/ike_natd.h \
+sa/tasks/ike_auth.c sa/tasks/ike_auth.h \
+sa/tasks/ike_config.c sa/tasks/ike_config.h \
+sa/tasks/ike_cert.c sa/tasks/ike_cert.h \
+sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \
+sa/tasks/ike_delete.c sa/tasks/ike_delete.h \
+sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \
+sa/tasks/child_create.c sa/tasks/child_create.h \
+sa/tasks/child_delete.c sa/tasks/child_delete.h \
+sa/tasks/child_rekey.c sa/tasks/child_rekey.h \
+sa/authenticators/authenticator.c sa/authenticators/authenticator.h \
+sa/authenticators/rsa_authenticator.c sa/authenticators/rsa_authenticator.h \
+sa/authenticators/psk_authenticator.c sa/authenticators/psk_authenticator.h \
+sa/task_manager.c sa/task_manager.h encoding/payloads/encryption_payload.c \
encoding/payloads/cert_payload.c encoding/payloads/payload.h encoding/payloads/traffic_selector_substructure.c \
encoding/payloads/configuration_attribute.h encoding/payloads/proposal_substructure.h \
encoding/payloads/transform_attribute.c encoding/payloads/transform_attribute.h \
@@ -51,10 +55,10 @@ encoding/payloads/vendor_id_payload.h encoding/payloads/proposal_substructure.c
encoding/parser.h encoding/message.c encoding/generator.c encoding/message.h encoding/generator.h \
encoding/parser.c daemon.c daemon.h network/packet.c \
network/socket.c network/packet.h network/socket.h queues/jobs/job.h queues/jobs/job.c \
-queues/jobs/retransmit_request_job.h queues/jobs/initiate_job.h \
+queues/jobs/retransmit_job.h queues/jobs/initiate_job.h \
queues/jobs/incoming_packet_job.h queues/jobs/incoming_packet_job.c \
queues/jobs/delete_ike_sa_job.c queues/jobs/delete_ike_sa_job.h \
-queues/jobs/retransmit_request_job.c queues/jobs/initiate_job.c \
+queues/jobs/retransmit_job.c queues/jobs/initiate_job.c \
queues/jobs/send_keepalive_job.c queues/jobs/send_keepalive_job.h \
queues/jobs/rekey_child_sa_job.c queues/jobs/rekey_child_sa_job.h queues/jobs/delete_child_sa_job.c queues/jobs/delete_child_sa_job.h \
queues/jobs/send_dpd_job.c queues/jobs/send_dpd_job.h queues/jobs/route_job.c queues/jobs/route_job.h \
diff --git a/src/charon/bus/bus.c b/src/charon/bus/bus.c
index afed16656..740663d5c 100644
--- a/src/charon/bus/bus.c
+++ b/src/charon/bus/bus.c
@@ -180,7 +180,7 @@ static int get_thread_number(private_bus_t *this)
static void add_listener(private_bus_t *this, bus_listener_t *listener)
{
pthread_mutex_lock(&this->mutex);
- this->listeners->insert_last(this->listeners, (void*)listener);
+ this->listeners->insert_last(this->listeners, listener);
pthread_mutex_unlock(&this->mutex);
}
@@ -301,7 +301,12 @@ static void vsignal(private_bus_t *this, signal_t signal, level_t level,
va_list args_copy;
va_copy(args_copy, args);
- listener->signal(listener, signal, level, thread, ike_sa, format, args_copy);
+ if (!listener->signal(listener, signal, level, thread,
+ ike_sa, format, args_copy))
+ {
+ /* unregister listener if requested */
+ iterator->remove(iterator);
+ }
va_end(args_copy);
}
iterator->destroy(iterator);
diff --git a/src/charon/bus/bus.h b/src/charon/bus/bus.h
index 974d460b9..200525fb7 100644
--- a/src/charon/bus/bus.h
+++ b/src/charon/bus/bus.h
@@ -224,6 +224,8 @@ struct bus_listener_t {
* an additional informational or error message with a printf() like
* variable argument list. This is in the va_list form, as forwarding
* a "..." parameters to functions is not (cleanly) possible.
+ * The implementing signal function returns TRUE to stay registered
+ * to the bus, or FALSE to unregister itself.
*
* @param this listener
* @param singal kind of the signal (up, down, rekeyed, ...)
@@ -232,8 +234,9 @@ struct bus_listener_t {
* @param ike_sa IKE_SA associated to the event
* @param format printf() style format string
* @param args vprintf() style va_list argument list
+ " @return TRUE to stay registered, FALSE to unregister
*/
- void (*signal) (bus_listener_t *this, signal_t signal, level_t level,
+ bool (*signal) (bus_listener_t *this, signal_t signal, level_t level,
int thread, ike_sa_t *ike_sa, char* format, va_list args);
};
diff --git a/src/charon/bus/listeners/file_logger.c b/src/charon/bus/listeners/file_logger.c
index 4c9e13b3f..14f9f72cf 100644
--- a/src/charon/bus/listeners/file_logger.c
+++ b/src/charon/bus/listeners/file_logger.c
@@ -53,7 +53,7 @@ struct private_file_logger_t {
/**
* Implementation of bus_listener_t.signal.
*/
-static void signal_(private_file_logger_t *this, signal_t signal, level_t level,
+static bool signal_(private_file_logger_t *this, signal_t signal, level_t level,
int thread, ike_sa_t* ike_sa, char *format, va_list args)
{
if (level <= this->levels[SIG_TYPE(signal)])
@@ -76,6 +76,8 @@ static void signal_(private_file_logger_t *this, signal_t signal, level_t level,
current = next;
}
}
+ /* always stay registered */
+ return TRUE;
}
/**
@@ -114,7 +116,7 @@ file_logger_t *file_logger_create(FILE *out)
private_file_logger_t *this = malloc_thing(private_file_logger_t);
/* public functions */
- this->public.listener.signal = (void(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_;
+ this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_;
this->public.set_level = (void(*)(file_logger_t*,signal_t,level_t))set_level;
this->public.destroy = (void(*)(file_logger_t*))destroy;
diff --git a/src/charon/bus/listeners/sys_logger.c b/src/charon/bus/listeners/sys_logger.c
index 58c59c4f7..d26d14dc0 100644
--- a/src/charon/bus/listeners/sys_logger.c
+++ b/src/charon/bus/listeners/sys_logger.c
@@ -54,7 +54,7 @@ struct private_sys_logger_t {
/**
* Implementation of bus_listener_t.signal.
*/
-static void signal_(private_sys_logger_t *this, signal_t signal, level_t level,
+static bool signal_(private_sys_logger_t *this, signal_t signal, level_t level,
int thread, ike_sa_t* ike_sa, char *format, va_list args)
{
if (level <= this->levels[SIG_TYPE(signal)])
@@ -78,6 +78,8 @@ static void signal_(private_sys_logger_t *this, signal_t signal, level_t level,
current = next;
}
}
+ /* always stay registered */
+ return TRUE;
}
/**
@@ -117,7 +119,7 @@ sys_logger_t *sys_logger_create(int facility)
private_sys_logger_t *this = malloc_thing(private_sys_logger_t);
/* public functions */
- this->public.listener.signal = (void(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_;
+ this->public.listener.signal = (bool(*)(bus_listener_t*,signal_t,level_t,int,ike_sa_t*,char*,va_list))signal_;
this->public.set_level = (void(*)(sys_logger_t*,signal_t,level_t))set_level;
this->public.destroy = (void(*)(sys_logger_t*))destroy;
diff --git a/src/charon/config/configuration.c b/src/charon/config/configuration.c
index 39fc4d922..f43afdaa4 100755
--- a/src/charon/config/configuration.c
+++ b/src/charon/config/configuration.c
@@ -33,25 +33,11 @@
#define HALF_OPEN_IKE_SA_TIMEOUT 30000
/**
- * The retransmission algorithm uses a multiple sequences.
- * Each sequence contains multiple retransmits. Those retransmits
- * are sent using a exponential backoff algorithm. The sequences
- * are retried with linear timings:
+ * Retransmission uses a backoff algorithm. The timeout is calculated using
+ * TIMEOUT * (BASE ** try).
+ * When try reaches TRIES, retransmission is given up.
*
- * <------sequence---------><------sequence---------><------sequence--------->
- *
- * T-R---R-----R---------R--R-R---R-----R---------R--R-R---R-----R---------R--X
- *
- * T = first transmit
- * R = retransmit
- * X = giving up, peer is dead
- *
- * if (retransmit >= TRIES * sequences)
- * => abort
- * TIMEOUT * (BASE ** (try % TRIES))
- *
- * Using an initial TIMEOUT of 4s, a BASE of 1.8, 5 TRIES
- * per sequnce and 3 sequences, this gives us:
+ * Using an initial TIMEOUT of 4s, a BASE of 1.8, and 5 TRIES gives us:
*
* | relative | absolute
* ---------------------------------------------------------
@@ -61,22 +47,8 @@
* 4s * (1.8 ** (3 % 5)) = 23s 47s
* 4s * (1.8 ** (4 % 5)) = 42s 89s
* 4s * (1.8 ** (5 % 5)) = 76s 165s
- * 4s * (1.8 ** (6 % 5)) = 4s 169s
- * 4s * (1.8 ** (7 % 5)) = 7s 176s
- * 4s * (1.8 ** (8 % 5)) = 13s 189s
- * 4s * (1.8 ** (9 % 5)) = 23s 212s
- * 4s * (1.8 ** (10 % 5)) = 42s 254s
- * 4s * (1.8 ** (11 % 5)) = 76s 330s
- * 4s * (1.8 ** (12 % 5)) = 4s 334
- * 4s * (1.8 ** (13 % 5)) = 7s 341s
- * 4s * (1.8 ** (14 % 5)) = 13s 354s
- * 4s * (1.8 ** (15 % 5)) = 23s 377s
- * 4s * (1.8 ** (16 % 5)) = 42s 419s
- * 4s * (1.8 ** (17 % 5)) = 76s 495s
*
- * If the configuration uses 1 sequence, the peer is considered dead
- * after 2min 45s when no reply comes in. If it uses 3 sequences, after
- * 8min 15s the DPD action is executed...
+ * The peer is considered dead after 2min 45s when no reply comes in.
*/
/**
@@ -119,17 +91,15 @@ struct private_configuration_t {
* Implementation of configuration_t.get_retransmit_timeout.
*/
static u_int32_t get_retransmit_timeout (private_configuration_t *this,
- u_int32_t retransmit_count,
- u_int32_t max_sequences)
+ u_int32_t retransmit_count)
{
- if (max_sequences != 0 &&
- retransmit_count >= RETRANSMIT_TRIES * max_sequences)
+ if (retransmit_count > RETRANSMIT_TRIES)
{
/* give up */
return 0;
}
- return (u_int32_t)(RETRANSMIT_TIMEOUT *
- pow(RETRANSMIT_BASE, retransmit_count % RETRANSMIT_TRIES));
+ return (u_int32_t)
+ (RETRANSMIT_TIMEOUT * pow(RETRANSMIT_BASE, retransmit_count));
}
/**
@@ -165,7 +135,7 @@ configuration_t *configuration_create()
/* public functions */
this->public.destroy = (void(*)(configuration_t*))destroy;
- this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t,u_int32_t))get_retransmit_timeout;
+ this->public.get_retransmit_timeout = (u_int32_t (*) (configuration_t*,u_int32_t))get_retransmit_timeout;
this->public.get_half_open_ike_sa_timeout = (u_int32_t (*) (configuration_t*)) get_half_open_ike_sa_timeout;
this->public.get_keepalive_interval = (u_int32_t (*) (configuration_t*)) get_keepalive_interval;
diff --git a/src/charon/config/configuration.h b/src/charon/config/configuration.h
index 2bb0b106e..77d1fd85d 100755
--- a/src/charon/config/configuration.h
+++ b/src/charon/config/configuration.h
@@ -40,19 +40,15 @@ struct configuration_t {
/**
* @brief Returns the retransmit timeout.
*
- * A return value of zero means the request should not be retransmitted again.
- * The retransmission algorithm uses sequences of retransmits, in which
- * every sequence contains exponential delayed retransmits. These
- * sequences are compareable to the keyingtries mechanism used in pluto.
+ * A return value of zero means the request should not be
+ * retransmitted again.
*
* @param this calling object
* @param retransmitted number of times a message was retransmitted so far
- * @param max_sequences maximum number of retransmission sequences to allow
- * @return time in milliseconds, when to schedule next retransmit
+ * @return time in milliseconds, when to do next retransmit
*/
u_int32_t (*get_retransmit_timeout) (configuration_t *this,
- u_int32_t retransmitted,
- u_int32_t max_sequences);
+ u_int32_t retransmitted);
/**
* @brief Returns the timeout for an half open IKE_SA in ms.
diff --git a/src/charon/config/policies/policy.c b/src/charon/config/policies/policy.c
index e68a8ad2b..fa23955fb 100644
--- a/src/charon/config/policies/policy.c
+++ b/src/charon/config/policies/policy.c
@@ -79,6 +79,16 @@ struct private_policy_t {
identification_t *other_id;
/**
+ * virtual IP to use locally
+ */
+ host_t *my_virtual_ip;
+
+ /**
+ * virtual IP to use remotly
+ */
+ host_t *other_virtual_ip;
+
+ /**
* Method to use for own authentication data
*/
auth_method_t auth_method;
@@ -209,7 +219,8 @@ static eap_type_t get_eap_type(private_policy_t *this)
/**
* Get traffic selectors, with wildcard-address update
*/
-static linked_list_t *get_traffic_selectors(private_policy_t *this, linked_list_t *list, host_t *host)
+static linked_list_t *get_traffic_selectors(private_policy_t *this,
+ linked_list_t *list, host_t *host)
{
iterator_t *iterator;
traffic_selector_t *current;
@@ -222,7 +233,10 @@ static linked_list_t *get_traffic_selectors(private_policy_t *this, linked_list_
/* we make a copy of the TS, this allows us to update wildcard
* addresses in it. We won't pollute the shared policy. */
current = current->clone(current);
- current->update_address_range(current, host);
+ if (host)
+ {
+ current->update_address_range(current, host);
+ }
result->insert_last(result, (void*)current);
}
@@ -269,7 +283,10 @@ static linked_list_t *select_traffic_selectors(private_policy_t *this,
/* we make a copy of the TS, this allows us to update wildcard
* addresses in it. We won't pollute the shared policy. */
stored_ts = stored_ts->clone(stored_ts);
- stored_ts->update_address_range(stored_ts, host);
+ if (host)
+ {
+ stored_ts->update_address_range(stored_ts, host);
+ }
supplied_iter->reset(supplied_iter);
/* iterate over all supplied traffic selectors */
@@ -457,6 +474,30 @@ static mode_t get_mode(private_policy_t *this)
}
/**
+ * Implementation of policy_t.get_virtual_ip.
+ */
+static host_t* get_virtual_ip(private_policy_t *this, host_t *suggestion)
+{
+ if (suggestion == NULL)
+ {
+ if (this->my_virtual_ip)
+ {
+ return this->my_virtual_ip->clone(this->my_virtual_ip);
+ }
+ return NULL;
+ }
+ if (this->other_virtual_ip)
+ {
+ return this->other_virtual_ip->clone(this->other_virtual_ip);
+ }
+ if (suggestion->is_anyaddr(suggestion))
+ {
+ return NULL;
+ }
+ return suggestion->clone(suggestion);
+}
+
+/**
* Implements policy_t.get_ref.
*/
static void get_ref(private_policy_t *this)
@@ -477,14 +518,8 @@ static void destroy(private_policy_t *this)
this->other_ts->destroy_offset(this->other_ts, offsetof(traffic_selector_t, destroy));
/* delete certification authorities */
- if (this->my_ca)
- {
- this->my_ca->destroy(this->my_ca);
- }
- if (this->other_ca)
- {
- this->other_ca->destroy(this->other_ca);
- }
+ DESTROY_IF(this->my_ca);
+ DESTROY_IF(this->other_ca);
/* delete updown script */
if (this->updown)
@@ -495,6 +530,8 @@ static void destroy(private_policy_t *this)
/* delete ids */
this->my_id->destroy(this->my_id);
this->other_id->destroy(this->other_id);
+ DESTROY_IF(this->my_virtual_ip);
+ DESTROY_IF(this->other_virtual_ip);
free(this->name);
free(this);
@@ -505,6 +542,7 @@ static void destroy(private_policy_t *this)
* Described in header-file
*/
policy_t *policy_create(char *name, identification_t *my_id, identification_t *other_id,
+ host_t *my_virtual_ip, host_t *other_virtual_ip,
auth_method_t auth_method, eap_type_t eap_type,
u_int32_t hard_lifetime, u_int32_t soft_lifetime,
u_int32_t jitter, char *updown, bool hostaccess,
@@ -536,6 +574,7 @@ policy_t *policy_create(char *name, identification_t *my_id, identification_t *o
this->public.get_soft_lifetime = (u_int32_t (*) (policy_t *))get_soft_lifetime;
this->public.get_hard_lifetime = (u_int32_t (*) (policy_t *))get_hard_lifetime;
this->public.get_mode = (mode_t (*) (policy_t *))get_mode;
+ this->public.get_virtual_ip = (host_t* (*)(policy_t*,host_t*))get_virtual_ip;
this->public.get_ref = (void (*) (policy_t*))get_ref;
this->public.destroy = (void (*) (policy_t*))destroy;
@@ -543,6 +582,8 @@ policy_t *policy_create(char *name, identification_t *my_id, identification_t *o
this->name = strdup(name);
this->my_id = my_id;
this->other_id = other_id;
+ this->my_virtual_ip = my_virtual_ip;
+ this->other_virtual_ip = other_virtual_ip;
this->auth_method = auth_method;
this->eap_type = eap_type;
this->hard_lifetime = hard_lifetime;
diff --git a/src/charon/config/policies/policy.h b/src/charon/config/policies/policy.h
index a2d9ae8d0..d8916b29e 100644
--- a/src/charon/config/policies/policy.h
+++ b/src/charon/config/policies/policy.h
@@ -329,6 +329,25 @@ struct policy_t {
mode_t (*get_mode) (policy_t *this);
/**
+ * @brief Get a virtual IP for the local or the remote host.
+ *
+ * By supplying NULL as IP, an IP for the local host is requested. It
+ * may be %any or specific.
+ * By supplying %any as host, an IP from the pool is selected to be
+ * served to the peer.
+ * If a specified host is supplied, it is checked if this address
+ * is acceptable to serve to the peer. If so, it is returned. Otherwise,
+ * an alternative IP is returned.
+ * In any mode, this call may return NULL indicating virtual IP should
+ * not be used.
+ *
+ * @param this policy
+ * @param suggestion NULL, %any or specific, see description
+ * @return clone of an IP to use, or NULL
+ */
+ host_t* (*get_virtual_ip) (policy_t *this, host_t *suggestion);
+
+ /**
* @brief Get a new reference.
*
* Get a new reference to this policy by increasing
@@ -356,6 +375,8 @@ struct policy_t {
* @brief Create a configuration object for IKE_AUTH and later.
*
* name-string gets cloned, ID's not.
+ * Virtual IPs are used if they are != NULL. A %any host means the virtual
+ * IP should be obtained from the other peer.
* Lifetimes are in seconds. To prevent to peers to start rekeying at the
* same time, a jitter may be specified. Rekeying of an SA starts at
* (soft_lifetime - random(0, jitter)). After a successful rekeying,
@@ -366,6 +387,8 @@ struct policy_t {
* @param name name of the policy
* @param my_id identification_t for ourselves
* @param other_id identification_t for the remote guy
+ * @param my_virtual_ip virtual IP for local host, or NULL
+ * @param other_virtual_ip virtual IP for remote host, or NULL
* @param auth_method Authentication method to use for our(!) auth data
* @param eap_type EAP type to use for peer authentication
* @param hard_lifetime lifetime before deleting an SA
@@ -381,6 +404,7 @@ struct policy_t {
*/
policy_t *policy_create(char *name,
identification_t *my_id, identification_t *other_id,
+ host_t *my_virtual_ip, host_t *other_virtual_ip,
auth_method_t auth_method, eap_type_t eap_type,
u_int32_t hard_lifetime, u_int32_t soft_lifetime,
u_int32_t jitter, char *updown, bool hostaccess,
diff --git a/src/charon/config/traffic_selector.c b/src/charon/config/traffic_selector.c
index 2afeb5e48..519c90f77 100644
--- a/src/charon/config/traffic_selector.c
+++ b/src/charon/config/traffic_selector.c
@@ -6,6 +6,7 @@
*/
/*
+ * Copyright (C) 2007 Tobias Brunner
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -494,6 +495,26 @@ static void update_address_range(private_traffic_selector_t *this, host_t *host)
}
/**
+ * Implements traffic_selector_t.includes.
+ */
+static bool includes(private_traffic_selector_t *this, host_t *host)
+{
+ chunk_t addr;
+ int family = host->get_family(host);
+
+ if ((family == AF_INET && this->type == TS_IPV4_ADDR_RANGE) ||
+ (family == AF_INET6 && this->type == TS_IPV6_ADDR_RANGE))
+ {
+ addr = host->get_address(host);
+
+ return memcmp(this->from, addr.ptr, addr.len) <= 0 &&
+ memcmp(this->to, addr.ptr, addr.len) >= 0;
+ }
+
+ return FALSE;
+}
+
+/**
* Implements traffic_selector_t.clone.
*/
static traffic_selector_t *clone_(private_traffic_selector_t *this)
@@ -698,6 +719,7 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts
this->public.get_type = (ts_type_t(*)(traffic_selector_t*))get_type;
this->public.get_protocol = (u_int8_t(*)(traffic_selector_t*))get_protocol;
this->public.is_host = (bool(*)(traffic_selector_t*,host_t*))is_host;
+ this->public.includes = (bool(*)(traffic_selector_t*,host_t*))includes;
this->public.update_address_range = (void(*)(traffic_selector_t*,host_t*))update_address_range;
this->public.clone = (traffic_selector_t*(*)(traffic_selector_t*))clone_;
this->public.destroy = (void(*)(traffic_selector_t*))destroy;
@@ -709,3 +731,6 @@ static private_traffic_selector_t *traffic_selector_create(u_int8_t protocol, ts
return this;
}
+
+/* vim: set ts=4 sw=4 noet: */
+
diff --git a/src/charon/config/traffic_selector.h b/src/charon/config/traffic_selector.h
index 5d1ccaf2f..7728ba307 100644
--- a/src/charon/config/traffic_selector.h
+++ b/src/charon/config/traffic_selector.h
@@ -6,6 +6,7 @@
*/
/*
+ * Copyright (C) 2007 Tobias Brunner
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -105,7 +106,7 @@ struct traffic_selector_t {
*
* Chunk is in network order gets allocated.
*
- * @param this calling object
+ * @param this called object
* @return chunk containing the address
*/
chunk_t (*get_from_address) (traffic_selector_t *this);
@@ -115,7 +116,7 @@ struct traffic_selector_t {
*
* Chunk is in network order gets allocated.
*
- * @param this calling object
+ * @param this called object
* @return chunk containing the address
*/
chunk_t (*get_to_address) (traffic_selector_t *this);
@@ -126,7 +127,7 @@ struct traffic_selector_t {
* Port is in host order, since the parser converts it.
* Size depends on protocol.
*
- * @param this calling object
+ * @param this called object
* @return port
*/
u_int16_t (*get_from_port) (traffic_selector_t *this);
@@ -137,7 +138,7 @@ struct traffic_selector_t {
* Port is in host order, since the parser converts it.
* Size depends on protocol.
*
- * @param this calling object
+ * @param this called object
* @return port
*/
u_int16_t (*get_to_port) (traffic_selector_t *this);
@@ -145,7 +146,7 @@ struct traffic_selector_t {
/**
* @brief Get the type of the traffic selector.
*
- * @param this calling obect
+ * @param this called object
* @return ts_type_t specifying the type
*/
ts_type_t (*get_type) (traffic_selector_t *this);
@@ -153,7 +154,7 @@ struct traffic_selector_t {
/**
* @brief Get the protocol id of this ts.
*
- * @param this calling obect
+ * @param this called object
* @return protocol id
*/
u_int8_t (*get_protocol) (traffic_selector_t *this);
@@ -167,7 +168,7 @@ struct traffic_selector_t {
* If host is NULL, the traffic selector is checked if it is a single host,
* but not a specific one.
*
- * @param this calling obect
+ * @param this called object
* @param host host_t specifying the address range
*/
bool (*is_host) (traffic_selector_t *this, host_t* host);
@@ -180,7 +181,7 @@ struct traffic_selector_t {
* starts from the supplied address and also ends there
* (which means it is a one-host-address-range ;-).
*
- * @param this calling obect
+ * @param this called object
* @param host host_t specifying the address range
*/
void (*update_address_range) (traffic_selector_t *this, host_t* host);
@@ -193,11 +194,19 @@ struct traffic_selector_t {
* @return pointer to a string.
*/
bool (*equals) (traffic_selector_t *this, traffic_selector_t *other);
+
+ /**
+ * @brief Check if a specific host is included in the address range of this traffic selector.
+ *
+ * @param this called object
+ * @param host the host to check
+ */
+ bool (*includes) (traffic_selector_t *this, host_t *host);
/**
* @brief Destroys the ts object
*
- * @param this calling object
+ * @param this called object
*/
void (*destroy) (traffic_selector_t *this);
};
@@ -269,3 +278,6 @@ traffic_selector_t *traffic_selector_create_from_subnet(
u_int8_t protocol, u_int16_t port);
#endif /* TRAFFIC_SELECTOR_H_ */
+
+/* vim: set ts=4 sw=4 noet: */
+
diff --git a/src/charon/encoding/message.c b/src/charon/encoding/message.c
index fb37c996d..acc3abd1b 100644
--- a/src/charon/encoding/message.c
+++ b/src/charon/encoding/message.c
@@ -345,6 +345,7 @@ static status_t get_payload_rule(private_message_t *this, payload_type_t payload
*/
static void set_ike_sa_id (private_message_t *this,ike_sa_id_t *ike_sa_id)
{
+ DESTROY_IF(this->ike_sa_id);
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
}
@@ -490,6 +491,29 @@ static void add_payload(private_message_t *this, payload_t *payload)
}
/**
+ * Implementation of message_t.add_notify.
+ */
+static void add_notify(private_message_t *this, bool flush, notify_type_t type,
+ chunk_t data)
+{
+ notify_payload_t *notify;
+ payload_t *payload;
+
+ if (flush)
+ {
+ while (this->payloads->remove_last(this->payloads,
+ (void**)&payload) == SUCCESS)
+ {
+ payload->destroy(payload);
+ }
+ }
+ notify = notify_payload_create();
+ notify->set_notify_type(notify, type);
+ notify->set_notification_data(notify, data);
+ add_payload(this, (payload_t*)notify);
+}
+
+/**
* Implementation of message_t.set_source.
*/
static void set_source(private_message_t *this, host_t *host)
@@ -522,7 +546,7 @@ static host_t * get_destination(private_message_t *this)
}
/**
- * Implementation of message_t.get_destination.
+ * Implementation of message_t.get_payload_iterator.
*/
static iterator_t *get_payload_iterator(private_message_t *this)
{
@@ -530,6 +554,27 @@ static iterator_t *get_payload_iterator(private_message_t *this)
}
/**
+ * Implementation of message_t.get_payload.
+ */
+static payload_t *get_payload(private_message_t *this, payload_type_t type)
+{
+ payload_t *current, *found = NULL;
+ iterator_t *iterator;
+
+ iterator = this->payloads->create_iterator(this->payloads, TRUE);
+ while (iterator->iterate(iterator, (void**)&current))
+ {
+ if (current->get_type(current) == type)
+ {
+ found = current;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return found;
+}
+
+/**
* output handler in printf()
*/
static int print(FILE *stream, const struct printf_info *info,
@@ -786,6 +831,10 @@ static status_t generate(private_message_t *this, crypter_t *crypter, signer_t*
*/
static packet_t *get_packet (private_message_t *this)
{
+ if (this->packet == NULL)
+ {
+ return NULL;
+ }
return this->packet->clone(this->packet);
}
@@ -794,6 +843,10 @@ static packet_t *get_packet (private_message_t *this)
*/
static chunk_t get_packet_data (private_message_t *this)
{
+ if (this->packet == NULL)
+ {
+ return chunk_empty;
+ }
return chunk_clone(this->packet->get_data(this->packet));
}
@@ -1147,7 +1200,6 @@ static status_t parse_body(private_message_t *this, crypter_t *crypter, signer_t
status = verify(this);
if (status != SUCCESS)
{
- DBG1(DBG_ENC, "verification of message failed");
return status;
}
@@ -1191,12 +1243,14 @@ message_t *message_create_from_packet(packet_t *packet)
this->public.set_request = (void(*)(message_t*, bool))set_request;
this->public.get_request = (bool(*)(message_t*))get_request;
this->public.add_payload = (void(*)(message_t*,payload_t*))add_payload;
+ this->public.add_notify = (void(*)(message_t*,bool,notify_type_t,chunk_t))add_notify;
this->public.generate = (status_t (*) (message_t *,crypter_t*,signer_t*,packet_t**)) generate;
this->public.set_source = (void (*) (message_t*,host_t*)) set_source;
this->public.get_source = (host_t * (*) (message_t*)) get_source;
this->public.set_destination = (void (*) (message_t*,host_t*)) set_destination;
this->public.get_destination = (host_t * (*) (message_t*)) get_destination;
this->public.get_payload_iterator = (iterator_t * (*) (message_t *)) get_payload_iterator;
+ this->public.get_payload = (payload_t * (*) (message_t *, payload_type_t)) get_payload;
this->public.parse_header = (status_t (*) (message_t *)) parse_header;
this->public.parse_body = (status_t (*) (message_t *,crypter_t*,signer_t*)) parse_body;
this->public.get_packet = (packet_t * (*) (message_t*)) get_packet;
diff --git a/src/charon/encoding/message.h b/src/charon/encoding/message.h
index dfb6d64af..73c2e05c6 100644
--- a/src/charon/encoding/message.h
+++ b/src/charon/encoding/message.h
@@ -184,6 +184,21 @@ struct message_t {
void (*add_payload) (message_t *this, payload_t *payload);
/**
+ * @brief Build a notify payload and add it to the message.
+ *
+ * This is a helper method to create notify messages or add
+ * notify payload to messages. The flush parameter specifies if existing
+ * payloads should get removed before appending the notify.
+ *
+ * @param this message_t object
+ * @param flush TRUE to remove existing payloads
+ * @param type type of the notify
+ * @param data a chunk of data to add to the notify, gets cloned
+ */
+ void (*add_notify) (message_t *this, bool flush, notify_type_t type,
+ chunk_t data);
+
+ /**
* @brief Parses header of message.
*
* Begins parisng of a message created via message_create_from_packet().
@@ -304,6 +319,17 @@ struct message_t {
iterator_t * (*get_payload_iterator) (message_t *this);
/**
+ * @brief Find a payload of a spicific type.
+ *
+ * Returns the first occurance.
+ *
+ * @param this message_t object
+ * @param type type of the payload to find
+ * @return payload, or NULL if no such payload found
+ */
+ payload_t* (*get_payload) (message_t *this, payload_type_t type);
+
+ /**
* @brief Returns a clone of the internal stored packet_t object.
*
* @param this message_t object
diff --git a/src/charon/encoding/payloads/cert_payload.c b/src/charon/encoding/payloads/cert_payload.c
index 2e690b45d..8f477ffd0 100644
--- a/src/charon/encoding/payloads/cert_payload.c
+++ b/src/charon/encoding/payloads/cert_payload.c
@@ -270,7 +270,7 @@ cert_payload_t *cert_payload_create()
/* private variables */
this->critical = FALSE;
this->next_payload = NO_PAYLOAD;
- this->payload_length =CERT_PAYLOAD_HEADER_LENGTH;
+ this->payload_length = CERT_PAYLOAD_HEADER_LENGTH;
this->cert_data = chunk_empty;
return (&(this->public));
diff --git a/src/charon/encoding/payloads/certreq_payload.c b/src/charon/encoding/payloads/certreq_payload.c
index 86f2e3524..fcddcf971 100644
--- a/src/charon/encoding/payloads/certreq_payload.c
+++ b/src/charon/encoding/payloads/certreq_payload.c
@@ -306,7 +306,10 @@ certreq_payload_t *certreq_payload_create_from_cacerts(void)
int count = iterator->get_count(iterator);
if (count == 0)
+ {
+ iterator->destroy(iterator);
return NULL;
+ }
this = certreq_payload_create();
keyids = chunk_alloc(count * HASH_SIZE_SHA1);
diff --git a/src/charon/encoding/payloads/configuration_attribute.c b/src/charon/encoding/payloads/configuration_attribute.c
index e7000e1b5..0aa82169f 100644
--- a/src/charon/encoding/payloads/configuration_attribute.c
+++ b/src/charon/encoding/payloads/configuration_attribute.c
@@ -27,6 +27,7 @@
#include <encoding/payloads/encodings.h>
#include <library.h>
+#include <daemon.h>
typedef struct private_configuration_attribute_t private_configuration_attribute_t;
@@ -50,7 +51,6 @@ struct private_configuration_attribute_t {
* Length of the attribute.
*/
u_int16_t attribute_length;
-
/**
* Attribute value as chunk.
@@ -58,7 +58,7 @@ struct private_configuration_attribute_t {
chunk_t attribute_value;
};
-ENUM_BEGIN(configuration_attribute_type_name, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS,
+ENUM_BEGIN(configuration_attribute_type_names, INTERNAL_IP4_ADDRESS, INTERNAL_IP6_ADDRESS,
"INTERNAL_IP4_ADDRESS",
"INTERNAL_IP4_NETMASK",
"INTERNAL_IP4_DNS",
@@ -67,14 +67,14 @@ ENUM_BEGIN(configuration_attribute_type_name, INTERNAL_IP4_ADDRESS, INTERNAL_IP6
"INTERNAL_IP4_DHCP",
"APPLICATION_VERSION",
"INTERNAL_IP6_ADDRESS");
-ENUM_NEXT(configuration_attribute_type_name, INTERNAL_IP6_DNS, INTERNAL_IP6_SUBNET, INTERNAL_IP6_ADDRESS,
+ENUM_NEXT(configuration_attribute_type_names, INTERNAL_IP6_DNS, INTERNAL_IP6_SUBNET, INTERNAL_IP6_ADDRESS,
"INTERNAL_IP6_DNS",
"INTERNAL_IP6_NBNS",
"INTERNAL_IP6_DHCP",
"INTERNAL_IP4_SUBNET",
"SUPPORTED_ATTRIBUTES",
"INTERNAL_IP6_SUBNET");
-ENUM_END(configuration_attribute_type_name, INTERNAL_IP6_SUBNET);
+ENUM_END(configuration_attribute_type_names, INTERNAL_IP6_SUBNET);
/**
* Encoding rules to parse or generate a configuration attribute.
@@ -111,6 +111,14 @@ encoding_rule_t configuration_attribute_encodings[] = {
*/
static status_t verify(private_configuration_attribute_t *this)
{
+ bool failed = FALSE;
+
+ if (this->attribute_length != this->attribute_value.len)
+ {
+ DBG1(DBG_ENC, "invalid attribute length");
+ return FAILED;
+ }
+
switch (this->attribute_type)
{
case INTERNAL_IP4_ADDRESS:
@@ -119,27 +127,54 @@ static status_t verify(private_configuration_attribute_t *this)
case INTERNAL_IP4_NBNS:
case INTERNAL_ADDRESS_EXPIRY:
case INTERNAL_IP4_DHCP:
- case APPLICATION_VERSION:
+ if (this->attribute_length != 0 && this->attribute_length != 4)
+ {
+ failed = TRUE;
+ }
+ break;
+ case INTERNAL_IP4_SUBNET:
+ if (this->attribute_length != 0 && this->attribute_length != 8)
+ {
+ failed = TRUE;
+ }
+ break;
case INTERNAL_IP6_ADDRESS:
+ case INTERNAL_IP6_SUBNET:
+ if (this->attribute_length != 0 && this->attribute_length != 17)
+ {
+ failed = TRUE;
+ }
+ break;
case INTERNAL_IP6_DNS:
case INTERNAL_IP6_NBNS:
case INTERNAL_IP6_DHCP:
- case INTERNAL_IP4_SUBNET:
+ if (this->attribute_length != 0 && this->attribute_length != 16)
+ {
+ failed = TRUE;
+ }
+ break;
case SUPPORTED_ATTRIBUTES:
- case INTERNAL_IP6_SUBNET:
- {
- /* Attribute types are not checked in here */
+ if (this->attribute_length % 2)
+ {
+ failed = TRUE;
+ }
+ break;
+ case APPLICATION_VERSION:
+ /* any length acceptable */
break;
- }
default:
+ DBG1(DBG_ENC, "unknown attribute type %N",
+ configuration_attribute_type_names, this->attribute_type);
return FAILED;
}
- if (this->attribute_length != this->attribute_value.len)
+ if (failed)
{
+ DBG1(DBG_ENC, "invalid attribute length %d for %N",
+ this->attribute_length, configuration_attribute_type_names,
+ this->attribute_type);
return FAILED;
}
-
return SUCCESS;
}
@@ -208,9 +243,8 @@ static chunk_t get_value (private_configuration_attribute_t *this)
return this->attribute_value;
}
-
/**
- * Implementation of configuration_attribute_t.set_attribute_type.
+ * Implementation of configuration_attribute_t.set_type.
*/
static void set_attribute_type (private_configuration_attribute_t *this, u_int16_t type)
{
@@ -218,7 +252,7 @@ static void set_attribute_type (private_configuration_attribute_t *this, u_int16
}
/**
- * Implementation of configuration_attribute_t.get_attribute_type.
+ * Implementation of configuration_attribute_t.get_type.
*/
static u_int16_t get_attribute_type (private_configuration_attribute_t *this)
{
@@ -226,7 +260,7 @@ static u_int16_t get_attribute_type (private_configuration_attribute_t *this)
}
/**
- * Implementation of configuration_attribute_t.get_attribute_length.
+ * Implementation of configuration_attribute_t.get_length.
*/
static u_int16_t get_attribute_length (private_configuration_attribute_t *this)
{
@@ -265,9 +299,9 @@ configuration_attribute_t *configuration_attribute_create()
/* public functions */
this->public.set_value = (void (*) (configuration_attribute_t *,chunk_t)) set_value;
this->public.get_value = (chunk_t (*) (configuration_attribute_t *)) get_value;
- this->public.set_attribute_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type;
- this->public.get_attribute_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type;
- this->public.get_attribute_length = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_length;
+ this->public.set_type = (void (*) (configuration_attribute_t *,u_int16_t type)) set_attribute_type;
+ this->public.get_type = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_type;
+ this->public.get_length = (u_int16_t (*) (configuration_attribute_t *)) get_attribute_length;
this->public.destroy = (void (*) (configuration_attribute_t *)) destroy;
/* set default values of the fields */
diff --git a/src/charon/encoding/payloads/configuration_attribute.h b/src/charon/encoding/payloads/configuration_attribute.h
index 5a11d0a35..5c4f65b14 100644
--- a/src/charon/encoding/payloads/configuration_attribute.h
+++ b/src/charon/encoding/payloads/configuration_attribute.h
@@ -109,7 +109,7 @@ struct configuration_attribute_t {
* @param this calling configuration_attribute_t object
* @param type type to set (most significant bit is set to zero)
*/
- void (*set_attribute_type) (configuration_attribute_t *this, u_int16_t type);
+ void (*set_type) (configuration_attribute_t *this, u_int16_t type);
/**
* @brief get the type of the attribute.
@@ -117,7 +117,7 @@ struct configuration_attribute_t {
* @param this calling configuration_attribute_t object
* @return type of the value
*/
- u_int16_t (*get_attribute_type) (configuration_attribute_t *this);
+ u_int16_t (*get_type) (configuration_attribute_t *this);
/**
* @brief get the length of an attribute.
@@ -125,7 +125,7 @@ struct configuration_attribute_t {
* @param this calling configuration_attribute_t object
* @return type of the value
*/
- u_int16_t (*get_attribute_length) (configuration_attribute_t *this);
+ u_int16_t (*get_length) (configuration_attribute_t *this);
/**
* @brief Destroys an configuration_attribute_t object.
diff --git a/src/charon/encoding/payloads/cp_payload.c b/src/charon/encoding/payloads/cp_payload.c
index bd16abc22..380ed9681 100644
--- a/src/charon/encoding/payloads/cp_payload.c
+++ b/src/charon/encoding/payloads/cp_payload.c
@@ -204,9 +204,9 @@ static size_t get_length(private_cp_payload_t *this)
/**
* Implementation of cp_payload_t.create_configuration_attribute_iterator.
*/
-static iterator_t *create_configuration_attribute_iterator (private_cp_payload_t *this,bool forward)
+static iterator_t *create_attribute_iterator (private_cp_payload_t *this)
{
- return this->attributes->create_iterator(this->attributes,forward);
+ return this->attributes->create_iterator(this->attributes, TRUE);
}
/**
@@ -261,7 +261,7 @@ cp_payload_t *cp_payload_create()
this->public.payload_interface.destroy = (void (*) (payload_t *))destroy;
/* public functions */
- this->public.create_configuration_attribute_iterator = (iterator_t* (*) (cp_payload_t *,bool)) create_configuration_attribute_iterator;
+ this->public.create_attribute_iterator = (iterator_t* (*) (cp_payload_t *)) create_attribute_iterator;
this->public.add_configuration_attribute = (void (*) (cp_payload_t *,configuration_attribute_t *)) add_configuration_attribute;
this->public.set_config_type = (void (*) (cp_payload_t *, config_type_t)) set_config_type;
this->public.get_config_type = (config_type_t (*) (cp_payload_t *)) get_config_type;
diff --git a/src/charon/encoding/payloads/cp_payload.h b/src/charon/encoding/payloads/cp_payload.h
index af36b48a3..27ff41005 100644
--- a/src/charon/encoding/payloads/cp_payload.h
+++ b/src/charon/encoding/payloads/cp_payload.h
@@ -77,23 +77,19 @@ struct cp_payload_t {
/**
* @brief Creates an iterator of stored configuration_attribute_t objects.
*
- * @warning The created iterator has to get destroyed by the caller!
- *
- * @warning When deleting an attribute using this iterator,
- * the length of this configuration_attribute_t has to be refreshed
- * by calling get_length()!
+ * When deleting an attribute using this iterator, the length of this
+ * configuration_attribute_t has to be refreshed by calling get_length()!
*
* @param this calling cp_payload_t object
- * @param[in] forward iterator direction (TRUE: front to end)
* @return created iterator_t object
*/
- iterator_t *(*create_configuration_attribute_iterator) (cp_payload_t *this, bool forward);
+ iterator_t *(*create_attribute_iterator) (cp_payload_t *this);
/**
* @brief Adds a configuration_attribute_t object to this object.
*
- * @warning The added configuration_attribute_t object is
- * getting destroyed in destroy function of cp_payload_t.
+ * The added configuration_attribute_t object is getting destroyed in
+ * destroy function of cp_payload_t.
*
* @param this calling cp_payload_t object
* @param attribute configuration_attribute_t object to add
diff --git a/src/charon/encoding/payloads/notify_payload.c b/src/charon/encoding/payloads/notify_payload.c
index 38ee0946d..a04901a90 100644
--- a/src/charon/encoding/payloads/notify_payload.c
+++ b/src/charon/encoding/payloads/notify_payload.c
@@ -209,6 +209,9 @@ static status_t verify(private_notify_payload_t *this)
diffie_hellman_group_t dh_group;
if (this->notification_data.len != 2)
{
+ DBG1(DBG_ENC, "invalid notify data length for %N (%d)",
+ notify_type_names, this->notify_type,
+ this->notification_data.len);
return FAILED;
}
dh_group = ntohs(*((u_int16_t*)this->notification_data.ptr));
@@ -403,7 +406,10 @@ static chunk_t get_notification_data(private_notify_payload_t *this)
static status_t set_notification_data(private_notify_payload_t *this, chunk_t notification_data)
{
chunk_free(&this->notification_data);
- this->notification_data = chunk_clone(notification_data);
+ if (notification_data.len > 0)
+ {
+ this->notification_data = chunk_clone(notification_data);
+ }
compute_length(this);
return SUCCESS;
}
diff --git a/src/charon/network/packet.c b/src/charon/network/packet.c
index ca8b2a616..f2fa91569 100644
--- a/src/charon/network/packet.c
+++ b/src/charon/network/packet.c
@@ -58,10 +58,7 @@ struct private_packet_t {
*/
static void set_source(private_packet_t *this, host_t *source)
{
- if (this->source)
- {
- this->source->destroy(this->source);
- }
+ DESTROY_IF(this->source);
this->source = source;
}
@@ -70,10 +67,7 @@ static void set_source(private_packet_t *this, host_t *source)
*/
static void set_destination(private_packet_t *this, host_t *destination)
{
- if (this->destination)
- {
- this->destination->destroy(this->destination);
- }
+ DESTROY_IF(this->destination);
this->destination = destination;
}
diff --git a/src/charon/queues/jobs/acquire_job.c b/src/charon/queues/jobs/acquire_job.c
index 4deadf3fe..b4ffb258d 100644
--- a/src/charon/queues/jobs/acquire_job.c
+++ b/src/charon/queues/jobs/acquire_job.c
@@ -57,8 +57,8 @@ static status_t execute(private_acquire_job_t *this)
{
ike_sa_t *ike_sa;
- ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager,
- this->reqid);
+ ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ this->reqid, TRUE);
if (ike_sa == NULL)
{
DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for acquiring",
diff --git a/src/charon/queues/jobs/delete_child_sa_job.c b/src/charon/queues/jobs/delete_child_sa_job.c
index 71ee3f00a..f694696b0 100644
--- a/src/charon/queues/jobs/delete_child_sa_job.c
+++ b/src/charon/queues/jobs/delete_child_sa_job.c
@@ -68,8 +68,8 @@ static status_t execute(private_delete_child_sa_job_t *this)
{
ike_sa_t *ike_sa;
- ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager,
- this->reqid);
+ ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ this->reqid, TRUE);
if (ike_sa == NULL)
{
DBG1(DBG_JOB, "CHILD_SA with reqid %d not found for delete",
diff --git a/src/charon/queues/jobs/delete_ike_sa_job.c b/src/charon/queues/jobs/delete_ike_sa_job.c
index 9e8173c39..706155aa6 100644
--- a/src/charon/queues/jobs/delete_ike_sa_job.c
+++ b/src/charon/queues/jobs/delete_ike_sa_job.c
@@ -62,41 +62,38 @@ static status_t execute(private_delete_ike_sa_job_t *this)
{
ike_sa_t *ike_sa;
- if (this->delete_if_established)
+ ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
+ this->ike_sa_id);
+ if (ike_sa)
{
- if (charon->ike_sa_manager->delete(charon->ike_sa_manager,
- this->ike_sa_id) != SUCCESS)
+ if (this->delete_if_established)
{
- DBG2(DBG_JOB, "IKE SA didn't exist anymore");
- }
- return DESTROY_ME;
- }
- else
- {
- ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id);
- if (ike_sa == NULL)
- {
- /* hm, somebody was faster ;-) */
- return DESTROY_ME;
+ if (ike_sa->delete(ike_sa) == DESTROY_ME)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(
+ charon->ike_sa_manager, ike_sa);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ }
}
-
- switch (ike_sa->get_state(ike_sa))
+ else
{
- case IKE_ESTABLISHED:
+ /* destroy only if not ESTABLISHED */
+ if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED)
{
- /* IKE_SA is established and so is not getting destroyed */
charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
- return DESTROY_ME;
}
- default:
+ else
{
- /* IKE_SA is half open and gets destroyed */
DBG1(DBG_JOB, "deleting half open IKE_SA after timeout");
- charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
- return DESTROY_ME;
+ charon->ike_sa_manager->checkin_and_destroy(
+ charon->ike_sa_manager, ike_sa);
}
}
}
+ return DESTROY_ME;
}
/**
diff --git a/src/charon/queues/jobs/incoming_packet_job.c b/src/charon/queues/jobs/incoming_packet_job.c
index 18671610d..c4f211a04 100644
--- a/src/charon/queues/jobs/incoming_packet_job.c
+++ b/src/charon/queues/jobs/incoming_packet_job.c
@@ -64,6 +64,13 @@ static void send_notify_response(private_incoming_packet_job_t *this,
packet_t *packet;
ike_sa_id_t *ike_sa_id;
+ if (request->get_exchange_type(request) != IKE_SA_INIT)
+ {
+ /* TODO: Use transforms implementing the "NULL" algorithm,
+ we are unable to generate message otherwise */
+ return;
+ }
+
ike_sa_id = request->get_ike_sa_id(request);
ike_sa_id = ike_sa_id->clone(ike_sa_id);
ike_sa_id->switch_initiator(ike_sa_id);
@@ -80,8 +87,6 @@ static void send_notify_response(private_incoming_packet_job_t *this,
ike_sa_id->destroy(ike_sa_id);
notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type);
response->add_payload(response, (payload_t *)notify);
- /* generation may fail, as most messages need a crypter/signer.
- * TODO: Use transforms implementing the "NULL" algorithm */
if (response->generate(response, NULL, NULL, &packet) != SUCCESS)
{
response->destroy(response);
@@ -107,12 +112,12 @@ static status_t execute(private_incoming_packet_job_t *this)
message = message_create_from_packet(this->packet->clone(this->packet));
src = message->get_source(message);
dst = message->get_destination(message);
- DBG1(DBG_NET, "received packet: from %#H to %#H", src, dst);
status = message->parse_header(message);
if (status != SUCCESS)
{
- DBG1(DBG_NET, "received message with invalid IKE header, ignored");
+ DBG1(DBG_NET, "received message from %H with invalid IKE header, "
+ "ignored", src);
message->destroy(message);
return DESTROY_ME;
}
@@ -120,11 +125,12 @@ static status_t execute(private_incoming_packet_job_t *this)
if ((message->get_major_version(message) != IKE_MAJOR_VERSION) ||
(message->get_minor_version(message) != IKE_MINOR_VERSION))
{
- DBG1(DBG_NET,
- "received a packet with IKE version %d.%d, not supported",
- message->get_major_version(message),
- message->get_minor_version(message));
- if ((message->get_exchange_type(message) == IKE_SA_INIT) && (message->get_request(message)))
+ DBG1(DBG_NET, "received message from %H with unsupported IKE "
+ "version %d.%d, ignored", src, message->get_major_version(message),
+ message->get_minor_version(message));
+
+ if (message->get_exchange_type(message) == IKE_SA_INIT &&
+ message->get_request(message))
{
send_notify_response(this, message, INVALID_MAJOR_VERSION);
}
@@ -138,19 +144,19 @@ static status_t execute(private_incoming_packet_job_t *this)
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, ike_sa_id);
if (ike_sa == NULL)
{
- DBG1(DBG_NET, "received packet for IKE_SA: %J, but no such IKE_SA",
- ike_sa_id);
+ DBG1(DBG_NET, "received packet from %#H for IKE_SA: %J, but no such "
+ "IKE_SA", src, ike_sa_id);
if (message->get_request(message))
{
- /* TODO: send notify if we have NULL crypters,
- * see todo in send_notify_response
- send_notify_response(this, message, INVALID_IKE_SPI); */
+ send_notify_response(this, message, INVALID_IKE_SPI);
}
ike_sa_id->destroy(ike_sa_id);
message->destroy(message);
return DESTROY_ME;
}
+ DBG1(DBG_NET, "received packet: from %#H to %#H", src, dst);
+
status = ike_sa->process_message(ike_sa, message);
if (status == DESTROY_ME)
{
diff --git a/src/charon/queues/jobs/initiate_job.c b/src/charon/queues/jobs/initiate_job.c
index 8b943a3f1..af50663d6 100644
--- a/src/charon/queues/jobs/initiate_job.c
+++ b/src/charon/queues/jobs/initiate_job.c
@@ -45,11 +45,6 @@ struct private_initiate_job_t {
connection_t *connection;
/**
- * host to connect to, use NULL to use connections one
- */
- host_t *other;
-
- /**
* associated policy to initiate
*/
policy_t *policy;
@@ -70,19 +65,12 @@ static status_t execute(private_initiate_job_t *this)
{
ike_sa_t *ike_sa;
- ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager,
this->connection->get_my_host(this->connection),
this->connection->get_other_host(this->connection),
this->policy->get_my_id(this->policy),
this->policy->get_other_id(this->policy));
-
- if (this->other)
- {
- ike_sa->set_other_host(ike_sa, this->other->clone(this->other));
- }
-
- this->connection->get_ref(this->connection);
- this->policy->get_ref(this->policy);
+
if (ike_sa->initiate(ike_sa, this->connection, this->policy) != SUCCESS)
{
DBG1(DBG_JOB, "initiation failed, going to delete IKE_SA");
@@ -101,15 +89,13 @@ static void destroy(private_initiate_job_t *this)
{
this->connection->destroy(this->connection);
this->policy->destroy(this->policy);
- DESTROY_IF(this->other);
free(this);
}
/*
* Described in header
*/
-initiate_job_t *initiate_job_create(connection_t *connection, host_t *other,
- policy_t *policy)
+initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy)
{
private_initiate_job_t *this = malloc_thing(private_initiate_job_t);
@@ -121,7 +107,6 @@ initiate_job_t *initiate_job_create(connection_t *connection, host_t *other,
/* private variables */
this->connection = connection;
this->policy = policy;
- this->other = other;
return &this->public;
}
diff --git a/src/charon/queues/jobs/initiate_job.h b/src/charon/queues/jobs/initiate_job.h
index 2fd0ced93..af1dd9ece 100644
--- a/src/charon/queues/jobs/initiate_job.h
+++ b/src/charon/queues/jobs/initiate_job.h
@@ -51,13 +51,11 @@ struct initiate_job_t {
* @brief Creates a job of type INITIATE_IKE_SA.
*
* @param connection connection_t to initialize
- * @param other another host to initiate to, NULL to use connections one
* @param policy policy to set up
* @return initiate_job_t object
*
* @ingroup jobs
*/
-initiate_job_t *initiate_job_create(connection_t *connection, host_t *other,
- policy_t *policy);
+initiate_job_t *initiate_job_create(connection_t *connection, policy_t *policy);
#endif /*INITIATE_IKE_SA_JOB_H_*/
diff --git a/src/charon/queues/jobs/job.c b/src/charon/queues/jobs/job.c
index d88843d7c..337558c2d 100644
--- a/src/charon/queues/jobs/job.c
+++ b/src/charon/queues/jobs/job.c
@@ -26,7 +26,7 @@
ENUM(job_type_names, INCOMING_PACKET, SEND_DPD,
"INCOMING_PACKET",
- "RETRANSMIT_REQUEST",
+ "RETRANSMIT",
"INITIATE",
"ROUTE",
"ACQUIRE",
diff --git a/src/charon/queues/jobs/job.h b/src/charon/queues/jobs/job.h
index ae3fe7974..ae67a2bdc 100644
--- a/src/charon/queues/jobs/job.h
+++ b/src/charon/queues/jobs/job.h
@@ -45,9 +45,9 @@ enum job_type_t {
/**
* Retransmit an IKEv2-Message.
*
- * Job is implemented in class retransmit_request_job_t
+ * Job is implemented in class retransmit_job_t
*/
- RETRANSMIT_REQUEST,
+ RETRANSMIT,
/**
* Set up a CHILD_SA, optional with an IKE_SA.
diff --git a/src/charon/queues/jobs/rekey_child_sa_job.c b/src/charon/queues/jobs/rekey_child_sa_job.c
index 5944aa77f..3422b614d 100644
--- a/src/charon/queues/jobs/rekey_child_sa_job.c
+++ b/src/charon/queues/jobs/rekey_child_sa_job.c
@@ -67,8 +67,8 @@ static status_t execute(private_rekey_child_sa_job_t *this)
{
ike_sa_t *ike_sa;
- ike_sa = charon->ike_sa_manager->checkout_by_child(charon->ike_sa_manager,
- this->reqid);
+ ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ this->reqid, TRUE);
if (ike_sa == NULL)
{
DBG2(DBG_JOB, "CHILD_SA with reqid %d not found for rekeying",
diff --git a/src/charon/queues/jobs/rekey_ike_sa_job.c b/src/charon/queues/jobs/rekey_ike_sa_job.c
index f0c4bef4f..2539d997e 100644
--- a/src/charon/queues/jobs/rekey_ike_sa_job.c
+++ b/src/charon/queues/jobs/rekey_ike_sa_job.c
@@ -61,7 +61,7 @@ static job_type_t get_type(private_rekey_ike_sa_job_t *this)
static status_t execute(private_rekey_ike_sa_job_t *this)
{
ike_sa_t *ike_sa;
- status_t status;
+ status_t status = SUCCESS;
ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
this->ike_sa_id);
@@ -73,7 +73,7 @@ static status_t execute(private_rekey_ike_sa_job_t *this)
if (this->reauth)
{
- status = ike_sa->reauth(ike_sa);
+ ike_sa->reestablish(ike_sa);
}
else
{
diff --git a/src/charon/queues/jobs/retransmit_request_job.c b/src/charon/queues/jobs/retransmit_job.c
index 1dee6e521..5bfa20dfd 100644
--- a/src/charon/queues/jobs/retransmit_request_job.c
+++ b/src/charon/queues/jobs/retransmit_job.c
@@ -1,12 +1,12 @@
/**
- * @file retransmit_request_job.c
+ * @file retransmit_job.c
*
- * @brief Implementation of retransmit_request_job_t.
+ * @brief Implementation of retransmit_job_t.
*
*/
/*
- * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -21,20 +21,20 @@
* for more details.
*/
-#include "retransmit_request_job.h"
+#include "retransmit_job.h"
#include <daemon.h>
-typedef struct private_retransmit_request_job_t private_retransmit_request_job_t;
+typedef struct private_retransmit_job_t private_retransmit_job_t;
/**
- * Private data of an retransmit_request_job_t Object.
+ * Private data of an retransmit_job_t Object.
*/
-struct private_retransmit_request_job_t {
+struct private_retransmit_job_t {
/**
- * Public retransmit_request_job_t interface.
+ * Public retransmit_job_t interface.
*/
- retransmit_request_job_t public;
+ retransmit_job_t public;
/**
* Message ID of the request to resend.
@@ -50,33 +50,32 @@ struct private_retransmit_request_job_t {
/**
* Implements job_t.get_type.
*/
-static job_type_t get_type(private_retransmit_request_job_t *this)
+static job_type_t get_type(private_retransmit_job_t *this)
{
- return RETRANSMIT_REQUEST;
+ return RETRANSMIT;
}
/**
* Implementation of job_t.execute.
*/
-static status_t execute(private_retransmit_request_job_t *this)
+static status_t execute(private_retransmit_job_t *this)
{
ike_sa_t *ike_sa;
- ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, this->ike_sa_id);
- if (ike_sa == NULL)
+ ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
+ this->ike_sa_id);
+ if (ike_sa)
{
- DBG2(DBG_JOB, "IKE SA could not be checked out. Already deleted?");
- return DESTROY_ME;
- }
-
- if (ike_sa->retransmit_request(ike_sa, this->message_id) == DESTROY_ME)
- {
- /* retransmission hopeless, kill SA */
- charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, ike_sa);
- }
- else
- {
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ if (ike_sa->retransmit(ike_sa, this->message_id) == DESTROY_ME)
+ {
+ /* retransmitted to many times, giving up */
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ ike_sa);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ }
}
return DESTROY_ME;
}
@@ -84,7 +83,7 @@ static status_t execute(private_retransmit_request_job_t *this)
/**
* Implements job_t.destroy.
*/
-static void destroy(private_retransmit_request_job_t *this)
+static void destroy(private_retransmit_job_t *this)
{
this->ike_sa_id->destroy(this->ike_sa_id);
free(this);
@@ -93,9 +92,9 @@ static void destroy(private_retransmit_request_job_t *this)
/*
* Described in header.
*/
-retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id)
+retransmit_job_t *retransmit_job_create(u_int32_t message_id,ike_sa_id_t *ike_sa_id)
{
- private_retransmit_request_job_t *this = malloc_thing(private_retransmit_request_job_t);
+ private_retransmit_job_t *this = malloc_thing(private_retransmit_job_t);
/* interface functions */
this->public.job_interface.get_type = (job_type_t (*) (job_t *)) get_type;
@@ -106,5 +105,5 @@ retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,ike
this->message_id = message_id;
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
- return &(this->public);
+ return &this->public;
}
diff --git a/src/charon/queues/jobs/retransmit_request_job.h b/src/charon/queues/jobs/retransmit_job.h
index 1897af16d..19e29b909 100644
--- a/src/charon/queues/jobs/retransmit_request_job.h
+++ b/src/charon/queues/jobs/retransmit_job.h
@@ -1,12 +1,12 @@
/**
- * @file retransmit_request_job.h
+ * @file retransmit_job.h
*
- * @brief Interface of retransmit_request_job_t.
+ * @brief Interface of retransmit_job_t.
*
*/
/*
- * Copyright (C) 2005-2006 Martin Willi
+ * Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -21,28 +21,28 @@
* for more details.
*/
-#ifndef RESEND_MESSAGE_JOB_H_
-#define RESEND_MESSAGE_JOB_H_
+#ifndef RETRANSMIT_JOB_H_
+#define RETRANSMIT_JOB_H_
-typedef struct retransmit_request_job_t retransmit_request_job_t;
+typedef struct retransmit_job_t retransmit_job_t;
#include <library.h>
#include <queues/jobs/job.h>
#include <sa/ike_sa_id.h>
/**
- * @brief Class representing an RETRANSMIT_REQUEST Job.
+ * @brief Class representing an retransmit Job.
*
* This job is scheduled every time a request is sent over the
* wire. If the response to the request is not received at schedule
* time, the retransmission will be initiated.
*
* @b Constructors:
- * - retransmit_request_job_create()
+ * - retransmit_job_create()
*
* @ingroup jobs
*/
-struct retransmit_request_job_t {
+struct retransmit_job_t {
/**
* The job_t interface.
*/
@@ -50,15 +50,15 @@ struct retransmit_request_job_t {
};
/**
- * @brief Creates a job of type RETRANSMIT_REQUEST.
+ * @brief Creates a job of type retransmit.
*
* @param message_id message_id of the request to resend
- * @param ike_sa_id identification of the ike_sa as ike_sa_id_t object (gets cloned)
- * @return retransmit_request_job_t object
+ * @param ike_sa_id identification of the ike_sa as ike_sa_id_t
+ * @return retransmit_job_t object
*
* @ingroup jobs
*/
-retransmit_request_job_t *retransmit_request_job_create(u_int32_t message_id,
- ike_sa_id_t *ike_sa_id);
+retransmit_job_t *retransmit_job_create(u_int32_t message_id,
+ ike_sa_id_t *ike_sa_id);
-#endif /* RESEND_MESSAGE_JOB_H_ */
+#endif /* RETRANSMIT_JOB_H_ */
diff --git a/src/charon/queues/jobs/route_job.c b/src/charon/queues/jobs/route_job.c
index 5a128474b..bb6281dcc 100644
--- a/src/charon/queues/jobs/route_job.c
+++ b/src/charon/queues/jobs/route_job.c
@@ -69,7 +69,7 @@ static status_t execute(private_route_job_t *this)
{
ike_sa_t *ike_sa;
- ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ ike_sa = charon->ike_sa_manager->checkout_by_peer(charon->ike_sa_manager,
this->connection->get_my_host(this->connection),
this->connection->get_other_host(this->connection),
this->policy->get_my_id(this->policy),
diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c
index 117471472..fa895eb7e 100644
--- a/src/charon/sa/child_sa.c
+++ b/src/charon/sa/child_sa.c
@@ -6,8 +6,8 @@
*/
/*
+ * Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -69,11 +69,6 @@ struct private_child_sa_t {
*/
child_sa_t public;
- /**
- * Name of the policy used by this CHILD_SA
- */
- char *name;
-
struct {
/** address of peer */
host_t *addr;
@@ -134,34 +129,9 @@ struct private_child_sa_t {
time_t install_time;
/**
- * Lifetime before rekeying
- */
- u_int32_t soft_lifetime;
-
- /**
- * Lifetime before delete
- */
- u_int32_t hard_lifetime;
-
- /**
* state of the CHILD_SA
*/
child_sa_state_t state;
-
- /**
- * transaction which is rekeying this CHILD_SA
- */
- transaction_t *rekeying_transaction;
-
- /**
- * Updown script
- */
- char *script;
-
- /**
- * Allow host access
- */
- bool hostaccess;
/**
* Specifies if NAT traversal is used
@@ -172,6 +142,11 @@ struct private_child_sa_t {
* mode this SA uses, tunnel/transport
*/
mode_t mode;
+
+ /**
+ * policy used to create this child
+ */
+ policy_t *policy;
};
/**
@@ -179,22 +154,7 @@ struct private_child_sa_t {
*/
static char *get_name(private_child_sa_t *this)
{
- return this->name;
-}
-
-/**
- * Implementation of child_sa_t.set_name.
- */
-static void set_name(private_child_sa_t *this, char* name)
-{
- char buffer[64];
-
- if (snprintf(buffer, sizeof(buffer), "%s[%d]",
- name, this->reqid - REQID_START) > 0)
- {
- free(this->name);
- this->name = strdup(buffer);
- }
+ return this->policy->get_name(this->policy);;
}
/**
@@ -234,14 +194,25 @@ static child_sa_state_t get_state(private_child_sa_t *this)
}
/**
+ * Implements child_sa_t.get_policy
+ */
+static policy_t* get_policy(private_child_sa_t *this)
+{
+ return this->policy;
+}
+
+/**
* Run the up/down script
*/
static void updown(private_child_sa_t *this, bool up)
{
sa_policy_t *policy;
iterator_t *iterator;
+ char *script;
- if (this->script == NULL)
+ script = this->policy->get_updown(this->policy);
+
+ if (script == NULL)
{
return;
}
@@ -307,7 +278,7 @@ static void updown(private_child_sa_t *this, bool up)
policy->my_ts->is_host(policy->my_ts,
this->me.addr) ? "-host" : "-client",
this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6",
- this->name,
+ this->policy->get_name(this->policy),
ifname,
this->reqid,
this->me.addr,
@@ -322,8 +293,8 @@ static void updown(private_child_sa_t *this, bool up)
other_client, other_client_mask,
policy->other_ts->get_from_port(policy->other_ts),
policy->other_ts->get_protocol(policy->other_ts),
- this->hostaccess? "PLUTO_HOST_ACCESS='1' " : "",
- this->script);
+ this->policy->get_hostaccess(this->policy) ?
+ "PLUTO_HOST_ACCESS='1' " : "", script);
free(ifname);
free(my_client);
free(other_client);
@@ -332,7 +303,7 @@ static void updown(private_child_sa_t *this, bool up)
if (shell == NULL)
{
- DBG1(DBG_CHD, "could not execute updown script '%s'", this->script);
+ DBG1(DBG_CHD, "could not execute updown script '%s'", script);
return;
}
@@ -447,7 +418,7 @@ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
static status_t install(private_child_sa_t *this, proposal_t *proposal,
mode_t mode, prf_plus_t *prf_plus, bool mine)
{
- u_int32_t spi;
+ u_int32_t spi, soft, hard;;
algorithm_t *enc_algo, *int_algo;
algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0};
algorithm_t int_algo_none = {AUTH_UNDEFINED, 0};
@@ -532,16 +503,15 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
natt = NULL;
}
+ soft = this->policy->get_soft_lifetime(this->policy);
+ hard = this->policy->get_hard_lifetime(this->policy);
/* send SA down to the kernel */
DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
status = charon->kernel_interface->add_sa(charon->kernel_interface,
- src, dst,
- spi, this->protocol,
- this->reqid,
- mine ? this->soft_lifetime : 0,
- this->hard_lifetime,
- enc_algo, int_algo,
+ src, dst, spi, this->protocol,
+ this->reqid, mine ? soft : 0,
+ hard, enc_algo, int_algo,
prf_plus, natt, mode, mine);
this->encryption = *enc_algo;
@@ -705,22 +675,6 @@ static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this)
}
/**
- * Implementation of child_sa_t.set_rekeying_transaction.
- */
-static void set_rekeying_transaction(private_child_sa_t *this, transaction_t *transaction)
-{
- this->rekeying_transaction = transaction;
-}
-
-/**
- * Implementation of child_sa_t.get_rekeying_transaction.
- */
-static transaction_t* get_rekeying_transaction(private_child_sa_t *this)
-{
- return this->rekeying_transaction;
-}
-
-/**
* Implementation of child_sa_t.get_use_time
*/
static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time)
@@ -769,7 +723,7 @@ static int print(FILE *stream, const struct printf_info *info,
private_child_sa_t *this = *((private_child_sa_t**)(args[0]));
iterator_t *iterator;
sa_policy_t *policy;
- u_int32_t now, rekeying;
+ u_int32_t now, rekeying, soft;
u_int32_t use, use_in, use_fwd;
status_t status;
size_t written = 0;
@@ -781,8 +735,9 @@ static int print(FILE *stream, const struct printf_info *info,
now = (u_int32_t)time(NULL);
- written += fprintf(stream, "%12s: %N, reqid: %d, %N", this->name,
- child_sa_state_names, this->state, this->reqid,
+ written += fprintf(stream, "%12s{%d}: %N, %N",
+ this->policy->get_name(this->policy), this->reqid,
+ child_sa_state_names, this->state,
mode_names, this->mode);
if (this->state == CHILD_INSTALLED)
@@ -793,7 +748,9 @@ static int print(FILE *stream, const struct printf_info *info,
if (info->alt)
{
- written += fprintf(stream, "\n%12s: ", this->name);
+ written += fprintf(stream, "\n%12s{%d}: ",
+ this->policy->get_name(this->policy),
+ this->reqid);
if (this->protocol == PROTO_ESP)
{
@@ -815,10 +772,11 @@ static int print(FILE *stream, const struct printf_info *info,
}
written += fprintf(stream, ", rekeying ");
+ soft = this->policy->get_soft_lifetime(this->policy);
/* calculate rekey times */
- if (this->soft_lifetime)
+ if (soft)
{
- rekeying = this->soft_lifetime - (now - this->install_time);
+ rekeying = soft - (now - this->install_time);
written += fprintf(stream, "in %ds", rekeying);
}
else
@@ -830,8 +788,9 @@ static int print(FILE *stream, const struct printf_info *info,
iterator = this->policies->create_iterator(this->policies, TRUE);
while (iterator->iterate(iterator, (void**)&policy))
{
- written += fprintf(stream, "\n%12s: %R===%R, last use: ",
- this->name, policy->my_ts, policy->other_ts);
+ written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ",
+ this->policy->get_name(this->policy), this->reqid,
+ policy->my_ts, policy->other_ts);
/* query time of last policy use */
@@ -1074,25 +1033,22 @@ static void destroy(private_child_sa_t *this)
this->other.addr->destroy(this->other.addr);
this->me.id->destroy(this->me.id);
this->other.id->destroy(this->other.id);
- free(this->name);
- free(this->script);
+ this->policy->destroy(this->policy);
free(this);
}
/*
* Described in header.
*/
-child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
+child_sa_t * child_sa_create(host_t *me, host_t* other,
identification_t *my_id, identification_t *other_id,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime,
- char *script, bool hostaccess, bool use_natt)
+ policy_t *policy, u_int32_t rekey, bool use_natt)
{
- static u_int32_t reqid = REQID_START;
+ static u_int32_t reqid = 0;
private_child_sa_t *this = malloc_thing(private_child_sa_t);
/* public functions */
this->public.get_name = (char*(*)(child_sa_t*))get_name;
- this->public.set_name = (void(*)(child_sa_t*,char*))set_name;
this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
@@ -1104,14 +1060,12 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors;
this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors;
this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time;
- this->public.set_rekeying_transaction = (void (*)(child_sa_t*,transaction_t*))set_rekeying_transaction;
- this->public.get_rekeying_transaction = (transaction_t* (*)(child_sa_t*))get_rekeying_transaction;
this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
+ this->public.get_policy = (policy_t*(*)(child_sa_t*))get_policy;
this->public.destroy = (void(*)(child_sa_t*))destroy;
/* private data */
- this->name = strdup("(uninitialized)");
this->me.addr = me->clone(me);
this->other.addr = other->clone(other);
this->me.id = my_id->clone(my_id);
@@ -1120,11 +1074,7 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->other.spi = 0;
this->alloc_ah_spi = 0;
this->alloc_esp_spi = 0;
- this->script = script ? strdup(script) : NULL;
- this->hostaccess = hostaccess;
this->use_natt = use_natt;
- this->soft_lifetime = soft_lifetime;
- this->hard_lifetime = hard_lifetime;
this->state = CHILD_CREATED;
/* reuse old reqid if we are rekeying an existing CHILD_SA */
this->reqid = rekey ? rekey : ++reqid;
@@ -1137,7 +1087,8 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->other_ts = linked_list_create();
this->protocol = PROTO_NONE;
this->mode = MODE_TUNNEL;
- this->rekeying_transaction = NULL;
+ this->policy = policy;
+ policy->get_ref(policy);
return &this->public;
}
diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h
index 06362f35e..bd0e032da 100644
--- a/src/charon/sa/child_sa.h
+++ b/src/charon/sa/child_sa.h
@@ -6,8 +6,8 @@
*/
/*
+ * Copyright (C) 2006-2007 Martin Willi
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2006 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -32,7 +32,7 @@ typedef struct child_sa_t child_sa_t;
#include <crypto/prf_plus.h>
#include <encoding/payloads/proposal_substructure.h>
#include <config/proposal.h>
-#include <sa/transactions/transaction.h>
+#include <config/policies/policy.h>
/**
* Where we should start with reqid enumeration
@@ -109,18 +109,10 @@ struct child_sa_t {
char* (*get_name) (child_sa_t *this);
/**
- * @brief Set the name of the policy this IKE_SA uses.
- *
- * @param this calling object
- * @param name name, gets cloned
- */
- void (*set_name) (child_sa_t *this, char* name);
-
- /**
- * @brief Get the unique reqid of the CHILD SA.
+ * @brief Get the reqid of the CHILD SA.
*
- * Every CHILD_SA has a unique reqid, which is also
- * stored down in the kernel.
+ * Every CHILD_SA has a reqid. The kernel uses this ID to
+ * identify it.
*
* @param this calling object
* @return reqid of the CHILD SA
@@ -259,23 +251,12 @@ struct child_sa_t {
void (*set_state) (child_sa_t *this, child_sa_state_t state);
/**
- * @brief Set the transaction which rekeys this CHILD_SA.
- *
- * Since either end may initiate CHILD_SA rekeying, we must detect
- * such situations to handle them cleanly. A rekeying transaction
- * registers itself to the CHILD_SA, and checks later if another
- * transaction is in progress of a rekey.
+ * @brief Get the policy used to set up this child sa.
*
* @param this calling object
- */
- void (*set_rekeying_transaction) (child_sa_t *this, transaction_t *transaction);
-
- /**
- * @brief Get the transaction which rekeys this CHILD_SA.
- *
- * @param this calling object
- */
- transaction_t* (*get_rekeying_transaction) (child_sa_t *this);
+ * @return policy
+ */
+ policy_t* (*get_policy) (child_sa_t *this);
/**
* @brief Destroys a child_sa.
@@ -288,23 +269,19 @@ struct child_sa_t {
/**
* @brief Constructor to create a new child_sa_t.
*
- * @param rekey_reqid reqid of old CHILD_SA when rekeying, 0 otherwise
* @param me own address
* @param other remote address
* @param my_id id of own peer
* @param other_id id of remote peer
- * @param soft_lifetime time before rekeying
- * @param hard_lifteime time before delete
- * @param script updown script to use when calling child_sa_t.script()
- * @param hostaccess allow host access (needed by updown script)
+ * @param policy policy this CHILD_SA instantiates
+ * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise
* @param use_natt TRUE if NAT traversal is used
* @return child_sa_t object
*
* @ingroup sa
*/
-child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other,
+child_sa_t * child_sa_create(host_t *me, host_t *other,
identification_t *my_id, identification_t* other_id,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime,
- char *script, bool hostaccess, bool use_natt);
+ policy_t *policy, u_int32_t reqid, bool use_natt);
#endif /*CHILD_SA_H_*/
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c
index e5a77edad..91ee4be0b 100644
--- a/src/charon/sa/ike_sa.c
+++ b/src/charon/sa/ike_sa.c
@@ -25,12 +25,14 @@
#include <sys/time.h>
#include <string.h>
#include <printf.h>
+#include <sys/stat.h>
#include "ike_sa.h"
#include <library.h>
#include <daemon.h>
#include <utils/linked_list.h>
+#include <utils/lexparser.h>
#include <crypto/diffie_hellman.h>
#include <crypto/prf_plus.h>
#include <crypto/crypters/crypter.h>
@@ -42,14 +44,19 @@
#include <encoding/payloads/transform_substructure.h>
#include <encoding/payloads/transform_attribute.h>
#include <encoding/payloads/ts_payload.h>
-#include <sa/transactions/transaction.h>
-#include <sa/transactions/ike_sa_init.h>
-#include <sa/transactions/delete_ike_sa.h>
-#include <sa/transactions/create_child_sa.h>
-#include <sa/transactions/delete_child_sa.h>
-#include <sa/transactions/dead_peer_detection.h>
-#include <sa/transactions/rekey_ike_sa.h>
-#include <queues/jobs/retransmit_request_job.h>
+#include <sa/task_manager.h>
+#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_natd.h>
+#include <sa/tasks/ike_auth.h>
+#include <sa/tasks/ike_config.h>
+#include <sa/tasks/ike_cert.h>
+#include <sa/tasks/ike_rekey.h>
+#include <sa/tasks/ike_delete.h>
+#include <sa/tasks/ike_dpd.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_delete.h>
+#include <sa/tasks/child_rekey.h>
+#include <queues/jobs/retransmit_job.h>
#include <queues/jobs/delete_ike_sa_job.h>
#include <queues/jobs/send_dpd_job.h>
#include <queues/jobs/send_keepalive_job.h>
@@ -57,6 +64,11 @@
#include <queues/jobs/route_job.h>
#include <queues/jobs/initiate_job.h>
+
+#ifndef RESOLV_CONF
+#define RESOLV_CONF "/etc/resolv.conf"
+#endif
+
ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING,
"CREATED",
"CONNECTING",
@@ -83,14 +95,29 @@ struct private_ike_sa_t {
ike_sa_id_t *ike_sa_id;
/**
+ * unique numerical ID for this IKE_SA.
+ */
+ u_int32_t unique_id;
+
+ /**
* Current state of the IKE_SA
*/
- ike_sa_state_t state;
+ ike_sa_state_t state;
+
+ /**
+ * connection used to establish this IKE_SA.
+ */
+ connection_t *connection;
+
+ /**
+ * Peer and authentication information to establish IKE_SA.
+ */
+ policy_t *policy;
/**
- * Name of the connection used by this IKE_SA
+ * Juggles tasks to process messages
*/
- char *name;
+ task_manager_t *task_manager;
/**
* Address of local host
@@ -158,11 +185,6 @@ struct private_ike_sa_t {
prf_t *auth_verify;
/**
- * NAT hasher.
- */
- hasher_t *nat_hasher;
-
- /**
* NAT status of local host.
*/
bool nat_here;
@@ -173,14 +195,19 @@ struct private_ike_sa_t {
bool nat_there;
/**
- * message ID for next outgoung request
+ * Virtual IP on local host, if any
*/
- u_int32_t message_id_out;
-
+ host_t *my_virtual_ip;
+
/**
- * will the IKE_SA be fully reauthenticated or rekeyed only?
+ * Virtual IP on remote host, if any
*/
- bool reauth;
+ host_t *other_virtual_ip;
+
+ /**
+ * List of DNS servers installed by us
+ */
+ linked_list_t *dns_servers;
/**
* Timestamps for this IKE_SA
@@ -197,51 +224,12 @@ struct private_ike_sa_t {
/** when IKE_SA gets deleted */
u_int32_t delete;
} time;
-
- /**
- * interval to send DPD liveness check
- */
- time_t dpd_delay;
-
- /**
- * number of retransmit sequences to go through before giving up (keyingtries)
- */
- u_int32_t retrans_sequences;
-
- /**
- * List of queued transactions to process
- */
- linked_list_t *transaction_queue;
-
- /**
- * Transaction currently initiated
- * (only one supported yet, window size = 1)
- */
- transaction_t *transaction_out;
-
- /**
- * last transaction initiated by peer processed.
- * (only one supported yet, window size = 1)
- * Stored for retransmission.
- */
- transaction_t *transaction_in;
-
- /**
- * Next incoming transaction expected. Used to
- * do multi transaction operations.
- */
- transaction_t *transaction_in_next;
-
- /**
- * Transaction which rekeys this IKE_SA, used do detect simultaneus rekeying
- */
- transaction_t *rekeying_transaction;
};
/**
* get the time of the latest traffic processed by the kernel
*/
-static time_t get_kernel_time(private_ike_sa_t* this, bool inbound)
+static time_t get_use_time(private_ike_sa_t* this, bool inbound)
{
iterator_t *iterator;
child_sa_t *child_sa;
@@ -257,49 +245,68 @@ static time_t get_kernel_time(private_ike_sa_t* this, bool inbound)
}
iterator->destroy(iterator);
- return latest;
+ if (inbound)
+ {
+ return max(this->time.inbound, latest);
+ }
+ else
+ {
+ return max(this->time.outbound, latest);
+ }
}
/**
- * get the time of the latest received traffice
+ * Implementation of ike_sa_t.get_unique_id
*/
-static time_t get_time_inbound(private_ike_sa_t *this)
+static u_int32_t get_unique_id(private_ike_sa_t *this)
{
- return max(this->time.inbound, get_kernel_time(this, TRUE));
+ return this->unique_id;
}
/**
- * get the time of the latest sent traffic
+ * Implementation of ike_sa_t.get_name.
*/
-static time_t get_time_outbound(private_ike_sa_t *this)
+static char *get_name(private_ike_sa_t *this)
{
- return max(this->time.outbound, get_kernel_time(this, FALSE));
+ if (this->connection)
+ {
+ return this->connection->get_name(this->connection);
+ }
+ return "(unnamed)";
}
/**
- * Implementation of ike_sa_t.get_name.
+ * Implementation of ike_sa_t.get_connection
*/
-static char *get_name(private_ike_sa_t *this)
+static connection_t* get_connection(private_ike_sa_t *this)
{
- return this->name;
+ return this->connection;
}
/**
- * Implementation of ike_sa_t.set_name.
+ * Implementation of ike_sa_t.set_connection
*/
-static void set_name(private_ike_sa_t *this, char* name)
+static void set_connection(private_ike_sa_t *this, connection_t *connection)
{
- free(this->name);
- this->name = strdup(name);
+ this->connection = connection;
+ connection->get_ref(connection);
}
/**
- * Implementation of ike_sa_t.apply_connection.
+ * Implementation of ike_sa_t.get_policy
*/
-static void apply_connection(private_ike_sa_t *this, connection_t *connection)
+static policy_t *get_policy(private_ike_sa_t *this)
{
- this->dpd_delay = connection->get_dpd_delay(connection);
- this->retrans_sequences = connection->get_retrans_seq(connection);
+ return this->policy;
+}
+
+/**
+ * Implementation of ike_sa_t.set_policy
+ */
+static void set_policy(private_ike_sa_t *this, policy_t *policy)
+{
+ policy->get_ref(policy);
+ this->policy = policy;
}
/**
@@ -341,35 +348,6 @@ static void set_other_host(private_ike_sa_t *this, host_t *other)
*/
static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
{
- /*
- * Quoting RFC 4306:
- *
- * 2.11. Address and Port Agility
- *
- * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and
- * AH associations for the same IP addresses it runs over. The IP
- * addresses and ports in the outer header are, however, not themselves
- * cryptographically protected, and IKE is designed to work even through
- * Network Address Translation (NAT) boxes. An implementation MUST
- * accept incoming requests even if the source port is not 500 or 4500,
- * and MUST respond to the address and port from which the request was
- * received. It MUST specify the address and port at which the request
- * was received as the source address and port in the response. IKE
- * functions identically over IPv4 or IPv6.
- *
- * [...]
- *
- * There are cases where a NAT box decides to remove mappings that
- * are still alive (for example, the keepalive interval is too long,
- * or the NAT box is rebooted). To recover in these cases, hosts
- * that are not behind a NAT SHOULD send all packets (including
- * retransmission packets) to the IP address and port from the last
- * valid authenticated packet from the other end (i.e., dynamically
- * update the address). A host behind a NAT SHOULD NOT do this
- * because it opens a DoS attack possibility. Any authenticated IKE
- * packet or any authenticated UDP-encapsulated ESP packet can be
- * used to detect that the IP address or the port has changed.
- */
iterator_t *iterator = NULL;
child_sa_t *child_sa = NULL;
host_diff_t my_diff, other_diff;
@@ -425,399 +403,149 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
{
child_sa->update_hosts(child_sa, this->my_host, this->other_host,
my_diff, other_diff);
- /* TODO: what to do if update fails? Delete CHILD_SA? */
}
iterator->destroy(iterator);
}
/**
- * called when the peer is not responding anymore
+ * Implementation of ike_sa_t.retransmit.
*/
-static void dpd_detected(private_ike_sa_t *this)
+static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id)
{
- connection_t *connection = NULL;
- policy_t *policy;
- linked_list_t *my_ts, *other_ts;
- child_sa_t* child_sa;
- dpd_action_t action;
- job_t *job;
-
- DBG2(DBG_IKE, "dead peer detected, handling CHILD_SAs dpd action");
-
- /* check for childrens with dpdaction = hold */
- while(this->child_sas->remove_first(this->child_sas,
- (void**)&child_sa) == SUCCESS)
+ this->time.outbound = time(NULL);
+ if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS)
{
- /* get the policy which belongs to this CHILD */
- my_ts = child_sa->get_my_traffic_selectors(child_sa);
- other_ts = child_sa->get_other_traffic_selectors(child_sa);
- policy = charon->policies->get_policy(charon->policies,
- this->my_id, this->other_id,
- my_ts, other_ts,
- this->my_host, this->other_host);
- if (policy == NULL)
- {
- DBG1(DBG_IKE, "no policy for CHILD to handle DPD");
- continue;
- }
+ connection_t *connection = NULL;
+ policy_t *policy;
+ linked_list_t *my_ts, *other_ts;
+ child_sa_t* child_sa;
+ dpd_action_t action;
+ job_t *job;
- action = policy->get_dpd_action(policy);
- /* get a connection for further actions */
- if (connection == NULL &&
- (action == DPD_ROUTE || action == DPD_RESTART))
+ DBG2(DBG_IKE, "dead peer detected, handling CHILD_SAs dpd action");
+
+ /* check for childrens with dpdaction = hold */
+ while(this->child_sas->remove_first(this->child_sas,
+ (void**)&child_sa) == SUCCESS)
{
- connection = charon->connections->get_connection_by_hosts(
- charon->connections,
- this->my_host, this->other_host);
- if (connection == NULL)
+ /* get the policy which belongs to this CHILD */
+ my_ts = child_sa->get_my_traffic_selectors(child_sa);
+ other_ts = child_sa->get_other_traffic_selectors(child_sa);
+ policy = charon->policies->get_policy(charon->policies,
+ this->my_id, this->other_id,
+ my_ts, other_ts,
+ this->my_host, this->other_host);
+ if (policy == NULL)
{
- SIG(IKE_UP_FAILED, "no connection found to handle DPD");
- break;
+ DBG1(DBG_IKE, "no policy for CHILD to handle DPD");
+ continue;
+ }
+
+ action = policy->get_dpd_action(policy);
+ /* get a connection for further actions */
+ if (connection == NULL &&
+ (action == DPD_ROUTE || action == DPD_RESTART))
+ {
+ connection = charon->connections->get_connection_by_hosts(
+ charon->connections,
+ this->my_host, this->other_host);
+ if (connection == NULL)
+ {
+ SIG(IKE_UP_FAILED, "no connection found to handle DPD");
+ break;
+ }
}
+
+ DBG1(DBG_IKE, "dpd action for %s is %N",
+ policy->get_name(policy), dpd_action_names, action);
+
+ switch (action)
+ {
+ case DPD_ROUTE:
+ connection->get_ref(connection);
+ job = (job_t*)route_job_create(connection, policy, TRUE);
+ charon->job_queue->add(charon->job_queue, job);
+ break;
+ case DPD_RESTART:
+ connection->get_ref(connection);
+ job = (job_t*)initiate_job_create(connection, policy);
+ charon->job_queue->add(charon->job_queue, job);
+ break;
+ default:
+ policy->destroy(policy);
+ break;
+ }
+ child_sa->destroy(child_sa);
}
- DBG1(DBG_IKE, "dpd action for %s is %N",
- policy->get_name(policy), dpd_action_names, action);
-
- switch (action)
+ /* send a proper signal to brief interested bus listeners */
+ switch (this->state)
{
- case DPD_ROUTE:
- connection->get_ref(connection);
- job = (job_t*)route_job_create(connection, policy, TRUE);
- charon->job_queue->add(charon->job_queue, job);
+ case IKE_CONNECTING:
+ SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding");
break;
- case DPD_RESTART:
- connection->get_ref(connection);
- job = (job_t*)initiate_job_create(connection, NULL, policy);
- charon->job_queue->add(charon->job_queue, job);
+ case IKE_REKEYING:
+ SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding");
+ break;
+ case IKE_DELETING:
+ SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding");
break;
default:
- policy->destroy(policy);
break;
}
- child_sa->destroy(child_sa);
- }
-
- /* send a proper signal to brief interested bus listeners */
- switch (this->state)
- {
- case IKE_CONNECTING:
- SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding");
- break;
- case IKE_REKEYING:
- SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding");
- break;
- case IKE_DELETING:
- SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding");
- break;
- default:
- break;
- }
-
- DESTROY_IF(connection);
-}
-
-/**
- * send a request and schedule retransmission
- */
-static status_t transmit_request(private_ike_sa_t *this)
-{
- message_t *request;
- packet_t *packet;
- status_t status;
- retransmit_request_job_t *job;
- u_int32_t transmitted;
- u_int32_t timeout;
- transaction_t *transaction = this->transaction_out;
- u_int32_t message_id;
-
- transmitted = transaction->requested(transaction);
- timeout = charon->configuration->get_retransmit_timeout(charon->configuration,
- transmitted,
- this->retrans_sequences);
- if (timeout == 0)
- {
- DBG1(DBG_IKE, "giving up after %d retransmits, deleting IKE_SA",
- transmitted - 1);
- dpd_detected(this);
+
+ DESTROY_IF(connection);
return DESTROY_ME;
}
-
- status = transaction->get_request(transaction, &request);
- if (status != SUCCESS)
- {
- /* generating request failed */
- return status;
- }
- message_id = transaction->get_message_id(transaction);
- /* if we retransmit, the request is already generated */
- if (transmitted == 0)
- {
- status = request->generate(request, this->crypter_out, this->signer_out, &packet);
- if (status != SUCCESS)
- {
- DBG1(DBG_IKE, "request generation failed. transaction discarded");
- return FAILED;
- }
- }
- else
- {
- DBG1(DBG_IKE, "sending retransmit %d for %N request with messageID %d",
- transmitted, exchange_type_names, request->get_exchange_type(request),
- message_id);
- packet = request->get_packet(request);
- }
- /* finally send */
- charon->send_queue->add(charon->send_queue, packet);
- this->time.outbound = time(NULL);
-
- /* schedule retransmission job */
- job = retransmit_request_job_create(message_id, this->ike_sa_id);
- charon->event_queue->add_relative(charon->event_queue, (job_t*)job, timeout);
return SUCCESS;
}
/**
- * Implementation of ike_sa.retransmit_request.
- */
-static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id)
-{
- if (this->transaction_out == NULL ||
- this->transaction_out->get_message_id(this->transaction_out) != message_id)
- {
- /* no retransmit necessary, transaction did already complete */
- return SUCCESS;
- }
- return transmit_request(this);
-}
-
-/**
- * Check for transactions in the queue and initiate the first transaction found.
+ * Implementation of ike_sa_t.generate
*/
-static status_t process_transaction_queue(private_ike_sa_t *this)
+static status_t generate_message(private_ike_sa_t *this, message_t *message,
+ packet_t **packet)
{
- if (this->transaction_out)
- {
- /* already a transaction in progress */
- return SUCCESS;
- }
-
- while (TRUE)
- {
- if (this->transaction_queue->remove_first(this->transaction_queue,
- (void**)&this->transaction_out) != SUCCESS)
- {
- /* transaction queue empty */
- return SUCCESS;
- }
- switch (transmit_request(this))
- {
- case SUCCESS:
- return SUCCESS;
- case DESTROY_ME:
- /* critical, IKE_SA unusable, destroy immediately */
- return DESTROY_ME;
- default:
- /* discard transaction, process next one */
- this->transaction_out->destroy(this->transaction_out);
- this->transaction_out = NULL;
- /* handle next transaction */
- continue;
- }
- }
-}
-
-/**
- * Queue a new transaction and execute the next outstanding transaction
- */
-static status_t queue_transaction(private_ike_sa_t *this, transaction_t *transaction, bool prefer)
-{
- /* inject next transaction */
- if (transaction)
- {
- if (prefer)
- {
- this->transaction_queue->insert_first(this->transaction_queue, transaction);
- }
- else
- {
- this->transaction_queue->insert_last(this->transaction_queue, transaction);
- }
- }
- /* process a transaction */
- return process_transaction_queue(this);
-}
-
-/**
- * process an incoming request.
- */
-static status_t process_request(private_ike_sa_t *this, message_t *request)
-{
- transaction_t *last, *current = NULL;
- message_t *response;
- packet_t *packet;
- u_int32_t request_mid;
- status_t status;
-
- request_mid = request->get_message_id(request);
- last = this->transaction_in;
-
- /* check if message ID is correct */
- if (last)
- {
- u_int32_t last_mid = last->get_message_id(last);
-
- if (last_mid == request_mid)
- {
- /* retransmit detected */
- DBG1(DBG_IKE, "received retransmitted request for message "
- "ID %d, retransmitting response", request_mid);
- last->get_response(last, request, &response, &this->transaction_in_next);
- packet = response->get_packet(response);
- charon->send_queue->add(charon->send_queue, packet);
- this->time.outbound = time(NULL);
- return SUCCESS;
- }
-
- if (last_mid > request_mid)
- {
- /* something seriously wrong here, message id may not decrease */
- DBG1(DBG_IKE, "received request with message ID %d, "
- "excepted %d, ingored", request_mid, last_mid + 1);
- return FAILED;
- }
- /* we allow jumps in message IDs, as long as they are incremental */
- if (last_mid + 1 < request_mid)
- {
- DBG1(DBG_IKE, "received request with message ID %d, excepted %d",
- request_mid, last_mid + 1);
- }
- }
- else
- {
- if (request_mid != 0)
- {
- /* warn, but allow it */
- DBG1(DBG_IKE, "first received request has message ID %d, "
- "excepted 0", request_mid);
- }
- }
-
- /* check if we already have a pre-created transaction for this request */
- if (this->transaction_in_next)
- {
- current = this->transaction_in_next;
- this->transaction_in_next = NULL;
- }
- else
- {
- current = transaction_create(&this->public, request);
- if (current == NULL)
- {
- DBG1(DBG_IKE, "no idea how to handle received message (exchange"
- " type %d), ignored", request->get_exchange_type(request));
- return FAILED;
- }
- }
-
- /* send message. get_request() always gives a valid response */
- status = current->get_response(current, request, &response, &this->transaction_in_next);
- if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS)
- {
- DBG1(DBG_IKE, "response generation failed, discarding transaction");
- current->destroy(current);
- return FAILED;
- }
-
- charon->send_queue->add(charon->send_queue, packet);
this->time.outbound = time(NULL);
- /* act depending on transaction result */
- switch (status)
- {
- case DESTROY_ME:
- /* transactions says we should destroy the IKE_SA, so do it */
- current->destroy(current);
- return DESTROY_ME;
- default:
- /* store for retransmission, destroy old transaction */
- this->transaction_in = current;
- if (last)
- {
- last->destroy(last);
- }
- return SUCCESS;
- }
-}
-
-/**
- * process an incoming response
- */
-static status_t process_response(private_ike_sa_t *this, message_t *response)
-{
- transaction_t *current, *new = NULL;
-
- current = this->transaction_out;
- /* check if message ID is that of our currently active transaction */
- if (current == NULL ||
- current->get_message_id(current) != response->get_message_id(response))
- {
- DBG1(DBG_IKE, "received response with message ID %d "
- "not requested, ignored", response->get_message_id(response));
- return FAILED;
- }
-
- switch (current->conclude(current, response, &new))
- {
- case DESTROY_ME:
- /* state requested to destroy IKE_SA */
- return DESTROY_ME;
- default:
- /* discard transaction, process next one */
- break;
- }
- /* transaction comleted, remove */
- current->destroy(current);
- this->transaction_out = NULL;
-
- /* queue new transaction */
- return queue_transaction(this, new, TRUE);
+ message->set_ike_sa_id(message, this->ike_sa_id);
+ message->set_destination(message, this->other_host->clone(this->other_host));
+ message->set_source(message, this->my_host->clone(this->my_host));
+ return message->generate(message, this->crypter_out, this->signer_out, packet);
}
/**
* send a notify back to the sender
*/
-static void send_notify_response(private_ike_sa_t *this,
- message_t *request,
+static void send_notify_response(private_ike_sa_t *this, message_t *request,
notify_type_t type)
{
- notify_payload_t *notify;
message_t *response;
- host_t *src, *dst;
packet_t *packet;
response = message_create();
- dst = request->get_source(request);
- src = request->get_destination(request);
- response->set_source(response, src->clone(src));
- response->set_destination(response, dst->clone(dst));
response->set_exchange_type(response, request->get_exchange_type(request));
response->set_request(response, FALSE);
response->set_message_id(response, request->get_message_id(request));
- response->set_ike_sa_id(response, this->ike_sa_id);
- notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type);
- response->add_payload(response, (payload_t *)notify);
- if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS)
+ response->add_notify(response, FALSE, type, chunk_empty);
+ if (this->my_host->is_anyaddr(this->my_host))
{
- response->destroy(response);
- return;
+ this->my_host->destroy(this->my_host);
+ this->my_host = request->get_destination(request);
+ this->my_host = this->my_host->clone(this->my_host);
+ }
+ if (this->other_host->is_anyaddr(this->other_host))
+ {
+ this->other_host->destroy(this->other_host);
+ this->other_host = request->get_source(request);
+ this->other_host = this->other_host->clone(this->other_host);
+ }
+ if (generate_message(this, response, &packet) == SUCCESS)
+ {
+ charon->send_queue->add(charon->send_queue, packet);
}
- charon->send_queue->add(charon->send_queue, packet);
- this->time.outbound = time(NULL);
response->destroy(response);
- return;
}
-
/**
* Implementation of ike_sa_t.process_message.
*/
@@ -875,27 +603,66 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
exchange_type_names, message->get_exchange_type(message),
message->get_request(message) ? "request" : "response",
message->get_message_id(message));
+ return status;
}
else
{
+ host_t *me, *other;
+
+ me = message->get_destination(message);
+ other = message->get_source(message);
+
+ /* if this IKE_SA is virgin, we check for a connection */
+ if (this->connection == NULL)
+ {
+ this->connection = charon->connections->get_connection_by_hosts(
+ charon->connections, me, other);
+ if (this->connection == NULL)
+ {
+ /* no connection found for these hosts, destroy */
+ send_notify_response(this, message, NO_PROPOSAL_CHOSEN);
+ return DESTROY_ME;
+ }
+ }
+
/* check if message is trustworthy, and update connection information */
if (this->state == IKE_CREATED ||
message->get_exchange_type(message) != IKE_SA_INIT)
{
- update_hosts(this, message->get_destination(message),
- message->get_source(message));
+ update_hosts(this, me, other);
this->time.inbound = time(NULL);
}
- if (is_request)
- {
- status = process_request(this, message);
- }
- else
- {
- status = process_response(this, message);
- }
+ return this->task_manager->process_message(this->task_manager, message);
+ }
+}
+
+/**
+ * apply the connection/policy information to this IKE_SA
+ */
+static void apply_config(private_ike_sa_t *this,
+ connection_t *connection, policy_t *policy)
+{
+ host_t *me, *other;
+ identification_t *my_id, *other_id;
+
+ if (this->connection == NULL && this->policy == NULL)
+ {
+ this->connection = connection;
+ connection->get_ref(connection);
+ this->policy = policy;
+ policy->get_ref(policy);
+
+ me = connection->get_my_host(connection);
+ other = connection->get_other_host(connection);
+ my_id = policy->get_my_id(policy);
+ other_id = policy->get_other_id(policy);
+ set_my_host(this, me->clone(me));
+ set_other_host(this, other->clone(other));
+ DESTROY_IF(this->my_id);
+ DESTROY_IF(this->other_id);
+ this->my_id = my_id->clone(my_id);
+ this->other_id = other_id->clone(other_id);
}
- return status;
}
/**
@@ -904,77 +671,29 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
static status_t initiate(private_ike_sa_t *this,
connection_t *connection, policy_t *policy)
{
- switch (this->state)
+ task_t *task;
+
+ if (this->state == IKE_CREATED)
{
- case IKE_CREATED:
- {
- /* in state CREATED, we must do the ike_sa_init
- * and ike_auth transactions. Along with these,
- * a CHILD_SA with the supplied policy is set up.
- */
- ike_sa_init_t *ike_sa_init;
-
- DBG2(DBG_IKE, "initiating new IKE_SA for CHILD_SA");
- if (this->my_host->is_anyaddr(this->my_host))
- {
- this->my_host->destroy(this->my_host);
- this->my_host = connection->get_my_host(connection);
- this->my_host = this->my_host->clone(this->my_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- this->other_host->destroy(this->other_host);
- this->other_host = connection->get_other_host(connection);
- this->other_host = this->other_host->clone(this->other_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- SIG(IKE_UP_START, "establishing new IKE_SA for CHILD_SA");
- SIG(IKE_UP_FAILED, "can not initiate a connection to %%any, aborting");
- policy->destroy(policy);
- connection->destroy(connection);
- return DESTROY_ME;
- }
-
- this->retrans_sequences = connection->get_retrans_seq(connection);
- this->dpd_delay = connection->get_dpd_delay(connection);
-
- this->message_id_out = 1;
- ike_sa_init = ike_sa_init_create(&this->public);
- ike_sa_init->set_config(ike_sa_init, connection, policy);
- return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
- }
- case IKE_DELETING:
- case IKE_REKEYING:
- {
- /* if we are in DELETING/REKEYING, we deny set up of a policy.
- * TODO: would it make sense to queue the transaction and adopt
- * all transactions to the new IKE_SA? */
- SIG(IKE_UP_START, "creating CHILD_SA in existing IKE_SA");
- SIG(IKE_UP_FAILED, "creating CHILD_SA discarded, as IKE_SA is in state %N",
- ike_sa_state_names, this->state);
- policy->destroy(policy);
- connection->destroy(connection);
- return FAILED;
- }
- case IKE_CONNECTING:
- case IKE_ESTABLISHED:
- {
- /* if we are ESTABLISHED or CONNECTING, we queue the
- * transaction to create the CHILD_SA. It gets processed
- * when the IKE_SA is ready to do so. We don't need the
- * connection, as the IKE_SA is already established/establishing.
- */
- create_child_sa_t *create_child;
-
- DBG1(DBG_IKE, "creating CHILD_SA in existing IKE_SA");
- connection->destroy(connection);
- create_child = create_child_sa_create(&this->public);
- create_child->set_policy(create_child, policy);
- return queue_transaction(this, (transaction_t*)create_child, FALSE);
- }
- }
- return FAILED;
+ /* if we aren't established/establishing, do so */
+ apply_config(this, connection, policy);
+
+ task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_natd_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_cert_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_config_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_auth_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ }
+
+ task = (task_t*)child_create_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+
+ return this->task_manager->initiate(this->task_manager);
}
/**
@@ -982,11 +701,11 @@ static status_t initiate(private_ike_sa_t *this,
*/
static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
{
- connection_t *connection;
policy_t *policy;
iterator_t *iterator;
child_sa_t *current, *child_sa = NULL;
- linked_list_t *my_ts, *other_ts;
+ task_t *task;
+ child_create_t *child_create;
if (this->state == IKE_DELETING)
{
@@ -1014,66 +733,28 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
"CHILD_SA not found", reqid);
return FAILED;
}
- my_ts = child_sa->get_my_traffic_selectors(child_sa);
- other_ts = child_sa->get_other_traffic_selectors(child_sa);
- policy = charon->policies->get_policy(charon->policies,
- this->my_id, this->other_id,
- my_ts, other_ts,
- this->my_host, this->other_host);
- if (policy == NULL)
- {
- SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid);
- SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: "
- "no policy found", reqid);
- return FAILED;
- }
+ policy = child_sa->get_policy(child_sa);
- switch (this->state)
+ if (this->state == IKE_CREATED)
{
- case IKE_CREATED:
- {
- ike_sa_init_t *ike_sa_init;
-
- connection = charon->connections->get_connection_by_hosts(
- charon->connections, this->my_host, this->other_host);
-
- if (connection == NULL)
- {
- SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid);
- SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: "
- "no connection found to establsih IKE_SA", reqid);
- policy->destroy(policy);
- return FAILED;
- }
-
- DBG1(DBG_IKE, "establishing IKE_SA to acquire CHILD_SA "
- "with reqid %d", reqid);
-
- this->message_id_out = 1;
- ike_sa_init = ike_sa_init_create(&this->public);
- ike_sa_init->set_config(ike_sa_init, connection, policy);
- /* reuse existing reqid */
- ike_sa_init->set_reqid(ike_sa_init, reqid);
- return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
- }
- case IKE_CONNECTING:
- case IKE_ESTABLISHED:
- {
- create_child_sa_t *create_child;
-
- DBG1(DBG_CHD, "acquiring CHILD_SA with reqid %d", reqid);
-
- create_child = create_child_sa_create(&this->public);
- create_child->set_policy(create_child, policy);
- /* reuse existing reqid */
- create_child->set_reqid(create_child, reqid);
- return queue_transaction(this, (transaction_t*)create_child, FALSE);
- }
- default:
- break;
+ task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_natd_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_cert_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_config_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_auth_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
}
- return FAILED;
+
+ child_create = child_create_create(&this->public, policy);
+ child_create->use_reqid(child_create, reqid);
+ this->task_manager->queue_task(this->task_manager, (task_t*)child_create);
+
+ return this->task_manager->initiate(this->task_manager);
}
/**
@@ -1148,55 +829,25 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t
switch (this->state)
{
+ case IKE_DELETING:
+ case IKE_REKEYING:
+ SIG(CHILD_ROUTE_FAILED,
+ "unable to route CHILD_SA, as its IKE_SA gets deleted");
+ return FAILED;
case IKE_CREATED:
- case IKE_CONNECTING:
- /* we update IKE_SA information as good as possible,
- * this allows us to set up the SA later when an acquire comes in. */
- if (this->my_id->get_type(this->my_id) == ID_ANY)
- {
- this->my_id->destroy(this->my_id);
- this->my_id = policy->get_my_id(policy);
- this->my_id = this->my_id->clone(this->my_id);
- }
- if (this->other_id->get_type(this->other_id) == ID_ANY)
- {
- this->other_id->destroy(this->other_id);
- this->other_id = policy->get_other_id(policy);
- this->other_id = this->other_id->clone(this->other_id);
- }
- if (this->my_host->is_anyaddr(this->my_host))
- {
- this->my_host->destroy(this->my_host);
- this->my_host = connection->get_my_host(connection);
- this->my_host = this->my_host->clone(this->my_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- this->other_host->destroy(this->other_host);
- this->other_host = connection->get_other_host(connection);
- this->other_host = this->other_host->clone(this->other_host);
- }
- set_name(this, connection->get_name(connection));
- this->retrans_sequences = connection->get_retrans_seq(connection);
- this->dpd_delay = connection->get_dpd_delay(connection);
+ /* apply connection information, we need it to acquire */
+ apply_config(this, connection, policy);
break;
+ case IKE_CONNECTING:
case IKE_ESTABLISHED:
- case IKE_REKEYING:
- /* nothing to do. We allow it for rekeying, as it will be
- * adopted by the new IKE_SA */
+ default:
break;
- case IKE_DELETING:
- /* TODO: hanlde this case, create a new IKE_SA and route CHILD_SA */
- SIG(CHILD_ROUTE_FAILED, "unable to route CHILD_SA, as its IKE_SA gets deleted");
- return FAILED;
}
- child_sa = child_sa_create(0, this->my_host, this->other_host,
- this->my_id, this->other_id,
- 0, 0,
- NULL, policy->get_hostaccess(policy),
- FALSE);
- child_sa->set_name(child_sa, policy->get_name(policy));
+ /* install kernel policies */
+ child_sa = child_sa_create(this->my_host, this->other_host,
+ this->my_id, this->other_id, policy, FALSE, 0);
+
my_ts = policy->get_my_traffic_selectors(policy, this->my_host);
other_ts = policy->get_other_traffic_selectors(policy, this->other_host);
status = child_sa->add_policies(child_sa, my_ts, other_ts,
@@ -1269,40 +920,45 @@ static status_t unroute(private_ike_sa_t *this, policy_t *policy)
static status_t send_dpd(private_ike_sa_t *this)
{
send_dpd_job_t *job;
- time_t diff;
+ time_t diff, delay;
+
+ delay = this->connection->get_dpd_delay(this->connection);
- if (this->dpd_delay == 0)
+ if (delay == 0)
{
/* DPD disabled */
return SUCCESS;
}
- if (this->transaction_out)
+ if (this->task_manager->busy(this->task_manager))
{
- /* there is a transaction in progress. Come back later */
+ /* an exchange is in the air, no need to start a DPD check */
diff = 0;
}
else
{
/* check if there was any inbound traffic */
time_t last_in, now;
- last_in = get_time_inbound(this);
+ last_in = get_use_time(this, TRUE);
now = time(NULL);
diff = now - last_in;
- if (diff >= this->dpd_delay)
+ if (diff >= delay)
{
/* to long ago, initiate dead peer detection */
- dead_peer_detection_t *dpd;
- DBG1(DBG_IKE, "sending DPD request");
- dpd = dead_peer_detection_create(&this->public);
- queue_transaction(this, (transaction_t*)dpd, FALSE);
+ task_t *task;
+
+ task = (task_t*)ike_dpd_create(TRUE);
diff = 0;
+ DBG1(DBG_IKE, "sending DPD request");
+
+ this->task_manager->queue_task(this->task_manager, task);
+ this->task_manager->initiate(this->task_manager);
}
}
/* recheck in "interval" seconds */
job = send_dpd_job_create(this->ike_sa_id);
charon->event_queue->add_relative(charon->event_queue, (job_t*)job,
- (this->dpd_delay - diff) * 1000);
+ (delay - diff) * 1000);
return SUCCESS;
}
@@ -1314,7 +970,7 @@ static void send_keepalive(private_ike_sa_t *this)
send_keepalive_job_t *job;
time_t last_out, now, diff, interval;
- last_out = get_time_outbound(this);
+ last_out = get_use_time(this, FALSE);
now = time(NULL);
diff = now - last_out;
@@ -1360,9 +1016,37 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state)
if (state == IKE_ESTABLISHED)
{
- this->time.established = time(NULL);
+ job_t *job;
+ u_int32_t now = time(NULL);
+ u_int32_t soft, hard;
+ bool reauth;
+
+ this->time.established = now;
/* start DPD checks */
send_dpd(this);
+
+ /* schedule rekeying/reauthentication */
+ soft = this->connection->get_soft_lifetime(this->connection);
+ hard = this->connection->get_hard_lifetime(this->connection);
+ reauth = this->connection->get_reauth(this->connection);
+ DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds",
+ reauth ? "reauthentication": "rekeying", soft, hard);
+
+ if (soft)
+ {
+ this->time.rekey = now + soft;
+ job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ soft * 1000);
+ }
+
+ if (hard)
+ {
+ this->time.delete = now + hard;
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ hard * 1000);
+ }
}
this->state = state;
@@ -1446,12 +1130,12 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other)
* Implementation of ike_sa_t.derive_keys.
*/
static status_t derive_keys(private_ike_sa_t *this,
- proposal_t *proposal, diffie_hellman_t *dh,
+ proposal_t *proposal, chunk_t secret,
chunk_t nonce_i, chunk_t nonce_r,
bool initiator, prf_t *child_prf, prf_t *old_prf)
{
prf_plus_t *prf_plus;
- chunk_t skeyseed, secret, key, nonces, prf_plus_seed;
+ chunk_t skeyseed, key, nonces, prf_plus_seed;
algorithm_t *algo;
size_t key_size;
crypter_t *crypter_i, *crypter_r;
@@ -1475,7 +1159,6 @@ static status_t derive_keys(private_ike_sa_t *this,
return FAILED;
}
- dh->get_shared_secret(dh, &secret);
DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
nonces = chunk_cat("cc", nonce_i, nonce_r);
*((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id);
@@ -1640,28 +1323,6 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa)
}
/**
- * Implementation of ike_sa_t.has_child_sa.
- */
-static bool has_child_sa(private_ike_sa_t *this, u_int32_t reqid)
-{
- iterator_t *iterator;
- child_sa_t *current;
- bool found = FALSE;
-
- iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
- while (iterator->iterate(iterator, (void**)&current))
- {
- if (current->get_reqid(current) == reqid)
- {
- found = TRUE;
- break;
- }
- }
- iterator->destroy(iterator);
- return found;
-}
-
-/**
* Implementation of ike_sa_t.get_child_sa.
*/
static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
@@ -1672,7 +1333,7 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
while (iterator->iterate(iterator, (void**)&current))
- {;
+ {
if (current->get_spi(current, inbound) == spi &&
current->get_protocol(current) == protocol)
{
@@ -1696,18 +1357,17 @@ static iterator_t* create_child_sa_iterator(private_ike_sa_t *this)
*/
static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
{
- create_child_sa_t *rekey;
child_sa_t *child_sa;
+ child_rekey_t *child_rekey;
child_sa = get_child_sa(this, protocol, spi, TRUE);
- if (child_sa == NULL)
+ if (child_sa)
{
- return NOT_FOUND;
+ child_rekey = child_rekey_create(&this->public, child_sa);
+ this->task_manager->queue_task(this->task_manager, &child_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
}
-
- rekey = create_child_sa_create(&this->public);
- rekey->rekeys_child(rekey, child_sa);
- return queue_transaction(this, (transaction_t*)rekey, FALSE);
+ return FAILED;
}
/**
@@ -1715,24 +1375,24 @@ static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u
*/
static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
{
- delete_child_sa_t *del;
child_sa_t *child_sa;
+ child_delete_t *child_delete;
child_sa = get_child_sa(this, protocol, spi, TRUE);
- if (child_sa == NULL)
+ if (child_sa)
{
- return NOT_FOUND;
+ child_delete = child_delete_create(&this->public, child_sa);
+ this->task_manager->queue_task(this->task_manager, &child_delete->task);
+ return this->task_manager->initiate(this->task_manager);
}
-
- del = delete_child_sa_create(&this->public);
- del->set_child_sa(del, child_sa);
- return queue_transaction(this, (transaction_t*)del, FALSE);
+ return FAILED;
}
/**
* Implementation of ike_sa_t.destroy_child_sa.
*/
-static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi)
{
iterator_t *iterator;
child_sa_t *child_sa;
@@ -1755,69 +1415,27 @@ static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
}
/**
- * Implementation of ike_sa_t.set_lifetimes.
- */
-static void set_lifetimes(private_ike_sa_t *this, bool reauth,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime)
-{
- job_t *job;
- u_int32_t now = time(NULL);
-
- this->reauth = reauth;
-
- if (soft_lifetime)
- {
- this->time.rekey = now + soft_lifetime;
- job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth);
- charon->event_queue->add_relative(charon->event_queue, job,
- soft_lifetime * 1000);
- }
-
- if (hard_lifetime)
- {
- this->time.delete = now + hard_lifetime;
- job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
- charon->event_queue->add_relative(charon->event_queue, job,
- hard_lifetime * 1000);
- }
-}
-
-/**
* Implementation of public_ike_sa_t.delete.
*/
static status_t delete_(private_ike_sa_t *this)
{
+ ike_delete_t *ike_delete;
+
switch (this->state)
{
- case IKE_CONNECTING:
- {
- /* this may happen if a half open IKE_SA gets closed after a
- * timeout. We signal here UP_FAILED to complete the SIG schema */
- SIG(IKE_UP_FAILED, "half open IKE_SA deleted after timeout");
- return DESTROY_ME;
- }
case IKE_ESTABLISHED:
- {
- delete_ike_sa_t *delete_ike_sa;
- if (this->transaction_out)
- {
- /* already a transaction in progress. As this may hang
- * around a while, we don't inform the other peer. */
- return DESTROY_ME;
- }
- delete_ike_sa = delete_ike_sa_create(&this->public);
- return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE);
- }
- case IKE_CREATED:
- case IKE_DELETING:
+ DBG1(DBG_IKE, "deleting IKE_SA");
+ /* do not log when rekeyed */
+ case IKE_REKEYING:
+ ike_delete = ike_delete_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, &ike_delete->task);
+ return this->task_manager->initiate(this->task_manager);
default:
- {
- SIG(IKE_DOWN_START, "closing IKE_SA");
- SIG(IKE_DOWN_SUCCESS, "IKE_SA closed between %H[%D]...%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
- return DESTROY_ME;
- }
+ DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification",
+ ike_sa_state_names, this->state);
+ break;
}
+ return DESTROY_ME;
}
/**
@@ -1825,161 +1443,373 @@ static status_t delete_(private_ike_sa_t *this)
*/
static status_t rekey(private_ike_sa_t *this)
{
- rekey_ike_sa_t *rekey_ike_sa;
-
- DBG1(DBG_IKE, "rekeying IKE_SA between %H[%D]..%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
+ ike_rekey_t *ike_rekey;
- if (this->state != IKE_ESTABLISHED)
- {
- SIG(IKE_REKEY_START, "rekeying IKE_SA");
- SIG(IKE_REKEY_FAILED, "unable to rekey IKE_SA in state %N",
- ike_sa_state_names, this->state);
- return FAILED;
- }
+ ike_rekey = ike_rekey_create(&this->public, TRUE);
- rekey_ike_sa = rekey_ike_sa_create(&this->public);
- return queue_transaction(this, (transaction_t*)rekey_ike_sa, FALSE);
+ this->task_manager->queue_task(this->task_manager, &ike_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
}
/**
- * Implementation of ike_sa_t.reauth.
+ * Implementation of ike_sa_t.reestablish
*/
-static status_t reauth(private_ike_sa_t *this)
+static void reestablish(private_ike_sa_t *this)
{
- connection_t *connection;
- child_sa_t *child_sa;
+ ike_sa_id_t *other_id;
+ private_ike_sa_t *other;
iterator_t *iterator;
+ child_sa_t *child_sa;
+ policy_t *policy;
+ task_t *task;
+ job_t *job;
- DBG1(DBG_IKE, "reauthenticating IKE_SA between %H[%D]..%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
+ other_id = ike_sa_id_create(0, 0, TRUE);
+ other = (private_ike_sa_t*)charon->ike_sa_manager->checkout(
+ charon->ike_sa_manager, other_id);
+ other_id->destroy(other_id);
- /* get a connection to initiate */
- connection = charon->connections->get_connection_by_hosts(charon->connections,
- this->my_host, this->other_host);
- if (connection == NULL)
+ apply_config(other, this->connection, this->policy);
+
+ if (this->state == IKE_ESTABLISHED)
{
- DBG1(DBG_IKE, "no connection found to reauthenticate");
- return FAILED;
+ task = (task_t*)ike_init_create(&other->public, TRUE, NULL);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_natd_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_cert_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_config_create(&other->public, other->policy);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_auth_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
}
- /* queue CREATE_CHILD_SA transactions to set up all CHILD_SAs */
+ other->task_manager->adopt_tasks(other->task_manager, this->task_manager);
+
+ /* Create task for established children, adopt routed children directly */
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
- while (iterator->iterate(iterator, (void**)&child_sa))
+ while(iterator->iterate(iterator, (void**)&child_sa))
{
- job_t *job;
- policy_t *policy;
- linked_list_t *my_ts, *other_ts;
- host_t *other;
-
- my_ts = child_sa->get_my_traffic_selectors(child_sa);
- other_ts = child_sa->get_other_traffic_selectors(child_sa);
- policy = charon->policies->get_policy(charon->policies,
- this->my_id, this->other_id, my_ts, other_ts,
- this->my_host, this->other_host);
- if (policy == NULL)
+ switch (child_sa->get_state(child_sa))
{
- DBG1(DBG_IKE, "policy not found to recreate CHILD_SA, skipped");
- continue;
+ case CHILD_ROUTED:
+ {
+ iterator->remove(iterator);
+ other->child_sas->insert_first(other->child_sas, child_sa);
+ break;
+ }
+ default:
+ {
+ policy = child_sa->get_policy(child_sa);
+ task = (task_t*)child_create_create(&other->public, policy);
+ other->task_manager->queue_task(other->task_manager, task);
+ break;
+ }
}
- connection->get_ref(connection);
- other = this->other_host->clone(this->other_host);
- job = (job_t*)initiate_job_create(connection, other, policy);
- charon->job_queue->add(charon->job_queue, job);
}
iterator->destroy(iterator);
- connection->destroy(connection);
- /* delete the old IKE_SA
- * TODO: we should delay the delete to avoid connectivity gaps?! */
- return delete_(this);
+ other->task_manager->initiate(other->task_manager);
+
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, &other->public);
+
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->job_queue->add(charon->job_queue, job);
}
/**
- * Implementation of ike_sa_t.get_rekeying_transaction.
+ * Implementation of ike_sa_t.inherit.
*/
-static transaction_t* get_rekeying_transaction(private_ike_sa_t *this)
+static void inherit(private_ike_sa_t *this, private_ike_sa_t *other)
{
- return this->rekeying_transaction;
+ child_sa_t *child_sa;
+ host_t *ip;
+
+ /* apply hosts and ids */
+ this->my_host->destroy(this->my_host);
+ this->other_host->destroy(this->other_host);
+ this->my_id->destroy(this->my_id);
+ this->other_id->destroy(this->other_id);
+ this->my_host = other->my_host->clone(other->my_host);
+ this->other_host = other->other_host->clone(other->other_host);
+ this->my_id = other->my_id->clone(other->my_id);
+ this->other_id = other->other_id->clone(other->other_id);
+
+ /* apply virtual assigned IPs... */
+ if (other->my_virtual_ip)
+ {
+ this->my_virtual_ip = other->my_virtual_ip;
+ other->my_virtual_ip = NULL;
+ }
+ if (other->other_virtual_ip)
+ {
+ this->other_virtual_ip = other->other_virtual_ip;
+ other->other_virtual_ip = NULL;
+ }
+
+ /* ... and DNS servers */
+ while (other->dns_servers->remove_last(other->dns_servers,
+ (void**)&ip) == SUCCESS)
+ {
+ this->dns_servers->insert_first(this->dns_servers, ip);
+ }
+
+ /* adopt all children */
+ while (other->child_sas->remove_last(other->child_sas,
+ (void**)&child_sa) == SUCCESS)
+ {
+ this->child_sas->insert_first(this->child_sas, (void*)child_sa);
+ }
}
/**
- * Implementation of ike_sa_t.set_rekeying_transaction.
+ * Implementation of ike_sa_t.is_natt_enabled.
*/
-static void set_rekeying_transaction(private_ike_sa_t *this, transaction_t *rekey)
+static bool is_natt_enabled(private_ike_sa_t *this)
{
- this->rekeying_transaction = rekey;
+ return this->nat_here || this->nat_there;
}
/**
- * Implementation of ike_sa_t.adopt_children.
+ * Implementation of ike_sa_t.enable_natt.
*/
-static void adopt_children(private_ike_sa_t *this, private_ike_sa_t *other)
+static void enable_natt(private_ike_sa_t *this, bool local)
{
- child_sa_t *child_sa;
-
- while (other->child_sas->remove_last(other->child_sas,
- (void**)&child_sa) == SUCCESS)
+ if (local)
{
- this->child_sas->insert_first(this->child_sas, (void*)child_sa);
+ DBG1(DBG_IKE, "local host is behind NAT, scheduling keep alives");
+ this->nat_here = TRUE;
+ send_keepalive(this);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "remote host is behind NAT");
+ this->nat_there = TRUE;
}
}
/**
- * Implementation of ike_sa_t.get_next_message_id.
+ * Implementation of ike_sa_t.reset
*/
-static u_int32_t get_next_message_id (private_ike_sa_t *this)
+static void reset(private_ike_sa_t *this)
{
- return this->message_id_out++;
+ /* the responder ID is reset, as peer may choose another one */
+ if (this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0);
+ }
+
+ set_state(this, IKE_CREATED);
+
+ this->task_manager->reset(this->task_manager);
}
/**
- * Implementation of ike_sa_t.is_natt_enabled.
+ * Implementation of ike_sa_t.set_virtual_ip
*/
-static bool is_natt_enabled(private_ike_sa_t *this)
+static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip)
{
- return this->nat_here || this->nat_there;
+ if (local)
+ {
+ DBG1(DBG_IKE, "installing new virtual IP %H", ip);
+ if (this->my_virtual_ip)
+ {
+ DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip);
+ charon->kernel_interface->del_ip(charon->kernel_interface,
+ this->my_virtual_ip,
+ this->other_host);
+ this->my_virtual_ip->destroy(this->my_virtual_ip);
+ }
+ if (charon->kernel_interface->add_ip(charon->kernel_interface, ip,
+ this->other_host) == SUCCESS)
+ {
+ this->my_virtual_ip = ip->clone(ip);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
+ this->my_virtual_ip = NULL;
+ }
+ }
+ else
+ {
+ DESTROY_IF(this->other_virtual_ip);
+ this->other_virtual_ip = ip->clone(ip);
+ }
}
/**
- * Implementation of ike_sa_t.enable_natt.
+ * Implementation of ike_sa_t.get_virtual_ip
*/
-static void enable_natt(private_ike_sa_t *this, bool local)
+static host_t* get_virtual_ip(private_ike_sa_t *this, bool local)
{
if (local)
{
- DBG1(DBG_IKE, "local host is behind NAT, using NAT-T, "
- "scheduled keep alives");
- this->nat_here = TRUE;
- send_keepalive(this);
+ return this->my_virtual_ip;
}
else
{
- DBG1(DBG_IKE, "remote host is behind NAT, using NAT-T");
- this->nat_there = TRUE;
+ return this->other_virtual_ip;
}
}
/**
+ * Implementation of ike_sa_t.remove_dns_server
+ */
+static void remove_dns_servers(private_ike_sa_t *this)
+{
+ FILE *file;
+ struct stat stats;
+ chunk_t contents, line, orig_line, token;
+ char string[INET6_ADDRSTRLEN];
+ host_t *ip;
+ iterator_t *iterator;
+
+ if (this->dns_servers->get_count(this->dns_servers) == 0)
+ {
+ /* don't touch anything if we have no nameservers installed */
+ return;
+ }
+
+ file = fopen(RESOLV_CONF, "r");
+ if (file == NULL || stat(RESOLV_CONF, &stats) != 0)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ contents = chunk_alloca((size_t)stats.st_size);
+
+ if (fread(contents.ptr, 1, contents.len, file) != contents.len)
+ {
+ DBG1(DBG_IKE, "unable to read DNS configuration file: %m");
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+ file = fopen(RESOLV_CONF, "w");
+ if (file == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ iterator = this->dns_servers->create_iterator(this->dns_servers, TRUE);
+ while (fetchline(&contents, &line))
+ {
+ bool found = FALSE;
+ orig_line = line;
+ if (extract_token(&token, ' ', &line) &&
+ strncasecmp(token.ptr, "nameserver", token.len) == 0)
+ {
+ if (!extract_token(&token, ' ', &line))
+ {
+ token = line;
+ }
+ iterator->reset(iterator);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ snprintf(string, sizeof(string), "%H", ip);
+ if (strlen(string) == token.len &&
+ strncmp(token.ptr, string, token.len) == 0)
+ {
+ iterator->remove(iterator);
+ ip->destroy(ip);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ /* write line untouched back to file */
+ fwrite(orig_line.ptr, orig_line.len, 1, file);
+ fprintf(file, "\n");
+ }
+ }
+ iterator->destroy(iterator);
+ fclose(file);
+}
+
+/**
+ * Implementation of ike_sa_t.add_dns_server
+ */
+static void add_dns_server(private_ike_sa_t *this, host_t *dns)
+{
+ FILE *file;
+ struct stat stats;
+ chunk_t contents;
+
+ DBG1(DBG_IKE, "installing DNS server %H", dns);
+
+ file = fopen(RESOLV_CONF, "a+");
+ if (file == NULL || stat(RESOLV_CONF, &stats) != 0)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ contents = chunk_alloca(stats.st_size);
+
+ if (fread(contents.ptr, 1, contents.len, file) != contents.len)
+ {
+ DBG1(DBG_IKE, "unable to read DNS configuration file: %m");
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+ file = fopen(RESOLV_CONF, "w");
+ if (file == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ if (fprintf(file, "nameserver %H # added by strongSwan, assigned by %D\n",
+ dns, this->other_id) < 0)
+ {
+ DBG1(DBG_IKE, "unable to write DNS configuration: %m");
+ }
+ else
+ {
+ this->dns_servers->insert_last(this->dns_servers, dns->clone(dns));
+ }
+ fwrite(contents.ptr, contents.len, 1, file);
+
+ fclose(file);
+}
+
+/**
* output handler in printf()
*/
static int print(FILE *stream, const struct printf_info *info,
const void *const *args)
{
int written = 0;
+ bool reauth = FALSE;
private_ike_sa_t *this = *((private_ike_sa_t**)(args[0]));
+ if (this->connection)
+ {
+ reauth = this->connection->get_reauth(this->connection);
+ }
+
if (this == NULL)
{
return fprintf(stream, "(null)");
}
- written = fprintf(stream, "%12s: %N, %H[%D]...%H[%D]",
- this->name, ike_sa_state_names, this->state,
- this->my_host, this->my_id, this->other_host, this->other_id);
- written += fprintf(stream, "\n%12s: IKE SPIs: %J, %s in %ds",
- this->name, this->ike_sa_id,
- this->reauth? "reauthentication":"rekeying",
+ written = fprintf(stream, "%12s[%d]: %N, %H[%D]...%H[%D]", get_name(this),
+ this->unique_id, ike_sa_state_names, this->state,
+ this->my_host, this->my_id, this->other_host,
+ this->other_id);
+ written += fprintf(stream, "\n%12s[%d]: IKE SPIs: %J, %s in %ds",
+ get_name(this), this->unique_id, this->ike_sa_id,
+ this->connection && reauth? "reauthentication":"rekeying",
this->time.rekey - time(NULL));
if (info->alt)
@@ -2003,11 +1833,7 @@ static void __attribute__ ((constructor))print_register()
static void destroy(private_ike_sa_t *this)
{
this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy));
- this->transaction_queue->destroy_offset(this->transaction_queue, offsetof(transaction_t, destroy));
- DESTROY_IF(this->transaction_in);
- DESTROY_IF(this->transaction_in_next);
- DESTROY_IF(this->transaction_out);
DESTROY_IF(this->crypter_in);
DESTROY_IF(this->crypter_out);
DESTROY_IF(this->signer_in);
@@ -2017,13 +1843,27 @@ static void destroy(private_ike_sa_t *this)
DESTROY_IF(this->auth_verify);
DESTROY_IF(this->auth_build);
+ if (this->my_virtual_ip)
+ {
+ charon->kernel_interface->del_ip(charon->kernel_interface,
+ this->my_virtual_ip, this->other_host);
+ this->my_virtual_ip->destroy(this->my_virtual_ip);
+ }
+ DESTROY_IF(this->other_virtual_ip);
+
+ remove_dns_servers(this);
+ this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy));
+
DESTROY_IF(this->my_host);
DESTROY_IF(this->other_host);
DESTROY_IF(this->my_id);
DESTROY_IF(this->other_id);
- free(this->name);
+ DESTROY_IF(this->connection);
+ DESTROY_IF(this->policy);
+
this->ike_sa_id->destroy(this->ike_sa_id);
+ this->task_manager->destroy(this->task_manager);
free(this);
}
@@ -2033,17 +1873,21 @@ static void destroy(private_ike_sa_t *this)
ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
{
private_ike_sa_t *this = malloc_thing(private_ike_sa_t);
+ static u_int32_t unique_id = 0;
/* Public functions */
this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state;
this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state;
this->public.get_name = (char*(*)(ike_sa_t*))get_name;
- this->public.set_name = (void(*)(ike_sa_t*,char*))set_name;
this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message;
this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate;
this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route;
this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute;
this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire;
+ this->public.get_connection = (connection_t*(*)(ike_sa_t*))get_connection;
+ this->public.set_connection = (void(*)(ike_sa_t*,connection_t*))set_connection;
+ this->public.get_policy = (policy_t*(*)(ike_sa_t*))get_policy;
+ this->public.set_policy = (void(*)(ike_sa_t*,policy_t*))set_policy;
this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id;
this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host;
this->public.set_my_host = (void(*)(ike_sa_t*,host_t*)) set_my_host;
@@ -2053,8 +1897,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id;
this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id;
this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id;
- this->public.get_next_message_id = (u_int32_t(*)(ike_sa_t*)) get_next_message_id;
- this->public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request;
+ this->public.retransmit = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit;
this->public.delete = (status_t(*)(ike_sa_t*))delete_;
this->public.destroy = (void(*)(ike_sa_t*))destroy;
this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd;
@@ -2063,9 +1906,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf;
this->public.get_auth_verify = (prf_t *(*) (ike_sa_t *)) get_auth_verify;
this->public.get_auth_build = (prf_t *(*) (ike_sa_t *)) get_auth_build;
- this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
+ this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa;
- this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa;
this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa;
this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator;
this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa;
@@ -2073,20 +1915,21 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa;
this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt;
this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled;
- this->public.set_lifetimes = (void(*)(ike_sa_t*,bool,u_int32_t,u_int32_t))set_lifetimes;
- this->public.apply_connection = (void(*)(ike_sa_t*,connection_t*))apply_connection;
this->public.rekey = (status_t(*)(ike_sa_t*))rekey;
- this->public.reauth = (status_t(*)(ike_sa_t*))reauth;
- this->public.get_rekeying_transaction = (transaction_t*(*)(ike_sa_t*))get_rekeying_transaction;
- this->public.set_rekeying_transaction = (void(*)(ike_sa_t*,transaction_t*))set_rekeying_transaction;
- this->public.adopt_children = (void(*)(ike_sa_t*,ike_sa_t*))adopt_children;
+ this->public.reestablish = (void(*)(ike_sa_t*))reestablish;
+ this->public.inherit = (void(*)(ike_sa_t*,ike_sa_t*))inherit;
+ this->public.generate_message = (status_t(*)(ike_sa_t*,message_t*,packet_t**))generate_message;
+ this->public.reset = (void(*)(ike_sa_t*))reset;
+ this->public.get_unique_id = (u_int32_t(*)(ike_sa_t*))get_unique_id;
+ this->public.set_virtual_ip = (void(*)(ike_sa_t*,bool,host_t*))set_virtual_ip;
+ this->public.get_virtual_ip = (host_t*(*)(ike_sa_t*,bool))get_virtual_ip;
+ this->public.add_dns_server = (void(*)(ike_sa_t*,host_t*))add_dns_server;
/* initialize private fields */
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
- this->name = strdup("(uninitialized)");
this->child_sas = linked_list_create();
- this->my_host = host_create_from_string("0.0.0.0", 0);
- this->other_host = host_create_from_string("0.0.0.0", 0);
+ this->my_host = host_create_any(AF_INET);
+ this->other_host = host_create_any(AF_INET);
this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty);
this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty);
this->crypter_in = NULL;
@@ -2099,21 +1942,18 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->child_prf = NULL;
this->nat_here = FALSE;
this->nat_there = FALSE;
- this->transaction_queue = linked_list_create();
- this->transaction_in = NULL;
- this->transaction_in_next = NULL;
- this->transaction_out = NULL;
- this->rekeying_transaction = NULL;
this->state = IKE_CREATED;
- this->message_id_out = 0;
- /* set to NOW, as when we rekey an existing IKE_SA no message is exchanged
- * and inbound therefore uninitialized */
this->time.inbound = this->time.outbound = time(NULL);
this->time.established = 0;
this->time.rekey = 0;
this->time.delete = 0;
- this->dpd_delay = 0;
- this->retrans_sequences = 0;
+ this->connection = NULL;
+ this->policy = NULL;
+ this->task_manager = task_manager_create(&this->public);
+ this->unique_id = ++unique_id;
+ this->my_virtual_ip = NULL;
+ this->other_virtual_ip = NULL;
+ this->dns_servers = linked_list_create();
return &this->public;
}
diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h
index 433e37292..2ba9313ab 100644
--- a/src/charon/sa/ike_sa.h
+++ b/src/charon/sa/ike_sa.h
@@ -33,7 +33,7 @@ typedef struct ike_sa_t ike_sa_t;
#include <encoding/payloads/proposal_substructure.h>
#include <sa/ike_sa_id.h>
#include <sa/child_sa.h>
-#include <sa/transactions/transaction.h>
+#include <sa/tasks/task.h>
#include <config/configuration.h>
#include <utils/randomizer.h>
#include <crypto/prfs/prf.h>
@@ -121,7 +121,7 @@ extern enum_name_t *ike_sa_state_names;
* An IKE_SA contains crypto information related to a connection
* with a peer. It contains multiple IPsec CHILD_SA, for which
* it is responsible. All traffic is handled by an IKE_SA, using
- * transactions.
+ * the task manager and its tasks.
*
* @b Constructors:
* - ike_sa_create()
@@ -141,6 +141,14 @@ struct ike_sa_t {
ike_sa_id_t* (*get_id) (ike_sa_t *this);
/**
+ * @brief Get the numerical ID uniquely defining this IKE_SA.
+ *
+ * @param this calling object
+ * @return unique ID
+ */
+ u_int32_t (*get_unique_id) (ike_sa_t *this);
+
+ /**
* @brief Get the state of the IKE_SA.
*
* @param this calling object
@@ -165,14 +173,6 @@ struct ike_sa_t {
char* (*get_name) (ike_sa_t *this);
/**
- * @brief Set the name of the connection this IKE_SA uses.
- *
- * @param this calling object
- * @param name name, gets cloned
- */
- void (*set_name) (ike_sa_t *this, char* name);
-
- /**
* @brief Get the own host address.
*
* @param this calling object
@@ -235,6 +235,38 @@ struct ike_sa_t {
* @param other identification
*/
void (*set_other_id) (ike_sa_t *this, identification_t *other);
+
+ /**
+ * @brief Get the connection used by this IKE_SA.
+ *
+ * @param this calling object
+ * @return connection
+ */
+ connection_t* (*get_connection) (ike_sa_t *this);
+
+ /**
+ * @brief Set the connection to use with this IKE_SA.
+ *
+ * @param this calling object
+ * @param connection connection to use
+ */
+ void (*set_connection) (ike_sa_t *this, connection_t* connection);
+
+ /**
+ * @brief Get the policy used by this IKE_SA.
+ *
+ * @param this calling object
+ * @return policy
+ */
+ policy_t* (*get_policy) (ike_sa_t *this);
+
+ /**
+ * @brief Set the policy to use with this IKE_SA.
+ *
+ * @param this calling object
+ * @param policy policy to use
+ */
+ void (*set_policy) (ike_sa_t *this, policy_t *policy);
/**
* @brief Initiate a new connection.
@@ -309,17 +341,6 @@ struct ike_sa_t {
status_t (*delete) (ike_sa_t *this);
/**
- * @brief Retransmits a request.
- *
- * @param this calling object
- * @param message_id ID of the request to retransmit
- * @return
- * - SUCCESS
- * - NOT_FOUND if request doesn't have to be retransmited
- */
- status_t (*retransmit_request) (ike_sa_t *this, u_int32_t message_id);
-
- /**
* @brief Processes a incoming IKEv2-Message.
*
* Message processing may fail. If a critical failure occurs,
@@ -327,7 +348,7 @@ struct ike_sa_t {
* destroy the IKE_SA immediatly, as it is unusable.
*
* @param this calling object
- * @param[in] message message to process
+ * @param message message to process
* @return
* - SUCCESS
* - FAILED
@@ -336,44 +357,33 @@ struct ike_sa_t {
status_t (*process_message) (ike_sa_t *this, message_t *message);
/**
- * @brief Get the next message ID for a request.
- *
+ * @brief Generate a IKE message to send it to the peer.
+ *
+ * This method generates all payloads in the message and encrypts/signs
+ * the packet.
+ *
* @param this calling object
- * @return the next message id
+ * @param message message to generate
+ * @param packet generated output packet
+ * @return
+ * - SUCCESS
+ * - FAILED
+ * - DESTROY_ME if this IKE_SA MUST be deleted
*/
- u_int32_t (*get_next_message_id) (ike_sa_t *this);
+ status_t (*generate_message) (ike_sa_t *this, message_t *message,
+ packet_t **packet);
/**
- * @brief Check if NAT traversal is enabled for this IKE_SA.
- *
- * @param this calling object
- * @return TRUE if NAT traversal enabled
- */
- bool (*is_natt_enabled) (ike_sa_t *this);
-
- /**
- * @brief Enable NAT detection for this IKE_SA.
- *
- * If a Network address translation is detected with
- * NAT_DETECTION notifys, a SA must switch to ports
- * 4500. To enable this behavior, call enable_natt().
- * It is relevant which peer is NATted, this is specified
- * with the "local" parameter. Call it twice when both
- * are NATted.
- *
- * @param this calling object
- * @param local TRUE, if we are NATted, FALSE if other
- */
- void (*enable_natt) (ike_sa_t *this, bool local);
-
- /**
- * @brief Apply connection parameters for this IKE_SA.
+ * @brief Retransmits a request.
*
- * @param this calling object
- * @param connection connection definition
+ * @param this calling object
+ * @param message_id ID of the request to retransmit
+ * @return
+ * - SUCCESS
+ * - NOT_FOUND if request doesn't have to be retransmited
*/
- void (*apply_connection) (ike_sa_t *this, connection_t *connection);
-
+ status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id);
+
/**
* @brief Sends a DPD request to the peer.
*
@@ -399,6 +409,29 @@ struct ike_sa_t {
* @param this calling object
*/
void (*send_keepalive) (ike_sa_t *this);
+
+ /**
+ * @brief Check if NAT traversal is enabled for this IKE_SA.
+ *
+ * @param this calling object
+ * @return TRUE if NAT traversal enabled
+ */
+ bool (*is_natt_enabled) (ike_sa_t *this);
+
+ /**
+ * @brief Enable NAT detection for this IKE_SA.
+ *
+ * If a Network address translation is detected with
+ * NAT_DETECTION notifys, a SA must switch to ports
+ * 4500. To enable this behavior, call enable_natt().
+ * It is relevant which peer is NATted, this is specified
+ * with the "local" parameter. Call it twice when both
+ * are NATted.
+ *
+ * @param this calling object
+ * @param local TRUE, if we are NATted, FALSE if other
+ */
+ void (*enable_natt) (ike_sa_t *this, bool local);
/**
* @brief Derive all keys and create the transforms for IKE communication.
@@ -411,15 +444,14 @@ struct ike_sa_t {
*
* @param this calling object
* @param proposal proposal which contains algorithms to use
- * @param dh diffie hellman object with shared secret
+ * @param secret secret derived from DH exchange, gets freed
* @param nonce_i initiators nonce
* @param nonce_r responders nonce
* @param initiator TRUE if initiator, FALSE otherwise
* @param child_prf PRF with SK_d key when rekeying, NULL otherwise
* @param old_prf general purpose PRF of old SA when rekeying
*/
- status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal,
- diffie_hellman_t *dh,
+ status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret,
chunk_t nonce_i, chunk_t nonce_r,
bool initiator, prf_t *child_prf, prf_t *old_prf);
@@ -464,15 +496,6 @@ struct ike_sa_t {
void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa);
/**
- * @brief Check if an IKE_SA has one or more CHILD_SAs with a given reqid.
- *
- * @param this calling object
- * @param reqid reqid of the CHILD
- * @return TRUE if it has such a CHILD, FALSE if not
- */
- bool (*has_child_sa) (ike_sa_t *this, u_int32_t reqid);
-
- /**
* @brief Get a CHILD_SA identified by protocol and SPI.
*
* @param this calling object
@@ -537,22 +560,6 @@ struct ike_sa_t {
status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi);
/**
- * @brief Set lifetimes of an IKE_SA.
- *
- * Two lifetimes are specified. The soft_lifetime says, when rekeying should
- * be initiated. The hard_lifetime says, when the IKE_SA has been expired
- * and must be deleted. Normally, hard_lifetime > soft_lifetime, and
- * hard_lifetime is only reached when rekeying at soft_lifetime fails.
- *
- * @param this calling object
- * @param reauth use full reauthentication instead of rekeying.
- * @param soft_lifetime soft_lifetime
- * @param hard_lifetime hard_lifetime
- */
- void (*set_lifetimes) (ike_sa_t *this, bool reauth,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime);
-
- /**
* @brief Rekey the IKE_SA.
*
* Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA.
@@ -563,42 +570,60 @@ struct ike_sa_t {
status_t (*rekey) (ike_sa_t *this);
/**
- * @brief Reauthentication the IKE_SA.
+ * @brief Restablish the IKE_SA.
*
* Create a completely new IKE_SA with authentication, recreates all children
- * within the IKE_SA and shuts the old SA down.
+ * within the IKE_SA, but lets the old IKE_SA untouched.
*
* @param this calling object
- * @return - SUCCESS, if IKE_SA rekeying initiated
*/
- status_t (*reauth) (ike_sa_t *this);
-
+ void (*reestablish) (ike_sa_t *this);
+
/**
- * @brief Get the transaction which rekeys this IKE_SA.
+ * @brief Set the virtual IP to use for this IKE_SA and its children.
+ *
+ * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same
+ * lifetime as the IKE_SA.
*
* @param this calling object
- * @return rekey_ike_sa_t transaction or NULL
*/
- transaction_t* (*get_rekeying_transaction) (ike_sa_t *this);
-
+ void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip);
+
/**
- * @brief Set the transaction which rekeys this IKE_SA.
+ * @brief Get the virtual IP configured.
*
* @param this calling object
- * @param rekey rekey_ike_sa_t transaction or NULL
+ * @param local TRUE to get local virtual IP, FALSE for remote
*/
- void (*set_rekeying_transaction) (ike_sa_t *this, transaction_t *rekey);
-
+ host_t* (*get_virtual_ip) (ike_sa_t *this, bool local);
+
+ /**
+ * @brief Add a DNS server to the system.
+ *
+ * An IRAS may send a DNS server. To use it, it is installed on the
+ * system. The DNS entry has a lifetime until the IKE_SA gets closed.
+ *
+ * @param this calling object
+ * @param dns DNS server to install on the system
+ */
+ void (*add_dns_server) (ike_sa_t *this, host_t *dns);
+
/**
- * @brief Move all children from other IKE_SA to this IKE_SA.
+ * @brief Inherit all attributes of other to this after rekeying.
*
- * After rekeying completes, all children are switched over to the
- * newly created IKE_SA.
+ * When rekeying is completed, all CHILD_SAs, the virtual IP and all
+ * outstanding tasks are moved from other to this.
+ *
+ * @param this calling object
+ */
+ void (*inherit) (ike_sa_t *this, ike_sa_t *other);
+
+ /**
+ * @brief Reset the IKE_SA, useable when initiating fails
*
- * @param this stepfather
- * @param other deceased (rekeyed) IKE_SA
+ * @param this calling object
*/
- void (*adopt_children) (ike_sa_t *this, ike_sa_t *other);
+ void (*reset) (ike_sa_t *this);
/**
* @brief Destroys a ike_sa_t object.
@@ -611,8 +636,6 @@ struct ike_sa_t {
/**
* @brief Creates an ike_sa_t object with a specific ID.
*
- * The ID gets cloned internally.
- *
* @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA
* @return ike_sa_t object
*
diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c
index 31972e0bc..6e9d867fd 100644
--- a/src/charon/sa/ike_sa_manager.c
+++ b/src/charon/sa/ike_sa_manager.c
@@ -297,13 +297,134 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this)
}
/**
+ * Implementation of of ike_sa_manager.checkout.
+ */
+static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
+{
+ bool responder_spi_set;
+ bool initiator_spi_set;
+ bool original_initiator;
+ ike_sa_t *ike_sa = NULL;
+
+ DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id);
+
+ DBG2(DBG_MGR, "%d IKE_SAs in manager",
+ this->ike_sa_list->get_count(this->ike_sa_list));
+
+ /* each access is locked */
+ pthread_mutex_lock(&(this->mutex));
+
+ responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id);
+ initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id);
+ original_initiator = ike_sa_id->is_initiator(ike_sa_id);
+
+ if ((initiator_spi_set && responder_spi_set) ||
+ ((initiator_spi_set && !responder_spi_set) && (original_initiator)))
+ {
+ /* we SHOULD have an IKE_SA for these SPIs in the list,
+ * if not, we can't handle the request...
+ */
+ entry_t *entry;
+ /* look for the entry */
+ if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ {
+ if (wait_for_entry(this, entry))
+ {
+ DBG2(DBG_MGR, "IKE_SA successfully checked out");
+ /* ok, this IKE_SA is finally ours */
+ entry->checked_out = TRUE;
+ ike_sa = entry->ike_sa;
+ /* update responder SPI when it's not set */
+ if (entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0)
+ {
+ ike_sa_id_t *ike_sa_ike_sa_id = ike_sa->get_id(ike_sa);
+ u_int64_t spi = ike_sa_id->get_responder_spi(ike_sa_id);
+
+ ike_sa_ike_sa_id->set_responder_spi(ike_sa_ike_sa_id, spi);
+ entry->ike_sa_id->set_responder_spi(entry->ike_sa_id, spi);
+ }
+ }
+ else
+ {
+ DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out");
+ }
+ }
+ else
+ {
+ DBG2(DBG_MGR, "IKE_SA not stored in list");
+ /* looks like there is no such IKE_SA, better luck next time... */
+ }
+ }
+ else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator))
+ {
+ /* an IKE_SA_INIT from an another endpoint,
+ * he is the initiator.
+ * For simplicity, we do NOT check for retransmitted
+ * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
+ * Request (even a retransmitted one) will result in a
+ * IKE_SA. This could be improved...
+ */
+ u_int64_t responder_spi;
+ entry_t *new_entry;
+
+ /* set SPIs, we are the responder */
+ responder_spi = get_next_spi(this);
+
+ /* we also set arguments spi, so its still valid */
+ ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
+
+ /* create entry */
+ new_entry = entry_create(ike_sa_id);
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
+
+ /* check ike_sa out */
+ DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs");
+ new_entry->checked_out = TRUE;
+ ike_sa = new_entry->ike_sa;
+ }
+ else if (!initiator_spi_set && !responder_spi_set)
+ {
+ /* checkout of a new and unused IKE_SA, used for rekeying */
+ entry_t *new_entry;
+
+ if (original_initiator)
+ {
+ ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this));
+ }
+ else
+ {
+ ike_sa_id->set_responder_spi(ike_sa_id, get_next_spi(this));
+ }
+ /* create entry */
+ new_entry = entry_create(ike_sa_id);
+ DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id);
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
+
+ /* check ike_sa out */
+ new_entry->checked_out = TRUE;
+ ike_sa = new_entry->ike_sa;
+ }
+ else
+ {
+ /* responder set, initiator not: here is something seriously wrong! */
+ DBG2(DBG_MGR, "invalid IKE_SA SPIs");
+ }
+
+ pthread_mutex_unlock(&(this->mutex));
+
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
* Implementation of of ike_sa_manager.checkout_by_id.
*/
-static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this,
- host_t *my_host,
- host_t *other_host,
- identification_t *my_id,
- identification_t *other_id)
+static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this,
+ host_t *my_host, host_t *other_host,
+ identification_t *my_id,
+ identification_t *other_id)
{
iterator_t *iterator;
entry_t *entry;
@@ -387,105 +508,53 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this,
}
/**
- * Implementation of of ike_sa_manager.checkout.
+ * Implementation of of ike_sa_manager.checkout_by_id.
*/
-static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
+static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id,
+ bool child)
{
- bool responder_spi_set;
- bool initiator_spi_set;
- bool original_initiator;
+ iterator_t *iterator, *children;
+ entry_t *entry;
ike_sa_t *ike_sa = NULL;
+ child_sa_t *child_sa;
- DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id);
-
- DBG2(DBG_MGR, "%d IKE_SAs in manager",
- this->ike_sa_list->get_count(this->ike_sa_list));
-
- /* each access is locked */
pthread_mutex_lock(&(this->mutex));
- responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id);
- initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id);
- original_initiator = ike_sa_id->is_initiator(ike_sa_id);
-
- if ((initiator_spi_set && responder_spi_set) ||
- ((initiator_spi_set && !responder_spi_set) && (original_initiator)))
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
{
- /* we SHOULD have an IKE_SA for these SPIs in the list,
- * if not, we can't handle the request...
- */
- entry_t *entry;
- /* look for the entry */
- if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ if (wait_for_entry(this, entry))
{
- if (wait_for_entry(this, entry))
+ /* look for a child with such a reqid ... */
+ if (child)
{
- DBG2(DBG_MGR, "IKE_SA successfully checked out");
- /* ok, this IKE_SA is finally ours */
- entry->checked_out = TRUE;
- ike_sa = entry->ike_sa;
+ children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (child_sa->get_reqid(child_sa) == id)
+ {
+ ike_sa = entry->ike_sa;
+ break;
+ }
+ }
+ children->destroy(children);
}
- else
+ else /* ... or for a IKE_SA with such a unique id */
{
- DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out");
+ if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
+ {
+ ike_sa = entry->ike_sa;
+ }
+ }
+ /* got one, return */
+ if (ike_sa)
+ {
+ entry->checked_out = TRUE;
+ break;
}
- }
- else
- {
- DBG2(DBG_MGR, "IKE_SA not stored in list");
- /* looks like there is no such IKE_SA, better luck next time... */
}
}
- else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator))
- {
- /* an IKE_SA_INIT from an another endpoint,
- * he is the initiator.
- * For simplicity, we do NOT check for retransmitted
- * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
- * Request (even a retransmitted one) will result in a
- * IKE_SA. This could be improved...
- */
- u_int64_t responder_spi;
- entry_t *new_entry;
-
- /* set SPIs, we are the responder */
- responder_spi = get_next_spi(this);
-
- /* we also set arguments spi, so its still valid */
- ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
-
- /* create entry */
- new_entry = entry_create(ike_sa_id);
-
- this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
-
- /* check ike_sa out */
- DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs");
- new_entry->checked_out = TRUE;
- ike_sa = new_entry->ike_sa;
- }
- else if (!initiator_spi_set && !responder_spi_set && original_initiator)
- {
- /* checkout of a new and unused IKE_SA, used for rekeying */
- entry_t *new_entry;
-
- ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this));
- /* create entry */
- new_entry = entry_create(ike_sa_id);
- DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id);
-
- this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
-
- /* check ike_sa out */
- new_entry->checked_out = TRUE;
- ike_sa = new_entry->ike_sa;
- }
- else
- {
- /* responder set, initiator not: here is something seriously wrong! */
- DBG2(DBG_MGR, "invalid IKE_SA SPIs");
- }
-
+ iterator->destroy(iterator);
pthread_mutex_unlock(&(this->mutex));
charon->bus->set_sa(charon->bus, ike_sa);
@@ -493,14 +562,15 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id
}
/**
- * Implementation of of ike_sa_manager.checkout_by_child.
+ * Implementation of of ike_sa_manager.checkout_by_name.
*/
-static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
- u_int32_t reqid)
+static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
+ bool child)
{
- iterator_t *iterator;
+ iterator_t *iterator, *children;
entry_t *entry;
ike_sa_t *ike_sa = NULL;
+ child_sa_t *child_sa;
pthread_mutex_lock(&(this->mutex));
@@ -509,12 +579,31 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
{
if (wait_for_entry(this, entry))
{
- /* ok, access is exclusive for us, check for child */
- if (entry->ike_sa->has_child_sa(entry->ike_sa, reqid))
+ /* look for a child with such a policy name ... */
+ if (child)
+ {
+ children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (streq(child_sa->get_name(child_sa), name))
+ {
+ ike_sa = entry->ike_sa;
+ break;
+ }
+ }
+ children->destroy(children);
+ }
+ else /* ... or for a IKE_SA with such a connection name */
+ {
+ if (streq(entry->ike_sa->get_name(entry->ike_sa), name))
+ {
+ ike_sa = entry->ike_sa;
+ }
+ }
+ /* got one, return */
+ if (ike_sa)
{
- /* match */
entry->checked_out = TRUE;
- ike_sa = entry->ike_sa;
break;
}
}
@@ -529,9 +618,16 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
/**
* Iterator hook for iterate, gets ike_sas instead of entries
*/
-static void* iterator_hook(void *value)
+static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in,
+ ike_sa_t **out)
{
- return ((entry_t*)value)->ike_sa;
+ /* check out entry */
+ if (wait_for_entry(this, in))
+ {
+ *out = in->ike_sa;
+ return TRUE;
+ }
+ return FALSE;
}
/**
@@ -540,9 +636,9 @@ static void* iterator_hook(void *value)
static iterator_t *create_iterator(private_ike_sa_manager_t* this)
{
iterator_t *iterator = this->ike_sa_list->create_iterator_locked(
- this->ike_sa_list, &this->mutex);
+ this->ike_sa_list, &this->mutex);
/* register hook to iterator over ike_sas, not entries */
- iterator->set_iterator_hook(iterator, iterator_hook);
+ iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this);
return iterator;
}
@@ -634,170 +730,6 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik
}
/**
- * Implementation of ike_sa_manager_t.delete.
- */
-static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
-{
- /* deletion is a bit complex, we must garant that no thread is waiting for
- * this SA.
- * We take this SA from the list, and start signaling while threads
- * are in the condvar.
- */
- entry_t *entry;
- status_t retval;
-
- DBG2(DBG_MGR, "delete IKE_SA: %J", ike_sa_id);
-
- pthread_mutex_lock(&(this->mutex));
-
- if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
- {
- /* we try a delete. If it succeeds, our job is done here. The
- * other peer will reply, and the IKE SA gets the finally deleted...
- */
- if (entry->ike_sa->delete(entry->ike_sa) == SUCCESS)
- {
- DBG2(DBG_MGR, "initiated delete for IKE_SA");
- }
- /* but if the IKE SA is not in a state where the deletion is
- * negotiated with the other peer, we can destroy the IKE SA on our own.
- */
- else
- {
-
- }
- retval = SUCCESS;
- }
- else
- {
- DBG2(DBG_MGR, "tried to delete nonexisting IKE_SA");
- retval = NOT_FOUND;
- }
-
- pthread_mutex_unlock(&(this->mutex));
- return retval;
-}
-
-/**
- * Implementation of ike_sa_manager_t.delete_by_name.
- */
-static status_t delete_by_name(private_ike_sa_manager_t *this, char *name)
-{
- iterator_t *iterator;
- iterator_t *child_iter;
- entry_t *entry;
- size_t name_len = strlen(name);
-
- pthread_mutex_lock(&(this->mutex));
-
- iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
- while (iterator->iterate(iterator, (void**)&entry))
- {
- if (wait_for_entry(this, entry))
- {
- /* delete ike_sa if:
- * name{x} matches completely
- * name{} matches by name
- * name matches by name
- */
- bool del = FALSE;
- char *ike_name;
- char *child_name;
- child_sa_t *child_sa;
-
- ike_name = entry->ike_sa->get_name(entry->ike_sa);
- /* check if "name{x}" matches completely */
- if (strcmp(name, ike_name) == 0)
- {
- del = TRUE;
- }
- /* check if name is in form of "name{}" and matches to ike_name */
- else if (name_len > 1 &&
- name[name_len - 2] == '{' && name[name_len - 1] == '}' &&
- strlen(ike_name) > name_len &&
- ike_name[name_len - 2] == '{' &&
- strncmp(name, ike_name, name_len - 2) == 0)
- {
- del = TRUE;
- }
- /* finally, check if name is "name" and matches ike_name */
- else if (name_len == strchr(ike_name, '{') - ike_name &&
- strncmp(name, ike_name, name_len) == 0)
- {
- del = TRUE;
- }
-
- if (del)
- {
- if (entry->ike_sa->delete(entry->ike_sa) == DESTROY_ME)
- {
- delete_entry(this, entry);
- iterator->reset(iterator);
- }
- /* no need to check children, as we delete all */
- continue;
- }
-
- /* and now the same game for all children. delete child_sa if:
- * name[x] matches completely
- * name[] matches by name
- * name matches by name
- */
- child_iter = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
- while (child_iter->iterate(child_iter, (void**)&child_sa))
- {
- /* skip ROUTED children, they have their "unroute" command */
- if (child_sa->get_state(child_sa) == CHILD_ROUTED)
- {
- continue;
- }
-
- child_name = child_sa->get_name(child_sa);
- del = FALSE;
- /* check if "name[x]" matches completely */
- if (strcmp(name, child_name) == 0)
- {
- del = TRUE;
- }
- /* check if name is in form of "name[]" and matches to child_name */
- else if (name_len > 1 &&
- name[name_len - 2] == '[' && name[name_len - 1] == ']' &&
- strlen(child_name) > name_len &&
- child_name[name_len - 2] == '[' &&
- strncmp(name, child_name, name_len - 2) == 0)
- {
- del = TRUE;
- }
- /* finally, check if name is "name" and matches child_name */
- else if (name_len == strchr(child_name, '[') - child_name &&
- strncmp(name, child_name, name_len) == 0)
- {
- del = TRUE;
- }
- if (del)
- {
- if (entry->ike_sa->delete_child_sa(entry->ike_sa,
- child_sa->get_protocol(child_sa),
- child_sa->get_spi(child_sa, TRUE)) == DESTROY_ME)
- {
- /* when a fatal error occurs, we are responsible to
- * remove the IKE_SA */
- delete_entry(this, entry);
- iterator->reset(iterator);
- break;
- }
- }
- }
- child_iter->destroy(child_iter);
- }
- }
- iterator->destroy(iterator);
- pthread_mutex_unlock(&(this->mutex));
-
- return SUCCESS;
-}
-
-/**
* Implementation of ike_sa_manager_t.destroy.
*/
static void destroy(private_ike_sa_manager_t *this)
@@ -859,19 +791,18 @@ ike_sa_manager_t *ike_sa_manager_create()
/* assign public functions */
this->public.destroy = (void(*)(ike_sa_manager_t*))destroy;
- this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_id;
this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout;
- this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child;
+ this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer;
+ this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id;
+ this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name;
this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator;
this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
- this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete_;
- this->public.delete_by_name = (status_t(*)(ike_sa_manager_t*,char*))delete_by_name;
this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy;
/* initialize private variables */
this->ike_sa_list = linked_list_create();
- pthread_mutex_init(&(this->mutex), NULL);
+ pthread_mutex_init(&this->mutex, NULL);
this->randomizer = randomizer_create();
- return (ike_sa_manager_t*)this;
+ return &this->public;
}
diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h
index 671062c4e..ecd3db156 100644
--- a/src/charon/sa/ike_sa_manager.h
+++ b/src/charon/sa/ike_sa_manager.h
@@ -59,7 +59,7 @@ struct ike_sa_manager_t {
* result in a deadlock!
*
* @param this the manager object
- * @param[in/out] ike_sa_id the SA identifier, will be updated
+ * @param ike_sa_id the SA identifier, will be updated
* @returns
* - checked out IKE_SA if found
* - NULL, if no such IKE_SA available
@@ -82,25 +82,44 @@ struct ike_sa_manager_t {
* @param other_id ID used by remote
* @return checked out/created IKE_SA
*/
- ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this,
- host_t *my_host, host_t* other_host,
- identification_t *my_id,
- identification_t *other_id);
+ ike_sa_t* (*checkout_by_peer) (ike_sa_manager_t* this,
+ host_t *my_host, host_t* other_host,
+ identification_t *my_id,
+ identification_t *other_id);
/**
- * @brief Check out an IKE_SA by protocol and SPI of one of its CHILD_SA.
+ * @brief Check out an IKE_SA a unique ID.
*
- * The kernel sends us expire messages for IPsec SAs. To fullfill
- * this request, we must check out the IKE SA which contains the
- * CHILD_SA the kernel wants to modify.
+ * Every IKE_SA and every CHILD_SA is uniquely identified by an ID.
+ * These checkout function uses, depending
+ * on the child parameter, the unique ID of the IKE_SA or the reqid
+ * of one of a IKE_SAs CHILD_SA.
*
* @param this the manager object
- * @param reqid reqid of the CHILD_SA
+ * @param id unique ID of the object
+ * @param child TRUE to use CHILD, FALSE to use IKE_SA
* @return
* - checked out IKE_SA, if found
* - NULL, if not found
*/
- ike_sa_t* (*checkout_by_child) (ike_sa_manager_t* this, u_int32_t reqid);
+ ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id,
+ bool child);
+
+ /**
+ * @brief Check out an IKE_SA by the policy/connection name.
+ *
+ * Check out the IKE_SA by the connections name or by a CHILD_SAs policy
+ * name.
+ *
+ * @param this the manager object
+ * @param name name of the connection/policy
+ * @param child TRUE to use policy name, FALSE to use conn name
+ * @return
+ * - checked out IKE_SA, if found
+ * - NULL, if not found
+ */
+ ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name,
+ bool child);
/**
* @brief Create an iterator over all stored IKE_SAs.
@@ -121,8 +140,8 @@ struct ike_sa_manager_t {
* The SA must be checked out again!
*
* @param this the manager object
- * @param[in/out] ike_sa_id the SA identifier, will be updated
- * @param[out] ike_sa checked out SA
+ * @param ike_sa_id the SA identifier, will be updated
+ * @param ike_sa checked out SA
* @returns
* - SUCCESS if checked in
* - NOT_FOUND when not found (shouldn't happen!)
@@ -130,47 +149,6 @@ struct ike_sa_manager_t {
status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
/**
- * @brief Delete a SA, which was not checked out.
- *
- * If the state allows it, the IKE SA is destroyed immediately. If it is
- * in the state ESTABLSIHED, a delete message
- * is sent to the remote peer, which has to be acknowledged.
- *
- * @warning do not use this when the SA is already checked out, this will
- * deadlock!
- *
- * @param this the manager object
- * @param[in/out] ike_sa_id the SA identifier
- * @returns
- * - SUCCESS if found
- * - NOT_FOUND when no such SA is available
- */
- status_t (*delete) (ike_sa_manager_t* this, ike_sa_id_t *ike_sa_id);
-
- /**
- * @brief Delete a SA identified by its name, which was not checked out.
- *
- * Using delete_by_name allows the delete of IKE_SAs and CHILD_SAs.
- * The supplied name may have one of the following format:
- *
- * name{x} => delete IKE_SA with "name" and unique id "x"
- * name{} => delete all IKE_SAs with "name"
- * name[x] => delete CHILD_SA with "name" and unique id "x"
- * name[] => delete all CHILD_SAs with "name"
- * name => delete all CHILD_SAs or IKE_SAs with "name"
- *
- * @warning do not use this when the SA is already checked out, this will
- * deadlock!
- *
- * @param this the manager object
- * @param name name in one of the format described above
- * @returns
- * - SUCCESS if found
- * - NOT_FOUND when no such SA is available
- */
- status_t (*delete_by_name) (ike_sa_manager_t* this, char *name);
-
- /**
* @brief Destroy a checked out SA.
*
* The IKE SA is destroyed without notification of the remote peer.
diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c
new file mode 100644
index 000000000..061b0239a
--- /dev/null
+++ b/src/charon/sa/task_manager.c
@@ -0,0 +1,780 @@
+/**
+ * @file task_manager.c
+ *
+ * @brief Implementation of task_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "task_manager.h"
+
+#include <daemon.h>
+#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_natd.h>
+#include <sa/tasks/ike_auth.h>
+#include <sa/tasks/ike_cert.h>
+#include <sa/tasks/ike_rekey.h>
+#include <sa/tasks/ike_delete.h>
+#include <sa/tasks/ike_config.h>
+#include <sa/tasks/ike_dpd.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_rekey.h>
+#include <sa/tasks/child_delete.h>
+#include <encoding/payloads/delete_payload.h>
+#include <queues/jobs/retransmit_job.h>
+
+typedef struct exchange_t exchange_t;
+
+/**
+ * An exchange in the air, used do detect and handle retransmission
+ */
+struct exchange_t {
+
+ /**
+ * Message ID used for this transaction
+ */
+ u_int32_t mid;
+
+ /**
+ * generated packet for retransmission
+ */
+ packet_t *packet;
+};
+
+typedef struct private_task_manager_t private_task_manager_t;
+
+/**
+ * private data of the task manager
+ */
+struct private_task_manager_t {
+
+ /**
+ * public functions
+ */
+ task_manager_t public;
+
+ /**
+ * associated IKE_SA we are serving
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Exchange we are currently handling as responder
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ } responding;
+
+ /**
+ * Exchange we are currently handling as initiator
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * how many times we have retransmitted so far
+ */
+ u_int retransmitted;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ /**
+ * type of the initated exchange
+ */
+ exchange_type_t type;
+
+ } initiating;
+
+ /**
+ * List of queued tasks not yet in action
+ */
+ linked_list_t *queued_tasks;
+
+ /**
+ * List of active tasks, initiated by ourselve
+ */
+ linked_list_t *active_tasks;
+
+ /**
+ * List of tasks initiated by peer
+ */
+ linked_list_t *passive_tasks;
+
+ /**
+ * ike_sa_init message we sent, stored here for later authentication
+ */
+ packet_t *ike_sa_init;
+};
+
+/**
+ * move a task of a specific type from the queue to the active list
+ */
+static bool activate_task(private_task_manager_t *this, task_type_t type)
+{
+ iterator_t *iterator;
+ task_t *task;
+ bool found = FALSE;
+
+ iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ if (task->get_type(task) == type)
+ {
+ DBG2(DBG_IKE, " activating %N task", task_type_names, type);
+ iterator->remove(iterator);
+ this->active_tasks->insert_last(this->active_tasks, task);
+ found = TRUE;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return found;
+}
+
+/**
+ * Implementation of task_manager_t.retransmit
+ */
+static status_t retransmit(private_task_manager_t *this, u_int32_t message_id)
+{
+ if (message_id == this->initiating.mid)
+ {
+ u_int32_t timeout;
+ job_t *job;
+
+ timeout = charon->configuration->get_retransmit_timeout(
+ charon->configuration, this->initiating.retransmitted);
+ if (timeout == 0)
+ {
+ DBG1(DBG_IKE, "giving up after %d retransmits",
+ this->initiating.retransmitted - 1);
+ return DESTROY_ME;
+ }
+
+ if (this->initiating.retransmitted)
+ {
+ DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
+ this->initiating.retransmitted, message_id);
+ }
+ this->initiating.retransmitted++;
+
+ charon->send_queue->add(charon->send_queue,
+ this->initiating.packet->clone(this->initiating.packet));
+ job = (job_t*)retransmit_job_create(this->initiating.mid,
+ this->ike_sa->get_id(this->ike_sa));
+ charon->event_queue->add_relative(charon->event_queue, job, timeout);
+ }
+ return SUCCESS;
+}
+
+/**
+ * build a request using the active task list
+ * Implementation of task_manager_t.initiate
+ */
+static status_t build_request(private_task_manager_t *this)
+{
+ iterator_t *iterator;
+ task_t *task;
+ message_t *message;
+ status_t status;
+ exchange_type_t exchange = 0;
+
+ if (this->active_tasks->get_count(this->active_tasks) == 0)
+ {
+ DBG2(DBG_IKE, "activating new tasks");
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_CREATED:
+ if (activate_task(this, IKE_INIT))
+ {
+ exchange = IKE_SA_INIT;
+ activate_task(this, IKE_NATD);
+ activate_task(this, IKE_CERT);
+ activate_task(this, IKE_AUTHENTICATE);
+ activate_task(this, IKE_CONFIG);
+ activate_task(this, CHILD_CREATE);
+ }
+ break;
+ case IKE_ESTABLISHED:
+ if (activate_task(this, CHILD_CREATE))
+ {
+ exchange = CREATE_CHILD_SA;
+ activate_task(this, IKE_CONFIG);
+ break;
+ }
+ if (activate_task(this, CHILD_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, CHILD_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, IKE_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, IKE_DEADPEER))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_REKEYING:
+ if (activate_task(this, IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_DELETING:
+ default:
+ break;
+ }
+ }
+ else
+ {
+ DBG2(DBG_IKE, "reinitiating already active tasks");
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task));
+ switch (task->get_type(task))
+ {
+ case IKE_INIT:
+ exchange = IKE_SA_INIT;
+ break;
+ case IKE_AUTHENTICATE:
+ exchange = IKE_AUTH;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ iterator->destroy(iterator);
+ }
+
+ if (exchange == 0)
+ {
+ DBG2(DBG_IKE, "nothing to initiate");
+ /* nothing to do yet... */
+ return SUCCESS;
+ }
+
+ message = message_create();
+ message->set_message_id(message, this->initiating.mid);
+ message->set_exchange_type(message, exchange);
+ this->initiating.type = exchange;
+ this->initiating.retransmitted = 0;
+
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ message->destroy(message);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ DESTROY_IF(this->initiating.packet);
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->initiating.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ /* message generation failed. There is nothing more to do than to
+ * close the SA */
+ return DESTROY_ME;
+ }
+
+ return retransmit(this, this->initiating.mid);
+}
+
+/**
+ * handle an incoming response message
+ */
+static status_t process_response(private_task_manager_t *this,
+ message_t *message)
+{
+ iterator_t *iterator;
+ task_t *task;
+
+ if (message->get_exchange_type(message) != this->initiating.type)
+ {
+ DBG1(DBG_IKE, "received %N response, but expected %N",
+ exchange_type_names, message->get_exchange_type(message),
+ exchange_type_names, this->initiating.type);
+ return DESTROY_ME;
+ }
+
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ this->initiating.mid++;
+
+ return build_request(this);
+}
+
+/**
+ * build a response depending on the "passive" task list
+ */
+static status_t build_response(private_task_manager_t *this,
+ exchange_type_t exchange)
+{
+ iterator_t *iterator;
+ task_t *task;
+ message_t *message;
+ bool delete = FALSE;
+ status_t status;
+
+ message = message_create();
+ message->set_exchange_type(message, exchange);
+ message->set_message_id(message, this->responding.mid);
+ message->set_request(message, FALSE);
+
+ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* destroy IKE_SA, but SEND response first */
+ delete = TRUE;
+ break;
+ }
+ if (delete)
+ {
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ /* message complete, send it */
+ DESTROY_IF(this->responding.packet);
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->responding.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+
+ charon->send_queue->add(charon->send_queue,
+ this->responding.packet->clone(this->responding.packet));
+ if (delete)
+ {
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+}
+
+/**
+ * handle an incoming request message
+ */
+static status_t process_request(private_task_manager_t *this,
+ message_t *message)
+{
+ iterator_t *iterator;
+ task_t *task = NULL;
+ exchange_type_t exchange;
+ payload_t *payload;
+ notify_payload_t *notify;
+
+ exchange = message->get_exchange_type(message);
+
+ /* create tasks depending on request type */
+ switch (exchange)
+ {
+ case IKE_SA_INIT:
+ {
+ task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_cert_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_config_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)child_create_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ case CREATE_CHILD_SA:
+ {
+ bool notify_found = FALSE, ts_found = FALSE;
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY:
+ {
+ /* if we find a rekey notify, its CHILD_SA rekeying */
+ notify = (notify_payload_t*)payload;
+ if (notify->get_notify_type(notify) == REKEY_SA &&
+ (notify->get_protocol_id(notify) == PROTO_AH ||
+ notify->get_protocol_id(notify) == PROTO_ESP))
+ {
+ notify_found = TRUE;
+ }
+ break;
+ }
+ case TRAFFIC_SELECTOR_INITIATOR:
+ case TRAFFIC_SELECTOR_RESPONDER:
+ {
+ /* if we don't find a TS, its IKE rekeying */
+ ts_found = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (ts_found)
+ {
+ if (notify_found)
+ {
+ task = (task_t*)child_rekey_create(this->ike_sa, NULL);
+ }
+ else
+ {
+ task = (task_t*)child_create_create(this->ike_sa, NULL);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
+ }
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+
+ break;
+ }
+ case INFORMATIONAL:
+ {
+ delete_payload_t *delete;
+
+ delete = (delete_payload_t*)message->get_payload(message, DELETE);
+ if (delete)
+ {
+ if (delete->get_protocol_id(delete) == PROTO_IKE)
+ {
+ task = (task_t*)ike_delete_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ else
+ {
+ task = (task_t*)child_delete_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_dpd_create(FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* let the tasks process the message */
+ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs at least another call to build() */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ return build_response(this, exchange);
+}
+
+/**
+ * Implementation of task_manager_t.process_message
+ */
+static status_t process_message(private_task_manager_t *this, message_t *msg)
+{
+ u_int32_t mid = msg->get_message_id(msg);
+
+ if (msg->get_request(msg))
+ {
+ if (mid == this->responding.mid)
+ {
+ if (process_request(this, msg) != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+ this->responding.mid++;
+ }
+ else if ((mid == this->responding.mid - 1) && this->responding.packet)
+ {
+ DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+ "retransmitting response", mid);
+ charon->send_queue->add(charon->send_queue,
+ this->responding.packet->clone(
+ this->responding.packet));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored",
+ mid, this->responding.mid);
+ }
+ }
+ else
+ {
+ if (mid == this->initiating.mid)
+ {
+ if (process_response(this, msg) != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored",
+ mid, this->initiating.mid);
+ return SUCCESS;
+ }
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_manager_t.queue_task
+ */
+static void queue_task(private_task_manager_t *this, task_t *task)
+{
+ DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+ this->queued_tasks->insert_last(this->queued_tasks, task);
+}
+
+/**
+ * Implementation of task_manager_t.adopt_tasks
+ */
+static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other)
+{
+ task_t *task;
+
+ /* move queued tasks from other to this */
+ while (other->queued_tasks->remove_last(other->queued_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+
+ /* reset active tasks and move them to others queued tasks */
+ while (other->active_tasks->remove_last(other->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+/**
+ * Implementation of task_manager_t.busy
+ */
+static bool busy(private_task_manager_t *this)
+{
+ return (this->active_tasks->get_count(this->active_tasks) > 0);
+}
+
+/**
+ * Implementation of task_manager_t.reset
+ */
+static void reset(private_task_manager_t *this)
+{
+ task_t *task;
+
+ /* reset message counters and retransmit packets */
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ DESTROY_IF(this->ike_sa_init);
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ this->ike_sa_init = NULL;
+ this->responding.mid = 0;
+ this->initiating.mid = -1;
+
+ /* reset active tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+/**
+ * Implementation of task_manager_t.destroy
+ */
+static void destroy(private_task_manager_t *this)
+{
+ task_t *task;
+
+ this->queued_tasks->destroy_offset(this->queued_tasks,
+ offsetof(task_t, destroy));
+ this->passive_tasks->destroy_offset(this->passive_tasks,
+ offsetof(task_t, destroy));
+
+ /* emmit outstanding signals for tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ switch (task->get_type(task))
+ {
+ case IKE_AUTH:
+ SIG(IKE_UP_FAILED, "establishing IKE_SA failed");
+ break;
+ case IKE_DELETE:
+ SIG(IKE_DOWN_FAILED, "deleteing IKE_SA properly failed");
+ break;
+ case IKE_REKEY:
+ SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed");
+ break;
+ case CHILD_CREATE:
+ SIG(CHILD_UP_FAILED, "establishing CHILD_SA failed");
+ break;
+ case CHILD_DELETE:
+ SIG(CHILD_DOWN_FAILED, "deleting CHILD_SA failed");
+ break;
+ case CHILD_REKEY:
+ SIG(IKE_REKEY_FAILED, "rekeying CHILD_SA failed");
+ break;
+ default:
+ break;
+ }
+ task->destroy(task);
+ }
+ this->active_tasks->destroy(this->active_tasks);
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ DESTROY_IF(this->ike_sa_init);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+task_manager_t *task_manager_create(ike_sa_t *ike_sa)
+{
+ private_task_manager_t *this = malloc_thing(private_task_manager_t);
+
+ this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message;
+ this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task;
+ this->public.initiate = (status_t(*)(task_manager_t*))build_request;
+ this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit;
+ this->public.reset = (void(*)(task_manager_t*))reset;
+ this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks;
+ this->public.busy = (bool(*)(task_manager_t*))busy;
+ this->public.destroy = (void(*)(task_manager_t*))destroy;
+
+ this->ike_sa = ike_sa;
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ this->responding.mid = 0;
+ this->initiating.mid = 0;
+ this->queued_tasks = linked_list_create();
+ this->active_tasks = linked_list_create();
+ this->passive_tasks = linked_list_create();
+ this->ike_sa_init = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h
new file mode 100644
index 000000000..c766d4a65
--- /dev/null
+++ b/src/charon/sa/task_manager.h
@@ -0,0 +1,144 @@
+/**
+ * @file task_manager.h
+ *
+ * @brief Interface of task_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef TASK_MANAGER_H_
+#define TASK_MANAGER_H_
+
+typedef struct task_manager_t task_manager_t;
+
+#include <library.h>
+#include <encoding/message.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief The task manager, juggles task and handles message exchanges.
+ *
+ * On incoming requests, the task manager creates new tasks on demand and
+ * juggles the request through all available tasks. Each task inspects the
+ * request and adds payloads as necessary to the response.
+ * On outgoing requests, the task manager delivers the request through the tasks
+ * to build it, the response gets processed by each task to complete.
+ * The task manager has an internal Queue to store task which should get
+ * completed.
+ * For the initial IKE_SA setup, several tasks are queued: One for the
+ * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup
+ * and maybe one for virtual IP assignement.
+ *
+ * @b Constructors:
+ * - task_manager_create()
+ *
+ * @ingroup sa
+ */
+struct task_manager_t {
+
+ /**
+ * @brief Process an incoming message.
+ *
+ * @param this calling object
+ * @param message message to add payloads to
+ * @return
+ * - DESTROY_ME if IKE_SA must be closed
+ * - SUCCESS otherwise
+ */
+ status_t (*process_message) (task_manager_t *this, message_t *message);
+
+ /**
+ * @brief Initiate an exchange with the currently queued tasks.
+ *
+ * @param this calling object
+ */
+ status_t (*initiate) (task_manager_t *this);
+
+ /**
+ * @brief Queue a task in the manager.
+ *
+ * @param this calling object
+ * @param task task to queue
+ */
+ void (*queue_task) (task_manager_t *this, task_t *task);
+
+ /**
+ * @brief Retransmit a request if it hasn't been acknowledged yet.
+ *
+ * A return value of INVALID_STATE means that the message was already
+ * acknowledged and has not to be retransmitted. A return value of SUCCESS
+ * means retransmission was required and the message has been resent.
+ *
+ * @param this calling object
+ * @param message_id ID of the message to retransmit
+ * @return
+ * - INVALID_STATE if retransmission not required
+ * - SUCCESS if retransmission sent
+ */
+ status_t (*retransmit) (task_manager_t *this, u_int32_t message_id);
+
+ /**
+ * @brief Migrate all tasks from other to this.
+ *
+ * To rekey or reestablish an IKE_SA completely, all queued or active
+ * tasks should get migrated to the new IKE_SA.
+ *
+ * @param this manager which gets all tasks
+ * @param other manager which gives away its tasks
+ */
+ void (*adopt_tasks) (task_manager_t *this, task_manager_t *other);
+
+ /**
+ * @brief Reset message ID counters of the task manager.
+ *
+ * The IKEv2 protocol requires to restart exchanges with message IDs
+ * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method
+ * resets the message IDs and resets all active tasks using the migrate()
+ * method.
+ *
+ * @param this calling object
+ * @param other manager which gives away its tasks
+ */
+ void (*reset) (task_manager_t *this);
+
+ /**
+ * @brief Check if we are currently waiting for a reply.
+ *
+ * @param this calling object
+ * @return TRUE if we are waiting, FALSE otherwise
+ */
+ bool (*busy) (task_manager_t *this);
+
+ /**
+ * @brief Destroy the task_manager_t.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (task_manager_t *this);
+};
+
+/**
+ * @brief Create an instance of the task manager.
+ *
+ * @param ike_sa IKE_SA to manage.
+ *
+ * @ingroup sa
+ */
+task_manager_t *task_manager_create(ike_sa_t *ike_sa);
+
+#endif /* TASK_MANAGER_H_ */
diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c
new file mode 100644
index 000000000..165baa4be
--- /dev/null
+++ b/src/charon/sa/tasks/child_create.c
@@ -0,0 +1,718 @@
+/**
+ * @file child_create.c
+ *
+ * @brief Implementation of the child_create task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_create.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+
+
+typedef struct private_child_create_t private_child_create_t;
+
+/**
+ * Private members of a child_create_t task.
+ */
+struct private_child_create_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_create_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * nonce chosen by us
+ */
+ chunk_t my_nonce;
+
+ /**
+ * nonce chosen by peer
+ */
+ chunk_t other_nonce;
+
+ /**
+ * policy to create the CHILD_SA from
+ */
+ policy_t *policy;
+
+ /**
+ * list of proposal candidates
+ */
+ linked_list_t *proposals;
+
+ /**
+ * selected proposal to use for CHILD_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * traffic selectors for initiators side
+ */
+ linked_list_t *tsi;
+
+ /**
+ * traffic selectors for responders side
+ */
+ linked_list_t *tsr;
+
+ /**
+ * mode the new CHILD_SA uses (transport/tunnel/beet)
+ */
+ mode_t mode;
+
+ /**
+ * reqid to use if we are rekeying
+ */
+ u_int32_t reqid;
+
+ /**
+ * CHILD_SA which gets established
+ */
+ child_sa_t *child_sa;
+};
+
+/**
+ * get the nonce from a message
+ */
+static status_t get_nonce(message_t *message, chunk_t *nonce)
+{
+ nonce_payload_t *payload;
+
+ payload = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (payload == NULL)
+ {
+ return FAILED;
+ }
+ *nonce = payload->get_nonce(payload);
+ return NEED_MORE;
+}
+
+/**
+ * generate a new nonce to include in a CREATE_CHILD_SA message
+ */
+static status_t generate_nonce(chunk_t *nonce)
+{
+ status_t status;
+ randomizer_t *randomizer = randomizer_create();
+
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ nonce);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "error generating random nonce value");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Check a list of traffic selectors if any selector belongs to host
+ */
+static bool ts_list_is_host(linked_list_t *list, host_t *host)
+{
+ traffic_selector_t *ts;
+ bool is_host = TRUE;
+ iterator_t *iterator = list->create_iterator(list, TRUE);
+
+ while (is_host && iterator->iterate(iterator, (void**)&ts))
+ {
+ is_host = is_host && ts->is_host(ts, host);
+ }
+ iterator->destroy(iterator);
+ return is_host;
+}
+
+/**
+ * Install a CHILD_SA for usage
+ */
+static status_t select_and_install(private_child_create_t *this)
+{
+ prf_plus_t *prf_plus;
+ status_t status;
+ chunk_t nonce_i, nonce_r, seed;
+ linked_list_t *my_ts, *other_ts;
+ host_t *me, *other, *other_vip, *my_vip;
+
+ if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message");
+ return FAILED;
+ }
+
+ if (this->initiator)
+ {
+ nonce_i = this->my_nonce;
+ nonce_r = this->other_nonce;
+ my_ts = this->tsi;
+ other_ts = this->tsr;
+ }
+ else
+ {
+ nonce_r = this->my_nonce;
+ nonce_i = this->other_nonce;
+ my_ts = this->tsr;
+ other_ts = this->tsi;
+ }
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
+ other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
+
+ this->proposal = this->policy->select_proposal(this->policy, this->proposals);
+
+ if (this->initiator && my_vip)
+ { /* if we have a virtual IP, shorten our TS to the minimum */
+ my_ts = this->policy->select_my_traffic_selectors(this->policy, my_ts,
+ my_vip);
+ }
+ else
+ { /* shorten in the host2host case only */
+ my_ts = this->policy->select_my_traffic_selectors(this->policy,
+ my_ts, me);
+ }
+ if (other_vip)
+ { /* if other has a virtual IP, shorten it's traffic selectors to it */
+ other_ts = this->policy->select_other_traffic_selectors(this->policy,
+ other_ts, other_vip);
+ }
+ else
+ { /* use his host for the host2host case */
+ other_ts = this->policy->select_other_traffic_selectors(this->policy,
+ other_ts, other);
+ }
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ if (this->initiator)
+ {
+ this->tsi = my_ts;
+ this->tsr = other_ts;
+ }
+ else
+ {
+ this->tsr = my_ts;
+ this->tsi = other_ts;
+ }
+
+ if (this->proposal == NULL ||
+ this->tsi->get_count(this->tsi) == 0 ||
+ this->tsr->get_count(this->tsr) == 0)
+ {
+ SIG(CHILD_UP_FAILED, "no acceptable proposal found");
+ return FAILED;
+ }
+
+ if (!this->initiator)
+ {
+ /* check if requested mode is acceptable, downgrade if required */
+ switch (this->mode)
+ {
+ case MODE_TRANSPORT:
+ if (!ts_list_is_host(this->tsi, other) ||
+ !ts_list_is_host(this->tsr, me))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
+ }
+ else if (this->ike_sa->is_natt_enabled(this->ike_sa))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using tranport mode, connection NATed");
+ }
+ break;
+ case MODE_BEET:
+ if (!ts_list_is_host(this->tsi, NULL) ||
+ !ts_list_is_host(this->tsr, NULL))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ seed = chunk_cata("cc", nonce_i, nonce_r);
+ prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
+
+ if (this->initiator)
+ {
+ status = this->child_sa->update(this->child_sa, this->proposal,
+ this->mode, prf_plus);
+ }
+ else
+ {
+ status = this->child_sa->add(this->child_sa, this->proposal,
+ this->mode, prf_plus);
+ }
+ prf_plus->destroy(prf_plus);
+
+ if (status != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel");
+ return status;
+ }
+
+ status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
+ this->mode);
+
+ if (status != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel");
+ return status;
+ }
+ /* add to IKE_SA, and remove from task */
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+ this->child_sa = NULL;
+ return SUCCESS;
+}
+
+/**
+ * build the payloads for the message
+ */
+static void build_payloads(private_child_create_t *this, message_t *message)
+{
+ sa_payload_t *sa_payload;
+ ts_payload_t *ts_payload;
+ nonce_payload_t *nonce_payload;
+
+ /* add SA payload */
+ if (this->initiator)
+ {
+ sa_payload = sa_payload_create_from_proposal_list(this->proposals);
+ }
+ else
+ {
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ /* add TSi/TSr payloads */
+ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
+ message->add_payload(message, (payload_t*)ts_payload);
+ ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
+ message->add_payload(message, (payload_t*)ts_payload);
+
+ /* add nonce payload if not in IKE_AUTH */
+ if (message->get_exchange_type(message) == CREATE_CHILD_SA)
+ {
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+ message->add_payload(message, (payload_t*)nonce_payload);
+ }
+
+ /* add a notify if we are not in tunnel mode */
+ switch (this->mode)
+ {
+ case MODE_TRANSPORT:
+ message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty);
+ break;
+ case MODE_BEET:
+ message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Read payloads from message
+ */
+static void process_payloads(private_child_create_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ sa_payload_t *sa_payload;
+ ts_payload_t *ts_payload;
+ notify_payload_t *notify_payload;
+
+ /* defaults to TUNNEL mode */
+ this->mode = MODE_TUNNEL;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ sa_payload = (sa_payload_t*)payload;
+ this->proposals = sa_payload->get_proposals(sa_payload);
+ break;
+ case TRAFFIC_SELECTOR_INITIATOR:
+ ts_payload = (ts_payload_t*)payload;
+ this->tsi = ts_payload->get_traffic_selectors(ts_payload);
+ break;
+ case TRAFFIC_SELECTOR_RESPONDER:
+ ts_payload = (ts_payload_t*)payload;
+ this->tsr = ts_payload->get_traffic_selectors(ts_payload);
+ break;
+ case NOTIFY:
+ notify_payload = (notify_payload_t*)payload;
+ switch (notify_payload ->get_notify_type(notify_payload ))
+ {
+ case USE_TRANSPORT_MODE:
+ this->mode = MODE_TRANSPORT;
+ break;
+ case USE_BEET_MODE:
+ this->mode = MODE_BEET;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_create_t *this, message_t *message)
+{
+ host_t *me, *other, *vip;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (generate_nonce(&this->my_nonce) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ SIG(CHILD_UP_START, "establishing CHILD_SA");
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ vip = this->policy->get_virtual_ip(this->policy, NULL);
+
+ if (vip)
+ { /* propose a 0.0.0.0/0 subnet when we use virtual ip */
+ this->tsi = this->policy->get_my_traffic_selectors(this->policy, NULL);
+ vip->destroy(vip);
+ }
+ else
+ { /* but shorten a 0.0.0.0/0 subnet to the actual address if host2host */
+ this->tsi = this->policy->get_my_traffic_selectors(this->policy, me);
+ }
+ this->tsr = this->policy->get_other_traffic_selectors(this->policy, other);
+ this->proposals = this->policy->get_proposals(this->policy);
+ this->mode = this->policy->get_mode(this->policy);
+
+ this->child_sa = child_sa_create(me, other,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->policy, this->reqid,
+ this->ike_sa->is_natt_enabled(this->ike_sa));
+
+ if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to allocate SPIs from kernel");
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->proposals = NULL;
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_create_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->other_nonce);
+ case CREATE_CHILD_SA:
+ get_nonce(message, &this->other_nonce);
+ break;
+ default:
+ break;
+ }
+
+ process_payloads(this, message);
+
+ if (this->tsi == NULL || this->tsr == NULL)
+ {
+ DBG1(DBG_IKE, "TS payload missing in message");
+ return NEED_MORE;
+ }
+
+ this->policy = charon->policies->get_policy(charon->policies,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->tsr, this->tsi,
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa));
+
+ if (this->policy && this->ike_sa->get_policy(this->ike_sa) == NULL)
+ {
+ this->ike_sa->set_policy(this->ike_sa, this->policy);
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_create_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (generate_nonce(&this->my_nonce) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (this->policy == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "received traffic selectors inacceptable");
+ message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
+ return SUCCESS;
+ }
+
+ this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->policy, this->reqid,
+ this->ike_sa->is_natt_enabled(this->ike_sa));
+
+ if (select_and_install(this) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ build_payloads(this, message);
+
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_create_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ status_t status;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->other_nonce);
+ case CREATE_CHILD_SA:
+ get_nonce(message, &this->other_nonce);
+ break;
+ default:
+ break;
+ }
+
+ /* check for erronous notifies */
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ if (type < 16383)
+ {
+ SIG(CHILD_UP_FAILED, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ /* an error in CHILD_SA creation is not critical */
+ return SUCCESS;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ status = select_and_install(this);
+
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_create_t *this)
+{
+ return CHILD_CREATE;
+}
+
+/**
+ * Implementation of child_create_t.use_reqid
+ */
+static void use_reqid(private_child_create_t *this, u_int32_t reqid)
+{
+ this->reqid = reqid;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ if (this->tsi)
+ {
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ }
+ if (this->tsr)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ }
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->proposal);
+ if (this->proposals)
+ {
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ }
+
+ this->ike_sa = ike_sa;
+ this->proposals = NULL;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->child_sa = NULL;
+ this->mode = MODE_TUNNEL;
+ this->reqid = 0;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_create_t *this)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ if (this->tsi)
+ {
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ }
+ if (this->tsr)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ }
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->proposal);
+ if (this->proposals)
+ {
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ }
+
+ DESTROY_IF(this->policy);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy)
+{
+ private_child_create_t *this = malloc_thing(private_child_create_t);
+
+ this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (policy)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ policy->get_ref(policy);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+
+ this->ike_sa = ike_sa;
+ this->policy = policy;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->proposals = NULL;
+ this->proposal = NULL;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->child_sa = NULL;
+ this->mode = MODE_TUNNEL;
+ this->reqid = 0;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h
new file mode 100644
index 000000000..1644865b9
--- /dev/null
+++ b/src/charon/sa/tasks/child_create.h
@@ -0,0 +1,72 @@
+/**
+ * @file child_create.h
+ *
+ * @brief Interface child_create_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CHILD_CREATE_H_
+#define CHILD_CREATE_H_
+
+typedef struct child_create_t child_create_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <config/policies/policy.h>
+
+/**
+ * @brief Task of type CHILD_CREATE, established a new CHILD_SA.
+ *
+ * This task may be included in the IKE_AUTH message or in a separate
+ * CREATE_CHILD_SA exchange.
+ *
+ * @b Constructors:
+ * - child_create_create()
+ *
+ * @ingroup tasks
+ */
+struct child_create_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Use a specific reqid for the CHILD_SA.
+ *
+ * When this task is used for rekeying, the same reqid is used
+ * for the new CHILD_SA.
+ *
+ * @param this calling object
+ * @param reqid reqid to use
+ */
+ void (*use_reqid) (child_create_t *this, u_int32_t reqid);
+};
+
+/**
+ * @brief Create a new child_create task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param policy policy if task initiator, NULL if responder
+ * @return child_create task to handle by the task_manager
+ */
+child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy);
+
+#endif /* CHILD_CREATE_H_ */
diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c
new file mode 100644
index 000000000..875f14acc
--- /dev/null
+++ b/src/charon/sa/tasks/child_delete.c
@@ -0,0 +1,264 @@
+/**
+ * @file child_delete.c
+ *
+ * @brief Implementation of the child_delete task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_child_delete_t private_child_delete_t;
+
+/**
+ * Private members of a child_delete_t task.
+ */
+struct private_child_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * CHILD_SAs which get deleted
+ */
+ linked_list_t *child_sas;
+};
+
+/**
+ * build the delete payloads from the listed child_sas
+ */
+static void build_payloads(private_child_delete_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ delete_payload_t *ah = NULL, *esp = NULL;
+ u_int32_t spi;
+ child_sa_t *child_sa;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ spi = child_sa->get_spi(child_sa, TRUE);
+ switch (child_sa->get_protocol(child_sa))
+ {
+ case PROTO_ESP:
+ if (esp == NULL)
+ {
+ esp = delete_payload_create(PROTO_ESP);
+ message->add_payload(message, (payload_t*)esp);
+ }
+ esp->add_spi(esp, spi);
+ break;
+ case PROTO_AH:
+ if (ah == NULL)
+ {
+ ah = delete_payload_create(PROTO_AH);
+ message->add_payload(message, (payload_t*)ah);
+ }
+ ah->add_spi(ah, spi);
+ break;
+ default:
+ break;
+ }
+ child_sa->set_state(child_sa, CHILD_DELETING);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * read in payloads and find the children to delete
+ */
+static void process_payloads(private_child_delete_t *this, message_t *message)
+{
+ iterator_t *payloads, *spis;
+ payload_t *payload;
+ delete_payload_t *delete_payload;
+ u_int32_t spi;
+ protocol_id_t protocol;
+ child_sa_t *child_sa;
+
+ payloads = message->get_payload_iterator(message);
+ while (payloads->iterate(payloads, (void**)&payload))
+ {
+ if (payload->get_type(payload) == DELETE)
+ {
+ delete_payload = (delete_payload_t*)payload;
+ protocol = delete_payload->get_protocol_id(delete_payload);
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ spis = delete_payload->create_spi_iterator(delete_payload);
+ while (spis->iterate(spis, (void**)&spi))
+ {
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ if (child_sa == NULL)
+ {
+ DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, "
+ "but no such SA", protocol_id_names, protocol, ntohl(spi));
+ break;
+ }
+
+ if (child_sa->get_state(child_sa) == CHILD_REKEYING)
+ {
+ /* TODO: handle rekeying */
+ }
+
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ }
+ spis->destroy(spis);
+ }
+ }
+ payloads->destroy(payloads);
+}
+
+/**
+ * destroy the children listed in this->child_sas
+ */
+static void destroy_children(private_child_delete_t *this)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+ protocol_id_t protocol;
+ u_int32_t spi;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ /* TODO: can we do this more cleanly? */
+ spi = child_sa->get_spi(child_sa, TRUE);
+ protocol = child_sa->get_protocol(child_sa);
+ this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_delete_t *this, message_t *message)
+{
+ build_payloads(this, message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_delete_t *this, message_t *message)
+{
+ /* flush the list before adding new SAs */
+ this->child_sas->destroy(this->child_sas);
+ this->child_sas = linked_list_create();
+
+ process_payloads(this, message);
+ destroy_children(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_delete_t *this, message_t *message)
+{
+ process_payloads(this, message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_delete_t *this, message_t *message)
+{
+ build_payloads(this, message);
+ destroy_children(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_delete_t *this)
+{
+ return CHILD_DELETE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+
+ this->child_sas->destroy(this->child_sas);
+ this->child_sas = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_delete_t *this)
+{
+ this->child_sas->destroy(this->child_sas);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ private_child_delete_t *this = malloc_thing(private_child_delete_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ this->ike_sa = ike_sa;
+ this->child_sas = linked_list_create();
+
+ if (child_sa != NULL)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h
new file mode 100644
index 000000000..327a2ec15
--- /dev/null
+++ b/src/charon/sa/tasks/child_delete.h
@@ -0,0 +1,58 @@
+/**
+ * @file child_delete.h
+ *
+ * @brief Interface child_delete_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CHILD_DELETE_H_
+#define CHILD_DELETE_H_
+
+typedef struct child_delete_t child_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <sa/child_sa.h>
+
+/**
+ * @brief Task of type child_delete, delete a CHILD_SA.
+ *
+ * @b Constructors:
+ * - child_delete_create()
+ *
+ * @ingroup tasks
+ */
+struct child_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new child_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param child_sa CHILD_SA to delete, or NULL as responder
+ * @return child_delete task to handle by the task_manager
+ */
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+
+#endif /* CHILD_DELETE_H_ */
diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c
new file mode 100644
index 000000000..41533b68c
--- /dev/null
+++ b/src/charon/sa/tasks/child_rekey.c
@@ -0,0 +1,353 @@
+/**
+ * @file child_rekey.c
+ *
+ * @brief Implementation of the child_rekey task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_rekey.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <sa/tasks/child_create.h>
+
+
+typedef struct private_child_rekey_t private_child_rekey_t;
+
+/**
+ * Private members of a child_rekey_t task.
+ */
+struct private_child_rekey_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_rekey_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * the CHILD_CREATE task which is reused to simplify rekeying
+ */
+ child_create_t *child_create;
+
+ /**
+ * CHILD_SA which gets rekeyed
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * redundandt CHILD_SA created simultaneously
+ */
+ child_sa_t *simultaneous;
+
+ /**
+ * the lowest nonce compared so far
+ */
+ chunk_t nonce;
+
+ /**
+ * TRUE if we have the lower nonce
+ */
+ bool winner;
+};
+
+/**
+ * get the nonce from a message, return TRUE if it was lower than this->nonce
+ */
+static bool get_nonce(private_child_rekey_t *this, message_t *message)
+{
+ nonce_payload_t *payload;
+ chunk_t nonce;
+
+ payload = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (payload == NULL)
+ {
+ return FALSE;
+ }
+ nonce = payload->get_nonce(payload);
+
+ if (this->nonce.ptr && memcmp(nonce.ptr, this->nonce.ptr,
+ min(nonce.len, this->nonce.len)) > 0)
+ {
+ chunk_free(&nonce);
+ return FALSE;
+ }
+
+ chunk_free(&this->nonce);
+ this->nonce = nonce;
+ return TRUE;
+}
+
+/**
+ * find a child using the REKEY_SA notify
+ */
+static void find_child(private_child_rekey_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ notify_payload_t *notify;
+ u_int32_t spi;
+ protocol_id_t protocol;
+
+ if (payload->get_type(payload) != NOTIFY)
+ {
+ continue;
+ }
+
+ notify = (notify_payload_t*)payload;
+ protocol = notify->get_protocol_id(notify);
+ spi = notify->get_spi(notify);
+
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ break;
+
+ }
+ iterator->destroy(iterator);
+}
+
+#if 0
+/**
+ * handle a detected simultaneous rekeying situation as responder
+ */
+static void simultaneous_r(private_child_rekey_t *this, message_t *message)
+{
+ private_child_rekey_t *other = NULL;
+ task_t *task;
+ iterator_t *iterator;
+
+ this->ike_sa->create_task_iterator(this->ike_sa);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ if (task->get_type(task) == CHILD_REKEY)
+ {
+ other = (private_child_rekey_t*)task;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (other)
+ {
+ other->simultaneous = this->child_create->get_child(this->child_create);
+
+ if (!get_nonce(other, message))
+ {
+ /* this wins the race, other lost */
+ other->winner = FALSE;
+ }
+ }
+}
+
+/**
+ * was there a simultaneous rekeying, did we win the nonce compare?
+ */
+static bool simultaneous_i(private_child_rekey_t *this, message_t *message)
+{
+ if (this->winner || get_nonce(this, message))
+ {
+ /* we have the lower nonce and win */
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_rekey_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ protocol_id_t protocol;
+ u_int32_t spi, reqid;
+
+ /* our CHILD_CREATE task does the hard work for us... */
+ reqid = this->child_sa->get_reqid(this->child_sa);
+ this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->task.build(&this->child_create->task, message);
+ get_nonce(this, message);
+
+ /* ... we just need the rekey notify */
+ protocol = this->child_sa->get_protocol(this->child_sa);
+ spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
+ notify->set_spi(notify, spi);
+ message->add_payload(message, (payload_t*)notify);
+
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_rekey_t *this, message_t *message)
+{
+ /* let the CHILD_CREATE task process the message */
+ this->child_create->task.process(&this->child_create->task, message);
+ get_nonce(this, message);
+
+ find_child(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_rekey_t *this, message_t *message)
+{
+ u_int32_t reqid;
+
+ if (this->child_sa == NULL ||
+ this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
+ {
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ /* let the CHILD_CREATE task build the response */
+ reqid = this->child_sa->get_reqid(this->child_sa);
+ this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->task.build(&this->child_create->task, message);
+ get_nonce(this, message);
+
+ if (this->child_sa->get_state(this->child_sa) == CHILD_REKEYING)
+ {
+ /* simultaneous_detected(this); */
+ }
+
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_rekey_t *this, message_t *message)
+{
+ protocol_id_t protocol;
+ u_int32_t spi;
+
+ this->child_create->task.process(&this->child_create->task, message);
+
+ /*if (!simultaneous_won(this, message))
+ {
+ * delete the redundant CHILD_SA, instead of the rekeyed *
+ this->child_sa = this->create_child->get_child(this->create_child);
+ }*/
+ spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ protocol = this->child_sa->get_protocol(this->child_sa);
+
+ /* TODO: don't delete when rekeying failed */
+ if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
+ {
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_rekey_t *this)
+{
+ return CHILD_REKEY;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
+{
+ this->child_create->task.migrate(&this->child_create->task, ike_sa);
+ chunk_free(&this->nonce);
+
+ this->ike_sa = ike_sa;
+ this->winner = TRUE;
+ this->simultaneous = NULL;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_rekey_t *this)
+{
+ this->child_create->task.destroy(&this->child_create->task);
+ chunk_free(&this->nonce);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
+ policy_t *policy;
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (child_sa != NULL)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ policy = child_sa->get_policy(child_sa);
+ this->child_create = child_create_create(ike_sa, policy);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ this->child_create = child_create_create(ike_sa, NULL);
+ }
+
+ this->ike_sa = ike_sa;
+ this->child_sa = child_sa;
+ this->nonce = chunk_empty;
+ this->winner = TRUE;
+ this->simultaneous = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h
new file mode 100644
index 000000000..77297b13d
--- /dev/null
+++ b/src/charon/sa/tasks/child_rekey.h
@@ -0,0 +1,58 @@
+/**
+ * @file child_rekey.h
+ *
+ * @brief Interface child_rekey_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef CHILD_REKEY_H_
+#define CHILD_REKEY_H_
+
+typedef struct child_rekey_t child_rekey_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/child_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA.
+ *
+ * @b Constructors:
+ * - child_rekey_create()
+ *
+ * @ingroup tasks
+ */
+struct child_rekey_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new CHILD_REKEY task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param child_sa child_sa to rekey, NULL if responder
+ * @return child_rekey task to handle by the task_manager
+ */
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+
+#endif /* CHILD_REKEY_H_ */
diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c
new file mode 100644
index 000000000..2e54b7797
--- /dev/null
+++ b/src/charon/sa/tasks/ike_auth.c
@@ -0,0 +1,513 @@
+/**
+ * @file ike_auth.c
+ *
+ * @brief Implementation of the ike_auth task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_auth.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+
+typedef struct private_ike_auth_t private_ike_auth_t;
+
+/**
+ * Private members of a ike_auth_t task.
+ */
+struct private_ike_auth_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_auth_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Nonce chosen by us in ike_init
+ */
+ chunk_t my_nonce;
+
+ /**
+ * Nonce chosen by peer in ike_init
+ */
+ chunk_t other_nonce;
+
+ /**
+ * IKE_SA_INIT message sent by us
+ */
+ packet_t *my_packet;
+
+ /**
+ * IKE_SA_INIT message sent by peer
+ */
+ packet_t *other_packet;
+
+ /**
+ * authenticator to authenticate us
+ */
+ authenticator_t *my_auth;
+
+ /**
+ * authenticator to authenticate peer
+ */
+ authenticator_t *other_auth;
+
+ /**
+ * has the peer been authenticated successfully?
+ */
+ bool peer_authenticated;
+};
+
+/**
+ * build the payloads for the message
+ */
+static status_t build_payloads(private_ike_auth_t *this, message_t *message)
+{
+ authenticator_t *auth;
+ auth_payload_t *auth_payload;
+ id_payload_t *id_payload;
+ chunk_t ike_sa_init;
+ identification_t *me, *other;
+ policy_t *policy;
+ auth_method_t method = AUTH_RSA;
+ status_t status;
+
+ /* add own ID payload */
+ me = this->ike_sa->get_my_id(this->ike_sa);
+ other = this->ike_sa->get_other_id(this->ike_sa);
+
+ id_payload = id_payload_create_from_identification(this->initiator, me);
+ message->add_payload(message, (payload_t*)id_payload);
+
+ /* as initiator, include other ID if it does not contain wildcards */
+ if (this->initiator && !other->contains_wildcards(other))
+ {
+ id_payload = id_payload_create_from_identification(FALSE, other);
+ message->add_payload(message, (payload_t*)id_payload);
+ }
+
+ /* create own authenticator and add auth payload */
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ if (policy)
+ {
+ method = policy->get_auth_method(policy);
+ }
+ auth = authenticator_create(this->ike_sa, method);
+ if (auth == NULL)
+ {
+ SIG(IKE_UP_FAILED, "configured authentication method %N not supported",
+ auth_method_names, method);
+ return FAILED;
+ }
+
+ ike_sa_init = this->my_packet->get_data(this->my_packet);
+ status = auth->build(auth, ike_sa_init, this->other_nonce, &auth_payload);
+ auth->destroy(auth);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "generating authentication data failed");
+ return FAILED;
+ }
+ message->add_payload(message, (payload_t*)auth_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * process payloads from message
+ */
+static void process_payloads(private_ike_auth_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ payload_type_t type;
+ identification_t *idi = NULL, *idr = NULL;
+ auth_payload_t *auth_payload = NULL;
+ authenticator_t *auth;
+ auth_method_t auth_method;
+ status_t status;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ type = payload->get_type(payload);
+ switch (type)
+ {
+ case ID_INITIATOR:
+ {
+ id_payload_t *id_payload = (id_payload_t*)payload;
+ idi = id_payload->get_identification(id_payload);
+ break;
+ }
+ case ID_RESPONDER:
+ {
+ id_payload_t *id_payload = (id_payload_t*)payload;
+ idr = id_payload->get_identification(id_payload);
+ break;
+ }
+ case AUTHENTICATION:
+ {
+ auth_payload = (auth_payload_t*)payload;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ /* apply IDs */
+ if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL))
+ {
+ SIG(IKE_UP_FAILED, "ID payload missing in message");
+ DESTROY_IF(idr); DESTROY_IF(idi);
+ return;
+ }
+
+ if (this->initiator)
+ {
+ identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa);
+ if (!idr->matches(idr, other_id, NULL))
+ {
+ SIG(IKE_UP_FAILED, "received inacceptable id %D, %D required", idr,
+ this->ike_sa->get_other_id(this->ike_sa));
+ DESTROY_IF(idi); DESTROY_IF(idr);
+ return;
+ }
+ this->ike_sa->set_other_id(this->ike_sa, idr);
+ }
+ else
+ {
+ identification_t *my_id = this->ike_sa->get_other_id(this->ike_sa);
+ if (idr)
+ {
+ if (!idr->matches(idr, my_id, NULL))
+ {
+ SIG(IKE_UP_FAILED, "received inacceptable id %D, %D required",
+ idr, this->ike_sa->get_other_id(this->ike_sa));
+ DESTROY_IF(idi); DESTROY_IF(idr);
+ return;
+ }
+ this->ike_sa->set_my_id(this->ike_sa, idr);
+ }
+ else
+ {
+ if (my_id->contains_wildcards(my_id))
+ {
+ SIG(IKE_UP_FAILED, "own ID (%D) not defined after exchange",
+ my_id);
+ DESTROY_IF(idi);
+ return;
+ }
+ }
+ this->ike_sa->set_other_id(this->ike_sa, idi);
+ }
+
+ /* verify auth payload */
+ if (auth_payload == NULL)
+ {
+ SIG(IKE_UP_FAILED, "AUTH payload missing in message");
+ return;
+ }
+
+ auth_method = auth_payload->get_auth_method(auth_payload);
+ auth = authenticator_create(this->ike_sa, auth_method);
+ if (auth == NULL)
+ {
+ SIG(IKE_UP_FAILED, "authentication method %N used by %D not "
+ "supported", auth_method_names, auth_method,
+ this->ike_sa->get_other_id(this->ike_sa));
+ return;
+ }
+ status = auth->verify(auth, this->other_packet->get_data(this->other_packet),
+ this->my_nonce, auth_payload);
+ auth->destroy(auth);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "authentication of %D using %N failed",
+ this->ike_sa->get_other_id(this->ike_sa),
+ auth_method_names, auth_method);
+ return;
+ }
+ this->peer_authenticated = TRUE;
+}
+
+/**
+ * collect the needed information in the IKE_SA_INIT exchange from our message
+ */
+static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message)
+{
+ nonce_payload_t *nonce;
+
+ /* get the nonce that was generated in ike_init */
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (nonce == NULL)
+ {
+ return FAILED;
+ }
+ this->my_nonce = nonce->get_nonce(nonce);
+
+ /* pre-generate the message, so we can store it for us */
+ if (this->ike_sa->generate_message(this->ike_sa, message,
+ &this->my_packet) != SUCCESS)
+ {
+ return FAILED;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * collect the needed information in the IKE_SA_INIT exchange from others message
+ */
+static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message)
+{
+ /* we collect the needed information in the IKE_SA_INIT exchange */
+ nonce_payload_t *nonce;
+
+ /* get the nonce that was generated in ike_init */
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (nonce == NULL)
+ {
+ return FAILED;
+ }
+ this->other_nonce = nonce->get_nonce(nonce);
+
+ /* pre-generate the message, so we can store it for us */
+ this->other_packet = message->get_packet(message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_my_init_data(this, message);
+ }
+
+ if (build_payloads(this, message) == SUCCESS)
+ {
+ return NEED_MORE;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_other_init_data(this, message);
+ }
+
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_my_init_data(this, message);
+ }
+
+ if (!this->peer_authenticated)
+ {
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ return FAILED;
+ }
+
+ if (build_payloads(this, message) == SUCCESS)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_auth_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_other_init_data(this, message);
+ }
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case NO_PROPOSAL_CHOSEN:
+ case SINGLE_PAIR_REQUIRED:
+ case NO_ADDITIONAL_SAS:
+ case INTERNAL_ADDRESS_FAILURE:
+ case FAILED_CP_REQUIRED:
+ case TS_UNACCEPTABLE:
+ case INVALID_SELECTORS:
+ /* these are errors, but are not critical as only the
+ * CHILD_SA won't get build, but IKE_SA establishes anyway */
+ DBG1(DBG_IKE, "received %N notify, no CHILD_SA built",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return SUCCESS;
+ default:
+ {
+ if (type < 16383)
+ {
+ DBG1(DBG_IKE, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ if (this->peer_authenticated)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_auth_t *this)
+{
+ return IKE_AUTHENTICATE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ DESTROY_IF(this->my_packet);
+ DESTROY_IF(this->other_packet);
+ DESTROY_IF(this->my_auth);
+ DESTROY_IF(this->other_auth);
+ this->my_packet = NULL;
+ this->other_packet = NULL;
+ this->my_auth = NULL;
+ this->other_auth = NULL;
+ this->peer_authenticated = FALSE;
+ this->ike_sa = ike_sa;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_auth_t *this)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ DESTROY_IF(this->my_packet);
+ DESTROY_IF(this->other_packet);
+ DESTROY_IF(this->my_auth);
+ DESTROY_IF(this->other_auth);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_auth_t *this = malloc_thing(private_ike_auth_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->my_packet = NULL;
+ this->other_packet = NULL;
+ this->my_auth = NULL;
+ this->other_auth = NULL;
+ this->peer_authenticated = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h
new file mode 100644
index 000000000..e59e6811a
--- /dev/null
+++ b/src/charon/sa/tasks/ike_auth.h
@@ -0,0 +1,60 @@
+/**
+ * @file ike_auth.h
+ *
+ * @brief Interface ike_auth_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_AUTH_H_
+#define IKE_AUTH_H_
+
+typedef struct ike_auth_t ike_auth_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_auth, authenticates an IKE_SA authenticators.
+ *
+ * The ike_auth task authenticates the IKE_SA using the IKE_AUTH
+ * exchange.
+ *
+ * @b Constructors:
+ * - ike_auth_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_auth_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new task of type IKE_AUTHENTICATE.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the initator of an exchange
+ * @return ike_auth task to handle by the task_manager
+ */
+ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_AUTH_H_ */
diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c
new file mode 100644
index 000000000..3cddc0073
--- /dev/null
+++ b/src/charon/sa/tasks/ike_cert.c
@@ -0,0 +1,361 @@
+/**
+ * @file ike_cert.c
+ *
+ * @brief Implementation of the ike_cert task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_cert.h"
+
+#include <daemon.h>
+#include <sa/ike_sa.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+
+
+typedef struct private_ike_cert_t private_ike_cert_t;
+
+/**
+ * Private members of a ike_cert_t task.
+ */
+struct private_ike_cert_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_cert_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * list of CA cert hashes requested, items point to 20 byte chunk
+ */
+ linked_list_t *cas;
+};
+
+/**
+ * read certificate requests
+ */
+static void process_certreqs(private_ike_cert_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE_REQUEST)
+ {
+ certreq_payload_t *certreq = (certreq_payload_t*)payload;
+ cert_encoding_t encoding;
+ chunk_t keyids, keyid;
+
+ encoding = certreq->get_cert_encoding(certreq);
+ if (encoding != CERT_X509_SIGNATURE)
+ {
+ DBG1(DBG_IKE, "certreq payload %N not supported, ignored",
+ cert_encoding_names, encoding);
+ continue;
+ }
+
+ keyids = certreq->get_data(certreq);
+
+ while (keyids.len >= HASH_SIZE_SHA1)
+ {
+ keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1);
+ keyid = chunk_clone(keyid);
+ this->cas->insert_last(this->cas, keyid.ptr);
+ keyids = chunk_skip(keyids, HASH_SIZE_SHA1);
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * import certificates
+ */
+static void process_certs(private_ike_cert_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE)
+ {
+ cert_encoding_t encoding;
+ x509_t *cert;
+ chunk_t cert_data;
+ bool found;
+ cert_payload_t *cert_payload = (cert_payload_t*)payload;
+
+ encoding = cert_payload->get_cert_encoding(cert_payload);
+ if (encoding != CERT_X509_SIGNATURE)
+ {
+ DBG1(DBG_IKE, "certificate payload %N not supported, ignored",
+ cert_encoding_names, encoding);
+ continue;
+ }
+
+ cert_data = cert_payload->get_data_clone(cert_payload);
+ cert = x509_create_from_chunk(cert_data);
+ if (cert)
+ {
+ if (charon->credentials->verify(charon->credentials,
+ cert, &found))
+ {
+ DBG2(DBG_IKE, "received end entity certificate is trusted, "
+ "added to store");
+ if (!found)
+ {
+ charon->credentials->add_end_certificate(
+ charon->credentials, cert);
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received end entity certificate is not "
+ "trusted, discarded");
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "parsing of received certificate failed, discarded");
+ chunk_free(&cert_data);
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * build certificate requests
+ */
+static void build_certreqs(private_ike_cert_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ identification_t *ca;
+ certreq_payload_t *certreq;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (connection->get_certreq_policy(connection) != CERT_NEVER_SEND)
+ {
+ policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (policy)
+ {
+ ca = policy->get_other_ca(policy);
+
+ if (ca && ca->get_type(ca) != ID_ANY)
+ {
+ certreq = certreq_payload_create_from_cacert(ca);
+ }
+ else
+ {
+ certreq = certreq_payload_create_from_cacerts();
+ }
+ }
+ else
+ {
+ certreq = certreq_payload_create_from_cacerts();
+ }
+
+ if (certreq)
+ {
+ message->add_payload(message, (payload_t*)certreq);
+ }
+ }
+}
+
+/**
+ * add certificates to message
+ */
+static void build_certs(private_ike_cert_t *this, message_t *message)
+{
+ policy_t *policy;
+ connection_t *connection;
+ x509_t *cert;
+ cert_payload_t *payload;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (policy && policy->get_auth_method(policy) == AUTH_RSA)
+ {
+ switch (connection->get_cert_policy(connection))
+ {
+ case CERT_NEVER_SEND:
+ break;
+ case CERT_SEND_IF_ASKED:
+ if (this->cas->get_count(this->cas) == 0)
+ {
+ break;
+ }
+ /* FALL */
+ case CERT_ALWAYS_SEND:
+ {
+ /* TODO: respect CA cert request */
+ cert = charon->credentials->get_certificate(charon->credentials,
+ policy->get_my_id(policy));
+ if (cert)
+ {
+ payload = cert_payload_create_from_x509(cert);
+ message->add_payload(message, (payload_t*)payload);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return NEED_MORE;
+ }
+
+ build_certreqs(this, message);
+ build_certs(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return NEED_MORE;
+ }
+
+ process_certreqs(this, message);
+ process_certs(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ build_certreqs(this, message);
+ return NEED_MORE;
+ }
+
+ build_certs(this, message);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ process_certreqs(this, message);
+ return NEED_MORE;
+ }
+
+ process_certs(this, message);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_cert_t *this)
+{
+ return IKE_CERT;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+
+ this->cas->destroy_function(this->cas, free);
+ this->cas = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_cert_t *this)
+{
+ this->cas->destroy_function(this->cas, free);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_cert_t *this = malloc_thing(private_ike_cert_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->cas = linked_list_create();
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert.h
new file mode 100644
index 000000000..ba0283953
--- /dev/null
+++ b/src/charon/sa/tasks/ike_cert.h
@@ -0,0 +1,61 @@
+/**
+ * @file ike_cert.h
+ *
+ * @brief Interface ike_cert_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_CERT_H_
+#define IKE_CERT_H_
+
+typedef struct ike_cert_t ike_cert_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_cert, exchanges certificates and
+ * certificate requests.
+ *
+ * @b Constructors:
+ * - ike_cert_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_cert_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_cert task.
+ *
+ * The initiator parameter means the original initiator, not the initiator
+ * of the certificate request.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_cert task to handle by the task_manager
+ */
+ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_CERT_H_ */
diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c
new file mode 100644
index 000000000..b6e883b48
--- /dev/null
+++ b/src/charon/sa/tasks/ike_config.c
@@ -0,0 +1,421 @@
+/**
+ * @file ike_config.c
+ *
+ * @brief Implementation of the ike_config task.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_config.h"
+
+#include <daemon.h>
+#include <encoding/payloads/cp_payload.h>
+
+typedef struct private_ike_config_t private_ike_config_t;
+
+/**
+ * Private members of a ike_config_t task.
+ */
+struct private_ike_config_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_config_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * associated policy with virtual IP configuration
+ */
+ policy_t *policy;
+
+ /**
+ * virtual ip
+ */
+ host_t *virtual_ip;
+
+ /**
+ * list of DNS servers
+ */
+ linked_list_t *dns;
+};
+
+/**
+ * build configuration payloads and attributes
+ */
+static void build_payloads(private_ike_config_t *this, message_t *message,
+ config_type_t type)
+{
+ cp_payload_t *cp;
+ configuration_attribute_t *ca;
+ chunk_t chunk, prefix;
+
+ if (!this->virtual_ip)
+ {
+ return;
+ }
+
+ cp = cp_payload_create();
+ cp->set_config_type(cp, type);
+
+ ca = configuration_attribute_create();
+
+ if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_ADDRESS);
+ if (this->virtual_ip->is_anyaddr(this->virtual_ip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ chunk = this->virtual_ip->get_address(this->virtual_ip);
+ }
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_ADDRESS);
+ if (this->virtual_ip->is_anyaddr(this->virtual_ip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ prefix = chunk_alloca(1);
+ *prefix.ptr = 64;
+ chunk = this->virtual_ip->get_address(this->virtual_ip);
+ chunk = chunk_cata("cc", chunk, prefix);
+ }
+ }
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+
+ /* we currently always add a DNS request if we request an IP */
+ if (this->initiator)
+ {
+ ca = configuration_attribute_create();
+ if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_DNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_DNS);
+ }
+ cp->add_configuration_attribute(cp, ca);
+ }
+ else
+ {
+ host_t *ip;
+ iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ ca = configuration_attribute_create();
+ if (ip->get_family(ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_DNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_DNS);
+ }
+ chunk = ip->get_address(ip);
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+ }
+ iterator->destroy(iterator);
+ }
+ message->add_payload(message, (payload_t*)cp);
+}
+
+/**
+ * process a single configuration attribute
+ */
+static void process_attribute(private_ike_config_t *this,
+ configuration_attribute_t *ca)
+{
+ host_t *ip;
+ chunk_t addr;
+ int family = AF_INET6;
+
+ switch (ca->get_type(ca))
+ {
+ case INTERNAL_IP4_ADDRESS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_ADDRESS:
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ /* skip prefix byte in IPv6 payload*/
+ if (family == AF_INET6)
+ {
+ addr.len--;
+ }
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip && !this->virtual_ip)
+ {
+ this->virtual_ip = ip;
+ }
+ break;
+ }
+ case INTERNAL_IP4_DNS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_DNS:
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip)
+ {
+ this->dns->insert_last(this->dns, ip);
+ }
+ break;
+ }
+ case INTERNAL_IP4_NBNS:
+ case INTERNAL_IP6_NBNS:
+ /* TODO */
+ default:
+ DBG1(DBG_IKE, "ignoring %N config attribute",
+ configuration_attribute_type_names,
+ ca->get_type(ca));
+ break;
+ }
+}
+
+/**
+ * Scan for configuration payloads and attributes
+ */
+static void process_payloads(private_ike_config_t *this, message_t *message)
+{
+ iterator_t *iterator, *attributes;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CONFIGURATION)
+ {
+ cp_payload_t *cp = (cp_payload_t*)payload;
+ configuration_attribute_t *ca;
+ switch (cp->get_config_type(cp))
+ {
+ case CFG_REQUEST:
+ case CFG_REPLY:
+ {
+ attributes = cp->create_attribute_iterator(cp);
+ while (attributes->iterate(attributes, (void**)&ca))
+ {
+ process_attribute(this, ca);
+ }
+ attributes->destroy(attributes);
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring %N config payload",
+ config_type_names, cp->get_config_type(cp));
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ this->virtual_ip = this->policy->get_virtual_ip(this->policy, NULL);
+
+ build_payloads(this, message, CFG_REQUEST);
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ process_payloads(this, message);
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ this->policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (this->policy && this->virtual_ip)
+ {
+ host_t *ip;
+
+ DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip);
+ ip = this->policy->get_virtual_ip(this->policy, this->virtual_ip);
+ if (ip == NULL)
+ {
+ DBG1(DBG_IKE, "not assigning a virtual IP to peer");
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip);
+ this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, ip);
+
+ this->virtual_ip->destroy(this->virtual_ip);
+ this->virtual_ip = ip;
+
+ /* DNS is for testing only */
+ if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS)
+ {
+ ip->destroy(ip);
+ ip = host_create_from_string("10.3.0.1", 0);
+ this->dns->insert_last(this->dns, ip);
+ ip = host_create_from_string("10.3.0.2", 0);
+ this->dns->insert_last(this->dns, ip);
+ }
+
+ build_payloads(this, message, CFG_REPLY);
+ }
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ host_t *ip;
+
+ DESTROY_IF(this->virtual_ip);
+ this->virtual_ip = NULL;
+
+ process_payloads(this, message);
+
+ if (this->virtual_ip)
+ {
+ this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip);
+
+ while (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS)
+ {
+ this->ike_sa->add_dns_server(this->ike_sa, ip);
+ ip->destroy(ip);
+ }
+ }
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_config_t *this)
+{
+ return IKE_CONFIG;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->virtual_ip);
+ this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+
+ this->ike_sa = ike_sa;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_config_t *this)
+{
+ DESTROY_IF(this->virtual_ip);
+ this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy)
+{
+ private_ike_config_t *this = malloc_thing(private_ike_config_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (policy)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+
+ this->ike_sa = ike_sa;
+ this->policy = policy;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h
new file mode 100644
index 000000000..0c9b961b4
--- /dev/null
+++ b/src/charon/sa/tasks/ike_config.h
@@ -0,0 +1,59 @@
+/**
+ * @file ike_config.h
+ *
+ * @brief Interface ike_config_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_CONFIG_H_
+#define IKE_CONFIG_H_
+
+typedef struct ike_config_t ike_config_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <config/policies/policy.h>
+
+/**
+ * @brief Task of type IKE_CONFIG, sets up a virtual IP and other
+ * configurations for an IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_config_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_config_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_config task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param policy policy for the initiator, NULL for the responder
+ * @return ike_config task to handle by the task_manager
+ */
+ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy);
+
+#endif /* IKE_CONFIG_H_ */
diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c
new file mode 100644
index 000000000..b9efc4ea9
--- /dev/null
+++ b/src/charon/sa/tasks/ike_delete.c
@@ -0,0 +1,169 @@
+/**
+ * @file ike_delete.c
+ *
+ * @brief Implementation of the ike_delete task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_ike_delete_t private_ike_delete_t;
+
+/**
+ * Private members of a ike_delete_t task.
+ */
+struct private_ike_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * are we responding to a delete, but have initated our own?
+ */
+ bool simultaneous;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_delete_t *this, message_t *message)
+{
+ delete_payload_t *delete_payload;
+
+ delete_payload = delete_payload_create(PROTO_IKE);
+ message->add_payload(message, (payload_t*)delete_payload);
+
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_delete_t *this, message_t *message)
+{
+ /* completed, delete IKE_SA by returning FAILED */
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_delete_t *this, message_t *message)
+{
+ /* we don't even scan the payloads, as the message wouldn't have
+ * come so far without being correct */
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_DELETING:
+ this->simultaneous = TRUE;
+ break;
+ case IKE_ESTABLISHED:
+ DBG1(DBG_IKE, "deleting IKE_SA on request");
+ /* warn only if we are established */
+ default:
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+ break;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_delete_t *this, message_t *message)
+{
+ if (this->simultaneous)
+ {
+ /* wait for peers response for our delete request */
+ return SUCCESS;
+ }
+ /* completed, delete IKE_SA by returning FAILED */
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_delete_t *this)
+{
+ return IKE_DELETE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->simultaneous = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_delete_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_delete_t *this = malloc_thing(private_ike_delete_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->simultaneous = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h
new file mode 100644
index 000000000..e8ec5ebbe
--- /dev/null
+++ b/src/charon/sa/tasks/ike_delete.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_delete.h
+ *
+ * @brief Interface ike_delete_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_DELETE_H_
+#define IKE_DELETE_H_
+
+typedef struct ike_delete_t ike_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_delete, delete an IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_delete_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if we initiate the delete
+ * @return ike_delete task to handle by the task_manager
+ */
+ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_DELETE_H_ */
diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c
new file mode 100644
index 000000000..1cb05c45c
--- /dev/null
+++ b/src/charon/sa/tasks/ike_dpd.c
@@ -0,0 +1,106 @@
+/**
+ * @file ike_dpd.c
+ *
+ * @brief Implementation of the ike_dpd task.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_dpd.h"
+
+#include <daemon.h>
+
+
+typedef struct private_ike_dpd_t private_ike_dpd_t;
+
+/**
+ * Private members of a ike_dpd_t task.
+ */
+struct private_ike_dpd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_dpd_t public;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ * Implementation of task_t.process for responder
+ */
+static status_t return_need_more(private_ike_dpd_t *this, message_t *message)
+{
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ * Implementation of task_t.build for responder
+ */
+static status_t return_success(private_ike_dpd_t *this, message_t *message)
+{
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_dpd_t *this)
+{
+ return IKE_DEADPEER;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_dpd_t *this, ike_sa_t *ike_sa)
+{
+
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_dpd_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_dpd_t *ike_dpd_create(bool initiator)
+{
+ private_ike_dpd_t *this = malloc_thing(private_ike_dpd_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))return_success;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))return_success;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more;
+ }
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h
new file mode 100644
index 000000000..531b0502d
--- /dev/null
+++ b/src/charon/sa/tasks/ike_dpd.h
@@ -0,0 +1,58 @@
+/**
+ * @file ike_dpd.h
+ *
+ * @brief Interface ike_dpd_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_DPD_H_
+#define IKE_DPD_H_
+
+typedef struct ike_dpd_t ike_dpd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_dpd, detects dead peers.
+ *
+ * The DPD task actually does nothing, as a DPD has no associated payloads.
+ *
+ * @b Constructors:
+ * - ike_dpd_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_dpd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_dpd task.
+ *
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_dpd task to handle by the task_manager
+ */
+ike_dpd_t *ike_dpd_create(bool initiator);
+
+#endif /* IKE_DPD_H_ */
diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c
new file mode 100644
index 000000000..9149aab91
--- /dev/null
+++ b/src/charon/sa/tasks/ike_init.c
@@ -0,0 +1,536 @@
+/**
+ * @file ike_init.c
+ *
+ * @brief Implementation of the ike_init task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_init.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+
+typedef struct private_ike_init_t private_ike_init_t;
+
+/**
+ * Private members of a ike_init_t task.
+ */
+struct private_ike_init_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_init_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Connection established by this IKE_SA
+ */
+ connection_t *connection;
+
+ /**
+ * diffie hellman group to use
+ */
+ diffie_hellman_group_t dh_group;
+
+ /**
+ * Diffie hellman object used to generate public DH value.
+ */
+ diffie_hellman_t *diffie_hellman;
+
+ /**
+ * nonce chosen by us
+ */
+ chunk_t my_nonce;
+
+ /**
+ * nonce chosen by peer
+ */
+ chunk_t other_nonce;
+
+ /**
+ * Negotiated proposal used for IKE_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * Old IKE_SA which gets rekeyed
+ */
+ ike_sa_t *old_sa;
+};
+
+/**
+ * build the payloads for the message
+ */
+static void build_payloads(private_ike_init_t *this, message_t *message)
+{
+ sa_payload_t *sa_payload;
+ ke_payload_t *ke_payload;
+ nonce_payload_t *nonce_payload;
+ linked_list_t *proposal_list;
+ ike_sa_id_t *id;
+ proposal_t *proposal;
+ iterator_t *iterator;
+
+ id = this->ike_sa->get_id(this->ike_sa);
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (this->initiator)
+ {
+ proposal_list = this->connection->get_proposals(this->connection);
+ if (this->old_sa)
+ {
+ /* include SPI of new IKE_SA when we are rekeying */
+ iterator = proposal_list->create_iterator(proposal_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&proposal))
+ {
+ proposal->set_spi(proposal, id->get_initiator_spi(id));
+ }
+ iterator->destroy(iterator);
+ }
+
+ sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+ proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
+ }
+ else
+ {
+ if (this->old_sa)
+ {
+ /* include SPI of new IKE_SA when we are rekeying */
+ this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
+ }
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
+ message->add_payload(message, (payload_t*)ke_payload);
+
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+
+ message->add_payload(message, (payload_t*)nonce_payload);
+}
+
+/**
+ * Read payloads from message
+ */
+static void process_payloads(private_ike_init_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ {
+ sa_payload_t *sa_payload = (sa_payload_t*)payload;
+ linked_list_t *proposal_list;
+
+ proposal_list = sa_payload->get_proposals(sa_payload);
+ this->proposal = this->connection->select_proposal(
+ this->connection, proposal_list);
+ proposal_list->destroy_offset(proposal_list,
+ offsetof(proposal_t, destroy));
+ break;
+ }
+ case KEY_EXCHANGE:
+ {
+ ke_payload_t *ke_payload = (ke_payload_t*)payload;
+ diffie_hellman_group_t dh_group;
+ chunk_t key_data;
+
+ dh_group = ke_payload->get_dh_group_number(ke_payload);
+
+ if (this->initiator)
+ {
+ if (dh_group != this->dh_group)
+ {
+ DBG1(DBG_IKE, "received a DH group not requested (%N)",
+ diffie_hellman_group_names, dh_group);
+ break;
+ }
+ }
+ else
+ {
+ this->dh_group = dh_group;
+ if (!this->connection->check_dh_group(this->connection,
+ dh_group))
+ {
+ break;
+ }
+ this->diffie_hellman = diffie_hellman_create(dh_group);
+ }
+ if (this->diffie_hellman)
+ {
+ key_data = ke_payload->get_key_exchange_data(ke_payload);
+ this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data);
+ }
+ break;
+ }
+ case NONCE:
+ {
+ nonce_payload_t *nonce_payload = (nonce_payload_t*)payload;
+ this->other_nonce = nonce_payload->get_nonce(nonce_payload);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_init_t *this, message_t *message)
+{
+ randomizer_t *randomizer;
+ status_t status;
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+ SIG(IKE_UP_START, "initiating IKE_SA to %H",
+ this->connection->get_other_host(this->connection));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ /* if the DH group is set via use_dh_group(), we already have a DH object */
+ if (!this->diffie_hellman)
+ {
+ this->dh_group = this->connection->get_dh_group(this->connection);
+ this->diffie_hellman = diffie_hellman_create(this->dh_group);
+ if (this->diffie_hellman == NULL)
+ {
+ SIG(IKE_UP_FAILED, "configured DH group %N not supported",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+ }
+
+ randomizer = randomizer_create();
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ &this->my_nonce);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "error generating random nonce value");
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_init_t *this, message_t *message)
+{
+ randomizer_t *randomizer;
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+ SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA",
+ message->get_source(message));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ randomizer = randomizer_create();
+ if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ &this->my_nonce) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "error generating random nonce value");
+ }
+ randomizer->destroy(randomizer);
+
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_init_t *this, message_t *message)
+{
+ chunk_t secret;
+ status_t status;
+
+ /* check if we have everything we need */
+ if (this->proposal == NULL ||
+ this->other_nonce.len == 0 || this->my_nonce.len == 0)
+ {
+ SIG(IKE_UP_FAILED, "received proposals inacceptable");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
+ if (this->diffie_hellman == NULL ||
+ this->diffie_hellman->get_shared_secret(this->diffie_hellman,
+ &secret) != SUCCESS)
+ {
+ chunk_t chunk;
+ u_int16_t dh_enc;
+
+ SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)",
+ diffie_hellman_group_names, this->dh_group);
+ this->dh_group = this->connection->get_dh_group(this->connection);
+ dh_enc = htons(this->dh_group);
+ chunk.ptr = (u_int8_t*)&dh_enc;
+ chunk.len = sizeof(dh_enc);
+ message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk);
+ DBG1(DBG_IKE, "requesting DH group %N",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+
+
+ if (this->old_sa)
+ {
+ ike_sa_id_t *id;
+ prf_t *prf, *child_prf;
+
+ /* Apply SPI if we are rekeying */
+ id = this->ike_sa->get_id(this->ike_sa);
+ id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
+
+ /* setup crypto keys for the rekeyed SA */
+ prf = this->old_sa->get_prf(this->old_sa);
+ child_prf = this->old_sa->get_child_prf(this->old_sa);
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->other_nonce, this->my_nonce,
+ FALSE, child_prf, prf);
+ }
+ else
+ {
+ /* setup crypto keys */
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->other_nonce, this->my_nonce,
+ FALSE, NULL, NULL);
+ }
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "key derivation failed");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_init_t *this, message_t *message)
+{
+ chunk_t secret;
+ status_t status;
+ iterator_t *iterator;
+ payload_t *payload;
+
+ /* check for erronous notifies */
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case INVALID_KE_PAYLOAD:
+ {
+ chunk_t data;
+ diffie_hellman_group_t old_dh_group;
+
+ old_dh_group = this->dh_group;
+ data = notify->get_notification_data(notify);
+ this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+
+ DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested"
+ " %N", diffie_hellman_group_names, old_dh_group,
+ diffie_hellman_group_names, this->dh_group);
+ if (!this->connection->check_dh_group(this->connection,
+ this->dh_group))
+ {
+ SIG(IKE_UP_FAILED, "requested DH group %N not "
+ "acceptable, giving up", diffie_hellman_group_names,
+ this->dh_group);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+
+ this->ike_sa->reset(this->ike_sa);
+
+ iterator->destroy(iterator);
+ return NEED_MORE;
+ }
+ default:
+ {
+ if (type < 16383)
+ {
+ SIG(IKE_UP_FAILED, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ /* check if we have everything */
+ if (this->proposal == NULL ||
+ this->other_nonce.len == 0 || this->my_nonce.len == 0)
+ {
+ SIG(IKE_UP_FAILED, "peers proposal selection invalid");
+ return FAILED;
+ }
+
+ if (this->diffie_hellman == NULL ||
+ this->diffie_hellman->get_shared_secret(this->diffie_hellman,
+ &secret) != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "peers DH group selection invalid");
+ return FAILED;
+ }
+
+ /* Apply SPI if we are rekeying */
+ if (this->old_sa)
+ {
+ ike_sa_id_t *id;
+ prf_t *prf, *child_prf;
+
+ id = this->ike_sa->get_id(this->ike_sa);
+ id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
+
+ /* setup crypto keys for the rekeyed SA */
+ prf = this->old_sa->get_prf(this->old_sa);
+ child_prf = this->old_sa->get_child_prf(this->old_sa);
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->my_nonce, this->other_nonce,
+ TRUE, child_prf, prf);
+ }
+ else
+ {
+ /* setup crypto keys for a new SA */
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->my_nonce, this->other_nonce,
+ TRUE, NULL, NULL);
+ }
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "key derivation failed");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_init_t *this)
+{
+ return IKE_INIT;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->diffie_hellman);
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+
+ this->ike_sa = ike_sa;
+ this->proposal = NULL;
+ this->diffie_hellman = diffie_hellman_create(this->dh_group);
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_init_t *this)
+{
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->diffie_hellman);
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
+{
+ private_ike_init_t *this = malloc_thing(private_ike_init_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->dh_group = MODP_NONE;
+ this->diffie_hellman = NULL;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->proposal = NULL;
+ this->connection = NULL;
+ this->old_sa = old_sa;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h
new file mode 100644
index 000000000..290cc46e8
--- /dev/null
+++ b/src/charon/sa/tasks/ike_init.h
@@ -0,0 +1,60 @@
+/**
+ * @file ike_init.h
+ *
+ * @brief Interface ike_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_INIT_H_
+#define IKE_INIT_H_
+
+typedef struct ike_init_t ike_init_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type IKE_INIT, creates an IKE_SA without authentication.
+ *
+ * The authentication of is handle in the ike_auth and/or ike_auth_eap task.
+ *
+ * @b Constructors:
+ * - ike_init_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_init_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new IKE_INIT task.
+ *
+ * @param ike_sa IKE_SA this task works for (new one when rekeying)
+ * @param initiator TRUE if thask is the original initator
+ * @param old_sa old IKE_SA when we are rekeying
+ * @return ike_init task to handle by the task_manager
+ */
+ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa);
+
+#endif /* IKE_INIT_H_ */
diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c
new file mode 100644
index 000000000..a25b6e0d7
--- /dev/null
+++ b/src/charon/sa/tasks/ike_natd.c
@@ -0,0 +1,378 @@
+/**
+ * @file ike_natd.c
+ *
+ * @brief Implementation of the ike_natd task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_natd.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/notify_payload.h>
+
+
+typedef struct private_ike_natd_t private_ike_natd_t;
+
+/**
+ * Private members of a ike_natd_t task.
+ */
+struct private_ike_natd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_natd_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Hasher used to build NAT detection hashes
+ */
+ hasher_t *hasher;
+
+ /**
+ * Did we process any NAT detection notifys for a source address?
+ */
+ bool src_seen;
+
+ /**
+ * Did we process any NAT detection notifys for a destination address?
+ */
+ bool dst_seen;
+
+ /**
+ * Have we found a matching source address NAT hash?
+ */
+ bool src_matched;
+
+ /**
+ * Have we found a matching destination address NAT hash?
+ */
+ bool dst_matched;
+};
+
+
+/**
+ * Build NAT detection hash for a host
+ */
+static chunk_t generate_natd_hash(private_ike_natd_t *this,
+ ike_sa_id_t *ike_sa_id, host_t *host)
+{
+ chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk;
+ chunk_t natd_hash;
+ u_int64_t spi_i, spi_r;
+ u_int16_t port;
+
+ /* prepare all requred chunks */
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi_i_chunk.ptr = (void*)&spi_i;
+ spi_i_chunk.len = sizeof(spi_i);
+ spi_r_chunk.ptr = (void*)&spi_r;
+ spi_r_chunk.len = sizeof(spi_r);
+ port = htons(host->get_port(host));
+ port_chunk.ptr = (void*)&port;
+ port_chunk.len = sizeof(port);
+ addr_chunk = host->get_address(host);
+ DBG2(DBG_IKE, "using SPI %J", ike_sa_id);
+
+ /* natd_hash = SHA1( spi_i | spi_r | address | port ) */
+ natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk);
+ this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash);
+ DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
+ DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
+
+ chunk_free(&natd_chunk);
+ return natd_hash;
+}
+
+/**
+ * Build a NAT detection notify payload.
+ */
+static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
+ notify_type_t type, host_t *host)
+{
+ chunk_t hash;
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ notify = notify_payload_create();
+ notify->set_notify_type(notify, type);
+ hash = generate_natd_hash(this, ike_sa_id, host);
+ notify->set_notification_data(notify, hash);
+ chunk_free(&hash);
+
+ return notify;
+}
+
+/**
+ * read notifys from message and evaluate them
+ */
+static void process_payloads(private_ike_natd_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ notify_payload_t *notify;
+ chunk_t hash, src_hash, dst_hash;
+ ike_sa_id_t *ike_sa_id;
+ host_t *me, *other;
+
+ /* Precompute NAT-D hashes for incoming NAT notify comparison */
+ ike_sa_id = message->get_ike_sa_id(message);
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ dst_hash = generate_natd_hash(this, ike_sa_id, me);
+ src_hash = generate_natd_hash(this, ike_sa_id, other);
+
+ DBG2(DBG_IKE, "precalculated src_hash %B", &src_hash);
+ DBG2(DBG_IKE, "precalculated dst_hash %B", &dst_hash);
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) != NOTIFY)
+ {
+ continue;
+ }
+ notify = (notify_payload_t*)payload;
+ switch (notify->get_notify_type(notify))
+ {
+ case NAT_DETECTION_DESTINATION_IP:
+ {
+ this->dst_seen = TRUE;
+ if (!this->dst_matched)
+ {
+ hash = notify->get_notification_data(notify);
+ DBG2(DBG_IKE, "received dst_hash %B", &hash);
+ if (chunk_equals(hash, dst_hash))
+ {
+ this->dst_matched = TRUE;
+ }
+ }
+ break;
+ }
+ case NAT_DETECTION_SOURCE_IP:
+ {
+ this->src_seen = TRUE;
+ if (!this->src_matched)
+ {
+ hash = notify->get_notification_data(notify);
+ DBG2(DBG_IKE, "received src_hash %B", &hash);
+ if (chunk_equals(hash, src_hash))
+ {
+ this->src_matched = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ chunk_free(&src_hash);
+ chunk_free(&dst_hash);
+
+ if (this->src_seen && this->dst_seen)
+ {
+ if (!this->dst_matched)
+ {
+ this->ike_sa->enable_natt(this->ike_sa, TRUE);
+ }
+ if (!this->src_matched)
+ {
+ this->ike_sa->enable_natt(this->ike_sa, FALSE);
+ }
+ }
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_natd_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ if (this->ike_sa->is_natt_enabled(this->ike_sa))
+ {
+ host_t *me, *other;
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ me->set_port(me, IKEV2_NATT_PORT);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ other->set_port(other, IKEV2_NATT_PORT);
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_natd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ linked_list_t *list;
+ host_t *host;
+
+ /* include one notify if our address is defined, all addresses otherwise */
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ if (host->is_anyaddr(host))
+ {
+ /* TODO: we could get the src address from netlink!? */
+ list = charon->socket->create_local_address_list(charon->socket);
+ while (list->remove_first(list, (void**)&host) == SUCCESS)
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ host->destroy(host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ list->destroy(list);
+ }
+ else
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_natd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ host_t *me, *other;
+ iterator_t *iterator;
+ u_int count;
+
+ /* when only one payload is in the message, an error occured.
+ * TODO: find a better hack */
+ iterator = message->get_payload_iterator(message);
+ count = iterator->get_count(iterator);
+ iterator->destroy(iterator);
+ if (count < 3)
+ {
+ return NEED_MORE;
+ }
+
+ if (this->src_seen && this->dst_seen)
+ {
+ /* initiator seems to support NAT detection, add response */
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
+ message->add_payload(message, (payload_t*)notify);
+
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_natd_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_natd_t *this)
+{
+ return IKE_NATD;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_natd_t *this)
+{
+ this->hasher->destroy(this->hasher);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_natd_t *this = malloc_thing(private_ike_natd_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->hasher = hasher_create(HASH_SHA1);
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h
new file mode 100644
index 000000000..8d0cb58b4
--- /dev/null
+++ b/src/charon/sa/tasks/ike_natd.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_natd.h
+ *
+ * @brief Interface ike_natd_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_NATD_H_
+#define IKE_NATD_H_
+
+typedef struct ike_natd_t ike_natd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange.
+ *
+ * @b Constructors:
+ * - ike_natd_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_natd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_natd task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_natd task to handle by the task_manager
+ */
+ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_NATD_H_ */
diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c
new file mode 100644
index 000000000..bbbd310bc
--- /dev/null
+++ b/src/charon/sa/tasks/ike_rekey.c
@@ -0,0 +1,232 @@
+/**
+ * @file ike_rekey.c
+ *
+ * @brief Implementation of the ike_rekey task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_rekey.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <sa/tasks/ike_init.h>
+#include <queues/jobs/delete_ike_sa_job.h>
+
+
+typedef struct private_ike_rekey_t private_ike_rekey_t;
+
+/**
+ * Private members of a ike_rekey_t task.
+ */
+struct private_ike_rekey_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_rekey_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * New IKE_SA which replaces the current one
+ */
+ ike_sa_t *new_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * the IKE_INIT task which is reused to simplify rekeying
+ */
+ ike_init_t *ike_init;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_rekey_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ ike_sa_id_t *id;
+
+ id = ike_sa_id_create(0, 0, TRUE);
+ this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+ id->destroy(id);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ this->new_sa->set_connection(this->new_sa, connection);
+ this->new_sa->set_policy(this->new_sa, policy);
+
+ this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
+ this->ike_init->task.build(&this->ike_init->task, message);
+
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_rekey_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ ike_sa_id_t *id;
+
+ id = ike_sa_id_create(0, 0, FALSE);
+ this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+ id->destroy(id);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ this->new_sa->set_connection(this->new_sa, connection);
+ this->new_sa->set_policy(this->new_sa, policy);
+
+ this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa);
+ this->ike_init->task.process(&this->ike_init->task, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_rekey_t *this, message_t *message)
+{
+ if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
+ {
+ return SUCCESS;
+ }
+
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+ this->new_sa->inherit(this->new_sa, this->ike_sa);
+ this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ this->new_sa = NULL;
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_rekey_t *this, message_t *message)
+{
+ job_t *job;
+
+ if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED)
+ {
+ return SUCCESS;
+ }
+
+ this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
+ this->new_sa->inherit(this->new_sa, this->ike_sa);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ this->new_sa = NULL;
+
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa),
+ TRUE);
+
+ charon->job_queue->add(charon->job_queue, job);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_rekey_t *this)
+{
+ return IKE_REKEY;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa)
+{
+ if (this->ike_init)
+ {
+ this->ike_init->task.destroy(&this->ike_init->task);
+ }
+ if (this->new_sa)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ this->new_sa);
+ }
+
+ this->ike_sa = ike_sa;
+ this->new_sa = NULL;
+ this->ike_init = NULL;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_rekey_t *this)
+{
+ if (this->ike_init)
+ {
+ this->ike_init->task.destroy(&this->ike_init->task);
+ }
+ if (this->new_sa)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ this->new_sa);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->new_sa = NULL;
+ this->ike_init = NULL;
+ this->initiator = initiator;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h
new file mode 100644
index 000000000..f1caf5758
--- /dev/null
+++ b/src/charon/sa/tasks/ike_rekey.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_rekey.h
+ *
+ * @brief Interface ike_rekey_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef IKE_REKEY_H_
+#define IKE_REKEY_H_
+
+typedef struct ike_rekey_t ike_rekey_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type IKE_REKEY, rekey an established IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_rekey_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_rekey_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new IKE_REKEY task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE for initiator, FALSE for responder
+ * @return IKE_REKEY task to handle by the task_manager
+ */
+ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_REKEY_H_ */
diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c
new file mode 100644
index 000000000..68d8ebf0c
--- /dev/null
+++ b/src/charon/sa/tasks/task.c
@@ -0,0 +1,38 @@
+/**
+ * @file task.c
+ *
+ * @brief Enum values for task types
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "task.h"
+
+ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
+ "IKE_INIT",
+ "IKE_NATD",
+ "IKE_AUTHENTICATE",
+ "IKE_CERT",
+ "IKE_CONFIG",
+ "IKE_DPD",
+ "IKE_REKEY",
+ "IKE_DELETE",
+ "IKE_DEADPEER",
+ "CHILD_CREATE",
+ "CHILD_DELETE",
+ "CHILD_REKEY",
+);
diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h
new file mode 100644
index 000000000..128d7db4a
--- /dev/null
+++ b/src/charon/sa/tasks/task.h
@@ -0,0 +1,151 @@
+/**
+ * @file task.h
+ *
+ * @brief Interface task_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#ifndef TASK_H_
+#define TASK_H_
+
+typedef enum task_type_t task_type_t;
+typedef struct task_t task_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <encoding/message.h>
+
+/**
+ * @brief Different kinds of tasks.
+ *
+ * @ingroup tasks
+ */
+enum task_type_t {
+ /** establish an unauthenticated IKE_SA */
+ IKE_INIT,
+ /** detect NAT situation */
+ IKE_NATD,
+ /** authenticate the initiated IKE_SA */
+ IKE_AUTHENTICATE,
+ /** exchange certificates and requests */
+ IKE_CERT,
+ /** Configuration payloads, virtual IP and such */
+ IKE_CONFIG,
+ /** DPD detection */
+ IKE_DEADPEER,
+ /** rekey an IKE_SA */
+ IKE_REKEY,
+ /** delete an IKE_SA */
+ IKE_DELETE,
+ /** liveness check */
+ IKE_DPD,
+ /** establish a CHILD_SA within an IKE_SA */
+ CHILD_CREATE,
+ /** delete an established CHILD_SA */
+ CHILD_DELETE,
+ /** rekey an CHILD_SA */
+ CHILD_REKEY,
+};
+
+/**
+ * enum names for task_type_t.
+ */
+extern enum_name_t *task_type_names;
+
+/**
+ * @brief Interface for a task, an operation handled within exchanges.
+ *
+ * A task is an elemantary operation. It may be handled by a single or by
+ * multiple exchanges. An exchange may even complete multiple tasks.
+ * A task has a build() and an process() operation. The build() operation
+ * creates payloads and adds it to the message. The process() operation
+ * inspects a message and handles its payloads. An initiator of an exchange
+ * first calls build() to build the request, and processes the response message
+ * with the process() method.
+ * A responder does the opposite; it calls process() first to handle an incoming
+ * request and secondly calls build() to build an appropriate response.
+ * Both methods return either SUCCESS, NEED_MORE or FAILED. A SUCCESS indicates
+ * that the task completed, even when the task completed unsuccesfully. The
+ * manager then removes the task from the list. A NEED_MORE is returned when
+ * the task needs further build()/process() calls to complete, the manager
+ * leaves the taks in the queue. A returned FAILED indicates a critical failure.
+ * The manager closes the IKE_SA whenever a task returns FAILED.
+ *
+ * @b Constructors:
+ * - None, use implementations specific constructors
+ *
+ * @ingroup tasks
+ */
+struct task_t {
+
+ /**
+ * @brief Build a request or response message for this task.
+ *
+ * @param this calling object
+ * @param message message to add payloads to
+ * @return
+ * - FAILED if a critical error occured
+ * - NEED_MORE if another call to build/process needed
+ * - SUCCESS if task completed
+ */
+ status_t (*build) (task_t *this, message_t *message);
+
+ /**
+ * @brief Process a request or response message for this task.
+ *
+ * @param this calling object
+ * @param message message to read payloads from
+ * @return
+ * - FAILED if a critical error occured
+ * - NEED_MORE if another call to build/process needed
+ * - SUCCESS if task completed
+ */
+ status_t (*process) (task_t *this, message_t *message);
+
+ /**
+ * @brief Get the type of the task implementation.
+ *
+ * @param this calling object
+ */
+ task_type_t (*get_type) (task_t *this);
+
+ /**
+ * @brief Migrate a task to a new IKE_SA.
+ *
+ * After migrating a task, it goes back to a state where it can be
+ * used again to initate an exchange. This is useful when a task
+ * has to get migrated to a new IKE_SA.
+ * A special usage is when a INVALID_KE_PAYLOAD is received. A call
+ * to reset resets the task, but uses another DH group for the next
+ * try.
+ * The ike_sa is the new IKE_SA this task belongs to and operates on.
+ *
+ * @param this calling object
+ * @param ike_sa new IKE_SA this task works for
+ */
+ void (*migrate) (task_t *this, ike_sa_t *ike_sa);
+
+ /**
+ * @brief Destroys a task_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (task_t *this);
+};
+
+#endif /* TASK_H_ */
diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c
deleted file mode 100644
index 67e4d782b..000000000
--- a/src/charon/sa/transactions/create_child_sa.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/**
- * @file create_child_sa.c
- *
- * @brief Implementation of create_child_sa_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "create_child_sa.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <encoding/payloads/ts_payload.h>
-#include <sa/transactions/delete_child_sa.h>
-#include <utils/randomizer.h>
-
-
-typedef struct private_create_child_sa_t private_create_child_sa_t;
-
-/**
- * Private members of a create_child_sa_t object..
- */
-struct private_create_child_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- create_child_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * initiators inbound SPI of the CHILD_SA which gets rekeyed
- */
- u_int32_t rekey_spi;
-
- /**
- * reqid to use for new CHILD_SA
- */
- u_int32_t reqid;
-
- /**
- * policy definition used
- */
- policy_t *policy;
-
- /**
- * Negotiated proposal used for CHILD_SA
- */
- proposal_t *proposal;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * lower of the nonces of a simultaneus rekeying request
- */
- chunk_t nonce_s;
-
- /**
- * Negotiated traffic selectors for initiator
- */
- linked_list_t *tsi;
-
- /**
- * Negotiated traffic selectors for responder
- */
- linked_list_t *tsr;
-
- /**
- * CHILD_SA created by this transaction
- */
- child_sa_t *child_sa;
-
- /**
- * CHILD_SA rekeyed if we are rekeying
- */
- child_sa_t *rekeyed_sa;
-
- /**
- * mode of the CHILD_SA to create: transport/tunnel
- */
- mode_t mode;
-
- /**
- * Have we lost the simultaneous rekeying nonce compare?
- */
- bool lost;
-
- /**
- * source of randomness
- */
- randomizer_t *randomizer;
-
- /**
- * signal to emit when transaction fails. As this transaction is used
- * for CHILD_SA creation AND rekeying, we must emit different signals.
- */
- signal_t failsig;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_create_child_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_create_child_sa_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of create_child_sa_t.set_policy.
- */
-static void set_policy(private_create_child_sa_t *this, policy_t *policy)
-{
- this->policy = policy;
-}
-
-/**
- * Implementation of create_child_sa_t.set_reqid.
- */
-static void set_reqid(private_create_child_sa_t *this, u_int32_t reqid)
-{
- this->reqid = reqid;
-}
-
-/**
- * Implementation of create_child_sa_t.rekeys_child.
- */
-static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa)
-{
- this->rekeyed_sa = child_sa;
- this->failsig = CHILD_REKEY_FAILED;
-}
-
-/**
- * Implementation of create_child_sa_t.cancel.
- */
-static void cancel(private_create_child_sa_t *this)
-{
- this->rekeyed_sa = NULL;
- this->lost = TRUE;
-}
-
-/**
- * Build a notify message.
- */
-static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message)
-{
- notify_payload_t *notify;
-
- if (flush_message)
- {
- payload_t *payload;
- iterator_t *iterator = message->get_payload_iterator(message);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- payload->destroy(payload);
- iterator->remove(iterator);
- }
- iterator->destroy(iterator);
- }
-
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- notify->set_notification_data(notify, data);
- message->add_payload(message, (payload_t*)notify);
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_create_child_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- identification_t *my_id, *other_id;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- /* check if we are not already rekeying */
- if (this->rekeyed_sa)
- {
- SIG(CHILD_REKEY_START, "rekeying CHILD_SA");
-
- switch (this->rekeyed_sa->get_state(this->rekeyed_sa))
- {
- case CHILD_REKEYING:
- SIG(CHILD_REKEY_FAILED,
- "rekeying a CHILD_SA which is already rekeying, aborted");
- return FAILED;
- case CHILD_DELETING:
- SIG(CHILD_REKEY_FAILED,
- "rekeying a CHILD_SA which is deleting, aborted");
- return FAILED;
- default:
- break;
- }
- this->rekeyed_sa->set_state(this->rekeyed_sa, CHILD_REKEYING);
- }
- else
- {
- SIG(CHILD_UP_START, "creating CHILD_SA");
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, CREATE_CHILD_SA);
- request->set_request(request, TRUE);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- *result = request;
- this->message = request;
-
- { /* build SA payload */
- sa_payload_t *sa_payload;
- linked_list_t *proposals;
- bool use_natt;
-
- /* get a policy, if we are rekeying */
- if (this->rekeyed_sa)
- {
- linked_list_t *my_ts, *other_ts;
- identification_t *my_id, *other_id;
-
- my_ts = this->rekeyed_sa->get_my_traffic_selectors(this->rekeyed_sa);
- other_ts = this->rekeyed_sa->get_other_traffic_selectors(this->rekeyed_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- this->policy = charon->policies->get_policy(charon->policies,
- my_id, other_id,
- my_ts, other_ts,
- me, other);
-
- this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa);
-
- if (this->policy == NULL)
- {
- SIG(IKE_REKEY_FAILED, "no policy found to rekey "
- "CHILD_SA with reqid %d", this->reqid);
- return FAILED;
- }
- }
-
- proposals = this->policy->get_proposals(this->policy);
- use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- this->policy->get_soft_lifetime(this->policy),
- this->policy->get_hard_lifetime(this->policy),
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- use_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
- if (this->child_sa->alloc(this->child_sa, proposals) != SUCCESS)
- {
- SIG(this->failsig, "could not install CHILD_SA, CHILD_SA creation failed");
- return FAILED;
- }
- sa_payload = sa_payload_create_from_proposal_list(proposals);
- proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- /* notify for transport/BEET mode, we propose it
- * independent of the traffic selectors */
- switch (this->policy->get_mode(this->policy))
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, chunk_empty, request, FALSE);
- }
- break;
- case MODE_BEET:
- build_notify(USE_BEET_MODE, chunk_empty, request, FALSE);
- break;
- }
-
- { /* build the NONCE payload for us (initiator) */
- nonce_payload_t *nonce_payload;
-
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_i) != SUCCESS)
- {
- SIG(this->failsig, "could not create nonce, CHILD_SA creation failed");
- return FAILED;
- }
- nonce_payload = nonce_payload_create();
- nonce_payload->set_nonce(nonce_payload, this->nonce_i);
- request->add_payload(request, (payload_t*)nonce_payload);
- }
-
- { /* build TSi payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_my_traffic_selectors(this->policy, me);
- ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- { /* build TSr payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_other_traffic_selectors(this->policy, other);
- ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- if (this->rekeyed_sa)
- { /* add REKEY_SA notify if we are rekeying */
- notify_payload_t *notify;
- protocol_id_t protocol;
-
- protocol = this->rekeyed_sa->get_protocol(this->rekeyed_sa);
- notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
- notify->set_spi(notify, this->rekeyed_sa->get_spi(this->rekeyed_sa, TRUE));
- request->add_payload(request, (payload_t*)notify);
-
- /* register us as rekeying to detect multiple rekeying */
- this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa,
- &this->public.transaction);
- }
-
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
-
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifys
- */
-static status_t process_notifys(private_create_child_sa_t *this, notify_payload_t *notify_payload)
-{
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- case SINGLE_PAIR_REQUIRED:
- {
- SIG(this->failsig, "received a SINGLE_PAIR_REQUIRED notify");
- return FAILED;
- }
- case TS_UNACCEPTABLE:
- {
- SIG(this->failsig, "received TS_UNACCEPTABLE notify");
- return FAILED;
- }
- case NO_PROPOSAL_CHOSEN:
- {
- SIG(this->failsig, "received NO_PROPOSAL_CHOSEN notify");
- return FAILED;
- }
- case USE_TRANSPORT_MODE:
- {
- this->mode = MODE_TRANSPORT;
- return SUCCESS;
- }
- case USE_BEET_MODE:
- {
- this->mode = MODE_BEET;
- return SUCCESS;
- }
- case REKEY_SA:
- {
- u_int32_t spi;
- protocol_id_t protocol;
-
- protocol = notify_payload->get_protocol_id(notify_payload);
- switch (protocol)
- {
- case PROTO_AH:
- case PROTO_ESP:
- spi = notify_payload->get_spi(notify_payload);
- this->rekeyed_sa = this->ike_sa->get_child_sa(this->ike_sa,
- protocol, spi,
- FALSE);
- this->failsig = CHILD_REKEY_FAILED;
- break;
- default:
- break;
- }
- return SUCCESS;
- }
- default:
- {
- if (notify_type < 16383)
- {
- SIG(this->failsig, "received %N notify error, CHILD_SA "
- "creation failed", notify_type_names, notify_type);
- return FAILED;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Check a list of traffic selectors if any selector belongs to host
- */
-static bool ts_list_is_host(linked_list_t *list, host_t *host)
-{
- traffic_selector_t *ts;
- bool is_host = TRUE;
- iterator_t *iterator = list->create_iterator(list, TRUE);
-
- while (is_host && iterator->iterate(iterator, (void**)&ts))
- {
- is_host = is_host && ts->is_host(ts, host);
- }
- iterator->destroy(iterator);
- return is_host;
-}
-
-/**
- * Install a CHILD_SA for usage
- */
-static status_t install_child_sa(private_create_child_sa_t *this, bool initiator)
-{
- prf_plus_t *prf_plus;
- chunk_t seed;
- status_t status;
-
- seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len);
- memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len);
- memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len);
- prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
- chunk_free(&seed);
-
- if (initiator)
- {
- status = this->child_sa->update(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- else
- {
- status = this->child_sa->add(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- prf_plus->destroy(prf_plus);
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
- if (initiator)
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsi,
- this->tsr, this->mode);
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsr,
- this->tsi, this->mode);
- }
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
-
- /* add to IKE_SA, and remove from transaction */
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
- this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
- this->child_sa = NULL;
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_create_child_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- identification_t *my_id, *other_id;
- message_t *response;
- status_t status;
- iterator_t *payloads;
- payload_t *payload;
- sa_payload_t *sa_request = NULL;
- nonce_payload_t *nonce_request = NULL;
- ts_payload_t *tsi_request = NULL;
- ts_payload_t *tsr_request = NULL;
- nonce_payload_t *nonce_response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, CREATE_CHILD_SA);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != CREATE_CHILD_SA)
- {
- DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted");
- return FAILED;
- }
-
- /* we do not allow the creation of new CHILDren/rekeying when IKE_SA is
- * rekeying */
- if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
- this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- build_notify(NO_ADDITIONAL_SAS, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "unable to create new CHILD_SAs, as rekeying in progress");
- return FAILED;
- }
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_request = (nonce_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_request = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_request = (ts_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- {
- u_int8_t dh_buffer[] = {0x00, 0x00}; /* MODP_NONE */
- chunk_t group = chunk_from_buf(dh_buffer);
- build_notify(INVALID_KE_PAYLOAD, group, response, TRUE);
- DBG1(DBG_IKE, "CREATE_CHILD_SA used PFS, sending INVALID_KE_PAYLOAD");
- return FAILED;
- }
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* after processing the notify payloads, we know if this transaction is
- * for rekeying or for a new CHILD_SA. We can emit the signals now. */
- if (this->rekeyed_sa)
- {
- SIG(CHILD_REKEY_START, "rekeying CHILD_SA");
- }
- else
- {
- SIG(CHILD_UP_START, "creating CHILD_SA");
- }
-
- /* check if we have all payloads */
- if (!(sa_request && nonce_request && tsi_request && tsr_request))
- {
- build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE);
- SIG(this->failsig, "request message incomplete, no CHILD_SA created");
- return FAILED;
- }
-
- { /* process nonce payload */
- this->nonce_i = nonce_request->get_nonce(nonce_request);
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_r) != SUCCESS)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- SIG(this->failsig, "nonce generation failed, no CHILD_SA created");
- return FAILED;
- }
- nonce_response = nonce_payload_create();
- nonce_response->set_nonce(nonce_response, this->nonce_r);
- }
-
- { /* get a policy and process traffic selectors */
- identification_t *my_id, *other_id;
- linked_list_t *my_ts, *other_ts;
-
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- my_ts = tsr_request->get_traffic_selectors(tsr_request);
- other_ts = tsi_request->get_traffic_selectors(tsi_request);
-
- this->policy = charon->policies->get_policy(charon->policies,
- my_id, other_id,
- my_ts, other_ts,
- me, other);
- if (this->policy)
- {
- this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me);
- this->tsi = this->policy->select_other_traffic_selectors(this->policy, other_ts, other);
- }
- my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
- other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
-
- if (this->policy == NULL)
- {
- SIG(this->failsig, "no acceptable policy found, sending TS_UNACCEPTABLE notify");
- build_notify(TS_UNACCEPTABLE, chunk_empty, response, TRUE);
- return FAILED;
- }
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
- sa_payload_t *sa_response;
- ts_payload_t *ts_response;
- bool use_natt;
- u_int32_t soft_lifetime, hard_lifetime;
-
- sa_response = sa_payload_create();
- /* get proposals from request, and select one with ours */
- proposal_list = sa_request->get_proposals(sa_request);
- DBG2(DBG_IKE, "selecting proposals:");
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* do we have a proposal? */
- if (this->proposal == NULL)
- {
- SIG(this->failsig, "CHILD_SA proposals unacceptable, sending NO_PROPOSAL_CHOSEN notify");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- /* do we have traffic selectors? */
- else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0)
- {
- SIG(this->failsig, "CHILD_SA traffic selectors unacceptable, sending TS_UNACCEPTABLE notify");
- build_notify(TS_UNACCEPTABLE, chunk_empty, response, TRUE);
- return FAILED;
- }
- else
- { /* create child sa */
- if (this->rekeyed_sa)
- {
- this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa);
- }
- soft_lifetime = this->policy->get_soft_lifetime(this->policy);
- hard_lifetime = this->policy->get_hard_lifetime(this->policy);
- use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- soft_lifetime, hard_lifetime,
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- use_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
-
- /* check mode, and include notify into reply */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (!ts_list_is_host(this->tsi, other) ||
- !ts_list_is_host(this->tsr, me))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, chunk_empty, response, FALSE);
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- else
- {
- build_notify(USE_BEET_MODE, chunk_empty, response, FALSE);
- }
- break;
- }
-
- if (install_child_sa(this, FALSE) != SUCCESS)
- {
- SIG(this->failsig, "installing CHILD_SA failed, sending NO_PROPOSAL_CHOSEN notify");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- /* add proposal to sa payload */
- sa_response->add_proposal(sa_response, this->proposal);
- }
- response->add_payload(response, (payload_t*)sa_response);
-
- /* add nonce/ts payload after sa payload */
- response->add_payload(response, (payload_t *)nonce_response);
- ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
- response->add_payload(response, (payload_t*)ts_response);
- ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
- response->add_payload(response, (payload_t*)ts_response);
- }
- /* CHILD_SA successfully created. If another transaction is already rekeying
- * this SA, our lower nonce must be registered for a later nonce compare. */
- if (this->rekeyed_sa)
- {
- private_create_child_sa_t *other;
-
- other = (private_create_child_sa_t*)
- this->rekeyed_sa->get_rekeying_transaction(this->rekeyed_sa);
- if (other)
- {
- /* store our lower nonce in the simultaneus transaction, it
- * will later compare it against its nonces when it calls conclude().
- */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- other->nonce_s = chunk_clone(this->nonce_i);
- }
- else
- {
- other->nonce_s = chunk_clone(this->nonce_r);
- }
- }
- else
- {
- /* we only signal when no other transaction is rekeying */
- SIG(CHILD_REKEY_SUCCESS, "CHILD_SA rekeyed");
- }
- this->rekeyed_sa->set_state(this->rekeyed_sa, CHILD_REKEYING);
- }
- else
- {
- SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created",
- this->policy->get_name(this->policy));
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_create_child_sa_t *this, message_t *response,
- transaction_t **next)
-{
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- sa_payload_t *sa_payload = NULL;
- nonce_payload_t *nonce_payload = NULL;
- ts_payload_t *tsi_payload = NULL;
- ts_payload_t *tsr_payload = NULL;
- status_t status;
- child_sa_t *new_child = NULL;
- delete_child_sa_t *delete_child_sa;
-
- /* check message type */
- if (response->get_exchange_type(response) != CREATE_CHILD_SA)
- {
- SIG(this->failsig, "CREATE_CHILD_SA response of invalid type, aborting");
- return FAILED;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_payload = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_payload = (nonce_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_payload = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_payload = (ts_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (!(sa_payload && nonce_payload && tsi_payload && tsr_payload))
- {
- SIG(this->failsig, "response message incomplete, no CHILD_SA built");
- return FAILED;
- }
-
- { /* process NONCE payload */
- this->nonce_r = nonce_payload->get_nonce(nonce_payload);
- }
-
- { /* process traffic selectors for us */
- linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload);
- this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received, me);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process traffic selectors for other */
- linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload);
- this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received, other);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process sa payload */
- linked_list_t *proposal_list;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- /* we have to re-check here if other's selection is valid */
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* everything fine to create CHILD? */
- if (this->proposal == NULL ||
- this->tsi->get_count(this->tsi) == 0 ||
- this->tsr->get_count(this->tsr) == 0)
- {
- SIG(this->failsig, "CHILD_SA negotiation failed, no CHILD_SA built");
- return FAILED;
- }
-
- /* check mode if it is acceptable */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- /* TODO: we should close the CHILD_SA if negotiated
- * mode is not acceptable for us */
- if (!ts_list_is_host(this->tsi, me) ||
- !ts_list_is_host(this->tsr, other))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- break;
- }
-
- new_child = this->child_sa;
- if (install_child_sa(this, TRUE) != SUCCESS)
- {
- SIG(this->failsig, "installing CHILD_SA failed, no CHILD_SA built");
- return FAILED;
- }
- }
- /* CHILD_SA successfully created. If the other peer initiated rekeying
- * in the meantime, we detect this by comparing the rekeying_transaction
- * of the SA. If it changed, we are not alone. Then we must compare the nonces.
- * If no simultaneous rekeying is going on, we just initiate the delete of
- * the superseded SA. */
- if (this->rekeyed_sa)
- {
- /* rekeying finished, update SA status */
- this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL);
-
- if (this->nonce_s.ptr)
- { /* simlutaneous rekeying is going on, not so good */
- chunk_t this_lowest;
-
- /* first get our lowest nonce */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- this_lowest = this->nonce_i;
- }
- else
- {
- this_lowest = this->nonce_r;
- }
- /* then compare against other lowest nonce */
- if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
- min(this_lowest.len, this->nonce_s.len)) < 0)
- {
- DBG1(DBG_IKE, "detected simultaneous CHILD_SA rekeying, deleting ours");
- this->lost = TRUE;
- }
- else
- {
- DBG1(DBG_IKE, "detected simultaneous CHILD_SA rekeying, but ours is preferred");
- }
- }
- /* delete the old SA if we have won the rekeying nonce compare*/
- if (!this->lost)
- {
- delete_child_sa = delete_child_sa_create(this->ike_sa);
- delete_child_sa->set_child_sa(delete_child_sa, this->rekeyed_sa);
- *next = (transaction_t*)delete_child_sa;
- }
- /* we send a rekey SUCCESS signal in any case. If the other transaction
- * detected our transaction, it did not send a signal. We do it for it. */
- SIG(CHILD_REKEY_SUCCESS, "CHILD_SA rekeyed");
- }
- else
- {
- SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created",
- this->policy->get_name(this->policy));
- }
- if (this->lost)
- {
- /* we have lost simlutaneous rekeying, delete the CHILD_SA we just have created */
- delete_child_sa = delete_child_sa_create(this->ike_sa);
- delete_child_sa->set_child_sa(delete_child_sa, new_child);
- *next = (transaction_t*)delete_child_sa;
- }
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_create_child_sa_t *this)
-{
- DESTROY_IF(this->message);
- DESTROY_IF(this->proposal);
- DESTROY_IF(this->child_sa);
- DESTROY_IF(this->policy);
- if (this->tsi)
- {
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- }
- if (this->tsr)
- {
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- }
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- chunk_free(&this->nonce_s);
- this->randomizer->destroy(this->randomizer);
- free(this);
-}
-
-/*
- * Described in header.
- */
-create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa)
-{
- private_create_child_sa_t *this = malloc_thing(private_create_child_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.set_policy = (void(*)(create_child_sa_t*,policy_t*))set_policy;
- this->public.set_reqid = (void(*)(create_child_sa_t*,u_int32_t))set_reqid;
- this->public.rekeys_child = (void(*)(create_child_sa_t*,child_sa_t*))rekeys_child;
- this->public.cancel = (void(*)(create_child_sa_t*))cancel;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->rekey_spi = 0;
- this->reqid = 0;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->nonce_s = chunk_empty;
- this->child_sa = NULL;
- this->rekeyed_sa = NULL;
- this->lost = FALSE;
- this->proposal = NULL;
- this->policy = NULL;
- this->tsi = NULL;
- this->tsr = NULL;
- this->mode = MODE_TUNNEL;
- this->randomizer = randomizer_create();
- this->failsig = CHILD_UP_FAILED;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/create_child_sa.h b/src/charon/sa/transactions/create_child_sa.h
deleted file mode 100644
index 8ce72e11d..000000000
--- a/src/charon/sa/transactions/create_child_sa.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * @file create_child_sa.h
- *
- * @brief Interface of transaction create_child_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#ifndef CREATE_CHILD_SA_H_
-#define CREATE_CHILD_SA_H_
-
-typedef struct create_child_sa_t create_child_sa_t;
-
-#include <sa/transactions/transaction.h>
-#include <sa/ike_sa.h>
-#include <sa/child_sa.h>
-
-/**
- * @brief A transaction to create a new or rekey an existing CHILD_SA.
- *
- * If the CHILD_SA is intended to create a new CHILD_SA, set the policy
- * with set_policy(). If it is intended to rekey an existing CHILD_SA,
- * set the appropriate CHILD_SA with rekeys_child().
- *
- * Rekeying of an CHILD_SA works the same way as creating a new one,
- * but includes an additional REKEY_SA notify and deletes the old
- * one (in a separate transaction).
- *
- * __________ _________
- * Cyq \/ Czq
- * __________/\_________
- * detect __________ _________ detect
- * Czp \/ Czp
- * compare nonces, won __________/\_________ compare nonces, lost
- *
- * delete old __________
- * Dxq \__________
- * __________
- * __________/ Dxp
- * __________ delete created
- * __________/ Dzq
- * __________
- * Dzp \__________
- *
- *
- * @b Constructors:
- * - create_child_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct create_child_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the policy to use for creating a new CHILD_SA.
- *
- * @param this calling object
- * @param policy policy for CHILD_SA
- */
- void (*set_policy) (create_child_sa_t* this, policy_t *policy);
-
- /**
- * @brief Set the reqid used for CHILD_SA setup.
- *
- * If we acquire, we must use the same reqid as the
- * installed policy.
- *
- * @param this calling object
- * @param reqid reqid to use for the CHILD_SA
- */
- void (*set_reqid) (create_child_sa_t* this, u_int32_t reqid);
-
- /**
- * @brief Set the CHILD_SA which gets rekeyed by the new one.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*rekeys_child) (create_child_sa_t* this, child_sa_t *child_sa);
-
- /**
- * @brief Cancel the request.
- *
- * Cancelling the request will set a flag in the transaction. When
- * the response for the transaction is received, the created CHILD_SA
- * gets deleted afterwards.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*cancel) (create_child_sa_t* this);
-};
-
-/**
- * @brief Create a new transaction which creates/rekeys CHILD_SAs.
- *
- * @param ike_sa assigned IKE_SA
- * @return created create_child_sa transaction
- *
- * @ingroup transactions
- */
-create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa);
-
-#endif /* CREATE_CHILD_SA_H_ */
diff --git a/src/charon/sa/transactions/dead_peer_detection.c b/src/charon/sa/transactions/dead_peer_detection.c
deleted file mode 100644
index 390ce3401..000000000
--- a/src/charon/sa/transactions/dead_peer_detection.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/**
- * @file dead_peer_detection.c
- *
- * @brief Implementation of the dead_peer_detection transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "dead_peer_detection.h"
-
-#include <daemon.h>
-
-
-typedef struct private_dead_peer_detection_t private_dead_peer_detection_t;
-
-/**
- * Private members of a dead_peer_detection_t object..
- */
-struct private_dead_peer_detection_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- dead_peer_detection_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_dead_peer_detection_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_dead_peer_detection_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_dead_peer_detection_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, INFORMATIONAL);
- request->set_request(request, TRUE);
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_dead_peer_detection_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, INFORMATIONAL);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- return SUCCESS;
-}
-
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_dead_peer_detection_t *this, message_t *response,
- transaction_t **transaction)
-{
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_dead_peer_detection_t *this)
-{
- DESTROY_IF(this->message);
- free(this);
-}
-
-/*
- * Described in header.
- */
-dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa)
-{
- private_dead_peer_detection_t *this = malloc_thing(private_dead_peer_detection_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/dead_peer_detection.h b/src/charon/sa/transactions/dead_peer_detection.h
deleted file mode 100644
index 78d7b9b4f..000000000
--- a/src/charon/sa/transactions/dead_peer_detection.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * @file dead_peer_detection.h
- *
- * @brief Interface of transaction dead_peer_detection.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-
-#ifndef DEAD_PEER_DETECTION_H_
-#define DEAD_PEER_DETECTION_H_
-
-typedef struct dead_peer_detection_t dead_peer_detection_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction used to detect dead peers.
- *
- * In IKEv2, dead peer detection is done using empty
- * informational messages. These must be acknowledged.
- *
- * @ingroup transactions
- */
-struct dead_peer_detection_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-};
-
-/**
- * @brief Create a new transaction which detects dead peers.
- *
- * @param ike_sa assigned IKE_SA
- * @return created dead_peer_detection transaction
- *
- * @ingroup transactions
- */
-dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa);
-
-#endif /* DEAD_PEER_DETECTION_H_ */
diff --git a/src/charon/sa/transactions/delete_child_sa.c b/src/charon/sa/transactions/delete_child_sa.c
deleted file mode 100644
index 7ec332004..000000000
--- a/src/charon/sa/transactions/delete_child_sa.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/**
- * @file delete_child_sa.c
- *
- * @brief Implementation of the delete_child_sa transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "delete_child_sa.h"
-
-#include <daemon.h>
-#include <encoding/payloads/delete_payload.h>
-#include <sa/transactions/create_child_sa.h>
-
-
-typedef struct private_delete_child_sa_t private_delete_child_sa_t;
-
-/**
- * Private members of a delete_child_sa_t object..
- */
-struct private_delete_child_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- delete_child_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * CHILD SA to delete
- */
- child_sa_t *child_sa;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_delete_child_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_delete_child_sa_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of delete_child_sa_t.set_child_sa.
- */
-static void set_child_sa(private_delete_child_sa_t *this, child_sa_t *child_sa)
-{
- this->child_sa = child_sa;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_delete_child_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, INFORMATIONAL);
- request->set_request(request, TRUE);
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- *result = request;
- this->message = request;
-
- { /* add delete payload */
- delete_payload_t *delete_payload;
- protocol_id_t protocol;
- u_int32_t spi;
-
- protocol = this->child_sa->get_protocol(this->child_sa);
- spi = this->child_sa->get_spi(this->child_sa, TRUE);
- delete_payload = delete_payload_create(protocol);
-
- DBG1(DBG_IKE, "created DELETE payload for %N CHILD_SA with SPI 0x%x",
- protocol_id_names, protocol, htonl(spi));
- delete_payload->add_spi(delete_payload, spi);
- request->add_payload(request, (payload_t*)delete_payload);
- }
-
- this->child_sa->set_state(this->child_sa, CHILD_DELETING);
-
- return SUCCESS;
-}
-
-/**
- * process a delete payload
- */
-static status_t process_delete(private_delete_child_sa_t *this, delete_payload_t *delete_request, message_t *response)
-{
- protocol_id_t protocol;
- u_int32_t spi;
- iterator_t *iterator;
- delete_payload_t *delete_response = NULL;
-
- /* get requested CHILD */
- protocol = delete_request->get_protocol_id(delete_request);
- if (protocol != PROTO_ESP && protocol != PROTO_AH)
- {
- DBG1(DBG_IKE, "CHILD_SA delete response contained unexpected protocol");
- return FAILED;
- }
-
- /* prepare response payload */
- if (response)
- {
- delete_response = delete_payload_create(protocol);
- response->add_payload(response, (payload_t*)delete_response);
- }
-
- iterator = delete_request->create_spi_iterator(delete_request);
- while (iterator->iterate(iterator, (void**)&spi))
- {
- child_sa_t *child_sa;
-
- child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE);
-
- if (child_sa != NULL)
- {
- create_child_sa_t *rekey;
-
- child_sa->set_state(child_sa, CHILD_DELETING);
-
- DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, deleting",
- protocol_id_names, protocol, ntohl(spi));
-
- rekey = (create_child_sa_t*)child_sa->get_rekeying_transaction(child_sa);
- if (rekey)
- {
- /* we have received a delete for an SA which we are still rekeying.
- * this means we have lost the nonce comparison, and the rekeying
- * will fail. We set a flag in the transaction for this special case.
- */
- rekey->cancel(rekey);
- }
- /* delete it, with inbound spi */
- spi = child_sa->get_spi(child_sa, TRUE);
- this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
- /* add delete response to message, if we are responding */
- if (response)
- {
- delete_response->add_spi(delete_response, spi);
- }
- }
- else
- {
- DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, but no such SA",
- protocol_id_names, protocol, ntohl(spi));
- }
- }
- iterator->destroy(iterator);
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_delete_child_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- iterator_t *payloads;
- payload_t *payload;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, INFORMATIONAL);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- if (request->get_exchange_type(request) != INFORMATIONAL)
- {
- DBG1(DBG_IKE, "INFORMATIONAL response of invalid type, aborting");
- return FAILED;
- }
-
- /* we can't handle a delete for a CHILD when we are rekeying. There
- * is no proper solution for this. We send a empty informational response,
- * as described in ikev2-clarifications draft */
- if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
- this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- DBG1(DBG_IKE, "unable to delete CHILD_SA, as rekeying in progress");
- return FAILED;
- }
-
- /* iterate over all payloads */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case DELETE:
- {
- process_delete(this, (delete_payload_t*)payload, response);
- break;
- }
- default:
- {
- DBG2(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_delete_child_sa_t *this, message_t *response,
- transaction_t **transaction)
-{
- iterator_t *payloads;
- payload_t *payload;
-
- /* check message type */
- if (response->get_exchange_type(response) != INFORMATIONAL)
- {
- DBG1(DBG_IKE, "INFORMATIONAL response of invalid type, aborting");
- return FAILED;
- }
-
- /* iterate over all payloads */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case DELETE:
- {
- process_delete(this, (delete_payload_t*)payload, NULL);
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_delete_child_sa_t *this)
-{
- DESTROY_IF(this->message);
- free(this);
-}
-
-/*
- * Described in header.
- */
-delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa)
-{
- private_delete_child_sa_t *this = malloc_thing(private_delete_child_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* publics */
- this->public.set_child_sa = (void(*)(delete_child_sa_t*,child_sa_t*))set_child_sa;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/delete_child_sa.h b/src/charon/sa/transactions/delete_child_sa.h
deleted file mode 100644
index b4cd8ea7a..000000000
--- a/src/charon/sa/transactions/delete_child_sa.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @file delete_child_sa.h
- *
- * @brief Interface of transaction delete_child_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-
-#ifndef DELETE_CHILD_SA_H_
-#define DELETE_CHILD_SA_H_
-
-typedef struct delete_child_sa_t delete_child_sa_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-
-/**
- * @brief A transaction used to delete a CHILD_SA.
- *
- * @b Constructors:
- * - delete_child_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct delete_child_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the CHILD_SA to delete.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*set_child_sa) (delete_child_sa_t* this, child_sa_t *child_sa);
-};
-
-/**
- * @brief Create a new transaction which deletes a CHILD_SA.
- *
- * @param ike_sa assigned IKE_SA
- * @return created delete_child_sa transaction
- *
- * @ingroup transactions
- */
-delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa);
-
-#endif /* DELETE_CHILD_SA_H_ */
diff --git a/src/charon/sa/transactions/delete_ike_sa.c b/src/charon/sa/transactions/delete_ike_sa.c
deleted file mode 100644
index 26817bb6a..000000000
--- a/src/charon/sa/transactions/delete_ike_sa.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * @file delete_ike_sa.c
- *
- * @brief Implementation of the delete_ike_sa transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "delete_ike_sa.h"
-
-#include <daemon.h>
-#include <encoding/payloads/delete_payload.h>
-
-typedef struct private_delete_ike_sa_t private_delete_ike_sa_t;
-
-/**
- * Private members of a delete_ike_sa_t object..
- */
-struct private_delete_ike_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- delete_ike_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * is the IKE_SA redundant and gets deleted without further notification?
- */
- bool redundant;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_delete_ike_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_delete_ike_sa_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_delete_ike_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- delete_payload_t *delete_payload;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->redundant = this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING;
-
- if (!this->redundant)
- {
- SIG(IKE_DOWN_START, "deleting IKE_SA");
- }
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, INFORMATIONAL);
- request->set_request(request, TRUE);
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- delete_payload = delete_payload_create(PROTO_IKE);
- request->add_payload(request, (payload_t*)delete_payload);
-
- /* transit to state SA_DELETING */
- this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
-
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_delete_ike_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- iterator_t *payloads;
- payload_t *payload;
- delete_payload_t *delete_request = NULL;
-
- /* check if we already have built a response (retransmission)
- * this only happens in special simultanous transaction cases,
- * as we delete the IKE_SA after the response is sent. */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
- this->redundant = this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING;
-
- if (!this->redundant)
- {
- SIG(IKE_DOWN_START, "deleting IKE_SA");
- }
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, INFORMATIONAL);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != INFORMATIONAL)
- {
- if (!this->redundant)
- {
- SIG(IKE_DOWN_FAILED, "INFORMATIONAL response of invalid type, deleting IKE_SA");
- }
- return DESTROY_ME;
- }
-
- /* iterate over all payloads */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case DELETE:
- {
- delete_request = (delete_payload_t *)payload;
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (delete_request &&
- delete_request->get_protocol_id(delete_request) == PROTO_IKE)
- {
- DBG1(DBG_IKE, "DELETE request for IKE_SA received, deleting IKE_SA");
- }
- else
- {
- /* should not happen, as we preparsed this at transaction construction */
- DBG1(DBG_IKE, "received a weird DELETE request for IKE_SA, deleting anyway");
- }
- if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- /* if we are already deleting an IKE_SA, we do not destroy. We wait
- * until we get the response for our initiated delete. */
- return SUCCESS;
- }
- this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
- if (!this->redundant)
- {
- SIG(IKE_DOWN_SUCCESS, "IKE_SA deleted on request");
- }
- return DESTROY_ME;
-}
-
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_delete_ike_sa_t *this, message_t *response,
- transaction_t **transaction)
-{
- /* check message type */
- if (response->get_exchange_type(response) != INFORMATIONAL)
- {
- if (!this->redundant)
- {
- SIG(IKE_DOWN_FAILED, "INFORMATIONAL response of invalid type, deleting IKE_SA");
- }
- return DESTROY_ME;
- }
- /* this is only an acknowledge. We can't do anything here, but delete
- * the IKE_SA. */
- if (!this->redundant)
- {
- SIG(IKE_DOWN_SUCCESS, "IKE_SA deleted");
- }
- return DESTROY_ME;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_delete_ike_sa_t *this)
-{
- DESTROY_IF(this->message);
- free(this);
-}
-
-/*
- * Described in header.
- */
-delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa)
-{
- private_delete_ike_sa_t *this = malloc_thing(private_delete_ike_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->redundant = FALSE;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/delete_ike_sa.h b/src/charon/sa/transactions/delete_ike_sa.h
deleted file mode 100644
index 139e65ebb..000000000
--- a/src/charon/sa/transactions/delete_ike_sa.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * @file delete_ike_sa.h
- *
- * @brief Interface of transaction delete_ike_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-
-#ifndef DELETE_IKE_SA_H_
-#define DELETE_IKE_SA_H_
-
-typedef struct delete_ike_sa_t delete_ike_sa_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction used to delete the IKE_SA.
- *
- * Notation as follows:
- * Mx{D} means: Message, with message ID "x", containing a Delete payload
- *
- * The clarifcation Document says in 5.8, that a IKE_SA delete should not
- * be acknowledged with the same delete. This only makes sense for CHILD_SAs,
- * as they are paired. IKE_SAs are not, there is only one for both ends.
- *
- * Normal case:
- * ----------------
- * Mx{D} -->
- * <-- Mx{}
- * Delete request is sent, and we wait for the acknowledge.
- *
- * Special case 1:
- * ---------------
- * Mx{D} -->
- * <-- My{D}
- * My{} -->
- * <-- Mx{}
- * Both initate a delete at the same time. We ack the delete, but wait for
- * our delete to be acknowledged.
- *
- * @b Constructors:
- * - delete_ike_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct delete_ike_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-};
-
-/**
- * @brief Create a new transaction which deletes the IKE_SA.
- *
- * @param ike_sa assigned IKE_SA
- * @return created delete_ike_sa transaction
- *
- * @ingroup transactions
- */
-delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa);
-
-#endif /* DELETE_IKE_SA_H_ */
diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c
deleted file mode 100644
index bf7fd6d12..000000000
--- a/src/charon/sa/transactions/ike_auth.c
+++ /dev/null
@@ -1,1562 +0,0 @@
-/**
- * @file ike_auth.c
- *
- * @brief Implementation of ike_auth_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005-2006 Martin Willi
- * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "ike_auth.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/id_payload.h>
-#include <encoding/payloads/cert_payload.h>
-#include <encoding/payloads/certreq_payload.h>
-#include <encoding/payloads/auth_payload.h>
-#include <encoding/payloads/ts_payload.h>
-#include <sa/authenticators/authenticator.h>
-#include <sa/authenticators/eap_authenticator.h>
-#include <sa/child_sa.h>
-
-
-typedef struct private_ike_auth_t private_ike_auth_t;
-
-/**
- * Private members of a ike_auth_t object..
- */
-struct private_ike_auth_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- ike_auth_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * encoded request message of ike_sa_init transaction
- */
- chunk_t init_request;
-
- /**
- * encoded response message of ike_sa_init transaction
- */
- chunk_t init_response;
-
- /**
- * connection definition used for IKE_SA setup
- */
- connection_t *connection;
-
- /**
- * policy definition used CHILD_SA creation
- */
- policy_t *policy;
-
- /**
- * Negotiated proposal used for CHILD_SA
- */
- proposal_t *proposal;
-
- /**
- * Negotiated traffic selectors for initiator
- */
- linked_list_t *tsi;
-
- /**
- * Negotiated traffic selectors for responder
- */
- linked_list_t *tsr;
-
- /**
- * CHILD_SA created along with IKE_AUTH
- */
- child_sa_t *child_sa;
-
- /**
- * did other peer create a CHILD_SA?
- */
- bool build_child;
-
- /**
- * reqid to use for CHILD_SA setup
- */
- u_int32_t reqid;
-
- /**
- * List of CA certificates the other peer trusts
- */
- linked_list_t *cacerts;
-
- /**
- * EAP uses this authentication, which is passed along multiple ike_auths
- */
- eap_authenticator_t *eap_auth;
-
- /**
- * if the client receives a EAP request, it is stored here for later use
- */
- eap_payload_t *eap_next;
-
- /**
- * set to TRUE if authentication should be done with EAP only
- */
- bool eap_only;
-
- /**
- * has the other peer been authenticated yet?
- */
- bool peer_authenticated;
-
- /**
- * mode the CHILD_SA uses: tranport, tunnel, BEET
- */
- mode_t mode;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_ike_auth_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_ike_auth_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of transaction_t.set_config.
- */
-static void set_config(private_ike_auth_t *this,
- connection_t *connection, policy_t *policy)
-{
- this->connection = connection;
- this->policy = policy;
-}
-
-/**
- * Implementation of transaction_t.set_reqid.
- */
-static void set_reqid(private_ike_auth_t *this, u_int32_t reqid)
-{
- this->reqid = reqid;
-}
-
-/**
- * Implementation of transaction_t.set_nonces.
- */
-static void set_nonces(private_ike_auth_t *this, chunk_t nonce_i, chunk_t nonce_r)
-{
- this->nonce_i = nonce_i;
- this->nonce_r = nonce_r;
-}
-
-/**
- * Implementation of transaction_t.set_init_messages.
- */
-static void set_init_messages(private_ike_auth_t *this, chunk_t init_request, chunk_t init_response)
-{
- this->init_request = init_request;
- this->init_response = init_response;
-}
-
-/**
- * Build a notify message.
- */
-static void build_notify(notify_type_t type, message_t *message, bool flush_message)
-{
- notify_payload_t *notify;
-
- if (flush_message)
- {
- payload_t *payload;
- iterator_t *iterator = message->get_payload_iterator(message);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- payload->destroy(payload);
- iterator->remove(iterator);
- }
- iterator->destroy(iterator);
- }
-
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- message->add_payload(message, (payload_t*)notify);
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_ike_auth_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- identification_t *my_id, *other_id;
- id_payload_t *my_id_payload;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->policy->get_my_id(this->policy);
- other_id = this->policy->get_other_id(this->policy);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, IKE_AUTH);
- request->set_request(request, TRUE);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
-
- if (this->eap_auth)
- {
- /* we already sent ID, SA, TS in an earlier ike_auth, we now
- * continiue EAP processing */
- if (this->eap_next)
- {
- /* if we have another outstanding EAP response, send it */
- request->add_payload(request, (payload_t*)this->eap_next);
- this->eap_next = NULL;
- return SUCCESS;
- }
- else
- {
- /* if not, we have received an EAP_SUCCESS, send AUTH payload.
- * we only send our data if:
- * a) The peer has been authenticated using RSA/PSK, or
- * b) The EAP method is mutual and gives us enough security
- */
- if (this->eap_auth->is_mutual(this->eap_auth) ||
- this->peer_authenticated)
- {
- auth_payload_t *auth_payload;
- authenticator_t *authenticator = (authenticator_t*)this->eap_auth;
-
- if (authenticator->build(authenticator, this->init_request,
- this->nonce_r, &auth_payload) != SUCCESS)
- {
- SIG(IKE_UP_FAILED,
- "EAP authentication data generation failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- request->add_payload(request, (payload_t*)auth_payload);
- return SUCCESS;
- }
- else
- {
- SIG(IKE_UP_FAILED,
- "peer didn't send authentication data, deleting IKE_SA");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
- }
- /* otherwise we do a normal ike_auth request... */
-
- { /* build ID payload */
- my_id_payload = id_payload_create_from_identification(TRUE, my_id);
- request->add_payload(request, (payload_t*)my_id_payload);
- }
-
- /* build certificate request payload */
- if (this->connection->get_certreq_policy(this->connection) != CERT_NEVER_SEND)
- {
- certreq_payload_t *certreq_payload;
- identification_t *other_ca = this->policy->get_other_ca(this->policy);
-
- if (other_ca)
- {
- if (other_ca->get_type(other_ca) == ID_ANY)
- {
- certreq_payload = certreq_payload_create_from_cacerts();
- }
- else
- {
- certreq_payload = certreq_payload_create_from_cacert(other_ca);
- }
- if (certreq_payload != NULL)
- {
- request->add_payload(request, (payload_t*)certreq_payload);
- }
- }
- }
-
- /* build certificate payload. TODO: Handle certreq from init_ike_sa. */
- if (this->policy->get_auth_method(this->policy) == AUTH_RSA &&
- this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND)
- {
- cert_payload_t *cert_payload;
-
- x509_t *cert = charon->credentials->get_certificate(charon->credentials, my_id);
-
- if (cert)
- {
- cert_payload = cert_payload_create_from_x509(cert);
- request->add_payload(request, (payload_t*)cert_payload);
- }
- else
- {
- DBG1(DBG_IKE, "could not find my certificate, certificate payload omitted");
- }
- }
-
- { /* build IDr payload, if other_id defined */
- id_payload_t *id_payload;
- if (!other_id->contains_wildcards(other_id))
- {
- id_payload = id_payload_create_from_identification(FALSE, other_id);
- request->add_payload(request, (payload_t*)id_payload);
- }
- }
-
- if (this->policy->get_auth_method(this->policy) != AUTH_EAP)
- { /* build auth payload */
- authenticator_t *authenticator;
- auth_payload_t *auth_payload;
- auth_method_t auth_method;
- status_t status;
-
- auth_method = this->policy->get_auth_method(this->policy);
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- status = authenticator->build(authenticator, this->init_request,
- this->nonce_r, &auth_payload);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "could not generate AUTH data, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- request->add_payload(request, (payload_t*)auth_payload);
- }
- else
- {
- this->eap_auth = eap_authenticator_create(this->ike_sa);
- /* include notify that we support EAP only authentication */
- build_notify(EAP_ONLY_AUTHENTICATION, request, FALSE);
- }
-
- { /* build SA payload for CHILD_SA */
- linked_list_t *proposal_list;
- sa_payload_t *sa_payload;
- u_int32_t soft_lifetime, hard_lifetime;
- bool enable_natt;
-
- proposal_list = this->policy->get_proposals(this->policy);
- soft_lifetime = this->policy->get_soft_lifetime(this->policy);
- hard_lifetime = this->policy->get_hard_lifetime(this->policy);
- enable_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- soft_lifetime, hard_lifetime,
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- enable_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
- if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "could not install CHILD_SA, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- sa_payload = sa_payload_create_from_proposal_list(proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- /* notify for transport/BEET mode, we propose it
- * independent of the traffic selectors */
- switch (this->policy->get_mode(this->policy))
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, request, FALSE);
- }
- break;
- case MODE_BEET:
- build_notify(USE_BEET_MODE, request, FALSE);
- break;
- }
-
- { /* build TSi payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_my_traffic_selectors(this->policy, me);
- ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
-
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- { /* build TSr payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_other_traffic_selectors(this->policy, other);
- ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
-
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifies
- */
-static status_t process_notifies(private_ike_auth_t *this, notify_payload_t *notify_payload)
-{
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- /* these notifies are not critical. no child_sa is built, but IKE stays alive */
- case SINGLE_PAIR_REQUIRED:
- {
- SIG(CHILD_UP_FAILED, "received a SINGLE_PAIR_REQUIRED notify");
- this->build_child = FALSE;
- return SUCCESS;
- }
- case TS_UNACCEPTABLE:
- {
- SIG(CHILD_UP_FAILED, "received TS_UNACCEPTABLE notify");
- this->build_child = FALSE;
- return SUCCESS;
- }
- case NO_PROPOSAL_CHOSEN:
- {
- SIG(CHILD_UP_FAILED, "received NO_PROPOSAL_CHOSEN notify");
- this->build_child = FALSE;
- return SUCCESS;
- }
- case EAP_ONLY_AUTHENTICATION:
- {
- DBG1(DBG_IKE, "peer requested EAP_ONLY_AUTHENTICATION");
- this->eap_only = TRUE;
- return SUCCESS;
- }
- case USE_TRANSPORT_MODE:
- {
- this->mode = MODE_TRANSPORT;
- return SUCCESS;
- }
- case USE_BEET_MODE:
- {
- this->mode = MODE_BEET;
- return SUCCESS;
- }
- default:
- {
- if (notify_type < 16383)
- {
- SIG(IKE_UP_FAILED, "received %N notify error, deleting IKE_SA",
- notify_type_names, notify_type);
- return DESTROY_ME;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Import certificate requests from a certreq payload
- */
-static void process_certificate_request(private_ike_auth_t *this,
- certreq_payload_t *certreq_payload)
-{
- chunk_t keyids;
- cert_encoding_t encoding = certreq_payload->get_cert_encoding(certreq_payload);
-
- if (encoding != CERT_X509_SIGNATURE)
- {
- DBG1(DBG_IKE, "certreq payload %N not supported, ignored",
- cert_encoding_names, encoding);
- return;
- }
-
- keyids = certreq_payload->get_data(certreq_payload);
- while (keyids.len >= HASH_SIZE_SHA1)
- {
- chunk_t keyid = { keyids.ptr, HASH_SIZE_SHA1};
- x509_t *cacert = charon->credentials->get_ca_certificate_by_keyid(
- charon->credentials, keyid);
- if (cacert)
- {
- DBG2(DBG_IKE, "request for certificate issued by ca '%D'",
- cacert->get_subject(cacert));
- this->cacerts->insert_last(this->cacerts, cacert);
- }
- else
- {
- DBG2(DBG_IKE, "request for certificate issued by unknown ca");
- }
- DBG2(DBG_IKE, " with keyid %#B", &keyid);
-
- keyids.ptr += HASH_SIZE_SHA1;
- keyids.len -= HASH_SIZE_SHA1;
- }
-}
-
-/**
- * Import a certificate from a cert payload
- */
-static void import_certificate(cert_payload_t *cert_payload)
-{
- bool found;
- x509_t *cert;
- cert_encoding_t encoding;
-
- encoding = cert_payload->get_cert_encoding(cert_payload);
- if (encoding != CERT_X509_SIGNATURE)
- {
- DBG1(DBG_IKE, "certificate payload %N not supported, ignored",
- cert_encoding_names, encoding);
- return;
- }
- cert = x509_create_from_chunk(cert_payload->get_data_clone(cert_payload));
- if (cert)
- {
- if (charon->credentials->verify(charon->credentials, cert, &found))
- {
- DBG2(DBG_IKE, "received end entity certificate is trusted, added to store");
- if (!found)
- {
- charon->credentials->add_end_certificate(charon->credentials, cert);
- }
- else
- {
- cert->destroy(cert);
- }
- }
- else
- {
- DBG1(DBG_IKE, "received end entity certificate is not trusted, discarded");
- cert->destroy(cert);
- }
- }
- else
- {
- DBG1(DBG_IKE, "parsing of received certificate failed, discarded");
- }
-}
-
-/**
- * Check a list of traffic selectors if any selector belongs to host
- */
-static bool ts_list_is_host(linked_list_t *list, host_t *host)
-{
- traffic_selector_t *ts;
- bool is_host = TRUE;
- iterator_t *iterator = list->create_iterator(list, TRUE);
-
- while (is_host && iterator->iterate(iterator, (void**)&ts))
- {
- is_host = is_host && ts->is_host(ts, host);
- }
- iterator->destroy(iterator);
- return is_host;
-}
-
-/**
- * Install a CHILD_SA for usage
- */
-static status_t install_child_sa(private_ike_auth_t *this, bool initiator)
-{
- prf_plus_t *prf_plus;
- chunk_t seed;
- status_t status;
-
- seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len);
- memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len);
- memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len);
- prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
- chunk_free(&seed);
-
- if (initiator)
- {
- status = this->child_sa->update(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- else
- {
- status = this->child_sa->add(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- prf_plus->destroy(prf_plus);
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
- if (initiator)
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsi,
- this->tsr, this->mode);
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsr,
- this->tsi, this->mode);
- }
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
-
- /* add to IKE_SA, and remove from transaction */
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
- this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
- this->child_sa = NULL;
- return SUCCESS;
-}
-
-/**
- * create a CHILD SA, install it, and build response message
- */
-static void setup_child_sa(private_ike_auth_t *this, message_t *response)
-{
- bool use_natt;
- u_int32_t soft_lifetime, hard_lifetime;
- host_t *me, *other;
- identification_t *my_id, *other_id;
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- soft_lifetime = this->policy->get_soft_lifetime(this->policy);
- hard_lifetime = this->policy->get_hard_lifetime(this->policy);
- use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- soft_lifetime, hard_lifetime,
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- use_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
- /* check mode, and include notify into reply */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (!ts_list_is_host(this->tsi, other) ||
- !ts_list_is_host(this->tsr, me))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, response, FALSE);
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- else
- {
- build_notify(USE_BEET_MODE, response, FALSE);
- }
- break;
- }
-
- if (install_child_sa(this, FALSE) != SUCCESS)
- {
- SIG(CHILD_UP_FAILED, "installing CHILD_SA %s failed, no CHILD_SA created",
- this->policy->get_name(this->policy));
- DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response");
- build_notify(NO_PROPOSAL_CHOSEN, response, FALSE);
- }
- else
- {
- /* build SA and TS payloads */
- SIG(CHILD_UP_SUCCESS, "CHILD_SA created");
- ts_payload_t *ts_response;
- sa_payload_t *sa_response = sa_payload_create();
- sa_response->add_proposal(sa_response, this->proposal);
- response->add_payload(response, (payload_t*)sa_response);
- ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
- response->add_payload(response, (payload_t*)ts_response);
- ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
- response->add_payload(response, (payload_t*)ts_response);
- }
-}
-
-/**
- * clone the transaction to requeue it for EAP handling
- */
-static private_ike_auth_t *clone_for_eap(private_ike_auth_t *this)
-{
- private_ike_auth_t *clone;
- clone = (private_ike_auth_t*)ike_auth_create(this->ike_sa);
-
- clone->tsi = this->tsi; this->tsi = NULL;
- clone->tsr = this->tsr; this->tsr = NULL;
- clone->eap_auth = this->eap_auth; this->eap_auth = NULL;
- clone->eap_next = this->eap_next; this->eap_next = NULL;
- clone->build_child = this->build_child;
- clone->nonce_i = this->nonce_i; this->nonce_i = chunk_empty;
- clone->nonce_r = this->nonce_r; this->nonce_r = chunk_empty;
- clone->child_sa = this->child_sa; this->child_sa = NULL;
- clone->proposal = this->proposal; this->proposal = NULL;
- clone->peer_authenticated = this->peer_authenticated;
- clone->connection = this->connection; this->connection = NULL;
- clone->policy = this->policy; this->policy = NULL;
-
- return clone;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_ike_auth_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- identification_t *my_id, *other_id;
- message_t *response;
- status_t status;
- iterator_t *payloads;
- payload_t *payload;
- id_payload_t *idi_request = NULL;
- id_payload_t *idr_request = NULL;
- auth_payload_t *auth_request = NULL;
- certreq_payload_t *certreq_request = NULL;
- cert_payload_t *cert_request = NULL;
- sa_payload_t *sa_request = NULL;
- ts_payload_t *tsi_request = NULL;
- ts_payload_t *tsr_request = NULL;
- eap_payload_t *eap_request = NULL;
- id_payload_t *idr_response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- SIG(CHILD_UP_START, "setting up CHILD_SA along with IKE_AUTH");
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, IKE_AUTH);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != IKE_AUTH)
- {
- SIG(IKE_UP_FAILED, "IKE_AUTH response of invalid type, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case ID_INITIATOR:
- idi_request = (id_payload_t*)payload;
- break;
- case ID_RESPONDER:
- idr_request = (id_payload_t*)payload;
- break;
- case AUTHENTICATION:
- auth_request = (auth_payload_t*)payload;
- break;
- case CERTIFICATE_REQUEST:
- certreq_request = (certreq_payload_t*)payload;
- process_certificate_request(this, certreq_request);
- break;
- case CERTIFICATE:
- cert_request = (cert_payload_t*)payload;
- break;
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_request = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_request = (ts_payload_t*)payload;
- break;
- case EXTENSIBLE_AUTHENTICATION:
- eap_request = (eap_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifies(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, returned FAILED means do next transaction */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- payloads->destroy(payloads);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* if message contains an EAP payload, we process it */
- if (eap_request && this->eap_auth)
- {
- eap_payload_t *eap_response;
- private_ike_auth_t *next_auth;
-
- status = this->eap_auth->process(this->eap_auth, eap_request, &eap_response);
- response->add_payload(response, (payload_t*)eap_response);
-
- if (status == FAILED)
- {
- /* shut down if EAP message is EAP_FAILURE */
- return DESTROY_ME;
- }
-
- next_auth = clone_for_eap(this);
- next_auth->message_id = this->message_id + 1;
- *next = (transaction_t*)next_auth;
- return SUCCESS;
- }
-
- /* if we do EAP authentication and a AUTH payload comes in, verify it */
- if (auth_request && this->eap_auth)
- {
- auth_payload_t *auth_response;
- authenticator_t *authenticator = (authenticator_t*)this->eap_auth;
-
- if (authenticator->verify(authenticator, this->init_request,
- this->nonce_r, auth_request) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
-
- if (authenticator->build(authenticator, this->init_response,
- this->nonce_i, &auth_response) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "EAP authentication data generation failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- response->add_payload(response, (payload_t*)auth_response);
-
- SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]",
- this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-
- setup_child_sa(this, response);
-
- return SUCCESS;
- }
-
- /* check if we have all payloads (AUTH is not checked, not required with EAP) */
- if (!(idi_request && sa_request && tsi_request && tsr_request))
- {
- build_notify(INVALID_SYNTAX, response, TRUE);
- SIG(IKE_UP_FAILED, "request message incomplete, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- { /* process ID payload */
- other_id = idi_request->get_identification(idi_request);
- if (idr_request)
- {
- my_id = idr_request->get_identification(idr_request);
- }
- else
- {
- my_id = identification_create_from_encoding(ID_ANY, chunk_empty);
- }
- }
-
- { /* get a policy and process traffic selectors */
- linked_list_t *my_ts, *other_ts;
-
- my_ts = tsr_request->get_traffic_selectors(tsr_request);
- other_ts = tsi_request->get_traffic_selectors(tsi_request);
-
- this->policy = charon->policies->get_policy(charon->policies,
- my_id, other_id,
- my_ts, other_ts,
- me, other);
-
- if (this->policy)
- {
- this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me);
- this->tsi = this->policy->select_other_traffic_selectors(this->policy, other_ts, other);
- }
- my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
- other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
-
- /* TODO: We should check somehow if we have a policy, but with other
- * traffic selectors. Then we would create a IKE_SA without a CHILD_SA. */
- if (this->policy == NULL)
- {
- SIG(IKE_UP_FAILED, "no acceptable policy for IDs %D - %D found, "
- "deleting IKE_SA", my_id, other_id);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- my_id->destroy(my_id);
- other_id->destroy(other_id);
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- my_id->destroy(my_id);
-
- /* get my id from policy, which must contain a fully qualified valid id */
- my_id = this->policy->get_my_id(this->policy);
- this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id));
- this->ike_sa->set_other_id(this->ike_sa, other_id);
-
- idr_response = id_payload_create_from_identification(FALSE, my_id);
- response->add_payload(response, (payload_t*)idr_response);
- }
-
- if (this->policy->get_auth_method(this->policy) == AUTH_RSA &&
- this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND)
- { /* build certificate payload */
- x509_t *cert;
- cert_payload_t *cert_payload;
-
- cert = charon->credentials->get_certificate(charon->credentials, my_id);
- if (cert)
- {
- cert_payload = cert_payload_create_from_x509(cert);
- response->add_payload(response, (payload_t *)cert_payload);
- }
- else
- {
- DBG1(DBG_IKE, "could not find my certificate, cert payload omitted");
- }
- }
-
- if (cert_request)
- { /* process certificate payload */
- import_certificate(cert_request);
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
-
- /* get proposals from request, and select one with ours */
- proposal_list = sa_request->get_proposals(sa_request);
- DBG2(DBG_IKE, "selecting proposals:");
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* do we have a proposal? */
- if (this->proposal == NULL)
- {
- SIG(CHILD_UP_FAILED, "CHILD_SA proposals unacceptable, no CHILD_SA created");
- DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response");
- build_notify(NO_PROPOSAL_CHOSEN, response, FALSE);
- this->build_child = FALSE;
- }
- /* do we have traffic selectors? */
- else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0)
- {
- SIG(CHILD_UP_FAILED, "CHILD_SA traffic selectors unacceptable, no CHILD_SA created");
- DBG1(DBG_IKE, "adding TS_UNACCEPTABLE notify to response");
- build_notify(TS_UNACCEPTABLE, response, FALSE);
- this->build_child = FALSE;
- }
- }
-
- if (!this->eap_only || this->policy->get_auth_method(this->policy) != AUTH_EAP)
- { /* build response AUTH payload when not using a mutual EAP authentication */
- auth_payload_t *auth_response;
- authenticator_t *authenticator;
- auth_method_t auth_method;
- status_t status;
-
- auth_method = this->policy->get_auth_method(this->policy);
- if (auth_method == AUTH_EAP)
- {
- SIG(IKE_UP_FAILED,
- "peer does not support EAP only authentication, deleting IKE_SA");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
-
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- status = authenticator->build(authenticator, this->init_response,
- this->nonce_i, &auth_response);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication data generation failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- response->add_payload(response, (payload_t*)auth_response);
- }
-
- if (auth_request)
- { /* process auth payload, if not using EAP */
- authenticator_t *authenticator;
- auth_method_t auth_method;
- status_t status;
-
- auth_method = auth_request->get_auth_method(auth_request);
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- status = authenticator->verify(authenticator, this->init_request,
- this->nonce_r, auth_request);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
-
- SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]",
- this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-
- /* set up CHILD_SA if negotiation succeded */
- if (this->build_child)
- {
- setup_child_sa(this, response);
- }
-
- this->peer_authenticated = TRUE;
- }
- else
- {
- /* if no AUTH payload was included, we start with an EAP exchange.
- * eap_response is a request in the EAP meaning, but is
- * contained in a IKEv2 response */
- eap_payload_t *eap_response;
- private_ike_auth_t *next_auth;
- eap_type_t eap_type;
-
- eap_type = this->policy->get_eap_type(this->policy);
- this->eap_auth = eap_authenticator_create(this->ike_sa);
- status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_response);
- response->add_payload(response, (payload_t*)eap_response);
-
- if (status == FAILED)
- {
- /* EAP initiaton failed, we send the EAP_FAILURE message and quit */
- return DESTROY_ME;
- }
- /* we send an EAP request. to handle the reply, we reschedule
- * this transaction, as it knows how to handle the reply */
- next_auth = clone_for_eap(this);
- next_auth->message_id = this->message_id + 1;
- *next = (transaction_t*)next_auth;
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_ike_auth_t *this, message_t *response,
- transaction_t **next)
-{
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- identification_t *other_id, *my_id;
- ts_payload_t *tsi_payload = NULL;
- ts_payload_t *tsr_payload = NULL;
- id_payload_t *idr_payload = NULL;
- cert_payload_t *cert_payload = NULL;
- auth_payload_t *auth_payload = NULL;
- sa_payload_t *sa_payload = NULL;
- eap_payload_t *eap_payload = NULL;
- status_t status;
-
- /* check message type */
- if (response->get_exchange_type(response) != IKE_AUTH)
- {
- SIG(IKE_UP_FAILED, "IKE_AUTH response of invalid type, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case ID_RESPONDER:
- idr_payload = (id_payload_t*)payload;
- break;
- case AUTHENTICATION:
- auth_payload = (auth_payload_t*)payload;
- break;
- case CERTIFICATE:
- cert_payload = (cert_payload_t*)payload;
- break;
- case SECURITY_ASSOCIATION:
- sa_payload = (sa_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_payload = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_payload = (ts_payload_t*)payload;
- break;
- case EXTENSIBLE_AUTHENTICATION:
- eap_payload = (eap_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifies(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, as transaction completet */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (idr_payload)
- { /* process idr payload */
- identification_t *configured_other_id;
- int wildcards;
-
- other_id = idr_payload->get_identification(idr_payload);
- configured_other_id = this->policy->get_other_id(this->policy);
-
- if (!other_id->matches(other_id, configured_other_id, &wildcards))
- {
- other_id->destroy(other_id);
- SIG(IKE_UP_FAILED, "other peer uses unacceptable ID (%D, excepted "
- "%D), deleting IKE_SA", other_id, configured_other_id);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- /* update other ID. It was already set, but may contain wildcards */
- this->ike_sa->set_other_id(this->ike_sa, other_id);
- }
-
- if (cert_payload)
- { /* process cert payload */
- import_certificate(cert_payload);
- }
-
- if (auth_payload && idr_payload)
- { /* authenticate peer */
- authenticator_t *authenticator;
- auth_method_t auth_method;
- status_t status;
-
- my_id = this->policy->get_my_id(this->policy);
- auth_method = auth_payload->get_auth_method(auth_payload);
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- status = authenticator->verify(authenticator, this->init_response,
- this->nonce_i, auth_payload);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication of '%D' with %N failed, "
- "deleting IKE_SA", other_id, auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- this->peer_authenticated = TRUE;
- }
-
- if (eap_payload && this->eap_auth)
- {
- switch (this->eap_auth->process(this->eap_auth, eap_payload, &this->eap_next))
- {
- case SUCCESS:
- {
- /* EAP message was EAP_SUCCESS, send AUTH in next transaction */
- DBG2(DBG_IKE, "EAP authentication exchanges completed successful");
- this->eap_next = NULL;
- /* fall through */
- }
- case NEED_MORE:
- {
- /* EAP message was a EAP_REQUEST, handle it in next transaction */
- private_ike_auth_t *next_auth = clone_for_eap(this);
- next_auth->message_id = this->message_id + 1;
- *next = (transaction_t*)next_auth;
- return SUCCESS;
- }
- case FAILED:
- default:
- {
- /* EAP message was EAP_FAILURE */
- SIG(IKE_UP_FAILED, "EAP authentication failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
- }
-
- if (!(auth_payload && sa_payload && tsi_payload && tsr_payload))
- {
- SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* if we do EAP authentication and a AUTH payload comes in, verify it */
- if (this->eap_auth &&
- (this->eap_auth->is_mutual(this->eap_auth) || this->peer_authenticated))
- {
- authenticator_t *authenticator = (authenticator_t*)this->eap_auth;
-
- if (authenticator->verify(authenticator, this->init_response,
- this->nonce_i, auth_payload) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- this->peer_authenticated = TRUE;
- }
-
- if (!this->peer_authenticated)
- {
- SIG(IKE_UP_FAILED, "server didn't send authentication data, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]",
- this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-
- { /* process traffic selectors for us */
- linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload);
- this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received, me);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process traffic selectors for other */
- linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload);
- this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received, other);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process sa payload */
- linked_list_t *proposal_list;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- /* we have to re-check here if other's selection is valid */
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* everything fine to create CHILD? */
- if (this->proposal == NULL ||
- this->tsi->get_count(this->tsi) == 0 ||
- this->tsr->get_count(this->tsr) == 0 ||
- !this->build_child)
- {
- SIG(CHILD_UP_FAILED, "CHILD_SA negotiation failed, no CHILD_SA built");
- }
- else
- {
- /* check mode if it is acceptable */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- /* TODO: we should close the CHILD_SA if negotiated
- * mode is not acceptable for us */
- if (!ts_list_is_host(this->tsi, me) ||
- !ts_list_is_host(this->tsr, other))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- break;
- }
-
- if (install_child_sa(this, TRUE) != SUCCESS)
- {
- SIG(CHILD_UP_FAILED, "installing CHILD_SA '%s' failed, no CHILD_SA built",
- this->policy->get_name(this->policy));
- /* TODO: we should send a DELETE for that CHILD to stay
- * synchronous with the peer */
- }
- else
- {
- SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created",
- this->policy->get_name(this->policy));
- }
- }
- }
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_ike_auth_t *this)
-{
- DESTROY_IF(this->message);
- DESTROY_IF(this->proposal);
- DESTROY_IF(this->child_sa);
- DESTROY_IF(this->policy);
- DESTROY_IF(this->connection);
- DESTROY_IF(this->cacerts);
- if (this->eap_auth)
- {
- this->eap_auth->authenticator_interface.destroy(&this->eap_auth->authenticator_interface);
- }
- DESTROY_IF(this->eap_next);
- if (this->tsi)
- {
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- }
- if (this->tsr)
- {
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- }
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- chunk_free(&this->init_request);
- chunk_free(&this->init_response);
- free(this);
-}
-
-/*
- * Described in header.
- */
-ike_auth_t *ike_auth_create(ike_sa_t *ike_sa)
-{
- private_ike_auth_t *this = malloc_thing(private_ike_auth_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.set_config = (void(*)(ike_auth_t*,connection_t*,policy_t*))set_config;
- this->public.set_reqid = (void(*)(ike_auth_t*,u_int32_t))set_reqid;
- this->public.set_nonces = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_nonces;
- this->public.set_init_messages = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_init_messages;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->init_request = chunk_empty;
- this->init_response = chunk_empty;
- this->child_sa = NULL;
- this->proposal = NULL;
- this->policy = NULL;
- this->connection = NULL;
- this->tsi = NULL;
- this->tsr = NULL;
- this->build_child = TRUE;
- this->eap_auth = NULL;
- this->eap_next = NULL;
- this->eap_only = FALSE;
- this->peer_authenticated = FALSE;
- this->reqid = 0;
- this->cacerts = linked_list_create();
- this->mode = MODE_TUNNEL;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/ike_auth.h b/src/charon/sa/transactions/ike_auth.h
deleted file mode 100644
index 490359eda..000000000
--- a/src/charon/sa/transactions/ike_auth.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * @file ike_auth.h
- *
- * @brief Interface of transaction ike_auth.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-
-#ifndef IKE_AUTH_H_
-#define IKE_AUTH_H_
-
-typedef struct ike_auth_t ike_auth_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction for the second message exchange to authenticate an IKE_SA.
- *
- * The second transaction is encrypted and authenticates the peers. It also
- * sets up a first CHILD_SA.
- *
- * @b Constructors:
- * - ike_auth_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct ike_auth_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the config used for the ike_auth exchange.
- *
- * The connection definition is used to complete IKE_SA setup, the
- * policy defines the CHILD_SA which is created along with the ike_auth
- * exchange.
- *
- * @param this calling object
- * @param connection connection definition
- * @param policy policy definition
- */
- void (*set_config) (ike_auth_t* this,
- connection_t *connection, policy_t *policy);
-
- /**
- * @brief Set the reqid used for CHILD_SA setup.
- *
- * The first two message exchanges may set up an associated
- * CHILD_SA. If we acquire, we must use the same reqid as the
- * installed policy.
- *
- * @param this calling object
- * @param reqid reqid to use for the CHILD_SA
- */
- void (*set_reqid) (ike_auth_t* this, u_int32_t reqid);
-
- /**
- * @brief Set the nonces used in the previous ike_sa_init transaction.
- *
- * The nonces are used to create the authentication data.
- *
- * @param this calling object
- * @param nonce_i initiator chosen nonce
- * @param nonce_r responder chosen nonce
- */
- void (*set_nonces) (ike_auth_t* this, chunk_t nonce_i, chunk_t nonce_r);
-
- /**
- * @brief Set the messages used in the previous ike_sa_init transaction.
- *
- * The messages are used to create the authentication data.
- *
- * @param this calling object
- * @param request encoded request message as a chunk
- * @param response encoded response message as a chunk
- */
- void (*set_init_messages) (ike_auth_t* this, chunk_t request, chunk_t response);
-};
-
-/**
- * @brief Create a new transaction which processes IKE_AUTH exchanges.
- *
- * @param ike_sa assigned IKE_SA
- * @return created ike_auth transaction
- *
- * @ingroup transactions
- */
-ike_auth_t *ike_auth_create(ike_sa_t *ike_sa);
-
-#endif /* IKE_AUTH_H_ */
diff --git a/src/charon/sa/transactions/ike_sa_init.c b/src/charon/sa/transactions/ike_sa_init.c
deleted file mode 100644
index b5728a986..000000000
--- a/src/charon/sa/transactions/ike_sa_init.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/**
- * @file ike_sa_init.c
- *
- * @brief Implementation of ike_sa_init_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005-2006 Martin Willi
- * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "ike_sa_init.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <crypto/diffie_hellman.h>
-#include <crypto/hashers/hasher.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/ke_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <sa/transactions/ike_auth.h>
-#include <queues/jobs/delete_ike_sa_job.h>
-#include <queues/jobs/rekey_ike_sa_job.h>
-
-
-typedef struct private_ike_sa_init_t private_ike_sa_init_t;
-
-/**
- * Private members of a ike_sa_init_t object..
- */
-struct private_ike_sa_init_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- ike_sa_init_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * Next transaction followed to this one. May be IKE_AUTH,
- * or a IKE_SA_INIT retry
- */
- transaction_t **next;
-
- /**
- * Diffie hellman object used to generate public DH value.
- */
- diffie_hellman_t *diffie_hellman;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * connection definition used for initiation
- */
- connection_t *connection;
-
- /**
- * policy definition forwarded to ike_auth transaction
- */
- policy_t *policy;
-
- /**
- * Negotiated proposal used for IKE_SA
- */
- proposal_t *proposal;
-
- /**
- * Reqid to pass to IKE_AUTH, used for created CHILD_SA
- */
- u_int32_t reqid;
-
- /**
- * Unique ID for to enumerate all IKE_SAs in its name
- */
- u_int32_t unique_id;
-
- /**
- * Randomizer to generate nonces
- */
- randomizer_t *randomizer;
-
- /**
- * Hasher used to build NAT detection hashes
- */
- hasher_t *nat_hasher;
-
- /**
- * Precomputed NAT hash for source address
- */
- chunk_t natd_src_hash;
-
- /**
- * Precomputed NAT hash for destination address
- */
- chunk_t natd_dst_hash;
-
- /**
- * Did we process any NAT detection notifys for a source address?
- */
- bool natd_src_seen;
-
- /**
- * Did we process any NAT detection notifys for a destination address?
- */
- bool natd_dst_seen;
-
- /**
- * Have we found a matching source address NAT hash?
- */
- bool natd_src_matched;
-
- /**
- * Have we found a matching destination address NAT hash?
- */
- bool natd_dst_matched;
-};
-
-/**
- * Implementation of ike_sa_init_t.use_dh_group.
- */
-static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_group)
-{
- if (this->connection->check_dh_group(this->connection, dh_group))
- {
- this->diffie_hellman = diffie_hellman_create(dh_group);
- if (this->diffie_hellman)
- {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
- * Implementation of ike_sa_init_t.set_config.
- */
-static void set_config(private_ike_sa_init_t *this,
- connection_t *connection, policy_t *policy)
-{
- this->connection = connection;
- this->policy = policy;
-}
-
-/**
- * Implementation of ike_sa_init_t.set_reqid.
- */
-static void set_reqid(private_ike_sa_init_t *this, u_int32_t reqid)
-{
- this->reqid = reqid;
-}
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_ike_sa_init_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_ike_sa_init_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Build NAT detection hash for a host
- */
-static chunk_t generate_natd_hash(private_ike_sa_init_t *this,
- ike_sa_id_t * ike_sa_id, host_t *host)
-{
- chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk;
- chunk_t natd_hash;
- u_int64_t spi_i, spi_r;
- u_int16_t port;
-
- /* prepare all requred chunks */
- spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
- spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
- spi_i_chunk.ptr = (void*)&spi_i;
- spi_i_chunk.len = sizeof(spi_i);
- spi_r_chunk.ptr = (void*)&spi_r;
- spi_r_chunk.len = sizeof(spi_r);
- port = htons(host->get_port(host));
- port_chunk.ptr = (void*)&port;
- port_chunk.len = sizeof(port);
- addr_chunk = host->get_address(host);
-
- /* natd_hash = SHA1( spi_i | spi_r | address | port ) */
- natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk);
- this->nat_hasher->allocate_hash(this->nat_hasher, natd_chunk, &natd_hash);
- DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
- DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
-
- chunk_free(&natd_chunk);
- return natd_hash;
-}
-
-/**
- * Build a NAT detection notify payload.
- */
-static notify_payload_t *build_natd_payload(private_ike_sa_init_t *this,
- notify_type_t type, host_t *host)
-{
- chunk_t hash;
- notify_payload_t *notify;
- ike_sa_id_t *ike_sa_id;
-
- ike_sa_id = this->ike_sa->get_id(this->ike_sa);
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- hash = generate_natd_hash(this, ike_sa_id, host);
- notify->set_notification_data(notify, hash);
- chunk_free(&hash);
-
- return notify;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_ike_sa_init_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- identification_t *my_id, *other_id;
- char name[64];
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* we already set up the IDs. Mine is already fully qualified, other
- * will be updated in the ike_auth transaction */
- my_id = this->policy->get_my_id(this->policy);
- other_id = this->policy->get_other_id(this->policy);
- this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id));
- this->ike_sa->set_other_id(this->ike_sa, other_id->clone(other_id));
- if (snprintf(name, sizeof(name), "%s{%d}",
- this->connection->get_name(this->connection),
- this->unique_id) > 0)
- {
- this->ike_sa->set_name(this->ike_sa, name);
- }
-
- /* setting up a IKE_SA implicitly requires setup of a CHILD_SA */
- SIG(IKE_UP_START, "initiating IKE_SA '%s' between %H[%D]...%H[%D]",
- this->connection->get_name(this->connection), me, my_id, other, other_id);
- SIG(CHILD_UP_START, "establishing CHILD_SA '%s' along with IKE_SA",
- this->policy->get_name(this->policy));
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, IKE_SA_INIT);
- request->set_request(request, TRUE);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- /* if the DH group is set via use_dh_group(), we already have a DH object */
- if (!this->diffie_hellman)
- {
- diffie_hellman_group_t dh_group;
-
- dh_group = this->connection->get_dh_group(this->connection);
- this->diffie_hellman = diffie_hellman_create(dh_group);
- if (this->diffie_hellman == NULL)
- {
- SIG(IKE_UP_FAILED, "DH group %N not supported, aborting",
- diffie_hellman_group_names, dh_group);
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
-
- { /* build the SA payload from proposals */
- sa_payload_t *sa_payload;
- linked_list_t *proposal_list;
-
- proposal_list = this->connection->get_proposals(this->connection);
- sa_payload = sa_payload_create_from_proposal_list(proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- { /* build the KE payload from the DH object */
- ke_payload_t *ke_payload;
-
- ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
-
- request->add_payload(request, (payload_t*)ke_payload);
- }
-
- { /* build the NONCE payload for us (initiator) */
- nonce_payload_t *nonce_payload;
-
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_i) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "could not generate nonce, aborting");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- nonce_payload = nonce_payload_create();
- nonce_payload->set_nonce(nonce_payload, this->nonce_i);
-
- request->add_payload(request, (payload_t*)nonce_payload);
- }
-
- { /* build NAT_DETECTION notifys */
- notify_payload_t *notify;
- linked_list_t *list;
- host_t *host;
-
- /* N(NAT_DETECTION_SOURCE_IP)+
- * we include only one notify if our address is defined, but all
- * possible if not */
- host = this->ike_sa->get_my_host(this->ike_sa);
- if (host->is_anyaddr(host))
- {
- /* TODO: we could get the src address from netlink */
- list = charon->socket->create_local_address_list(charon->socket);
- while (list->remove_first(list, (void**)&host) == SUCCESS)
- {
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- host->destroy(host);
- request->add_payload(request, (payload_t*)notify);
- }
- list->destroy(list);
- }
- else
- {
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- request->add_payload(request, (payload_t*)notify);
- }
-
- /* N(NAT_DETECTION_DESTINATION_IP) */
- notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
- request->add_payload(request, (payload_t*)notify);
- }
-
- this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifys
- */
-static status_t process_notifys(private_ike_sa_init_t *this, notify_payload_t *notify_payload)
-{
- chunk_t notification_data;
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- case NO_PROPOSAL_CHOSEN:
- {
- SIG(IKE_UP_FAILED,
- "received a NO_PROPOSAL_CHOSEN notify, deleting IKE_SA");
- return DESTROY_ME;
- }
- case INVALID_MAJOR_VERSION:
- {
- SIG(IKE_UP_FAILED,
- "received a INVALID_MAJOR_VERSION notify, deleting IKE_SA");
- return DESTROY_ME;
- }
- case INVALID_KE_PAYLOAD:
- {
- chunk_t notify_data;
- diffie_hellman_group_t dh_group, old_dh_group;
- ike_sa_init_t *retry;
-
- old_dh_group = this->connection->get_dh_group(this->connection);
- notify_data = notify_payload->get_notification_data(notify_payload);
- dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
-
- DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested %N",
- diffie_hellman_group_names, old_dh_group,
- diffie_hellman_group_names, dh_group);
- if (!this->connection->check_dh_group(this->connection, dh_group))
- {
- SIG(IKE_UP_FAILED, "DH group %N not acceptable, aborting",
- diffie_hellman_group_names, dh_group);
- return DESTROY_ME;
- }
- retry = ike_sa_init_create(this->ike_sa);
- retry->set_config(retry, this->connection, this->policy);
- this->connection = NULL;
- this->policy = NULL;
- retry->use_dh_group(retry, dh_group);
- *this->next = (transaction_t*)retry;
- return FAILED;
- }
- case NAT_DETECTION_DESTINATION_IP:
- {
- this->natd_dst_seen = TRUE;
- if (this->natd_dst_matched)
- {
- return SUCCESS;
- }
- notification_data = notify_payload->get_notification_data(notify_payload);
- if (chunk_equals(notification_data, this->natd_dst_hash))
- {
- this->natd_dst_matched = TRUE;
- DBG2(DBG_IKE, "NAT-D dst hash match");
- }
- else
- {
- DBG2(DBG_IKE, "NAT-D dst hash mismatch");
- }
- return SUCCESS;
- }
- case NAT_DETECTION_SOURCE_IP:
- {
- this->natd_src_seen = TRUE;;
- if (this->natd_src_matched)
- {
- return SUCCESS;
- }
- notification_data = notify_payload->get_notification_data(notify_payload);
- if (chunk_equals(notification_data, this->natd_src_hash))
- {
- this->natd_src_matched = TRUE;
- DBG2(DBG_IKE, "NAT-D src hash match");
- }
- else
- {
- DBG2(DBG_IKE, "NAT-D src hash mismatch");
- }
- return SUCCESS;
- }
- default:
- {
- if (notify_type < 16383)
- {
- SIG(IKE_UP_FAILED, "received %N notify error, deleting IKE_SA",
- notify_type_names, notify_type);
- return DESTROY_ME;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_ike_sa_init_t *this,
- message_t *request, message_t **result,
- transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- status_t status;
- iterator_t *payloads;
- payload_t *payload;
- sa_payload_t *sa_request = NULL;
- ke_payload_t *ke_request = NULL;
- nonce_payload_t *nonce_request = NULL;
- ike_sa_id_t *ike_sa_id;
- u_int32_t timeout;
- char name[64];
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = request->get_destination(request);
- other = request->get_source(request);
- this->message_id = request->get_message_id(request);
-
- SIG(IKE_UP_START, "establishing IKE_SA between %H...%H", me, other);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, IKE_SA_INIT);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != IKE_SA_INIT)
- {
- SIG(IKE_UP_FAILED, "IKE_SA_INIT request of invalid type, deleting IKE_SA");
- return DESTROY_ME;
- }
-
- /* this is the first message to process, find a connection for IKE_SA */
- this->connection = charon->connections->get_connection_by_hosts(
- charon->connections, me, other);
- if (this->connection == NULL)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
-
- SIG(IKE_UP_FAILED, "no connection for hosts %H...%H found, "
- "deleting IKE_SA", me, other);
- return DESTROY_ME;
- }
-
- if (snprintf(name, sizeof(name), "%s{%d}",
- this->connection->get_name(this->connection),
- this->unique_id) > 0)
- {
- this->ike_sa->set_name(this->ike_sa, name);
- }
- this->ike_sa->apply_connection(this->ike_sa, this->connection);
-
- /* Precompute NAT-D hashes for incoming NAT notify comparison */
- ike_sa_id = request->get_ike_sa_id(request);
- this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me);
- this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other);
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- ke_request = (ke_payload_t*)payload;
- break;
- case NONCE:
- nonce_request = (nonce_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, returned FAILED means do next transaction */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- payloads->destroy(payloads);
- return DESTROY_ME;
- }
- break;
- }
- default:
- {
- DBG2(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* check if we have all payloads */
- if (!(sa_request && ke_request && nonce_request))
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, INVALID_SYNTAX);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "received request message incomplete, deleting IKE_SA");
- return DESTROY_ME;
- }
-
- { /* process SA payload:
- * -------------------
- * - extract proposals
- * - select our most preferred proposal found in extracted
- * - if no matches, return NO_PROPOSAL_CHOSEN
- * - add sa payload with selected proposal
- */
- sa_payload_t* sa_response;
- linked_list_t *proposal_list;
-
- proposal_list = sa_request->get_proposals(sa_request);
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
- if (this->proposal == NULL)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "request did not contain any acceptable "
- "proposals, deleting IKE_SA");
- return DESTROY_ME;
- }
- sa_response = sa_payload_create_from_proposal(this->proposal);
- response->add_payload(response, (payload_t *)sa_response);
- }
-
- { /* process KE payload:
- * --------------------
- * - check if used group match the selected proposal
- * - if not, stop with INVALID_KE_PAYLOAD
- * - apply others public value to complete diffie hellman exchange
- * - add our public value to response
- */
- diffie_hellman_group_t used_group;
- ke_payload_t *ke_response;
-
- used_group = ke_request->get_dh_group_number(ke_request);
-
- if (!this->connection->check_dh_group(this->connection, used_group) ||
- (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL)
- {
- u_int16_t notify_group;
- chunk_t notify_chunk;
- notify_payload_t *notify;
- iterator_t *iterator;
- payload_t *payload;
-
- notify_group = this->connection->get_dh_group(this->connection);
- SIG(IKE_UP_FAILED, "request used inacceptable DH group %N, sending "
- "INVALID_KE_PAYLOAD with %N, deleting IKE_SA",
- diffie_hellman_group_names, used_group,
- diffie_hellman_group_names, notify_group);
-
- /* remove already added payloads */
- iterator = response->get_payload_iterator(response);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- iterator->remove(iterator);
- payload->destroy(payload);
- }
- iterator->destroy(iterator);
-
- notify_group = htons(notify_group);
- notify_chunk.ptr = (u_int8_t*)&notify_group;
- notify_chunk.len = sizeof(notify_group);
- notify = notify_payload_create();
- notify->set_notify_type(notify, INVALID_KE_PAYLOAD);
- notify->set_notification_data(notify, notify_chunk);
- response->add_payload(response, (payload_t*)notify);
- return DESTROY_ME;
- }
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_request->get_key_exchange_data(ke_request));
-
- /* build response */
- ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
- response->add_payload(response, (payload_t*)ke_response);
- }
-
- { /* process nonce payload:
- * ----------------------
- * - get nonce from payload
- * - generate own nonce and add to reply
- */
- nonce_payload_t *nonce_response;
-
- this->nonce_i = nonce_request->get_nonce(nonce_request);
-
- /* build response nonce */
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_r) != SUCCESS)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "could not create nonce, deleting IKE_SA");
- return DESTROY_ME;
- }
- nonce_response = nonce_payload_create();
- nonce_response->set_nonce(nonce_response, this->nonce_r);
- response->add_payload(response, (payload_t *)nonce_response);
- }
-
- { /* processs NATT stuff:
- * --------------------
- * - check if we or other is behind NAT
- * - enable NATT if so
- * - build NAT detection notifys for reply
- */
- notify_payload_t *notify;
-
- if ((!this->natd_src_seen && this->natd_dst_seen) ||
- (this->natd_src_seen && !this->natd_dst_seen))
- {
- notify = notify_payload_create();
- notify->set_notify_type(notify, INVALID_SYNTAX);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "request contained invalid number of NAT-D"
- "payloads, deleting IKE_SA");
- return DESTROY_ME;
- }
- if (this->natd_dst_seen && !this->natd_dst_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, TRUE);
- }
- if (this->natd_src_seen && !this->natd_src_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, FALSE);
- }
- /* build response NAT DETECTION notifys, if remote supports it */
- if (this->natd_src_seen || this->natd_dst_seen)
- {
- /* N(NAT_DETECTION_SOURCE_IP) */
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
- response->add_payload(response, (payload_t*)notify);
-
- /* N(NAT_DETECTION_DESTINATION_IP) */
- notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
- response->add_payload(response, (payload_t*)notify);
- }
- }
-
- /* derive all the keys used in the IKE_SA */
- if (this->ike_sa->derive_keys(this->ike_sa, this->proposal,
- this->diffie_hellman,
- this->nonce_i, this->nonce_r,
- FALSE, NULL, NULL) != SUCCESS)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "error creating transforms from proposal, deleting IKE_SA");
- return DESTROY_ME;
- }
-
- this->ike_sa->set_lifetimes(this->ike_sa,
- this->connection->get_reauth(this->connection),
- this->connection->get_soft_lifetime(this->connection),
- this->connection->get_hard_lifetime(this->connection));
-
- { /* create ike_auth transaction, which will store informations for us */
- packet_t *response_packet;
- chunk_t request_chunk, response_chunk;
- ike_auth_t *ike_auth;
-
- /* we normally do not generate the message. But we need the generated message
- * for authentication in the next state, so we do it here. This is not problematic,
- * as we don't use a crypter/signer in ike_sa_init... */
- if (response->generate(response, NULL, NULL, &response_packet) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "error in response generation, deleting IKE_SA");
- return DESTROY_ME;
- }
- response_packet->destroy(response_packet);
- request_chunk = request->get_packet_data(request);
- response_chunk = response->get_packet_data(response);
-
- /* create next transaction, for which we except a message */
- ike_auth = ike_auth_create(this->ike_sa);
- ike_auth->set_config(ike_auth, this->connection, this->policy);
- ike_auth->set_reqid(ike_auth, this->reqid);
- this->connection = NULL;
- this->policy = NULL;
- ike_auth->set_nonces(ike_auth,
- chunk_clone(this->nonce_i),
- chunk_clone(this->nonce_r));
- ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk);
- *next = (transaction_t*)ike_auth;
- }
-
- /* everything went fine. Now we set a timeout to destroy half initiated IKE_SAs */
- timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration);
- if (timeout)
- {
- job_t *job = (job_t*)delete_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- charon->event_queue->add_relative(charon->event_queue, job, timeout);
- }
- /* set new state */
- this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
-
- return SUCCESS;
-}
-
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_ike_sa_init_t *this, message_t *response,
- transaction_t **next)
-{
- u_int64_t responder_spi;
- ike_sa_id_t *ike_sa_id;
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- sa_payload_t *sa_payload = NULL;
- ke_payload_t *ke_payload = NULL;
- nonce_payload_t *nonce_payload = NULL;
- status_t status;
-
- /* check message type */
- if (response->get_exchange_type(response) != IKE_SA_INIT)
- {
- SIG(IKE_UP_FAILED, "IKE_SA_INIT response of invalid type, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* allow setting of next transaction in other functions */
- this->next = next;
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* check if SPI has been updated, but apply only if all goes ok later */
- responder_spi = response->get_responder_spi(response);
- if (responder_spi == 0)
- {
- SIG(IKE_UP_FAILED, "response contained a SPI of zero, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* Precompute NAT-D hashes for later comparison */
- ike_sa_id = response->get_ike_sa_id(response);
- this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other);
- this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me);
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- {
- sa_payload = (sa_payload_t*)payload;
- break;
- }
- case KEY_EXCHANGE:
- {
- ke_payload = (ke_payload_t*)payload;
- break;
- }
- case NONCE:
- {
- nonce_payload = (nonce_payload_t*)payload;
- break;
- }
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, returned FAILED means do next transaction */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (!(nonce_payload && sa_payload && ke_payload))
- {
- SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- { /* process SA payload:
- * -------------------
- * - get proposals from it
- * - check if peer selected a proposal
- * - verify it's selection againts our set
- */
- linked_list_t *proposal_list;
-
- /* get the list of selected proposals, the peer has to select only one proposal */
- proposal_list = sa_payload->get_proposals (sa_payload);
- if (proposal_list->get_count(proposal_list) != 1)
- {
- SIG(IKE_UP_FAILED, "response did not contain a single proposal, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
- return DESTROY_ME;
- }
-
- /* we have to re-check if the others selection is valid */
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- if (this->proposal == NULL)
- {
- SIG(IKE_UP_FAILED, "peer selected a proposal we did not offer, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
-
- { /* process KE payload:
- * -------------------
- * - extract others public value
- * - complete diffie-hellman exchange
- */
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_payload->get_key_exchange_data(ke_payload));
- }
-
- { /* process NONCE payload:
- * ----------------------
- * - extract nonce used for key derivation */
- this->nonce_r = nonce_payload->get_nonce(nonce_payload);
- }
-
- { /* process NATT stuff:
- * -------------------
- * - check if we or other is NATted
- * - switch to port 4500 if so
- */
- if ((!this->natd_dst_seen && this->natd_src_seen) ||
- (this->natd_dst_seen && !this->natd_src_seen))
- {
- SIG(IKE_UP_FAILED, "request contained invalid number of NAT-D payloads, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- if (this->natd_src_seen && !this->natd_src_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, FALSE);
- }
- if (this->natd_dst_seen && !this->natd_dst_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, TRUE);
- }
- if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- me = this->ike_sa->get_my_host(this->ike_sa);
- me->set_port(me, IKEV2_NATT_PORT);
- other = this->ike_sa->get_other_host(this->ike_sa);
- other->set_port(other, IKEV2_NATT_PORT);
-
- DBG2(DBG_IKE, "switching to port %d", IKEV2_NATT_PORT);
- }
- }
-
- /* because we are original initiator we have to update the responder SPI to the new one */
- ike_sa_id = this->ike_sa->get_id(this->ike_sa);
- ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
-
- /* derive all the keys used in the IKE_SA */
- if (this->ike_sa->derive_keys(this->ike_sa, this->proposal,
- this->diffie_hellman,
- this->nonce_i, this->nonce_r,
- TRUE, NULL, NULL) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "error creating transforms from proposal, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- this->ike_sa->set_lifetimes(this->ike_sa,
- this->connection->get_reauth(this->connection),
- this->connection->get_soft_lifetime(this->connection),
- this->connection->get_hard_lifetime(this->connection));
-
- { /* create ike_auth transaction, which will continue IKE_SA setup */
- chunk_t request_chunk, response_chunk;
- ike_auth_t *ike_auth;
-
- request_chunk = this->message->get_packet_data(this->message);
- response_chunk = response->get_packet_data(response);
-
- /* create next transaction, for which we except a message */
- ike_auth = ike_auth_create(this->ike_sa);
- ike_auth->set_config(ike_auth, this->connection, this->policy);
- ike_auth->set_reqid(ike_auth, this->reqid);
- this->connection = NULL;
- this->policy = NULL;
- ike_auth->set_nonces(ike_auth,
- chunk_clone(this->nonce_i),
- chunk_clone(this->nonce_r));
- ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk);
- *next = (transaction_t*)ike_auth;
- }
-
- return SUCCESS;
-}
-
-static void destroy(private_ike_sa_init_t *this)
-{
- DESTROY_IF(this->message);
- DESTROY_IF(this->diffie_hellman);
- DESTROY_IF(this->proposal);
- DESTROY_IF(this->connection);
- DESTROY_IF(this->policy);
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- this->randomizer->destroy(this->randomizer);
- this->nat_hasher->destroy(this->nat_hasher);
- chunk_free(&this->natd_src_hash);
- chunk_free(&this->natd_dst_hash);
- free(this);
-}
-
-/*
- * Described in header.
- */
-ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa)
-{
- static u_int unique_id = 0;
- private_ike_sa_init_t *this = malloc_thing(private_ike_sa_init_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.set_config = (void(*)(ike_sa_init_t*,connection_t*,policy_t*))set_config;
- this->public.set_reqid = (void(*)(ike_sa_init_t*,u_int32_t))set_reqid;
- this->public.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->diffie_hellman = NULL;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->connection = NULL;
- this->policy = NULL;
- this->proposal = NULL;
- this->unique_id = ++unique_id;
- this->reqid = 0;
- this->randomizer = randomizer_create();
- this->nat_hasher = hasher_create(HASH_SHA1);
- this->natd_src_hash = chunk_empty;
- this->natd_dst_hash = chunk_empty;
- this->natd_src_seen = FALSE;
- this->natd_dst_seen = FALSE;
- this->natd_src_matched = FALSE;
- this->natd_dst_matched = FALSE;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/ike_sa_init.h b/src/charon/sa/transactions/ike_sa_init.h
deleted file mode 100644
index ab820fea5..000000000
--- a/src/charon/sa/transactions/ike_sa_init.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * @file ike_sa_init.h
- *
- * @brief Interface of transaction ike_sa_init.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-
-#ifndef IKE_SA_INIT_H_
-#define IKE_SA_INIT_H_
-
-typedef struct ike_sa_init_t ike_sa_init_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction for the first message exchange to set up an IKE_SA.
- *
- * @b Constructors:
- * - ike_sa_init_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct ike_sa_init_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set connection & policy to use for initiation.
- *
- * The policy is not used directly, but forwarded to the
- * ike_auth transaction.
- *
- * @param this calling object
- * @param connection connection to use for initiation
- * @param policy policy used in ike_auth transaction
- */
- void (*set_config) (ike_sa_init_t* this,
- connection_t *connection, policy_t *policy);
-
- /**
- * @brief Set the reqid used for CHILD_SA setup.
- *
- * The first two message exchanges may set up an associated
- * CHILD_SA. If we acquire, we must use the same reqid as the
- * installed policy. This requid is passed to the ike_auth
- * transaction which creates the CHILD_AS.
- *
- * @param this calling object
- * @param reqid reqid to use for the CHILD_SA
- */
- void (*set_reqid) (ike_sa_init_t* this, u_int32_t reqid);
-
- /**
- * @brief Set the Diffie Hellman group to use for initiating.
- *
- * If a first exchange fails with a INVALID_KE_PAYLOAD, the second
- * try uses the DH group proposed by the responder.
- *
- * @param this calling object
- * @param dh_group diffie hellman group to use
- * @return FALSE, if DH group not allowed/supported
- */
- bool (*use_dh_group) (ike_sa_init_t* this, diffie_hellman_group_t dh_group);
-};
-
-/**
- * @brief Create a new transaction which processes IKE_SA_INIT exchanges.
- *
- * @param ike_sa assigned IKE_SA
- * @return created ike_sa_init transaction
- *
- * @ingroup transactions
- */
-ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa);
-
-#endif /* IKE_SA_INIT_H_ */
diff --git a/src/charon/sa/transactions/rekey_ike_sa.c b/src/charon/sa/transactions/rekey_ike_sa.c
deleted file mode 100644
index 23e7e5638..000000000
--- a/src/charon/sa/transactions/rekey_ike_sa.c
+++ /dev/null
@@ -1,889 +0,0 @@
-/**
- * @file rekey_ike_sa.c
- *
- * @brief Implementation of rekey_ike_sa_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "rekey_ike_sa.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <encoding/payloads/ke_payload.h>
-#include <sa/transactions/delete_ike_sa.h>
-#include <utils/randomizer.h>
-
-
-typedef struct private_rekey_ike_sa_t private_rekey_ike_sa_t;
-
-/**
- * Private members of a rekey_ike_sa_t object..
- */
-struct private_rekey_ike_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- rekey_ike_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * IKE_SA we set up, replaces ike_sa
- */
- ike_sa_t *new_sa;
-
- /**
- * Connection used to replace IKE_SA
- */
- connection_t *connection;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * lower of the nonces of a simultaneus rekeying request
- */
- chunk_t nonce_s;
-
- /**
- * Diffie hellman to generate new shared secret
- */
- diffie_hellman_t *diffie_hellman;
-
- /**
- * negotiated proposal to use
- */
- proposal_t *proposal;
-
- /**
- * Have we lost the simultaneous rekeying nonce compare?
- */
- bool lost;
-
- /**
- * source of randomness for nonces
- */
- randomizer_t *randomizer;
-
- /**
- * next transaction processed by the IKE_SA
- */
- transaction_t **next;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_rekey_ike_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_rekey_ike_sa_t *this)
-{
- return this->requested++;
-}
-
-
-/**
- * Implementation of rekey_ike_sa_t.use_dh_group.
- */
-static void use_dh_group(private_rekey_ike_sa_t *this, diffie_hellman_group_t dh_group)
-{
- this->diffie_hellman = diffie_hellman_create(dh_group);
-}
-
-/**
- * Implementation of rekey_ike_sa_t.cancel.
- */
-static void cancel(private_rekey_ike_sa_t *this)
-{
- this->lost = TRUE;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- /* check for correct state, except when retrying with another dh group */
- if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED &&
- !this->diffie_hellman)
- {
- DBG1(DBG_IKE, "tried to rekey in state %N, aborted",
- ike_sa_state_names, this->ike_sa->get_state(this->ike_sa));
- return FAILED;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, CREATE_CHILD_SA);
- request->set_request(request, TRUE);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- *result = request;
- this->message = request;
-
- { /* build SA payload */
- sa_payload_t *sa_payload;
- linked_list_t *proposals;
- ike_sa_id_t *ike_sa_id;
- iterator_t *iterator;
- proposal_t *proposal;
- u_int64_t spi;
-
- /* get a connection to replace current IKE_SA */
- this->connection = charon->connections->get_connection_by_name(
- charon->connections,
- this->ike_sa->get_name(this->ike_sa));
- /* if connection lookup by name fails, try it with the hosts */
- if (this->connection == NULL)
- {
- this->connection = charon->connections->get_connection_by_hosts(
- charon->connections,
- me, other);
- if (this->connection == NULL)
- {
- DBG1(DBG_IKE, "no connection found to rekey IKE_SA");
- return FAILED;
- }
- }
-
- /* create a new SA */
- ike_sa_id = ike_sa_id_create(0, 0, TRUE);
- this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
- ike_sa_id);
- spi = ike_sa_id->get_initiator_spi(ike_sa_id);
- ike_sa_id->destroy(ike_sa_id);
-
- proposals = this->connection->get_proposals(this->connection);
- iterator = proposals->create_iterator(proposals, TRUE);
- while (iterator->iterate(iterator, (void**)&proposal))
- {
- proposal->set_spi(proposal, spi);
- }
- iterator->destroy(iterator);
-
- sa_payload = sa_payload_create_from_proposal_list(proposals);
- proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- { /* build the NONCE payload for us (initiator) */
- nonce_payload_t *nonce_payload;
-
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_i) != SUCCESS)
- {
- return FAILED;
- }
- nonce_payload = nonce_payload_create();
- nonce_payload->set_nonce(nonce_payload, this->nonce_i);
- request->add_payload(request, (payload_t*)nonce_payload);
- }
-
- /* if the DH group is set via use_dh_group(), we already have a DH object */
- if (!this->diffie_hellman)
- {
- diffie_hellman_group_t dh_group;
-
- dh_group = this->connection->get_dh_group(this->connection);
- this->diffie_hellman = diffie_hellman_create(dh_group);
- if (this->diffie_hellman == NULL)
- {
- DBG1(DBG_IKE, "DH group %N not supported, aborting",
- diffie_hellman_group_names, dh_group);
- return FAILED;
- }
- }
-
- { /* build the KE payload from the DH object */
- ke_payload_t *ke_payload;
-
- ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
- request->add_payload(request, (payload_t*)ke_payload);
- }
-
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
-
- /* register us as rekeying to detect multiple rekeying */
- this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
- this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction);
-
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifys
- */
-static status_t process_notifys(private_rekey_ike_sa_t *this, notify_payload_t *notify_payload)
-{
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE,"process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- case NO_PROPOSAL_CHOSEN:
- {
- DBG1(DBG_IKE, "received a NO_PROPOSAL_CHOSEN notify, IKE_SA rekeying failed");
- return FAILED;
- }
- case INVALID_KE_PAYLOAD:
- {
- chunk_t notify_data;
- diffie_hellman_group_t dh_group, old_dh_group;
- rekey_ike_sa_t *retry;
-
- old_dh_group = this->connection->get_dh_group(this->connection);
- notify_data = notify_payload->get_notification_data(notify_payload);
- dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
-
- DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested %N",
- diffie_hellman_group_names, old_dh_group,
- diffie_hellman_group_names, dh_group);
- if (!this->connection->check_dh_group(this->connection, dh_group))
- {
- DBG1(DBG_IKE, "requested DH group not acceptable, IKE_SA rekeying failed");
- return FAILED;
- }
- retry = rekey_ike_sa_create(this->ike_sa);
- retry->use_dh_group(retry, dh_group);
- *this->next = (transaction_t*)retry;
- return FAILED;
- }
- default:
- {
- if (notify_type < 16383)
- {
- DBG1(DBG_IKE, "received %N notify error, IKE_SA rekeying failed",
- notify_type_names, notify_type);
- return FAILED;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Switch to the newly created IKE_SA
- */
-static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
-{
- identification_t *my_id, *other_id;
- host_t *my_host, *other_host;
- char *name;
-
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
- my_host = this->ike_sa->get_my_host(this->ike_sa);
- other_host = this->ike_sa->get_other_host(this->ike_sa);
- name = this->ike_sa->get_name(this->ike_sa);
-
- this->new_sa->set_my_id(this->new_sa, my_id->clone(my_id));
- this->new_sa->set_other_id(this->new_sa, other_id->clone(other_id));
- this->new_sa->set_my_host(this->new_sa, my_host->clone(my_host));
- this->new_sa->set_other_host(this->new_sa, other_host->clone(other_host));
- this->new_sa->set_name(this->new_sa, name);
-
- if (this->new_sa->derive_keys(this->new_sa, this->proposal,
- this->diffie_hellman,
- this->nonce_i, this->nonce_r, initiator,
- this->ike_sa->get_child_prf(this->ike_sa),
- this->ike_sa->get_prf(this->ike_sa)
- ) != SUCCESS)
- {
- return FAILED;
- }
-
- this->new_sa->apply_connection(this->new_sa, this->connection);
- this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
- this->new_sa->set_lifetimes(this->new_sa,
- this->connection->get_reauth(this->connection),
- this->connection->get_soft_lifetime(this->connection),
- this->connection->get_hard_lifetime(this->connection));
- return SUCCESS;
-}
-
-/**
- * Build a notify message.
- */
-static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message)
-{
- notify_payload_t *notify;
-
- if (flush_message)
- {
- payload_t *payload;
- iterator_t *iterator = message->get_payload_iterator(message);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- payload->destroy(payload);
- iterator->remove(iterator);
- }
- iterator->destroy(iterator);
- }
-
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- notify->set_notification_data(notify, data);
- message->add_payload(message, (payload_t*)notify);
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- status_t status;
- iterator_t *payloads, *iterator;
- payload_t *payload;
- child_sa_t *child_sa;
- sa_payload_t *sa_request = NULL;
- nonce_payload_t *nonce_request = NULL;
- ke_payload_t *ke_request = NULL;
- nonce_payload_t *nonce_response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, CREATE_CHILD_SA);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != CREATE_CHILD_SA)
- {
- DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted");
- return FAILED;
- }
-
- /* if we already initiate a delete, we do not allow rekeying */
- if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
- return FAILED;
- }
-
- /* if we have a CHILD which is "half-open", we do not allow rekeying */
- iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
- while (iterator->iterate(iterator, (void**)&child_sa))
- {
- child_sa_state_t state = child_sa->get_state(child_sa);
- if (state == CHILD_CREATED ||
- state == CHILD_REKEYING ||
- state == CHILD_DELETING)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
- iterator->destroy(iterator);
- return FAILED;
- }
- }
- iterator->destroy(iterator);
-
- /* apply for notify processing */
- this->next = next;
-
-
- /* get a connection to replace current IKE_SA */
- this->connection = charon->connections->get_connection_by_name(
- charon->connections, this->ike_sa->get_name(this->ike_sa));
- /* if connection lookup by name fails, try it with the hosts */
- if (this->connection == NULL)
- {
- this->connection = charon->connections->get_connection_by_hosts(
- charon->connections, me, other);
- if (this->connection == NULL)
- {
- DBG1(DBG_IKE, "no connection found to rekey IKE_SA, sending NO_RROPOSAL_CHOSEN");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- }
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_request = (nonce_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- {
- ke_request = (ke_payload_t*)payload;
- break;
- }
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* check if we have all payloads */
- if (!(sa_request && nonce_request && ke_request))
- {
- build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "request message incomplete, IKE_SA rekeying failed");
- return FAILED;
- }
-
- { /* process nonce payload */
- this->nonce_i = nonce_request->get_nonce(nonce_request);
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_r) != SUCCESS)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- nonce_response = nonce_payload_create();
- nonce_response->set_nonce(nonce_response, this->nonce_r);
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
- sa_payload_t *sa_response;
- u_int64_t spi;
- ike_sa_id_t *ike_sa_id;
-
- sa_response = sa_payload_create();
- /* get proposals from request, and select one with ours */
- proposal_list = sa_request->get_proposals(sa_request);
- DBG2(DBG_IKE, "selecting proposals:");
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* do we have a proposal? */
- if (this->proposal == NULL)
- {
- DBG1(DBG_IKE, "no proposals acceptable to rekey IKE_SA, sending NO_PROPOSAL_CHOSEN");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
-
- /* create IKE_SA with new SPIs */
- spi = this->proposal->get_spi(this->proposal);
- ike_sa_id = ike_sa_id_create(spi, 0, FALSE);
- this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
- ike_sa_id);
- spi = ike_sa_id->get_responder_spi(ike_sa_id);
- ike_sa_id->destroy(ike_sa_id);
- this->proposal->set_spi(this->proposal, spi);
-
- sa_response->add_proposal(sa_response, this->proposal);
- response->add_payload(response, (payload_t*)sa_response);
- /* add nonce after sa payload */
- response->add_payload(response, (payload_t *)nonce_response);
- }
-
- { /* process KE payload */
- diffie_hellman_group_t used_group;
- ke_payload_t *ke_response;
-
- used_group = ke_request->get_dh_group_number(ke_request);
-
- if (!this->connection->check_dh_group(this->connection, used_group) ||
- (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL)
- {
- u_int16_t notify_group;
- chunk_t notify_chunk;
-
- notify_group = this->connection->get_dh_group(this->connection);
- DBG1(DBG_IKE, "request used inacceptable DH group %N, sending "
- "INVALID_KE_PAYLOAD with %N",
- diffie_hellman_group_names, used_group,
- diffie_hellman_group_names, notify_group);
-
- notify_group = htons(notify_group);
- notify_chunk.ptr = (u_int8_t*)&notify_group;
- notify_chunk.len = sizeof(notify_group);
- build_notify(INVALID_KE_PAYLOAD, notify_chunk, response, TRUE);
- return FAILED;
- }
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_request->get_key_exchange_data(ke_request));
-
- /* build response */
- ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
- response->add_payload(response, (payload_t*)ke_response);
- }
-
- status = switchto_new_sa(this, FALSE);
- if (status != SUCCESS)
- {
- return status;
- }
-
- /* IKE_SA successfully created. If another transaction is already rekeying
- * this SA, our lower nonce must be registered for a later nonce compare. */
- {
- private_rekey_ike_sa_t *other;
-
- other = (private_rekey_ike_sa_t*)
- this->ike_sa->get_rekeying_transaction(this->ike_sa);
- if (other)
- {
- /* store our lower nonce in the simultaneus transaction, we
- * will later compare it against his nonces when we calls conclude().
- * We do not adopt childrens yet, as we don't know if we'll win
- * the race...
- */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- other->nonce_s = chunk_clone(this->nonce_i);
- }
- else
- {
- other->nonce_s = chunk_clone(this->nonce_r);
- }
- /* overwrite "other" in IKE_SA, allows "other" to access "this" */
- this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction);
- }
- else
- {
- /* if we have no simultaneus transaction, we can safely adopt
- * all children and complete. */
- this->new_sa->adopt_children(this->new_sa, this->ike_sa);
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
- this->new_sa = NULL;
- }
- this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
- }
-
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
- transaction_t **next)
-{
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- sa_payload_t *sa_payload = NULL;
- nonce_payload_t *nonce_payload = NULL;
- ke_payload_t *ke_payload = NULL;
- private_rekey_ike_sa_t *other_trans;
- status_t status;
-
- /* check message type */
- if (response->get_exchange_type(response) != CREATE_CHILD_SA)
- {
- DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborting");
- return FAILED;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* apply for notify processing */
- this->next = next;
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_payload = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_payload = (nonce_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- ke_payload = (ke_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (!(sa_payload && nonce_payload && ke_payload))
- {
- DBG1(DBG_IKE, "response message incomplete, rekeying IKE_SA failed");
- return FAILED;
- }
-
- { /* process NONCE payload */
- this->nonce_r = nonce_payload->get_nonce(nonce_payload);
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
- ike_sa_id_t *ike_sa_id;
- u_int64_t spi;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- /* we have to re-check here if other's selection is valid */
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- if (this->proposal == NULL)
- {
- DBG1(DBG_IKE, "no proposal selected, rekeying IKE_SA failed");
- return FAILED;
- }
- spi = this->proposal->get_spi(this->proposal);
- ike_sa_id = this->new_sa->get_id(this->new_sa);
- ike_sa_id->set_responder_spi(ike_sa_id, spi);
- }
-
- { /* process KE payload */
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_payload->get_key_exchange_data(ke_payload));
- }
-
- if (switchto_new_sa(this, TRUE) != SUCCESS)
- {
- /* this should not happen. But if, we destroy the new SAs */
- this->new_sa->set_state(this->new_sa, IKE_REKEYING);
- *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
- return FAILED;
- }
-
- /* IKE_SA successfully created. If the other peer initiated rekeying
- * in the meantime, we detect this by comparing the rekeying_transaction
- * of the SA. If it changed, we are not alone. Then we must compare the nonces.
- * If no simultaneous rekeying is going on, we just initiate the delete of
- * the superseded SA. */
- other_trans = (private_rekey_ike_sa_t*)
- this->ike_sa->get_rekeying_transaction(this->ike_sa);
- this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
-
- if (this->nonce_s.ptr)
- { /* simlutaneous rekeying is going on, not so good */
- chunk_t this_lowest;
-
- /* first get our lowest nonce */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- this_lowest = this->nonce_i;
- }
- else
- {
- this_lowest = this->nonce_r;
- }
- /* then compare against other lowest nonce */
- if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
- min(this_lowest.len, this->nonce_s.len)) < 0)
- {
- DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, deleting ours");
- this->lost = TRUE;
- }
- else
- {
- DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, but ours is preferred");
- }
- if (this->lost)
- {
- /* the other has won, he gets our children */
- other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
- /* we have lost simlutaneous rekeying, delete the SA we just have created */
- this->new_sa->set_state(this->new_sa, IKE_REKEYING);
- *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
- }
- else
- {
- other_trans->new_sa->set_state(other_trans->new_sa, IKE_REKEYING);
- }
- /* other trans' SA is still not checked in, so do it now. It's SA will get
- * deleted by remote peer. */
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
- other_trans->new_sa = NULL;
- }
-
- if (!this->lost)
- {
- /* we have won. delete old IKE_SA, and migrate all children */
- this->new_sa->adopt_children(this->new_sa, this->ike_sa);
- *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
- }
-
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
- this->new_sa = NULL;
-
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_rekey_ike_sa_t *this)
-{
- if (this->new_sa)
- {
- charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
- this->new_sa);
- }
- DESTROY_IF(this->message);
- DESTROY_IF(this->connection);
- DESTROY_IF(this->diffie_hellman);
- DESTROY_IF(this->proposal);
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- chunk_free(&this->nonce_s);
- this->randomizer->destroy(this->randomizer);
- free(this);
-}
-
-/*
- * Described in header.
- */
-rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa)
-{
- private_rekey_ike_sa_t *this = malloc_thing(private_rekey_ike_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.use_dh_group = (void(*)(rekey_ike_sa_t*,diffie_hellman_group_t))use_dh_group;
- this->public.cancel = (void(*)(rekey_ike_sa_t*))cancel;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->nonce_s = chunk_empty;
- this->new_sa = NULL;
- this->lost = FALSE;
- this->connection = NULL;
- this->randomizer = randomizer_create();
- this->diffie_hellman = NULL;
- this->proposal = NULL;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/rekey_ike_sa.h b/src/charon/sa/transactions/rekey_ike_sa.h
deleted file mode 100644
index dbb65237e..000000000
--- a/src/charon/sa/transactions/rekey_ike_sa.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * @file rekey_ike_sa.h
- *
- * @brief Interface of transaction rekey_ike_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#ifndef REKEY_IKE_SA_H
-#define REKEY_IKE_SA_H
-
-typedef struct rekey_ike_sa_t rekey_ike_sa_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-#include <crypto/diffie_hellman.h>
-
-
-/**
- * @brief A transaction to rekey an established IKE_SA
- *
- * @b Constructors:
- * - rekey_ike_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct rekey_ike_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the Diffie Hellman group to use for initiating.
- *
- * If a first exchange fails with a INVALID_KE_PAYLOAD, the second
- * try uses the DH group proposed by the responder.
- *
- * @param this calling object
- * @param dh_group diffie hellman group to use
- */
- void (*use_dh_group) (rekey_ike_sa_t* this, diffie_hellman_group_t dh_group);
-
- /**
- * @brief Cancel the request.
- *
- * Cancelling the request will set a flag in the transaction.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*cancel) (rekey_ike_sa_t* this);
-};
-
-/**
- * @brief Create a new transaction to rekey an existing IKE_SA.
- *
- * @param ike_sa existing IKE_SA
- * @return created rekey_ike_sa transaction
- *
- * @ingroup transactions
- */
-rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa);
-
-#endif /* REKEY_IKE_SA_H */
diff --git a/src/charon/sa/transactions/transaction.c b/src/charon/sa/transactions/transaction.c
deleted file mode 100644
index d4da1cdcb..000000000
--- a/src/charon/sa/transactions/transaction.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/**
- * @file transaction.c
- *
- * @brief Generic contstructor for the different transaction types.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#include "transaction.h"
-
-#include <sa/child_sa.h>
-#include <sa/transactions/ike_sa_init.h>
-#include <sa/transactions/ike_auth.h>
-#include <sa/transactions/delete_ike_sa.h>
-#include <sa/transactions/create_child_sa.h>
-#include <sa/transactions/delete_child_sa.h>
-#include <sa/transactions/dead_peer_detection.h>
-#include <sa/transactions/rekey_ike_sa.h>
-#include <encoding/payloads/ts_payload.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <encoding/payloads/notify_payload.h>
-#include <encoding/payloads/delete_payload.h>
-
-/*
- * see header file
- */
-transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request)
-{
- iterator_t *iterator;
- payload_t *current;
- transaction_t *transaction = NULL;
-
- if (!request->get_request(request))
- {
- return NULL;
- }
-
- switch (request->get_exchange_type(request))
- {
- case IKE_SA_INIT:
- {
- if (ike_sa->get_state(ike_sa) == IKE_CREATED)
- {
- transaction = (transaction_t*)ike_sa_init_create(ike_sa);
- }
- break;
- }
- case IKE_AUTH:
- {
- /* IKE_AUTH is always created in IKE_SA_INIT, it never should
- * appear alone */
- break;
- }
- case CREATE_CHILD_SA:
- {
- if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
- {
- break;
- }
- /* check protocol of SA payload */
- iterator = request->get_payload_iterator(request);
- while (iterator->iterate(iterator, (void**)&current))
- {
- if (current->get_type(current) == SECURITY_ASSOCIATION)
- {
- iterator_t *prop_iter;
- proposal_substructure_t *prop_struct;
- sa_payload_t *sa_payload = (sa_payload_t*)current;
-
- prop_iter = sa_payload->create_proposal_substructure_iterator(sa_payload, TRUE);
- if (prop_iter->iterate(prop_iter, (void**)&prop_struct))
- {
- switch (prop_struct->get_protocol_id(prop_struct))
- {
- case PROTO_IKE:
- transaction = (transaction_t*)
- rekey_ike_sa_create(ike_sa);
- break;
- case PROTO_AH:
- case PROTO_ESP:
- transaction = (transaction_t*)
- create_child_sa_create(ike_sa);
- break;
- default:
- break;
- }
- }
- prop_iter->destroy(prop_iter);
- }
- if (transaction)
- {
- break;
- }
- }
- iterator->destroy(iterator);
- break;
- }
- case INFORMATIONAL:
- {
- if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
- {
- break;
- }
- u_int payload_count = 0;
- iterator = request->get_payload_iterator(request);
- while (iterator->iterate(iterator, (void**)&current))
- {
- payload_count++;
- switch (current->get_type(current))
- {
- case DELETE:
- {
- delete_payload_t *delete_payload = (delete_payload_t*)current;
- switch (delete_payload->get_protocol_id(delete_payload))
- {
- case PROTO_IKE:
- transaction = (transaction_t*)
- delete_ike_sa_create(ike_sa);
- break;
- case PROTO_AH:
- case PROTO_ESP:
- transaction = (transaction_t*)
- delete_child_sa_create(ike_sa);
- break;
- default:
- break;
- }
- break;
- }
- default:
- break;
- }
- if (transaction)
- {
- break;
- }
- }
- iterator->destroy(iterator);
- /* empty informationals are used for dead peer detection in
- * IKEv2. We use a special transaction for it. */
- if (payload_count == 0)
- {
- transaction = (transaction_t*)
- dead_peer_detection_create(ike_sa);
- }
- break;
- }
- default:
- break;
- }
- return transaction;
-}
diff --git a/src/charon/sa/transactions/transaction.h b/src/charon/sa/transactions/transaction.h
deleted file mode 100644
index 4a401e12b..000000000
--- a/src/charon/sa/transactions/transaction.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * @file transaction.h
- *
- * @brief Interface transaction_t.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-#ifndef TRANSACTION_H_
-#define TRANSACTION_H_
-
-typedef struct transaction_t transaction_t;
-
-#include <library.h>
-#include <encoding/message.h>
-#include <sa/ike_sa.h>
-
-/**
- * @brief This interface represents a transaction an established IKE_SA can do.
- *
- * To every transaction, a message ID is associated. IKEv2 uses strict message
- * IDs, which are equal for a request/response pair in a transaction.
- * An initiator of a transaction does the following:
- * - create the transaction using a specific constructor
- * - call request() to get the message for initiaton
- * - call conclude() to process received reply
- * The other peer does the following:
- * - create a transanction using the generic transaction constructor
- * - call respond() to get a reply to send
- *
- * The responder must not destroy the transaction, until the
- * initiator initiates another transaction (or a number of transactions
- * > window size). This allows us to redo a transaction in case of a
- * message loss. The initiator can destroy the the transaction once
- * the conclude() function is called.
- *
- * @b Constructors:
- * - transaction_create()
- * - ike_sa_init_create()
- * - ike_auth_create()
- *
- * @ingroup transactions
- */
-struct transaction_t {
-
- /**
- * @brief Get the request to use for initiating the transaction.
- *
- * A transaction creates a request only once. The request is stored
- * internally and may be queried multiple times for retransmission.
- * The transaction is not responsible for generating/encrypting the
- * message, this is the job of the caller. But it MAY be already
- * generated when calling get_request() the second time.
- *
- * @param this calling object
- * @param[out] request resultin request
- * @return
- * - FAILED if transaction failed
- * - DESTROY_ME if transaction failed and IKE SA
- * must be deleted
- * - SUCCESS
- */
- status_t (*get_request) (transaction_t *this, message_t **request);
-
- /**
- * @brief Build the response for a received request.
- *
- * A transaction creates a response only once for a unique request.
- * This allows the use of get_response multiple times for retransmission
- * purposes.
- * The transaction is not responsible for generating/encrypting the
- * response, nor is it responsible for decrypting/parsing the request.
- * This is the job of the caller. But the response MAY be already
- * generated when calling get_request() the second time.
- * The initiator waits for a response, so we send one in every case. This
- * means response points always to a valid message. This message
- * may not be modified or destroyed, it gets destroyed along with the
- * transaction.
- * The get_response() function may return a next transaction. This allows
- * passing of informations from one transaction to a next one.
- *
- * @param this calling object
- * @param request received request
- * @param[out] response resulting response
- * @param[out] next transaction expected as next, or NULL
- * @return
- * - FAILED if transaction failed
- * - DESTROY_ME if transaction failed and IKE SA
- * must be deleted
- * - SUCCESS
- */
- status_t (*get_response) (transaction_t *this, message_t *request,
- message_t **response, transaction_t **next);
-
- /**
- * @brief Conclude an initiated transaction with a received response.
- *
- * The response must be decrypted and parsed. The conclude function
- * may return a new transaction. This transaction has to be executed
- * next to complete a multi-exchange scenario. It allows a clean
- * transaction mechanism, as the transaction knows best whats to do
- * after it completes. It must only be executed if conclude returns
- * SUCCESS.
- *
- * @param this calling object
- * @param response received response
- * @param[out] next transaction to execute as next, or NULL
- * @return
- * - FAILED if transaction failed
- * - DESTROY_ME if transaction failed and IKE SA
- * must be deleted
- * - SUCCESS
- */
- status_t (*conclude) (transaction_t *this, message_t *response,
- transaction_t **next);
-
- /**
- * @brief Get the message ID associated with this transaction.
- *
- * Every transaction consists of a message pair with the same
- * message ID. This ID can be queried with get_message_id().
- *
- * @param this calling object
- * @return message id
- */
- u_int32_t (*get_message_id) (transaction_t *this);
-
- /**
- * @brief Times we already sent the request (retransmitted).
- *
- * The transaction stores an internal counter to see how
- * many times we sent the request. This counter is incremented
- * each time after a call to requested().
- *
- * @param this calling object
- * @return message id
- */
- u_int32_t (*requested) (transaction_t *this);
-
- /**
- * @brief Destroys a transaction_t object.
- *
- * @param this calling object
- */
- void (*destroy) (transaction_t *this);
-};
-
-/**
- * @brief Create a transaction instance based on a received request.
- *
- * Incoming requests are handled by a transaction. But as we don't
- * know what kind of transaction we use for a specific request, we use
- * a generic constructor. This constructor decides which instance will
- * handle the transaction, and creates it.
- *
- * @param ike_sa ike_sa associated with this transaction
- * @param request received request
- * @return
- * - created transaction, or
- * - NULL no transaction needed
- *
- * @ingroup transactions
- */
-transaction_t *transaction_create(ike_sa_t *ike_sa, message_t* request);
-
-#endif /* TRANSACTION_H_ */
diff --git a/src/charon/threads/kernel_interface.c b/src/charon/threads/kernel_interface.c
index 3ec2bd58e..e595108ee 100644
--- a/src/charon/threads/kernel_interface.c
+++ b/src/charon/threads/kernel_interface.c
@@ -6,7 +6,9 @@
*/
/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
+ * Copyright (C) 2006-2007 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -36,6 +38,9 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <ifaddrs.h>
#include "kernel_interface.h"
@@ -152,7 +157,21 @@ char* lookup_algorithm(kernel_algorithm_t *kernel_algo,
return NULL;
}
+typedef struct rt_refcount_t rt_refcount_t;
+struct rt_refcount_t {
+ /** Index of the interface the route is bound to */
+ int if_index;
+
+ /** Source ip of the route */
+ host_t *src_ip;
+
+ /** Destination net */
+ chunk_t dst_net;
+
+ /** Destination net prefixlen */
+ u_int8_t prefixlen;
+};
typedef struct kernel_policy_t kernel_policy_t;
@@ -170,15 +189,33 @@ struct kernel_policy_t {
/** parameters of installed policy */
struct xfrm_selector sel;
+ /** associated route installed for this policy */
+ rt_refcount_t *route;
+
/** by how many CHILD_SA's this policy is used */
u_int refcount;
};
+typedef struct vip_refcount_t vip_refcount_t;
+
+/**
+ * Reference counter for for virtual ips.
+ */
+struct vip_refcount_t {
+ /** Index of the interface the ip is bound to */
+ u_int8_t if_index;
+
+ /** The ip address */
+ host_t *ip;
+
+ /** Number of times this ip is used */
+ u_int refcount;
+};
typedef struct private_kernel_interface_t private_kernel_interface_t;
/**
- * Private Variables and Functions of kernel_interface class.
+ * Private variables and functions of kernel_interface class.
*/
struct private_kernel_interface_t {
/**
@@ -197,9 +234,14 @@ struct private_kernel_interface_t {
pthread_mutex_t pol_mutex;
/**
- * Netlink communication socket.
+ * Netlink communication socket for XFRM IPsec.
*/
- int socket;
+ int xfrm_socket;
+
+ /**
+ * Netlink communication socket for routing & addresses.
+ */
+ int rt_socket;
/**
* Process id of kernel thread
@@ -217,9 +259,14 @@ struct private_kernel_interface_t {
linked_list_t *responses;
/**
- * Thread which receives messages.
+ * Thread which receives xfrm messages.
+ */
+ pthread_t xfrm_thread;
+
+ /**
+ * Thread which receives rt messages.
*/
- pthread_t thread;
+ pthread_t rt_thread;
/**
* Mutex locks access to replies list.
@@ -230,35 +277,56 @@ struct private_kernel_interface_t {
* Condvar allows signaling of threads waiting for a reply.
*/
pthread_cond_t condvar;
-};
+
+ /**
+ * List of reference counter objects for all virtual ips.
+ */
+ linked_list_t *vips;
+ /**
+ * Mutex to lock access to vip list.
+ */
+ pthread_mutex_t vip_mutex;
+};
/**
- * Send a message down to the kernel and wait for its response
+ * Sends a message down to the kernel and waits for its response
*/
static status_t send_message(private_kernel_interface_t *this,
- struct nlmsghdr *request, struct nlmsghdr **response)
+ struct nlmsghdr *request,
+ struct nlmsghdr **response,
+ int socket)
{
size_t length;
struct sockaddr_nl addr;
request->nlmsg_seq = ++this->seq;
- request->nlmsg_pid = 0;
+ request->nlmsg_pid = getpid();
memset(&addr, 0, sizeof(struct sockaddr_nl));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0;
+
+ /*
+ // set timeout to 10 secs
+ struct timespec tm;
+ tm.tv_sec = 10;
+ */
- length = sendto(this->socket,(void *)request, request->nlmsg_len, 0,
+ length = sendto(socket,(void *)request, request->nlmsg_len, 0,
(struct sockaddr *)&addr, sizeof(addr));
+ DBG2(DBG_IKE, "%d bytes sent to kernel", length);
if (length < 0)
{
+ DBG1(DBG_IKE,"0 byte could be sent");
return FAILED;
}
else if (length != request->nlmsg_len)
{
+ DBG1(DBG_IKE,"Request length %d does not match the sent bytes %d",
+ request->nlmsg_len, length);
return FAILED;
}
@@ -290,6 +358,8 @@ static status_t send_message(private_kernel_interface_t *this,
break;
}
/* TODO: we should time out, if something goes wrong!??? */
+ //if(pthread_cond_timedwait(&(this->condvar), &(this->rep_mutex), &tm) == ETIMEDOUT)
+ // return FAILED;
pthread_cond_wait(&(this->condvar), &(this->rep_mutex));
}
@@ -299,46 +369,73 @@ static status_t send_message(private_kernel_interface_t *this,
}
/**
- * Implementation of private_kernel_interface_t.receive_messages.
+ * Reads from a netlink socket and returns the message in a buffer.
*/
-static void receive_messages(private_kernel_interface_t *this)
+static void netlink_package_receiver(int socket, unsigned char *response, int response_size)
{
- while(TRUE)
+ while (TRUE)
{
- unsigned char response[BUFFER_SIZE];
- struct nlmsghdr *hdr, *listed_response;
- while (TRUE)
+ struct sockaddr_nl addr;
+ socklen_t addr_length;
+ size_t length;
+ addr_length = sizeof(addr);
+
+ length = recvfrom(socket, response, response_size, 0, (struct sockaddr*)&addr, &addr_length);
+ if (length < 0)
{
- struct sockaddr_nl addr;
- socklen_t addr_length;
- size_t length;
-
- addr_length = sizeof(addr);
-
- length = recvfrom(this->socket, &response, sizeof(response), 0, (struct sockaddr*)&addr, &addr_length);
- if (length < 0)
- {
- if (errno == EINTR)
- {
- /* interrupted, try again */
- continue;
- }
- charon->kill(charon, "receiving from netlink socket failed");
- }
- if (!NLMSG_OK((struct nlmsghdr *)response, length))
+ if (errno == EINTR)
{
- /* bad netlink message */
+ /* interrupted, try again */
continue;
}
- if (addr.nl_pid != 0)
- {
- /* not from kernel. not interested, try another one */
- continue;
- }
- /* good message, handle it */
- break;
+ charon->kill(charon, "receiving from netlink socket failed\n");
}
+ if (!NLMSG_OK((struct nlmsghdr *)response, length))
+ {
+ /* bad netlink message */
+ continue;
+ }
+
+ if (addr.nl_pid != 0)
+ {
+ /* not from kernel. not interested, try another one */
+ continue;
+ }
+ /* good message, handle it */
+ return;
+ }
+}
+
+/**
+ * Takes a Netlink package from the response buffer and writes it to this->responses.
+ * Then it signals all waiting threads.
+ */
+static void add_to_package_list(private_kernel_interface_t *this, unsigned char *response)
+{
+ struct nlmsghdr *hdr = (struct nlmsghdr*)response;
+ /* add response to queue */
+ struct nlmsghdr *listed_response = malloc(hdr->nlmsg_len);
+ memcpy(listed_response, response, hdr->nlmsg_len);
+
+ pthread_mutex_lock(&(this->rep_mutex));
+ this->responses->insert_last(this->responses, (void*)listed_response);
+ pthread_mutex_unlock(&(this->rep_mutex));
+ /* signal ALL waiting threads */
+ pthread_cond_broadcast(&(this->condvar));
+}
+
+/**
+ * Receives packages from this->xfrm_socket and puts them to this->package_list
+ */
+static void receive_xfrm_messages(private_kernel_interface_t *this)
+{
+ while(TRUE)
+ {
+ unsigned char response[BUFFER_SIZE];
+ struct nlmsghdr *hdr;
+ netlink_package_receiver(this->xfrm_socket, response, sizeof(response));
+
/* we handle ACQUIRE and EXPIRE messages directly */
hdr = (struct nlmsghdr*)response;
if (hdr->nlmsg_type == XFRM_MSG_ACQUIRE)
@@ -396,28 +493,47 @@ static void receive_messages(private_kernel_interface_t *this)
}
charon->job_queue->add(charon->job_queue, job);
}
- /* NLMSG_ERROR is sent back for acknowledge (or on error), an
- * XFRM_MSG_NEWSA is returned when we alloc spis and when
- * updating SAs.
- * XFRM_MSG_NEWPOLICY is returned when we query a policy.
- * list these responses for the sender
- */
- else if (hdr->nlmsg_type == NLMSG_ERROR ||
- hdr->nlmsg_type == XFRM_MSG_NEWSA ||
- hdr->nlmsg_type == XFRM_MSG_NEWPOLICY)
+ /* NLMSG_ERROR is sent back for acknowledge (or on error).
+ * XFRM_MSG_NEWSA is returned when we alloc spis and when
+ * updating SAs.
+ * XFRM_MSG_NEWPOLICY is returned when we query a policy.
+ */
+ else if (hdr->nlmsg_type == NLMSG_ERROR
+ || hdr->nlmsg_type == XFRM_MSG_NEWSA
+ || hdr->nlmsg_type == XFRM_MSG_NEWPOLICY)
{
- /* add response to queue */
- listed_response = malloc(hdr->nlmsg_len);
- memcpy(listed_response, &response, hdr->nlmsg_len);
-
- pthread_mutex_lock(&(this->rep_mutex));
- this->responses->insert_last(this->responses, (void*)listed_response);
- pthread_mutex_unlock(&(this->rep_mutex));
- /* signal ALL waiting threads */
- pthread_cond_broadcast(&(this->condvar));
+ add_to_package_list(this, response);
+ }
+ /* we are not interested in anything other.
+ * anyway, move on to the next message
+ */
+ continue;
+ }
+}
+
+/**
+ * Receives packages from this->rt_socket and puts them to this->package_list
+ */
+static void receive_rt_messages(private_kernel_interface_t *this)
+{
+ while(TRUE)
+ {
+ unsigned char response[BUFFER_SIZE];
+ struct nlmsghdr *hdr;
+ netlink_package_receiver(this->rt_socket,response,BUFFER_SIZE);
+
+ hdr = (struct nlmsghdr*)response;
+ /* NLMSG_ERROR is sent back for acknowledge (or on error).
+ * RTM_NEWROUTE is returned when we add a route.
+ */
+ if (hdr->nlmsg_type == NLMSG_ERROR ||
+ hdr->nlmsg_type == RTM_NEWROUTE)
+ {
+ add_to_package_list(this, response);
}
/* we are not interested in anything other.
- * anyway, move on to the next message */
+ * anyway, move on to the next message.
+ */
continue;
}
}
@@ -464,7 +580,7 @@ static status_t get_spi(private_kernel_interface_t *this,
userspi->min = 0xc0000000;
userspi->max = 0xcFFFFFFF;
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -627,12 +743,12 @@ static status_t add_sa(private_kernel_interface_t *this,
* -> does that mean that NAT-T encap doesn't work in transport mode?
* No. The reason the kernel ignores NAT-OA is that it recomputes
* (or, rather, just ignores) the checksum. If packets pass
- * the IPSec checks it marks them "checksum ok" so OA isn't needed. */
+ * the IPsec checks it marks them "checksum ok" so OA isn't needed. */
rthdr = XFRM_RTA_NEXT(rthdr);
}
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -684,7 +800,7 @@ static status_t update_sa(
sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
sa_id->family = dst->get_family(dst);
- if (send_message(this, hdr, &update) != SUCCESS)
+ if (send_message(this, hdr, &update, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -744,7 +860,7 @@ static status_t update_sa(
}
}
- if (send_message(this, update, &response) != SUCCESS)
+ if (send_message(this, update, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
free(update);
@@ -798,7 +914,7 @@ static status_t query_sa(private_kernel_interface_t *this, host_t *dst,
sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
sa_id->family = dst->get_family(dst);
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -850,7 +966,7 @@ static status_t del_sa(private_kernel_interface_t *this, host_t *dst,
sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
sa_id->family = dst->get_family(dst);
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -880,7 +996,7 @@ static void ts2subnet(traffic_selector_t* ts,
/* there is no way to do this cleanly, as the address range may
* be anything else but a subnet. We use from_addr as subnet
* and try to calculate a usable subnet mask.
- */
+ */
int byte, bit;
bool found = FALSE;
chunk_t from, to;
@@ -891,7 +1007,8 @@ static void ts2subnet(traffic_selector_t* ts,
*mask = (size * 8);
/* go trough all bits of the addresses, beginning in the front.
- * As longer as they equal, the subnet gets larger */
+ * as long as they are equal, the subnet gets larger
+ */
for (byte = 0; byte < size; byte++)
{
for (bit = 7; bit >= 0; bit--)
@@ -963,6 +1080,106 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src,
}
/**
+ * Tries to find an ip address of a local interface that is included in the
+ * supplied traffic selector.
+ */
+static status_t find_addr_by_ts(traffic_selector_t *ts, host_t **ip)
+{
+ host_t *try = NULL;
+
+#ifdef HAVE_GETIFADDRS
+ struct ifaddrs *list;
+ struct ifaddrs *cur;
+
+ if (getifaddrs(&list) < 0)
+ {
+ return FAILED;
+ }
+
+ for (cur = list; cur != NULL; cur = cur->ifa_next)
+ {
+ if (!(cur->ifa_flags & IFF_UP) || !cur->ifa_addr)
+ {
+ /* ignore interfaces which are down or have no address assigned */
+ continue;
+ }
+
+ try = host_create_from_sockaddr(cur->ifa_addr);
+
+ if (try && ts->includes(ts, try))
+ {
+ if (ip)
+ {
+ *ip = try;
+ }
+ freeifaddrs(list);
+ return SUCCESS;
+ }
+
+ DESTROY_IF(try);
+ }
+ freeifaddrs(list);
+ return FAILED;
+#else /* !HAVE_GETIFADDRS */
+
+ /* only IPv4 supported yet */
+ if (ts->get_type != TS_IPV4_ADDR_RANGE)
+ {
+ return FAILED;
+ }
+
+ int skt = socket(PF_INET, SOCK_DGRAM, 0);
+ struct ifconf conf;
+ struct ifreq reqs[16];
+
+ conf.ifc_len = sizeof(reqs);
+ conf.ifc_req = reqs;
+
+ if (ioctl(skt, SIOCGIFCONF, &conf) == -1)
+ {
+ DBG1(DBG_NET, "checking address using ioctl() failed: %m");
+ close(skt);
+ return FAILED;
+ }
+ close(skt);
+
+ while (conf.ifc_len >= sizeof(struct ifreq))
+ {
+ /* only IPv4 supported yet */
+ if (conf.ifc_req->ifr_addr.sa_family != AF_INET)
+ {
+ continue;
+ }
+
+ try = host_create_from_sockaddr(conf.ifc_req->ifr_addr);
+
+ if (try && ts->includes(ts, try))
+ {
+ if (ip)
+ {
+ *ip = try;
+ }
+
+ return SUCCESS;
+ }
+
+ DESTROY_IF(try);
+
+ conf.ifc_len -= sizeof(struct ifreq);
+ conf.ifc_req++;
+ }
+ return FAILED;
+#endif /* HAVE_GETIFADDRS */
+}
+
+/**
+ * forward declarations
+ */
+static status_t manage_srcroute(private_kernel_interface_t*,int,int,rt_refcount_t*);
+static int get_iface(private_kernel_interface_t*,host_t*);
+static void rt_refcount_destroy(rt_refcount_t*);
+
+/**
* Implementation of kernel_interface_t.add_policy.
*/
static status_t add_policy(private_kernel_interface_t *this,
@@ -980,7 +1197,6 @@ static status_t add_policy(private_kernel_interface_t *this,
struct nlmsghdr *response;
struct xfrm_userpolicy_info *policy_info;
struct nlmsghdr *hdr;
- status_t status = SUCCESS;
/* create a policy */
policy = malloc_thing(kernel_policy_t);
@@ -1075,7 +1291,7 @@ static status_t add_policy(private_kernel_interface_t *this,
host2xfrm(src, &tmpl->saddr);
host2xfrm(dst, &tmpl->id.daddr);
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -1083,17 +1299,45 @@ static status_t add_policy(private_kernel_interface_t *this,
else if (response->nlmsg_type != NLMSG_ERROR)
{
DBG1(DBG_KNL, "netlink request XFRM_MSG_UPDPOLICY not acknowledged");
- status = FAILED;
+ free(response);
+ return FAILED;
}
else if (((struct nlmsgerr*)NLMSG_DATA(response))->error)
{
DBG1(DBG_KNL, "netlink request XFRM_MSG_UPDPOLICY got an error: %s",
strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error));
- status = FAILED;
+ free(response);
+ return FAILED;
}
+
+ if (direction == POLICY_FWD)
+ {
+ policy->route = malloc_thing(rt_refcount_t);
+ if (find_addr_by_ts(dst_ts, &policy->route->src_ip) == SUCCESS)
+ {
+ policy->route->if_index = get_iface(this, src);
+ policy->route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
+ memcpy(policy->route->dst_net.ptr, &policy->sel.saddr, policy->route->dst_net.len);
+ policy->route->prefixlen = policy->sel.prefixlen_s;
+
+ if (manage_srcroute(this, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL,
+ policy->route) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "error installing route");
+ rt_refcount_destroy(policy->route);
+ policy->route = NULL;
+ }
+ }
+ else
+ {
+ free(policy->route);
+ policy->route = NULL;
+ }
+ }
+
free(response);
- return status;
+ return SUCCESS;
}
/**
@@ -1124,7 +1368,7 @@ static status_t query_policy(private_kernel_interface_t *this,
policy_id->sel = ts2selector(src_ts, dst_ts);
policy_id->dir = direction;
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -1166,12 +1410,12 @@ static status_t del_policy(private_kernel_interface_t *this,
policy_dir_t direction)
{
kernel_policy_t *current, policy, *to_delete = NULL;
+ rt_refcount_t *route;
unsigned char request[BUFFER_SIZE];
struct nlmsghdr *response;
struct nlmsghdr *hdr;
struct xfrm_userpolicy_id *policy_id;
iterator_t *iterator;
- status_t status = SUCCESS;
DBG2(DBG_KNL, "deleting policy");
@@ -1221,9 +1465,10 @@ static status_t del_policy(private_kernel_interface_t *this,
policy_id->sel = to_delete->sel;
policy_id->dir = direction;
+ route = to_delete->route;
free(to_delete);
- if (send_message(this, hdr, &response) != SUCCESS)
+ if (send_message(this, hdr, &response, this->xfrm_socket) != SUCCESS)
{
DBG1(DBG_KNL, "netlink communication failed");
return FAILED;
@@ -1231,27 +1476,347 @@ static status_t del_policy(private_kernel_interface_t *this,
else if (response->nlmsg_type != NLMSG_ERROR)
{
DBG1(DBG_KNL, "netlink request XFRM_MSG_DELPOLICY not acknowledged");
- status = FAILED;
+ free(response);
+ return FAILED;
}
else if (((struct nlmsgerr*)NLMSG_DATA(response))->error)
{
DBG1(DBG_KNL, "netlink request XFRM_MSG_DELPOLICY got an error: %s",
strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error));
- status = FAILED;
+ free(response);
+ return FAILED;
+ }
+
+ if (route)
+ {
+ if (manage_srcroute(this, RTM_DELROUTE, 0, route) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "error uninstalling route");
+ }
+ rt_refcount_destroy(route);
}
free(response);
- return status;
+ return SUCCESS;
+}
+
+/**
+ * Sends an RT_NETLINK request to the kernel.
+ */
+static status_t send_rtrequest(private_kernel_interface_t *this, struct nlmsghdr *hdr)
+{
+ struct nlmsghdr *response;
+
+ if (send_message(this, hdr, &response, this->rt_socket) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "netlink communication failed");
+ return FAILED;
+ }
+ else if (((struct nlmsgerr*)NLMSG_DATA(response))->error)
+ {
+ DBG1(DBG_KNL, "netlink request got an error: %s (%d)",
+ strerror(-((struct nlmsgerr*)NLMSG_DATA(response))->error),
+ -((struct nlmsgerr*)NLMSG_DATA(response))->error);
+ free(response);
+ return FAILED;
+ }
+
+ free(response);
+ return SUCCESS;
+}
+
+/**
+ * Creates an rtattr and adds it to the netlink message.
+ */
+static status_t add_rtattr(struct nlmsghdr *hdr, int max_len,
+ int rta_type, void *data, int data_len)
+{
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data_len) > max_len)
+ {
+ DBG1(DBG_KNL, "netlink message exceeded bound of %d", max_len);
+ return FAILED;
+ }
+
+ rta = (struct rtattr*)(((char*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len));
+
+ rta->rta_type = rta_type;
+ rta->rta_len = RTA_LENGTH(data_len);
+ memcpy(RTA_DATA(rta), data, data_len);
+
+ hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len;
+ return SUCCESS;
+}
+
+/**
+ * Manages the creation and deletion of ip addresses on an interface.
+ * By setting the appropriate nlmsg_type, the ip will be set or unset.
+ */
+static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type,
+ int flags, int if_index, host_t *ip)
+{
+ unsigned char request[BUFFER_SIZE];
+ struct nlmsghdr *hdr;
+ struct ifaddrmsg *msg;
+ chunk_t chunk;
+
+ DBG2(DBG_KNL, "adding virtual IP %H to interface %d", ip, if_index);
+
+ memset(&request, 0, sizeof(request));
+
+ chunk = ip->get_address(ip);
+
+ hdr = (struct nlmsghdr*)request;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
+ hdr->nlmsg_type = nlmsg_type;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
+
+ msg = (struct ifaddrmsg*)NLMSG_DATA(hdr);
+ msg->ifa_family = ip->get_family(ip);
+ msg->ifa_flags = 0;
+ msg->ifa_prefixlen = 8 * chunk.len;
+ msg->ifa_scope = RT_SCOPE_UNIVERSE;
+ msg->ifa_index = if_index;
+
+ if (add_rtattr(hdr, sizeof(request), IFA_LOCAL,
+ chunk.ptr, chunk.len) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ return send_rtrequest(this, hdr);
+}
+
+static int get_iface(private_kernel_interface_t *this, host_t* ip)
+{
+ unsigned char request[BUFFER_SIZE];
+ struct nlmsghdr *hdr;
+ struct rtmsg *msg;
+ struct rtattr* rta;
+ chunk_t chunk;
+ int ifindex = 0;
+
+ DBG2(DBG_KNL, "getting interface for %H", ip);
+
+ memset(&request, 0, sizeof(request));
+
+ chunk = ip->get_address(ip);
+
+ hdr = (struct nlmsghdr*)request;
+ hdr->nlmsg_flags = NLM_F_REQUEST;
+ hdr->nlmsg_type = RTM_GETROUTE;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+
+ msg = (struct rtmsg*)NLMSG_DATA(hdr);
+ msg->rtm_family = ip->get_family(ip);
+ msg->rtm_table = 0;
+ msg->rtm_protocol = 0;
+ msg->rtm_scope = 0;
+ msg->rtm_type = 0;
+ msg->rtm_src_len = 0;
+ msg->rtm_dst_len = 8 * chunk.len;
+ msg->rtm_tos = 0;
+ msg->rtm_flags = RT_TABLE_UNSPEC | RTPROT_UNSPEC;
+
+ if (add_rtattr(hdr, sizeof(request), RTA_DST,
+ chunk.ptr, chunk.len) != SUCCESS)
+ {
+ return 0;
+ }
+
+ if(send_message(this, hdr, &hdr, this->rt_socket) != SUCCESS)
+ {
+ return 0;
+ }
+ rta = (struct rtattr*)(NLMSG_DATA(hdr) + NLMSG_LENGTH(sizeof(struct rtmsg)));
+
+ while(RTA_OK(rta, hdr->nlmsg_len))
+ {
+ if(rta->rta_type == RTA_OIF)
+ {
+ ifindex = *((int*)RTA_DATA(rta));
+ break;
+ }
+ rta = RTA_NEXT(rta, hdr->nlmsg_len);
+ }
+ free(hdr);
+ if (ifindex == 0)
+ {
+ DBG1(DBG_KNL, "address %H not reachable, unable to get interface", ip);
+ }
+ return ifindex;
+}
+
+/**
+ * Manages source routes in the routing table.
+ * By setting the appropriate nlmsg_type, the route will be set or unset.
+ */
+static status_t manage_srcroute(private_kernel_interface_t *this,
+ int nlmsg_type, int flags, rt_refcount_t *route)
+{
+ struct nlmsghdr *hdr;
+ struct rtmsg *msg;
+ unsigned char request[BUFFER_SIZE];
+ chunk_t src;
+
+ memset(&request, 0, sizeof(request));
+
+ hdr = (struct nlmsghdr*)request;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | flags;
+ hdr->nlmsg_type = nlmsg_type;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+
+ msg = (struct rtmsg*)NLMSG_DATA(hdr);
+ msg->rtm_family = route->src_ip->get_family(route->src_ip);
+ msg->rtm_dst_len = route->prefixlen;
+ msg->rtm_table = RT_TABLE_MAIN;
+ msg->rtm_protocol = RTPROT_STATIC;
+ msg->rtm_type = RTN_UNICAST;
+ msg->rtm_scope = RT_SCOPE_UNIVERSE;
+
+ src = route->src_ip->get_address(route->src_ip);
+
+ if (add_rtattr(hdr, sizeof(request), RTA_DST,
+ route->dst_net.ptr, route->dst_net.len) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ if (add_rtattr(hdr, sizeof(request), RTA_PREFSRC,
+ src.ptr, src.len) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ if (add_rtattr(hdr, sizeof(request), RTA_OIF,
+ &route->if_index, sizeof(route->if_index)) != SUCCESS)
+ {
+ return FAILED;
+ }
+
+ return send_rtrequest(this, hdr);
+}
+
+/**
+ * destroy an rt_refcount object
+ */
+static void rt_refcount_destroy(rt_refcount_t *this)
+{
+ this->src_ip->destroy(this->src_ip);
+ chunk_free(&this->dst_net);
+ free(this);
+}
+
+/**
+ * destroy a vip_refcount object
+ */
+static void vip_refcount_destroy(vip_refcount_t *this)
+{
+ this->ip->destroy(this->ip);
+ free(this);
+}
+
+/**
+ * Implementation of kernel_interface_t.add_ip.
+ */
+static status_t add_ip(private_kernel_interface_t *this,
+ host_t *virtual_ip, host_t *dst_ip)
+{
+ int targetif;
+ vip_refcount_t *listed;
+ iterator_t *iterator;
+
+ DBG2(DBG_KNL, "adding ip addr: %H", virtual_ip);
+
+ targetif = get_iface(this, dst_ip);
+ if (targetif == 0)
+ {
+ return FAILED;
+ }
+
+ /* beware of deadlocks (e.g. send/receive packets while holding the lock) */
+ iterator = this->vips->create_iterator_locked(this->vips, &(this->vip_mutex));
+ while (iterator->iterate(iterator, (void**)&listed))
+ {
+ if (listed->if_index == targetif &&
+ virtual_ip->ip_equals(virtual_ip, listed->ip))
+ {
+ listed->refcount++;
+ iterator->destroy(iterator);
+ return SUCCESS;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (manage_ipaddr(this, RTM_NEWADDR, NLM_F_CREATE | NLM_F_EXCL,
+ targetif, virtual_ip) == SUCCESS)
+ {
+ listed = malloc_thing(vip_refcount_t);
+ listed->ip = virtual_ip->clone(virtual_ip);
+ listed->if_index = targetif;
+ listed->refcount = 1;
+ this->vips->insert_last(this->vips, listed);
+ return SUCCESS;
+ }
+
+ return FAILED;
+}
+
+/**
+ * Implementation of kernel_interface_t.del_ip.
+ */
+static status_t del_ip(private_kernel_interface_t *this,
+ host_t *virtual_ip, host_t *dst_ip)
+{
+ int targetif;
+ vip_refcount_t *listed;
+ iterator_t *iterator;
+
+ DBG2(DBG_KNL, "deleting ip addr: %H", virtual_ip);
+
+ targetif = get_iface(this, dst_ip);
+ if (targetif == 0)
+ {
+ return FAILED;
+ }
+
+ /* beware of deadlocks (e.g. send/receive packets while holding the lock) */
+ iterator = this->vips->create_iterator_locked(this->vips, &(this->vip_mutex));
+ while (iterator->iterate(iterator, (void**)&listed))
+ {
+ if (listed->if_index == targetif &&
+ virtual_ip->ip_equals(virtual_ip, listed->ip))
+ {
+ listed->refcount--;
+ if (listed->refcount == 0)
+ {
+ iterator->remove(iterator);
+ vip_refcount_destroy(listed);
+ iterator->destroy(iterator);
+ return manage_ipaddr(this, RTM_DELADDR, 0, targetif, virtual_ip);
+ }
+ iterator->destroy(iterator);
+ return SUCCESS;
+ }
+ }
+ iterator->destroy(iterator);
+
+ return FAILED;
}
/**
* Implementation of kernel_interface_t.destroy.
*/
static void destroy(private_kernel_interface_t *this)
-{
- pthread_cancel(this->thread);
- pthread_join(this->thread, NULL);
- close(this->socket);
+{
+ pthread_cancel(this->xfrm_thread);
+ pthread_join(this->xfrm_thread, NULL);
+ pthread_cancel(this->rt_thread);
+ pthread_join(this->rt_thread, NULL);
+ close(this->xfrm_socket);
+ close(this->rt_socket);
+ this->vips->destroy_function(this->vips, (void*)vip_refcount_destroy);
this->responses->destroy(this->responses);
this->policies->destroy(this->policies);
free(this);
@@ -1262,7 +1827,8 @@ static void destroy(private_kernel_interface_t *this)
*/
kernel_interface_t *kernel_interface_create()
{
- struct sockaddr_nl addr;
+ struct sockaddr_nl addr_xfrm;
+ struct sockaddr_nl addr_rt;
private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t);
/* public functions */
@@ -1274,45 +1840,88 @@ kernel_interface_t *kernel_interface_create()
this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t,bool))add_policy;
this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy;
+ this->public.add_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) add_ip;
+ this->public.del_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) del_ip;
this->public.destroy = (void(*)(kernel_interface_t*)) destroy;
/* private members */
this->pid = getpid();
this->responses = linked_list_create();
+ this->vips = linked_list_create();
this->policies = linked_list_create();
pthread_mutex_init(&(this->rep_mutex),NULL);
pthread_mutex_init(&(this->pol_mutex),NULL);
+ pthread_mutex_init(&(this->vip_mutex),NULL);
pthread_cond_init(&(this->condvar),NULL);
this->seq = 0;
- /* open netlink socket */
- this->socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM);
- if (this->socket <= 0)
+ /* open xfrm netlink socket */
+ this->xfrm_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_XFRM);
+ if (this->xfrm_socket <= 0)
{
- this->responses->destroy(this->responses);
- free(this);
- charon->kill(charon, "Unable to create netlink socket");
+ DBG1(DBG_KNL, "Unable to create xfrm netlink socket");
+ goto kill;
}
- /* bind the socket and reqister for ACQUIRE & EXPIRE */
- addr.nl_family = AF_NETLINK;
- addr.nl_pid = getpid();
- addr.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE;
- if (bind(this->socket, (struct sockaddr*)&addr, sizeof(addr)) != 0)
+ /* bind the xfrm socket and reqister for ACQUIRE & EXPIRE */
+ addr_xfrm.nl_family = AF_NETLINK;
+ addr_xfrm.nl_pid = getpid();
+ addr_xfrm.nl_groups = XFRMGRP_ACQUIRE | XFRMGRP_EXPIRE;
+ if (bind(this->xfrm_socket, (struct sockaddr*)&addr_xfrm, sizeof(addr_xfrm)))
{
- this->responses->destroy(this->responses);
- close(this->socket);
- free(this);
- charon->kill(charon, "Unable to bind netlink socket");
+ DBG1(DBG_KNL, "Unable to bind xfrm netlink socket");
+ goto kill_xfrm;
}
- if (pthread_create(&this->thread, NULL, (void*(*)(void*))receive_messages, this) != 0)
+ if (pthread_create(&this->xfrm_thread, NULL,
+ (void*(*)(void*))receive_xfrm_messages, this))
+ {
+ DBG1(DBG_KNL, "Unable to create xfrm netlink thread");
+ goto kill_xfrm;
+ }
+
+ /* open rt netlink socket */
+ this->rt_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (this->rt_socket <= 0)
+ {
+ DBG1(DBG_KNL, "Unable to create rt netlink socket");
+ goto kill_xfrm_all;
+ }
+
+ /* bind the socket_rt */
+ addr_rt.nl_family = AF_NETLINK;
+ addr_rt.nl_pid = getpid();
+ addr_rt.nl_groups = 0;
+ if (bind(this->rt_socket, (struct sockaddr*)&addr_rt, sizeof(addr_rt)))
{
- this->responses->destroy(this->responses);
- close(this->socket);
- free(this);
- charon->kill(charon, "Unable to create netlink thread");
+ DBG1(DBG_KNL, "Unable to bind rt netlink socket");
+ goto kill_rt;
}
+ if (pthread_create(&this->rt_thread, NULL,
+ (void*(*)(void*))receive_rt_messages, this))
+ {
+ DBG1(DBG_KNL, "Unable to create rt netlink thread");
+ goto kill_rt;
+ }
+
return &this->public;
+
+kill_rt:
+ close(this->rt_socket);
+kill_xfrm_all:
+ pthread_cancel(this->xfrm_thread);
+ pthread_join(this->xfrm_thread, NULL);
+kill_xfrm:
+ close(this->xfrm_socket);
+kill:
+ this->responses->destroy(this->responses);
+ this->policies->destroy(this->policies);
+ this->vips->destroy(this->vips);
+ free(this);
+ charon->kill(charon, "Unable to create kernel_interface");
+ return NULL;
}
+
+/* vim: set ts=4 sw=4 noet: */
+
diff --git a/src/charon/threads/kernel_interface.h b/src/charon/threads/kernel_interface.h
index 991d8e17c..805a2b89d 100644
--- a/src/charon/threads/kernel_interface.h
+++ b/src/charon/threads/kernel_interface.h
@@ -264,6 +264,40 @@ struct kernel_interface_t {
policy_dir_t direction);
/**
+ * @brief Add a virtual IP to an interface.
+ *
+ * Virtual IPs are attached to an interface. If an IP is added multiple
+ * times, the IP is refcounted and not removed until del_ip() was called
+ * as many times as add_ip().
+ * The virtual IP is attached to the interface used to reach a specified
+ * destination host.
+ *
+ * @param this calling object
+ * @param virtual_ip virtual ip address to assign
+ * @param dst_ip destination host to select outgoing interface
+ * @return
+ * - SUCCESS
+ * - FAILED if kernel comm failed
+ */
+ status_t (*add_ip) (kernel_interface_t *this, host_t *virtual_ip,
+ host_t *dst_ip);
+
+ /**
+ * @brief Remove a virtual IP from an interface.
+ *
+ * The kernel interface uses refcounting, see add_ip().
+ *
+ * @param this calling object
+ * @param virtual_ip virtual ip address to assign
+ * @param dst_ip destination host to select outgoing interface
+ * @return
+ * - SUCCESS
+ * - FAILED if kernel comm failed
+ */
+ status_t (*del_ip) (kernel_interface_t *this, host_t *virtual_ip,
+ host_t *dst_ip);
+
+ /**
* @brief Destroys a kernel_interface object.
*
* @param kernel_interface_t calling object
diff --git a/src/charon/threads/stroke_interface.c b/src/charon/threads/stroke_interface.c
index a4ae03301..c491be975 100755
--- a/src/charon/threads/stroke_interface.c
+++ b/src/charon/threads/stroke_interface.c
@@ -197,6 +197,7 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out)
bool my_ca_same = FALSE;
bool other_ca_same =FALSE;
host_t *my_host, *other_host, *my_subnet, *other_subnet;
+ host_t *my_vip = NULL, *other_vip = NULL;
proposal_t *proposal;
traffic_selector_t *my_ts, *other_ts;
@@ -205,6 +206,8 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out)
pop_string(msg, &msg->add_conn.other.address);
pop_string(msg, &msg->add_conn.me.subnet);
pop_string(msg, &msg->add_conn.other.subnet);
+ pop_string(msg, &msg->add_conn.me.sourceip);
+ pop_string(msg, &msg->add_conn.other.sourceip);
pop_string(msg, &msg->add_conn.me.id);
pop_string(msg, &msg->add_conn.other.id);
pop_string(msg, &msg->add_conn.me.cert);
@@ -223,6 +226,8 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out)
DBG2(DBG_CFG, " right=%s", msg->add_conn.other.address);
DBG2(DBG_CFG, " leftsubnet=%s", msg->add_conn.me.subnet);
DBG2(DBG_CFG, " rightsubnet=%s", msg->add_conn.other.subnet);
+ DBG2(DBG_CFG, " leftsourceip=%s", msg->add_conn.me.sourceip);
+ DBG2(DBG_CFG, " rightsourceip=%s", msg->add_conn.other.sourceip);
DBG2(DBG_CFG, " leftid=%s", msg->add_conn.me.id);
DBG2(DBG_CFG, " rightid=%s", msg->add_conn.other.id);
DBG2(DBG_CFG, " leftcert=%s", msg->add_conn.me.cert);
@@ -303,7 +308,13 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out)
my_subnet->destroy(my_subnet);
goto destroy_ids;
}
-
+
+ if (msg->add_conn.me.virtual_ip)
+ {
+ my_vip = host_create_from_string(msg->add_conn.me.sourceip, 0);
+ }
+ other_vip = host_create_from_string(msg->add_conn.other.sourceip, 0);
+
my_ts = traffic_selector_create_from_subnet(my_subnet,
msg->add_conn.me.subnet ? msg->add_conn.me.subnet_mask : 0,
msg->add_conn.me.protocol, msg->add_conn.me.port);
@@ -429,7 +440,7 @@ static void stroke_add_conn(stroke_msg_t *msg, FILE *out)
connection->add_proposal(connection, proposal);
}
- policy = policy_create(msg->add_conn.name, my_id, other_id,
+ policy = policy_create(msg->add_conn.name, my_id, other_id, my_vip, other_vip,
msg->add_conn.auth_method, msg->add_conn.eap_type,
msg->add_conn.rekey.ipsec_lifetime,
msg->add_conn.rekey.ipsec_lifetime - msg->add_conn.rekey.margin,
@@ -559,16 +570,7 @@ static void stroke_initiate(stroke_msg_t *msg, FILE *out)
return;
}
- job = initiate_job_create(connection, NULL, policy);
- /*
- if (msg->output_verbosity < 0)
- {
- TODO: detach immediately if verbosity is SILENT. Local credential store
- is not threadsave yet, so this would cause crashes!!
- charon->job_queue->add(charon->job_queue, (job_t*)job);
- return;
-}*/
-
+ job = initiate_job_create(connection, policy);
charon->bus->set_listen_state(charon->bus, TRUE);
charon->job_queue->add(charon->job_queue, (job_t*)job);
while (TRUE)
@@ -664,10 +666,100 @@ static void stroke_route(stroke_msg_t *msg, FILE *out, bool route)
*/
static void stroke_terminate(stroke_msg_t *msg, FILE *out)
{
+ char *string, *pos = NULL, *name = NULL;
+ u_int32_t id = 0;
+ bool child;
+ int len;
+ status_t status = SUCCESS;;
+ ike_sa_t *ike_sa;
+
pop_string(msg, &(msg->terminate.name));
- DBG1(DBG_CFG, "received stroke: terminate '%s'", msg->terminate.name);
+ string = msg->terminate.name;
+ DBG1(DBG_CFG, "received stroke: terminate '%s'", string);
- charon->ike_sa_manager->delete_by_name(charon->ike_sa_manager, msg->terminate.name);
+ len = strlen(string);
+ if (len < 1)
+ {
+ DBG1(DBG_CFG, "error parsing string");
+ return;
+ }
+ switch (string[len-1])
+ {
+ case '}':
+ child = TRUE;
+ pos = strchr(string, '{');
+ break;
+ case ']':
+ child = FALSE;
+ pos = strchr(string, '[');
+ break;
+ default:
+ name = string;
+ child = FALSE;
+ break;
+ }
+
+ if (name)
+ { /* must be a single name */
+ DBG1(DBG_CFG, "check out by single name '%s'", name);
+ ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager,
+ name, child);
+ }
+ else if (pos == string + len - 2)
+ { /* must be name[] or name{} */
+ string[len-2] = '\0';
+ DBG1(DBG_CFG, "check out by name '%s'", string);
+ ike_sa = charon->ike_sa_manager->checkout_by_name(charon->ike_sa_manager,
+ string, child);
+ }
+ else
+ { /* must be name[123] or name{23} */
+ string[len-1] = '\0';
+ id = atoi(pos + 1);
+ if (id == 0)
+ {
+ DBG1(DBG_CFG, "error parsing string");
+ return;
+ }
+ DBG1(DBG_CFG, "check out by id '%d'", id);
+ ike_sa = charon->ike_sa_manager->checkout_by_id(charon->ike_sa_manager,
+ id, child);
+ }
+ if (ike_sa == NULL)
+ {
+ DBG1(DBG_CFG, "no such IKE_SA found");
+ return;
+ }
+
+ if (!child)
+ {
+ status = ike_sa->delete(ike_sa);
+ }
+ else
+ {
+ child_sa_t *child_sa;
+ iterator_t *iterator = ike_sa->create_child_sa_iterator(ike_sa);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ if ((id && id == child_sa->get_reqid(child_sa)) ||
+ (string && streq(string, child_sa->get_name(child_sa))))
+ {
+ u_int32_t spi = child_sa->get_spi(child_sa, TRUE);
+ protocol_id_t proto = child_sa->get_protocol(child_sa);
+
+ status = ike_sa->delete_child_sa(ike_sa, proto, spi);
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ }
+ if (status == DESTROY_ME)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ ike_sa);
+ return;
+ }
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
}
/**
diff --git a/src/libstrongswan/utils/host.c b/src/libstrongswan/utils/host.c
index f9be454fa..8cbfd6ab8 100644
--- a/src/libstrongswan/utils/host.c
+++ b/src/libstrongswan/utils/host.c
@@ -6,7 +6,8 @@
*/
/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2006-2007 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -45,7 +46,7 @@ struct private_host_t {
union {
/** generic type */
struct sockaddr address;
- /** maximux sockaddr size */
+ /** maximum sockaddr size */
struct sockaddr_storage address_max;
/** IPv4 address */
struct sockaddr_in address4;
@@ -495,3 +496,31 @@ host_t *host_create_from_sockaddr(sockaddr_t *sockaddr)
free(this);
return NULL;
}
+
+/*
+ * Described in header.
+ */
+host_t *host_create_any(int family)
+{
+ private_host_t *this = host_create_empty();
+
+ memset(&this->address_max, 0, sizeof(struct sockaddr_storage));
+ this->address.sa_family = family;
+
+ switch (family)
+ {
+ case AF_INET:
+ {
+ this->socklen = sizeof(struct sockaddr_in);
+ return &(this->public);
+ }
+ case AF_INET6:
+ {
+ this->socklen = sizeof(struct sockaddr_in6);
+ return &this->public;
+ }
+ default:
+ break;
+ }
+ return NULL;
+}
diff --git a/src/libstrongswan/utils/host.h b/src/libstrongswan/utils/host.h
index 20b5c6345..461300438 100644
--- a/src/libstrongswan/utils/host.h
+++ b/src/libstrongswan/utils/host.h
@@ -6,7 +6,8 @@
*/
/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Copyright (C) 2006-2007 Tobias Brunner
+ * Copyright (C) 2006 Daniel Roethlisberger
* Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -216,4 +217,16 @@ host_t *host_create_from_chunk(int family, chunk_t address, u_int16_t port);
*/
host_t *host_create_from_sockaddr(sockaddr_t *sockaddr);
+/**
+ * @brief Create a host without an address, a "any" host.
+ *
+ * @param family family of the any host
+ * @return
+ * - host_t object
+ * - NULL, if family not supported.
+ *
+ * @ingroup network
+ */
+host_t *host_create_any(int family);
+
#endif /*HOST_H_*/
diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c
index 6c77b41e8..341af39c0 100644
--- a/src/libstrongswan/utils/identification.c
+++ b/src/libstrongswan/utils/identification.c
@@ -497,7 +497,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
bool next_a, next_b;
/* initialize wildcard counter */
- *wildcards = 0;
+ if (wildcards)
+ {
+ *wildcards = 0;
+ }
/* initialize DN parsing */
if (init_rdn(a, &rdn_a, &attribute_a, &next_a) != SUCCESS
@@ -522,7 +525,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
/* does rdn_b contain a wildcard? */
if (value_b.len == 1 && *value_b.ptr == '*')
{
- (*wildcards)++;
+ if (wildcards)
+ {
+ (*wildcards)++;
+ }
continue;
}
/* same lengths for values */
@@ -549,7 +555,10 @@ bool match_dn(chunk_t a, chunk_t b, int *wildcards)
}
/* the two DNs match! */
- *wildcards = min(*wildcards, MAX_WILDCARDS);
+ if (wildcards)
+ {
+ *wildcards = min(*wildcards, MAX_WILDCARDS);
+ }
return TRUE;
}
@@ -750,10 +759,16 @@ static bool matches_binary(private_identification_t *this,
{
if (other->type == ID_ANY)
{
- *wildcards = MAX_WILDCARDS;
+ if (wildcards)
+ {
+ *wildcards = MAX_WILDCARDS;
+ }
return TRUE;
}
- *wildcards = 0;
+ if (wildcards)
+ {
+ *wildcards = 0;
+ }
return this->type == other->type &&
chunk_equals(this->encoded, other->encoded);
}
@@ -769,7 +784,10 @@ static bool matches_string(private_identification_t *this,
if (other->type == ID_ANY)
{
- *wildcards = MAX_WILDCARDS;
+ if (wildcards)
+ {
+ *wildcards = MAX_WILDCARDS;
+ }
return TRUE;
}
@@ -779,7 +797,10 @@ static bool matches_string(private_identification_t *this,
/* try a binary comparison first */
if (equals_binary(this, other))
{
- *wildcards = 0;
+ if (wildcards)
+ {
+ *wildcards = 0;
+ }
return TRUE;
}
@@ -789,7 +810,10 @@ static bool matches_string(private_identification_t *this,
/* check for single wildcard at the head of the string */
if (*other->encoded.ptr == '*')
{
- *wildcards = 1;
+ if (wildcards)
+ {
+ *wildcards = 1;
+ }
/* single asterisk matches any string */
if (len-- == 1)
@@ -809,7 +833,10 @@ static bool matches_string(private_identification_t *this,
static bool matches_any(private_identification_t *this,
private_identification_t *other, int *wildcards)
{
- *wildcards = 0;
+ if (wildcards)
+ {
+ *wildcards = 0;
+ }
return other->type == ID_ANY;
}
@@ -822,7 +849,10 @@ static bool matches_dn(private_identification_t *this,
{
if (other->type == ID_ANY)
{
- *wildcards = MAX_WILDCARDS;
+ if (wildcards)
+ {
+ *wildcards = MAX_WILDCARDS;
+ }
return TRUE;
}
diff --git a/src/libstrongswan/utils/identification.h b/src/libstrongswan/utils/identification.h
index 80fc27d7c..59c568eaf 100644
--- a/src/libstrongswan/utils/identification.h
+++ b/src/libstrongswan/utils/identification.h
@@ -182,7 +182,7 @@ struct identification_t {
*
* @param this the ID without wildcard
* @param other the ID containing a wildcard
- * @param wildcards returns the number of wildcards
+ * @param wildcards returns the number of wildcards, may be NULL
* @return TRUE if match is found
*/
bool (*matches) (identification_t *this, identification_t *other, int *wildcards);
diff --git a/src/libstrongswan/utils/iterator.h b/src/libstrongswan/utils/iterator.h
index 51a8d6061..02a15c534 100644
--- a/src/libstrongswan/utils/iterator.h
+++ b/src/libstrongswan/utils/iterator.h
@@ -24,6 +24,19 @@
#ifndef ITERATOR_H_
#define ITERATOR_H_
+#include <library.h>
+
+/**
+ * @brief Iterator hook function prototype.
+ *
+ * @param param user supplied parameter
+ * @param in the value the hook receives from the iterator
+ * @param out the value supplied as a result to the iterator
+ * @return TRUE to return "out", FALSE to skip this value
+ */
+typedef bool (iterator_hook_t)(void *param, void *in, void **out);
+
+
typedef struct iterator_t iterator_t;
/**
@@ -76,8 +89,10 @@ struct iterator_t {
*
* @param this calling object
* @param hook iterator hook which manipulates the iterated value
+ * @param param user supplied parameter to pass back to the hook
*/
- void (*set_iterator_hook) (iterator_t *this, void*(*hook)(void*));
+ void (*set_iterator_hook) (iterator_t *this, iterator_hook_t *hook,
+ void *param);
/**
* @brief Inserts a new item before the given iterator position.
diff --git a/src/libstrongswan/utils/linked_list.c b/src/libstrongswan/utils/linked_list.c
index 8c5068870..de043a02e 100644
--- a/src/libstrongswan/utils/linked_list.c
+++ b/src/libstrongswan/utils/linked_list.c
@@ -132,7 +132,12 @@ struct private_iterator_t {
/**
* iteration hook
*/
- void* (*hook)(void*);
+ iterator_hook_t *hook;
+
+ /**
+ * user parameter for iterator hook
+ */
+ void *hook_param;
};
/**
@@ -146,23 +151,27 @@ static int get_list_count(private_iterator_t *this)
/**
* default iterator hook which does nothing
*/
-static void *iterator_hook(void *value)
+static bool iterator_hook(void *param, void *in, void **out)
{
- return value;
+ *out = in;
+ return TRUE;
}
/**
* Implementation of iterator_t.set_iterator_hook.
*/
-static void set_iterator_hook(private_iterator_t *this, void*(*hook)(void*))
+static void set_iterator_hook(private_iterator_t *this, iterator_hook_t *hook,
+ void* param)
{
if (hook == NULL)
{
this->hook = iterator_hook;
+ this->hook_param = NULL;
}
else
{
this->hook = hook;
+ this->hook_param = param;
}
}
@@ -178,7 +187,10 @@ static bool iterate(private_iterator_t *this, void** value)
if (this->current == NULL)
{
this->current = (this->forward) ? this->list->first : this->list->last;
- *value = this->hook(this->current->value);
+ if (!this->hook(this->hook_param, this->current->value, value))
+ {
+ return iterate(this, value);
+ }
return TRUE;
}
if (this->forward)
@@ -188,16 +200,21 @@ static bool iterate(private_iterator_t *this, void** value)
return FALSE;
}
this->current = this->current->next;
- *value = this->hook(this->current->value);
+ if (!this->hook(this->hook_param, this->current->value, value))
+ {
+ return iterate(this, value);
+ }
return TRUE;
}
- /* backward */
if (this->current->previous == NULL)
{
return FALSE;
}
this->current = this->current->previous;
- *value = this->hook(this->current->value);
+ if (!this->hook(this->hook_param, this->current->value, value))
+ {
+ return iterate(this, value);
+ }
return TRUE;
}
@@ -225,11 +242,15 @@ static status_t remove_(private_iterator_t *this)
{
return NOT_FOUND;
}
- /* find out the new iterator position */
- if (this->current->previous != NULL)
+ /* find out the new iterator position, depending on iterator direction */
+ if (this->forward && this->current->previous != NULL)
{
new_current = this->current->previous;
}
+ else if (!this->forward && this->current->next != NULL)
+ {
+ new_current = this->current->next;
+ }
else
{
new_current = NULL;
@@ -679,7 +700,7 @@ static iterator_t *create_iterator(private_linked_list_t *linked_list, bool forw
this->public.get_count = (int (*) (iterator_t*)) get_list_count;
this->public.iterate = (bool (*) (iterator_t*, void **value)) iterate;
- this->public.set_iterator_hook = (void(*)(iterator_t*, void*(*)(void*)))set_iterator_hook;
+ this->public.set_iterator_hook = (void(*)(iterator_t*, iterator_hook_t*, void*))set_iterator_hook;
this->public.insert_before = (void (*) (iterator_t*, void *item)) insert_before;
this->public.insert_after = (void (*) (iterator_t*, void *item)) insert_after;
this->public.replace = (status_t (*) (iterator_t*, void **, void *)) replace;
diff --git a/src/starter/confread.c b/src/starter/confread.c
index 9f1453394..9e83581fa 100644
--- a/src/starter/confread.c
+++ b/src/starter/confread.c
@@ -252,7 +252,8 @@ kw_end(starter_conn_t *conn, starter_end_t *end, kw_token_t token
plog("# natip and sourceip cannot be defined at the same time");
goto err;
}
- if (streq(value, "%modeconfig") || streq(value, "%modecfg"))
+ if (streq(value, "%modeconfig") || streq(value, "%modecfg") ||
+ streq(value, "%config") || streq(value, "%cfg"))
{
end->modecfg = TRUE;
}
diff --git a/src/starter/starterstroke.c b/src/starter/starterstroke.c
index 28d777a21..79ad33f61 100644
--- a/src/starter/starterstroke.c
+++ b/src/starter/starterstroke.c
@@ -166,6 +166,9 @@ static void starter_stroke_add_end(stroke_msg_t *msg, stroke_end_t *msg_end, sta
msg_end->hostaccess = conn_end->hostaccess;
msg_end->protocol = conn_end->protocol;
msg_end->port = conn_end->port;
+ msg_end->virtual_ip = conn_end->modecfg;
+ ip_address2string(&conn_end->srcip, buffer, sizeof(buffer));
+ msg_end->sourceip = push_string(msg, buffer);
}
int starter_stroke_add_conn(starter_conn_t *conn)
diff --git a/src/stroke/stroke.c b/src/stroke/stroke.c
index 3d186bd0c..1fed6e281 100644
--- a/src/stroke/stroke.c
+++ b/src/stroke/stroke.c
@@ -127,6 +127,8 @@ static int add_connection(char *name,
msg.add_conn.me.address = push_string(&msg, my_addr);
msg.add_conn.me.subnet = push_string(&msg, my_net);
msg.add_conn.me.subnet_mask = my_netmask;
+ msg.add_conn.me.sourceip = NULL;
+ msg.add_conn.me.virtual_ip = 0;
msg.add_conn.me.cert = NULL;
msg.add_conn.me.ca = NULL;
msg.add_conn.me.sendcert = 1;
@@ -138,6 +140,8 @@ static int add_connection(char *name,
msg.add_conn.other.address = push_string(&msg, other_addr);
msg.add_conn.other.subnet = push_string(&msg, other_net);
msg.add_conn.other.subnet_mask = other_netmask;
+ msg.add_conn.other.sourceip = NULL;
+ msg.add_conn.other.virtual_ip = 0;
msg.add_conn.other.cert = NULL;
msg.add_conn.other.ca = NULL;
msg.add_conn.other.sendcert = 1;
diff --git a/src/stroke/stroke.h b/src/stroke/stroke.h
index ced655583..57ddf29be 100644
--- a/src/stroke/stroke.h
+++ b/src/stroke/stroke.h
@@ -100,6 +100,8 @@ struct stroke_end_t {
char *ca;
char *updown;
char *address;
+ char *sourceip;
+ u_int8_t virtual_ip;
char *subnet;
int subnet_mask;
int sendcert;