summaryrefslogtreecommitdiff
path: root/src/libhydra
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2015-10-22 11:43:58 +0200
committerYves-Alexis Perez <corsac@debian.org>2015-10-22 11:43:58 +0200
commit5dca9ea0e2931f0e2a056c7964d311bcc30a01b8 (patch)
tree037f1ec5bb860846938ddcf29771c24e9c529be0 /src/libhydra
parentb238cf34df3fe4476ae6b7012e7cb3e9769d4d51 (diff)
downloadvyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.tar.gz
vyos-strongswan-5dca9ea0e2931f0e2a056c7964d311bcc30a01b8.zip
Imported Upstream version 5.3.3
Diffstat (limited to 'src/libhydra')
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c53
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c40
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c16
-rw-r--r--src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c118
-rw-r--r--src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c17
-rw-r--r--src/libhydra/tests/hydra_tests.c4
6 files changed, 171 insertions, 77 deletions
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
index f22e07d95..605476ef1 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -199,6 +199,7 @@ static kernel_algorithm_t encryption_algs[] = {
/* {ENCR_CAMELLIA_CCM_ICV16, "***" }, */
{ENCR_SERPENT_CBC, "serpent" },
{ENCR_TWOFISH_CBC, "twofish" },
+ {ENCR_CHACHA20_POLY1305, "rfc7539esp(chacha20,poly1305)"},
};
/**
@@ -734,6 +735,7 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src,
traffic_selector_t *dst)
{
struct xfrm_selector sel;
+ u_int16_t port;
memset(&sel, 0, sizeof(sel));
sel.family = (src->get_type(src) == TS_IPV4_ADDR_RANGE) ? AF_INET : AF_INET6;
@@ -746,13 +748,13 @@ static struct xfrm_selector ts2selector(traffic_selector_t *src,
if ((sel.proto == IPPROTO_ICMP || sel.proto == IPPROTO_ICMPV6) &&
(sel.dport || sel.sport))
{
- /* the ICMP type is encoded in the most significant 8 bits and the ICMP
- * code in the least significant 8 bits of the port. via XFRM we have
- * to pass the ICMP type and code in the source and destination port
- * fields, respectively. the port is in network byte order. */
- u_int16_t port = max(sel.dport, sel.sport);
- sel.sport = htons(port & 0xff);
- sel.dport = htons(port >> 8);
+ /* the kernel expects the ICMP type and code in the source and
+ * destination port fields, respectively. */
+ port = ntohs(max(sel.dport, sel.sport));
+ sel.sport = htons(traffic_selector_icmp_type(port));
+ sel.sport_mask = sel.sport ? ~0 : 0;
+ sel.dport = htons(traffic_selector_icmp_code(port));
+ sel.dport_mask = sel.dport ? ~0 : 0;
}
sel.ifindex = 0;
sel.user = 0;
@@ -1291,6 +1293,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
case ENCR_AES_GCM_ICV16:
case ENCR_NULL_AUTH_AES_GMAC:
case ENCR_CAMELLIA_CCM_ICV16:
+ case ENCR_CHACHA20_POLY1305:
icv_size += 32;
/* FALL */
case ENCR_AES_CCM_ICV12:
@@ -2022,23 +2025,36 @@ METHOD(kernel_ipsec_t, flush_sas, status_t,
netlink_buf_t request;
struct nlmsghdr *hdr;
struct xfrm_usersa_flush *flush;
+ struct {
+ u_int8_t proto;
+ char *name;
+ } protos[] = {
+ { IPPROTO_AH, "AH" },
+ { IPPROTO_ESP, "ESP" },
+ { IPPROTO_COMP, "IPComp" },
+ };
+ int i;
memset(&request, 0, sizeof(request));
- DBG2(DBG_KNL, "flushing all SAD entries");
-
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
hdr->nlmsg_type = XFRM_MSG_FLUSHSA;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_flush));
flush = NLMSG_DATA(hdr);
- flush->proto = IPSEC_PROTO_ANY;
- if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
+ for (i = 0; i < countof(protos); i++)
{
- DBG1(DBG_KNL, "unable to flush SAD entries");
- return FAILED;
+ DBG2(DBG_KNL, "flushing all %s SAD entries", protos[i].name);
+
+ flush->proto = protos[i].proto;
+
+ if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "unable to flush %s SAD entries", protos[i].name);
+ return FAILED;
+ }
}
return SUCCESS;
}
@@ -2057,6 +2073,7 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
ipsec_sa_t *ipsec = mapping->sa;
struct xfrm_userpolicy_info *policy_info;
struct nlmsghdr *hdr;
+ status_t status;
int i;
/* clone the policy so we are able to check it out again later */
@@ -2151,7 +2168,14 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
}
this->mutex->unlock(this->mutex);
- if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
+ status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
+ if (status == ALREADY_DONE && !update)
+ {
+ DBG1(DBG_KNL, "policy already exists, try to update it");
+ hdr->nlmsg_type = XFRM_MSG_UPDPOLICY;
+ status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
+ }
+ if (status != SUCCESS)
{
return FAILED;
}
@@ -2560,6 +2584,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
if (!add_mark(hdr, sizeof(request), mark))
{
+ this->mutex->unlock(this->mutex);
return FAILED;
}
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
index 1515b01cc..4e5e02d07 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
@@ -491,6 +491,16 @@ struct private_kernel_netlink_net_t {
bool rta_prefsrc_for_ipv6;
/**
+ * whether marks can be used in route lookups
+ */
+ bool rta_mark;
+
+ /**
+ * the mark excluded from the routing rule used for virtual IPs
+ */
+ mark_t routing_mark;
+
+ /**
* whether to prefer temporary IPv6 addresses over public ones
*/
bool prefer_temporary_addrs;
@@ -1676,18 +1686,25 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
family = dest->get_family(dest);
hdr = &request.hdr;
hdr->nlmsg_flags = NLM_F_REQUEST;
- if (family == AF_INET || this->rta_prefsrc_for_ipv6 ||
- this->routing_table || match_net)
- { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes.
- * as we want to ignore routes with virtual IPs we cannot use DUMP
- * if these routes are not installed in a separate table */
- hdr->nlmsg_flags |= NLM_F_DUMP;
- }
hdr->nlmsg_type = RTM_GETROUTE;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
msg = NLMSG_DATA(hdr);
msg->rtm_family = family;
+ if (!match_net && this->rta_mark && this->routing_mark.value)
+ {
+ /* if our routing rule excludes packets with a certain mark we can
+ * get the preferred route without having to dump all routes */
+ chunk = chunk_from_thing(this->routing_mark.value);
+ netlink_add_attribute(hdr, RTA_MARK, chunk, sizeof(request));
+ }
+ else if (family == AF_INET || this->rta_prefsrc_for_ipv6 ||
+ this->routing_table || match_net)
+ { /* kernels prior to 3.0 do not support RTA_PREFSRC for IPv6 routes.
+ * as we want to ignore routes with virtual IPs we cannot use DUMP
+ * if these routes are not installed in a separate table */
+ hdr->nlmsg_flags |= NLM_F_DUMP;
+ }
if (candidate)
{
chunk = candidate->get_address(candidate);
@@ -2412,6 +2429,10 @@ static status_t manage_rule(private_kernel_netlink_net_t *this, int nlmsg_type,
netlink_add_attribute(hdr, FRA_FWMARK, chunk, sizeof(request));
chunk = chunk_from_thing(mark.mask);
netlink_add_attribute(hdr, FRA_FWMASK, chunk, sizeof(request));
+ if (msg->rtm_flags & FIB_RULE_INVERT)
+ {
+ this->routing_mark = mark;
+ }
}
#else
DBG1(DBG_KNL, "setting firewall mark on routing rule is not supported");
@@ -2435,6 +2456,10 @@ static void check_kernel_features(private_kernel_netlink_net_t *this)
case 3:
if (a == 2)
{
+ if (b == 6 && c >= 36)
+ {
+ this->rta_mark = TRUE;
+ }
DBG2(DBG_KNL, "detected Linux %d.%d.%d, no support for "
"RTA_PREFSRC for IPv6 routes", a, b, c);
break;
@@ -2443,6 +2468,7 @@ static void check_kernel_features(private_kernel_netlink_net_t *this)
case 2:
/* only 3.x+ uses two part version numbers */
this->rta_prefsrc_for_ipv6 = TRUE;
+ this->rta_mark = TRUE;
break;
default:
break;
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c
index b0e3103d3..f7ce992a3 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_shared.c
@@ -185,8 +185,8 @@ static ssize_t read_msg(private_netlink_socket_t *this,
return -1;
}
}
- len = recv(this->socket, buf, buflen, block ? 0 : MSG_DONTWAIT);
- if (len == buflen)
+ len = recv(this->socket, buf, buflen, MSG_TRUNC|(block ? 0 : MSG_DONTWAIT));
+ if (len > buflen)
{
DBG1(DBG_KNL, "netlink response exceeds buffer size");
return 0;
@@ -571,7 +571,7 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
.protocol = protocol,
.names = names,
.buflen = lib->settings->get_int(lib->settings,
- "%s.plugins.kernel-netlink.buflen", 4096, lib->ns),
+ "%s.plugins.kernel-netlink.buflen", 0, lib->ns),
.timeout = lib->settings->get_int(lib->settings,
"%s.plugins.kernel-netlink.timeout", 0, lib->ns),
.retries = lib->settings->get_int(lib->settings,
@@ -582,6 +582,16 @@ netlink_socket_t *netlink_socket_create(int protocol, enum_name_t *names,
.parallel = parallel,
);
+ if (!this->buflen)
+ {
+ long pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize == -1)
+ {
+ pagesize = 4096;
+ }
+ /* base this on NLMSG_GOODSIZE */
+ this->buflen = min(pagesize, 8192);
+ }
if (this->socket == -1)
{
DBG1(DBG_KNL, "unable to create netlink socket");
diff --git a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
index 3b32ba553..5027e1759 100644
--- a/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
+++ b/src/libhydra/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
@@ -106,6 +106,12 @@
#define SADB_X_EALG_CASTCBC SADB_X_EALG_CAST128CBC
#endif
+#if !defined(SADB_X_EALG_AES_GCM_ICV8) && defined(SADB_X_EALG_AESGCM8)
+#define SADB_X_EALG_AES_GCM_ICV8 SADB_X_EALG_AESGCM8
+#define SADB_X_EALG_AES_GCM_ICV12 SADB_X_EALG_AESGCM12
+#define SADB_X_EALG_AES_GCM_ICV16 SADB_X_EALG_AESGCM16
+#endif
+
#ifndef SOL_IP
#define SOL_IP IPPROTO_IP
#define SOL_IPV6 IPPROTO_IPV6
@@ -508,15 +514,30 @@ static policy_entry_t *create_policy_entry(traffic_selector_t *src_ts,
INIT(policy,
.direction = dir,
);
+ u_int16_t port;
+ u_int8_t proto;
src_ts->to_subnet(src_ts, &policy->src.net, &policy->src.mask);
dst_ts->to_subnet(dst_ts, &policy->dst.net, &policy->dst.mask);
/* src or dest proto may be "any" (0), use more restrictive one */
- policy->src.proto = max(src_ts->get_protocol(src_ts),
- dst_ts->get_protocol(dst_ts));
- policy->src.proto = policy->src.proto ? policy->src.proto : IPSEC_PROTO_ANY;
- policy->dst.proto = policy->src.proto;
+ proto = max(src_ts->get_protocol(src_ts), dst_ts->get_protocol(dst_ts));
+ /* map the ports to ICMP type/code how the Linux kernel expects them, that
+ * is, type in src, code in dst */
+ if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)
+ {
+ port = max(policy->src.net->get_port(policy->src.net),
+ policy->dst.net->get_port(policy->dst.net));
+ policy->src.net->set_port(policy->src.net,
+ traffic_selector_icmp_type(port));
+ policy->dst.net->set_port(policy->dst.net,
+ traffic_selector_icmp_code(port));
+ }
+ else if (!proto)
+ {
+ proto = IPSEC_PROTO_ANY;
+ }
+ policy->src.proto = policy->dst.proto = proto;
return policy;
}
@@ -826,9 +847,11 @@ static kernel_algorithm_t encryption_algs[] = {
/* {ENCR_AES_CCM_ICV8, SADB_X_EALG_AES_CCM_ICV8 }, */
/* {ENCR_AES_CCM_ICV12, SADB_X_EALG_AES_CCM_ICV12 }, */
/* {ENCR_AES_CCM_ICV16, SADB_X_EALG_AES_CCM_ICV16 }, */
-/* {ENCR_AES_GCM_ICV8, SADB_X_EALG_AES_GCM_ICV8 }, */
-/* {ENCR_AES_GCM_ICV12, SADB_X_EALG_AES_GCM_ICV12 }, */
-/* {ENCR_AES_GCM_ICV16, SADB_X_EALG_AES_GCM_ICV16 }, */
+#ifdef SADB_X_EALG_AES_GCM_ICV8 /* assume the others are defined too */
+ {ENCR_AES_GCM_ICV8, SADB_X_EALG_AES_GCM_ICV8 },
+ {ENCR_AES_GCM_ICV12, SADB_X_EALG_AES_GCM_ICV12 },
+ {ENCR_AES_GCM_ICV16, SADB_X_EALG_AES_GCM_ICV16 },
+#endif
{END_OF_LIST, 0 },
};
@@ -942,28 +965,6 @@ static size_t hostcpy(void *dest, host_t *host, bool include_port)
}
/**
- * Copy a host_t as sockaddr_t to the given memory location and map the port to
- * ICMP/ICMPv6 message type/code as the Linux kernel expects it, that is, the
- * type in the source and the code in the destination address.
- * @return the number of bytes copied
- */
-static size_t hostcpy_icmp(void *dest, host_t *host, u_int16_t type)
-{
- size_t len;
-
- len = hostcpy(dest, host, TRUE);
- if (type == SADB_EXT_ADDRESS_SRC)
- {
- set_port(dest, traffic_selector_icmp_type(host->get_port(host)));
- }
- else
- {
- set_port(dest, traffic_selector_icmp_code(host->get_port(host)));
- }
- return len;
-}
-
-/**
* add a host to the given sadb_msg
*/
static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type,
@@ -975,14 +976,7 @@ static void add_addr_ext(struct sadb_msg *msg, host_t *host, u_int16_t type,
addr->sadb_address_exttype = type;
addr->sadb_address_proto = proto;
addr->sadb_address_prefixlen = prefixlen;
- if (proto == IPPROTO_ICMP || proto == IPPROTO_ICMPV6)
- {
- len = hostcpy_icmp(addr + 1, host, type);
- }
- else
- {
- len = hostcpy(addr + 1, host, include_port);
- }
+ len = hostcpy(addr + 1, host, include_port);
addr->sadb_address_len = PFKEY_LEN(sizeof(*addr) + len);
PFKEY_EXT_ADD(msg, addr);
}
@@ -2078,31 +2072,44 @@ METHOD(kernel_ipsec_t, flush_sas, status_t,
{
unsigned char request[PFKEY_BUFFER_SIZE];
struct sadb_msg *msg, *out;
+ struct {
+ u_int8_t proto;
+ char *name;
+ } protos[] = {
+ { SADB_SATYPE_AH, "AH" },
+ { SADB_SATYPE_ESP, "ESP" },
+ { SADB_X_SATYPE_IPCOMP, "IPComp" },
+ };
size_t len;
+ int i;
memset(&request, 0, sizeof(request));
- DBG2(DBG_KNL, "flushing all SAD entries");
-
msg = (struct sadb_msg*)request;
msg->sadb_msg_version = PF_KEY_V2;
msg->sadb_msg_type = SADB_FLUSH;
- msg->sadb_msg_satype = SADB_SATYPE_UNSPEC;
msg->sadb_msg_len = PFKEY_LEN(sizeof(struct sadb_msg));
- if (pfkey_send(this, msg, &out, &len) != SUCCESS)
- {
- DBG1(DBG_KNL, "unable to flush SAD entries");
- return FAILED;
- }
- else if (out->sadb_msg_errno)
+ for (i = 0; i < countof(protos); i++)
{
- DBG1(DBG_KNL, "unable to flush SAD entries: %s (%d)",
- strerror(out->sadb_msg_errno), out->sadb_msg_errno);
+ DBG2(DBG_KNL, "flushing all %s SAD entries", protos[i].name);
+
+ msg->sadb_msg_satype = protos[i].proto;
+ if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "unable to flush %s SAD entries", protos[i].name);
+ return FAILED;
+ }
+ else if (out->sadb_msg_errno)
+ {
+ DBG1(DBG_KNL, "unable to flush %s SAD entries: %s (%d)",
+ protos[i].name, strerror(out->sadb_msg_errno),
+ out->sadb_msg_errno);
+ free(out);
+ return FAILED;
+ }
free(out);
- return FAILED;
}
- free(out);
return SUCCESS;
}
@@ -2357,6 +2364,7 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
pfkey_msg_t response;
size_t len;
ipsec_mode_t proto_mode;
+ status_t status;
memset(&request, 0, sizeof(request));
@@ -2444,7 +2452,15 @@ static status_t add_policy_internal(private_kernel_pfkey_ipsec_t *this,
this->mutex->unlock(this->mutex);
- if (pfkey_send(this, msg, &out, &len) != SUCCESS)
+ status = pfkey_send(this, msg, &out, &len);
+ if (status == SUCCESS && !update && out->sadb_msg_errno == EEXIST)
+ {
+ DBG1(DBG_KNL, "policy already exists, try to update it");
+ free(out);
+ msg->sadb_msg_type = SADB_X_SPDUPDATE;
+ status = pfkey_send(this, msg, &out, &len);
+ }
+ if (status != SUCCESS)
{
return FAILED;
}
diff --git a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
index 0f7802270..df80c29b8 100644
--- a/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
+++ b/src/libhydra/plugins/kernel_pfroute/kernel_pfroute_net.c
@@ -408,6 +408,11 @@ struct private_kernel_pfroute_net_t
* Time in ms to wait for IP addresses to appear/disappear
*/
int vip_wait;
+
+ /**
+ * whether to actually install virtual IPs
+ */
+ bool install_virtual_ip;
};
@@ -1197,6 +1202,11 @@ METHOD(kernel_net_t, add_ip, status_t,
tun_device_t *tun;
bool timeout = FALSE;
+ if (!this->install_virtual_ip)
+ { /* disabled by config */
+ return SUCCESS;
+ }
+
tun = tun_device_create(NULL);
if (!tun)
{
@@ -1271,6 +1281,11 @@ METHOD(kernel_net_t, del_ip, status_t,
host_t *addr;
bool timeout = FALSE, found = FALSE;
+ if (!this->install_virtual_ip)
+ { /* disabled by config */
+ return SUCCESS;
+ }
+
this->lock->write_lock(this->lock);
enumerator = this->tuns->create_enumerator(this->tuns);
while (enumerator->enumerate(enumerator, &tun))
@@ -1848,6 +1863,8 @@ kernel_pfroute_net_t *kernel_pfroute_net_create()
.roam_lock = spinlock_create(),
.vip_wait = lib->settings->get_int(lib->settings,
"%s.plugins.kernel-pfroute.vip_wait", 1000, lib->ns),
+ .install_virtual_ip = lib->settings->get_bool(lib->settings,
+ "%s.install_virtual_ip", TRUE, lib->ns),
);
timerclear(&this->last_route_reinstall);
timerclear(&this->next_roam);
diff --git a/src/libhydra/tests/hydra_tests.c b/src/libhydra/tests/hydra_tests.c
index 90abd8369..0d6387be7 100644
--- a/src/libhydra/tests/hydra_tests.c
+++ b/src/libhydra/tests/hydra_tests.c
@@ -26,8 +26,8 @@
static test_configuration_t tests[] = {
#define TEST_SUITE(x) \
{ .suite = x, },
-#define TEST_SUITE_DEPEND(x, type, args) \
- { .suite = x, .feature = PLUGIN_DEPENDS(type, args) },
+#define TEST_SUITE_DEPEND(x, type, ...) \
+ { .suite = x, .feature = PLUGIN_DEPENDS(type, __VA_ARGS__) },
#include "hydra_tests.h"
{ .suite = NULL, }
};