diff options
author | Yves-Alexis Perez <corsac@corsac.net> | 2017-05-30 20:59:31 +0200 |
---|---|---|
committer | Yves-Alexis Perez <corsac@corsac.net> | 2017-05-30 20:59:31 +0200 |
commit | bba25e2ff6c4a193acb54560ea4417537bd2954e (patch) | |
tree | 9e074fe343f9ab6f5ce1e9c5142d9a6cf180fcda /src/libcharon/plugins/kernel_netlink | |
parent | 05ddd767992d68bb38c7f16ece142e8c2e9ae016 (diff) | |
download | vyos-strongswan-bba25e2ff6c4a193acb54560ea4417537bd2954e.tar.gz vyos-strongswan-bba25e2ff6c4a193acb54560ea4417537bd2954e.zip |
New upstream version 5.5.3
Diffstat (limited to 'src/libcharon/plugins/kernel_netlink')
5 files changed, 218 insertions, 115 deletions
diff --git a/src/libcharon/plugins/kernel_netlink/Makefile.in b/src/libcharon/plugins/kernel_netlink/Makefile.in index 26a7090b3..7f25c5202 100644 --- a/src/libcharon/plugins/kernel_netlink/Makefile.in +++ b/src/libcharon/plugins/kernel_netlink/Makefile.in @@ -397,6 +397,7 @@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ fips_mode = @fips_mode@ +fuzz_plugins = @fuzz_plugins@ gtk_CFLAGS = @gtk_CFLAGS@ gtk_LIBS = @gtk_LIBS@ host = @host@ @@ -419,6 +420,7 @@ json_CFLAGS = @json_CFLAGS@ json_LIBS = @json_LIBS@ libdir = @libdir@ libexecdir = @libexecdir@ +libfuzzer = @libfuzzer@ libiptc_CFLAGS = @libiptc_CFLAGS@ libiptc_LIBS = @libiptc_LIBS@ linux_headers = @linux_headers@ diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c index becf6b5dc..c411b829d 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c @@ -78,9 +78,6 @@ /** Base priority for installed policies */ #define PRIO_BASE 200000 -/** Default lifetime of an acquire XFRM state (in seconds) */ -#define DEFAULT_ACQUIRE_LIFETIME 165 - /** * Map the limit for bytes and packets to XFRM_INF by default */ @@ -545,10 +542,10 @@ static policy_sa_t *policy_sa_create(private_kernel_netlink_ipsec_t *this, /** * Destroy a policy_sa(_in)_t object */ -static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, +static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t dir, private_kernel_netlink_ipsec_t *this) { - if (*dir == POLICY_OUT) + if (dir == POLICY_OUT) { policy_sa_out_t *out = (policy_sa_out_t*)policy; out->src_ts->destroy(out->src_ts); @@ -558,6 +555,16 @@ static void policy_sa_destroy(policy_sa_t *policy, policy_dir_t *dir, free(policy); } +CALLBACK(policy_sa_destroy_cb, void, + policy_sa_t *policy, va_list args) +{ + private_kernel_netlink_ipsec_t *this; + policy_dir_t dir; + + VA_ARGS_VGET(args, dir, this); + policy_sa_destroy(policy, dir, this); +} + typedef struct policy_entry_t policy_entry_t; /** @@ -602,9 +609,8 @@ static void policy_entry_destroy(private_kernel_netlink_ipsec_t *this, } if (policy->used_by) { - policy->used_by->invoke_function(policy->used_by, - (linked_list_invoke_t)policy_sa_destroy, - &policy->direction, this); + policy->used_by->invoke_function(policy->used_by, policy_sa_destroy_cb, + policy->direction, this); policy->used_by->destroy(policy->used_by); } free(policy); @@ -1639,12 +1645,46 @@ METHOD(kernel_ipsec_t, add_sa, status_t, data->replay_window); sa->replay_window = data->replay_window; } + if (data->hw_offload) + { + host_t *local = data->inbound ? id->dst : id->src; + char *ifname; + + if (charon->kernel->get_interface(charon->kernel, local, &ifname)) + { + struct xfrm_user_offload *offload; + + offload = netlink_reserve(hdr, sizeof(request), + XFRMA_OFFLOAD_DEV, sizeof(*offload)); + if (!offload) + { + free(ifname); + goto failed; + } + offload->ifindex = if_nametoindex(ifname); + if (local->get_family(local) == AF_INET6) + { + offload->flags |= XFRM_OFFLOAD_IPV6; + } + offload->flags |= data->inbound ? XFRM_OFFLOAD_INBOUND : 0; + free(ifname); + } + } } - if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS) + status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); + if (status == NOT_FOUND && data->update) { - DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x%s", ntohl(id->spi), - markstr); + DBG1(DBG_KNL, "allocated SPI not found anymore, try to add SAD entry"); + hdr->nlmsg_type = XFRM_MSG_NEWSA; + status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr); + } + + if (status != SUCCESS) + { + DBG1(DBG_KNL, "unable to add SAD entry with SPI %.8x%s (%N)", ntohl(id->spi), + markstr, status_names, status); + status = FAILED; goto failed; } @@ -1919,13 +1959,13 @@ METHOD(kernel_ipsec_t, update_sa, status_t, kernel_ipsec_update_sa_t *data) { netlink_buf_t request; - struct nlmsghdr *hdr, *out = NULL; + struct nlmsghdr *hdr, *out_hdr = NULL, *out = NULL; struct xfrm_usersa_id *sa_id; - struct xfrm_usersa_info *out_sa = NULL, *sa; + struct xfrm_usersa_info *sa; size_t len; struct rtattr *rta; size_t rtasize; - struct xfrm_encap_tmpl* tmpl = NULL; + struct xfrm_encap_tmpl* encap = NULL; struct xfrm_replay_state *replay = NULL; struct xfrm_replay_state_esn *replay_esn = NULL; struct xfrm_lifetime_cur *lifetime = NULL; @@ -1983,7 +2023,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, { case XFRM_MSG_NEWSA: { - out_sa = NLMSG_DATA(hdr); + out_hdr = hdr; break; } case NLMSG_ERROR: @@ -2002,7 +2042,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, break; } } - if (out_sa == NULL) + if (!out_hdr) { DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x%s", ntohl(id->spi), markstr); @@ -2029,7 +2069,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t, hdr->nlmsg_type = XFRM_MSG_NEWSA; hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); sa = NLMSG_DATA(hdr); - memcpy(sa, NLMSG_DATA(out), sizeof(struct xfrm_usersa_info)); + memcpy(sa, NLMSG_DATA(out_hdr), sizeof(struct xfrm_usersa_info)); sa->family = data->new_dst->get_family(data->new_dst); if (!id->src->ip_equals(id->src, data->new_src)) @@ -2041,8 +2081,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t, host2xfrm(data->new_dst, &sa->id.daddr); } - rta = XFRM_RTA(out, struct xfrm_usersa_info); - rtasize = XFRM_PAYLOAD(out, struct xfrm_usersa_info); + rta = XFRM_RTA(out_hdr, struct xfrm_usersa_info); + rtasize = XFRM_PAYLOAD(out_hdr, struct xfrm_usersa_info); while (RTA_OK(rta, rtasize)) { /* copy all attributes, but not XFRMA_ENCAP if we are disabling it */ @@ -2050,9 +2090,34 @@ METHOD(kernel_ipsec_t, update_sa, status_t, { if (rta->rta_type == XFRMA_ENCAP) { /* update encap tmpl */ - tmpl = RTA_DATA(rta); - tmpl->encap_sport = ntohs(data->new_src->get_port(data->new_src)); - tmpl->encap_dport = ntohs(data->new_dst->get_port(data->new_dst)); + encap = RTA_DATA(rta); + encap->encap_sport = ntohs(data->new_src->get_port(data->new_src)); + encap->encap_dport = ntohs(data->new_dst->get_port(data->new_dst)); + } + if (rta->rta_type == XFRMA_OFFLOAD_DEV) + { /* update offload device */ + struct xfrm_user_offload *offload; + host_t *local; + char *ifname; + + offload = RTA_DATA(rta); + local = offload->flags & XFRM_OFFLOAD_INBOUND ? data->new_dst + : data->new_src; + + if (charon->kernel->get_interface(charon->kernel, local, + &ifname)) + { + offload->ifindex = if_nametoindex(ifname); + if (local->get_family(local) == AF_INET6) + { + offload->flags |= XFRM_OFFLOAD_IPV6; + } + else + { + offload->flags &= ~XFRM_OFFLOAD_IPV6; + } + free(ifname); + } } netlink_add_attribute(hdr, rta->rta_type, chunk_create(RTA_DATA(rta), RTA_PAYLOAD(rta)), @@ -2061,17 +2126,18 @@ METHOD(kernel_ipsec_t, update_sa, status_t, rta = RTA_NEXT(rta, rtasize); } - if (tmpl == NULL && data->new_encap) + if (encap == NULL && data->new_encap) { /* add tmpl if we are enabling it */ - tmpl = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, sizeof(*tmpl)); - if (!tmpl) + encap = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP, + sizeof(*encap)); + if (!encap) { goto failed; } - tmpl->encap_type = UDP_ENCAP_ESPINUDP; - tmpl->encap_sport = ntohs(data->new_src->get_port(data->new_src)); - tmpl->encap_dport = ntohs(data->new_dst->get_port(data->new_dst)); - memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); + encap->encap_type = UDP_ENCAP_ESPINUDP; + encap->encap_sport = ntohs(data->new_src->get_port(data->new_src)); + encap->encap_dport = ntohs(data->new_dst->get_port(data->new_dst)); + memset(&encap->encap_oa, 0, sizeof (xfrm_address_t)); } if (replay_esn) @@ -2711,7 +2777,7 @@ METHOD(kernel_ipsec_t, del_policy, status_t, ipsec_sa_equals(mapping->sa, &assigned_sa)) { current->used_by->remove_at(current->used_by, enumerator); - policy_sa_destroy(mapping, &id->dir, this); + policy_sa_destroy(mapping, id->dir, this); break; } if (is_installed) @@ -3171,7 +3237,6 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() { private_kernel_netlink_ipsec_t *this; bool register_for_events = TRUE; - FILE *f; INIT(this, .public = { @@ -3216,15 +3281,6 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create() register_for_events = FALSE; } - f = fopen("/proc/sys/net/core/xfrm_acq_expires", "w"); - if (f) - { - fprintf(f, "%u", lib->settings->get_int(lib->settings, - "%s.plugins.kernel-netlink.xfrm_acq_expires", - DEFAULT_ACQUIRE_LIFETIME, lib->ns)); - fclose(f); - } - this->socket_xfrm = netlink_socket_create(NETLINK_XFRM, xfrm_msg_names, lib->settings->get_bool(lib->settings, "%s.plugins.kernel-netlink.parallel_xfrm", FALSE, lib->ns)); diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c index 2dc76d941..0dd3e30cb 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_net.c @@ -163,19 +163,21 @@ static void iface_entry_destroy(iface_entry_t *this) free(this); } -/** - * find an interface entry by index - */ -static bool iface_entry_by_index(iface_entry_t *this, int *ifindex) +CALLBACK(iface_entry_by_index, bool, + iface_entry_t *this, va_list args) { - return this->ifindex == *ifindex; + int ifindex; + + VA_ARGS_VGET(args, ifindex); + return this->ifindex == ifindex; } -/** - * find an interface entry by name - */ -static bool iface_entry_by_name(iface_entry_t *this, char *ifname) +CALLBACK(iface_entry_by_name, bool, + iface_entry_t *this, va_list args) { + char *ifname; + + VA_ARGS_VGET(args, ifname); return streq(this->ifname, ifname); } @@ -1112,8 +1114,8 @@ static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this, { iface_entry_t *iface; - if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, - (void**)&iface, &index) == SUCCESS) + if (this->ifaces->find_first(this->ifaces, iface_entry_by_index, + (void**)&iface, index)) { return iface_entry_up_and_usable(iface); } @@ -1125,9 +1127,13 @@ static bool is_interface_up_and_usable(private_kernel_netlink_net_t *this, * * this->lock must be locked when calling this function */ -static void addr_entry_unregister(addr_entry_t *addr, iface_entry_t *iface, - private_kernel_netlink_net_t *this) +CALLBACK(addr_entry_unregister, void, + addr_entry_t *addr, va_list args) { + private_kernel_netlink_net_t *this; + iface_entry_t *iface; + + VA_ARGS_VGET(args, iface, this); if (addr->refcount) { addr_map_entry_remove(this->vips, addr, iface); @@ -1171,9 +1177,8 @@ static void process_link(private_kernel_netlink_net_t *this, { case RTM_NEWLINK: { - if (this->ifaces->find_first(this->ifaces, - (void*)iface_entry_by_index, (void**)&entry, - &msg->ifi_index) != SUCCESS) + if (!this->ifaces->find_first(this->ifaces, iface_entry_by_index, + (void**)&entry, msg->ifi_index)) { INIT(entry, .ifindex = msg->ifi_index, @@ -1217,7 +1222,7 @@ static void process_link(private_kernel_netlink_net_t *this, * another interface? */ this->ifaces->remove_at(this->ifaces, enumerator); current->addrs->invoke_function(current->addrs, - (void*)addr_entry_unregister, current, this); + addr_entry_unregister, current, this); iface_entry_destroy(current); break; } @@ -1288,8 +1293,8 @@ static void process_addr(private_kernel_netlink_net_t *this, } this->lock->write_lock(this->lock); - if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, - (void**)&iface, &msg->ifa_index) == SUCCESS) + if (this->ifaces->find_first(this->ifaces, iface_entry_by_index, + (void**)&iface, msg->ifa_index)) { addr_map_entry_t *entry, lookup = { .ip = host, @@ -1518,35 +1523,39 @@ typedef struct { kernel_address_type_t which; } address_enumerator_t; -/** - * cleanup function for address enumerator - */ -static void address_enumerator_destroy(address_enumerator_t *data) +CALLBACK(address_enumerator_destroy, void, + address_enumerator_t *data) { data->this->lock->unlock(data->this->lock); free(data); } -/** - * filter for addresses - */ -static bool filter_addresses(address_enumerator_t *data, - addr_entry_t** in, host_t** out) +CALLBACK(filter_addresses, bool, + address_enumerator_t *data, enumerator_t *orig, va_list args) { - if (!(data->which & ADDR_TYPE_VIRTUAL) && (*in)->refcount) - { /* skip virtual interfaces added by us */ - return FALSE; - } - if (!(data->which & ADDR_TYPE_REGULAR) && !(*in)->refcount) - { /* address is regular, but not requested */ - return FALSE; - } - if ((*in)->scope >= RT_SCOPE_LINK) - { /* skip addresses with a unusable scope */ - return FALSE; + addr_entry_t *addr; + host_t **out; + + VA_ARGS_VGET(args, out); + + while (orig->enumerate(orig, &addr)) + { + if (!(data->which & ADDR_TYPE_VIRTUAL) && addr->refcount) + { /* skip virtual interfaces added by us */ + continue; + } + if (!(data->which & ADDR_TYPE_REGULAR) && !addr->refcount) + { /* address is regular, but not requested */ + continue; + } + if (addr->scope >= RT_SCOPE_LINK) + { /* skip addresses with a unusable scope */ + continue; + } + *out = addr->ip; + return TRUE; } - *out = (*in)->ip; - return TRUE; + return FALSE; } /** @@ -1556,30 +1565,35 @@ static enumerator_t *create_iface_enumerator(iface_entry_t *iface, address_enumerator_t *data) { return enumerator_create_filter( - iface->addrs->create_enumerator(iface->addrs), - (void*)filter_addresses, data, NULL); + iface->addrs->create_enumerator(iface->addrs), + filter_addresses, data, NULL); } -/** - * filter for interfaces - */ -static bool filter_interfaces(address_enumerator_t *data, iface_entry_t** in, - iface_entry_t** out) +CALLBACK(filter_interfaces, bool, + address_enumerator_t *data, enumerator_t *orig, va_list args) { - if (!(data->which & ADDR_TYPE_IGNORED) && !(*in)->usable) - { /* skip interfaces excluded by config */ - return FALSE; - } - if (!(data->which & ADDR_TYPE_LOOPBACK) && ((*in)->flags & IFF_LOOPBACK)) - { /* ignore loopback devices */ - return FALSE; - } - if (!(data->which & ADDR_TYPE_DOWN) && !((*in)->flags & IFF_UP)) - { /* skip interfaces not up */ - return FALSE; + iface_entry_t *iface, **out; + + VA_ARGS_VGET(args, out); + + while (orig->enumerate(orig, &iface)) + { + if (!(data->which & ADDR_TYPE_IGNORED) && !iface->usable) + { /* skip interfaces excluded by config */ + continue; + } + if (!(data->which & ADDR_TYPE_LOOPBACK) && (iface->flags & IFF_LOOPBACK)) + { /* ignore loopback devices */ + continue; + } + if (!(data->which & ADDR_TYPE_DOWN) && !(iface->flags & IFF_UP)) + { /* skip interfaces not up */ + continue; + } + *out = iface; + return TRUE; } - *out = *in; - return TRUE; + return FALSE; } METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, @@ -1596,9 +1610,9 @@ METHOD(kernel_net_t, create_address_enumerator, enumerator_t*, return enumerator_create_nested( enumerator_create_filter( this->ifaces->create_enumerator(this->ifaces), - (void*)filter_interfaces, data, NULL), + filter_interfaces, data, NULL), (void*)create_iface_enumerator, data, - (void*)address_enumerator_destroy); + address_enumerator_destroy); } METHOD(kernel_net_t, get_interface_name, bool, @@ -1661,8 +1675,8 @@ static int get_interface_index(private_kernel_netlink_net_t *this, char* name) DBG2(DBG_KNL, "getting iface index for %s", name); this->lock->read_lock(this->lock); - if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, - (void**)&iface, name) == SUCCESS) + if (this->ifaces->find_first(this->ifaces, iface_entry_by_name, + (void**)&iface, name)) { ifindex = iface->ifindex; } @@ -1687,8 +1701,8 @@ static char *get_interface_name_by_index(private_kernel_netlink_net_t *this, DBG2(DBG_KNL, "getting iface name for index %d", index); this->lock->read_lock(this->lock); - if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_index, - (void**)&iface, &index) == SUCCESS) + if (this->ifaces->find_first(this->ifaces, iface_entry_by_index, + (void**)&iface, index)) { name = strdup(iface->ifname); } @@ -1928,7 +1942,7 @@ static host_t *get_route(private_kernel_netlink_net_t *this, host_t *dest, table = (uintptr_t)route->table; if (this->rt_exclude->find_first(this->rt_exclude, NULL, - (void**)&table) == SUCCESS) + (void**)&table)) { /* route is from an excluded routing table */ continue; } @@ -2165,8 +2179,14 @@ METHOD(enumerator_t, destroy_subnet_enumerator, void, } METHOD(enumerator_t, enumerate_subnets, bool, - subnet_enumerator_t *this, host_t **net, uint8_t *mask, char **ifname) + subnet_enumerator_t *this, va_list args) { + host_t **net; + uint8_t *mask; + char **ifname; + + VA_ARGS_VGET(args, net, mask, ifname); + if (!this->current) { this->current = this->msg; @@ -2270,7 +2290,8 @@ METHOD(kernel_net_t, create_local_subnet_enumerator, enumerator_t*, INIT(enumerator, .public = { - .enumerate = (void*)_enumerate_subnets, + .enumerate = enumerator_enumerate_default, + .venumerate = _enumerate_subnets, .destroy = _destroy_subnet_enumerator, }, .private = this, @@ -2380,11 +2401,11 @@ METHOD(kernel_net_t, add_ip, status_t, } /* try to find the target interface, either by config or via src ip */ if (!this->install_virtual_ip_on || - this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, - (void**)&iface, this->install_virtual_ip_on) != SUCCESS) + !this->ifaces->find_first(this->ifaces, iface_entry_by_name, + (void**)&iface, this->install_virtual_ip_on)) { - if (this->ifaces->find_first(this->ifaces, (void*)iface_entry_by_name, - (void**)&iface, iface_name) != SUCCESS) + if (!this->ifaces->find_first(this->ifaces, iface_entry_by_name, + (void**)&iface, iface_name)) { /* if we don't find the requested interface we just use the first */ this->ifaces->get_first(this->ifaces, (void**)&iface); } diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c index 8bafc3c55..58350028f 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_plugin.c @@ -19,6 +19,8 @@ #include "kernel_netlink_ipsec.h" #include "kernel_netlink_net.h" +#include <sa/task_manager.h> + typedef struct private_kernel_netlink_plugin_t private_kernel_netlink_plugin_t; /** @@ -50,6 +52,24 @@ METHOD(plugin_t, get_features, int, return countof(f); } +METHOD(plugin_t, reload, bool, + private_kernel_netlink_plugin_t *this) +{ + u_int timeout; + FILE *f; + + f = fopen("/proc/sys/net/core/xfrm_acq_expires", "w"); + if (f) + { + timeout = lib->settings->get_int(lib->settings, + "%s.plugins.kernel-netlink.xfrm_acq_expires", + task_manager_total_retransmit_timeout(), lib->ns); + fprintf(f, "%u", timeout); + fclose(f); + } + return TRUE; +} + METHOD(plugin_t, destroy, void, private_kernel_netlink_plugin_t *this) { @@ -76,10 +96,13 @@ plugin_t *kernel_netlink_plugin_create() .plugin = { .get_name = _get_name, .get_features = _get_features, + .reload = _reload, .destroy = _destroy, }, }, ); + reload(this); + return &this->public.plugin; } diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c index da54031a1..cf85cb0a6 100644 --- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c +++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_shared.c @@ -333,7 +333,8 @@ static status_t send_once(private_netlink_socket_t *this, struct nlmsghdr *in, while (!entry->complete) { if (this->parallel && - lib->watcher->get_state(lib->watcher) != WATCHER_STOPPED) + lib->watcher->get_state(lib->watcher) != WATCHER_STOPPED && + lib->processor->get_total_threads(lib->processor)) { if (this->timeout) { |