aboutsummaryrefslogtreecommitdiffstats
path: root/src/libhydra
diff options
context:
space:
mode:
Diffstat (limited to 'src/libhydra')
-rw-r--r--src/libhydra/attributes/mem_pool.c55
-rw-r--r--src/libhydra/attributes/mem_pool.h16
-rw-r--r--src/libhydra/kernel/kernel_interface.c18
-rw-r--r--src/libhydra/kernel/kernel_interface.h19
-rw-r--r--src/libhydra/kernel/kernel_ipsec.h8
-rw-r--r--src/libhydra/kernel/kernel_net.h8
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c398
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c4
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c23
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h25
-rw-r--r--src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c4
11 files changed, 293 insertions, 285 deletions
diff --git a/src/libhydra/attributes/mem_pool.c b/src/libhydra/attributes/mem_pool.c
index af53e10de..c82b1d02f 100644
--- a/src/libhydra/attributes/mem_pool.c
+++ b/src/libhydra/attributes/mem_pool.c
@@ -21,7 +21,7 @@
#include <collections/linked_list.h>
#include <threading/mutex.h>
-#define POOL_LIMIT (sizeof(uintptr_t)*8)
+#define POOL_LIMIT (sizeof(u_int)*8 - 1)
typedef struct private_mem_pool_t private_mem_pool_t;
@@ -513,12 +513,11 @@ METHOD(mem_pool_t, destroy, void,
}
/**
- * Described in header
+ * Generic constructor
*/
-mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
+static private_mem_pool_t *create_generic(char *name)
{
private_mem_pool_t *this;
- int addr_bits;
INIT(this,
.public = {
@@ -538,6 +537,18 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
.mutex = mutex_create(MUTEX_TYPE_DEFAULT),
);
+ return this;
+}
+
+/**
+ * Described in header
+ */
+mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
+{
+ private_mem_pool_t *this;
+ int addr_bits;
+
+ this = create_generic(name);
if (base)
{
addr_bits = base->get_family(base) == AF_INET ? 32 : 128;
@@ -550,7 +561,7 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
DBG1(DBG_CFG, "virtual IP pool too large, limiting to %H/%d",
base, addr_bits - bits);
}
- this->size = 1 << (bits);
+ this->size = 1 << bits;
if (this->size > 2)
{ /* do not use first and last addresses of a block */
@@ -563,3 +574,37 @@ mem_pool_t *mem_pool_create(char *name, host_t *base, int bits)
return &this->public;
}
+/**
+ * Described in header
+ */
+mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to)
+{
+ private_mem_pool_t *this;
+ chunk_t fromaddr, toaddr;
+ u_int32_t diff;
+
+ fromaddr = from->get_address(from);
+ toaddr = to->get_address(to);
+
+ if (from->get_family(from) != to->get_family(to) ||
+ fromaddr.len != toaddr.len || fromaddr.len < sizeof(diff) ||
+ memcmp(fromaddr.ptr, toaddr.ptr, toaddr.len) > 0)
+ {
+ DBG1(DBG_CFG, "invalid IP address range: %H-%H", from, to);
+ return NULL;
+ }
+ if (fromaddr.len > sizeof(diff) &&
+ !chunk_equals(chunk_create(fromaddr.ptr, fromaddr.len - sizeof(diff)),
+ chunk_create(toaddr.ptr, toaddr.len - sizeof(diff))))
+ {
+ DBG1(DBG_CFG, "IP address range too large: %H-%H", from, to);
+ return NULL;
+ }
+ this = create_generic(name);
+ this->base = from->clone(from);
+ diff = untoh32(toaddr.ptr + toaddr.len - sizeof(diff)) -
+ untoh32(fromaddr.ptr + fromaddr.len - sizeof(diff));
+ this->size = diff + 1;
+
+ return &this->public;
+}
diff --git a/src/libhydra/attributes/mem_pool.h b/src/libhydra/attributes/mem_pool.h
index 692885ecd..7347bb547 100644
--- a/src/libhydra/attributes/mem_pool.h
+++ b/src/libhydra/attributes/mem_pool.h
@@ -89,7 +89,7 @@ struct mem_pool_t {
*
* @param id the id to acquire an address for
* @param requested acquire this address, if possible
- * @param existing TRUE to look for an existing lease, FALSE for a new one
+ * @param operation acquire operation to perform, see above
* @return the acquired address
*/
host_t* (*acquire_address)(mem_pool_t *this, identification_t *id,
@@ -128,9 +128,19 @@ struct mem_pool_t {
*
* @param name name of this pool
* @param base base address of this pool, NULL to create an empty pool
- * @param bits net mask
+ * @param bits number of non-network bits in base, as in CIDR notation
+ * @return memory pool instance
*/
mem_pool_t *mem_pool_create(char *name, host_t *base, int bits);
-#endif /** MEM_POOL_H_ @} */
+/**
+ * Create an in-memory IP address from a range.
+ *
+ * @param name name of this pool
+ * @param from start of ranged pool
+ * @param to end of ranged pool
+ * @return memory pool instance, NULL if range invalid
+ */
+mem_pool_t *mem_pool_create_range(char *name, host_t *from, host_t *to);
+#endif /** MEM_POOL_H_ @} */
diff --git a/src/libhydra/kernel/kernel_interface.c b/src/libhydra/kernel/kernel_interface.c
index 8948e0561..b82fd6d3d 100644
--- a/src/libhydra/kernel/kernel_interface.c
+++ b/src/libhydra/kernel/kernel_interface.c
@@ -137,6 +137,22 @@ struct private_kernel_interface_t {
bool ifaces_exclude;
};
+METHOD(kernel_interface_t, get_features, kernel_feature_t,
+ private_kernel_interface_t *this)
+{
+ kernel_feature_t features = 0;
+
+ if (this->ipsec && this->ipsec->get_features)
+ {
+ features |= this->ipsec->get_features(this->ipsec);
+ }
+ if (this->net && this->net->get_features)
+ {
+ features |= this->net->get_features(this->net);
+ }
+ return features;
+}
+
METHOD(kernel_interface_t, get_spi, status_t,
private_kernel_interface_t *this, host_t *src, host_t *dst,
u_int8_t protocol, u_int32_t reqid, u_int32_t *spi)
@@ -682,6 +698,7 @@ kernel_interface_t *kernel_interface_create()
INIT(this,
.public = {
+ .get_features = _get_features,
.get_spi = _get_spi,
.get_cpi = _get_cpi,
.add_sa = _add_sa,
@@ -757,4 +774,3 @@ kernel_interface_t *kernel_interface_create()
return &this->public;
}
-
diff --git a/src/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h
index 8d8ef2e83..8d8d048d0 100644
--- a/src/libhydra/kernel/kernel_interface.h
+++ b/src/libhydra/kernel/kernel_interface.h
@@ -47,6 +47,7 @@
#define KERNEL_INTERFACE_H_
typedef struct kernel_interface_t kernel_interface_t;
+typedef enum kernel_feature_t kernel_feature_t;
#include <networking/host.h>
#include <crypto/prf_plus.h>
@@ -56,6 +57,17 @@ typedef struct kernel_interface_t kernel_interface_t;
#include <kernel/kernel_net.h>
/**
+ * Bitfield of optional features a kernel backend supports.
+ *
+ * This feature-set is for both, kernel_ipsec_t and kernel_net_t. Each
+ * backend returns a subset of these features.
+ */
+enum kernel_feature_t {
+ /** IPsec can process ESPv3 (RFC 4303) TFC padded packets */
+ KERNEL_ESP_V3_TFC = (1<<0),
+};
+
+/**
* Constructor function for ipsec kernel interface
*/
typedef kernel_ipsec_t* (*kernel_ipsec_constructor_t)(void);
@@ -74,6 +86,13 @@ typedef kernel_net_t* (*kernel_net_constructor_t)(void);
struct kernel_interface_t {
/**
+ * Get the feature set supported by the net and ipsec kernel backends.
+ *
+ * @return ORed feature-set of backends
+ */
+ kernel_feature_t (*get_features)(kernel_interface_t *this);
+
+ /**
* Get a SPI from the kernel.
*
* @param src source address of SA
diff --git a/src/libhydra/kernel/kernel_ipsec.h b/src/libhydra/kernel/kernel_ipsec.h
index 1da0805cb..c8afcaffd 100644
--- a/src/libhydra/kernel/kernel_ipsec.h
+++ b/src/libhydra/kernel/kernel_ipsec.h
@@ -30,6 +30,7 @@ typedef struct kernel_ipsec_t kernel_ipsec_t;
#include <ipsec/ipsec_types.h>
#include <selectors/traffic_selector.h>
#include <plugins/plugin.h>
+#include <kernel/kernel_interface.h>
/**
* Interface to the ipsec subsystem of the kernel.
@@ -45,6 +46,13 @@ typedef struct kernel_ipsec_t kernel_ipsec_t;
struct kernel_ipsec_t {
/**
+ * Get the feature set supported by this kernel backend.
+ *
+ * @return ORed feature-set of backend
+ */
+ kernel_feature_t (*get_features)(kernel_ipsec_t *this);
+
+ /**
* Get a SPI from the kernel.
*
* @param src source address of SA
diff --git a/src/libhydra/kernel/kernel_net.h b/src/libhydra/kernel/kernel_net.h
index 6a3b2cee7..0d3417f1d 100644
--- a/src/libhydra/kernel/kernel_net.h
+++ b/src/libhydra/kernel/kernel_net.h
@@ -28,6 +28,7 @@ typedef enum kernel_address_type_t kernel_address_type_t;
#include <collections/enumerator.h>
#include <networking/host.h>
#include <plugins/plugin.h>
+#include <kernel/kernel_interface.h>
/**
* Type of addresses (e.g. when enumerating them)
@@ -56,6 +57,13 @@ enum kernel_address_type_t {
struct kernel_net_t {
/**
+ * Get the feature set supported by this kernel backend.
+ *
+ * @return ORed feature-set of backend
+ */
+ kernel_feature_t (*get_features)(kernel_net_t *this);
+
+ /**
* Get our outgoing source address for a destination.
*
* Does a route lookup to get the source address used to reach dest.
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
index f9b2634a0..3e84d1699 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -95,12 +95,6 @@
#define XFRM_RTA(nlh, x) ((struct rtattr*)(NLMSG_DATA(nlh) + \
NLMSG_ALIGN(sizeof(x))))
/**
- * Returns a pointer to the next rtattr following rta.
- * !!! Do not use this to parse messages. Use RTA_NEXT and RTA_OK instead !!!
- */
-#define XFRM_RTA_NEXT(rta) ((struct rtattr*)(((char*)(rta)) + \
- RTA_ALIGN((rta)->rta_len)))
-/**
* Returns the total size of attached rta data
* (after 'usual' netlink data x like 'struct xfrm_usersa_info')
*/
@@ -176,8 +170,6 @@ ENUM(xfrm_attr_type_names, XFRMA_UNSPEC, XFRMA_REPLAY_ESN_VAL,
"XFRMA_REPLAY_ESN_VAL",
);
-#define END_OF_LIST -1
-
/**
* Algorithms for encryption
*/
@@ -208,7 +200,6 @@ static kernel_algorithm_t encryption_algs[] = {
/* {ENCR_CAMELLIA_CCM_ICV16, "***" }, */
{ENCR_SERPENT_CBC, "serpent" },
{ENCR_TWOFISH_CBC, "twofish" },
- {END_OF_LIST, NULL }
};
/**
@@ -226,7 +217,6 @@ static kernel_algorithm_t integrity_algs[] = {
/* {AUTH_DES_MAC, "***" }, */
/* {AUTH_KPDK_MD5, "***" }, */
{AUTH_AES_XCBC_96, "xcbc(aes)" },
- {END_OF_LIST, NULL }
};
/**
@@ -237,7 +227,6 @@ static kernel_algorithm_t compression_algs[] = {
{IPCOMP_DEFLATE, "deflate" },
{IPCOMP_LZS, "lzs" },
{IPCOMP_LZJH, "lzjh" },
- {END_OF_LIST, NULL }
};
/**
@@ -246,33 +235,39 @@ static kernel_algorithm_t compression_algs[] = {
static char* lookup_algorithm(transform_type_t type, int ikev2)
{
kernel_algorithm_t *list;
- char *name = NULL;
+ int i, count;
+ char *name;
switch (type)
{
case ENCRYPTION_ALGORITHM:
list = encryption_algs;
+ count = countof(encryption_algs);
break;
case INTEGRITY_ALGORITHM:
list = integrity_algs;
+ count = countof(integrity_algs);
break;
case COMPRESSION_ALGORITHM:
list = compression_algs;
+ count = countof(compression_algs);
break;
default:
return NULL;
}
- while (list->ikev2 != END_OF_LIST)
+ for (i = 0; i < count; i++)
{
- if (list->ikev2 == ikev2)
+ if (list[i].ikev2 == ikev2)
{
- return list->name;
+ return list[i].name;
}
- list++;
}
- hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface, ikev2,
- type, NULL, &name);
- return name;
+ if (hydra->kernel_interface->lookup_algorithm(hydra->kernel_interface,
+ ikev2, type, NULL, &name))
+ {
+ return name;
+ }
+ return NULL;
}
typedef struct private_kernel_netlink_ipsec_t private_kernel_netlink_ipsec_t;
@@ -787,7 +782,7 @@ static traffic_selector_t* selector2ts(struct xfrm_selector *sel, bool src)
if (host)
{
return traffic_selector_create_from_subnet(host, prefixlen,
- sel->proto, port);
+ sel->proto, port, port ?: 65535);
}
return NULL;
}
@@ -1036,6 +1031,12 @@ static job_requeue_t receive_events(private_kernel_netlink_ipsec_t *this)
return JOB_REQUEUE_DIRECT;
}
+METHOD(kernel_ipsec_t, get_features, kernel_feature_t,
+ private_kernel_netlink_ipsec_t *this)
+{
+ return KERNEL_ESP_V3_TFC;
+}
+
/**
* Get an SPI for a specific protocol from the kernel.
*/
@@ -1144,6 +1145,26 @@ METHOD(kernel_ipsec_t, get_cpi, status_t,
return SUCCESS;
}
+/**
+ * Add a XFRM mark to message if required
+ */
+static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark)
+{
+ if (mark.value)
+ {
+ struct xfrm_mark *xmrk;
+
+ xmrk = netlink_reserve(hdr, buflen, XFRMA_MARK, sizeof(*xmrk));
+ if (!xmrk)
+ {
+ return FALSE;
+ }
+ xmrk->v = mark.value;
+ xmrk->m = mark.mask;
+ }
+ return TRUE;
+}
+
METHOD(kernel_ipsec_t, add_sa, status_t,
private_kernel_netlink_ipsec_t *this, host_t *src, host_t *dst,
u_int32_t spi, u_int8_t protocol, u_int32_t reqid, mark_t mark,
@@ -1216,8 +1237,6 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
sa->lft.soft_use_expires_seconds = 0;
sa->lft.hard_use_expires_seconds = 0;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_info);
-
switch (enc_alg)
{
case ENCR_UNDEFINED:
@@ -1250,23 +1269,17 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
encryption_algorithm_names, enc_alg, enc_key.len * 8);
- rthdr->rta_type = XFRMA_ALG_AEAD;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_aead) +
- enc_key.len);
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AEAD,
+ sizeof(*algo) + enc_key.len);
+ if (!algo)
{
goto failed;
}
-
- algo = (struct xfrm_algo_aead*)RTA_DATA(rthdr);
algo->alg_key_len = enc_key.len * 8;
algo->alg_icv_len = icv_size;
strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
algo->alg_name[sizeof(algo->alg_name) - 1] = '\0';
memcpy(algo->alg_key, enc_key.ptr, enc_key.len);
-
- rthdr = XFRM_RTA_NEXT(rthdr);
break;
}
default:
@@ -1283,21 +1296,16 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
DBG2(DBG_KNL, " using encryption algorithm %N with key size %d",
encryption_algorithm_names, enc_alg, enc_key.len * 8);
- rthdr->rta_type = XFRMA_ALG_CRYPT;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + enc_key.len);
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_CRYPT,
+ sizeof(*algo) + enc_key.len);
+ if (!algo)
{
goto failed;
}
-
- algo = (struct xfrm_algo*)RTA_DATA(rthdr);
algo->alg_key_len = enc_key.len * 8;
strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
algo->alg_name[sizeof(algo->alg_name) - 1] = '\0';
memcpy(algo->alg_key, enc_key.ptr, enc_key.len);
-
- rthdr = XFRM_RTA_NEXT(rthdr);
}
}
@@ -1335,17 +1343,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
/* the kernel uses SHA256 with 96 bit truncation by default,
* use specified truncation size supported by newer kernels.
* also use this for untruncated MD5 and SHA1. */
- rthdr->rta_type = XFRMA_ALG_AUTH_TRUNC;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo_auth) +
- int_key.len);
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH_TRUNC,
+ sizeof(*algo) + int_key.len);
+ if (!algo)
{
goto failed;
}
-
- algo = (struct xfrm_algo_auth*)RTA_DATA(rthdr);
algo->alg_key_len = int_key.len * 8;
algo->alg_trunc_len = trunc_len;
strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
@@ -1356,27 +1359,23 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
{
struct xfrm_algo* algo;
- rthdr->rta_type = XFRMA_ALG_AUTH;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo) + int_key.len);
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_AUTH,
+ sizeof(*algo) + int_key.len);
+ if (!algo)
{
goto failed;
}
-
- algo = (struct xfrm_algo*)RTA_DATA(rthdr);
algo->alg_key_len = int_key.len * 8;
strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
algo->alg_name[sizeof(algo->alg_name) - 1] = '\0';
memcpy(algo->alg_key, int_key.ptr, int_key.len);
}
- rthdr = XFRM_RTA_NEXT(rthdr);
}
if (ipcomp != IPCOMP_NONE)
{
- rthdr->rta_type = XFRMA_ALG_COMP;
+ struct xfrm_algo* algo;
+
alg_name = lookup_algorithm(COMPRESSION_ALGORITHM, ipcomp);
if (alg_name == NULL)
{
@@ -1387,35 +1386,26 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
DBG2(DBG_KNL, " using compression algorithm %N",
ipcomp_transform_names, ipcomp);
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_algo));
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ algo = netlink_reserve(hdr, sizeof(request), XFRMA_ALG_COMP,
+ sizeof(*algo));
+ if (!algo)
{
goto failed;
}
-
- struct xfrm_algo* algo = (struct xfrm_algo*)RTA_DATA(rthdr);
algo->alg_key_len = 0;
strncpy(algo->alg_name, alg_name, sizeof(algo->alg_name));
algo->alg_name[sizeof(algo->alg_name) - 1] = '\0';
-
- rthdr = XFRM_RTA_NEXT(rthdr);
}
if (encap)
{
struct xfrm_encap_tmpl *tmpl;
- rthdr->rta_type = XFRMA_ENCAP;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl));
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl));
+ if (!tmpl)
{
goto failed;
}
-
- tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr);
tmpl->encap_type = UDP_ENCAP_ESPINUDP;
tmpl->encap_sport = htons(src->get_port(src));
tmpl->encap_dport = htons(dst->get_port(dst));
@@ -1430,44 +1420,24 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
* No. The reason the kernel ignores NAT-OA is that it recomputes
* (or, rather, just ignores) the checksum. If packets pass the IPsec
* checks it marks them "checksum ok" so OA isn't needed. */
- rthdr = XFRM_RTA_NEXT(rthdr);
}
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- goto failed;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
- rthdr = XFRM_RTA_NEXT(rthdr);
+ goto failed;
}
if (tfc)
{
u_int32_t *tfcpad;
- rthdr->rta_type = XFRMA_TFCPAD;
- rthdr->rta_len = RTA_LENGTH(sizeof(u_int32_t));
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ tfcpad = netlink_reserve(hdr, sizeof(request), XFRMA_TFCPAD,
+ sizeof(*tfcpad));
+ if (!tfcpad)
{
goto failed;
}
-
- tfcpad = (u_int32_t*)RTA_DATA(rthdr);
*tfcpad = tfc;
- rthdr = XFRM_RTA_NEXT(rthdr);
}
if (protocol != IPPROTO_COMP)
@@ -1478,24 +1448,18 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
* XFRMA_REPLAY_ESN_VAL attribute to configure a bitmap */
struct xfrm_replay_state_esn *replay;
- rthdr->rta_type = XFRMA_REPLAY_ESN_VAL;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) +
- (this->replay_window + 7) / 8);
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ replay = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL,
+ sizeof(*replay) + (this->replay_window + 7) / 8);
+ if (!replay)
{
goto failed;
}
-
- replay = (struct xfrm_replay_state_esn*)RTA_DATA(rthdr);
/* bmp_len contains number uf __u32's */
replay->bmp_len = this->replay_bmp;
replay->replay_window = this->replay_window;
DBG2(DBG_KNL, " using replay window of %u packets",
this->replay_window);
- rthdr = XFRM_RTA_NEXT(rthdr);
if (esn)
{
DBG2(DBG_KNL, " using extended sequence numbers (ESN)");
@@ -1567,22 +1531,9 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this,
aevent_id->sa_id.proto = protocol;
aevent_id->sa_id.family = dst->get_family(dst);
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_aevent_id);
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- return;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
+ return;
}
if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
@@ -1668,22 +1619,9 @@ METHOD(kernel_ipsec_t, query_sa, status_t,
sa_id->proto = protocol;
sa_id->family = dst->get_family(dst);
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id);
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- return FAILED;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
+ return FAILED;
}
if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
@@ -1771,22 +1709,9 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
sa_id->proto = protocol;
sa_id->family = dst->get_family(dst);
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id);
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- return FAILED;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
+ return FAILED;
}
switch (this->socket_xfrm->send_ack(this->socket_xfrm, hdr))
@@ -1818,7 +1743,6 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
bool old_encap, bool new_encap, mark_t mark)
{
netlink_buf_t request;
- u_char *pos;
struct nlmsghdr *hdr, *out = NULL;
struct xfrm_usersa_id *sa_id;
struct xfrm_usersa_info *out_sa = NULL, *sa;
@@ -1853,22 +1777,9 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
sa_id->proto = protocol;
sa_id->family = dst->get_family(dst);
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_usersa_id);
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- return FAILED;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
+ return FAILED;
}
if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
@@ -1919,11 +1830,11 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
ntohl(spi), src, dst, new_src, new_dst);
/* copy over the SA from out to request */
hdr = (struct nlmsghdr*)request;
- memcpy(hdr, out, min(out->nlmsg_len, sizeof(request)));
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = XFRM_MSG_NEWSA;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info));
sa = NLMSG_DATA(hdr);
+ memcpy(sa, NLMSG_DATA(out), sizeof(struct xfrm_usersa_info));
sa->family = new_dst->get_family(new_dst);
if (!src->ip_equals(src, new_src))
@@ -1937,75 +1848,60 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
rta = XFRM_RTA(out, struct xfrm_usersa_info);
rtasize = XFRM_PAYLOAD(out, struct xfrm_usersa_info);
- pos = (u_char*)XFRM_RTA(hdr, struct xfrm_usersa_info);
- while(RTA_OK(rta, rtasize))
+ while (RTA_OK(rta, rtasize))
{
/* copy all attributes, but not XFRMA_ENCAP if we are disabling it */
if (rta->rta_type != XFRMA_ENCAP || new_encap)
{
if (rta->rta_type == XFRMA_ENCAP)
{ /* update encap tmpl */
- tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta);
+ tmpl = RTA_DATA(rta);
tmpl->encap_sport = ntohs(new_src->get_port(new_src));
tmpl->encap_dport = ntohs(new_dst->get_port(new_dst));
}
- memcpy(pos, rta, rta->rta_len);
- pos += RTA_ALIGN(rta->rta_len);
- hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
+ netlink_add_attribute(hdr, rta->rta_type,
+ chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)),
+ sizeof(request));
}
rta = RTA_NEXT(rta, rtasize);
}
- rta = (struct rtattr*)pos;
if (tmpl == NULL && new_encap)
{ /* add tmpl if we are enabling it */
- rta->rta_type = XFRMA_ENCAP;
- rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl));
-
- hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl));
+ if (!tmpl)
{
goto failed;
}
-
- tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta);
tmpl->encap_type = UDP_ENCAP_ESPINUDP;
tmpl->encap_sport = ntohs(new_src->get_port(new_src));
tmpl->encap_dport = ntohs(new_dst->get_port(new_dst));
memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t));
-
- rta = XFRM_RTA_NEXT(rta);
}
if (replay_esn)
{
- rta->rta_type = XFRMA_REPLAY_ESN_VAL;
- rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) +
- this->replay_bmp);
+ struct xfrm_replay_state_esn *state;
- hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL,
+ sizeof(*state) + this->replay_bmp);
+ if (!state)
{
goto failed;
}
- memcpy(RTA_DATA(rta), replay_esn,
- sizeof(struct xfrm_replay_state_esn) + this->replay_bmp);
-
- rta = XFRM_RTA_NEXT(rta);
+ memcpy(state, replay_esn, sizeof(*state) + this->replay_bmp);
}
else if (replay)
{
- rta->rta_type = XFRMA_REPLAY_VAL;
- rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state));
+ struct xfrm_replay_state *state;
- hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
+ state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_VAL,
+ sizeof(*state));
+ if (!state)
{
goto failed;
}
- memcpy(RTA_DATA(rta), replay, sizeof(struct xfrm_replay_state));
-
- rta = XFRM_RTA_NEXT(rta);
+ memcpy(state, replay, sizeof(*state));
}
else
{
@@ -2102,11 +1998,9 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
policy_info->lft.soft_use_expires_seconds = 0;
policy_info->lft.hard_use_expires_seconds = 0;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_info);
-
if (mapping->type == POLICY_IPSEC)
{
- struct xfrm_user_tmpl *tmpl = (struct xfrm_user_tmpl*)RTA_DATA(rthdr);
+ struct xfrm_user_tmpl *tmpl;
struct {
u_int8_t proto;
bool use;
@@ -2116,25 +2010,29 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
{ IPPROTO_AH, ipsec->cfg.ah.use },
};
ipsec_mode_t proto_mode = ipsec->cfg.mode;
-
- rthdr->rta_type = XFRMA_TMPL;
- rthdr->rta_len = 0; /* actual length is set below */
+ int count = 0;
for (i = 0; i < countof(protos); i++)
{
- if (!protos[i].use)
+ if (protos[i].use)
{
- continue;
+ count++;
}
+ }
+ tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_TMPL,
+ count * sizeof(*tmpl));
+ if (!tmpl)
+ {
+ this->mutex->unlock(this->mutex);
+ return FAILED;
+ }
- rthdr->rta_len += RTA_LENGTH(sizeof(struct xfrm_user_tmpl));
- hdr->nlmsg_len += RTA_ALIGN(RTA_LENGTH(sizeof(struct xfrm_user_tmpl)));
- if (hdr->nlmsg_len > sizeof(request))
+ for (i = 0; i < countof(protos); i++)
+ {
+ if (!protos[i].use)
{
- this->mutex->unlock(this->mutex);
- return FAILED;
+ continue;
}
-
tmpl->reqid = ipsec->cfg.reqid;
tmpl->id.proto = protos[i].proto;
tmpl->aalgos = tmpl->ealgos = tmpl->calgos = ~0;
@@ -2154,27 +2052,12 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
/* use transport mode for other SAs */
proto_mode = MODE_TRANSPORT;
}
-
- rthdr = XFRM_RTA_NEXT(rthdr);
}
- if (ipsec->mark.value)
+ if (!add_mark(hdr, sizeof(request), ipsec->mark))
{
- struct xfrm_mark *mrk;
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- this->mutex->unlock(this->mutex);
- return FAILED;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = ipsec->mark.value;
- mrk->m = ipsec->mark.mask;
+ this->mutex->unlock(this->mutex);
+ return FAILED;
}
this->mutex->unlock(this->mutex);
@@ -2196,14 +2079,15 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
/* install a route, if:
* - this is a forward policy (to just get one for each child)
- * - we are in tunnel/BEET mode
+ * - we are in tunnel/BEET mode or install a bypass policy
* - routing is not disabled via strongswan.conf
*/
- if (policy->direction == POLICY_FWD &&
- ipsec->cfg.mode != MODE_TRANSPORT && this->install_routes)
+ if (policy->direction == POLICY_FWD && this->install_routes &&
+ (mapping->type != POLICY_IPSEC || ipsec->cfg.mode != MODE_TRANSPORT))
{
policy_sa_fwd_t *fwd = (policy_sa_fwd_t*)mapping;
route_entry_t *route;
+ host_t *iface;
INIT(route,
.prefixlen = policy->sel.prefixlen_s,
@@ -2219,9 +2103,17 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len);
+ /* get the interface to install the route for. If we have a local
+ * address, use it. Otherwise (for shunt policies) use the
+ * routes source address. */
+ iface = ipsec->dst;
+ if (iface->is_anyaddr(iface))
+ {
+ iface = route->src_ip;
+ }
/* install route via outgoing interface */
if (!hydra->kernel_interface->get_interface(hydra->kernel_interface,
- ipsec->dst, &route->if_name))
+ iface, &route->if_name))
{
this->mutex->unlock(this->mutex);
route_entry_destroy(route);
@@ -2392,23 +2284,9 @@ METHOD(kernel_ipsec_t, query_policy, status_t,
policy_id->sel = ts2selector(src_ts, dst_ts);
policy_id->dir = direction;
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_id);
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
-
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- return FAILED;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
+ return FAILED;
}
if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
@@ -2564,23 +2442,9 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
policy_id->sel = current->sel;
policy_id->dir = direction;
- if (mark.value)
+ if (!add_mark(hdr, sizeof(request), mark))
{
- struct xfrm_mark *mrk;
- struct rtattr *rthdr = XFRM_RTA(hdr, struct xfrm_userpolicy_id);
-
- rthdr->rta_type = XFRMA_MARK;
- rthdr->rta_len = RTA_LENGTH(sizeof(struct xfrm_mark));
- hdr->nlmsg_len += RTA_ALIGN(rthdr->rta_len);
- if (hdr->nlmsg_len > sizeof(request))
- {
- this->mutex->unlock(this->mutex);
- return FAILED;
- }
-
- mrk = (struct xfrm_mark*)RTA_DATA(rthdr);
- mrk->v = mark.value;
- mrk->m = mark.mask;
+ return FAILED;
}
if (current->route)
@@ -2734,6 +2598,7 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
INIT(this,
.public = {
.interface = {
+ .get_features = _get_features,
.get_spi = _get_spi,
.get_cpi = _get_cpi,
.add_sa = _add_sa,
@@ -2822,4 +2687,3 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
return &this->public;
}
-
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
index e47887859..3e0725a35 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
@@ -1757,6 +1757,10 @@ METHOD(kernel_net_t, add_ip, status_t,
DBG2(DBG_KNL, "virtual IP %H installed on %s", virtual_ip,
entry->iface->ifname);
this->lock->unlock(this->lock);
+ /* during IKEv1 reauthentication, children get moved from
+ * old the new SA before the virtual IP is available. This
+ * kills the route for our virtual IP, reinstall. */
+ queue_route_reinstall(this, strdup(entry->iface->ifname));
return SUCCESS;
}
}
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c
index 561e8529d..fd00c23af 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c
@@ -292,7 +292,7 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data,
{
struct rtattr *rta;
- if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(data.len) > buflen)
+ if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(data.len) > buflen)
{
DBG1(DBG_KNL, "unable to add attribute, buffer too small");
return;
@@ -304,3 +304,24 @@ void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data,
memcpy(RTA_DATA(rta), data.ptr, data.len);
hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len;
}
+
+/**
+ * Described in header.
+ */
+void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len)
+{
+ struct rtattr *rta;
+
+ if (NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(len) > buflen)
+ {
+ DBG1(DBG_KNL, "unable to add attribute, buffer too small");
+ return NULL;
+ }
+
+ rta = ((void*)hdr) + NLMSG_ALIGN(hdr->nlmsg_len);
+ rta->rta_type = type;
+ rta->rta_len = RTA_LENGTH(len);
+ hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + rta->rta_len;
+
+ return RTA_DATA(rta);
+}
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h
index dfd27a21a..8be935bc3 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.h
@@ -42,7 +42,8 @@ struct netlink_socket_t {
* @param out received netlink message
* @param out_len length of the received message
*/
- status_t (*send)(netlink_socket_t *this, struct nlmsghdr *in, struct nlmsghdr **out, size_t *out_len);
+ status_t (*send)(netlink_socket_t *this, struct nlmsghdr *in,
+ struct nlmsghdr **out, size_t *out_len);
/**
* Send a netlink message and wait for its acknowledge.
@@ -67,11 +68,23 @@ netlink_socket_t *netlink_socket_create(int protocol);
/**
* Creates an rtattr and adds it to the given netlink message.
*
- * @param hdr netlink message
- * @param rta_type type of the rtattr
- * @param data data to add to the rtattr
- * @param buflen length of the netlink message buffer
+ * @param hdr netlink message
+ * @param rta_type type of the rtattr
+ * @param data data to add to the rtattr
+ * @param buflen length of the netlink message buffer
*/
-void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data, size_t buflen);
+void netlink_add_attribute(struct nlmsghdr *hdr, int rta_type, chunk_t data,
+ size_t buflen);
+
+/**
+ * Reserve space in a netlink message for given size and type, returning buffer.
+ *
+ * @param hdr netlink message
+ * @param buflen size of full netlink buffer
+ * @param type RTA type
+ * @param len length of RTA data
+ * @return buffer to len bytes of attribute data, NULL on error
+ */
+void* netlink_reserve(struct nlmsghdr *hdr, int buflen, int type, int len);
#endif /* KERNEL_NETLINK_SHARED_H_ */
diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
index 71bdbbe2b..88b028447 100644
--- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
+++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
@@ -953,7 +953,8 @@ static traffic_selector_t* sadb_address2ts(struct sadb_address *address)
ts = traffic_selector_create_from_subnet(host,
address->sadb_address_prefixlen,
address->sadb_address_proto,
- host->get_port(host));
+ host->get_port(host),
+ host->get_port(host) ?: 65535);
return ts;
}
@@ -2654,4 +2655,3 @@ kernel_pfkey_ipsec_t *kernel_pfkey_ipsec_create()
return &this->public;
}
-