diff --git a/libknet/compress.c b/libknet/compress.c index 536b0439..c7c0d0e6 100644 --- a/libknet/compress.c +++ b/libknet/compress.c @@ -1,481 +1,481 @@ /* * Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include "internals.h" #include "compress.h" #include "compress_model.h" #include "logging.h" #include "threads_common.h" #include "common.h" /* * internal module switch data */ /* * DO NOT CHANGE MODEL_ID HERE OR ONWIRE COMPATIBILITY * WILL BREAK! * * Always add new items before the last NULL. */ -compress_model_t compress_modules_cmds[] = { +static compress_model_t compress_modules_cmds[] = { { "none" , 0, 0, 0, NULL }, { "zlib" , 1, WITH_COMPRESS_ZLIB , 0, NULL }, { "lz4" , 2, WITH_COMPRESS_LZ4 , 0, NULL }, { "lz4hc", 3, WITH_COMPRESS_LZ4 , 0, NULL }, { "lzo2" , 4, WITH_COMPRESS_LZO2 , 0, NULL }, { "lzma" , 5, WITH_COMPRESS_LZMA , 0, NULL }, { "bzip2", 6, WITH_COMPRESS_BZIP2, 0, NULL }, { NULL, 255, 0, 0, NULL } }; static int max_model = 0; static struct timespec last_load_failure; static int compress_get_model(const char *model) { int idx = 0; while (compress_modules_cmds[idx].model_name != NULL) { if (!strcmp(compress_modules_cmds[idx].model_name, model)) { return compress_modules_cmds[idx].model_id; } idx++; } return -1; } static int compress_get_max_model(void) { int idx = 0; while (compress_modules_cmds[idx].model_name != NULL) { idx++; } return idx - 1; } static int compress_is_valid_model(int compress_model) { int idx = 0; while (compress_modules_cmds[idx].model_name != NULL) { if ((compress_model == compress_modules_cmds[idx].model_id) && (compress_modules_cmds[idx].built_in == 1)) { return 0; } idx++; } return -1; } static int val_level( knet_handle_t knet_h, int compress_model, int compress_level) { if (compress_modules_cmds[compress_model].ops->val_level != NULL) { return compress_modules_cmds[compress_model].ops->val_level(knet_h, compress_level); } return 0; } /* * compress_check_lib_is_init needs to be invoked in a locked context! */ static int compress_check_lib_is_init(knet_handle_t knet_h, int cmp_model) { /* * lack of a .is_init function means that the module does not require * init per handle so we use a fake reference in the compress_int_data * to identify that we already increased the libref for this handle */ if (compress_modules_cmds[cmp_model].loaded == 1) { if (compress_modules_cmds[cmp_model].ops->is_init == NULL) { if (knet_h->compress_int_data[cmp_model] != NULL) { return 1; } } else { if (compress_modules_cmds[cmp_model].ops->is_init(knet_h, cmp_model) == 1) { return 1; } } } return 0; } /* * compress_load_lib should _always_ be invoked in write lock context */ static int compress_load_lib(knet_handle_t knet_h, int cmp_model, int rate_limit) { struct timespec clock_now; unsigned long long timediff; /* * checking again for paranoia and because * compress_check_lib_is_init is usually invoked in read context * and we need to switch from read to write locking in between. * another thread might have init the library in the meantime */ if (compress_check_lib_is_init(knet_h, cmp_model)) { return 0; } /* * due to the fact that decompress can load libraries * on demand, depending on the compress model selected * on other nodes, it is possible for an attacker * to send crafted packets to attempt to load libraries * at random in a DoS fashion. * If there is an error loading a library, then we want * to rate_limit a retry to reload the library every X * seconds to avoid a lock DoS that could greatly slow * down libknet. */ if (rate_limit) { if ((last_load_failure.tv_sec != 0) || (last_load_failure.tv_nsec != 0)) { clock_gettime(CLOCK_MONOTONIC, &clock_now); timespec_diff(last_load_failure, clock_now, &timediff); if (timediff < 10000000000) { errno = EAGAIN; return -1; } } } if (compress_modules_cmds[cmp_model].loaded == 0) { compress_modules_cmds[cmp_model].ops = load_module (knet_h, "compress", compress_modules_cmds[cmp_model].model_name); if (!compress_modules_cmds[cmp_model].ops) { clock_gettime(CLOCK_MONOTONIC, &last_load_failure); return -1; } if (compress_modules_cmds[cmp_model].ops->abi_ver != KNET_COMPRESS_MODEL_ABI) { log_err(knet_h, KNET_SUB_COMPRESS, "ABI mismatch loading module %s. knet ver: %d, module ver: %d", compress_modules_cmds[cmp_model].model_name, KNET_COMPRESS_MODEL_ABI, compress_modules_cmds[cmp_model].ops->abi_ver); errno = EINVAL; return -1; } compress_modules_cmds[cmp_model].loaded = 1; } if (compress_modules_cmds[cmp_model].ops->init != NULL) { if (compress_modules_cmds[cmp_model].ops->init(knet_h, cmp_model) < 0) { return -1; } } else { knet_h->compress_int_data[cmp_model] = (void *)&"1"; } return 0; } static int compress_lib_test(knet_handle_t knet_h) { int savederrno = 0; unsigned char src[KNET_DATABUFSIZE]; unsigned char dst[KNET_DATABUFSIZE_COMPRESS]; ssize_t dst_comp_len = KNET_DATABUFSIZE_COMPRESS, dst_decomp_len = KNET_DATABUFSIZE; unsigned int i; memset(src, 0, KNET_DATABUFSIZE); memset(dst, 0, KNET_DATABUFSIZE_COMPRESS); /* * NOTE: we cannot use compress and decompress API calls due to locking * so we need to call directly into the modules */ if (compress_modules_cmds[knet_h->compress_model].ops->compress(knet_h, src, KNET_DATABUFSIZE, dst, &dst_comp_len) < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_COMPRESS, "Unable to compress test buffer. Please check your compression settings: %s", strerror(savederrno)); errno = savederrno; return -1; } if (compress_modules_cmds[knet_h->compress_model].ops->decompress(knet_h, dst, dst_comp_len, src, &dst_decomp_len) < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_COMPRESS, "Unable to decompress test buffer. Please check your compression settings: %s", strerror(savederrno)); errno = savederrno; return -1; } for (i = 0; i < KNET_DATABUFSIZE; i++) { if (src[i] != 0) { log_err(knet_h, KNET_SUB_COMPRESS, "Decompressed buffer contains incorrect data"); errno = EINVAL; return -1; } } return 0; } int compress_init( knet_handle_t knet_h) { max_model = compress_get_max_model(); if (max_model > KNET_MAX_COMPRESS_METHODS) { log_err(knet_h, KNET_SUB_COMPRESS, "Too many compress methods defined in compress.c."); errno = EINVAL; return -1; } memset(&last_load_failure, 0, sizeof(struct timespec)); return 0; } int compress_cfg( knet_handle_t knet_h, struct knet_handle_compress_cfg *knet_handle_compress_cfg) { int savederrno = 0, err = 0; int cmp_model; cmp_model = compress_get_model(knet_handle_compress_cfg->compress_model); if (cmp_model < 0) { log_err(knet_h, KNET_SUB_COMPRESS, "compress model %s not supported", knet_handle_compress_cfg->compress_model); errno = EINVAL; return -1; } log_debug(knet_h, KNET_SUB_COMPRESS, "Initizializing compress module [%s/%d/%u]", knet_handle_compress_cfg->compress_model, knet_handle_compress_cfg->compress_level, knet_handle_compress_cfg->compress_threshold); if (cmp_model > 0) { if (compress_modules_cmds[cmp_model].built_in == 0) { log_err(knet_h, KNET_SUB_COMPRESS, "compress model %s support has not been built in. Please contact your vendor or fix the build", knet_handle_compress_cfg->compress_model); errno = EINVAL; return -1; } if (knet_handle_compress_cfg->compress_threshold > KNET_MAX_PACKET_SIZE) { log_err(knet_h, KNET_SUB_COMPRESS, "compress threshold cannot be higher than KNET_MAX_PACKET_SIZE (%d).", KNET_MAX_PACKET_SIZE); errno = EINVAL; return -1; } if (knet_handle_compress_cfg->compress_threshold == 0) { knet_h->compress_threshold = KNET_COMPRESS_THRESHOLD; log_debug(knet_h, KNET_SUB_COMPRESS, "resetting compression threshold to default (%d)", KNET_COMPRESS_THRESHOLD); } else { knet_h->compress_threshold = knet_handle_compress_cfg->compress_threshold; } savederrno = pthread_rwlock_rdlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_COMPRESS, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!compress_check_lib_is_init(knet_h, cmp_model)) { /* * need to switch to write lock, load the lib, and return with a write lock * this is not racy because compress_load_lib is written idempotent. */ pthread_rwlock_unlock(&shlib_rwlock); savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_COMPRESS, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (compress_load_lib(knet_h, cmp_model, 0) < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_COMPRESS, "Unable to load library: %s", strerror(savederrno)); err = -1; goto out_unlock; } } if (val_level(knet_h, cmp_model, knet_handle_compress_cfg->compress_level) < 0) { log_err(knet_h, KNET_SUB_COMPRESS, "compress level %d not supported for model %s", knet_handle_compress_cfg->compress_level, knet_handle_compress_cfg->compress_model); savederrno = EINVAL; err = -1; goto out_unlock; } knet_h->compress_model = cmp_model; knet_h->compress_level = knet_handle_compress_cfg->compress_level; if (compress_lib_test(knet_h) < 0) { savederrno = errno; err = -1; goto out_unlock; } out_unlock: pthread_rwlock_unlock(&shlib_rwlock); } if (err) { knet_h->compress_model = 0; knet_h->compress_level = 0; } errno = savederrno; return err; } void compress_fini( knet_handle_t knet_h, int all) { int savederrno = 0; int idx = 0; savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_COMPRESS, "Unable to get write lock: %s", strerror(savederrno)); return; } while (compress_modules_cmds[idx].model_name != NULL) { if ((compress_modules_cmds[idx].built_in == 1) && (compress_modules_cmds[idx].loaded == 1) && (compress_modules_cmds[idx].model_id > 0) && (knet_h->compress_int_data[idx] != NULL) && (idx < KNET_MAX_COMPRESS_METHODS)) { if ((all) || (compress_modules_cmds[idx].model_id == knet_h->compress_model)) { if (compress_modules_cmds[idx].ops->fini != NULL) { compress_modules_cmds[idx].ops->fini(knet_h, idx); } else { knet_h->compress_int_data[idx] = NULL; } } } idx++; } pthread_rwlock_unlock(&shlib_rwlock); return; } /* * compress does not require compress_check_lib_is_init * because it's protected by compress_cfg */ int compress( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return compress_modules_cmds[knet_h->compress_model].ops->compress(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int decompress( knet_handle_t knet_h, int compress_model, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { int savederrno = 0, err = 0; if (compress_model > max_model) { log_err(knet_h, KNET_SUB_COMPRESS, "Received packet with unknown compress model %d", compress_model); errno = EINVAL; return -1; } if (compress_is_valid_model(compress_model) < 0) { log_err(knet_h, KNET_SUB_COMPRESS, "Received packet compressed with %s but support is not built in this version of libknet. Please contact your distribution vendor or fix the build.", compress_modules_cmds[compress_model].model_name); errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_COMPRESS, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!compress_check_lib_is_init(knet_h, compress_model)) { /* * need to switch to write lock, load the lib, and return with a write lock * this is not racy because compress_load_lib is written idempotent. */ pthread_rwlock_unlock(&shlib_rwlock); savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_COMPRESS, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (compress_load_lib(knet_h, compress_model, 1) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_COMPRESS, "Unable to load library: %s", strerror(savederrno)); goto out_unlock; } } err = compress_modules_cmds[compress_model].ops->decompress(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); savederrno = errno; out_unlock: pthread_rwlock_unlock(&shlib_rwlock); errno = savederrno; return err; } int knet_get_compress_list(struct knet_compress_info *compress_list, size_t *compress_list_entries) { int err = 0; int idx = 0; int outidx = 0; if (!compress_list_entries) { errno = EINVAL; return -1; } while (compress_modules_cmds[idx].model_name != NULL) { if (compress_modules_cmds[idx].built_in) { if (compress_list) { compress_list[outidx].name = compress_modules_cmds[idx].model_name; } outidx++; } idx++; } *compress_list_entries = outidx; return err; } diff --git a/libknet/crypto.c b/libknet/crypto.c index 43c21994..6b1ead7d 100644 --- a/libknet/crypto.c +++ b/libknet/crypto.c @@ -1,212 +1,212 @@ /* * Copyright (C) 2012-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include "crypto.h" #include "crypto_model.h" #include "internals.h" #include "logging.h" #include "common.h" /* * internal module switch data */ -crypto_model_t crypto_modules_cmds[] = { +static crypto_model_t crypto_modules_cmds[] = { { "nss", WITH_CRYPTO_NSS, 0, NULL }, { "openssl", WITH_CRYPTO_OPENSSL, 0, NULL }, { NULL, 0, 0, NULL } }; static int crypto_get_model(const char *model) { int idx = 0; while (crypto_modules_cmds[idx].model_name != NULL) { if (!strcmp(crypto_modules_cmds[idx].model_name, model)) return idx; idx++; } return -1; } /* * exported API */ int crypto_encrypt_and_sign ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].ops->crypt(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int crypto_encrypt_and_signv ( knet_handle_t knet_h, const struct iovec *iov_in, int iovcnt_in, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].ops->cryptv(knet_h, iov_in, iovcnt_in, buf_out, buf_out_len); } int crypto_authenticate_and_decrypt ( knet_handle_t knet_h, const unsigned char *buf_in, const ssize_t buf_in_len, unsigned char *buf_out, ssize_t *buf_out_len) { return crypto_modules_cmds[knet_h->crypto_instance->model].ops->decrypt(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } int crypto_init( knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) { int savederrno = 0; int model = 0; model = crypto_get_model(knet_handle_crypto_cfg->crypto_model); if (model < 0) { log_err(knet_h, KNET_SUB_CRYPTO, "model %s not supported", knet_handle_crypto_cfg->crypto_model); return -1; } if (crypto_modules_cmds[model].built_in == 0) { log_err(knet_h, KNET_SUB_CRYPTO, "this version of libknet was built without %s support. Please contact your vendor or fix the build.", knet_handle_crypto_cfg->crypto_model); return -1; } savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to get write lock: %s", strerror(savederrno)); return -1; } if (!crypto_modules_cmds[model].loaded) { crypto_modules_cmds[model].ops = load_module (knet_h, "crypto", crypto_modules_cmds[model].model_name); if (!crypto_modules_cmds[model].ops) { savederrno = errno; log_err(knet_h, KNET_SUB_CRYPTO, "Unable to load %s lib", crypto_modules_cmds[model].model_name); goto out_err; } if (crypto_modules_cmds[model].ops->abi_ver != KNET_CRYPTO_MODEL_ABI) { log_err(knet_h, KNET_SUB_CRYPTO, "ABI mismatch loading module %s. knet ver: %d, module ver: %d", crypto_modules_cmds[model].model_name, KNET_CRYPTO_MODEL_ABI, crypto_modules_cmds[model].ops->abi_ver); savederrno = EINVAL; goto out_err; } crypto_modules_cmds[model].loaded = 1; } log_debug(knet_h, KNET_SUB_CRYPTO, "Initizializing crypto module [%s/%s/%s]", knet_handle_crypto_cfg->crypto_model, knet_handle_crypto_cfg->crypto_cipher_type, knet_handle_crypto_cfg->crypto_hash_type); knet_h->crypto_instance = malloc(sizeof(struct crypto_instance)); if (!knet_h->crypto_instance) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to allocate memory for crypto instance"); pthread_rwlock_unlock(&shlib_rwlock); savederrno = ENOMEM; goto out_err; } /* * if crypto_modules_cmds.ops->init fails, it is expected that * it will clean everything by itself. * crypto_modules_cmds.ops->fini is not invoked on error. */ knet_h->crypto_instance->model = model; if (crypto_modules_cmds[knet_h->crypto_instance->model].ops->init(knet_h, knet_handle_crypto_cfg)) { savederrno = errno; goto out_err; } log_debug(knet_h, KNET_SUB_CRYPTO, "security network overhead: %zu", knet_h->sec_header_size); pthread_rwlock_unlock(&shlib_rwlock); return 0; out_err: if (knet_h->crypto_instance) { free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; } pthread_rwlock_unlock(&shlib_rwlock); errno = savederrno; return -1; } void crypto_fini( knet_handle_t knet_h) { int savederrno = 0; int model = 0; savederrno = pthread_rwlock_wrlock(&shlib_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_CRYPTO, "Unable to get write lock: %s", strerror(savederrno)); return; } if (knet_h->crypto_instance) { model = knet_h->crypto_instance->model; if (crypto_modules_cmds[model].ops->fini != NULL) { crypto_modules_cmds[model].ops->fini(knet_h); } free(knet_h->crypto_instance); knet_h->crypto_instance = NULL; } pthread_rwlock_unlock(&shlib_rwlock); return; } int knet_get_crypto_list(struct knet_crypto_info *crypto_list, size_t *crypto_list_entries) { int err = 0; int idx = 0; int outidx = 0; if (!crypto_list_entries) { errno = EINVAL; return -1; } while (crypto_modules_cmds[idx].model_name != NULL) { if (crypto_modules_cmds[idx].built_in) { if (crypto_list) { crypto_list[outidx].name = crypto_modules_cmds[idx].model_name; } outidx++; } idx++; } *crypto_list_entries = outidx; return err; } diff --git a/libknet/threads_tx.c b/libknet/threads_tx.c index 20798c0c..a1b407d3 100644 --- a/libknet/threads_tx.c +++ b/libknet/threads_tx.c @@ -1,756 +1,748 @@ /* * Copyright (C) 2012-2018 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include "compat.h" #include "compress.h" #include "crypto.h" #include "host.h" #include "link.h" #include "logging.h" #include "transports.h" #include "transport_common.h" #include "threads_common.h" #include "threads_heartbeat.h" #include "threads_tx.h" #include "netutils.h" /* * SEND */ static int _dispatch_to_links(knet_handle_t knet_h, struct knet_host *dst_host, struct knet_mmsghdr *msg, int msgs_to_send) { int link_idx, msg_idx, sent_msgs, prev_sent, progress; int err = 0, savederrno = 0; unsigned int i; struct knet_mmsghdr *cur; struct knet_link *cur_link; for (link_idx = 0; link_idx < dst_host->active_link_entries; link_idx++) { sent_msgs = 0; prev_sent = 0; progress = 1; cur_link = &dst_host->link[dst_host->active_links[link_idx]]; if (cur_link->transport_type == KNET_TRANSPORT_LOOPBACK) { continue; } msg_idx = 0; while (msg_idx < msgs_to_send) { msg[msg_idx].msg_hdr.msg_name = &cur_link->dst_addr; /* Cast for Linux/BSD compatibility */ for (i=0; i<(unsigned int)msg[msg_idx].msg_hdr.msg_iovlen; i++) { cur_link->status.stats.tx_data_bytes += msg[msg_idx].msg_hdr.msg_iov[i].iov_len; } cur_link->status.stats.tx_data_packets++; msg_idx++; } retry: cur = &msg[prev_sent]; sent_msgs = _sendmmsg(dst_host->link[dst_host->active_links[link_idx]].outsock, &cur[0], msgs_to_send - prev_sent, MSG_DONTWAIT | MSG_NOSIGNAL); savederrno = errno; err = transport_tx_sock_error(knet_h, dst_host->link[dst_host->active_links[link_idx]].transport_type, dst_host->link[dst_host->active_links[link_idx]].outsock, sent_msgs, savederrno); switch(err) { case -1: /* unrecoverable error */ cur_link->status.stats.tx_data_errors++; goto out_unlock; break; case 0: /* ignore error and continue */ break; case 1: /* retry to send those same data */ cur_link->status.stats.tx_data_retries++; goto retry; break; } prev_sent = prev_sent + sent_msgs; if ((sent_msgs >= 0) && (prev_sent < msgs_to_send)) { if ((sent_msgs) || (progress)) { if (sent_msgs) { progress = 1; } else { progress = 0; } #ifdef DEBUG log_debug(knet_h, KNET_SUB_TX, "Unable to send all (%d/%d) data packets to host %s (%u) link %s:%s (%u)", sent_msgs, msg_idx, dst_host->name, dst_host->host_id, dst_host->link[dst_host->active_links[link_idx]].status.dst_ipaddr, dst_host->link[dst_host->active_links[link_idx]].status.dst_port, dst_host->link[dst_host->active_links[link_idx]].link_id); #endif goto retry; } if (!progress) { savederrno = EAGAIN; err = -1; goto out_unlock; } } if ((dst_host->link_handler_policy == KNET_LINK_POLICY_RR) && (dst_host->active_link_entries > 1)) { uint8_t cur_link_id = dst_host->active_links[0]; memmove(&dst_host->active_links[0], &dst_host->active_links[1], KNET_MAX_LINK - 1); dst_host->active_links[dst_host->active_link_entries - 1] = cur_link_id; break; } } out_unlock: errno = savederrno; return err; } static int _parse_recv_from_sock(knet_handle_t knet_h, size_t inlen, int8_t channel, int is_sync) { size_t outlen, frag_len; struct knet_host *dst_host; knet_node_id_t dst_host_ids_temp[KNET_MAX_HOST]; size_t dst_host_ids_entries_temp = 0; knet_node_id_t dst_host_ids[KNET_MAX_HOST]; size_t dst_host_ids_entries = 0; int bcast = 1; struct knet_hostinfo *knet_hostinfo; struct iovec iov_out[PCKT_FRAG_MAX][2]; int iovcnt_out = 2; uint8_t frag_idx; unsigned int temp_data_mtu; size_t host_idx; int send_mcast = 0; struct knet_header *inbuf; int savederrno = 0; int err = 0; seq_num_t tx_seq_num; struct knet_mmsghdr msg[PCKT_FRAG_MAX]; int msgs_to_send, msg_idx; unsigned int i; int j; int send_local = 0; int data_compressed = 0; size_t uncrypted_frag_size; inbuf = knet_h->recv_from_sock_buf; if ((knet_h->enabled != 1) && (inbuf->kh_type != KNET_HEADER_TYPE_HOST_INFO)) { /* data forward is disabled */ log_debug(knet_h, KNET_SUB_TX, "Received data packet but forwarding is disabled"); savederrno = ECANCELED; err = -1; goto out_unlock; } /* * move this into a separate function to expand on * extra switching rules */ switch(inbuf->kh_type) { case KNET_HEADER_TYPE_DATA: if (knet_h->dst_host_filter_fn) { bcast = knet_h->dst_host_filter_fn( knet_h->dst_host_filter_fn_private_data, (const unsigned char *)inbuf->khp_data_userdata, inlen, KNET_NOTIFY_TX, knet_h->host_id, knet_h->host_id, &channel, dst_host_ids_temp, &dst_host_ids_entries_temp); if (bcast < 0) { log_debug(knet_h, KNET_SUB_TX, "Error from dst_host_filter_fn: %d", bcast); savederrno = EFAULT; err = -1; goto out_unlock; } if ((!bcast) && (!dst_host_ids_entries_temp)) { log_debug(knet_h, KNET_SUB_TX, "Message is unicast but no dst_host_ids_entries"); savederrno = EINVAL; err = -1; goto out_unlock; } if ((!bcast) && (dst_host_ids_entries_temp > KNET_MAX_HOST)) { log_debug(knet_h, KNET_SUB_TX, "dst_host_filter_fn returned too many destinations"); savederrno = EINVAL; err = -1; goto out_unlock; } } /* Send to localhost if appropriate and enabled */ if (knet_h->has_loop_link) { send_local = 0; if (bcast) { send_local = 1; } else { for (i=0; i< dst_host_ids_entries_temp; i++) { if (dst_host_ids_temp[i] == knet_h->host_id) { send_local = 1; } } } if (send_local) { const unsigned char *buf = inbuf->khp_data_userdata; ssize_t buflen = inlen; struct knet_link *local_link; local_link = knet_h->host_index[knet_h->host_id]->link; local_retry: err = write(knet_h->sockfd[channel].sockfd[knet_h->sockfd[channel].is_created], buf, buflen); if (err < 0) { log_err(knet_h, KNET_SUB_TRANSP_LOOPBACK, "send local failed. error=%s\n", strerror(errno)); local_link->status.stats.tx_data_errors++; } if (err > 0 && err < buflen) { log_debug(knet_h, KNET_SUB_TRANSP_LOOPBACK, "send local incomplete=%d bytes of %zu\n", err, inlen); local_link->status.stats.tx_data_retries++; buf += err; buflen -= err; usleep(KNET_THREADS_TIMERES / 16); goto local_retry; } if (err == buflen) { local_link->status.stats.tx_data_packets++; local_link->status.stats.tx_data_bytes += inlen; } } } break; case KNET_HEADER_TYPE_HOST_INFO: knet_hostinfo = (struct knet_hostinfo *)inbuf->khp_data_userdata; if (knet_hostinfo->khi_bcast == KNET_HOSTINFO_UCAST) { bcast = 0; dst_host_ids_temp[0] = knet_hostinfo->khi_dst_node_id; dst_host_ids_entries_temp = 1; knet_hostinfo->khi_dst_node_id = htons(knet_hostinfo->khi_dst_node_id); } break; default: log_warn(knet_h, KNET_SUB_TX, "Receiving unknown messages from socket"); savederrno = ENOMSG; err = -1; goto out_unlock; break; } if (is_sync) { if ((bcast) || ((!bcast) && (dst_host_ids_entries_temp > 1))) { log_debug(knet_h, KNET_SUB_TX, "knet_send_sync is only supported with unicast packets for one destination"); savederrno = E2BIG; err = -1; goto out_unlock; } } /* * check destinations hosts before spending time * in fragmenting/encrypting packets to save * time processing data for unreachable hosts. * for unicast, also remap the destination data * to skip unreachable hosts. */ if (!bcast) { dst_host_ids_entries = 0; for (host_idx = 0; host_idx < dst_host_ids_entries_temp; host_idx++) { dst_host = knet_h->host_index[dst_host_ids_temp[host_idx]]; if (!dst_host) { continue; } if (!(dst_host->host_id == knet_h->host_id && knet_h->has_loop_link) && dst_host->status.reachable) { dst_host_ids[dst_host_ids_entries] = dst_host_ids_temp[host_idx]; dst_host_ids_entries++; } } if (!dst_host_ids_entries) { savederrno = EHOSTDOWN; err = -1; goto out_unlock; } } else { send_mcast = 0; for (dst_host = knet_h->host_head; dst_host != NULL; dst_host = dst_host->next) { if (!(dst_host->host_id == knet_h->host_id && knet_h->has_loop_link) && dst_host->status.reachable) { send_mcast = 1; break; } } if (!send_mcast) { savederrno = EHOSTDOWN; err = -1; goto out_unlock; } } if (!knet_h->data_mtu) { /* * using MIN_MTU_V4 for data mtu is not completely accurate but safe enough */ log_debug(knet_h, KNET_SUB_TX, "Received data packet but data MTU is still unknown." " Packet might not be delivered." " Assuming minimum IPv4 MTU (%d)", KNET_PMTUD_MIN_MTU_V4); temp_data_mtu = KNET_PMTUD_MIN_MTU_V4; } else { /* * take a copy of the mtu to avoid value changing under * our feet while we are sending a fragmented pckt */ temp_data_mtu = knet_h->data_mtu; } /* * compress data */ if ((knet_h->compress_model > 0) && (inlen > knet_h->compress_threshold)) { size_t cmp_outlen = KNET_DATABUFSIZE_COMPRESS; struct timespec start_time; struct timespec end_time; uint64_t compress_time; clock_gettime(CLOCK_MONOTONIC, &start_time); err = compress(knet_h, (const unsigned char *)inbuf->khp_data_userdata, inlen, knet_h->send_to_links_buf_compress, (ssize_t *)&cmp_outlen); if (err < 0) { log_warn(knet_h, KNET_SUB_COMPRESS, "Compression failed (%d): %s", err, strerror(errno)); } else { /* Collect stats */ clock_gettime(CLOCK_MONOTONIC, &end_time); timespec_diff(start_time, end_time, &compress_time); if (compress_time < knet_h->stats.tx_compress_time_min) { knet_h->stats.tx_compress_time_min = compress_time; } if (compress_time > knet_h->stats.tx_compress_time_max) { knet_h->stats.tx_compress_time_max = compress_time; } knet_h->stats.tx_compress_time_ave = (unsigned long long)(knet_h->stats.tx_compress_time_ave * knet_h->stats.tx_compressed_packets + compress_time) / (knet_h->stats.tx_compressed_packets+1); knet_h->stats.tx_compressed_packets++; knet_h->stats.tx_compressed_original_bytes += inlen; knet_h->stats.tx_compressed_size_bytes += cmp_outlen; if (cmp_outlen < inlen) { memmove(inbuf->khp_data_userdata, knet_h->send_to_links_buf_compress, cmp_outlen); inlen = cmp_outlen; data_compressed = 1; } } } if ((knet_h->compress_model > 0) && (inlen <= knet_h->compress_threshold)) { knet_h->stats.tx_uncompressed_packets++; } /* * prepare the outgoing buffers */ frag_len = inlen; frag_idx = 0; inbuf->khp_data_bcast = bcast; inbuf->khp_data_frag_num = ceil((float)inlen / temp_data_mtu); inbuf->khp_data_channel = channel; if (data_compressed) { inbuf->khp_data_compress = knet_h->compress_model; } else { inbuf->khp_data_compress = 0; } if (pthread_mutex_lock(&knet_h->tx_seq_num_mutex)) { log_debug(knet_h, KNET_SUB_TX, "Unable to get seq mutex lock"); goto out_unlock; } knet_h->tx_seq_num++; /* * force seq_num 0 to detect a node that has crashed and rejoining * the knet instance. seq_num 0 will clear the buffers in the RX * thread */ if (knet_h->tx_seq_num == 0) { knet_h->tx_seq_num++; } /* * cache the value in locked context */ tx_seq_num = knet_h->tx_seq_num; inbuf->khp_data_seq_num = htons(knet_h->tx_seq_num); pthread_mutex_unlock(&knet_h->tx_seq_num_mutex); /* * forcefully broadcast a ping to all nodes every SEQ_MAX / 8 * pckts. * this solves 2 problems: * 1) on TX socket overloads we generate extra pings to keep links alive * 2) in 3+ nodes setup, where all the traffic is flowing between node 1 and 2, * node 3+ will be able to keep in sync on the TX seq_num even without * receiving traffic or pings in betweens. This avoids issues with * rollover of the circular buffer */ if (tx_seq_num % (SEQ_MAX / 8) == 0) { _send_pings(knet_h, 0); } if (inbuf->khp_data_frag_num > 1) { while (frag_idx < inbuf->khp_data_frag_num) { /* * set the iov_base */ iov_out[frag_idx][0].iov_base = (void *)knet_h->send_to_links_buf[frag_idx]; iov_out[frag_idx][0].iov_len = KNET_HEADER_DATA_SIZE; iov_out[frag_idx][1].iov_base = inbuf->khp_data_userdata + (temp_data_mtu * frag_idx); /* * set the len */ if (frag_len > temp_data_mtu) { iov_out[frag_idx][1].iov_len = temp_data_mtu; } else { iov_out[frag_idx][1].iov_len = frag_len; } /* * copy the frag info on all buffers */ knet_h->send_to_links_buf[frag_idx]->kh_type = inbuf->kh_type; knet_h->send_to_links_buf[frag_idx]->khp_data_seq_num = inbuf->khp_data_seq_num; knet_h->send_to_links_buf[frag_idx]->khp_data_frag_num = inbuf->khp_data_frag_num; knet_h->send_to_links_buf[frag_idx]->khp_data_bcast = inbuf->khp_data_bcast; knet_h->send_to_links_buf[frag_idx]->khp_data_channel = inbuf->khp_data_channel; knet_h->send_to_links_buf[frag_idx]->khp_data_compress = inbuf->khp_data_compress; frag_len = frag_len - temp_data_mtu; frag_idx++; } iovcnt_out = 2; } else { iov_out[frag_idx][0].iov_base = (void *)inbuf; iov_out[frag_idx][0].iov_len = frag_len + KNET_HEADER_DATA_SIZE; iovcnt_out = 1; } if (knet_h->crypto_instance) { struct timespec start_time; struct timespec end_time; uint64_t crypt_time; frag_idx = 0; while (frag_idx < inbuf->khp_data_frag_num) { clock_gettime(CLOCK_MONOTONIC, &start_time); if (crypto_encrypt_and_signv( knet_h, iov_out[frag_idx], iovcnt_out, knet_h->send_to_links_buf_crypt[frag_idx], (ssize_t *)&outlen) < 0) { log_debug(knet_h, KNET_SUB_TX, "Unable to encrypt packet"); savederrno = ECHILD; err = -1; goto out_unlock; } clock_gettime(CLOCK_MONOTONIC, &end_time); timespec_diff(start_time, end_time, &crypt_time); if (crypt_time < knet_h->stats.tx_crypt_time_min) { knet_h->stats.tx_crypt_time_min = crypt_time; } if (crypt_time > knet_h->stats.tx_crypt_time_max) { knet_h->stats.tx_crypt_time_max = crypt_time; } knet_h->stats.tx_crypt_time_ave = (knet_h->stats.tx_crypt_time_ave * knet_h->stats.tx_crypt_packets + crypt_time) / (knet_h->stats.tx_crypt_packets+1); uncrypted_frag_size = 0; for (j=0; j < iovcnt_out; j++) { uncrypted_frag_size += iov_out[frag_idx][j].iov_len; } knet_h->stats.tx_crypt_byte_overhead += (outlen - uncrypted_frag_size); knet_h->stats.tx_crypt_packets++; iov_out[frag_idx][0].iov_base = knet_h->send_to_links_buf_crypt[frag_idx]; iov_out[frag_idx][0].iov_len = outlen; frag_idx++; } iovcnt_out = 1; } memset(&msg, 0, sizeof(msg)); msgs_to_send = inbuf->khp_data_frag_num; msg_idx = 0; while (msg_idx < msgs_to_send) { msg[msg_idx].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); msg[msg_idx].msg_hdr.msg_iov = &iov_out[msg_idx][0]; msg[msg_idx].msg_hdr.msg_iovlen = iovcnt_out; msg_idx++; } if (!bcast) { for (host_idx = 0; host_idx < dst_host_ids_entries; host_idx++) { dst_host = knet_h->host_index[dst_host_ids[host_idx]]; err = _dispatch_to_links(knet_h, dst_host, &msg[0], msgs_to_send); savederrno = errno; if (err) { goto out_unlock; } } } else { for (dst_host = knet_h->host_head; dst_host != NULL; dst_host = dst_host->next) { if (dst_host->status.reachable) { err = _dispatch_to_links(knet_h, dst_host, &msg[0], msgs_to_send); savederrno = errno; if (err) { goto out_unlock; } } } } out_unlock: errno = savederrno; return err; } int knet_send_sync(knet_handle_t knet_h, const char *buff, const size_t buff_len, const int8_t channel) { int savederrno = 0, err = 0; if (!knet_h) { errno = EINVAL; return -1; } if (buff == NULL) { errno = EINVAL; return -1; } if (buff_len <= 0) { errno = EINVAL; return -1; } if (buff_len > KNET_MAX_PACKET_SIZE) { errno = EINVAL; return -1; } if (channel < 0) { errno = EINVAL; return -1; } if (channel >= KNET_DATAFD_MAX) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_TX, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h->sockfd[channel].in_use) { savederrno = EINVAL; err = -1; goto out; } savederrno = pthread_mutex_lock(&knet_h->tx_mutex); if (savederrno) { log_err(knet_h, KNET_SUB_TX, "Unable to get TX mutex lock: %s", strerror(savederrno)); err = -1; goto out; } knet_h->recv_from_sock_buf->kh_type = KNET_HEADER_TYPE_DATA; memmove(knet_h->recv_from_sock_buf->khp_data_userdata, buff, buff_len); err = _parse_recv_from_sock(knet_h, buff_len, channel, 1); savederrno = errno; pthread_mutex_unlock(&knet_h->tx_mutex); out: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } static void _handle_send_to_links(knet_handle_t knet_h, struct msghdr *msg, int sockfd, int8_t channel, int type) { ssize_t inlen = 0; int savederrno = 0, docallback = 0; if ((channel >= 0) && (channel < KNET_DATAFD_MAX) && (!knet_h->sockfd[channel].is_socket)) { inlen = readv(sockfd, msg->msg_iov, 1); } else { inlen = recvmsg(sockfd, msg, MSG_DONTWAIT | MSG_NOSIGNAL); } if (inlen == 0) { savederrno = 0; docallback = 1; - goto out; - } - if (inlen < 0) { - savederrno = errno; - docallback = 1; - goto out; - } - - knet_h->recv_from_sock_buf->kh_type = type; - _parse_recv_from_sock(knet_h, inlen, channel, 0); - -out: - if (inlen < 0) { + } else if (inlen < 0) { struct epoll_event ev; + savederrno = errno; + docallback = 1; memset(&ev, 0, sizeof(struct epoll_event)); if (epoll_ctl(knet_h->send_to_links_epollfd, EPOLL_CTL_DEL, knet_h->sockfd[channel].sockfd[knet_h->sockfd[channel].is_created], &ev)) { log_err(knet_h, KNET_SUB_TX, "Unable to del datafd %d from linkfd epoll pool: %s", knet_h->sockfd[channel].sockfd[0], strerror(savederrno)); } else { knet_h->sockfd[channel].has_error = 1; } - + } else { + knet_h->recv_from_sock_buf->kh_type = type; + _parse_recv_from_sock(knet_h, inlen, channel, 0); } if (docallback) { knet_h->sock_notify_fn(knet_h->sock_notify_fn_private_data, knet_h->sockfd[channel].sockfd[0], channel, KNET_NOTIFY_TX, inlen, savederrno); } } void *_handle_send_to_links_thread(void *data) { knet_handle_t knet_h = (knet_handle_t) data; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; int i, nev, type; int8_t channel; struct iovec iov_in; struct msghdr msg; struct sockaddr_storage address; set_thread_status(knet_h, KNET_THREAD_TX, KNET_THREAD_RUNNING); memset(&iov_in, 0, sizeof(iov_in)); iov_in.iov_base = (void *)knet_h->recv_from_sock_buf->khp_data_userdata; iov_in.iov_len = KNET_MAX_PACKET_SIZE; memset(&msg, 0, sizeof(struct msghdr)); msg.msg_name = &address; msg.msg_namelen = sizeof(struct sockaddr_storage); msg.msg_iov = &iov_in; msg.msg_iovlen = 1; knet_h->recv_from_sock_buf->kh_version = KNET_HEADER_VERSION; knet_h->recv_from_sock_buf->khp_data_frag_seq = 0; knet_h->recv_from_sock_buf->kh_node = htons(knet_h->host_id); for (i = 0; i < PCKT_FRAG_MAX; i++) { knet_h->send_to_links_buf[i]->kh_version = KNET_HEADER_VERSION; knet_h->send_to_links_buf[i]->khp_data_frag_seq = i + 1; knet_h->send_to_links_buf[i]->kh_node = htons(knet_h->host_id); } while (!shutdown_in_progress(knet_h)) { nev = epoll_wait(knet_h->send_to_links_epollfd, events, KNET_EPOLL_MAX_EVENTS + 1, KNET_THREADS_TIMERES / 1000); /* * we use timeout to detect if thread is shutting down */ if (nev == 0) { continue; } if (pthread_rwlock_rdlock(&knet_h->global_rwlock) != 0) { log_debug(knet_h, KNET_SUB_TX, "Unable to get read lock"); continue; } for (i = 0; i < nev; i++) { if (events[i].data.fd == knet_h->hostsockfd[0]) { type = KNET_HEADER_TYPE_HOST_INFO; channel = -1; } else { type = KNET_HEADER_TYPE_DATA; for (channel = 0; channel < KNET_DATAFD_MAX; channel++) { if ((knet_h->sockfd[channel].in_use) && (knet_h->sockfd[channel].sockfd[knet_h->sockfd[channel].is_created] == events[i].data.fd)) { break; } } if (channel >= KNET_DATAFD_MAX) { log_debug(knet_h, KNET_SUB_TX, "No available channels"); continue; /* channel not found */ } } if (pthread_mutex_lock(&knet_h->tx_mutex) != 0) { log_debug(knet_h, KNET_SUB_TX, "Unable to get mutex lock"); continue; } _handle_send_to_links(knet_h, &msg, events[i].data.fd, channel, type); pthread_mutex_unlock(&knet_h->tx_mutex); } pthread_rwlock_unlock(&knet_h->global_rwlock); } set_thread_status(knet_h, KNET_THREAD_TX, KNET_THREAD_STOPPED); return NULL; } diff --git a/libknet/transports.c b/libknet/transports.c index e7f1196a..67ebc6ef 100644 --- a/libknet/transports.c +++ b/libknet/transports.c @@ -1,268 +1,268 @@ /* * Copyright (C) 2017-2018 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include "libknet.h" #include "compat.h" #include "host.h" #include "link.h" #include "logging.h" #include "common.h" #include "transports.h" #include "transport_loopback.h" #include "transport_udp.h" #include "transport_sctp.h" #include "threads_common.h" #define empty_module 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }, -knet_transport_ops_t transport_modules_cmd[KNET_MAX_TRANSPORTS] = { +static knet_transport_ops_t transport_modules_cmd[KNET_MAX_TRANSPORTS] = { { "LOOPBACK", KNET_TRANSPORT_LOOPBACK, 1, KNET_PMTUD_LOOPBACK_OVERHEAD, loopback_transport_init, loopback_transport_free, loopback_transport_link_set_config, loopback_transport_link_clear_config, loopback_transport_link_dyn_connect, loopback_transport_rx_sock_error, loopback_transport_tx_sock_error, loopback_transport_rx_is_data }, { "UDP", KNET_TRANSPORT_UDP, 1, KNET_PMTUD_UDP_OVERHEAD, udp_transport_init, udp_transport_free, udp_transport_link_set_config, udp_transport_link_clear_config, udp_transport_link_dyn_connect, udp_transport_rx_sock_error, udp_transport_tx_sock_error, udp_transport_rx_is_data }, { "SCTP", KNET_TRANSPORT_SCTP, #ifdef HAVE_NETINET_SCTP_H 1, KNET_PMTUD_SCTP_OVERHEAD, sctp_transport_init, sctp_transport_free, sctp_transport_link_set_config, sctp_transport_link_clear_config, sctp_transport_link_dyn_connect, sctp_transport_rx_sock_error, sctp_transport_tx_sock_error, sctp_transport_rx_is_data }, #else empty_module #endif { NULL, KNET_MAX_TRANSPORTS, empty_module }; /* * transport wrappers */ int start_all_transports(knet_handle_t knet_h) { int idx = 0, savederrno = 0, err = 0; while (transport_modules_cmd[idx].transport_name != NULL) { if (transport_modules_cmd[idx].built_in) { if (transport_modules_cmd[idx].transport_init(knet_h) < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Failed to allocate transport handle for %s: %s", transport_modules_cmd[idx].transport_name, strerror(savederrno)); err = -1; goto out; } } idx++; } out: errno = savederrno; return err; } void stop_all_transports(knet_handle_t knet_h) { int idx = 0; while (transport_modules_cmd[idx].transport_name != NULL) { if (transport_modules_cmd[idx].built_in) { transport_modules_cmd[idx].transport_free(knet_h); } idx++; } } int transport_link_set_config(knet_handle_t knet_h, struct knet_link *kn_link, uint8_t transport) { if (!transport_modules_cmd[transport].built_in) { errno = EINVAL; return -1; } kn_link->transport_connected = 0; kn_link->transport_type = transport; kn_link->proto_overhead = transport_modules_cmd[transport].transport_mtu_overhead; return transport_modules_cmd[transport].transport_link_set_config(knet_h, kn_link); } int transport_link_clear_config(knet_handle_t knet_h, struct knet_link *kn_link) { return transport_modules_cmd[kn_link->transport_type].transport_link_clear_config(knet_h, kn_link); } int transport_link_dyn_connect(knet_handle_t knet_h, int sockfd, struct knet_link *kn_link) { return transport_modules_cmd[kn_link->transport_type].transport_link_dyn_connect(knet_h, sockfd, kn_link); } int transport_rx_sock_error(knet_handle_t knet_h, uint8_t transport, int sockfd, int recv_err, int recv_errno) { return transport_modules_cmd[transport].transport_rx_sock_error(knet_h, sockfd, recv_err, recv_errno); } int transport_tx_sock_error(knet_handle_t knet_h, uint8_t transport, int sockfd, int recv_err, int recv_errno) { return transport_modules_cmd[transport].transport_tx_sock_error(knet_h, sockfd, recv_err, recv_errno); } int transport_rx_is_data(knet_handle_t knet_h, uint8_t transport, int sockfd, struct knet_mmsghdr *msg) { return transport_modules_cmd[transport].transport_rx_is_data(knet_h, sockfd, msg); } /* * public api */ int knet_get_transport_list(struct knet_transport_info *transport_list, size_t *transport_list_entries) { int err = 0; int idx = 0; int outidx = 0; if (!transport_list_entries) { errno = EINVAL; return -1; } while (transport_modules_cmd[idx].transport_name != NULL) { if (transport_modules_cmd[idx].built_in) { if (transport_list) { transport_list[outidx].name = transport_modules_cmd[idx].transport_name; transport_list[outidx].id = transport_modules_cmd[idx].transport_id; } outidx++; } idx++; } *transport_list_entries = outidx; return err; } const char *knet_get_transport_name_by_id(uint8_t transport) { int savederrno = 0; const char *name = NULL; if (transport == KNET_MAX_TRANSPORTS) { errno = EINVAL; return name; } if ((transport_modules_cmd[transport].transport_name) && (transport_modules_cmd[transport].built_in)) { name = transport_modules_cmd[transport].transport_name; } else { savederrno = ENOENT; } errno = savederrno; return name; } uint8_t knet_get_transport_id_by_name(const char *name) { int savederrno = 0; uint8_t err = KNET_MAX_TRANSPORTS; int i, found; if (!name) { errno = EINVAL; return err; } i = 0; found = 0; while (transport_modules_cmd[i].transport_name != NULL) { if (transport_modules_cmd[i].built_in) { if (!strcmp(transport_modules_cmd[i].transport_name, name)) { err = transport_modules_cmd[i].transport_id; found = 1; break; } } i++; } if (!found) { savederrno = EINVAL; } errno = savederrno; return err; } int knet_handle_set_transport_reconnect_interval(knet_handle_t knet_h, uint32_t msecs) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!msecs) { errno = EINVAL; return -1; } if (msecs < 1000) { log_warn(knet_h, KNET_SUB_HANDLE, "reconnect internval below 1 sec (%u msecs) might be too aggressive", msecs); } if (msecs > 60000) { log_warn(knet_h, KNET_SUB_HANDLE, "reconnect internval above 1 minute (%u msecs) could cause long delays in network convergiance", msecs); } savederrno = get_global_wrlock(knet_h); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->reconnect_int = msecs; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_get_transport_reconnect_interval(knet_handle_t knet_h, uint32_t *msecs) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!msecs) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } *msecs = knet_h->reconnect_int; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; }