diff options
| author | Yves-Alexis Perez <corsac@debian.org> | 2016-10-20 16:18:38 +0200 |
|---|---|---|
| committer | Yves-Alexis Perez <corsac@debian.org> | 2016-10-20 16:18:38 +0200 |
| commit | 25663e04c3ab01ef8dc9f906608282319cfea2db (patch) | |
| tree | a0ca5e70f66d74dbe552c996a4f3a285cdfc35e4 /src/libcharon/plugins/kernel_netlink | |
| parent | bf372706c469764d59e9f29c39e3ecbebd72b8d2 (diff) | |
| download | vyos-strongswan-25663e04c3ab01ef8dc9f906608282319cfea2db.tar.gz vyos-strongswan-25663e04c3ab01ef8dc9f906608282319cfea2db.zip | |
New upstream version 5.5.1
Diffstat (limited to 'src/libcharon/plugins/kernel_netlink')
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) |
