aboutsummaryrefslogtreecommitdiffstats
path: root/src/libipsec
diff options
context:
space:
mode:
Diffstat (limited to 'src/libipsec')
-rw-r--r--src/libipsec/Android.mk15
-rw-r--r--src/libipsec/Makefile.am15
-rw-r--r--src/libipsec/esp_context.c300
-rw-r--r--src/libipsec/esp_context.h110
-rw-r--r--src/libipsec/esp_packet.c468
-rw-r--r--src/libipsec/esp_packet.h151
-rw-r--r--src/libipsec/ip_packet.c192
-rw-r--r--src/libipsec/ip_packet.h96
-rw-r--r--src/libipsec/ipsec.c16
-rw-r--r--src/libipsec/ipsec.h29
-rw-r--r--src/libipsec/ipsec_event_listener.h48
-rw-r--r--src/libipsec/ipsec_event_relay.c193
-rw-r--r--src/libipsec/ipsec_event_relay.h79
-rw-r--r--src/libipsec/ipsec_policy.c212
-rw-r--r--src/libipsec/ipsec_policy.h140
-rw-r--r--src/libipsec/ipsec_policy_mgr.c286
-rw-r--r--src/libipsec/ipsec_policy_mgr.h119
-rw-r--r--src/libipsec/ipsec_processor.c324
-rw-r--r--src/libipsec/ipsec_processor.h115
-rw-r--r--src/libipsec/ipsec_sa.c234
-rw-r--r--src/libipsec/ipsec_sa.h169
-rw-r--r--src/libipsec/ipsec_sa_mgr.c626
-rw-r--r--src/libipsec/ipsec_sa_mgr.h167
23 files changed, 4094 insertions, 10 deletions
diff --git a/src/libipsec/Android.mk b/src/libipsec/Android.mk
index 99ff69106..81f4632ef 100644
--- a/src/libipsec/Android.mk
+++ b/src/libipsec/Android.mk
@@ -3,14 +3,23 @@ include $(CLEAR_VARS)
# copy-n-paste from Makefile.am
LOCAL_SRC_FILES := \
-ipsec.c ipsec.h
+ipsec.c ipsec.h \
+esp_context.c esp_context.h \
+esp_packet.c esp_packet.h \
+ip_packet.c ip_packet.h \
+ipsec_event_listener.h \
+ipsec_event_relay.c ipsec_event_relay.h \
+ipsec_policy.c ipsec_policy.h \
+ipsec_policy_mgr.c ipsec_policy_mgr.h \
+ipsec_processor.c ipsec_processor.h \
+ipsec_sa.c ipsec_sa.h \
+ipsec_sa_mgr.c ipsec_sa_mgr.h
# build libipsec ---------------------------------------------------------------
LOCAL_C_INCLUDES += \
$(libvstr_PATH) \
$(strongswan_PATH)/src/include \
- $(strongswan_PATH)/src/libhydra \
$(strongswan_PATH)/src/libstrongswan
LOCAL_CFLAGS := $(strongswan_CFLAGS)
@@ -23,7 +32,7 @@ LOCAL_ARM_MODE := arm
LOCAL_PRELINK_MODULE := false
-LOCAL_SHARED_LIBRARIES += libstrongswan libhydra
+LOCAL_SHARED_LIBRARIES += libstrongswan
include $(BUILD_SHARED_LIBRARY)
diff --git a/src/libipsec/Makefile.am b/src/libipsec/Makefile.am
index 0b8faf724..35b8d7916 100644
--- a/src/libipsec/Makefile.am
+++ b/src/libipsec/Makefile.am
@@ -1,11 +1,22 @@
ipseclib_LTLIBRARIES = libipsec.la
libipsec_la_SOURCES = \
-ipsec.c ipsec.h
+ipsec.c ipsec.h \
+esp_context.c esp_context.h \
+esp_packet.c esp_packet.h \
+ip_packet.c ip_packet.h \
+ipsec_event_listener.h \
+ipsec_event_relay.c ipsec_event_relay.h \
+ipsec_policy.c ipsec_policy.h \
+ipsec_policy_mgr.c ipsec_policy_mgr.h \
+ipsec_processor.c ipsec_processor.h \
+ipsec_sa.c ipsec_sa.h \
+ipsec_sa_mgr.c ipsec_sa_mgr.h
libipsec_la_LIBADD =
-INCLUDES = -I$(top_srcdir)/src/libstrongswan
+INCLUDES = \
+ -I$(top_srcdir)/src/libstrongswan
EXTRA_DIST = Android.mk
diff --git a/src/libipsec/esp_context.c b/src/libipsec/esp_context.c
new file mode 100644
index 000000000..c7fb7ab2f
--- /dev/null
+++ b/src/libipsec/esp_context.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 <limits.h>
+
+#include "esp_context.h"
+
+#include <library.h>
+#include <debug.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+
+/**
+ * Should be a multiple of 8
+ */
+#define ESP_DEFAULT_WINDOW_SIZE 128
+
+typedef struct private_esp_context_t private_esp_context_t;
+
+/**
+ * Private additions to esp_context_t.
+ */
+struct private_esp_context_t {
+
+ /**
+ * Public members
+ */
+ esp_context_t public;
+
+ /**
+ * Crypter used to encrypt/decrypt ESP packets
+ */
+ crypter_t *crypter;
+
+ /**
+ * Signer to authenticate ESP packets
+ */
+ signer_t *signer;
+
+ /**
+ * The highest sequence number that was successfully verified
+ * and authenticated, or assigned in an outbound context
+ */
+ u_int32_t last_seqno;
+
+ /**
+ * The bit in the window of the highest authenticated sequence number
+ */
+ u_int seqno_index;
+
+ /**
+ * The size of the anti-replay window (in bits)
+ */
+ u_int window_size;
+
+ /**
+ * The anti-replay window buffer
+ */
+ chunk_t window;
+
+ /**
+ * TRUE in case of an inbound ESP context
+ */
+ bool inbound;
+};
+
+/**
+ * Set or unset a bit in the window.
+ */
+static inline void set_window_bit(private_esp_context_t *this,
+ u_int index, bool set)
+{
+ u_int i = index / CHAR_BIT;
+
+ if (set)
+ {
+ this->window.ptr[i] |= 1 << (index % CHAR_BIT);
+ }
+ else
+ {
+ this->window.ptr[i] &= ~(1 << (index % CHAR_BIT));
+ }
+}
+
+/**
+ * Get a bit from the window.
+ */
+static inline bool get_window_bit(private_esp_context_t *this, u_int index)
+{
+ u_int i = index / CHAR_BIT;
+
+ return this->window.ptr[i] & (1 << index % CHAR_BIT);
+}
+
+/**
+ * Returns TRUE if the supplied seqno is not already marked in the window
+ */
+static bool check_window(private_esp_context_t *this, u_int32_t seqno)
+{
+ u_int offset;
+
+ offset = this->last_seqno - seqno;
+ offset = (this->seqno_index - offset) % this->window_size;
+ return !get_window_bit(this, offset);
+}
+
+METHOD(esp_context_t, verify_seqno, bool,
+ private_esp_context_t *this, u_int32_t seqno)
+{
+ if (!this->inbound)
+ {
+ return FALSE;
+ }
+
+ if (seqno > this->last_seqno)
+ { /* |----------------------------------------|
+ * <---------^ ^ or <---------^ ^
+ * WIN H S WIN H S
+ */
+ return TRUE;
+ }
+ else if (seqno > 0 && this->window_size > this->last_seqno - seqno)
+ { /* |----------------------------------------|
+ * <---------^ or <---------^
+ * WIN ^ H WIN ^ H
+ * S S
+ */
+ return check_window(this, seqno);
+ }
+ else
+ { /* |----------------------------------------|
+ * ^ <---------^
+ * S WIN H
+ */
+ return FALSE;
+ }
+}
+
+METHOD(esp_context_t, set_authenticated_seqno, void,
+ private_esp_context_t *this, u_int32_t seqno)
+{
+ u_int i, shift;
+
+ if (!this->inbound)
+ {
+ return;
+ }
+
+ if (seqno > this->last_seqno)
+ { /* shift the window to the new highest authenticated seqno */
+ shift = seqno - this->last_seqno;
+ shift = shift < this->window_size ? shift : this->window_size;
+ for (i = 0; i < shift; ++i)
+ {
+ this->seqno_index = (this->seqno_index + 1) % this->window_size;
+ set_window_bit(this, this->seqno_index, FALSE);
+ }
+ set_window_bit(this, this->seqno_index, TRUE);
+ this->last_seqno = seqno;
+ }
+ else
+ { /* seqno is inside the window, set the corresponding window bit */
+ i = this->last_seqno - seqno;
+ set_window_bit(this, (this->seqno_index - i) % this->window_size, TRUE);
+ }
+}
+
+METHOD(esp_context_t, get_seqno, u_int32_t,
+ private_esp_context_t *this)
+{
+ return this->last_seqno;
+}
+
+METHOD(esp_context_t, next_seqno, bool,
+ private_esp_context_t *this, u_int32_t *seqno)
+{
+ if (this->inbound || this->last_seqno == UINT32_MAX)
+ { /* inbound or segno would cycle */
+ return FALSE;
+ }
+ *seqno = ++this->last_seqno;
+ return TRUE;
+}
+
+METHOD(esp_context_t, get_signer, signer_t *,
+ private_esp_context_t *this)
+{
+ return this->signer;
+}
+
+METHOD(esp_context_t, get_crypter, crypter_t *,
+ private_esp_context_t *this)
+{
+ return this->crypter;
+}
+
+METHOD(esp_context_t, destroy, void,
+ private_esp_context_t *this)
+{
+ chunk_free(&this->window);
+ DESTROY_IF(this->crypter);
+ DESTROY_IF(this->signer);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key,
+ int int_alg, chunk_t int_key, bool inbound)
+{
+ private_esp_context_t *this;
+
+ INIT(this,
+ .public = {
+ .get_crypter = _get_crypter,
+ .get_signer = _get_signer,
+ .get_seqno = _get_seqno,
+ .next_seqno = _next_seqno,
+ .verify_seqno = _verify_seqno,
+ .set_authenticated_seqno = _set_authenticated_seqno,
+ .destroy = _destroy,
+ },
+ .inbound = inbound,
+ .window_size = ESP_DEFAULT_WINDOW_SIZE,
+ );
+
+ switch(enc_alg)
+ {
+ case ENCR_AES_CBC:
+ this->crypter = lib->crypto->create_crypter(lib->crypto, enc_alg,
+ enc_key.len);
+ break;
+ default:
+ break;
+ }
+ if (!this->crypter)
+ {
+ DBG1(DBG_ESP, "failed to create ESP context: unsupported encryption "
+ "algorithm");
+ destroy(this);
+ return NULL;
+ }
+ if (!this->crypter->set_key(this->crypter, enc_key))
+ {
+ DBG1(DBG_ESP, "failed to create ESP context: setting encryption key "
+ "failed");
+ destroy(this);
+ return NULL;
+ }
+
+ switch(int_alg)
+ {
+ case AUTH_HMAC_SHA1_96:
+ case AUTH_HMAC_SHA2_256_128:
+ case AUTH_HMAC_SHA2_384_192:
+ case AUTH_HMAC_SHA2_512_256:
+ this->signer = lib->crypto->create_signer(lib->crypto, int_alg);
+ break;
+ default:
+ break;
+ }
+ if (!this->signer)
+ {
+ DBG1(DBG_ESP, "failed to create ESP context: unsupported integrity "
+ "algorithm");
+ destroy(this);
+ return NULL;
+ }
+ if (!this->signer->set_key(this->signer, int_key))
+ {
+ DBG1(DBG_ESP, "failed to create ESP context: setting signature key "
+ "failed");
+ destroy(this);
+ return NULL;
+ }
+
+ if (inbound)
+ {
+ this->window = chunk_alloc(this->window_size / CHAR_BIT + 1);
+ memset(this->window.ptr, 0, this->window.len);
+ }
+ return &this->public;
+}
+
+
diff --git a/src/libipsec/esp_context.h b/src/libipsec/esp_context.h
new file mode 100644
index 000000000..db247dced
--- /dev/null
+++ b/src/libipsec/esp_context.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup esp_context esp_context
+ * @{ @ingroup libipsec
+ */
+
+#ifndef ESP_CONTEXT_H_
+#define ESP_CONTEXT_H_
+
+#include <library.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+
+typedef struct esp_context_t esp_context_t;
+
+/**
+ * ESP context, handles sequence numbers and maintains cryptographic primitives
+ */
+struct esp_context_t {
+
+ /**
+ * Get the crypter.
+ *
+ * @return crypter
+ */
+ crypter_t *(*get_crypter)(esp_context_t *this);
+
+ /**
+ * Get the signer.
+ *
+ * @return signer
+ */
+ signer_t *(*get_signer)(esp_context_t *this);
+
+ /**
+ * Get the current outbound ESP sequence number or the highest authenticated
+ * inbound sequence number.
+ *
+ * @return current sequence number, in host byte order
+ */
+ u_int32_t (*get_seqno)(esp_context_t *this);
+
+ /**
+ * Allocate the next outbound ESP sequence number.
+ *
+ * @param seqno the sequence number, in host byte order
+ * @return FALSE if the sequence number cycled or inbound context
+ */
+ bool (*next_seqno)(esp_context_t *this, u_int32_t *seqno);
+
+ /**
+ * Verify an ESP sequence number. Checks whether a packet with this
+ * sequence number was already received, using the anti-replay window.
+ * This operation does not modify the internal state. After the sequence
+ * number is successfully verified and the ESP packet is authenticated,
+ * set_authenticated_seqno() should be called.
+ *
+ * @param seqno the sequence number to verify, in host byte order
+ * @return TRUE when sequence number is valid
+ */
+ bool (*verify_seqno)(esp_context_t *this, u_int32_t seqno);
+
+ /**
+ * Adds a sequence number that was successfully verified and
+ * authenticated. A user MUST call verify_seqno() immediately before
+ * calling this method.
+ *
+ * @param seqno verified and authenticated seq number in host byte order
+ */
+ void (*set_authenticated_seqno)(esp_context_t *this,
+ u_int32_t seqno);
+
+ /**
+ * Destroy an esp_context_t
+ */
+ void (*destroy)(esp_context_t *this);
+
+};
+
+/**
+ * Create an esp_context_t instance
+ *
+ * @param enc_alg encryption algorithm
+ * @param enc_key encryption key
+ * @param int_alg integrity protection algorithm
+ * @param int_key integrity protection key
+ * @param inbound TRUE to create an inbound ESP context
+ * @return ESP context instance, or NULL if creation fails
+ */
+esp_context_t *esp_context_create(int enc_alg, chunk_t enc_key, int int_alg,
+ chunk_t int_key, bool inbound);
+
+#endif /** ESP_CONTEXT_H_ @}*/
+
diff --git a/src/libipsec/esp_packet.c b/src/libipsec/esp_packet.c
new file mode 100644
index 000000000..bfcab95eb
--- /dev/null
+++ b/src/libipsec/esp_packet.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "esp_packet.h"
+
+#include <library.h>
+#include <debug.h>
+#include <crypto/crypters/crypter.h>
+#include <crypto/signers/signer.h>
+#include <bio/bio_reader.h>
+#include <bio/bio_writer.h>
+
+#include <netinet/in.h>
+
+typedef struct private_esp_packet_t private_esp_packet_t;
+
+/**
+ * Private additions to esp_packet_t.
+ */
+struct private_esp_packet_t {
+
+ /**
+ * Public members
+ */
+ esp_packet_t public;
+
+ /**
+ * Raw ESP packet
+ */
+ packet_t *packet;
+
+ /**
+ * Payload of this packet
+ */
+ ip_packet_t *payload;
+
+ /**
+ * Next Header info (e.g. IPPROTO_IPIP)
+ */
+ u_int8_t next_header;
+
+};
+
+/**
+ * Forward declaration for clone()
+ */
+static private_esp_packet_t *esp_packet_create_internal(packet_t *packet);
+
+METHOD(packet_t, set_source, void,
+ private_esp_packet_t *this, host_t *src)
+{
+ return this->packet->set_source(this->packet, src);
+}
+
+METHOD2(esp_packet_t, packet_t, get_source, host_t*,
+ private_esp_packet_t *this)
+{
+ return this->packet->get_source(this->packet);
+}
+
+METHOD(packet_t, set_destination, void,
+ private_esp_packet_t *this, host_t *dst)
+{
+ return this->packet->set_destination(this->packet, dst);
+}
+
+METHOD2(esp_packet_t, packet_t, get_destination, host_t*,
+ private_esp_packet_t *this)
+{
+ return this->packet->get_destination(this->packet);
+}
+
+METHOD(packet_t, get_data, chunk_t,
+ private_esp_packet_t *this)
+{
+ return this->packet->get_data(this->packet);
+}
+
+METHOD(packet_t, set_data, void,
+ private_esp_packet_t *this, chunk_t data)
+{
+ return this->packet->set_data(this->packet, data);
+}
+
+METHOD(packet_t, skip_bytes, void,
+ private_esp_packet_t *this, size_t bytes)
+{
+ return this->packet->skip_bytes(this->packet, bytes);
+}
+
+METHOD(packet_t, clone, packet_t*,
+ private_esp_packet_t *this)
+{
+ private_esp_packet_t *pkt;
+
+ pkt = esp_packet_create_internal(this->packet->clone(this->packet));
+ pkt->payload = this->payload ? this->payload->clone(this->payload) : NULL;
+ pkt->next_header = this->next_header;
+ return &pkt->public.packet;
+}
+
+METHOD(esp_packet_t, parse_header, bool,
+ private_esp_packet_t *this, u_int32_t *spi)
+{
+ bio_reader_t *reader;
+ u_int32_t seq;
+
+ reader = bio_reader_create(this->packet->get_data(this->packet));
+ if (!reader->read_uint32(reader, spi) ||
+ !reader->read_uint32(reader, &seq))
+ {
+ DBG1(DBG_ESP, "failed to parse ESP header: invalid length");
+ reader->destroy(reader);
+ return FALSE;
+ }
+ reader->destroy(reader);
+
+ DBG2(DBG_ESP, "parsed ESP header with SPI %.8x [seq %u]", *spi, seq);
+ *spi = htonl(*spi);
+ return TRUE;
+}
+
+/**
+ * Check padding as specified in RFC 4303
+ */
+static bool check_padding(chunk_t padding)
+{
+ size_t i;
+
+ for (i = 0; i < padding.len; ++i)
+ {
+ if (padding.ptr[i] != (u_int8_t)(i + 1))
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Remove the padding from the payload and set the next header info
+ */
+static bool remove_padding(private_esp_packet_t *this, chunk_t plaintext)
+{
+ u_int8_t next_header, pad_length;
+ chunk_t padding, payload;
+ bio_reader_t *reader;
+
+ reader = bio_reader_create(plaintext);
+ if (!reader->read_uint8_end(reader, &next_header) ||
+ !reader->read_uint8_end(reader, &pad_length))
+ {
+ DBG1(DBG_ESP, "parsing ESP payload failed: invalid length");
+ goto failed;
+ }
+ if (!reader->read_data_end(reader, pad_length, &padding) ||
+ !check_padding(padding))
+ {
+ DBG1(DBG_ESP, "parsing ESP payload failed: invalid padding");
+ goto failed;
+ }
+ this->payload = ip_packet_create(reader->peek(reader));
+ reader->destroy(reader);
+ if (!this->payload)
+ {
+ DBG1(DBG_ESP, "parsing ESP payload failed: unsupported payload");
+ return FALSE;
+ }
+ this->next_header = next_header;
+ payload = this->payload->get_encoding(this->payload);
+
+ DBG3(DBG_ESP, "ESP payload:\n payload %B\n padding %B\n "
+ "padding length = %hhu, next header = %hhu", &payload, &padding,
+ pad_length, this->next_header);
+ return TRUE;
+
+failed:
+ reader->destroy(reader);
+ chunk_free(&plaintext);
+ return FALSE;
+}
+
+METHOD(esp_packet_t, decrypt, status_t,
+ private_esp_packet_t *this, esp_context_t *esp_context)
+{
+ bio_reader_t *reader;
+ u_int32_t spi, seq;
+ chunk_t data, iv, icv, ciphertext, plaintext;
+ crypter_t *crypter;
+ signer_t *signer;
+
+ DESTROY_IF(this->payload);
+ this->payload = NULL;
+
+ data = this->packet->get_data(this->packet);
+ crypter = esp_context->get_crypter(esp_context);
+ signer = esp_context->get_signer(esp_context);
+
+ reader = bio_reader_create(data);
+ if (!reader->read_uint32(reader, &spi) ||
+ !reader->read_uint32(reader, &seq) ||
+ !reader->read_data(reader, crypter->get_iv_size(crypter), &iv) ||
+ !reader->read_data_end(reader, signer->get_block_size(signer), &icv) ||
+ reader->remaining(reader) % crypter->get_block_size(crypter))
+ {
+ DBG1(DBG_ESP, "ESP decryption failed: invalid length");
+ return PARSE_ERROR;
+ }
+ ciphertext = reader->peek(reader);
+ reader->destroy(reader);
+
+ if (!esp_context->verify_seqno(esp_context, seq))
+ {
+ DBG1(DBG_ESP, "ESP sequence number verification failed:\n "
+ "src %H, dst %H, SPI %.8x [seq %u]",
+ get_source(this), get_destination(this), spi, seq);
+ return VERIFY_ERROR;
+ }
+ DBG3(DBG_ESP, "ESP decryption:\n SPI %.8x [seq %u]\n IV %B\n "
+ "encrypted %B\n ICV %B", spi, seq, &iv, &ciphertext, &icv);
+
+ if (!signer->get_signature(signer, chunk_create(data.ptr, 8), NULL) ||
+ !signer->get_signature(signer, iv, NULL) ||
+ !signer->verify_signature(signer, ciphertext, icv))
+ {
+ DBG1(DBG_ESP, "ICV verification failed!");
+ return FAILED;
+ }
+ esp_context->set_authenticated_seqno(esp_context, seq);
+
+ if (!crypter->decrypt(crypter, ciphertext, iv, &plaintext))
+ {
+ DBG1(DBG_ESP, "ESP decryption failed");
+ return FAILED;
+ }
+
+ if (!remove_padding(this, plaintext))
+ {
+ return PARSE_ERROR;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Generate the padding as specified in RFC4303
+ */
+static void generate_padding(chunk_t padding)
+{
+ size_t i;
+
+ for (i = 0; i < padding.len; ++i)
+ {
+ padding.ptr[i] = (u_int8_t)(i + 1);
+ }
+}
+
+METHOD(esp_packet_t, encrypt, status_t,
+ private_esp_packet_t *this, esp_context_t *esp_context, u_int32_t spi)
+{
+ chunk_t iv, icv, padding, payload, ciphertext, auth_data;
+ bio_writer_t *writer;
+ u_int32_t next_seqno;
+ size_t blocksize, plainlen;
+ crypter_t *crypter;
+ signer_t *signer;
+ rng_t *rng;
+
+ this->packet->set_data(this->packet, chunk_empty);
+
+ if (!esp_context->next_seqno(esp_context, &next_seqno))
+ {
+ DBG1(DBG_ESP, "ESP encapsulation failed: sequence numbers cycled");
+ return FAILED;
+ }
+
+ rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ if (!rng)
+ {
+ DBG1(DBG_ESP, "ESP encryption failed: could not find RNG");
+ return NOT_FOUND;
+ }
+ crypter = esp_context->get_crypter(esp_context);
+ signer = esp_context->get_signer(esp_context);
+
+ blocksize = crypter->get_block_size(crypter);
+ iv.len = crypter->get_iv_size(crypter);
+ icv.len = signer->get_block_size(signer);
+
+ /* plaintext = payload, padding, pad_length, next_header */
+ payload = this->payload ? this->payload->get_encoding(this->payload)
+ : chunk_empty;
+ plainlen = payload.len + 2;
+ padding.len = blocksize - (plainlen % blocksize);
+ plainlen += padding.len;
+
+ /* len = spi, seq, IV, plaintext, ICV */
+ writer = bio_writer_create(2 * sizeof(u_int32_t) + iv.len + plainlen +
+ icv.len);
+ writer->write_uint32(writer, ntohl(spi));
+ writer->write_uint32(writer, next_seqno);
+
+ iv = writer->skip(writer, iv.len);
+ if (!rng->get_bytes(rng, iv.len, iv.ptr))
+ {
+ DBG1(DBG_ESP, "ESP encryption failed: could not generate IV");
+ writer->destroy(writer);
+ rng->destroy(rng);
+ return FAILED;
+ }
+ rng->destroy(rng);
+
+ /* plain-/ciphertext will start here */
+ ciphertext = writer->get_buf(writer);
+ ciphertext.ptr += ciphertext.len;
+ ciphertext.len = plainlen;
+
+ writer->write_data(writer, payload);
+
+ padding = writer->skip(writer, padding.len);
+ generate_padding(padding);
+
+ writer->write_uint8(writer, padding.len);
+ writer->write_uint8(writer, this->next_header);
+
+ DBG3(DBG_ESP, "ESP before encryption:\n payload = %B\n padding = %B\n "
+ "padding length = %hhu, next header = %hhu", &payload, &padding,
+ (u_int8_t)padding.len, this->next_header);
+
+ /* encrypt the content inline */
+ if (!crypter->encrypt(crypter, ciphertext, iv, NULL))
+ {
+ DBG1(DBG_ESP, "ESP encryption failed");
+ writer->destroy(writer);
+ return FAILED;
+ }
+
+ /* calculate signature */
+ auth_data = writer->get_buf(writer);
+ icv = writer->skip(writer, icv.len);
+ if (!signer->get_signature(signer, auth_data, icv.ptr))
+ {
+ DBG1(DBG_ESP, "ESP encryption failed: signature generation failed");
+ writer->destroy(writer);
+ return FAILED;
+ }
+
+ DBG3(DBG_ESP, "ESP packet:\n SPI %.8x [seq %u]\n IV %B\n "
+ "encrypted %B\n ICV %B", ntohl(spi), next_seqno, &iv,
+ &ciphertext, &icv);
+
+ this->packet->set_data(this->packet, writer->extract_buf(writer));
+ writer->destroy(writer);
+ return SUCCESS;
+}
+
+METHOD(esp_packet_t, get_next_header, u_int8_t,
+ private_esp_packet_t *this)
+{
+ return this->next_header;
+}
+
+METHOD(esp_packet_t, get_payload, ip_packet_t*,
+ private_esp_packet_t *this)
+{
+ return this->payload;
+}
+
+METHOD(esp_packet_t, extract_payload, ip_packet_t*,
+ private_esp_packet_t *this)
+{
+ ip_packet_t *payload;
+
+ payload = this->payload;
+ this->payload = NULL;
+ return payload;
+}
+
+METHOD2(esp_packet_t, packet_t, destroy, void,
+ private_esp_packet_t *this)
+{
+ DESTROY_IF(this->payload);
+ this->packet->destroy(this->packet);
+ free(this);
+}
+
+static private_esp_packet_t *esp_packet_create_internal(packet_t *packet)
+{
+ private_esp_packet_t *this;
+
+ INIT(this,
+ .public = {
+ .packet = {
+ .set_source = _set_source,
+ .get_source = _get_source,
+ .set_destination = _set_destination,
+ .get_destination = _get_destination,
+ .get_data = _get_data,
+ .set_data = _set_data,
+ .skip_bytes = _skip_bytes,
+ .clone = _clone,
+ .destroy = _destroy,
+ },
+ .get_source = _get_source,
+ .get_destination = _get_destination,
+ .get_next_header = _get_next_header,
+ .parse_header = _parse_header,
+ .decrypt = _decrypt,
+ .encrypt = _encrypt,
+ .get_payload = _get_payload,
+ .extract_payload = _extract_payload,
+ .destroy = _destroy,
+ },
+ .packet = packet,
+ .next_header = IPPROTO_NONE,
+ );
+ return this;
+}
+
+/**
+ * Described in header.
+ */
+esp_packet_t *esp_packet_create_from_packet(packet_t *packet)
+{
+ private_esp_packet_t *this;
+
+ this = esp_packet_create_internal(packet);
+
+ return &this->public;
+}
+
+/**
+ * Described in header.
+ */
+esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst,
+ ip_packet_t *payload)
+{
+ private_esp_packet_t *this;
+ packet_t *packet;
+
+ packet = packet_create_from_data(src, dst, chunk_empty);
+ this = esp_packet_create_internal(packet);
+ this->payload = payload;
+ if (payload)
+ {
+ this->next_header = payload->get_version(payload) == 4 ? IPPROTO_IPIP
+ : IPPROTO_IPV6;
+ }
+ else
+ {
+ this->next_header = IPPROTO_NONE;
+ }
+ return &this->public;
+}
diff --git a/src/libipsec/esp_packet.h b/src/libipsec/esp_packet.h
new file mode 100644
index 000000000..a1d1602c1
--- /dev/null
+++ b/src/libipsec/esp_packet.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup esp_packet esp_packet
+ * @{ @ingroup libipsec
+ */
+
+#ifndef ESP_PACKET_H_
+#define ESP_PACKET_H_
+
+#include "ip_packet.h"
+#include "esp_context.h"
+
+#include <library.h>
+#include <utils/host.h>
+#include <utils/packet.h>
+
+typedef struct esp_packet_t esp_packet_t;
+
+/**
+ * ESP packet
+ */
+struct esp_packet_t {
+
+ /**
+ * Implements packet_t interface to access the raw ESP packet
+ */
+ packet_t packet;
+
+ /**
+ * Get the source address of this packet
+ *
+ * @return source host
+ */
+ host_t *(*get_source)(esp_packet_t *this);
+
+ /**
+ * Get the destination address of this packet
+ *
+ * @return destination host
+ */
+ host_t *(*get_destination)(esp_packet_t *this);
+
+ /**
+ * Parse the packet header before decryption. Tries to read the SPI
+ * from the packet to find a corresponding SA.
+ *
+ * @param spi parsed SPI, in network byte order
+ * @return TRUE when successful, FALSE otherwise (e.g. when the
+ * length of the packet is invalid)
+ */
+ bool (*parse_header)(esp_packet_t *this, u_int32_t *spi);
+
+ /**
+ * Authenticate and decrypt the packet. Also verifies the sequence number
+ * using the supplied ESP context and updates the anti-replay window.
+ *
+ * @param esp_context ESP context of corresponding inbound IPsec SA
+ * @return - SUCCESS if successfully authenticated,
+ * decrypted and parsed
+ * - PARSE_ERROR if the length of the packet or the
+ * padding is invalid
+ * - VERIFY_ERROR if the sequence number
+ * verification failed
+ * - FAILED if the ICV (MAC) check or the actual
+ * decryption failed
+ */
+ status_t (*decrypt)(esp_packet_t *this, esp_context_t *esp_context);
+
+ /**
+ * Encapsulate and encrypt the packet. The sequence number will be generated
+ * using the supplied ESP context.
+ *
+ * @param esp_context ESP context of corresponding outbound IPsec SA
+ * @param spi SPI value to use, in network byte order
+ * @return - SUCCESS if encrypted
+ * - FAILED if sequence number cycled or any of the
+ * cryptographic functions failed
+ * - NOT_FOUND if no suitable RNG could be found
+ */
+ status_t (*encrypt)(esp_packet_t *this, esp_context_t *esp_context,
+ u_int32_t spi);
+
+ /**
+ * Get the next header field of a packet.
+ *
+ * @note Packet has to be in the decrypted state.
+ *
+ * @return next header field
+ */
+ u_int8_t (*get_next_header)(esp_packet_t *this);
+
+ /**
+ * Get the plaintext payload of this packet.
+ *
+ * @return plaintext payload (internal data),
+ * NULL if not decrypted
+ */
+ ip_packet_t *(*get_payload)(esp_packet_t *this);
+
+ /**
+ * Extract the plaintext payload from this packet.
+ *
+ * @return plaintext payload (has to be destroyed),
+ * NULL if not decrypted
+ */
+ ip_packet_t *(*extract_payload)(esp_packet_t *this);
+
+ /**
+ * Destroy an esp_packet_t
+ */
+ void (*destroy)(esp_packet_t *this);
+
+};
+
+/**
+ * Create an ESP packet out of data from the wire.
+ *
+ * @param packet the packet data as received, gets owned
+ * @return esp_packet_t instance
+ */
+esp_packet_t *esp_packet_create_from_packet(packet_t *packet);
+
+/**
+ * Create an ESP packet from a plaintext payload
+ *
+ * @param src source address
+ * @param dst destination address
+ * @param payload plaintext payload, gets owned
+ * @return esp_packet_t instance
+ */
+esp_packet_t *esp_packet_create_from_payload(host_t *src, host_t *dst,
+ ip_packet_t *payload);
+
+#endif /** ESP_PACKET_H_ @}*/
+
diff --git a/src/libipsec/ip_packet.c b/src/libipsec/ip_packet.c
new file mode 100644
index 000000000..4593ba5c8
--- /dev/null
+++ b/src/libipsec/ip_packet.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 "ip_packet.h"
+
+#include <library.h>
+#include <debug.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#ifdef HAVE_NETINET_IP6_H
+#include <netinet/ip6.h>
+#endif
+
+typedef struct private_ip_packet_t private_ip_packet_t;
+
+/**
+ * Private additions to ip_packet_t.
+ */
+struct private_ip_packet_t {
+
+ /**
+ * Public members
+ */
+ ip_packet_t public;
+
+ /**
+ * Source address
+ */
+ host_t *src;
+
+ /**
+ * Destination address
+ */
+ host_t *dst;
+
+ /**
+ * IP packet
+ */
+ chunk_t packet;
+
+ /**
+ * IP version
+ */
+ u_int8_t version;
+
+ /**
+ * Protocol|Next Header field
+ */
+ u_int8_t next_header;
+
+};
+
+METHOD(ip_packet_t, get_version, u_int8_t,
+ private_ip_packet_t *this)
+{
+ return this->version;
+}
+
+METHOD(ip_packet_t, get_source, host_t*,
+ private_ip_packet_t *this)
+{
+ return this->src;
+}
+
+METHOD(ip_packet_t, get_destination, host_t*,
+ private_ip_packet_t *this)
+{
+ return this->dst;
+}
+
+METHOD(ip_packet_t, get_encoding, chunk_t,
+ private_ip_packet_t *this)
+{
+ return this->packet;
+}
+
+METHOD(ip_packet_t, get_next_header, u_int8_t,
+ private_ip_packet_t *this)
+{
+ return this->next_header;
+}
+
+METHOD(ip_packet_t, clone, ip_packet_t*,
+ private_ip_packet_t *this)
+{
+ return ip_packet_create(this->packet);
+}
+
+METHOD(ip_packet_t, destroy, void,
+ private_ip_packet_t *this)
+{
+ this->src->destroy(this->src);
+ this->dst->destroy(this->dst);
+ chunk_free(&this->packet);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+ip_packet_t *ip_packet_create(chunk_t packet)
+{
+ private_ip_packet_t *this;
+ u_int8_t version, next_header;
+ host_t *src, *dst;
+
+ if (packet.len < 1)
+ {
+ DBG1(DBG_ESP, "IP packet too short");
+ goto failed;
+ }
+
+ version = (packet.ptr[0] & 0xf0) >> 4;
+
+ switch (version)
+ {
+ case 4:
+ {
+ struct iphdr *ip;
+
+ if (packet.len < sizeof(struct iphdr))
+ {
+ DBG1(DBG_ESP, "IPv4 packet too short");
+ goto failed;
+ }
+ ip = (struct iphdr*)packet.ptr;
+ src = host_create_from_chunk(AF_INET,
+ chunk_from_thing(ip->saddr), 0);
+ dst = host_create_from_chunk(AF_INET,
+ chunk_from_thing(ip->daddr), 0);
+ next_header = ip->protocol;
+ break;
+ }
+#ifdef HAVE_NETINET_IP6_H
+ case 6:
+ {
+ struct ip6_hdr *ip;
+
+ if (packet.len < sizeof(struct ip6_hdr))
+ {
+ DBG1(DBG_ESP, "IPv6 packet too short");
+ goto failed;
+ }
+ ip = (struct ip6_hdr*)packet.ptr;
+ src = host_create_from_chunk(AF_INET6,
+ chunk_from_thing(ip->ip6_src), 0);
+ dst = host_create_from_chunk(AF_INET6,
+ chunk_from_thing(ip->ip6_dst), 0);
+ next_header = ip->ip6_nxt;
+ }
+#endif /* HAVE_NETINET_IP6_H */
+ default:
+ DBG1(DBG_ESP, "unsupported IP version");
+ goto failed;
+ }
+
+ INIT(this,
+ .public = {
+ .get_version = _get_version,
+ .get_source = _get_source,
+ .get_destination = _get_destination,
+ .get_next_header = _get_next_header,
+ .get_encoding = _get_encoding,
+ .clone = _clone,
+ .destroy = _destroy,
+ },
+ .src = src,
+ .dst = dst,
+ .packet = packet,
+ .version = version,
+ .next_header = next_header,
+ );
+ return &this->public;
+
+failed:
+ chunk_free(&packet);
+ return NULL;
+}
diff --git a/src/libipsec/ip_packet.h b/src/libipsec/ip_packet.h
new file mode 100644
index 000000000..b4fc298ff
--- /dev/null
+++ b/src/libipsec/ip_packet.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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.
+ */
+
+/**
+ * @defgroup ip_packet ip_packet
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IP_PACKET_H_
+#define IP_PACKET_H_
+
+#include <library.h>
+#include <utils/host.h>
+#include <utils/packet.h>
+
+typedef struct ip_packet_t ip_packet_t;
+
+/**
+ * IP packet
+ */
+struct ip_packet_t {
+
+ /**
+ * IP version of this packet
+ *
+ * @return ip version
+ */
+ u_int8_t (*get_version)(ip_packet_t *this);
+
+ /**
+ * Get the source address of this packet
+ *
+ * @return source host
+ */
+ host_t *(*get_source)(ip_packet_t *this);
+
+ /**
+ * Get the destination address of this packet
+ *
+ * @return destination host
+ */
+ host_t *(*get_destination)(ip_packet_t *this);
+
+ /**
+ * Get the protocol (IPv4) or next header (IPv6) field of this packet.
+ *
+ * @return protocol|next header field
+ */
+ u_int8_t (*get_next_header)(ip_packet_t *this);
+
+ /**
+ * Get the complete IP packet (including the header)
+ *
+ * @return IP packet (internal data)
+ */
+ chunk_t (*get_encoding)(ip_packet_t *this);
+
+ /**
+ * Clone the IP packet
+ *
+ * @return clone of the packet
+ */
+ ip_packet_t *(*clone)(ip_packet_t *this);
+
+ /**
+ * Destroy an ip_packet_t
+ */
+ void (*destroy)(ip_packet_t *this);
+
+};
+
+/**
+ * Create an IP packet out of data from the wire (or decapsulated from another
+ * packet).
+ *
+ * @note The raw IP packet gets either owned by the new object, or destroyed,
+ * if the data is invalid.
+ *
+ * @param packet the IP packet (including header), gets owned
+ * @return ip_packet_t instance, or NULL if invalid
+ */
+ip_packet_t *ip_packet_create(chunk_t packet);
+
+#endif /** IP_PACKET_H_ @}*/
diff --git a/src/libipsec/ipsec.c b/src/libipsec/ipsec.c
index add3b463a..50d9163ea 100644
--- a/src/libipsec/ipsec.c
+++ b/src/libipsec/ipsec.c
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
* Copyright (C) 2012 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
@@ -41,6 +43,10 @@ ipsec_t *ipsec;
void libipsec_deinit()
{
private_ipsec_t *this = (private_ipsec_t*)ipsec;
+ DESTROY_IF(this->public.processor);
+ DESTROY_IF(this->public.events);
+ DESTROY_IF(this->public.policies);
+ DESTROY_IF(this->public.sas);
free(this);
ipsec = NULL;
}
@@ -52,10 +58,7 @@ bool libipsec_init()
{
private_ipsec_t *this;
- INIT(this,
- .public = {
- },
- );
+ INIT(this);
ipsec = &this->public;
if (lib->integrity &&
@@ -64,6 +67,11 @@ bool libipsec_init()
DBG1(DBG_LIB, "integrity check of libipsec failed");
return FALSE;
}
+
+ this->public.sas = ipsec_sa_mgr_create();
+ this->public.policies = ipsec_policy_mgr_create();
+ this->public.events = ipsec_event_relay_create();
+ this->public.processor = ipsec_processor_create();
return TRUE;
}
diff --git a/src/libipsec/ipsec.h b/src/libipsec/ipsec.h
index 80bef5426..7ee49432a 100644
--- a/src/libipsec/ipsec.h
+++ b/src/libipsec/ipsec.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
* Copyright (C) 2012 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
@@ -23,15 +25,40 @@
#ifndef IPSEC_H_
#define IPSEC_H_
-typedef struct ipsec_t ipsec_t;
+#include "ipsec_sa_mgr.h"
+#include "ipsec_policy_mgr.h"
+#include "ipsec_event_relay.h"
+#include "ipsec_processor.h"
#include <library.h>
+typedef struct ipsec_t ipsec_t;
+
/**
* User space IPsec implementation.
*/
struct ipsec_t {
+ /**
+ * IPsec SA manager instance
+ */
+ ipsec_sa_mgr_t *sas;
+
+ /**
+ * IPsec policy manager instance
+ */
+ ipsec_policy_mgr_t *policies;
+
+ /**
+ * Event relay instance
+ */
+ ipsec_event_relay_t *events;
+
+ /**
+ * IPsec processor instance
+ */
+ ipsec_processor_t *processor;
+
};
/**
diff --git a/src/libipsec/ipsec_event_listener.h b/src/libipsec/ipsec_event_listener.h
new file mode 100644
index 000000000..c5c39b0f1
--- /dev/null
+++ b/src/libipsec/ipsec_event_listener.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_event_listener ipsec_event_listener
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_EVENT_LISTENER_H_
+#define IPSEC_EVENT_LISTENER_H_
+
+typedef struct ipsec_event_listener_t ipsec_event_listener_t;
+
+#include <library.h>
+
+/**
+ * Listener interface for IPsec events
+ *
+ * All methods are optional.
+ */
+struct ipsec_event_listener_t {
+
+ /**
+ * Called when the lifetime of an IPsec SA expired
+ *
+ * @param reqid reqid of the expired SA
+ * @param protocol protocol of the expired SA
+ * @param spi spi of the expired SA
+ * @param hard TRUE if this is a hard expire, FALSE otherwise
+ */
+ void (*expire)(u_int32_t reqid, u_int8_t protocol, u_int32_t spi,
+ bool hard);
+
+};
+
+#endif /** IPSEC_EVENT_LISTENER_H_ @}*/
diff --git a/src/libipsec/ipsec_event_relay.c b/src/libipsec/ipsec_event_relay.c
new file mode 100644
index 000000000..34222258c
--- /dev/null
+++ b/src/libipsec/ipsec_event_relay.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "ipsec_event_relay.h"
+
+#include <library.h>
+#include <debug.h>
+#include <threading/rwlock.h>
+#include <utils/linked_list.h>
+#include <utils/blocking_queue.h>
+#include <processing/jobs/callback_job.h>
+
+typedef struct private_ipsec_event_relay_t private_ipsec_event_relay_t;
+
+/**
+ * Private additions to ipsec_event_relay_t.
+ */
+struct private_ipsec_event_relay_t {
+
+ /**
+ * Public members
+ */
+ ipsec_event_relay_t public;
+
+ /**
+ * Registered listeners
+ */
+ linked_list_t *listeners;
+
+ /**
+ * Lock to safely access the list of listeners
+ */
+ rwlock_t *lock;
+
+ /**
+ * Blocking queue for events
+ */
+ blocking_queue_t *queue;
+};
+
+/**
+ * Helper struct used to manage events in a queue
+ */
+typedef struct {
+
+ /**
+ * Type of the event
+ */
+ enum {
+ IPSEC_EVENT_EXPIRE,
+ } type;
+
+ /**
+ * Reqid of the SA, if any
+ */
+ u_int32_t reqid;
+
+ /**
+ * SPI of the SA, if any
+ */
+ u_int32_t spi;
+
+ /**
+ * Additional data for specific event types
+ */
+ union {
+
+ struct {
+ /** Protocol of the SA */
+ u_int8_t protocol;
+ /** TRUE in case of a hard expire */
+ bool hard;
+ } expire;
+
+ } data;
+
+} ipsec_event_t;
+
+/**
+ * Dequeue events and relay them to listeners
+ */
+static job_requeue_t handle_events(private_ipsec_event_relay_t *this)
+{
+ enumerator_t *enumerator;
+ ipsec_event_listener_t *current;
+ ipsec_event_t *event;
+
+ event = this->queue->dequeue(this->queue);
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->listeners->create_enumerator(this->listeners);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ switch (event->type)
+ {
+ case IPSEC_EVENT_EXPIRE:
+ if (current->expire)
+ {
+ current->expire(event->reqid, event->data.expire.protocol,
+ event->spi, event->data.expire.hard);
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ return JOB_REQUEUE_DIRECT;
+}
+
+METHOD(ipsec_event_relay_t, expire, void,
+ private_ipsec_event_relay_t *this, u_int32_t reqid, u_int8_t protocol,
+ u_int32_t spi, bool hard)
+{
+ ipsec_event_t *event;
+
+ INIT(event,
+ .type = IPSEC_EVENT_EXPIRE,
+ .reqid = reqid,
+ .spi = spi,
+ .data = {
+ .expire = {
+ .protocol = protocol,
+ .hard = hard,
+ },
+ },
+ );
+ this->queue->enqueue(this->queue, event);
+}
+
+METHOD(ipsec_event_relay_t, register_listener, void,
+ private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
+{
+ this->lock->write_lock(this->lock);
+ this->listeners->insert_last(this->listeners, listener);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(ipsec_event_relay_t, unregister_listener, void,
+ private_ipsec_event_relay_t *this, ipsec_event_listener_t *listener)
+{
+ this->lock->write_lock(this->lock);
+ this->listeners->remove(this->listeners, listener, NULL);
+ this->lock->unlock(this->lock);
+}
+
+METHOD(ipsec_event_relay_t, destroy, void,
+ private_ipsec_event_relay_t *this)
+{
+ this->queue->destroy_function(this->queue, free);
+ this->listeners->destroy(this->listeners);
+ this->lock->destroy(this->lock);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+ipsec_event_relay_t *ipsec_event_relay_create()
+{
+ private_ipsec_event_relay_t *this;
+
+ INIT(this,
+ .public = {
+ .expire = _expire,
+ .register_listener = _register_listener,
+ .unregister_listener = _unregister_listener,
+ .destroy = _destroy,
+ },
+ .listeners = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .queue = blocking_queue_create(),
+ );
+
+ lib->processor->queue_job(lib->processor,
+ (job_t*)callback_job_create((callback_job_cb_t)handle_events, this,
+ NULL, (callback_job_cancel_t)return_false));
+
+ return &this->public;
+}
diff --git a/src/libipsec/ipsec_event_relay.h b/src/libipsec/ipsec_event_relay.h
new file mode 100644
index 000000000..c6935d546
--- /dev/null
+++ b/src/libipsec/ipsec_event_relay.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_event_relay ipsec_event_relay
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_EVENT_RELAY_H_
+#define IPSEC_EVENT_RELAY_H_
+
+#include "ipsec_event_listener.h"
+
+#include <library.h>
+
+typedef struct ipsec_event_relay_t ipsec_event_relay_t;
+
+/**
+ * Event relay manager.
+ *
+ * Used to notify upper layers about changes
+ */
+struct ipsec_event_relay_t {
+
+ /**
+ * Raise an expire event.
+ *
+ * @param reqid reqid of the expired IPsec SA
+ * @param protocol protocol (e.g ESP) of the expired SA
+ * @param spi SPI of the expired SA
+ * @param hard TRUE for a hard expire, FALSE otherwise
+ */
+ void (*expire)(ipsec_event_relay_t *this, u_int32_t reqid,
+ u_int8_t protocol, u_int32_t spi, bool hard);
+
+ /**
+ * Register a listener to events raised by this manager
+ *
+ * @param listener the listener to register
+ */
+ void (*register_listener)(ipsec_event_relay_t *this,
+ ipsec_event_listener_t *listener);
+
+ /**
+ * Unregister a listener
+ *
+ * @param listener the listener to unregister
+ */
+ void (*unregister_listener)(ipsec_event_relay_t *this,
+ ipsec_event_listener_t *listener);
+
+ /**
+ * Destroy an ipsec_event_relay_t
+ */
+ void (*destroy)(ipsec_event_relay_t *this);
+
+};
+
+/**
+ * Create an ipsec_event_relay_t instance
+ *
+ * @return IPsec event relay instance
+ */
+ipsec_event_relay_t *ipsec_event_relay_create();
+
+#endif /** IPSEC_EVENT_RELAY_H_ @}*/
diff --git a/src/libipsec/ipsec_policy.c b/src/libipsec/ipsec_policy.c
new file mode 100644
index 000000000..af8ea9f9d
--- /dev/null
+++ b/src/libipsec/ipsec_policy.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "ipsec_policy.h"
+
+#include <debug.h>
+
+typedef struct private_ipsec_policy_t private_ipsec_policy_t;
+
+/**
+ * Private additions to ipsec_policy_t.
+ */
+struct private_ipsec_policy_t {
+
+ /**
+ * Public members
+ */
+ ipsec_policy_t public;
+
+ /**
+ * SA source address
+ */
+ host_t *src;
+
+ /**
+ * SA destination address
+ */
+ host_t *dst;
+
+ /**
+ * Source traffic selector
+ */
+ traffic_selector_t *src_ts;
+
+ /**
+ * Destination traffic selector
+ */
+ traffic_selector_t *dst_ts;
+
+ /**
+ * If any of the two TS has a protocol selector we cache it here
+ */
+ u_int8_t protocol;
+
+ /**
+ * Traffic direction
+ */
+ policy_dir_t direction;
+
+ /**
+ * Policy type
+ */
+ policy_type_t type;
+
+ /**
+ * SA configuration
+ */
+ ipsec_sa_cfg_t sa;
+
+ /**
+ * Mark
+ */
+ mark_t mark;
+
+ /**
+ * Policy priority
+ */
+ policy_priority_t priority;
+
+ /**
+ * Reference counter
+ */
+ refcount_t refcount;
+
+};
+
+METHOD(ipsec_policy_t, match, bool,
+ private_ipsec_policy_t *this, traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
+ mark_t mark, policy_priority_t priority)
+{
+ return (this->direction == direction &&
+ this->priority == priority &&
+ this->sa.reqid == reqid &&
+ memeq(&this->mark, &mark, sizeof(mark_t)) &&
+ this->src_ts->equals(this->src_ts, src_ts) &&
+ this->dst_ts->equals(this->dst_ts, dst_ts));
+}
+
+METHOD(ipsec_policy_t, match_packet, bool,
+ private_ipsec_policy_t *this, ip_packet_t *packet)
+{
+ u_int8_t proto = packet->get_next_header(packet);
+ host_t *src = packet->get_source(packet),
+ *dst = packet->get_destination(packet);
+
+ return (!this->protocol || this->protocol == proto) &&
+ this->src_ts->includes(this->src_ts, src) &&
+ this->dst_ts->includes(this->dst_ts, dst);
+}
+
+METHOD(ipsec_policy_t, get_source_ts, traffic_selector_t*,
+ private_ipsec_policy_t *this)
+{
+ return this->src_ts;
+}
+
+METHOD(ipsec_policy_t, get_destination_ts, traffic_selector_t*,
+ private_ipsec_policy_t *this)
+{
+ return this->dst_ts;
+}
+
+METHOD(ipsec_policy_t, get_reqid, u_int32_t,
+ private_ipsec_policy_t *this)
+{
+ return this->sa.reqid;
+}
+
+METHOD(ipsec_policy_t, get_direction, policy_dir_t,
+ private_ipsec_policy_t *this)
+{
+ return this->direction;
+}
+
+METHOD(ipsec_policy_t, get_priority, policy_priority_t,
+ private_ipsec_policy_t *this)
+{
+ return this->priority;
+}
+
+METHOD(ipsec_policy_t, get_type, policy_type_t,
+ private_ipsec_policy_t *this)
+{
+ return this->type;
+}
+
+METHOD(ipsec_policy_t, get_ref, ipsec_policy_t*,
+ private_ipsec_policy_t *this)
+{
+ ref_get(&this->refcount);
+ return &this->public;
+}
+
+METHOD(ipsec_policy_t, destroy, void,
+ private_ipsec_policy_t *this)
+{
+ if (ref_put(&this->refcount))
+ {
+ this->src->destroy(this->src);
+ this->dst->destroy(this->dst);
+ this->src_ts->destroy(this->src_ts);
+ this->dst_ts->destroy(this->dst_ts);
+ free(this);
+ }
+}
+
+/**
+ * Described in header.
+ */
+ipsec_policy_t *ipsec_policy_create(host_t *src, host_t *dst,
+ traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts,
+ policy_dir_t direction, policy_type_t type,
+ ipsec_sa_cfg_t *sa, mark_t mark,
+ policy_priority_t priority)
+{
+ private_ipsec_policy_t *this;
+
+ INIT(this,
+ .public = {
+ .match = _match,
+ .match_packet = _match_packet,
+ .get_source_ts = _get_source_ts,
+ .get_destination_ts = _get_destination_ts,
+ .get_direction = _get_direction,
+ .get_priority = _get_priority,
+ .get_reqid = _get_reqid,
+ .get_type = _get_type,
+ .get_ref = _get_ref,
+ .destroy = _destroy,
+ },
+ .src = src->clone(src),
+ .dst = dst->clone(dst),
+ .src_ts = src_ts->clone(src_ts),
+ .dst_ts = dst_ts->clone(dst_ts),
+ .protocol = max(src_ts->get_protocol(src_ts),
+ dst_ts->get_protocol(dst_ts)),
+ .direction = direction,
+ .type = type,
+ .sa = *sa,
+ .mark = mark,
+ .priority = priority,
+ .refcount = 1,
+ );
+
+ return &this->public;
+}
diff --git a/src/libipsec/ipsec_policy.h b/src/libipsec/ipsec_policy.h
new file mode 100644
index 000000000..67ad0b0ed
--- /dev/null
+++ b/src/libipsec/ipsec_policy.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_policy ipsec_policy
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_POLICY_H
+#define IPSEC_POLICY_H
+
+#include "ip_packet.h"
+
+#include <library.h>
+#include <utils/host.h>
+#include <ipsec/ipsec_types.h>
+#include <selectors/traffic_selector.h>
+
+typedef struct ipsec_policy_t ipsec_policy_t;
+
+/**
+ * IPsec Policy
+ */
+struct ipsec_policy_t {
+
+ /**
+ * Get the source traffic selector of this policy
+ *
+ * @return the source traffic selector
+ */
+ traffic_selector_t *(*get_source_ts)(ipsec_policy_t *this);
+
+ /**
+ * Get the destination traffic selector of this policy
+ *
+ * @return the destination traffic selector
+ */
+ traffic_selector_t *(*get_destination_ts)(ipsec_policy_t *this);
+
+ /**
+ * Get the direction of this policy
+ *
+ * @return direction
+ */
+ policy_dir_t (*get_direction)(ipsec_policy_t *this);
+
+ /**
+ * Get the priority of this policy
+ *
+ * @return priority
+ */
+ policy_priority_t (*get_priority)(ipsec_policy_t *this);
+
+ /**
+ * Get the type of this policy (e.g. IPsec)
+ *
+ * @return the policy type
+ */
+ policy_type_t (*get_type)(ipsec_policy_t *this);
+
+ /**
+ * Get the reqid associated to this policy
+ *
+ * @return the reqid
+ */
+ u_int32_t (*get_reqid)(ipsec_policy_t *this);
+
+ /**
+ * Get another reference to this policy
+ *
+ * @return additional reference to the policy
+ */
+ ipsec_policy_t *(*get_ref)(ipsec_policy_t *this);
+
+ /**
+ * Check if this policy matches all given parameters
+ *
+ * @param src_ts source traffic selector
+ * @param dst_ts destination traffic selector
+ * @param direction traffic direction
+ * @param reqid reqid of the policy
+ * @param mark mark for this policy
+ * @param prioirty policy priority
+ * @return TRUE if policy matches all parameters
+ */
+ bool (*match)(ipsec_policy_t *this, traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts, policy_dir_t direction,
+ u_int32_t reqid, mark_t mark, policy_priority_t priority);
+
+ /**
+ * Check if this policy matches the given IP packet
+ *
+ * @param packet IP packet
+ * @return TRUE if policy matches the packet
+ */
+ bool (*match_packet)(ipsec_policy_t *this, ip_packet_t *packet);
+
+ /**
+ * Destroy an ipsec_policy_t
+ */
+ void (*destroy)(ipsec_policy_t *this);
+
+};
+
+/**
+ * Create an ipsec_policy_t instance
+ *
+ * @param src source address of SA
+ * @param dst dest address of SA
+ * @param src_ts traffic selector to match traffic source
+ * @param dst_ts traffic selector to match traffic dest
+ * @param direction direction of traffic, POLICY_(IN|OUT|FWD)
+ * @param type type of policy, POLICY_(IPSEC|PASS|DROP)
+ * @param sa details about the SA(s) tied to this policy
+ * @param mark mark for this policy
+ * @param priority priority of this policy
+ * @return ipsec policy instance
+ */
+ipsec_policy_t *ipsec_policy_create(host_t *src, host_t *dst,
+ traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts,
+ policy_dir_t direction, policy_type_t type,
+ ipsec_sa_cfg_t *sa, mark_t mark,
+ policy_priority_t priority);
+
+#endif /** IPSEC_POLICY_H @}*/
diff --git a/src/libipsec/ipsec_policy_mgr.c b/src/libipsec/ipsec_policy_mgr.c
new file mode 100644
index 000000000..41ba792c3
--- /dev/null
+++ b/src/libipsec/ipsec_policy_mgr.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "ipsec_policy_mgr.h"
+
+#include <debug.h>
+#include <threading/rwlock.h>
+#include <utils/linked_list.h>
+
+/** Base priority for installed policies */
+#define PRIO_BASE 512
+
+typedef struct private_ipsec_policy_mgr_t private_ipsec_policy_mgr_t;
+
+/**
+ * Private additions to ipsec_policy_mgr_t.
+ */
+struct private_ipsec_policy_mgr_t {
+
+ /**
+ * Public members of ipsec_policy_mgr_t.
+ */
+ ipsec_policy_mgr_t public;
+
+ /**
+ * Installed policies (ipsec_policy_entry_t*)
+ */
+ linked_list_t *policies;
+
+ /**
+ * Lock to safely access the list of policies
+ */
+ rwlock_t *lock;
+
+};
+
+/**
+ * Helper struct to store policies in a list sorted by the same pseudo-priority
+ * used by the NETLINK kernel interface.
+ */
+typedef struct {
+
+ /**
+ * Priority used to sort policies
+ */
+ u_int32_t priority;
+
+ /**
+ * The policy
+ */
+ ipsec_policy_t *policy;
+
+} ipsec_policy_entry_t;
+
+/**
+ * Calculate the pseudo-priority to sort policies. This is the same algorithm
+ * used by the NETLINK kernel interface (i.e. high priority -> low value).
+ */
+static u_int32_t calculate_priority(policy_priority_t policy_priority,
+ traffic_selector_t *src,
+ traffic_selector_t *dst)
+{
+ u_int32_t priority = PRIO_BASE;
+ u_int16_t port;
+ u_int8_t mask, proto;
+ host_t *net;
+
+ switch (policy_priority)
+ {
+ case POLICY_PRIORITY_FALLBACK:
+ priority <<= 1;
+ /* fall-through */
+ case POLICY_PRIORITY_ROUTED:
+ priority <<= 1;
+ /* fall-through */
+ case POLICY_PRIORITY_DEFAULT:
+ break;
+ }
+ /* calculate priority based on selector size, small size = high prio */
+ src->to_subnet(src, &net, &mask);
+ priority -= mask;
+ proto = src->get_protocol(src);
+ port = net->get_port(net);
+ net->destroy(net);
+
+ dst->to_subnet(dst, &net, &mask);
+ priority -= mask;
+ proto = max(proto, dst->get_protocol(dst));
+ port = max(port, net->get_port(net));
+ net->destroy(net);
+
+ priority <<= 2; /* make some room for the two flags */
+ priority += port ? 0 : 2;
+ priority += proto ? 0 : 1;
+ return priority;
+}
+
+/**
+ * Create a policy entry
+ */
+static ipsec_policy_entry_t *policy_entry_create(ipsec_policy_t *policy)
+{
+ ipsec_policy_entry_t *this;
+
+ INIT(this,
+ .policy = policy,
+ .priority = calculate_priority(policy->get_priority(policy),
+ policy->get_source_ts(policy),
+ policy->get_destination_ts(policy)),
+ );
+ return this;
+}
+
+/**
+ * Destroy a policy entry
+ */
+static void policy_entry_destroy(ipsec_policy_entry_t *this)
+{
+ this->policy->destroy(this->policy);
+ free(this);
+}
+
+METHOD(ipsec_policy_mgr_t, add_policy, status_t,
+ private_ipsec_policy_mgr_t *this, host_t *src, host_t *dst,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts,
+ policy_dir_t direction, policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
+ policy_priority_t priority)
+{
+ enumerator_t *enumerator;
+ ipsec_policy_entry_t *entry, *current;
+ ipsec_policy_t *policy;
+
+ if (type != POLICY_IPSEC || direction == POLICY_FWD)
+ { /* we ignore these policies as we currently have no use for them */
+ return SUCCESS;
+ }
+
+ DBG2(DBG_ESP, "adding policy %R === %R %N", src_ts, dst_ts,
+ policy_dir_names, direction);
+
+ policy = ipsec_policy_create(src, dst, src_ts, dst_ts, direction, type, sa,
+ mark, priority);
+ entry = policy_entry_create(policy);
+
+ this->lock->write_lock(this->lock);
+ enumerator = this->policies->create_enumerator(this->policies);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (current->priority >= entry->priority)
+ {
+ break;
+ }
+ }
+ this->policies->insert_before(this->policies, enumerator, entry);
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ return SUCCESS;
+}
+
+METHOD(ipsec_policy_mgr_t, del_policy, status_t,
+ private_ipsec_policy_mgr_t *this, traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts, policy_dir_t direction, u_int32_t reqid,
+ mark_t mark, policy_priority_t policy_priority)
+{
+ enumerator_t *enumerator;
+ ipsec_policy_entry_t *current, *found = NULL;
+ u_int32_t priority;
+
+ if (direction == POLICY_FWD)
+ { /* we ignore these policies as we currently have no use for them */
+ return SUCCESS;
+ }
+ DBG2(DBG_ESP, "deleting policy %R === %R %N", src_ts, dst_ts,
+ policy_dir_names, direction);
+
+ priority = calculate_priority(policy_priority, src_ts, dst_ts);
+
+ this->lock->write_lock(this->lock);
+ enumerator = this->policies->create_enumerator(this->policies);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (current->priority == priority &&
+ current->policy->match(current->policy, src_ts, dst_ts, direction,
+ reqid, mark, policy_priority))
+ {
+ this->policies->remove_at(this->policies, enumerator);
+ found = current;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ if (found)
+ {
+ policy_entry_destroy(found);
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+METHOD(ipsec_policy_mgr_t, flush_policies, status_t,
+ private_ipsec_policy_mgr_t *this)
+{
+ ipsec_policy_entry_t *entry;
+
+ DBG2(DBG_ESP, "flushing policies");
+
+ this->lock->write_lock(this->lock);
+ while (this->policies->remove_last(this->policies,
+ (void**)&entry) == SUCCESS)
+ {
+ policy_entry_destroy(entry);
+ }
+ this->lock->unlock(this->lock);
+ return SUCCESS;
+}
+
+METHOD(ipsec_policy_mgr_t, find_by_packet, ipsec_policy_t*,
+ private_ipsec_policy_mgr_t *this, ip_packet_t *packet, bool inbound)
+{
+ enumerator_t *enumerator;
+ ipsec_policy_entry_t *current;
+ ipsec_policy_t *found = NULL;
+
+ this->lock->read_lock(this->lock);
+ enumerator = this->policies->create_enumerator(this->policies);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ ipsec_policy_t *policy = current->policy;
+
+ if ((inbound == (policy->get_direction(policy) == POLICY_IN)) &&
+ policy->match_packet(policy, packet))
+ {
+ found = policy->get_ref(policy);
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->lock->unlock(this->lock);
+ return found;
+}
+
+METHOD(ipsec_policy_mgr_t, destroy, void,
+ private_ipsec_policy_mgr_t *this)
+{
+ flush_policies(this);
+ this->policies->destroy(this->policies);
+ this->lock->destroy(this->lock);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+ipsec_policy_mgr_t *ipsec_policy_mgr_create()
+{
+ private_ipsec_policy_mgr_t *this;
+
+ INIT(this,
+ .public = {
+ .add_policy = _add_policy,
+ .del_policy = _del_policy,
+ .flush_policies = _flush_policies,
+ .find_by_packet = _find_by_packet,
+ .destroy = _destroy,
+ },
+ .policies = linked_list_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
diff --git a/src/libipsec/ipsec_policy_mgr.h b/src/libipsec/ipsec_policy_mgr.h
new file mode 100644
index 000000000..d3ee1074f
--- /dev/null
+++ b/src/libipsec/ipsec_policy_mgr.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_policy_mgr ipsec_policy_mgr
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_POLICY_MGR_H_
+#define IPSEC_POLICY_MGR_H_
+
+#include "ipsec_policy.h"
+#include "ip_packet.h"
+
+#include <library.h>
+#include <utils/host.h>
+#include <utils/linked_list.h>
+#include <ipsec/ipsec_types.h>
+#include <selectors/traffic_selector.h>
+
+typedef struct ipsec_policy_mgr_t ipsec_policy_mgr_t;
+
+/**
+ * IPsec policy manager
+ *
+ * The first methods are modeled after those in kernel_ipsec_t.
+ *
+ * @note Only policies of type POLICY_IPSEC are currently used, also policies
+ * with direction POLICY_FWD are ignored. Any packets that do not match an
+ * installed policy will be dropped.
+ */
+struct ipsec_policy_mgr_t {
+
+ /**
+ * Add a policy
+ *
+ * A policy is always associated to an SA. Traffic which matches a
+ * policy is handled by the SA with the same reqid.
+ *
+ * @param src source address of SA
+ * @param dst dest address of SA
+ * @param src_ts traffic selector to match traffic source
+ * @param dst_ts traffic selector to match traffic dest
+ * @param direction direction of traffic, POLICY_(IN|OUT|FWD)
+ * @param type type of policy, POLICY_(IPSEC|PASS|DROP)
+ * @param sa details about the SA(s) tied to this policy
+ * @param mark mark for this policy
+ * @param priority priority of this policy
+ * @return SUCCESS if operation completed
+ */
+ status_t (*add_policy)(ipsec_policy_mgr_t *this,
+ host_t *src, host_t *dst, traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts, policy_dir_t direction,
+ policy_type_t type, ipsec_sa_cfg_t *sa, mark_t mark,
+ policy_priority_t priority);
+
+ /**
+ * Remove a policy
+ *
+ * @param src_ts traffic selector to match traffic source
+ * @param dst_ts traffic selector to match traffic dest
+ * @param direction direction of traffic, POLICY_(IN|OUT|FWD)
+ * @param reqid unique ID of the associated SA
+ * @param mark optional mark
+ * @param priority priority of the policy
+ * @return SUCCESS if operation completed
+ */
+ status_t (*del_policy)(ipsec_policy_mgr_t *this,
+ traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts,
+ policy_dir_t direction, u_int32_t reqid, mark_t mark,
+ policy_priority_t priority);
+
+ /**
+ * Flush all policies
+ *
+ * @return SUCCESS if operation completed
+ */
+ status_t (*flush_policies)(ipsec_policy_mgr_t *this);
+
+ /**
+ * Find the policy that matches the given IP packet best
+ *
+ * @param packet IP packet to match
+ * @param inbound TRUE for an inbound packet
+ * @return reference to the policy, or NULL if none found
+ */
+ ipsec_policy_t *(*find_by_packet)(ipsec_policy_mgr_t *this,
+ ip_packet_t *packet, bool inbound);
+
+ /**
+ * Destroy an ipsec_policy_mgr_t
+ */
+ void (*destroy)(ipsec_policy_mgr_t *this);
+
+};
+
+/**
+ * Create an ipsec_policy_mgr instance
+ *
+ * @return ipsec_policy_mgr
+ */
+ipsec_policy_mgr_t *ipsec_policy_mgr_create();
+
+#endif /** IPSEC_POLICY_MGR_H_ @}*/
diff --git a/src/libipsec/ipsec_processor.c b/src/libipsec/ipsec_processor.c
new file mode 100644
index 000000000..a91d9e074
--- /dev/null
+++ b/src/libipsec/ipsec_processor.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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 "ipsec.h"
+#include "ipsec_processor.h"
+
+#include <debug.h>
+#include <library.h>
+#include <threading/rwlock.h>
+#include <utils/blocking_queue.h>
+#include <processing/jobs/callback_job.h>
+
+typedef struct private_ipsec_processor_t private_ipsec_processor_t;
+
+/**
+ * Private additions to ipsec_processor_t.
+ */
+struct private_ipsec_processor_t {
+
+ /**
+ * Public members
+ */
+ ipsec_processor_t public;
+
+ /**
+ * Queue for inbound packets (esp_packet_t*)
+ */
+ blocking_queue_t *inbound_queue;
+
+ /**
+ * Queue for outbound packets (ip_packet_t*)
+ */
+ blocking_queue_t *outbound_queue;
+
+ /**
+ * Registered inbound callback
+ */
+ struct {
+ ipsec_inbound_cb_t cb;
+ void *data;
+ } inbound;
+
+ /**
+ * Registered outbound callback
+ */
+ struct {
+ ipsec_outbound_cb_t cb;
+ void *data;
+ } outbound;
+
+ /**
+ * Lock used to synchronize access to the callbacks
+ */
+ rwlock_t *lock;
+};
+
+/**
+ * Deliver an inbound IP packet to the registered listener
+ */
+static void deliver_inbound(private_ipsec_processor_t *this,
+ esp_packet_t *packet)
+{
+ this->lock->read_lock(this->lock);
+ if (this->inbound.cb)
+ {
+ this->inbound.cb(this->inbound.data, packet->extract_payload(packet));
+ }
+ else
+ {
+ DBG2(DBG_ESP, "no inbound callback registered, dropping packet");
+ }
+ packet->destroy(packet);
+ this->lock->unlock(this->lock);
+}
+
+/**
+ * Processes inbound packets
+ */
+static job_requeue_t process_inbound(private_ipsec_processor_t *this)
+{
+ esp_packet_t *packet;
+ ipsec_sa_t *sa;
+ u_int8_t next_header;
+ u_int32_t spi;
+
+ packet = (esp_packet_t*)this->inbound_queue->dequeue(this->inbound_queue);
+
+ if (!packet->parse_header(packet, &spi))
+ {
+ packet->destroy(packet);
+ return JOB_REQUEUE_DIRECT;
+ }
+
+ sa = ipsec->sas->checkout_by_spi(ipsec->sas, spi,
+ packet->get_destination(packet));
+ if (!sa)
+ {
+ DBG2(DBG_ESP, "inbound ESP packet does not belong to an installed SA");
+ packet->destroy(packet);
+ return JOB_REQUEUE_DIRECT;
+ }
+
+ if (!sa->is_inbound(sa))
+ {
+ DBG1(DBG_ESP, "error: IPsec SA is not inbound");
+ packet->destroy(packet);
+ ipsec->sas->checkin(ipsec->sas, sa);
+ return JOB_REQUEUE_DIRECT;
+ }
+
+ if (packet->decrypt(packet, sa->get_esp_context(sa)) != SUCCESS)
+ {
+ ipsec->sas->checkin(ipsec->sas, sa);
+ packet->destroy(packet);
+ return JOB_REQUEUE_DIRECT;
+ }
+ ipsec->sas->checkin(ipsec->sas, sa);
+
+ next_header = packet->get_next_header(packet);
+ switch (next_header)
+ {
+ case IPPROTO_IPIP:
+ case IPPROTO_IPV6:
+ {
+ ipsec_policy_t *policy;
+ ip_packet_t *ip_packet;
+
+ ip_packet = packet->get_payload(packet);
+ policy = ipsec->policies->find_by_packet(ipsec->policies,
+ ip_packet, TRUE);
+ if (policy)
+ { /* TODO-IPSEC: update policy/sa stats? */
+ deliver_inbound(this, packet);
+ policy->destroy(policy);
+ break;
+ }
+ DBG1(DBG_ESP, "discarding inbound IP packet due to policy");
+ /* no matching policy found, fall-through */
+ }
+ case IPPROTO_NONE:
+ /* discard dummy packets */
+ /* fall-through */
+ default:
+ packet->destroy(packet);
+ break;
+ }
+ return JOB_REQUEUE_DIRECT;
+}
+
+/**
+ * Send an ESP packet using the registered outbound callback
+ */
+static void send_outbound(private_ipsec_processor_t *this,
+ esp_packet_t *packet)
+{
+ this->lock->read_lock(this->lock);
+ if (this->outbound.cb)
+ {
+ this->outbound.cb(this->outbound.data, packet);
+ }
+ else
+ {
+ DBG2(DBG_ESP, "no outbound callback registered, dropping packet");
+ packet->destroy(packet);
+ }
+ this->lock->unlock(this->lock);
+}
+
+/**
+ * Processes outbound packets
+ */
+static job_requeue_t process_outbound(private_ipsec_processor_t *this)
+{
+ ipsec_policy_t *policy;
+ esp_packet_t *esp_packet;
+ ip_packet_t *packet;
+ ipsec_sa_t *sa;
+ host_t *src, *dst;
+
+ packet = (ip_packet_t*)this->outbound_queue->dequeue(this->outbound_queue);
+
+ policy = ipsec->policies->find_by_packet(ipsec->policies, packet, FALSE);
+ if (!policy)
+ {
+ DBG1(DBG_ESP, "no matching outbound IPsec policy for %H == %H",
+ packet->get_source(packet), packet->get_destination(packet));
+ packet->destroy(packet);
+ return JOB_REQUEUE_DIRECT;
+ }
+
+ sa = ipsec->sas->checkout_by_reqid(ipsec->sas, policy->get_reqid(policy),
+ FALSE);
+ if (!sa)
+ { /* TODO-IPSEC: send an acquire to uppper layer */
+ DBG1(DBG_ESP, "could not find an outbound IPsec SA for reqid {%u}, "
+ "dropping packet", policy->get_reqid(policy));
+ packet->destroy(packet);
+ policy->destroy(policy);
+ return JOB_REQUEUE_DIRECT;
+ }
+ src = sa->get_source(sa);
+ dst = sa->get_destination(sa);
+ esp_packet = esp_packet_create_from_payload(src->clone(src),
+ dst->clone(dst), packet);
+ if (esp_packet->encrypt(esp_packet, sa->get_esp_context(sa),
+ sa->get_spi(sa)) != SUCCESS)
+ {
+ ipsec->sas->checkin(ipsec->sas, sa);
+ esp_packet->destroy(esp_packet);
+ policy->destroy(policy);
+ return JOB_REQUEUE_DIRECT;
+ }
+ /* TODO-IPSEC: update policy/sa counters? */
+ ipsec->sas->checkin(ipsec->sas, sa);
+ policy->destroy(policy);
+ send_outbound(this, esp_packet);
+ return JOB_REQUEUE_DIRECT;
+}
+
+METHOD(ipsec_processor_t, queue_inbound, void,
+ private_ipsec_processor_t *this, esp_packet_t *packet)
+{
+ this->inbound_queue->enqueue(this->inbound_queue, packet);
+}
+
+METHOD(ipsec_processor_t, queue_outbound, void,
+ private_ipsec_processor_t *this, ip_packet_t *packet)
+{
+ this->outbound_queue->enqueue(this->outbound_queue, packet);
+}
+
+METHOD(ipsec_processor_t, register_inbound, void,
+ private_ipsec_processor_t *this, ipsec_inbound_cb_t cb, void *data)
+{
+ this->lock->write_lock(this->lock);
+ this->inbound.cb = cb;
+ this->inbound.data = data;
+ this->lock->unlock(this->lock);
+}
+
+METHOD(ipsec_processor_t, unregister_inbound, void,
+ private_ipsec_processor_t *this, ipsec_inbound_cb_t cb)
+{
+ this->lock->write_lock(this->lock);
+ if (this->inbound.cb == cb)
+ {
+ this->inbound.cb = NULL;
+ }
+ this->lock->unlock(this->lock);
+}
+
+METHOD(ipsec_processor_t, register_outbound, void,
+ private_ipsec_processor_t *this, ipsec_outbound_cb_t cb, void *data)
+{
+ this->lock->write_lock(this->lock);
+ this->outbound.cb = cb;
+ this->outbound.data = data;
+ this->lock->unlock(this->lock);
+}
+
+METHOD(ipsec_processor_t, unregister_outbound, void,
+ private_ipsec_processor_t *this, ipsec_outbound_cb_t cb)
+{
+ this->lock->write_lock(this->lock);
+ if (this->outbound.cb == cb)
+ {
+ this->outbound.cb = NULL;
+ }
+ this->lock->unlock(this->lock);
+}
+
+METHOD(ipsec_processor_t, destroy, void,
+ private_ipsec_processor_t *this)
+{
+ this->inbound_queue->destroy_offset(this->inbound_queue,
+ offsetof(esp_packet_t, destroy));
+ this->outbound_queue->destroy_offset(this->outbound_queue,
+ offsetof(ip_packet_t, destroy));
+ this->lock->destroy(this->lock);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+ipsec_processor_t *ipsec_processor_create()
+{
+ private_ipsec_processor_t *this;
+
+ INIT(this,
+ .public = {
+ .queue_inbound = _queue_inbound,
+ .queue_outbound = _queue_outbound,
+ .register_inbound = _register_inbound,
+ .unregister_inbound = _unregister_inbound,
+ .register_outbound = _register_outbound,
+ .unregister_outbound = _unregister_outbound,
+ .destroy = _destroy,
+ },
+ .inbound_queue = blocking_queue_create(),
+ .outbound_queue = blocking_queue_create(),
+ .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ );
+
+ lib->processor->queue_job(lib->processor,
+ (job_t*)callback_job_create((callback_job_cb_t)process_inbound, this,
+ NULL, (callback_job_cancel_t)return_false));
+ lib->processor->queue_job(lib->processor,
+ (job_t*)callback_job_create((callback_job_cb_t)process_outbound, this,
+ NULL, (callback_job_cancel_t)return_false));
+ return &this->public;
+}
diff --git a/src/libipsec/ipsec_processor.h b/src/libipsec/ipsec_processor.h
new file mode 100644
index 000000000..0a409828b
--- /dev/null
+++ b/src/libipsec/ipsec_processor.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_processor ipsec_processor
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_PROCESSOR_H_
+#define IPSEC_PROCESSOR_H_
+
+#include "ip_packet.h"
+#include "esp_packet.h"
+
+typedef struct ipsec_processor_t ipsec_processor_t;
+
+/**
+ * Callback called to deliver an inbound plaintext packet.
+ *
+ * @param data data supplied during registration of the callback
+ * @param packet plaintext IP packet to deliver
+ */
+typedef void (*ipsec_inbound_cb_t)(void *data, ip_packet_t *packet);
+
+/**
+ * Callback called to send an ESP packet.
+ *
+ * @note The ESP packet currently comes without IP header (and without UDP
+ * header in case of UDP encapsulation)
+ *
+ * @param data data supplied during registration of the callback
+ * @param packet ESP packet to send
+ */
+typedef void (*ipsec_outbound_cb_t)(void *data, esp_packet_t *packet);
+
+/**
+ * IPsec processor
+ */
+struct ipsec_processor_t {
+
+ /**
+ * Queue an inbound ESP packet for processing.
+ *
+ * @param packet the ESP packet to process
+ */
+ void (*queue_inbound)(ipsec_processor_t *this, esp_packet_t *packet);
+
+ /**
+ * Queue an outbound plaintext IP packet for processing.
+ *
+ * @param packet the plaintext IP packet
+ */
+ void (*queue_outbound)(ipsec_processor_t *this, ip_packet_t *packet);
+
+ /**
+ * Register the callback used to deliver inbound plaintext packets.
+ *
+ * @param cb the inbound callback function
+ * @param data optional data provided to callback
+ */
+ void (*register_inbound)(ipsec_processor_t *this, ipsec_inbound_cb_t cb,
+ void *data);
+
+ /**
+ * Unregister a previously registered inbound callback.
+ *
+ * @param cb previously registered callback function
+ */
+ void (*unregister_inbound)(ipsec_processor_t *this,
+ ipsec_inbound_cb_t cb);
+
+ /**
+ * Register the callback used to send outbound ESP packets.
+ *
+ * @param cb the outbound callback function
+ * @param data optional data provided to callback
+ */
+ void (*register_outbound)(ipsec_processor_t *this, ipsec_outbound_cb_t cb,
+ void *data);
+
+ /**
+ * Unregister a previously registered outbound callback.
+ *
+ * @param cb previously registered callback function
+ */
+ void (*unregister_outbound)(ipsec_processor_t *this,
+ ipsec_outbound_cb_t cb);
+
+ /**
+ * Destroy an ipsec_processor_t.
+ */
+ void (*destroy)(ipsec_processor_t *this);
+
+};
+
+/**
+ * Create an ipsec_processor_t instance
+ *
+ * @return IPsec processor instance
+ */
+ipsec_processor_t *ipsec_processor_create();
+
+#endif /** IPSEC_PROCESSOR_H_ @}*/
diff --git a/src/libipsec/ipsec_sa.c b/src/libipsec/ipsec_sa.c
new file mode 100644
index 000000000..cccd16404
--- /dev/null
+++ b/src/libipsec/ipsec_sa.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "ipsec_sa.h"
+
+#include <library.h>
+#include <debug.h>
+
+typedef struct private_ipsec_sa_t private_ipsec_sa_t;
+
+/**
+ * Private additions to ipsec_sa_t.
+ */
+struct private_ipsec_sa_t {
+
+ /**
+ * Public members
+ */
+ ipsec_sa_t public;
+
+ /**
+ * SPI of this SA
+ */
+ u_int32_t spi;
+
+ /**
+ * Source address
+ */
+ host_t *src;
+
+ /**
+ * Destination address
+ */
+ host_t *dst;
+
+ /**
+ * Protocol
+ */
+ u_int8_t protocol;
+
+ /**
+ * Reqid of this SA
+ */
+ u_int32_t reqid;
+
+ /**
+ * Lifetime configuration
+ */
+ lifetime_cfg_t lifetime;
+
+ /**
+ * IPsec mode
+ */
+ ipsec_mode_t mode;
+
+ /**
+ * TRUE if extended sequence numbers are used
+ */
+ bool esn;
+
+ /**
+ * TRUE if this is an inbound SA
+ */
+ bool inbound;
+
+ /**
+ * ESP context
+ */
+ esp_context_t *esp_context;
+};
+
+METHOD(ipsec_sa_t, get_source, host_t*,
+ private_ipsec_sa_t *this)
+{
+ return this->src;
+}
+
+METHOD(ipsec_sa_t, get_destination, host_t*,
+ private_ipsec_sa_t *this)
+{
+ return this->dst;
+}
+
+METHOD(ipsec_sa_t, get_spi, u_int32_t,
+ private_ipsec_sa_t *this)
+{
+ return this->spi;
+}
+
+METHOD(ipsec_sa_t, get_reqid, u_int32_t,
+ private_ipsec_sa_t *this)
+{
+ return this->reqid;
+}
+
+METHOD(ipsec_sa_t, get_protocol, u_int8_t,
+ private_ipsec_sa_t *this)
+{
+ return this->protocol;
+}
+
+METHOD(ipsec_sa_t, get_lifetime, lifetime_cfg_t*,
+ private_ipsec_sa_t *this)
+{
+ return &this->lifetime;
+}
+
+METHOD(ipsec_sa_t, is_inbound, bool,
+ private_ipsec_sa_t *this)
+{
+ return this->inbound;
+}
+
+METHOD(ipsec_sa_t, get_esp_context, esp_context_t*,
+ private_ipsec_sa_t *this)
+{
+ return this->esp_context;
+}
+
+METHOD(ipsec_sa_t, match_by_spi_dst, bool,
+ private_ipsec_sa_t *this, u_int32_t spi, host_t *dst)
+{
+ return this->spi == spi && this->dst->ip_equals(this->dst, dst);
+}
+
+METHOD(ipsec_sa_t, match_by_spi_src_dst, bool,
+ private_ipsec_sa_t *this, u_int32_t spi, host_t *src, host_t *dst)
+{
+ return this->spi == spi && this->src->ip_equals(this->src, src) &&
+ this->dst->ip_equals(this->dst, dst);
+}
+
+METHOD(ipsec_sa_t, match_by_reqid, bool,
+ private_ipsec_sa_t *this, u_int32_t reqid, bool inbound)
+{
+ return this->reqid == reqid && this->inbound == inbound;
+}
+
+METHOD(ipsec_sa_t, destroy, void,
+ private_ipsec_sa_t *this)
+{
+ this->src->destroy(this->src);
+ this->dst->destroy(this->dst);
+ DESTROY_IF(this->esp_context);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
+ u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc,
+ lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
+ u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode,
+ u_int16_t ipcomp, u_int16_t cpi, bool encap, bool esn, bool inbound,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
+{
+ private_ipsec_sa_t *this;
+
+ if (protocol != IPPROTO_ESP)
+ {
+ DBG1(DBG_ESP, " IPsec SA: protocol not supported");
+ return NULL;
+ }
+ if (!encap)
+ {
+ DBG1(DBG_ESP, " IPsec SA: only UDP encapsulation is supported");
+ return NULL;
+ }
+ if (esn)
+ {
+ DBG1(DBG_ESP, " IPsec SA: ESN not supported");
+ return NULL;
+ }
+ if (ipcomp != IPCOMP_NONE)
+ {
+ DBG1(DBG_ESP, " IPsec SA: compression not supported");
+ return NULL;
+ }
+ if (mode != MODE_TUNNEL)
+ {
+ DBG1(DBG_ESP, " IPsec SA: unsupported mode");
+ return NULL;
+ }
+
+ INIT(this,
+ .public = {
+ .destroy = _destroy,
+ .get_source = _get_source,
+ .get_destination = _get_destination,
+ .get_spi = _get_spi,
+ .get_reqid = _get_reqid,
+ .get_protocol = _get_protocol,
+ .get_lifetime = _get_lifetime,
+ .is_inbound = _is_inbound,
+ .match_by_spi_dst = _match_by_spi_dst,
+ .match_by_spi_src_dst = _match_by_spi_src_dst,
+ .match_by_reqid = _match_by_reqid,
+ .get_esp_context = _get_esp_context,
+ },
+ .spi = spi,
+ .src = src->clone(src),
+ .dst = dst->clone(dst),
+ .lifetime = *lifetime,
+ .protocol = protocol,
+ .reqid = reqid,
+ .mode = mode,
+ .esn = esn,
+ .inbound = inbound,
+ );
+
+ this->esp_context = esp_context_create(enc_alg, enc_key, int_alg, int_key,
+ inbound);
+ if (!this->esp_context)
+ {
+ destroy(this);
+ return NULL;
+ }
+ return &this->public;
+}
diff --git a/src/libipsec/ipsec_sa.h b/src/libipsec/ipsec_sa.h
new file mode 100644
index 000000000..5fd03b6e4
--- /dev/null
+++ b/src/libipsec/ipsec_sa.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_sa ipsec_sa
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_SA_H_
+#define IPSEC_SA_H_
+
+#include "esp_context.h"
+
+#include <library.h>
+#include <utils/host.h>
+#include <selectors/traffic_selector.h>
+#include <ipsec/ipsec_types.h>
+
+typedef struct ipsec_sa_t ipsec_sa_t;
+
+/**
+ * IPsec Security Association (SA)
+ */
+struct ipsec_sa_t {
+
+ /**
+ * Get the source address for this SA
+ *
+ * @return source address of this SA
+ */
+ host_t *(*get_source)(ipsec_sa_t *this);
+
+ /**
+ * Get the destination address for this SA
+ *
+ * @return destination address of this SA
+ */
+ host_t *(*get_destination)(ipsec_sa_t *this);
+
+ /**
+ * Get the SPI for this SA
+ *
+ * @return SPI of this SA
+ */
+ u_int32_t (*get_spi)(ipsec_sa_t *this);
+
+ /**
+ * Get the reqid of this SA
+ *
+ * @return reqid of this SA
+ */
+ u_int32_t (*get_reqid)(ipsec_sa_t *this);
+
+ /**
+ * Get the protocol (e.g. IPPROTO_ESP) of this SA
+ *
+ * @return protocol of this SA
+ */
+ u_int8_t (*get_protocol)(ipsec_sa_t *this);
+
+ /**
+ * Returns whether this SA is inbound or outbound
+ *
+ * @return TRUE if inbound, FALSE if outbound
+ */
+ bool (*is_inbound)(ipsec_sa_t *this);
+
+ /**
+ * Get the lifetime information for this SA
+ * Note that this information is always relative to the time when the
+ * SA was installed (i.e. it is not adjusted over time)
+ *
+ * @return lifetime of this SA
+ */
+ lifetime_cfg_t *(*get_lifetime)(ipsec_sa_t *this);
+
+ /**
+ * Get the ESP context for this SA
+ *
+ * @return ESP context of this SA
+ */
+ esp_context_t *(*get_esp_context)(ipsec_sa_t *this);
+
+ /**
+ * Check if this SA matches all given parameters
+ *
+ * @param spi SPI
+ * @param dst destination address
+ * @return TRUE if this SA matches all parameters, FALSE otherwise
+ */
+ bool (*match_by_spi_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *dst);
+
+ /**
+ * Check if this SA matches all given parameters
+ *
+ * @param spi SPI
+ * @param src source address
+ * @param dst destination address
+ * @return TRUE if this SA matches all parameters, FALSE otherwise
+ */
+ bool (*match_by_spi_src_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *src,
+ host_t *dst);
+
+ /**
+ * Check if this SA matches all given parameters
+ *
+ * @param reqid reqid
+ * @param inbound TRUE for inbound SA, FALSE for outbound
+ * @return TRUE if this SA matches all parameters, FALSE otherwise
+ */
+ bool (*match_by_reqid)(ipsec_sa_t *this, u_int32_t reqid, bool inbound);
+
+ /**
+ * Destroy an ipsec_sa_t
+ */
+ void (*destroy)(ipsec_sa_t *this);
+
+};
+
+/**
+ * Create an ipsec_sa_t instance
+ *
+ * @param spi SPI for this SA
+ * @param src source address for this SA (gets cloned)
+ * @param dst destination address for this SA (gets cloned)
+ * @param protocol protocol for this SA (only ESP is supported)
+ * @param reqid reqid for this SA
+ * @param mark mark for this SA (ignored)
+ * @param tfc Traffic Flow Confidentiality (currently not supported)
+ * @param lifetime lifetime for this SA
+ * @param enc_alg encryption algorithm for this SA
+ * @param enc_key encryption key for this SA
+ * @param int_alg integrity protection algorithm
+ * @param int_key integrity protection key
+ * @param mode mode for this SA (only tunnel mode is supported)
+ * @param ipcomp IPcomp transform (not supported, use IPCOMP_NONE)
+ * @param cpi CPI for IPcomp (ignored)
+ * @param encap enable UDP encapsulation (must be TRUE)
+ * @param esn Extended Sequence Numbers (currently not supported)
+ * @param inbound TRUE if this is an inbound SA, FALSE otherwise
+ * @param src_ts source traffic selector
+ * @param dst_ts destination traffic selector
+ * @return the IPsec SA, or NULL if the creation failed
+ */
+ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
+ u_int8_t protocol, u_int32_t reqid, mark_t mark,
+ u_int32_t tfc, lifetime_cfg_t *lifetime,
+ u_int16_t enc_alg, chunk_t enc_key,
+ u_int16_t int_alg, chunk_t int_key,
+ ipsec_mode_t mode, u_int16_t ipcomp, u_int16_t cpi,
+ bool encap, bool esn, bool inbound,
+ traffic_selector_t *src_ts,
+ traffic_selector_t *dst_ts);
+
+#endif /** IPSEC_SA_H_ @}*/
diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c
new file mode 100644
index 000000000..e42c77aa5
--- /dev/null
+++ b/src/libipsec/ipsec_sa_mgr.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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 "ipsec.h"
+#include "ipsec_sa_mgr.h"
+
+#include <debug.h>
+#include <library.h>
+#include <processing/jobs/callback_job.h>
+#include <threading/condvar.h>
+#include <threading/mutex.h>
+#include <utils/hashtable.h>
+#include <utils/linked_list.h>
+
+typedef struct private_ipsec_sa_mgr_t private_ipsec_sa_mgr_t;
+
+/**
+ * Private additions to ipsec_sa_mgr_t.
+ */
+struct private_ipsec_sa_mgr_t {
+
+ /**
+ * Public members of ipsec_sa_mgr_t.
+ */
+ ipsec_sa_mgr_t public;
+
+ /**
+ * Installed SAs
+ */
+ linked_list_t *sas;
+
+ /**
+ * SPIs allocated using get_spi()
+ */
+ hashtable_t *allocated_spis;
+
+ /**
+ * Mutex used to synchronize access to the SA manager
+ */
+ mutex_t *mutex;
+
+ /**
+ * RNG used to generate SPIs
+ */
+ rng_t *rng;
+};
+
+/**
+ * Struct to keep track of locked IPsec SAs
+ */
+typedef struct {
+
+ /**
+ * IPsec SA
+ */
+ ipsec_sa_t *sa;
+
+ /**
+ * Set if this SA is currently in use by a thread
+ */
+ bool locked;
+
+ /**
+ * Condvar used by threads to wait for this entry
+ */
+ condvar_t *condvar;
+
+ /**
+ * Number of threads waiting for this entry
+ */
+ u_int waiting_threads;
+
+ /**
+ * Set if this entry is awaiting deletion
+ */
+ bool awaits_deletion;
+
+} ipsec_sa_entry_t;
+
+/**
+ * Helper struct for expiration events
+ */
+typedef struct {
+
+ /**
+ * IPsec SA manager
+ */
+ private_ipsec_sa_mgr_t *manager;
+
+ /**
+ * Entry that expired
+ */
+ ipsec_sa_entry_t *entry;
+
+ /**
+ * 0 if this is a hard expire, otherwise the offset in s (soft->hard)
+ */
+ u_int32_t hard_offset;
+
+} ipsec_sa_expired_t;
+
+/*
+ * Used for the hash table of allocated SPIs
+ */
+static bool spi_equals(u_int32_t *spi, u_int32_t *other_spi)
+{
+ return *spi == *other_spi;
+}
+
+static u_int spi_hash(u_int32_t *spi)
+{
+ return chunk_hash(chunk_from_thing(*spi));
+}
+
+/**
+ * Create an SA entry
+ */
+static ipsec_sa_entry_t *create_entry(ipsec_sa_t *sa)
+{
+ ipsec_sa_entry_t *this;
+
+ INIT(this,
+ .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .sa = sa,
+ );
+ return this;
+}
+
+/**
+ * Destroy an SA entry
+ */
+static void destroy_entry(ipsec_sa_entry_t *entry)
+{
+ entry->condvar->destroy(entry->condvar);
+ entry->sa->destroy(entry->sa);
+ free(entry);
+}
+
+/**
+ * Makes sure an entry is safe to remove
+ * Must be called with this->mutex held.
+ *
+ * @return TRUE if entry can be removed, FALSE if entry is already
+* being removed by another thread
+ */
+static bool wait_remove_entry(private_ipsec_sa_mgr_t *this,
+ ipsec_sa_entry_t *entry)
+{
+ if (entry->awaits_deletion)
+ {
+ /* this will be deleted by another thread already */
+ return FALSE;
+ }
+ entry->awaits_deletion = TRUE;
+ while (entry->locked)
+ {
+ entry->condvar->wait(entry->condvar, this->mutex);
+ }
+ while (entry->waiting_threads > 0)
+ {
+ entry->condvar->broadcast(entry->condvar);
+ entry->condvar->wait(entry->condvar, this->mutex);
+ }
+ return TRUE;
+}
+
+/**
+ * Waits until an is available and then locks it.
+ * Must only be called with this->mutex held
+ */
+static bool wait_for_entry(private_ipsec_sa_mgr_t *this,
+ ipsec_sa_entry_t *entry)
+{
+ while (entry->locked && !entry->awaits_deletion)
+ {
+ entry->waiting_threads++;
+ entry->condvar->wait(entry->condvar, this->mutex);
+ entry->waiting_threads--;
+ }
+ if (entry->awaits_deletion)
+ {
+ /* others may still be waiting, */
+ entry->condvar->signal(entry->condvar);
+ return FALSE;
+ }
+ entry->locked = TRUE;
+ return TRUE;
+}
+
+/**
+ * Flushes all entries
+ * Must be called with this->mutex held.
+ */
+static void flush_entries(private_ipsec_sa_mgr_t *this)
+{
+ ipsec_sa_entry_t *current;
+ enumerator_t *enumerator;
+
+ DBG2(DBG_ESP, "flushing SAD");
+
+ enumerator = this->sas->create_enumerator(this->sas);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (wait_remove_entry(this, current))
+ {
+ this->sas->remove_at(this->sas, enumerator);
+ destroy_entry(current);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
+/*
+ * Different match functions to find SAs in the linked list
+ */
+static bool match_entry_by_ptr(ipsec_sa_entry_t *item, ipsec_sa_entry_t *entry)
+{
+ return item == entry;
+}
+
+static bool match_entry_by_sa_ptr(ipsec_sa_entry_t *item, ipsec_sa_t *sa)
+{
+ return item->sa == sa;
+}
+
+static bool match_entry_by_spi_inbound(ipsec_sa_entry_t *item, u_int32_t spi,
+ bool inbound)
+{
+ return item->sa->get_spi(item->sa) == spi &&
+ item->sa->is_inbound(item->sa) == inbound;
+}
+
+static bool match_entry_by_spi_src_dst(ipsec_sa_entry_t *item, u_int32_t spi,
+ host_t *src, host_t *dst)
+{
+ return item->sa->match_by_spi_src_dst(item->sa, spi, src, dst);
+}
+
+static bool match_entry_by_reqid_inbound(ipsec_sa_entry_t *item,
+ u_int32_t reqid, bool inbound)
+{
+ return item->sa->match_by_reqid(item->sa, reqid, inbound);
+}
+
+static bool match_entry_by_spi_dst(ipsec_sa_entry_t *item, u_int32_t spi,
+ host_t *dst)
+{
+ return item->sa->match_by_spi_dst(item->sa, spi, dst);
+}
+
+/**
+ * Remove an entry
+ */
+static bool remove_entry(private_ipsec_sa_mgr_t *this, ipsec_sa_entry_t *entry)
+{
+ ipsec_sa_entry_t *current;
+ enumerator_t *enumerator;
+ bool removed = FALSE;
+
+ enumerator = this->sas->create_enumerator(this->sas);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (current == entry)
+ {
+ if (wait_remove_entry(this, current))
+ {
+ this->sas->remove_at(this->sas, enumerator);
+ removed = TRUE;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return removed;
+}
+
+/**
+ * Callback for expiration events
+ */
+static job_requeue_t sa_expired(ipsec_sa_expired_t *expired)
+{
+ private_ipsec_sa_mgr_t *this = expired->manager;
+
+ this->mutex->lock(this->mutex);
+ if (this->sas->find_first(this->sas, (void*)match_entry_by_ptr,
+ NULL, expired->entry) == SUCCESS)
+ {
+ u_int32_t hard_offset = expired->hard_offset;
+ ipsec_sa_t *sa = expired->entry->sa;
+
+ ipsec->events->expire(ipsec->events, sa->get_reqid(sa),
+ sa->get_protocol(sa), sa->get_spi(sa),
+ hard_offset == 0);
+ if (hard_offset)
+ { /* soft limit reached, schedule hard expire */
+ expired->hard_offset = 0;
+ this->mutex->unlock(this->mutex);
+ return JOB_RESCHEDULE(hard_offset);
+ }
+ /* hard limit reached */
+ if (remove_entry(this, expired->entry))
+ {
+ destroy_entry(expired->entry);
+ }
+ }
+ this->mutex->unlock(this->mutex);
+ return JOB_REQUEUE_NONE;
+}
+
+/**
+ * Schedule a job to handle IPsec SA expiration
+ */
+static void schedule_expiration(private_ipsec_sa_mgr_t *this,
+ ipsec_sa_entry_t *entry)
+{
+ lifetime_cfg_t *lifetime = entry->sa->get_lifetime(entry->sa);
+ ipsec_sa_expired_t *expired;
+ callback_job_t *job;
+ u_int32_t timeout;
+
+ INIT(expired,
+ .manager = this,
+ .entry = entry,
+ );
+
+ /* schedule a rekey first, a hard timeout will be scheduled then, if any */
+ expired->hard_offset = lifetime->time.life - lifetime->time.rekey;
+ timeout = lifetime->time.rekey;
+
+ if (lifetime->time.life <= lifetime->time.rekey ||
+ lifetime->time.rekey == 0)
+ { /* no rekey, schedule hard timeout */
+ expired->hard_offset = 0;
+ timeout = lifetime->time.life;
+ }
+
+ job = callback_job_create((callback_job_cb_t)sa_expired, expired,
+ (callback_job_cleanup_t)free, NULL);
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, timeout);
+}
+
+/**
+ * Remove all allocated SPIs
+ */
+static void flush_allocated_spis(private_ipsec_sa_mgr_t *this)
+{
+ enumerator_t *enumerator;
+ u_int32_t *current;
+
+ DBG2(DBG_ESP, "flushing allocated SPIs");
+ enumerator = this->allocated_spis->create_enumerator(this->allocated_spis);
+ while (enumerator->enumerate(enumerator, NULL, (void**)&current))
+ {
+ this->allocated_spis->remove_at(this->allocated_spis, enumerator);
+ DBG2(DBG_ESP, " removed allocated SPI %.8x", ntohl(*current));
+ free(current);
+ }
+ enumerator->destroy(enumerator);
+}
+
+/**
+ * Pre-allocate an SPI for an inbound SA
+ */
+static bool allocate_spi(private_ipsec_sa_mgr_t *this, u_int32_t spi)
+{
+ u_int32_t *spi_alloc;
+
+ if (this->allocated_spis->get(this->allocated_spis, &spi) ||
+ this->sas->find_first(this->sas, (void*)match_entry_by_spi_inbound,
+ NULL, spi, TRUE) == SUCCESS)
+ {
+ return FALSE;
+ }
+ spi_alloc = malloc_thing(u_int32_t);
+ *spi_alloc = spi;
+ this->allocated_spis->put(this->allocated_spis, spi_alloc, spi_alloc);
+ return TRUE;
+}
+
+METHOD(ipsec_sa_mgr_t, get_spi, status_t,
+ private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int8_t protocol,
+ u_int32_t reqid, u_int32_t *spi)
+{
+ u_int32_t spi_new;
+
+ DBG2(DBG_ESP, "allocating SPI for reqid {%u}", reqid);
+
+ this->mutex->lock(this->mutex);
+ if (!this->rng)
+ {
+ this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ if (!this->rng)
+ {
+ this->mutex->unlock(this->mutex);
+ DBG1(DBG_ESP, "failed to create RNG for SPI generation");
+ return FAILED;
+ }
+ }
+
+ do
+ {
+ if (!this->rng->get_bytes(this->rng, sizeof(spi_new),
+ (u_int8_t*)&spi_new))
+ {
+ this->mutex->unlock(this->mutex);
+ DBG1(DBG_ESP, "failed to allocate SPI for reqid {%u}", reqid);
+ return FAILED;
+ }
+ /* make sure the SPI is valid (not in range 0-255) */
+ spi_new |= 0x00000100;
+ spi_new = htonl(spi_new);
+ }
+ while (!allocate_spi(this, spi_new));
+ this->mutex->unlock(this->mutex);
+
+ *spi = spi_new;
+
+ DBG2(DBG_ESP, "allocated SPI %.8x for reqid {%u}", ntohl(*spi), reqid);
+ return SUCCESS;
+}
+
+METHOD(ipsec_sa_mgr_t, add_sa, status_t,
+ private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
+ u_int8_t protocol, u_int32_t reqid, mark_t mark, u_int32_t tfc,
+ lifetime_cfg_t *lifetime, u_int16_t enc_alg, chunk_t enc_key,
+ u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
+ u_int16_t cpi, bool encap, bool esn, bool inbound,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts)
+{
+ ipsec_sa_entry_t *entry;
+ ipsec_sa_t *sa_new;
+
+ DBG2(DBG_ESP, "adding SAD entry with SPI %.8x and reqid {%u}",
+ ntohl(spi), reqid);
+ DBG2(DBG_ESP, " using encryption algorithm %N with key size %d",
+ encryption_algorithm_names, enc_alg, enc_key.len * 8);
+ DBG2(DBG_ESP, " using integrity algorithm %N with key size %d",
+ integrity_algorithm_names, int_alg, int_key.len * 8);
+
+ sa_new = ipsec_sa_create(spi, src, dst, protocol, reqid, mark, tfc,
+ lifetime, enc_alg, enc_key, int_alg, int_key, mode,
+ ipcomp, cpi, encap, esn, inbound, src_ts, dst_ts);
+ if (!sa_new)
+ {
+ DBG1(DBG_ESP, "failed to create SAD entry");
+ return FAILED;
+ }
+
+ this->mutex->lock(this->mutex);
+
+ if (inbound)
+ { /* remove any pre-allocated SPIs */
+ u_int32_t *spi_alloc;
+
+ spi_alloc = this->allocated_spis->remove(this->allocated_spis, &spi);
+ free(spi_alloc);
+ }
+
+ if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
+ NULL, spi, src, dst) == SUCCESS)
+ {
+ this->mutex->unlock(this->mutex);
+ DBG1(DBG_ESP, "failed to install SAD entry: already installed");
+ sa_new->destroy(sa_new);
+ return FAILED;
+ }
+
+ entry = create_entry(sa_new);
+ schedule_expiration(this, entry);
+ this->sas->insert_last(this->sas, entry);
+
+ this->mutex->unlock(this->mutex);
+ return SUCCESS;
+}
+
+METHOD(ipsec_sa_mgr_t, del_sa, status_t,
+ private_ipsec_sa_mgr_t *this, host_t *src, host_t *dst, u_int32_t spi,
+ u_int8_t protocol, u_int16_t cpi, mark_t mark)
+{
+ ipsec_sa_entry_t *current, *found = NULL;
+ enumerator_t *enumerator;
+
+ this->mutex->lock(this->mutex);
+ enumerator = this->sas->create_enumerator(this->sas);
+ while (enumerator->enumerate(enumerator, (void**)&current))
+ {
+ if (match_entry_by_spi_src_dst(current, spi, src, dst))
+ {
+ if (wait_remove_entry(this, current))
+ {
+ this->sas->remove_at(this->sas, enumerator);
+ found = current;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ this->mutex->unlock(this->mutex);
+
+ if (found)
+ {
+ DBG2(DBG_ESP, "deleted %sbound SAD entry with SPI %.8x",
+ found->sa->is_inbound(found->sa) ? "in" : "out", ntohl(spi));
+ destroy_entry(found);
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+METHOD(ipsec_sa_mgr_t, checkout_by_reqid, ipsec_sa_t*,
+ private_ipsec_sa_mgr_t *this, u_int32_t reqid, bool inbound)
+{
+ ipsec_sa_entry_t *entry;
+ ipsec_sa_t *sa = NULL;
+
+ this->mutex->lock(this->mutex);
+ if (this->sas->find_first(this->sas, (void*)match_entry_by_reqid_inbound,
+ (void**)&entry, reqid, inbound) == SUCCESS &&
+ wait_for_entry(this, entry))
+ {
+ sa = entry->sa;
+ }
+ this->mutex->unlock(this->mutex);
+ return sa;
+}
+
+METHOD(ipsec_sa_mgr_t, checkout_by_spi, ipsec_sa_t*,
+ private_ipsec_sa_mgr_t *this, u_int32_t spi, host_t *dst)
+{
+ ipsec_sa_entry_t *entry;
+ ipsec_sa_t *sa = NULL;
+
+ this->mutex->lock(this->mutex);
+ if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_dst,
+ (void**)&entry, spi, dst) == SUCCESS &&
+ wait_for_entry(this, entry))
+ {
+ sa = entry->sa;
+ }
+ this->mutex->unlock(this->mutex);
+ return sa;
+}
+
+METHOD(ipsec_sa_mgr_t, checkin, void,
+ private_ipsec_sa_mgr_t *this, ipsec_sa_t *sa)
+{
+ ipsec_sa_entry_t *entry;
+
+ this->mutex->lock(this->mutex);
+ if (this->sas->find_first(this->sas, (void*)match_entry_by_sa_ptr,
+ (void**)&entry, sa) == SUCCESS)
+ {
+ if (entry->locked)
+ {
+ entry->locked = FALSE;
+ entry->condvar->signal(entry->condvar);
+ }
+ }
+ this->mutex->unlock(this->mutex);
+}
+
+METHOD(ipsec_sa_mgr_t, flush_sas, status_t,
+ private_ipsec_sa_mgr_t *this)
+{
+ this->mutex->lock(this->mutex);
+ flush_entries(this);
+ this->mutex->unlock(this->mutex);
+ return SUCCESS;
+}
+
+METHOD(ipsec_sa_mgr_t, destroy, void,
+ private_ipsec_sa_mgr_t *this)
+{
+ this->mutex->lock(this->mutex);
+ flush_entries(this);
+ flush_allocated_spis(this);
+ this->mutex->unlock(this->mutex);
+
+ this->allocated_spis->destroy(this->allocated_spis);
+ this->sas->destroy(this->sas);
+
+ this->mutex->destroy(this->mutex);
+ DESTROY_IF(this->rng);
+ free(this);
+}
+
+/**
+ * Described in header.
+ */
+ipsec_sa_mgr_t *ipsec_sa_mgr_create()
+{
+ private_ipsec_sa_mgr_t *this;
+
+ INIT(this,
+ .public = {
+ .get_spi = _get_spi,
+ .add_sa = _add_sa,
+ .del_sa = _del_sa,
+ .checkout_by_spi = _checkout_by_spi,
+ .checkout_by_reqid = _checkout_by_reqid,
+ .checkin = _checkin,
+ .flush_sas = _flush_sas,
+ .destroy = _destroy,
+ },
+ .sas = linked_list_create(),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ .allocated_spis = hashtable_create((hashtable_hash_t)spi_hash,
+ (hashtable_equals_t)spi_equals, 16),
+ );
+
+ return &this->public;
+}
diff --git a/src/libipsec/ipsec_sa_mgr.h b/src/libipsec/ipsec_sa_mgr.h
new file mode 100644
index 000000000..303b36f0e
--- /dev/null
+++ b/src/libipsec/ipsec_sa_mgr.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012 Giuliano Grassi
+ * Copyright (C) 2012 Ralf Sager
+ * 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.
+ */
+
+/**
+ * @defgroup ipsec_sa_mgr ipsec_sa_mgr
+ * @{ @ingroup libipsec
+ */
+
+#ifndef IPSEC_SA_MGR_H_
+#define IPSEC_SA_MGR_H_
+
+#include "ipsec_sa.h"
+
+#include <library.h>
+#include <ipsec/ipsec_types.h>
+#include <selectors/traffic_selector.h>
+#include <utils/host.h>
+
+typedef struct ipsec_sa_mgr_t ipsec_sa_mgr_t;
+
+/**
+ * IPsec SA manager
+ *
+ * The first methods are modeled after those in kernel_ipsec_t.
+ */
+struct ipsec_sa_mgr_t {
+
+ /**
+ * Allocate an SPI for an inbound IPsec SA
+ *
+ * @param src source address of the SA
+ * @param dst destination address of the SA
+ * @param protocol protocol of the SA (only ESP supported)
+ * @param reqid reqid for the SA
+ * @param spi the allocated SPI
+ * @return SUCCESS of operation successful
+ */
+ status_t (*get_spi)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
+ u_int8_t protocol, u_int32_t reqid, u_int32_t *spi);
+
+ /**
+ * Add a new SA
+ *
+ * @param src source address for this SA (gets cloned)
+ * @param dst destination address for this SA (gets cloned)
+ * @param spi SPI for this SA
+ * @param protocol protocol for this SA (only ESP is supported)
+ * @param reqid reqid for this SA
+ * @param mark mark for this SA (ignored)
+ * @param tfc Traffic Flow Confidentiality (not yet supported)
+ * @param lifetime lifetime for this SA
+ * @param enc_alg encryption algorithm for this SA
+ * @param enc_key encryption key for this SA
+ * @param int_alg integrity protection algorithm
+ * @param int_key integrity protection key
+ * @param mode mode for this SA (only tunnel mode is supported)
+ * @param ipcomp IPcomp transform (not supported, use IPCOMP_NONE)
+ * @param cpi CPI for IPcomp (ignored)
+ * @param encap enable UDP encapsulation (must be TRUE)
+ * @param esn Extended Sequence Numbers (currently not supported)
+ * @param inbound TRUE if this is an inbound SA, FALSE otherwise
+ * @param src_ts source traffic selector
+ * @param dst_ts destination traffic selector
+ * @return SUCCESS if operation completed
+ */
+ status_t (*add_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
+ u_int32_t spi, u_int8_t protocol, u_int32_t reqid,
+ mark_t mark, u_int32_t tfc, lifetime_cfg_t *lifetime,
+ u_int16_t enc_alg, chunk_t enc_key, u_int16_t int_alg,
+ chunk_t int_key, ipsec_mode_t mode, u_int16_t ipcomp,
+ u_int16_t cpi, bool encap, bool esn, bool inbound,
+ traffic_selector_t *src_ts, traffic_selector_t *dst_ts);
+
+ /**
+ * Delete a previously added SA
+ *
+ * @param spi SPI of the SA
+ * @param src source address of the SA
+ * @param dst destination address of the SA
+ * @param protocol protocol of the SA
+ * @param cpi CPI for IPcomp
+ * @param mark optional mark
+ * @return SUCCESS if operation completed
+ */
+ status_t (*del_sa)(ipsec_sa_mgr_t *this, host_t *src, host_t *dst,
+ u_int32_t spi, u_int8_t protocol, u_int16_t cpi,
+ mark_t mark);
+
+ /**
+ * Flush all SAs
+ *
+ * @return SUCCESS if operation completed
+ */
+ status_t (*flush_sas)(ipsec_sa_mgr_t *this);
+
+ /**
+ * Checkout an installed IPsec SA by SPI and destination address
+ * Can be used to find the correct SA for an inbound packet.
+ *
+ * The matching SA is locked until it is checked in using checkin().
+ * If the matching SA is already checked out, this call blocks until the
+ * SA is checked in.
+ *
+ * Since other threads may be waiting for the checked out SA, it should be
+ * checked in as soon as possible after use.
+ *
+ * @param spi SPI (e.g. of an inbound packet)
+ * @param dst destination address (e.g. of an inbound packet)
+ * @return the matching IPsec SA, or NULL if none is found
+ */
+ ipsec_sa_t *(*checkout_by_spi)(ipsec_sa_mgr_t *this, u_int32_t spi,
+ host_t *dst);
+
+ /**
+ * Checkout an installed IPsec SA by its reqid and inbound/outbound flag.
+ * Can be used to find the correct SA for an outbound packet.
+ *
+ * The matching SA is locked until it is checked in using checkin().
+ * If the matching SA is already checked out, this call blocks until the
+ * SA is checked in.
+ *
+ * Since other threads may be waiting for a checked out SA, it should be
+ * checked in as soon as possible after use.
+ *
+ * @param reqid reqid of the SA
+ * @param inbound TRUE for an inbound SA, FALSE for an outbound SA
+ * @return the matching IPsec SA, or NULL if none is found
+ */
+ ipsec_sa_t *(*checkout_by_reqid)(ipsec_sa_mgr_t *this, u_int32_t reqid,
+ bool inbound);
+
+ /**
+ * Checkin an SA after use.
+ *
+ * @param sa checked out SA
+ */
+ void (*checkin)(ipsec_sa_mgr_t *this, ipsec_sa_t *sa);
+
+ /**
+ * Destroy an ipsec_sa_mgr_t
+ */
+ void (*destroy)(ipsec_sa_mgr_t *this);
+
+};
+
+/**
+ * Create an ipsec_sa_mgr instance
+ *
+ * @return IPsec SA manager instance
+ */
+ipsec_sa_mgr_t *ipsec_sa_mgr_create();
+
+#endif /** IPSEC_SA_MGR_H_ @}*/