summaryrefslogtreecommitdiff
path: root/src/libhydra/plugins/kernel_netlink
diff options
context:
space:
mode:
authorYves-Alexis Perez <corsac@debian.org>2014-07-11 07:23:31 +0200
committerYves-Alexis Perez <corsac@debian.org>2014-07-11 07:23:31 +0200
commit81c63b0eed39432878f78727f60a1e7499645199 (patch)
tree82387d8fecd1c20788fd8bd784a9b0bde091fb6b /src/libhydra/plugins/kernel_netlink
parentc5ebfc7b9c16551fe825dc1d79c3f7e2f096f6c9 (diff)
downloadvyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.tar.gz
vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.zip
Imported Upstream version 5.2.0
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink')
-rw-r--r--src/libhydra/plugins/kernel_netlink/Makefile.am2
-rw-r--r--src/libhydra/plugins/kernel_netlink/Makefile.in8
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c88
-rw-r--r--src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c264
4 files changed, 264 insertions, 98 deletions
diff --git a/src/libhydra/plugins/kernel_netlink/Makefile.am b/src/libhydra/plugins/kernel_netlink/Makefile.am
index ad573523e..c91f9a9e4 100644
--- a/src/libhydra/plugins/kernel_netlink/Makefile.am
+++ b/src/libhydra/plugins/kernel_netlink/Makefile.am
@@ -6,7 +6,7 @@ AM_CPPFLAGS = \
-DROUTING_TABLE_PRIO=${routing_table_prio}
AM_CFLAGS = \
- -rdynamic
+ $(PLUGIN_CFLAGS)
if MONOLITHIC
noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la
diff --git a/src/libhydra/plugins/kernel_netlink/Makefile.in b/src/libhydra/plugins/kernel_netlink/Makefile.in
index 26cde7cbf..2a67bd595 100644
--- a/src/libhydra/plugins/kernel_netlink/Makefile.in
+++ b/src/libhydra/plugins/kernel_netlink/Makefile.in
@@ -1,4 +1,4 @@
-# Makefile.in generated by automake 1.13.3 from Makefile.am.
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2013 Free Software Foundation, Inc.
@@ -267,6 +267,7 @@ NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
+OPENSSL_LIB = @OPENSSL_LIB@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
@@ -285,6 +286,7 @@ PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PLUGIN_CFLAGS = @PLUGIN_CFLAGS@
PTHREADLIB = @PTHREADLIB@
PYTHON = @PYTHON@
PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
@@ -312,6 +314,7 @@ abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+aikgen_plugins = @aikgen_plugins@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
@@ -403,6 +406,7 @@ srcdir = @srcdir@
starter_plugins = @starter_plugins@
strongswan_conf = @strongswan_conf@
strongswan_options = @strongswan_options@
+swanctldir = @swanctldir@
sysconfdir = @sysconfdir@
systemdsystemunitdir = @systemdsystemunitdir@
t_plugins = @t_plugins@
@@ -421,7 +425,7 @@ AM_CPPFLAGS = \
-DROUTING_TABLE_PRIO=${routing_table_prio}
AM_CFLAGS = \
- -rdynamic
+ $(PLUGIN_CFLAGS)
@MONOLITHIC_TRUE@noinst_LTLIBRARIES = libstrongswan-kernel-netlink.la
@MONOLITHIC_FALSE@plugin_LTLIBRARIES = libstrongswan-kernel-netlink.la
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
index c864a92f4..d9b55cfa7 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -70,11 +70,8 @@
#define SOL_UDP IPPROTO_UDP
#endif
-/** Default priority of installed policies */
-#define PRIO_BASE 512
-
-/** Default replay window size, if not set using charon.replay_window */
-#define DEFAULT_REPLAY_WINDOW 32
+/** Base priority for installed policies */
+#define PRIO_BASE 384
/** Default lifetime of an acquire XFRM state (in seconds) */
#define DEFAULT_ACQUIRE_LIFETIME 165
@@ -180,7 +177,7 @@ static kernel_algorithm_t encryption_algs[] = {
{ENCR_3DES, "des3_ede" },
/* {ENCR_RC5, "***" }, */
/* {ENCR_IDEA, "***" }, */
- {ENCR_CAST, "cast128" },
+ {ENCR_CAST, "cast5" },
{ENCR_BLOWFISH, "blowfish" },
/* {ENCR_3IDEA, "***" }, */
/* {ENCR_DES_IV32, "***" }, */
@@ -316,16 +313,6 @@ struct private_kernel_netlink_ipsec_t {
* Whether to track the history of a policy
*/
bool policy_history;
-
- /**
- * Size of the replay window, in packets (= bits)
- */
- u_int32_t replay_window;
-
- /**
- * Size of the replay window bitmap, in number of __u32 blocks
- */
- u_int32_t replay_bmp;
};
typedef struct route_entry_t route_entry_t;
@@ -619,6 +606,9 @@ static inline u_int32_t get_priority(policy_entry_t *policy,
priority <<= 1;
/* fall-through */
case POLICY_PRIORITY_DEFAULT:
+ priority <<= 1;
+ /* fall-through */
+ case POLICY_PRIORITY_PASS:
break;
}
/* calculate priority based on selector size, small size = high prio */
@@ -631,14 +621,6 @@ static inline u_int32_t get_priority(policy_entry_t *policy,
}
/**
- * Return the length of the ESN bitmap
- */
-static inline size_t esn_bmp_len(private_kernel_netlink_ipsec_t *this)
-{
- return this->replay_bmp * sizeof(u_int32_t);
-}
-
-/**
* Convert the general ipsec mode to the one defined in xfrm.h
*/
static u_int8_t mode2kernel(ipsec_mode_t mode)
@@ -1194,8 +1176,9 @@ 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,
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 initiator, bool encap, bool esn, bool inbound,
+ u_int16_t int_alg, chunk_t int_key, ipsec_mode_t mode,
+ u_int16_t ipcomp, u_int16_t cpi, u_int32_t replay_window,
+ bool initiator, bool encap, bool esn, bool inbound,
traffic_selector_t* src_ts, traffic_selector_t* dst_ts)
{
netlink_buf_t request;
@@ -1213,8 +1196,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
lifetime_cfg_t lft = {{0,0,0},{0,0,0},{0,0,0}};
add_sa(this, src, dst, htonl(ntohs(cpi)), IPPROTO_COMP, reqid, mark,
tfc, &lft, ENCR_UNDEFINED, chunk_empty, AUTH_UNDEFINED,
- chunk_empty, mode, ipcomp, 0, initiator, FALSE, FALSE, inbound,
- src_ts, dst_ts);
+ chunk_empty, mode, ipcomp, 0, 0, initiator, FALSE, FALSE,
+ inbound, src_ts, dst_ts);
ipcomp = IPCOMP_NONE;
/* use transport mode ESP SA, IPComp uses tunnel mode */
mode = MODE_TRANSPORT;
@@ -1480,23 +1463,24 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
if (protocol != IPPROTO_COMP)
{
- if (esn || this->replay_window > DEFAULT_REPLAY_WINDOW)
+ if (replay_window != 0 && (esn || replay_window > 32))
{
/* for ESN or larger replay windows we need the new
* XFRMA_REPLAY_ESN_VAL attribute to configure a bitmap */
struct xfrm_replay_state_esn *replay;
+ u_int32_t bmp_size;
+ bmp_size = round_up(replay_window, sizeof(u_int32_t) * 8) / 8;
replay = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL,
- sizeof(*replay) + esn_bmp_len(this));
+ sizeof(*replay) + bmp_size);
if (!replay)
{
goto failed;
}
/* 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);
+ replay->bmp_len = bmp_size / sizeof(u_int32_t);
+ replay->replay_window = replay_window;
+ DBG2(DBG_KNL, " using replay window of %u packets", replay_window);
if (esn)
{
@@ -1506,9 +1490,8 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
}
else
{
- DBG2(DBG_KNL, " using replay window of %u packets",
- this->replay_window);
- sa->replay_window = this->replay_window;
+ DBG2(DBG_KNL, " using replay window of %u packets", replay_window);
+ sa->replay_window = replay_window;
}
}
@@ -1542,6 +1525,7 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this,
u_int32_t spi, u_int8_t protocol,
host_t *dst, mark_t mark,
struct xfrm_replay_state_esn **replay_esn,
+ u_int32_t *replay_esn_len,
struct xfrm_replay_state **replay)
{
netlink_buf_t request;
@@ -1618,9 +1602,10 @@ static void get_replay_state(private_kernel_netlink_ipsec_t *this,
break;
}
if (rta->rta_type == XFRMA_REPLAY_ESN_VAL &&
- RTA_PAYLOAD(rta) >= sizeof(**replay_esn) + esn_bmp_len(this))
+ RTA_PAYLOAD(rta) >= sizeof(**replay_esn))
{
*replay_esn = malloc(RTA_PAYLOAD(rta));
+ *replay_esn_len = RTA_PAYLOAD(rta);
memcpy(*replay_esn, RTA_DATA(rta), RTA_PAYLOAD(rta));
break;
}
@@ -1804,6 +1789,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
struct xfrm_encap_tmpl* tmpl = NULL;
struct xfrm_replay_state *replay = NULL;
struct xfrm_replay_state_esn *replay_esn = NULL;
+ u_int32_t replay_esn_len;
status_t status = FAILED;
/* if IPComp is used, we first update the IPComp SA */
@@ -1868,7 +1854,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
goto failed;
}
- get_replay_state(this, spi, protocol, dst, mark, &replay_esn, &replay);
+ get_replay_state(this, spi, protocol, dst, mark, &replay_esn, &replay_esn_len, &replay);
/* delete the old SA (without affecting the IPComp SA) */
if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS)
@@ -1936,12 +1922,12 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
struct xfrm_replay_state_esn *state;
state = netlink_reserve(hdr, sizeof(request), XFRMA_REPLAY_ESN_VAL,
- sizeof(*state) + esn_bmp_len(this));
+ replay_esn_len);
if (!state)
{
goto failed;
}
- memcpy(state, replay_esn, sizeof(*state) + esn_bmp_len(this));
+ memcpy(state, replay_esn, replay_esn_len);
}
else if (replay)
{
@@ -2149,9 +2135,20 @@ static status_t add_policy_internal(private_kernel_netlink_ipsec_t *this,
fwd->dst_ts, &route->src_ip, NULL) == SUCCESS)
{
/* get the nexthop to src (src as we are in POLICY_FWD) */
- route->gateway = hydra->kernel_interface->get_nexthop(
+ if (!ipsec->src->is_anyaddr(ipsec->src))
+ {
+ route->gateway = hydra->kernel_interface->get_nexthop(
hydra->kernel_interface, ipsec->src,
- ipsec->dst);
+ -1, ipsec->dst);
+ }
+ else
+ { /* for shunt policies */
+ iface = xfrm2host(policy->sel.family, &policy->sel.saddr, 0);
+ route->gateway = hydra->kernel_interface->get_nexthop(
+ hydra->kernel_interface, iface,
+ policy->sel.prefixlen_s, route->src_ip);
+ iface->destroy(iface);
+ }
route->dst_net = chunk_alloc(policy->sel.family == AF_INET ? 4 : 16);
memcpy(route->dst_net.ptr, &policy->sel.saddr, route->dst_net.len);
@@ -2686,13 +2683,8 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
.policy_history = TRUE,
.install_routes = lib->settings->get_bool(lib->settings,
"%s.install_routes", TRUE, lib->ns),
- .replay_window = lib->settings->get_int(lib->settings,
- "%s.replay_window", DEFAULT_REPLAY_WINDOW, lib->ns),
);
- this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) /
- (sizeof(u_int32_t) * 8);
-
if (streq(lib->ns, "starter"))
{ /* starter has no threads, so we do not register for kernel events */
register_for_events = FALSE;
diff --git a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
index 3cf317634..82b637d1e 100644
--- a/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
+++ b/src/libhydra/plugins/kernel_netlink/kernel_netlink_net.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2013 Tobias Brunner
+ * Copyright (C) 2008-2014 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -88,6 +88,9 @@ struct addr_entry_t {
/** the ip address */
host_t *ip;
+ /** address flags */
+ u_char flags;
+
/** scope of the address */
u_char scope;
@@ -467,6 +470,11 @@ struct private_kernel_netlink_net_t {
bool rta_prefsrc_for_ipv6;
/**
+ * whether to prefer temporary IPv6 addresses over public ones
+ */
+ bool prefer_temporary_addrs;
+
+ /**
* list with routing tables to be excluded from route lookup
*/
linked_list_t *rt_exclude;
@@ -653,20 +661,156 @@ static void addr_map_entry_remove(hashtable_t *map, addr_entry_t *addr,
}
/**
- * get the first non-virtual ip address on the given interface.
- * if a candidate address is given, we first search for that address and if not
+ * Determine the type or scope of the given unicast IP address. This is not
+ * the same thing returned in rtm_scope/ifa_scope.
+ *
+ * We use return values as defined in RFC 6724 (referring to RFC 4291).
+ */
+static u_char get_scope(host_t *ip)
+{
+ chunk_t addr;
+
+ addr = ip->get_address(ip);
+ switch (addr.len)
+ {
+ case 4:
+ /* we use the mapping defined in RFC 6724, 3.2 */
+ if (addr.ptr[0] == 127)
+ { /* link-local, same as the IPv6 loopback address */
+ return 2;
+ }
+ if (addr.ptr[0] == 169 && addr.ptr[1] == 254)
+ { /* link-local */
+ return 2;
+ }
+ break;
+ case 16:
+ if (IN6_IS_ADDR_LOOPBACK((struct in6_addr*)addr.ptr))
+ { /* link-local, according to RFC 4291, 2.5.3 */
+ return 2;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr*)addr.ptr))
+ {
+ return 2;
+ }
+ if (IN6_IS_ADDR_SITELOCAL((struct in6_addr*)addr.ptr))
+ { /* deprecated, according to RFC 4291, 2.5.7 */
+ return 5;
+ }
+ break;
+ default:
+ break;
+ }
+ /* global */
+ return 14;
+}
+
+/**
+ * Returns the length of the common prefix in bits up to the length of a's
+ * prefix, defined by RFC 6724 as the portion of the address not including the
+ * interface ID, which is 64-bit for most unicast addresses (see RFC 4291).
+ */
+static u_char common_prefix(host_t *a, host_t *b)
+{
+ chunk_t aa, ba;
+ u_char byte, bits = 0, match;
+
+ aa = a->get_address(a);
+ ba = b->get_address(b);
+ for (byte = 0; byte < 8; byte++)
+ {
+ if (aa.ptr[byte] != ba.ptr[byte])
+ {
+ match = aa.ptr[byte] ^ ba.ptr[byte];
+ for (bits = 8; match; match >>= 1)
+ {
+ bits--;
+ }
+ break;
+ }
+ }
+ return byte * 8 + bits;
+}
+
+/**
+ * Compare two IP addresses and return TRUE if the second address is the better
+ * choice of the two to reach the destination.
+ * For IPv6 we approximately follow RFC 6724.
+ */
+static bool is_address_better(private_kernel_netlink_net_t *this,
+ addr_entry_t *a, addr_entry_t *b, host_t *d)
+{
+ u_char sa, sb, sd, pa, pb;
+
+ /* rule 2: prefer appropriate scope */
+ if (d)
+ {
+ sa = get_scope(a->ip);
+ sb = get_scope(b->ip);
+ sd = get_scope(d);
+ if (sa < sb)
+ {
+ return sa < sd;
+ }
+ else if (sb < sa)
+ {
+ return sb >= sd;
+ }
+ }
+ if (a->ip->get_family(a->ip) == AF_INET)
+ { /* stop here for IPv4, default to addresses found earlier */
+ return FALSE;
+ }
+ /* rule 3: avoid deprecated addresses (RFC 4862) */
+ if ((a->flags & IFA_F_DEPRECATED) != (b->flags & IFA_F_DEPRECATED))
+ {
+ return a->flags & IFA_F_DEPRECATED;
+ }
+ /* rule 4 is not applicable as we don't know if an address is a home or
+ * care-of addresses.
+ * rule 5 does not apply as we only compare addresses from one interface
+ * rule 6 requires a policy table (optionally configurable) to match
+ * configurable labels
+ */
+ /* rule 7: prefer temporary addresses (WE REVERSE THIS BY DEFAULT!) */
+ if ((a->flags & IFA_F_TEMPORARY) != (b->flags & IFA_F_TEMPORARY))
+ {
+ if (this->prefer_temporary_addrs)
+ {
+ return b->flags & IFA_F_TEMPORARY;
+ }
+ return a->flags & IFA_F_TEMPORARY;
+ }
+ /* rule 8: use longest matching prefix */
+ if (d)
+ {
+ pa = common_prefix(a->ip, d);
+ pb = common_prefix(b->ip, d);
+ if (pa != pb)
+ {
+ return pb > pa;
+ }
+ }
+ /* default to addresses found earlier */
+ return FALSE;
+}
+
+/**
+ * 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.
+ * Returned host is a clone, has to be freed by caller.
*
- * this->lock must be held when calling this function
+ * 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 *candidate)
+ int ifindex, int family, host_t *dest,
+ host_t *candidate)
{
iface_entry_t *iface;
enumerator_t *addrs;
- addr_entry_t *addr;
- host_t *ip = NULL;
+ addr_entry_t *addr, *best = NULL;
if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index,
(void**)&iface, &ifindex) == SUCCESS)
@@ -676,29 +820,25 @@ static host_t *get_interface_address(private_kernel_netlink_net_t *this,
addrs = iface->addrs->create_enumerator(iface->addrs);
while (addrs->enumerate(addrs, &addr))
{
- if (addr->refcount)
- { /* ignore virtual IP addresses */
+ if (addr->refcount ||
+ addr->ip->get_family(addr->ip) != family)
+ { /* ignore virtual IP addresses and ensure family matches */
continue;
}
- if (addr->ip->get_family(addr->ip) == family)
+ if (candidate && candidate->ip_equals(candidate, addr->ip))
+ { /* stop if we find the candidate */
+ best = addr;
+ break;
+ }
+ else if (!best || is_address_better(this, best, addr, dest))
{
- if (!candidate || candidate->ip_equals(candidate, addr->ip))
- { /* stop at the first address if we don't search for a
- * candidate or if the candidate matches */
- ip = addr->ip;
- break;
- }
- else if (!ip)
- { /* store the first address as fallback if candidate is
- * not found */
- ip = addr->ip;
- }
+ best = addr;
}
}
addrs->destroy(addrs);
}
}
- return ip ? ip->clone(ip) : NULL;
+ return best ? best->ip->clone(best->ip) : NULL;
}
/**
@@ -989,6 +1129,7 @@ static void process_addr(private_kernel_netlink_net_t *this,
route_ifname = strdup(iface->ifname);
INIT(addr,
.ip = host->clone(host),
+ .flags = msg->ifa_flags,
.scope = msg->ifa_scope,
);
iface->addrs->insert_last(iface->addrs, addr);
@@ -1076,7 +1217,8 @@ static void process_route(private_kernel_netlink_net_t *this, struct nlmsghdr *h
}
if (!host && rta_oif)
{
- host = get_interface_address(this, rta_oif, msg->rtm_family, NULL);
+ host = get_interface_address(this, rta_oif, msg->rtm_family,
+ NULL, NULL);
}
if (!host || is_known_vip(this, host))
{ /* ignore routes added for virtual IPs */
@@ -1318,9 +1460,10 @@ static int get_interface_index(private_kernel_netlink_net_t *this, char* name)
}
/**
- * check if an address (chunk) addr is in subnet (net with net_len net bits)
+ * 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, chunk_t net, int net_len)
+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;
@@ -1329,7 +1472,7 @@ static bool addr_in_subnet(chunk_t addr, chunk_t net, int net_len)
{ /* any address matches a /0 network */
return TRUE;
}
- if (addr.len != net.len || net_len > 8 * net.len )
+ if (addr.len != net.len || net_len > 8 * net.len || prefix < net_len)
{
return FALSE;
}
@@ -1445,7 +1588,8 @@ static rt_entry_t *parse_route(struct nlmsghdr *hdr, rt_entry_t *route)
* Get a route: If "nexthop", the nexthop is returned. source addr otherwise.
*/
static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
- bool nexthop, host_t *candidate, u_int recursion)
+ int prefix, bool nexthop, host_t *candidate,
+ u_int recursion)
{
netlink_buf_t request;
struct nlmsghdr *hdr, *out, *current;
@@ -1456,18 +1600,25 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
rt_entry_t *route = NULL, *best = NULL;
enumerator_t *enumerator;
host_t *addr = NULL;
+ bool match_net;
+ int family;
if (recursion > MAX_ROUTE_RECURSION)
{
return NULL;
}
+ chunk = dest->get_address(dest);
+ len = chunk.len * 8;
+ prefix = prefix < 0 ? len : min(prefix, len);
+ match_net = prefix != len;
memset(&request, 0, sizeof(request));
+ family = dest->get_family(dest);
hdr = (struct nlmsghdr*)request;
hdr->nlmsg_flags = NLM_F_REQUEST;
- if (dest->get_family(dest) == AF_INET || this->rta_prefsrc_for_ipv6 ||
- this->routing_table)
+ 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 */
@@ -1477,19 +1628,22 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
msg = (struct rtmsg*)NLMSG_DATA(hdr);
- msg->rtm_family = dest->get_family(dest);
+ msg->rtm_family = family;
if (candidate)
{
chunk = candidate->get_address(candidate);
netlink_add_attribute(hdr, RTA_PREFSRC, chunk, sizeof(request));
}
- chunk = dest->get_address(dest);
- netlink_add_attribute(hdr, RTA_DST, chunk, sizeof(request));
+ if (!match_net)
+ {
+ chunk = dest->get_address(dest);
+ netlink_add_attribute(hdr, RTA_DST, chunk, sizeof(request));
+ }
if (this->socket->send(this->socket, hdr, &out, &len) != SUCCESS)
{
- DBG2(DBG_KNL, "getting %s to reach %H failed",
- nexthop ? "nexthop" : "address", dest);
+ DBG2(DBG_KNL, "getting %s to reach %H/%d failed",
+ nexthop ? "nexthop" : "address", dest, prefix);
return NULL;
}
routes = linked_list_create();
@@ -1524,7 +1678,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
{ /* interface is down */
continue;
}
- if (!addr_in_subnet(chunk, route->dst, route->dst_len))
+ if (!addr_in_subnet(chunk, prefix, route->dst, route->dst_len))
{ /* route destination does not contain dest */
continue;
}
@@ -1580,7 +1734,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
else if (route->oif)
{ /* no match yet, maybe it is assigned to the same interface */
host_t *src = get_interface_address(this, route->oif,
- msg->rtm_family, candidate);
+ msg->rtm_family, dest, candidate);
if (src && src->ip_equals(src, candidate))
{
route->src_host->destroy(route->src_host);
@@ -1599,7 +1753,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
if (route->oif)
{ /* no src, but an interface - get address from it */
route->src_host = get_interface_address(this, route->oif,
- msg->rtm_family, candidate);
+ msg->rtm_family, dest, candidate);
if (route->src_host)
{ /* we handle this address the same as the one above */
if (!candidate ||
@@ -1619,7 +1773,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
gtw = host_create_from_chunk(msg->rtm_family, route->gtw, 0);
if (gtw && !gtw->ip_equals(gtw, dest))
{
- route->src_host = get_route(this, gtw, FALSE, candidate,
+ route->src_host = get_route(this, gtw, -1, FALSE, candidate,
recursion + 1);
}
DESTROY_IF(gtw);
@@ -1643,7 +1797,10 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
{
addr = host_create_from_chunk(msg->rtm_family, best->gtw, 0);
}
- addr = addr ?: dest->clone(dest);
+ if (!addr && !match_net)
+ { /* fallback to destination address */
+ addr = dest->clone(dest);
+ }
}
else
{
@@ -1658,13 +1815,13 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
if (addr)
{
- DBG2(DBG_KNL, "using %H as %s to reach %H", addr,
- nexthop ? "nexthop" : "address", dest);
+ DBG2(DBG_KNL, "using %H as %s to reach %H/%d", addr,
+ nexthop ? "nexthop" : "address", dest, prefix);
}
else if (!recursion)
{
- DBG2(DBG_KNL, "no %s found to reach %H",
- nexthop ? "nexthop" : "address", dest);
+ DBG2(DBG_KNL, "no %s found to reach %H/%d",
+ nexthop ? "nexthop" : "address", dest, prefix);
}
return addr;
}
@@ -1672,13 +1829,13 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest,
METHOD(kernel_net_t, get_source_addr, host_t*,
private_kernel_netlink_net_t *this, host_t *dest, host_t *src)
{
- return get_route(this, dest, FALSE, src, 0);
+ return get_route(this, dest, -1, FALSE, src, 0);
}
METHOD(kernel_net_t, get_nexthop, host_t*,
- private_kernel_netlink_net_t *this, host_t *dest, host_t *src)
+ private_kernel_netlink_net_t *this, host_t *dest, int prefix, host_t *src)
{
- return get_route(this, dest, TRUE, src, 0);
+ return get_route(this, dest, prefix, TRUE, src, 0);
}
/**
@@ -1711,6 +1868,17 @@ static status_t manage_ipaddr(private_kernel_netlink_net_t *this, int nlmsg_type
netlink_add_attribute(hdr, IFA_LOCAL, chunk, sizeof(request));
+ if (ip->get_family(ip) == AF_INET6 && this->rta_prefsrc_for_ipv6)
+ { /* if source routes are possible we let the virtual IP get deprecated
+ * immediately (but mark it as valid forever) so it gets only used if
+ * forced by our route, and not by the default IPv6 address selection */
+ struct ifa_cacheinfo cache = {
+ .ifa_valid = 0xFFFFFFFF,
+ .ifa_prefered = 0,
+ };
+ netlink_add_attribute(hdr, IFA_CACHEINFO, chunk_from_thing(cache),
+ sizeof(request));
+ }
return this->socket->send_ack(this->socket, hdr);
}
@@ -2294,6 +2462,8 @@ kernel_netlink_net_t *kernel_netlink_net_create()
"%s.install_virtual_ip", TRUE, lib->ns),
.install_virtual_ip_on = lib->settings->get_str(lib->settings,
"%s.install_virtual_ip_on", NULL, lib->ns),
+ .prefer_temporary_addrs = lib->settings->get_bool(lib->settings,
+ "%s.prefer_temporary_addrs", FALSE, lib->ns),
.roam_events = lib->settings->get_bool(lib->settings,
"%s.plugins.kernel-netlink.roam_events", TRUE, lib->ns),
);