diff --git a/libknet/compress.c b/libknet/compress.c index 80a30681..41f6434c 100644 --- a/libknet/compress.c +++ b/libknet/compress.c @@ -1,137 +1,145 @@ /* * Copyright (C) 2010-2017 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 "internals.h" #include "compress.h" #include "logging.h" #include "compress_zlib.h" #include "compress_lz4.h" /* * internal module switch data */ /* * DO NOT CHANGE ORDER HERE OR ONWIRE COMPATIBILITY * WILL BREAK! * * add after zlib and before NULL/NULL/NULL. */ compress_model_t compress_modules_cmds[] = { { "none", NULL, NULL, NULL }, { "zlib", zlib_val_level, zlib_compress, zlib_decompress }, { "lz4", lz4_val_level, lz4_compress, lz4_decompress }, { "lz4hc", lz4hc_val_level, lz4hc_compress, lz4_decompress }, { NULL, NULL, NULL, NULL }, }; +/* + * used exclusively by the test suite (see api_knet_send_compress) + */ +const char *get_model_by_idx(int idx) +{ + return compress_modules_cmds[idx].model_name; +} + static int 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 idx; idx++; } return -1; } static int get_max_model(void) { int idx = 0; while (compress_modules_cmds[idx].model_name != NULL) { idx++; } return idx - 1; } static int val_level( knet_handle_t knet_h, int compress_model, int compress_level) { return compress_modules_cmds[compress_model].val_level(knet_h, compress_level); } int compress_init( knet_handle_t knet_h, struct knet_handle_compress_cfg *knet_handle_compress_cfg) { int cmp_model; knet_h->compress_max_model = get_max_model(); if (!knet_handle_compress_cfg) { return 0; } 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); cmp_model = 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; } if (cmp_model > 0) { 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); 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; } } knet_h->compress_model = cmp_model; knet_h->compress_level = knet_handle_compress_cfg->compress_level; return 0; } 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].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) { return compress_modules_cmds[compress_model].decompress(knet_h, buf_in, buf_in_len, buf_out, buf_out_len); } diff --git a/libknet/compress.h b/libknet/compress.h index cb835396..4d220ff0 100644 --- a/libknet/compress.h +++ b/libknet/compress.h @@ -1,49 +1,54 @@ /* * Copyright (C) 2010-2017 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __KNET_COMPRESS_H__ #define __KNET_COMPRESS_H__ #include "internals.h" typedef struct { const char *model_name; int (*val_level)(knet_handle_t knet_h, int compress_level); 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); int (*decompress)(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); } compress_model_t; int compress_init( knet_handle_t knet_h, struct knet_handle_compress_cfg *knet_handle_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); 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); +/* + * used exclusively by the test suite (see api_knet_send_compress) + */ +const char *get_model_by_idx(int idx); + #endif diff --git a/libknet/tests/api-check.mk b/libknet/tests/api-check.mk index f1cc8ff5..2a7e2ad5 100644 --- a/libknet/tests/api-check.mk +++ b/libknet/tests/api-check.mk @@ -1,227 +1,235 @@ # # Copyright (C) 2016 Red Hat, Inc. All rights reserved. # # Authors: Fabio M. Di Nitto # # This software licensed under GPL-2.0+, LGPL-2.0+ # api_checks = \ api_knet_handle_new_test \ api_knet_handle_free_test \ api_knet_handle_compress_test \ api_knet_handle_crypto_test \ api_knet_handle_setfwd_test \ api_knet_handle_enable_filter_test \ api_knet_handle_enable_sock_notify_test \ api_knet_handle_add_datafd_test \ api_knet_handle_remove_datafd_test \ api_knet_handle_get_channel_test \ api_knet_handle_get_datafd_test \ api_knet_handle_get_transport_list_test \ api_knet_handle_get_transport_name_by_id_test \ api_knet_handle_get_transport_id_by_name_test \ api_knet_handle_set_transport_reconnect_interval_test \ api_knet_handle_get_transport_reconnect_interval_test \ api_knet_recv_test \ api_knet_send_test \ + api_knet_send_compress_test \ api_knet_send_sync_test \ api_knet_send_loopback_test \ api_knet_handle_pmtud_setfreq_test \ api_knet_handle_pmtud_getfreq_test \ api_knet_handle_enable_pmtud_notify_test \ api_knet_handle_pmtud_get_test \ api_knet_host_add_test \ api_knet_host_remove_test \ api_knet_host_set_name_test \ api_knet_host_get_name_by_host_id_test \ api_knet_host_get_id_by_host_name_test \ api_knet_host_get_host_list_test \ api_knet_host_set_policy_test \ api_knet_host_get_policy_test \ api_knet_host_get_status_test \ api_knet_host_enable_status_change_notify_test \ api_knet_log_get_subsystem_name_test \ api_knet_log_get_subsystem_id_test \ api_knet_log_get_loglevel_name_test \ api_knet_log_get_loglevel_id_test \ api_knet_log_set_loglevel_test \ api_knet_log_get_loglevel_test \ api_knet_strtoaddr_test \ api_knet_addrtostr_test \ api_knet_link_set_config_test \ api_knet_link_clear_config_test \ api_knet_link_get_config_test \ api_knet_link_set_ping_timers_test \ api_knet_link_get_ping_timers_test \ api_knet_link_set_pong_count_test \ api_knet_link_get_pong_count_test \ api_knet_link_set_priority_test \ api_knet_link_get_priority_test \ api_knet_link_set_enable_test \ api_knet_link_get_enable_test \ api_knet_link_get_link_list_test \ api_knet_link_get_status_test api_knet_handle_new_test_SOURCES = api_knet_handle_new.c \ test-common.c api_knet_handle_free_test_SOURCES = api_knet_handle_free.c \ test-common.c api_knet_handle_compress_test_SOURCES = api_knet_handle_compress.c \ test-common.c api_knet_handle_crypto_test_SOURCES = api_knet_handle_crypto.c \ test-common.c api_knet_handle_setfwd_test_SOURCES = api_knet_handle_setfwd.c \ test-common.c api_knet_handle_enable_filter_test_SOURCES = api_knet_handle_enable_filter.c \ test-common.c api_knet_handle_enable_sock_notify_test_SOURCES = api_knet_handle_enable_sock_notify.c \ test-common.c api_knet_handle_add_datafd_test_SOURCES = api_knet_handle_add_datafd.c \ test-common.c api_knet_handle_remove_datafd_test_SOURCES = api_knet_handle_remove_datafd.c \ test-common.c api_knet_handle_get_channel_test_SOURCES = api_knet_handle_get_channel.c \ test-common.c api_knet_handle_get_datafd_test_SOURCES = api_knet_handle_get_datafd.c \ test-common.c api_knet_handle_get_transport_list_test_SOURCES = api_knet_handle_get_transport_list.c \ test-common.c api_knet_handle_get_transport_name_by_id_test_SOURCES = api_knet_handle_get_transport_name_by_id.c \ test-common.c api_knet_handle_get_transport_id_by_name_test_SOURCES = api_knet_handle_get_transport_id_by_name.c \ test-common.c api_knet_handle_set_transport_reconnect_interval_test_SOURCES = api_knet_handle_set_transport_reconnect_interval.c \ test-common.c api_knet_handle_get_transport_reconnect_interval_test_SOURCES = api_knet_handle_get_transport_reconnect_interval.c \ test-common.c api_knet_recv_test_SOURCES = api_knet_recv.c \ test-common.c api_knet_send_test_SOURCES = api_knet_send.c \ test-common.c +api_knet_send_compress_test_SOURCES = api_knet_send_compress.c \ + ../compress.c \ + ../logging.c \ + ../compress_zlib.c \ + ../compress_lz4.c \ + test-common.c + api_knet_send_loopback_test_SOURCES = api_knet_send_loopback.c \ test-common.c api_knet_send_sync_test_SOURCES = api_knet_send_sync.c \ test-common.c api_knet_handle_pmtud_setfreq_test_SOURCES = api_knet_handle_pmtud_setfreq.c \ test-common.c api_knet_handle_pmtud_getfreq_test_SOURCES = api_knet_handle_pmtud_getfreq.c \ test-common.c api_knet_handle_enable_pmtud_notify_test_SOURCES = api_knet_handle_enable_pmtud_notify.c \ test-common.c api_knet_handle_pmtud_get_test_SOURCES = api_knet_handle_pmtud_get.c \ test-common.c api_knet_host_add_test_SOURCES = api_knet_host_add.c \ test-common.c api_knet_host_remove_test_SOURCES = api_knet_host_remove.c \ test-common.c api_knet_host_set_name_test_SOURCES = api_knet_host_set_name.c \ test-common.c api_knet_host_get_name_by_host_id_test_SOURCES = api_knet_host_get_name_by_host_id.c \ test-common.c api_knet_host_get_id_by_host_name_test_SOURCES = api_knet_host_get_id_by_host_name.c \ test-common.c api_knet_host_get_host_list_test_SOURCES = api_knet_host_get_host_list.c \ test-common.c api_knet_host_set_policy_test_SOURCES = api_knet_host_set_policy.c \ test-common.c api_knet_host_get_policy_test_SOURCES = api_knet_host_get_policy.c \ test-common.c api_knet_host_get_status_test_SOURCES = api_knet_host_get_status.c \ test-common.c api_knet_host_enable_status_change_notify_test_SOURCES = api_knet_host_enable_status_change_notify.c \ test-common.c api_knet_log_get_subsystem_name_test_SOURCES = api_knet_log_get_subsystem_name.c \ test-common.c api_knet_log_get_subsystem_id_test_SOURCES = api_knet_log_get_subsystem_id.c \ test-common.c api_knet_log_get_loglevel_name_test_SOURCES = api_knet_log_get_loglevel_name.c \ test-common.c api_knet_log_get_loglevel_id_test_SOURCES = api_knet_log_get_loglevel_id.c \ test-common.c api_knet_log_set_loglevel_test_SOURCES = api_knet_log_set_loglevel.c \ test-common.c api_knet_log_get_loglevel_test_SOURCES = api_knet_log_get_loglevel.c \ test-common.c api_knet_strtoaddr_test_SOURCES = api_knet_strtoaddr.c api_knet_addrtostr_test_SOURCES = api_knet_addrtostr.c api_knet_link_set_config_test_SOURCES = api_knet_link_set_config.c \ test-common.c api_knet_link_clear_config_test_SOURCES = api_knet_link_clear_config.c \ test-common.c api_knet_link_get_config_test_SOURCES = api_knet_link_get_config.c \ test-common.c api_knet_link_set_ping_timers_test_SOURCES = api_knet_link_set_ping_timers.c \ test-common.c api_knet_link_get_ping_timers_test_SOURCES = api_knet_link_get_ping_timers.c \ test-common.c api_knet_link_set_pong_count_test_SOURCES = api_knet_link_set_pong_count.c \ test-common.c api_knet_link_get_pong_count_test_SOURCES = api_knet_link_get_pong_count.c \ test-common.c api_knet_link_set_priority_test_SOURCES = api_knet_link_set_priority.c \ test-common.c api_knet_link_get_priority_test_SOURCES = api_knet_link_get_priority.c \ test-common.c api_knet_link_set_enable_test_SOURCES = api_knet_link_set_enable.c \ test-common.c api_knet_link_get_enable_test_SOURCES = api_knet_link_get_enable.c \ test-common.c api_knet_link_get_link_list_test_SOURCES = api_knet_link_get_link_list.c \ test-common.c api_knet_link_get_status_test_SOURCES = api_knet_link_get_status.c \ test-common.c diff --git a/libknet/tests/api_knet_send_compress.c b/libknet/tests/api_knet_send_compress.c new file mode 100644 index 00000000..12400dd5 --- /dev/null +++ b/libknet/tests/api_knet_send_compress.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. All rights reserved. + * + * Authors: Fabio M. Di Nitto + * + * This software licensed under GPL-2.0+, LGPL-2.0+ + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "libknet.h" + +#include "compress.h" +#include "internals.h" +#include "netutils.h" +#include "test-common.h" + +static int private_data; + +static void sock_notify(void *pvt_data, + int datafd, + int8_t channel, + uint8_t tx_rx, + int error, + int errorno) +{ + return; +} + +static void test(const char *model) +{ + knet_handle_t knet_h; + int logfds[2]; + int datafd = 0; + int8_t channel = 0; + //struct knet_link_status link_status; + char send_buff[KNET_MAX_PACKET_SIZE]; + char recv_buff[KNET_MAX_PACKET_SIZE]; + ssize_t send_len = 0; + int recv_len = 0; + int savederrno; + struct sockaddr_storage lo; + struct knet_handle_compress_cfg knet_handle_compress_cfg; + + memset(&lo, 0, sizeof(struct sockaddr_storage)); + + if (knet_strtoaddr("127.0.0.1", "50000", &lo, sizeof(struct sockaddr_storage)) < 0) { + printf("Unable to convert loopback to sockaddr: %s\n", strerror(errno)); + exit(FAIL); + } + + memset(send_buff, 0, sizeof(send_buff)); + + setup_logpipes(logfds); + + knet_h = knet_handle_new(1, logfds[1], KNET_LOG_DEBUG); + + if (!knet_h) { + printf("knet_handle_new failed: %s\n", strerror(errno)); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + flush_logs(logfds[0], stdout); + + printf("Test knet_send with %s and valid data\n", model); + + memset(&knet_handle_compress_cfg, 0, sizeof(struct knet_handle_compress_cfg)); + strncpy(knet_handle_compress_cfg.compress_model, model, sizeof(knet_handle_compress_cfg.compress_model) - 1); + knet_handle_compress_cfg.compress_level = 4; + knet_handle_compress_cfg.compress_threshold = 0; + + if (knet_handle_compress(knet_h, &knet_handle_compress_cfg) < 0) { + printf("knet_handle_compress did not accept zlib compress mode with compress level 1 cfg\n"); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (knet_handle_enable_sock_notify(knet_h, &private_data, sock_notify) < 0) { + printf("knet_handle_enable_sock_notify failed: %s\n", strerror(errno)); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + datafd = 0; + channel = -1; + + if (knet_handle_add_datafd(knet_h, &datafd, &channel) < 0) { + printf("knet_handle_add_datafd failed: %s\n", strerror(errno)); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (knet_host_add(knet_h, 1) < 0) { + printf("knet_host_add failed: %s\n", strerror(errno)); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (knet_link_set_config(knet_h, 1, 0, KNET_TRANSPORT_UDP, &lo, &lo, 0) < 0) { + printf("Unable to configure link: %s\n", strerror(errno)); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (knet_link_set_enable(knet_h, 1, 0, 1) < 0) { + printf("knet_link_set_enable failed: %s\n", strerror(errno)); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (knet_handle_setfwd(knet_h, 1) < 0) { + printf("knet_handle_setfwd failed: %s\n", strerror(errno)); + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + while(knet_h->host_index[1]->status.reachable != 1) { + printf("waiting host to be reachable\n"); + sleep(1); + } + + send_len = knet_send(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel); + if (send_len <= 0) { + printf("knet_send failed: %s\n", strerror(errno)); + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (send_len != sizeof(send_buff)) { + printf("knet_send sent only %zd bytes: %s\n", send_len, strerror(errno)); + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + flush_logs(logfds[0], stdout); + + sleep(1); + + recv_len = knet_recv(knet_h, recv_buff, KNET_MAX_PACKET_SIZE, channel); + savederrno = errno; + if (recv_len != send_len) { + printf("knet_recv received only %d bytes: %s (errno: %d)\n", recv_len, strerror(errno), errno); + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + if ((is_helgrind()) && (recv_len == -1) && (savederrno == EAGAIN)) { + printf("helgrind exception. this is normal due to possible timeouts\n"); + exit(PASS); + } + exit(FAIL); + } + + if (memcmp(recv_buff, send_buff, KNET_MAX_PACKET_SIZE)) { + printf("recv and send buffers are different!\n"); + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + +#if 0 + /* A sanity check on the stats */ + if (knet_link_get_status(knet_h, 1, 0, &link_status, sizeof(link_status)) < 0) { + printf("knet_link_get_status failed: %s\n", strerror(errno)); + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); + exit(FAIL); + } + + if (link_status.stats.tx_data_packets != 2 || + link_status.stats.rx_data_packets != 2 || + link_status.stats.tx_data_bytes < KNET_MAX_PACKET_SIZE || + link_status.stats.rx_data_bytes < KNET_MAX_PACKET_SIZE || + link_status.stats.tx_data_bytes > KNET_MAX_PACKET_SIZE*2 || + link_status.stats.rx_data_bytes > KNET_MAX_PACKET_SIZE*2) { + printf("stats look wrong: tx_packets: %lu (%lu bytes), rx_packets: %lu (%lu bytes)\n", + link_status.stats.tx_data_packets, + link_status.stats.tx_data_bytes, + link_status.stats.rx_data_packets, + link_status.stats.rx_data_bytes); + } + + flush_logs(logfds[0], stdout); +#endif + + knet_link_set_enable(knet_h, 1, 0, 0); + knet_link_clear_config(knet_h, 1, 0); + knet_host_remove(knet_h, 1); + knet_handle_free(knet_h); + flush_logs(logfds[0], stdout); + close_logpipes(logfds); +} + +int main(int argc, char *argv[]) +{ + int idx = 0; + const char *model = NULL; + + need_root(); + + while ((model = get_model_by_idx(idx)) != NULL) { + test(model); + idx++; + } + + return PASS; +}