summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/kernel_netlink
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2016-10-20 16:18:38 +0200
committerYves-Alexis Perez <corsac@debian.org>2016-10-20 16:18:38 +0200
commit25663e04c3ab01ef8dc9f906608282319cfea2db (patch)
treea0ca5e70f66d74dbe552c996a4f3a285cdfc35e4 /src/libcharon/plugins/kernel_netlink
parentbf372706c469764d59e9f29c39e3ecbebd72b8d2 (diff)
downloadvyos-strongswan-25663e04c3ab01ef8dc9f906608282319cfea2db.tar.gz
vyos-strongswan-25663e04c3ab01ef8dc9f906608282319cfea2db.zip
New upstream version 5.5.1
Diffstat (limited to 'src/libcharon/plugins/kernel_netlink')
-rw-r--r--src/libcharon/plugins/kernel_netlink/Makefile.in5
-rw-r--r--src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c113
-rw-r--r--src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c190
3 files changed, 249 insertions, 59 deletions
diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.in b/src/libcharon/plugins/kernel_netlink/Makefile.in
index 2435dea92..b2df52a61 100644
--- a/src/libcharon/plugins/kernel_netlink/Makefile.in
+++ b/src/libcharon/plugins/kernel_netlink/Makefile.in
@@ -392,7 +392,6 @@ clearsilver_LIBS = @clearsilver_LIBS@
cmd_plugins = @cmd_plugins@
datadir = @datadir@
datarootdir = @datarootdir@
-dbusservicedir = @dbusservicedir@
dev_headers = @dev_headers@
docdir = @docdir@
dvidir = @dvidir@
@@ -426,8 +425,6 @@ libiptc_LIBS = @libiptc_LIBS@
linux_headers = @linux_headers@
localedir = @localedir@
localstatedir = @localstatedir@
-maemo_CFLAGS = @maemo_CFLAGS@
-maemo_LIBS = @maemo_LIBS@
manager_plugins = @manager_plugins@
mandir = @mandir@
medsrv_plugins = @medsrv_plugins@
@@ -481,6 +478,8 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
+tss2_CFLAGS = @tss2_CFLAGS@
+tss2_LIBS = @tss2_LIBS@
urandom_device = @urandom_device@
xml_CFLAGS = @xml_CFLAGS@
xml_LIBS = @xml_LIBS@
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index 9c2a7c315..f3846ec07 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -1245,7 +1245,7 @@ METHOD(kernel_ipsec_t, get_cpi, status_t,
*/
static void format_mark(char *buf, int buflen, mark_t mark)
{
- if (mark.value)
+ if (mark.value | mark.mask)
{
snprintf(buf, buflen, " (mark %u/0x%08x)", mark.value, mark.mask);
}
@@ -1256,7 +1256,7 @@ static void format_mark(char *buf, int buflen, mark_t mark)
*/
static bool add_mark(struct nlmsghdr *hdr, int buflen, mark_t mark)
{
- if (mark.value)
+ if (mark.value | mark.mask)
{
struct xfrm_mark *xmrk;
@@ -2528,6 +2528,7 @@ METHOD(kernel_ipsec_t, add_policy, status_t,
id->dir, markstr, cur_priority, use_count);
return SUCCESS;
}
+ policy->reqid = assigned_sa->sa->cfg.reqid;
if (this->policy_update)
{
@@ -2720,6 +2721,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t,
return SUCCESS;
}
current->used_by->get_first(current->used_by, (void**)&mapping);
+ current->reqid = mapping->sa->cfg.reqid;
DBG2(DBG_KNL, "updating policy %R === %R %N%s [priority %u, "
"refcount %d]", id->src_ts, id->dst_ts, policy_dir_names, id->dir,
@@ -3044,6 +3046,110 @@ METHOD(kernel_ipsec_t, destroy, void,
free(this);
}
+/**
+ * Get the currently configured SPD hashing thresholds for an address family
+ */
+static bool get_spd_hash_thresh(private_kernel_netlink_ipsec_t *this,
+ int type, uint8_t *lbits, uint8_t *rbits)
+{
+ netlink_buf_t request;
+ struct nlmsghdr *hdr, *out;
+ struct xfrmu_spdhthresh *thresh;
+ struct rtattr *rta;
+ size_t len, rtasize;
+ bool success = FALSE;
+
+ memset(&request, 0, sizeof(request));
+
+ hdr = &request.hdr;
+ hdr->nlmsg_flags = NLM_F_REQUEST;
+ hdr->nlmsg_type = XFRM_MSG_GETSPDINFO;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(uint32_t));
+
+ if (this->socket_xfrm->send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
+ {
+ hdr = out;
+ while (NLMSG_OK(hdr, len))
+ {
+ switch (hdr->nlmsg_type)
+ {
+ case XFRM_MSG_NEWSPDINFO:
+ {
+ rta = XFRM_RTA(hdr, uint32_t);
+ rtasize = XFRM_PAYLOAD(hdr, uint32_t);
+ while (RTA_OK(rta, rtasize))
+ {
+ if (rta->rta_type == type &&
+ RTA_PAYLOAD(rta) == sizeof(*thresh))
+ {
+ thresh = RTA_DATA(rta);
+ *lbits = thresh->lbits;
+ *rbits = thresh->rbits;
+ success = TRUE;
+ break;
+ }
+ rta = RTA_NEXT(rta, rtasize);
+ }
+ break;
+ }
+ case NLMSG_ERROR:
+ {
+ struct nlmsgerr *err = NLMSG_DATA(hdr);
+ DBG1(DBG_KNL, "getting SPD hash threshold failed: %s (%d)",
+ strerror(-err->error), -err->error);
+ break;
+ }
+ default:
+ hdr = NLMSG_NEXT(hdr, len);
+ continue;
+ case NLMSG_DONE:
+ break;
+ }
+ break;
+ }
+ free(out);
+ }
+ return success;
+}
+
+/**
+ * Configure SPD hashing threshold for an address family
+ */
+static void setup_spd_hash_thresh(private_kernel_netlink_ipsec_t *this,
+ char *key, int type, uint8_t def)
+{
+ struct xfrmu_spdhthresh *thresh;
+ struct nlmsghdr *hdr;
+ netlink_buf_t request;
+ uint8_t lbits, rbits;
+
+ if (!get_spd_hash_thresh(this, type, &lbits, &rbits))
+ {
+ return;
+ }
+ memset(&request, 0, sizeof(request));
+
+ hdr = &request.hdr;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ hdr->nlmsg_type = XFRM_MSG_NEWSPDINFO;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(uint32_t));
+
+ thresh = netlink_reserve(hdr, sizeof(request), type, sizeof(*thresh));
+ thresh->lbits = lib->settings->get_int(lib->settings,
+ "%s.plugins.kernel-netlink.spdh_thresh.%s.lbits",
+ def, lib->ns, key);
+ thresh->rbits = lib->settings->get_int(lib->settings,
+ "%s.plugins.kernel-netlink.spdh_thresh.%s.rbits",
+ def, lib->ns, key);
+ if (thresh->lbits != lbits || thresh->rbits != rbits)
+ {
+ if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "setting SPD hash threshold failed");
+ }
+ }
+}
+
/*
* Described in header.
*/
@@ -3114,6 +3220,9 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
return NULL;
}
+ setup_spd_hash_thresh(this, "ipv4", XFRMA_SPD_IPV4_HTHRESH, 32);
+ setup_spd_hash_thresh(this, "ipv6", XFRMA_SPD_IPV6_HTHRESH, 128);
+
if (register_for_events)
{
struct sockaddr_nl addr;
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
index 93c2ccccb..0132f7269 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c
@@ -702,6 +702,54 @@ static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr,
}
/**
+ * Check if an address or net (addr with prefix net bits) is in
+ * subnet (net with net_len net bits)
+ */
+static bool addr_in_subnet(chunk_t addr, int prefix, chunk_t net, int net_len)
+{
+ static const u_char mask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+ int byte = 0;
+
+ if (net_len == 0)
+ { /* any address matches a /0 network */
+ return TRUE;
+ }
+ if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len)
+ {
+ return FALSE;
+ }
+ /* scan through all bytes in network order */
+ while (net_len > 0)
+ {
+ if (net_len < 8)
+ {
+ return (mask[net_len] & addr.ptr[byte]) == (mask[net_len] & net.ptr[byte]);
+ }
+ else
+ {
+ if (addr.ptr[byte] != net.ptr[byte])
+ {
+ return FALSE;
+ }
+ byte++;
+ net_len -= 8;
+ }
+ }
+ return TRUE;
+}
+
+/**
+ * Check if the given address is in subnet (net with net_len net bits)
+ */
+static bool host_in_subnet(host_t *host, chunk_t net, int net_len)
+{
+ chunk_t addr;
+
+ addr = host->get_address(host);
+ return addr_in_subnet(addr, addr.len * 8, net, net_len);
+}
+
+/**
* Determine the type or scope of the given unicast IP address. This is not
* the same thing returned in rtm_scope/ifa_scope.
*
@@ -837,7 +885,8 @@ static bool is_address_better(private_kernel_netlink_net_t *this,
}
/**
- * Get a non-virtual IP address on the given interface.
+ * Get a non-virtual IP address on the given interfaces and optionally in a
+ * given subnet.
*
* If a candidate address is given, we first search for that address and if not
* found return the address as above.
@@ -845,19 +894,21 @@ static bool is_address_better(private_kernel_netlink_net_t *this,
*
* this->lock must be held when calling this function.
*/
-static host_t *get_interface_address(private_kernel_netlink_net_t *this,
- int ifindex, int family, host_t *dest,
- host_t *candidate)
+static host_t *get_matching_address(private_kernel_netlink_net_t *this,
+ int *ifindex, int family, chunk_t net,
+ uint8_t mask, host_t *dest,
+ host_t *candidate)
{
+ enumerator_t *ifaces, *addrs;
iface_entry_t *iface;
- enumerator_t *addrs;
addr_entry_t *addr, *best = NULL;
+ bool candidate_matched = FALSE;
- if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index,
- (void**)&iface, &ifindex) == SUCCESS)
+ ifaces = this->ifaces->create_enumerator(this->ifaces);
+ while (ifaces->enumerate(ifaces, &iface))
{
- if (iface->usable)
- { /* only use interfaces not excluded by config */
+ if (iface->usable && (!ifindex || iface->ifindex == *ifindex))
+ { /* only use matching interfaces not excluded by config */
addrs = iface->addrs->create_enumerator(iface->addrs);
while (addrs->enumerate(addrs, &addr))
{
@@ -866,9 +917,14 @@ static host_t *get_interface_address(private_kernel_netlink_net_t *this,
{ /* ignore virtual IP addresses and ensure family matches */
continue;
}
+ if (net.ptr && !host_in_subnet(addr->ip, net, mask))
+ { /* optionally match a subnet */
+ continue;
+ }
if (candidate && candidate->ip_equals(candidate, addr->ip))
{ /* stop if we find the candidate */
best = addr;
+ candidate_matched = TRUE;
break;
}
else if (!best || is_address_better(this, best, addr, dest))
@@ -877,12 +933,50 @@ static host_t *get_interface_address(private_kernel_netlink_net_t *this,
}
}
addrs->destroy(addrs);
+ if (ifindex || candidate_matched)
+ {
+ break;
+ }
}
}
+ ifaces->destroy(ifaces);
return best ? best->ip->clone(best->ip) : NULL;
}
/**
+ * Get a non-virtual IP address on the given interface.
+ *
+ * If a candidate address is given, we first search for that address and if not
+ * found return the address as above.
+ * Returned host is a clone, has to be freed by caller.
+ *
+ * this->lock must be held when calling this function.
+ */
+static host_t *get_interface_address(private_kernel_netlink_net_t *this,
+ int ifindex, int family, host_t *dest,
+ host_t *candidate)
+{
+ return get_matching_address(this, &ifindex, family, chunk_empty, 0, dest,
+ candidate);
+}
+
+/**
+ * Get a non-virtual IP address in the given subnet.
+ *
+ * If a candidate address is given, we first search for that address and if not
+ * found return the address as above.
+ * Returned host is a clone, has to be freed by caller.
+ *
+ * this->lock must be held when calling this function.
+ */
+static host_t *get_subnet_address(private_kernel_netlink_net_t *this,
+ int family, chunk_t net, uint8_t mask,
+ host_t *dest, host_t *candidate)
+{
+ return get_matching_address(this, NULL, family, net, mask, dest, candidate);
+}
+
+/**
* callback function that raises the delayed roam event
*/
static job_requeue_t roam_event(private_kernel_netlink_net_t *this)
@@ -1528,51 +1622,16 @@ static char *get_interface_name_by_index(private_kernel_netlink_net_t *this,
}
/**
- * check if an address or net (addr with prefix net bits) is in
- * subnet (net with net_len net bits)
- */
-static bool addr_in_subnet(chunk_t addr, int prefix, chunk_t net, int net_len)
-{
- static const u_char mask[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
- int byte = 0;
-
- if (net_len == 0)
- { /* any address matches a /0 network */
- return TRUE;
- }
- if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len)
- {
- return FALSE;
- }
- /* scan through all bytes in network order */
- while (net_len > 0)
- {
- if (net_len < 8)
- {
- return (mask[net_len] & addr.ptr[byte]) == (mask[net_len] & net.ptr[byte]);
- }
- else
- {
- if (addr.ptr[byte] != net.ptr[byte])
- {
- return FALSE;
- }
- byte++;
- net_len -= 8;
- }
- }
- return TRUE;
-}
-
-/**
* Store information about a route retrieved via RTNETLINK
*/
typedef struct {
chunk_t gtw;
- chunk_t src;
+ chunk_t pref_src;
chunk_t dst;
+ chunk_t src;
host_t *src_host;
uint8_t dst_len;
+ uint8_t src_len;
uint32_t table;
uint32_t oif;
uint32_t priority;
@@ -1626,9 +1685,11 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
if (route)
{
route->gtw = chunk_empty;
- route->src = chunk_empty;
+ route->pref_src = chunk_empty;
route->dst = chunk_empty;
route->dst_len = msg->rtm_dst_len;
+ route->src = chunk_empty;
+ route->src_len = msg->rtm_src_len;
route->table = msg->rtm_table;
route->oif = 0;
route->priority = 0;
@@ -1637,6 +1698,7 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
{
INIT(route,
.dst_len = msg->rtm_dst_len,
+ .src_len = msg->rtm_src_len,
.table = msg->rtm_table,
);
}
@@ -1646,7 +1708,7 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
switch (rta->rta_type)
{
case RTA_PREFSRC:
- route->src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta));
+ route->pref_src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta));
break;
case RTA_GATEWAY:
route->gtw = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta));
@@ -1654,6 +1716,9 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
case RTA_DST:
route->dst = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta));
break;
+ case RTA_SRC:
+ route->src = chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta));
+ break;
case RTA_OIF:
if (RTA_PAYLOAD(rta) == sizeof(route->oif))
{
@@ -1790,10 +1855,10 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
{ /* route destination does not contain dest */
continue;
}
- if (route->src.ptr)
+ if (route->pref_src.ptr)
{ /* verify source address, if any */
host_t *src = host_create_from_chunk(msg->rtm_family,
- route->src, 0);
+ route->pref_src, 0);
if (src && is_known_vip(this, src))
{ /* ignore routes installed by us */
src->destroy(src);
@@ -1863,12 +1928,29 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
best = best ?: route;
continue;
}
+ if (route->src.ptr)
+ { /* no src, but a source selector, try to find a matching address */
+ route->src_host = get_subnet_address(this, msg->rtm_family,
+ route->src, route->src_len, dest,
+ candidate);
+ if (route->src_host)
+ { /* we handle this address the same as the one above */
+ if (!candidate ||
+ candidate->ip_equals(candidate, route->src_host))
+ {
+ best = route;
+ break;
+ }
+ best = best ?: route;
+ continue;
+ }
+ }
if (route->oif)
{ /* no src, but an interface - get address from it */
route->src_host = get_interface_address(this, route->oif,
msg->rtm_family, dest, candidate);
if (route->src_host)
- { /* we handle this address the same as the one above */
+ { /* more of the same */
if (!candidate ||
candidate->ip_equals(candidate, route->src_host))
{
@@ -1913,9 +1995,9 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
if (best || routes->get_first(routes, (void**)&best) == SUCCESS)
{
addr = host_create_from_chunk(msg->rtm_family, best->gtw, 0);
- if (iface && route->oif)
+ if (iface && best->oif)
{
- *iface = get_interface_name_by_index(this, route->oif);
+ *iface = get_interface_name_by_index(this, best->oif);
}
}
if (!addr && !match_net)