diff options
author | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@debian.org> | 2014-07-11 07:23:31 +0200 |
commit | 81c63b0eed39432878f78727f60a1e7499645199 (patch) | |
tree | 82387d8fecd1c20788fd8bd784a9b0bde091fb6b /src/libhydra/plugins/kernel_netlink | |
parent | c5ebfc7b9c16551fe825dc1d79c3f7e2f096f6c9 (diff) | |
download | vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.tar.gz vyos-strongswan-81c63b0eed39432878f78727f60a1e7499645199.zip |
Imported Upstream version 5.2.0
Diffstat (limited to 'src/libhydra/plugins/kernel_netlink')
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), ); |