diff --git a/kronosnetd/cfg.h b/kronosnetd/cfg.h index e57ddc20..dfaf847a 100644 --- a/kronosnetd/cfg.h +++ b/kronosnetd/cfg.h @@ -1,55 +1,55 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __CFG_H__ #define __CFG_H__ #include #include #include "libtap.h" #include "libknet.h" #define KNET_RING_DEFPORT 50000 struct knet_cfg_eth { tap_t tap; int auto_mtu; - uint8_t node_id; + knet_node_id_t node_id; }; struct knet_cfg_ring { knet_handle_t knet_h; int data_mtu; int base_port; }; struct knet_cfg { struct knet_cfg_eth cfg_eth; struct knet_cfg_ring cfg_ring; int active; struct knet_handle_crypto_cfg knet_handle_crypto_cfg; struct knet_cfg *next; }; struct knet_cfg_top { char *conffile; char *logfile; char *vty_ipv4; char *vty_ipv6; char *vty_port; struct knet_cfg *knet_cfg; }; struct knet_cfg *knet_get_iface(const char *name, const int create); void knet_destroy_iface(struct knet_cfg *knet_iface); extern struct knet_cfg_top knet_cfg_head; #endif diff --git a/kronosnetd/etherfilter.c b/kronosnetd/etherfilter.c index d31b8e23..db91a801 100644 --- a/kronosnetd/etherfilter.c +++ b/kronosnetd/etherfilter.c @@ -1,64 +1,64 @@ /* * Copyright (C) 2010-2015 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 "etherfilter.h" /* * stole from linux kernel/include/linux/etherdevice.h */ static inline int is_zero_ether_addr(const uint8_t *addr) { return !(addr[0] | addr[1] | addr[2] | addr[3] | addr[4] | addr[5]); } static inline int is_multicast_ether_addr(const uint8_t *addr) { return 0x01 & addr[0]; } static inline int is_broadcast_ether_addr(const uint8_t *addr) { return (addr[0] & addr[1] & addr[2] & addr[3] & addr[4] & addr[5]) == 0xff; } int ether_host_filter_fn (void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_host_id, + knet_node_id_t this_host_id, + knet_node_id_t src_host_id, int8_t *channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { struct ether_header *eth_h = (struct ether_header *)outdata; uint8_t *dst_mac = (uint8_t *)eth_h->ether_dhost; uint16_t dst_host_id; if (is_zero_ether_addr(dst_mac)) return -1; if (is_multicast_ether_addr(dst_mac) || is_broadcast_ether_addr(dst_mac)) { return 1; } memmove(&dst_host_id, &dst_mac[4], 2); dst_host_ids[0] = ntohs(dst_host_id); *dst_host_ids_entries = 1; return 0; } diff --git a/kronosnetd/etherfilter.h b/kronosnetd/etherfilter.h index 1bb5b0b0..d78b7627 100644 --- a/kronosnetd/etherfilter.h +++ b/kronosnetd/etherfilter.h @@ -1,24 +1,25 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Author: Fabio M. Di Nitto * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __ETHERFILTER_H__ #define __ETHERFILTER_H__ #include +#include "libknet.h" int ether_host_filter_fn (void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_host_id, + knet_node_id_t this_host_id, + knet_node_id_t src_host_id, int8_t *channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries); #endif diff --git a/kronosnetd/vty.h b/kronosnetd/vty.h index 9fb7ad6d..5f67b42c 100644 --- a/kronosnetd/vty.h +++ b/kronosnetd/vty.h @@ -1,73 +1,74 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __VTY_H__ #define __VTY_H__ #include #include #include +#include "libknet.h" #define KNET_VTY_DEFAULT_PORT 50000 #define KNET_VTY_DEFAULT_MAX_CONN 4 #define KNET_VTY_TOTAL_MAX_CONN 16 #define KNET_VTY_CLI_TIMEOUT 300 #define KNET_VTY_MAX_LINE 512 #define KNET_VTY_MAX_HIST 50 struct knet_vty_global_conf { int idle_timeout; }; struct knet_vty { pthread_t vty_thread; /* thread struct for this vty */ struct sockaddr_storage src_sa; /* source IP */ socklen_t src_sa_len; /* sa len */ char ip[128]; /* ip addr of source */ char username[64]; /* username */ char line[KNET_VTY_MAX_LINE]; /* input line */ char *history[KNET_VTY_MAX_HIST]; /* history */ int history_idx; /* index to history */ int history_pos; /* position in the history */ int insert_mode; /* add or insert */ int line_idx; /* index on the input line */ int cursor_pos; /* position of the cursor in the line */ int escape; /* escape status */ int escape_code; /* escape code buffer */ int user_can_enable;/* user is in group kronosnetadm */ int vty_sock; /* tcp socket for this vty */ int conn_num; /* vty number */ int active; /* vty is active */ int got_epipe; /* vty_sock has been closed */ int idle; /* idle time */ int idle_timeout; /* in seconds or 0 to disable automatic logout */ int node; /* node number of the menus */ int prevnode; /* node number of the menus (used by VTY node) */ void *param; /* pointer to cmd param */ int paramoffset; /* required if param is set */ int logfd; /* fd to pass to iface create */ int loglevel; /* loglevel (debug, etc) */ void *iface; /* pointer to iface we are working on */ - uint8_t host_id; /* peer/host we are working on */ + knet_node_id_t host_id; /* peer/host we are working on */ uint8_t link_id; /* link id we are working on */ int filemode; /* tell print_conf to add or not carriage return */ struct knet_vty_global_conf *vty_global_conf; /* pointer to vty global config */ }; extern pthread_mutex_t knet_vty_mutex; extern int knet_vty_config; extern struct knet_vty knet_vtys[KNET_VTY_TOTAL_MAX_CONN]; int knet_vty_main_loop(int debug); #endif diff --git a/kronosnetd/vty_cli_cmds.c b/kronosnetd/vty_cli_cmds.c index ec037331..63146e55 100644 --- a/kronosnetd/vty_cli_cmds.c +++ b/kronosnetd/vty_cli_cmds.c @@ -1,2185 +1,2185 @@ /* * Copyright (C) 2010-2015 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 #include "cfg.h" #include "etherfilter.h" #include "logging.h" #include "libtap.h" #include "netutils.h" #include "vty.h" #include "vty_cli.h" #include "vty_cli_cmds.h" #include "vty_utils.h" #define KNET_VTY_MAX_MATCHES 64 #define KNET_VTY_MATCH_HELP 0 #define KNET_VTY_MATCH_EXEC 1 #define KNET_VTY_MATCH_EXPAND 2 #define CMDS_PARAM_NOMORE 0 #define CMDS_PARAM_KNET 1 #define CMDS_PARAM_IP 2 #define CMDS_PARAM_IP_PREFIX 3 #define CMDS_PARAM_IP_PORT 4 #define CMDS_PARAM_BOOL 5 #define CMDS_PARAM_INT 6 #define CMDS_PARAM_NODEID 7 #define CMDS_PARAM_NAME 8 #define CMDS_PARAM_MTU 9 #define CMDS_PARAM_CRYPTO_MODEL 10 #define CMDS_PARAM_CRYPTO_TYPE 11 #define CMDS_PARAM_HASH_TYPE 12 #define CMDS_PARAM_POLICY 13 #define CMDS_PARAM_LINK_ID 14 #define CMDS_PARAM_LINK_PRI 15 #define CMDS_PARAM_LINK_KEEPAL 16 #define CMDS_PARAM_LINK_HOLDTI 17 #define CMDS_PARAM_LINK_PONG 18 #define CMDS_PARAM_VTY_TIMEOUT 19 #define CMDS_PARAM_PMTU_FREQ 20 #define CMDS_PARAM_LINK_TRANSP 21 /* * CLI helper functions - menu/node stuff starts below */ /* * return 0 if we find a command in vty->line and cmd/len/no are set * return -1 if we cannot find a command. no can be trusted. cmd/len would be empty */ static int get_command(struct knet_vty *vty, char **cmd, int *cmdlen, int *cmdoffset, int *no) { int start = 0, idx; for (idx = 0; idx < vty->line_idx; idx++) { if (vty->line[idx] != ' ') break; } if (!strncmp(&vty->line[idx], "no ", 3)) { *no = 1; idx = idx + 3; for (; idx < vty->line_idx; idx++) { if (vty->line[idx] != ' ') break; } } else { *no = 0; } start = idx; if (start == vty->line_idx) return -1; *cmd = &vty->line[start]; *cmdoffset = start; for (idx = start; idx < vty->line_idx; idx++) { if (vty->line[idx] == ' ') break; } *cmdlen = idx - start; return 0; } /* * still not sure why I need to count backwards... */ static void get_n_word_from_end(struct knet_vty *vty, int n, char **word, int *wlen, int *woffset) { int widx; int idx, end, start; start = end = vty->line_idx; for (widx = 0; widx < n; widx++) { for (idx = start - 1; idx > 0; idx--) { if (vty->line[idx] != ' ') break; } end = idx; for (idx = end; idx > 0; idx--) { if (vty->line[idx-1] == ' ') break; } start = idx; } *wlen = (end - start) + 1; *word = &vty->line[start]; *woffset = start; } static int expected_params(const vty_param_t *params) { int idx = 0; while(params[idx].param != CMDS_PARAM_NOMORE) idx++; return idx; } static int count_words(struct knet_vty *vty, int offset) { int idx, widx = 0; int status = 0; for (idx = offset; idx < vty->line_idx; idx++) { if (vty->line[idx] == ' ') { status = 0; continue; } if ((vty->line[idx] != ' ') && (!status)) { widx++; status = 1; continue; } } return widx; } static int param_to_int(const char *param, int paramlen) { char buf[KNET_VTY_MAX_LINE]; memset(buf, 0, sizeof(buf)); memmove(buf, param, paramlen); return atoi(buf); } static int param_to_str(char *buf, int bufsize, const char *param, int paramlen) { if (bufsize < paramlen) return -1; memset(buf, 0, bufsize); memmove(buf, param, paramlen); return paramlen; } static const vty_node_cmds_t *get_cmds(struct knet_vty *vty, char **cmd, int *cmdlen, int *cmdoffset) { int no; const vty_node_cmds_t *cmds = knet_vty_nodes[vty->node].cmds; get_command(vty, cmd, cmdlen, cmdoffset, &no); if (no) cmds = knet_vty_nodes[vty->node].no_cmds; return cmds; } static int check_param(struct knet_vty *vty, const int paramtype, char *param, int paramlen) { int err = 0; char buf[KNET_VTY_MAX_LINE]; int tmp; struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; memset(buf, 0, sizeof(buf)); switch(paramtype) { case CMDS_PARAM_NOMORE: break; case CMDS_PARAM_KNET: if (paramlen >= IFNAMSIZ) { knet_vty_write(vty, "interface name too long%s", telnet_newline); err = -1; } break; case CMDS_PARAM_IP: break; case CMDS_PARAM_IP_PREFIX: break; case CMDS_PARAM_IP_PORT: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 65279)) { knet_vty_write(vty, "port number must be a value between 0 and 65279%s", telnet_newline); err = -1; } break; case CMDS_PARAM_BOOL: break; case CMDS_PARAM_INT: break; case CMDS_PARAM_NODEID: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 255)) { knet_vty_write(vty, "node id must be a value between 0 and 255%s", telnet_newline); err = -1; } break; case CMDS_PARAM_NAME: if (paramlen >= KNET_MAX_HOST_LEN) { knet_vty_write(vty, "name cannot exceed %d char in len%s", KNET_MAX_HOST_LEN - 1, telnet_newline); } break; case CMDS_PARAM_MTU: tmp = param_to_int(param, paramlen); if ((tmp < 576) || (tmp > 65536)) { knet_vty_write(vty, "mtu should be a value between 576 and 65536 (note: max value depends on the media)%s", telnet_newline); err = -1; } break; case CMDS_PARAM_PMTU_FREQ: tmp = param_to_int(param, paramlen); if ((tmp < 5) || (tmp > 600)) { knet_vty_write(vty, "PMTUd frequency should be a value between 5 and 600%s", telnet_newline); err = -1; } break; case CMDS_PARAM_CRYPTO_MODEL: param_to_str(buf, KNET_VTY_MAX_LINE, param, paramlen); if (!strncmp("none", buf, 4)) break; if (!strncmp("nss", buf, 3)) break; knet_vty_write(vty, "unknown encryption model: %s. Supported: none/nss%s", param, telnet_newline); err = -1; break; case CMDS_PARAM_CRYPTO_TYPE: param_to_str(buf, KNET_VTY_MAX_LINE, param, paramlen); if (!strncmp("none", buf, 4)) break; if (!strncmp("aes256", buf, 6)) break; if (!strncmp("aes192", buf, 6)) break; if (!strncmp("aes128", buf, 6)) break; if (!strncmp("3des", buf, 4)) break; knet_vty_write(vty, "unknown encryption method: %s. Supported: none/aes256/aes192/aes128/3des%s", param, telnet_newline); err = -1; break; case CMDS_PARAM_HASH_TYPE: param_to_str(buf, KNET_VTY_MAX_LINE, param, paramlen); if (!strncmp("none", buf, 4)) break; if (!strncmp("md5", buf, 3)) break; if (!strncmp("sha1", buf, 4)) break; if (!strncmp("sha256", buf, 6)) break; if (!strncmp("sha384", buf, 6)) break; if (!strncmp("sha512", buf, 6)) break; knet_vty_write(vty, "unknown hash method: %s. Supported none/md5/sha1/sha256/sha384/sha512%s", param, telnet_newline); err = -1; break; case CMDS_PARAM_POLICY: param_to_str(buf, KNET_VTY_MAX_LINE, param, paramlen); if (!strncmp("passive", buf, 7)) break; if (!strncmp("active", buf, 6)) break; if (!strncmp("round-robin", buf, 11)) break; knet_vty_write(vty, "unknown switching policy: %s. Supported passive/active/round-robin%s", param, telnet_newline); err = -1; break; case CMDS_PARAM_LINK_ID: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 7)) { knet_vty_write(vty, "link id should be a value between 0 and 7%s", telnet_newline); err = -1; } break; case CMDS_PARAM_LINK_TRANSP: param_to_str(buf, KNET_VTY_MAX_LINE, param, paramlen); if (knet_handle_get_transport_id_by_name(knet_iface->cfg_ring.knet_h, buf) == KNET_MAX_TRANSPORTS) { knet_vty_write(vty, "link transport is invalid%s", telnet_newline); err = -1; } break; case CMDS_PARAM_LINK_PRI: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 255)) { knet_vty_write(vty, "link priority should be a value between 0 and 256%s", telnet_newline); err = -1; } break; case CMDS_PARAM_LINK_KEEPAL: tmp = param_to_int(param, paramlen); if ((tmp <= 0) || (tmp > 60000)) { knet_vty_write(vty, "link keepalive should be a value between 0 and 60000 (milliseconds). Default: 1000%s", telnet_newline); err = -1; } break; case CMDS_PARAM_LINK_HOLDTI: tmp = param_to_int(param, paramlen); if ((tmp <= 0) || (tmp > 60000)) { knet_vty_write(vty, "link holdtimer should be a value between 0 and 60000 (milliseconds). Default: 5000%s", telnet_newline); err = -1; } break; case CMDS_PARAM_LINK_PONG: tmp = param_to_int(param, paramlen); if (tmp < 1) { knet_vty_write(vty, "pong_count must be a value between 0 and 255%s", telnet_newline); err = -1; } break; case CMDS_PARAM_VTY_TIMEOUT: tmp = param_to_int(param, paramlen); if ((tmp < 0) || (tmp > 3600)) { knet_vty_write(vty, "vty logout timeout should be a value between 0 (disabled) and 3600 seconds. Default: %d%s", KNET_VTY_CLI_TIMEOUT, telnet_newline); } break; default: knet_vty_write(vty, "CLI ERROR: unknown parameter type%s", telnet_newline); err = -1; break; } return err; } static void describe_param(struct knet_vty *vty, const int paramtype) { switch(paramtype) { case CMDS_PARAM_NOMORE: knet_vty_write(vty, "no more parameters%s", telnet_newline); break; case CMDS_PARAM_KNET: knet_vty_write(vty, "KNET_IFACE_NAME - interface name (max %d chars) eg: kronosnet0%s", IFNAMSIZ, telnet_newline); break; case CMDS_PARAM_IP: knet_vty_write(vty, "IP address - ipv4 or ipv6 address to add/remove%s", telnet_newline); break; case CMDS_PARAM_IP_PREFIX: knet_vty_write(vty, "IP prefix len (eg. 24, 64)%s", telnet_newline); break; case CMDS_PARAM_IP_PORT: knet_vty_write(vty, "base port (eg: %d) %s", KNET_RING_DEFPORT, telnet_newline); case CMDS_PARAM_BOOL: break; case CMDS_PARAM_INT: break; case CMDS_PARAM_NODEID: knet_vty_write(vty, "NODEID - unique identifier for this interface in this kronos network (value between 0 and 255)%s", telnet_newline); break; case CMDS_PARAM_NAME: knet_vty_write(vty, "NAME - unique name identifier for this entity (max %d chars)%s", KNET_MAX_HOST_LEN - 1, telnet_newline); break; case CMDS_PARAM_MTU: knet_vty_write(vty, "MTU - a value between 576 and 65536 (note: max value depends on the media)%s", telnet_newline); break; case CMDS_PARAM_PMTU_FREQ: knet_vty_write(vty, "PMTUd frequency - a value in seconds between 5 and 600 (default: 5)%s", telnet_newline); break; case CMDS_PARAM_CRYPTO_MODEL: knet_vty_write(vty, "MODEL - define encryption backend: none or nss%s", telnet_newline); break; case CMDS_PARAM_CRYPTO_TYPE: knet_vty_write(vty, "CRYPTO - define packets encryption method: none or aes256%s", telnet_newline); break; case CMDS_PARAM_HASH_TYPE: knet_vty_write(vty, "HASH - define packets hashing method: none/md5/sha1/sha256/sha384/sha512%s", telnet_newline); break; case CMDS_PARAM_POLICY: knet_vty_write(vty, "POLICY - define packets switching policy: passive/active/round-robin%s", telnet_newline); break; case CMDS_PARAM_LINK_ID: knet_vty_write(vty, "LINKID - specify the link identification number (0-7)%s", telnet_newline); break; case CMDS_PARAM_LINK_TRANSP: knet_vty_write(vty, "TRANSPORT - specify the link transport protocol (UDP/SCTP/..)%s", telnet_newline); break; case CMDS_PARAM_LINK_PRI: knet_vty_write(vty, "PRIORITY - specify the link priority for passive switching (0 to 255, default is 0). The higher value is preferred over lower value%s", telnet_newline); break; case CMDS_PARAM_LINK_KEEPAL: knet_vty_write(vty, "KEEPALIVE - specify the keepalive interval for this link (0 to 60000 milliseconds, default is 1000).%s", telnet_newline); break; case CMDS_PARAM_LINK_HOLDTI: knet_vty_write(vty, "HOLDTIME - specify how much time has to pass without connection before a link is considered dead (0 to 60000 milliseconds, default is 5000).%s", telnet_newline); break; case CMDS_PARAM_VTY_TIMEOUT: knet_vty_write(vty, "VTY_TIMEOUT - specify the number of seconds before a session is automatically closed.%s", telnet_newline); break; default: /* this should never happen */ knet_vty_write(vty, "CLI ERROR: unknown parameter type%s", telnet_newline); break; } } static void print_help(struct knet_vty *vty, const vty_node_cmds_t *cmds, int idx) { if ((idx < 0) || (cmds == NULL) || (cmds[idx].cmd == NULL)) return; if (cmds[idx].help != NULL) { knet_vty_write(vty, "%s\t%s%s", cmds[idx].cmd, cmds[idx].help, telnet_newline); } else { knet_vty_write(vty, "%s\tNo help available for this command%s", cmds[idx].cmd, telnet_newline); } } static int get_param(struct knet_vty *vty, int wanted_paranum, char **param, int *paramlen, int *paramoffset) { int eparams, tparams; const vty_param_t *params = (const vty_param_t *)vty->param; int paramstart = vty->paramoffset; eparams = expected_params(params); tparams = count_words(vty, paramstart); if (tparams > eparams) return -1; if (wanted_paranum == -1) { get_n_word_from_end(vty, 1, param, paramlen, paramoffset); return tparams; } if (tparams < wanted_paranum) return -1; get_n_word_from_end(vty, (tparams - wanted_paranum) + 1, param, paramlen, paramoffset); return tparams - wanted_paranum; } static int match_command(struct knet_vty *vty, const vty_node_cmds_t *cmds, char *cmd, int cmdlen, int cmdoffset, int mode) { int idx = 0, found = -1, paramoffset = 0, paramlen = 0, last_param = 0; char *param = NULL; int paramstart = cmdlen + cmdoffset; int matches[KNET_VTY_MAX_MATCHES]; memset(&matches, -1, sizeof(matches)); while ((cmds[idx].cmd != NULL) && (idx < KNET_VTY_MAX_MATCHES)) { if (!strncmp(cmds[idx].cmd, cmd, cmdlen)) { found++; matches[found] = idx; } idx++; } if (idx >= KNET_VTY_MAX_MATCHES) { knet_vty_write(vty, "Too many matches for this command%s", telnet_newline); return -1; } if (found < 0) { knet_vty_write(vty, "There is no such command%s", telnet_newline); return -1; } switch(mode) { case KNET_VTY_MATCH_HELP: if (found == 0) { if ((cmdoffset <= vty->cursor_pos) && (vty->cursor_pos <= paramstart)) { print_help(vty, cmds, matches[0]); break; } if (cmds[matches[0]].params != NULL) { vty->param = (void *)cmds[matches[0]].params; vty->paramoffset = paramstart; last_param = get_param(vty, -1, ¶m, ¶mlen, ¶moffset); if ((paramoffset <= vty->cursor_pos) && (vty->cursor_pos <= (paramoffset + paramlen))) last_param--; if (last_param >= CMDS_PARAM_NOMORE) { describe_param(vty, cmds[matches[0]].params[last_param].param); if (paramoffset > 0) check_param(vty, cmds[matches[0]].params[last_param].param, param, paramlen); } break; } } if (found >= 0) { idx = 0; while (matches[idx] >= 0) { print_help(vty, cmds, matches[idx]); idx++; } } break; case KNET_VTY_MATCH_EXEC: if (found == 0) { int exec = 0; if (cmds[matches[0]].params != NULL) { int eparams, tparams; eparams = expected_params(cmds[matches[0]].params); tparams = count_words(vty, paramstart); if (eparams != tparams) { exec = -1; idx = 0; knet_vty_write(vty, "Parameter required for this command:%s", telnet_newline); while(cmds[matches[0]].params[idx].param != CMDS_PARAM_NOMORE) { describe_param(vty, cmds[matches[0]].params[idx].param); idx++; } break; } idx = 0; vty->param = (void *)cmds[matches[0]].params; vty->paramoffset = paramstart; while(cmds[matches[0]].params[idx].param != CMDS_PARAM_NOMORE) { get_param(vty, idx + 1, ¶m, ¶mlen, ¶moffset); if (check_param(vty, cmds[matches[0]].params[idx].param, param, paramlen) < 0) { exec = -1; if (vty->filemode) return -1; } idx++; } } if (!exec) { if (cmds[matches[0]].params != NULL) { vty->param = (void *)cmds[matches[0]].params; vty->paramoffset = paramstart; } if (cmds[matches[0]].func != NULL) { return cmds[matches[0]].func(vty); } else { /* this will eventually disappear */ knet_vty_write(vty, "no fn associated to this command%s", telnet_newline); } } } if (found > 0) { knet_vty_write(vty, "Ambiguous command.%s", telnet_newline); } break; case KNET_VTY_MATCH_EXPAND: if (found == 0) { int cmdreallen; if (vty->cursor_pos > cmdoffset+cmdlen) /* complete param? */ break; cmdreallen = strlen(cmds[matches[0]].cmd); memset(vty->line + cmdoffset, 0, cmdlen); memmove(vty->line + cmdoffset, cmds[matches[0]].cmd, cmdreallen); vty->line[cmdreallen + cmdoffset] = ' '; vty->line_idx = cmdreallen + cmdoffset + 1; vty->cursor_pos = cmdreallen + cmdoffset + 1; } if (found > 0) { /* add completion to string base root */ int count = 0; idx = 0; while (matches[idx] >= 0) { knet_vty_write(vty, "%s\t\t", cmds[matches[idx]].cmd); idx++; count++; if (count == 4) { knet_vty_write(vty, "%s",telnet_newline); count = 0; } } knet_vty_write(vty, "%s",telnet_newline); } break; default: /* this should never really happen */ log_info("Unknown match mode"); break; } return found; } /* forward declarations */ /* common to almost all nodes */ static int knet_cmd_logout(struct knet_vty *vty); static int knet_cmd_who(struct knet_vty *vty); static int knet_cmd_exit_node(struct knet_vty *vty); static int knet_cmd_help(struct knet_vty *vty); /* root node */ static int knet_cmd_config(struct knet_vty *vty); /* config node */ static int knet_cmd_interface(struct knet_vty *vty); static int knet_cmd_no_interface(struct knet_vty *vty); static int knet_cmd_status(struct knet_vty *vty); static int knet_cmd_show_conf(struct knet_vty *vty); static int knet_cmd_write_conf(struct knet_vty *vty); /* interface node */ static int knet_cmd_mtu(struct knet_vty *vty); static int knet_cmd_no_mtu(struct knet_vty *vty); static int knet_cmd_ip(struct knet_vty *vty); static int knet_cmd_no_ip(struct knet_vty *vty); static int knet_cmd_peer(struct knet_vty *vty); static int knet_cmd_no_peer(struct knet_vty *vty); static int knet_cmd_start(struct knet_vty *vty); static int knet_cmd_stop(struct knet_vty *vty); static int knet_cmd_crypto(struct knet_vty *vty); static int knet_cmd_pmtufreq(struct knet_vty *vty); static int knet_cmd_no_pmtufreq(struct knet_vty *vty); /* peer node */ static int knet_cmd_link(struct knet_vty *vty); static int knet_cmd_no_link(struct knet_vty *vty); static int knet_cmd_switch_policy(struct knet_vty *vty); /* link node */ static int knet_cmd_link_pri(struct knet_vty *vty); static int knet_cmd_link_pong(struct knet_vty *vty); static int knet_cmd_link_timer(struct knet_vty *vty); /* vty node */ static int knet_cmd_vty(struct knet_vty *vty); static int knet_cmd_vty_timeout(struct knet_vty *vty); /* root node description */ vty_node_cmds_t root_cmds[] = { { "configure", "enter configuration mode", NULL, knet_cmd_config }, { "exit", "exit from CLI", NULL, knet_cmd_logout }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "status", "display current network status", NULL, knet_cmd_status }, { "vty", "enter vty configuration mode", NULL, knet_cmd_vty }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { NULL, NULL, NULL, NULL }, }; /* config node description */ vty_param_t no_int_params[] = { { CMDS_PARAM_KNET }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t no_config_cmds[] = { { "interface", "destroy kronosnet interface", no_int_params, knet_cmd_no_interface }, { NULL, NULL, NULL, NULL }, }; vty_param_t int_params[] = { { CMDS_PARAM_KNET }, { CMDS_PARAM_NODEID }, { CMDS_PARAM_IP_PORT }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t config_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "interface", "configure kronosnet interface", int_params, knet_cmd_interface }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "no", "revert command", NULL, NULL }, { "status", "display current network status", NULL, knet_cmd_status }, { "vty", "enter vty configuration mode", NULL, knet_cmd_vty }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* interface node description */ vty_param_t ip_params[] = { { CMDS_PARAM_IP }, { CMDS_PARAM_IP_PREFIX }, { CMDS_PARAM_NOMORE }, }; vty_param_t peer_params[] = { { CMDS_PARAM_NAME }, { CMDS_PARAM_NODEID }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t no_interface_cmds[] = { { "ip", "remove ip address", ip_params, knet_cmd_no_ip }, { "mtu", "revert to default MTU", NULL, knet_cmd_no_mtu }, { "pmtudfreq", "revert to default PMTUd frequency (default: 5)", NULL, knet_cmd_no_pmtufreq }, { "peer", "remove peer from this interface", peer_params, knet_cmd_no_peer }, { NULL, NULL, NULL, NULL }, }; vty_param_t mtu_params[] = { { CMDS_PARAM_MTU }, { CMDS_PARAM_NOMORE }, }; vty_param_t pmtu_params[] = { { CMDS_PARAM_PMTU_FREQ }, { CMDS_PARAM_NOMORE }, }; vty_param_t crypto_params[] = { { CMDS_PARAM_CRYPTO_MODEL }, { CMDS_PARAM_CRYPTO_TYPE }, { CMDS_PARAM_HASH_TYPE }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t interface_cmds[] = { { "crypto", "enable crypto/hmac", crypto_params, knet_cmd_crypto }, { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "ip", "add ip address", ip_params, knet_cmd_ip }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "mtu", "set mtu (default: auto)", mtu_params, knet_cmd_mtu }, { "pmtudfreq", "PMTUd frequency (default: 5)", pmtu_params, knet_cmd_pmtufreq }, { "no", "revert command", NULL, NULL }, { "peer", "add peer endpoint", peer_params, knet_cmd_peer }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "start", "start forwarding engine", NULL, knet_cmd_start }, { "status", "display current network status", NULL, knet_cmd_status }, { "stop", "stop forwarding engine", NULL, knet_cmd_stop }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* peer node description */ vty_param_t nolink_params[] = { { CMDS_PARAM_LINK_ID }, { CMDS_PARAM_NOMORE }, }; vty_param_t link_params[] = { { CMDS_PARAM_LINK_ID }, { CMDS_PARAM_IP }, { CMDS_PARAM_IP }, { CMDS_PARAM_LINK_TRANSP }, { CMDS_PARAM_NOMORE }, }; vty_param_t switch_params[] = { { CMDS_PARAM_POLICY }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t no_peer_cmds[] = { { "link", "remove peer endpoint", nolink_params, knet_cmd_no_link}, { NULL, NULL, NULL, NULL }, }; vty_node_cmds_t peer_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "link", "add peer endpoint", link_params, knet_cmd_link }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "no", "revert command", NULL, NULL }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "status", "display current network status", NULL, knet_cmd_status }, { "switch-policy", "configure switching policy engine", switch_params, knet_cmd_switch_policy }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* link node description */ vty_param_t link_pri_params[] = { { CMDS_PARAM_LINK_PRI }, { CMDS_PARAM_NOMORE }, }; vty_param_t link_timer_params[] = { { CMDS_PARAM_LINK_KEEPAL }, { CMDS_PARAM_LINK_HOLDTI }, { CMDS_PARAM_NOMORE }, }; vty_param_t pong_count_params[] = { { CMDS_PARAM_LINK_PONG }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t link_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "no", "revert command", NULL, NULL }, { "pong_count", "set number of pongs to be received before a link is considered alive", pong_count_params, knet_cmd_link_pong }, { "priority", "set priority of this link for passive switching", link_pri_params, knet_cmd_link_pri }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "status", "display current network status", NULL, knet_cmd_status }, { "timers", "set link keepalive and holdtime", link_timer_params, knet_cmd_link_timer }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; vty_param_t vty_timeout_params[] = { { CMDS_PARAM_VTY_TIMEOUT }, { CMDS_PARAM_NOMORE }, }; vty_node_cmds_t vty_cmds[] = { { "exit", "exit configuration mode", NULL, knet_cmd_exit_node }, { "help", "display basic help", NULL, knet_cmd_help }, { "logout", "exit from CLI", NULL, knet_cmd_logout }, { "show", "show running config", NULL, knet_cmd_show_conf }, { "status", "display current network status", NULL, knet_cmd_status }, { "timeout", "set number of seconds before session is automatically closed", vty_timeout_params, knet_cmd_vty_timeout }, { "who", "display users connected to CLI", NULL, knet_cmd_who }, { "write", "write current config to file", NULL, knet_cmd_write_conf }, { NULL, NULL, NULL, NULL }, }; /* nodes */ vty_nodes_t knet_vty_nodes[] = { { NODE_ROOT, "knet", root_cmds, NULL }, { NODE_CONFIG, "config", config_cmds, no_config_cmds }, { NODE_INTERFACE, "iface", interface_cmds, no_interface_cmds }, { NODE_PEER, "peer", peer_cmds, no_peer_cmds }, { NODE_LINK, "link", link_cmds, NULL }, { NODE_VTY, "vty", vty_cmds, NULL }, { -1, NULL, NULL }, }; /* command execution */ /* vty */ static int knet_cmd_vty_timeout(struct knet_vty *vty) { int paramlen = 0, paramoffset = 0, timeout; char *param = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); timeout = param_to_int(param, paramlen); if ((vty->filemode) || (vty->prevnode == NODE_CONFIG)) { vty->vty_global_conf->idle_timeout = timeout; } vty->idle_timeout = timeout; return 0; } static int knet_cmd_vty(struct knet_vty *vty) { vty->prevnode = vty->node; vty->node = NODE_VTY; return 0; } /* links */ static int knet_cmd_link_pong(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0; char *param = NULL; uint8_t pong_count; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); pong_count = param_to_int(param, paramlen); knet_link_set_pong_count(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, pong_count); return 0; } static int knet_cmd_link_timer(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0; char *param = NULL; time_t keepalive, holdtime; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); keepalive = param_to_int(param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); holdtime = param_to_int(param, paramlen); knet_link_set_ping_timers(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, keepalive, holdtime, 2048); return 0; } static int knet_cmd_link_pri(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0; char *param = NULL; uint8_t priority; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); priority = param_to_int(param, paramlen); if (knet_link_set_priority(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, priority)) { knet_vty_write(vty, "Error: unable to update link priority%s", telnet_newline); return -1; } return 0; } static int knet_cmd_no_link(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; struct knet_link_status status; int paramlen = 0, paramoffset = 0; char *param = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); vty->link_id = param_to_int(param, paramlen); knet_link_get_status(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, &status); if (status.enabled) { if (knet_link_set_enable(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, 0)) { knet_vty_write(vty, "Error: unable to update switching cache%s", telnet_newline); return -1; } knet_link_clear_config(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id); } return 0; } static int knet_cmd_link(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; struct knet_link_status status; int paramlen = 0, paramoffset = 0, err = 0; char *param = NULL; char src_ipaddr[KNET_MAX_HOST_LEN], src_port[KNET_MAX_PORT_LEN], dst_ipaddr[KNET_MAX_HOST_LEN], dst_port[KNET_MAX_PORT_LEN]; struct sockaddr_storage src_addr; struct sockaddr_storage dst_addr; struct sockaddr_storage *dst = NULL; char transport[10]; uint8_t transport_id; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); vty->link_id = param_to_int(param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); param_to_str(src_ipaddr, KNET_MAX_HOST_LEN, param, paramlen); memset(src_port, 0, sizeof(src_port)); snprintf(src_port, KNET_MAX_PORT_LEN, "%d", knet_iface->cfg_ring.base_port + vty->host_id); get_param(vty, 3, ¶m, ¶mlen, ¶moffset); param_to_str(dst_ipaddr, KNET_MAX_HOST_LEN, param, paramlen); memset(dst_port, 0, sizeof(dst_port)); snprintf(dst_port, KNET_MAX_PORT_LEN, "%d", knet_iface->cfg_ring.base_port + knet_iface->cfg_eth.node_id); get_param(vty, 4, ¶m, ¶mlen, ¶moffset); param_to_str(transport, sizeof(transport), param, paramlen); transport_id = knet_handle_get_transport_id_by_name(knet_iface->cfg_ring.knet_h, transport); knet_link_get_status(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, &status); if (!status.enabled) { if (knet_strtoaddr(src_ipaddr, src_port, &src_addr, sizeof(struct sockaddr_storage)) != 0) { knet_vty_write(vty, "Error: unable to convert source ip addr to sockaddr!%s", telnet_newline); err = -1; goto out_clean; } if (!strncmp(dst_ipaddr, "dynamic", 7)) { dst = NULL; } else { if (knet_strtoaddr(dst_ipaddr, dst_port, &dst_addr, sizeof(struct sockaddr_storage)) != 0) { knet_vty_write(vty, "Error: unable to convert destination ip addr to sockaddr!%s", telnet_newline); err = -1; goto out_clean; } dst = &dst_addr; } knet_link_set_config(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, transport_id, &src_addr, dst); knet_link_set_ping_timers(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, 1000, 5000, 2048); knet_link_set_enable(knet_iface->cfg_ring.knet_h, vty->host_id, vty->link_id, 1); } vty->node = NODE_LINK; out_clean: return err; } static int knet_cmd_switch_policy(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, err = 0; char *param = NULL; char policystr[16]; int policy = -1; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(policystr, sizeof(policystr), param, paramlen); if (!strncmp("passive", policystr, 7)) policy = KNET_LINK_POLICY_PASSIVE; if (!strncmp("active", policystr, 6)) policy = KNET_LINK_POLICY_ACTIVE; if (!strncmp("round-robin", policystr, 11)) policy = KNET_LINK_POLICY_RR; if (policy < 0) { knet_vty_write(vty, "Error: unknown switching policy method%s", telnet_newline); return -1; } err = knet_host_set_policy(knet_iface->cfg_ring.knet_h, vty->host_id, policy); if (err) knet_vty_write(vty, "Error: unable to set switching policy to %s%s", policystr, telnet_newline); return err; } /* * -1 on internal error * 0 host does not exist * 1 host exists */ -static int knet_find_host(struct knet_vty *vty, const char *nodename, const uint8_t requested_node_id) +static int knet_find_host(struct knet_vty *vty, const char *nodename, const knet_node_id_t requested_node_id) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int have_nodeid, have_name; - uint8_t node_id; + knet_node_id_t node_id; char name[KNET_MAX_HOST_LEN]; have_nodeid = knet_host_get_id_by_host_name(knet_iface->cfg_ring.knet_h, nodename, &node_id); have_name = knet_host_get_name_by_host_id(knet_iface->cfg_ring.knet_h, requested_node_id, name); /* * host does not exist without a name */ if (have_name < 0) { return 0; } /* * internal error.. get out */ if ((have_nodeid < 0) || (have_name < 0)) { knet_vty_write(vty, "Error: unable to query libknet for name/nodeid info%s", telnet_newline); return -1; } if ((!have_name) && (!have_nodeid)) { if (!strcmp(name, nodename) && (node_id == requested_node_id)) return 1; } knet_vty_write(vty, "Error: requested nodename or id already exists in libknet%s", telnet_newline); return -1; } static int knet_cmd_no_peer(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, requested_node_id = 0, err = 0; char *param = NULL; char nodename[KNET_MAX_HOST_LEN]; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(nodename, sizeof(nodename), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); requested_node_id = param_to_int(param, paramlen); if (requested_node_id == knet_iface->cfg_eth.node_id) { knet_vty_write(vty, "Error: remote peer id cannot be the same as local id%s", telnet_newline); return -1; } err = knet_find_host(vty, nodename, requested_node_id); if (err < 0) goto out_clean; if (err != 1) { knet_vty_write(vty, "Error: peer not found in list%s", telnet_newline); goto out_clean; } err = knet_host_remove(knet_iface->cfg_ring.knet_h, requested_node_id); if (err < 0) { knet_vty_write(vty, "Error: unable to remove peer from current config%s", telnet_newline); goto out_clean; } out_clean: return err; } static int knet_cmd_peer(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, requested_node_id = 0, err = 0, host = 0; char *param = NULL; char nodename[KNET_MAX_HOST_LEN]; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(nodename, sizeof(nodename), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); requested_node_id = param_to_int(param, paramlen); if (requested_node_id == knet_iface->cfg_eth.node_id) { knet_vty_write(vty, "Error: remote peer id cannot be the same as local id%s", telnet_newline); return -1; } err = knet_find_host(vty, nodename, requested_node_id); if (err < 0) goto out_clean; if (err == 0) { err = knet_host_add(knet_iface->cfg_ring.knet_h, requested_node_id); if (err < 0) { knet_vty_write(vty, "Error: unable to allocate memory for host struct!%s", telnet_newline); goto out_clean; } host = 1; knet_host_set_name(knet_iface->cfg_ring.knet_h, requested_node_id, nodename); knet_host_set_policy(knet_iface->cfg_ring.knet_h, requested_node_id, KNET_LINK_POLICY_PASSIVE); } vty->host_id = requested_node_id; vty->node = NODE_PEER; out_clean: if (err < 0) { if (host) knet_host_remove(knet_iface->cfg_ring.knet_h, requested_node_id); } return err; } static int knet_cmd_no_ip(struct knet_vty *vty) { int paramlen = 0, paramoffset = 0; char *param = NULL; char ipaddr[KNET_MAX_HOST_LEN], prefix[4]; struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_string = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(ipaddr, sizeof(ipaddr), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); param_to_str(prefix, sizeof(prefix), param, paramlen); if (tap_del_ip(knet_iface->cfg_eth.tap, ipaddr, prefix, &error_string) < 0) { knet_vty_write(vty, "Error: Unable to del ip addr %s/%s on device %s%s", ipaddr, prefix, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); if (error_string) { knet_vty_write(vty, "(%s)%s", error_string, telnet_newline); free(error_string); } return -1; } return 0; } static int knet_cmd_ip(struct knet_vty *vty) { int paramlen = 0, paramoffset = 0; char *param = NULL; char ipaddr[512], prefix[4]; struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_string = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(ipaddr, sizeof(ipaddr), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); param_to_str(prefix, sizeof(prefix), param, paramlen); if (tap_add_ip(knet_iface->cfg_eth.tap, ipaddr, prefix, &error_string) < 0) { knet_vty_write(vty, "Error: Unable to set ip addr %s/%s on device %s%s", ipaddr, prefix, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); if (error_string) { knet_vty_write(vty, "(%s)%s", error_string, telnet_newline); free(error_string); } return -1; } return 0; } static void knet_cmd_auto_mtu_notify(void *private_data, unsigned int data_mtu) { struct knet_cfg *knet_iface = (struct knet_cfg *)private_data; /* * 48 is the magic number! yes it is.. it's the magic number... */ knet_iface->cfg_ring.data_mtu = data_mtu - 48; if (!knet_iface->cfg_eth.auto_mtu) { int mtu = 0; mtu = tap_get_mtu(knet_iface->cfg_eth.tap); if (mtu < 0) { log_debug("Unable to get current MTU?"); } else { if (data_mtu < mtu) { log_debug("Manually configured MTU (%d) is higher than automatically detected MTU (%d)", mtu, data_mtu); } } return; } if (tap_set_mtu(knet_iface->cfg_eth.tap, knet_iface->cfg_ring.data_mtu) < 0) { log_warn("Error: Unable to set requested mtu %d on device %s via mtu notify", knet_iface->cfg_ring.data_mtu, tap_get_name(knet_iface->cfg_eth.tap)); } else { log_info("Device %s new mtu: %d (via mtu notify)", tap_get_name(knet_iface->cfg_eth.tap), knet_iface->cfg_ring.data_mtu); } } static int knet_cmd_no_pmtufreq(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; if (knet_handle_pmtud_setfreq(knet_iface->cfg_ring.knet_h, 5) < 0) { knet_vty_write(vty, "Error: Unable to reset PMTUd frequency to 5 seconds on device %s%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } return 0; } static int knet_cmd_pmtufreq(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, pmtufreq = 5; char *param = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); pmtufreq = param_to_int(param, paramlen); if (knet_handle_pmtud_setfreq(knet_iface->cfg_ring.knet_h, pmtufreq) < 0) { knet_vty_write(vty, "Error: Unable to set PMTUd frequency to %d seconds on device %s%s", pmtufreq, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } return 0; } static int knet_cmd_no_mtu(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; /* allow automatic updates of mtu */ knet_iface->cfg_eth.auto_mtu = 1; if (knet_iface->cfg_ring.data_mtu > 0) { if (tap_set_mtu(knet_iface->cfg_eth.tap, knet_iface->cfg_ring.data_mtu) < 0) { knet_iface->cfg_eth.auto_mtu = 0; knet_vty_write(vty, "Error: Unable to set auto detected mtu on device %s%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } } else { if (tap_reset_mtu(knet_iface->cfg_eth.tap) < 0) { knet_iface->cfg_eth.auto_mtu = 0; knet_vty_write(vty, "Error: Unable to reset mtu on device %s%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } } return 0; } static int knet_cmd_mtu(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0, expected_mtu = 0; char *param = NULL; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); expected_mtu = param_to_int(param, paramlen); /* disable mtu auto updates */ knet_iface->cfg_eth.auto_mtu = 0; if ((knet_iface->cfg_ring.data_mtu) && (expected_mtu > knet_iface->cfg_ring.data_mtu)) { knet_vty_write(vty, "WARNING: Manually configured MTU (%d) is higher than automatically detected MTU (%d)%s", expected_mtu, knet_iface->cfg_ring.data_mtu, telnet_newline); } if (tap_set_mtu(knet_iface->cfg_eth.tap, expected_mtu) < 0) { knet_iface->cfg_eth.auto_mtu = 1; knet_vty_write(vty, "Error: Unable to set requested mtu %d on device %s%s", expected_mtu, tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); return -1; } return 0; } static int knet_cmd_stop(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_down = NULL, *error_postdown = NULL; int err = 0; err = tap_set_down(knet_iface->cfg_eth.tap, &error_down, &error_postdown); if (err < 0) { knet_vty_write(vty, "Error: Unable to set interface %s down!%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); } else { if (knet_iface->cfg_ring.knet_h) knet_handle_setfwd(knet_iface->cfg_ring.knet_h, 0); knet_iface->active = 0; } if (error_down) { knet_vty_write(vty, "down script output:%s(%s)%s", telnet_newline, error_down, telnet_newline); free(error_down); } if (error_postdown) { knet_vty_write(vty, "post-down script output:%s(%s)%s", telnet_newline, error_postdown, telnet_newline); free(error_postdown); } return err; } static int knet_cmd_crypto(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; int paramlen = 0, paramoffset = 0; char *param = NULL; int err = 0; struct knet_handle_crypto_cfg knet_handle_crypto_cfg_new; int fd = -1; char keyfile[PATH_MAX]; struct stat sb; if (knet_iface->active) { knet_vty_write(vty, "Error: Unable to activate encryption while interface is active%s", telnet_newline); return -1; } memset(&knet_handle_crypto_cfg_new, 0, sizeof(struct knet_handle_crypto_cfg)); get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(knet_handle_crypto_cfg_new.crypto_model, sizeof(knet_handle_crypto_cfg_new.crypto_model), param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); param_to_str(knet_handle_crypto_cfg_new.crypto_cipher_type, sizeof(knet_handle_crypto_cfg_new.crypto_cipher_type), param, paramlen); get_param(vty, 3, ¶m, ¶mlen, ¶moffset); param_to_str(knet_handle_crypto_cfg_new.crypto_hash_type, sizeof(knet_handle_crypto_cfg_new.crypto_hash_type), param, paramlen); if ((!strncmp("none", knet_handle_crypto_cfg_new.crypto_model, 4)) || ((!strncmp("none", knet_handle_crypto_cfg_new.crypto_cipher_type, 4)) && ((!strncmp("none", knet_handle_crypto_cfg_new.crypto_hash_type, 4))))) goto no_key; memset(keyfile, 0, PATH_MAX); snprintf(keyfile, PATH_MAX - 1, DEFAULT_CONFIG_DIR "/cryptokeys.d/%s", tap_get_name(knet_iface->cfg_eth.tap)); fd = open(keyfile, O_RDONLY); if (fd < 0) { knet_vty_write(vty, "Error: Unable to open security key: %s%s", keyfile, telnet_newline); err = -1; return -1; } if (fstat(fd, &sb)) { knet_vty_write(vty, "Error: Unable to verify security key: %s%s", keyfile, telnet_newline); goto key_error; } if (!S_ISREG(sb.st_mode)) { knet_vty_write(vty, "Error: Key %s does not appear to be a regular file%s", keyfile, telnet_newline); goto key_error; } knet_handle_crypto_cfg_new.private_key_len = (unsigned int)sb.st_size; if ((knet_handle_crypto_cfg_new.private_key_len < KNET_MIN_KEY_LEN) || (knet_handle_crypto_cfg_new.private_key_len > KNET_MAX_KEY_LEN)) { knet_vty_write(vty, "Error: Key %s is %u long. Must be %u <= key_len <= %u%s", keyfile, knet_handle_crypto_cfg_new.private_key_len, KNET_MIN_KEY_LEN, KNET_MAX_KEY_LEN, telnet_newline); goto key_error; } if (((sb.st_mode & S_IRWXU) != S_IRUSR) || (sb.st_mode & S_IRWXG) || (sb.st_mode & S_IRWXO)) { knet_vty_write(vty, "Error: Key %s does not have the correct permission (must be user read-only)%s", keyfile, telnet_newline); goto key_error; } if (read(fd, &knet_handle_crypto_cfg_new.private_key, knet_handle_crypto_cfg_new.private_key_len) != knet_handle_crypto_cfg_new.private_key_len) { knet_vty_write(vty, "Error: Unable to read key %s%s", keyfile, telnet_newline); goto key_error; } close(fd); no_key: err = knet_handle_crypto(knet_iface->cfg_ring.knet_h, &knet_handle_crypto_cfg_new); if (!err) { memmove(&knet_iface->knet_handle_crypto_cfg, &knet_handle_crypto_cfg_new, sizeof(struct knet_handle_crypto_cfg)); } else { knet_vty_write(vty, "Error: Unable to initialize crypto module%s", telnet_newline); } return err; key_error: close(fd); return -1; } static int knet_cmd_start(struct knet_vty *vty) { struct knet_cfg *knet_iface = (struct knet_cfg *)vty->iface; char *error_preup = NULL, *error_up = NULL; int err = 0; err = tap_set_up(knet_iface->cfg_eth.tap, &error_preup, &error_up); if (err < 0) { knet_vty_write(vty, "Error: Unable to set interface %s up!%s", tap_get_name(knet_iface->cfg_eth.tap), telnet_newline); knet_handle_setfwd(knet_iface->cfg_ring.knet_h, 0); } else { knet_handle_setfwd(knet_iface->cfg_ring.knet_h, 1); knet_iface->active = 1; } if (error_preup) { knet_vty_write(vty, "pre-up script output:%s(%s)%s", telnet_newline, error_preup, telnet_newline); free(error_preup); } if (error_up) { knet_vty_write(vty, "up script output:%s(%s)%s", telnet_newline, error_up, telnet_newline); free(error_up); } return err; } static int knet_cmd_no_interface(struct knet_vty *vty) { int err = 0, paramlen = 0, paramoffset = 0; char *param = NULL; char device[IFNAMSIZ]; struct knet_cfg *knet_iface = NULL; char *ip_list = NULL; int ip_list_entries = 0, j, i, offset = 0; char *error_string = NULL; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; uint8_t link_ids[KNET_MAX_LINK]; size_t host_ids_entries = 0, link_ids_entries = 0; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(device, IFNAMSIZ, param, paramlen); knet_iface = knet_get_iface(device, 0); if (!knet_iface) { knet_vty_write(vty, "Error: Unable to find requested interface%s", telnet_newline); return -1; } vty->iface = (void *)knet_iface; /* * disable PTMUd notification before shutting down the tap device */ knet_handle_enable_pmtud_notify(knet_iface->cfg_ring.knet_h, NULL, NULL); tap_get_ips(knet_iface->cfg_eth.tap, &ip_list, &ip_list_entries); if ((ip_list) && (ip_list_entries > 0)) { for (i = 1; i <= ip_list_entries; i++) { tap_del_ip(knet_iface->cfg_eth.tap, ip_list + offset, ip_list + offset + strlen(ip_list + offset) + 1, &error_string); if (error_string) { free(error_string); error_string = NULL; } offset = offset + strlen(ip_list) + 1; offset = offset + strlen(ip_list + offset) + 1; } free(ip_list); ip_list = NULL; ip_list_entries = 0; } knet_host_get_host_list(knet_iface->cfg_ring.knet_h, host_ids, &host_ids_entries); for (j = 0; j < host_ids_entries; j++) { knet_link_get_link_list(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids, &link_ids_entries); for (i = 0; i < link_ids_entries; i++) { knet_link_set_enable(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], 0); knet_link_clear_config(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i]); } knet_host_remove(knet_iface->cfg_ring.knet_h, host_ids[j]); } knet_cmd_stop(vty); if (knet_iface->cfg_ring.knet_h) { knet_handle_free(knet_iface->cfg_ring.knet_h); knet_iface->cfg_ring.knet_h = NULL; } if (knet_iface->cfg_eth.tap) tap_close(knet_iface->cfg_eth.tap); if (knet_iface) knet_destroy_iface(knet_iface); return err; } static void sock_notify_fn(void *private_data, int datafd, int8_t chan, uint8_t tx_rx, int error, int errorno) { struct knet_vty *vty = (struct knet_vty *)private_data; knet_vty_write(vty, "Error: received sock notify, datafd: %d channel: %d direction: %u error: %d errno: %d (%s)%s", datafd, chan, tx_rx, error, errorno, strerror(errorno), telnet_newline); } static int knet_cmd_interface(struct knet_vty *vty) { int err = 0, paramlen = 0, paramoffset = 0, found = 0, requested_id, tapfd; uint16_t baseport; uint8_t *bport = (uint8_t *)&baseport; char *param = NULL; char device[IFNAMSIZ]; char mac[18]; struct knet_cfg *knet_iface = NULL; int8_t channel = 0; get_param(vty, 1, ¶m, ¶mlen, ¶moffset); param_to_str(device, IFNAMSIZ, param, paramlen); get_param(vty, 2, ¶m, ¶mlen, ¶moffset); requested_id = param_to_int(param, paramlen); get_param(vty, 3, ¶m, ¶mlen, ¶moffset); baseport = param_to_int(param, paramlen); knet_iface = knet_get_iface(device, 1); if (!knet_iface) { knet_vty_write(vty, "Error: Unable to allocate memory for config structures%s", telnet_newline); return -1; } if (knet_iface->cfg_eth.tap) { found = 1; goto tap_found; } if (!knet_iface->cfg_eth.tap) knet_iface->cfg_eth.tap = tap_open(device, IFNAMSIZ, DEFAULT_CONFIG_DIR); if ((!knet_iface->cfg_eth.tap) && (errno == EBUSY)) { knet_vty_write(vty, "Error: interface %s seems to exist in the system%s", device, telnet_newline); err = -1; goto out_clean; } if (!knet_iface->cfg_eth.tap) { knet_vty_write(vty, "Error: Unable to create %s system tap device%s", device, telnet_newline); err = -1; goto out_clean; } tap_found: if (knet_iface->cfg_ring.knet_h) goto knet_found; knet_iface->cfg_ring.base_port = baseport; tapfd = tap_get_fd(knet_iface->cfg_eth.tap); knet_iface->cfg_ring.knet_h = knet_handle_new(requested_id, vty->logfd, vty->loglevel); if (!knet_iface->cfg_ring.knet_h) { knet_vty_write(vty, "Error: Unable to create ring handle for device %s%s", device, telnet_newline); err = -1; goto out_clean; } if (knet_handle_enable_sock_notify(knet_iface->cfg_ring.knet_h, &vty, sock_notify_fn)) { knet_vty_write(vty, "Error: Unable to add sock notify callback to to knet_handle %s%s", strerror(errno), telnet_newline); err = -1; goto out_clean; } if (knet_handle_add_datafd(knet_iface->cfg_ring.knet_h, &tapfd, &channel) < 0) { knet_vty_write(vty, "Error: Unable to add tapfd to knet_handle %s%s", strerror(errno), telnet_newline); err = -1; goto out_clean; } knet_handle_enable_filter(knet_iface->cfg_ring.knet_h, NULL, ether_host_filter_fn); if (knet_handle_enable_pmtud_notify(knet_iface->cfg_ring.knet_h, knet_iface, knet_cmd_auto_mtu_notify) < 0) { knet_vty_write(vty, "Error: Unable to configure auto mtu notification for device %s%s", device, telnet_newline); err = -1; goto out_clean; } knet_iface->cfg_eth.auto_mtu = 1; /* * make this configurable */ knet_handle_pmtud_setfreq(knet_iface->cfg_ring.knet_h, 5); knet_found: if (found) { if (requested_id == knet_iface->cfg_eth.node_id) goto out_found; knet_vty_write(vty, "Error: no interface %s with nodeid %d found%s", device, requested_id, telnet_newline); goto out_clean; } else { knet_iface->cfg_eth.node_id = requested_id; } baseport = htons(baseport); memset(&mac, 0, sizeof(mac)); snprintf(mac, sizeof(mac) - 1, "54:54:%x:%x:0:%x", bport[0], bport[1], knet_iface->cfg_eth.node_id); if (tap_set_mac(knet_iface->cfg_eth.tap, mac) < 0) { knet_vty_write(vty, "Error: Unable to set mac address %s on device %s%s", mac, device, telnet_newline); err = -1; goto out_clean; } out_found: vty->node = NODE_INTERFACE; vty->iface = (void *)knet_iface; out_clean: if (err) { if (knet_iface->cfg_ring.knet_h) knet_handle_free(knet_iface->cfg_ring.knet_h); if (knet_iface->cfg_eth.tap) tap_close(knet_iface->cfg_eth.tap); knet_destroy_iface(knet_iface); } return err; } static int knet_cmd_exit_node(struct knet_vty *vty) { knet_vty_exit_node(vty); return 0; } static int knet_cmd_status(struct knet_vty *vty) { int i, j; struct knet_cfg *knet_iface = knet_cfg_head.knet_cfg; struct knet_link_status status; const char *nl = telnet_newline; struct timespec now; char nodename[KNET_MAX_HOST_LEN]; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; uint8_t link_ids[KNET_MAX_LINK]; size_t host_ids_entries = 0, link_ids_entries = 0; uint8_t policy; clock_gettime(CLOCK_MONOTONIC, &now); knet_vty_write(vty, "Current knet status%s", nl); knet_vty_write(vty, "-------------------%s", nl); while (knet_iface != NULL) { knet_vty_write(vty, "interface %s (active: %d)%s", tap_get_name(knet_iface->cfg_eth.tap), knet_iface->active, nl); knet_host_get_host_list(knet_iface->cfg_ring.knet_h, host_ids, &host_ids_entries); for (j = 0; j < host_ids_entries; j++) { knet_host_get_name_by_host_id(knet_iface->cfg_ring.knet_h, host_ids[j], nodename); knet_vty_write(vty, " peer %s ", nodename); knet_host_get_policy(knet_iface->cfg_ring.knet_h, host_ids[j], &policy); switch (policy) { case KNET_LINK_POLICY_PASSIVE: knet_vty_write(vty, "(passive)%s", nl); break; case KNET_LINK_POLICY_ACTIVE: knet_vty_write(vty, "(active)%s", nl); break; case KNET_LINK_POLICY_RR: knet_vty_write(vty, "(round-robin)%s", nl); break; } knet_link_get_link_list(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids, &link_ids_entries); for (i = 0; i < link_ids_entries; i++) { uint8_t dynamic, transport; const char *transport_name; struct sockaddr_storage src_addr; struct sockaddr_storage dst_addr; if (!knet_link_get_config(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &transport, &src_addr, &dst_addr, &dynamic)) { transport_name = knet_handle_get_transport_name_by_id(knet_iface->cfg_ring.knet_h, transport); knet_link_get_status(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &status); if (status.enabled == 1) { if (dynamic) { knet_vty_write(vty, " link %s dynamic (%s/connected: %d)%s", status.src_ipaddr, transport_name, status.connected, nl); } else { knet_vty_write(vty, " link %s %s (%s/connected: %d)%s", status.src_ipaddr, status.dst_ipaddr, transport_name, status.connected, nl); } if (status.connected) { knet_vty_write(vty, " average latency: %llu us%s", status.latency, nl); if ((dynamic) && (status.dynconnected)) { knet_vty_write(vty, " source ip: %s%s", status.dst_ipaddr, nl); } } else { knet_vty_write(vty, " last heard: "); if (status.pong_last.tv_sec) { knet_vty_write(vty, "%lu s ago%s", (long unsigned int)now.tv_sec - status.pong_last.tv_sec, nl); } else { knet_vty_write(vty, "never%s", nl); } } } } } } knet_iface = knet_iface->next; } return 0; } static int knet_cmd_print_conf(struct knet_vty *vty) { int i, j; struct knet_cfg *knet_iface = knet_cfg_head.knet_cfg; struct knet_link_status status; const char *nl = telnet_newline; char *ip_list = NULL; int ip_list_entries = 0; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; uint8_t link_ids[KNET_MAX_LINK]; size_t host_ids_entries = 0, link_ids_entries = 0; char nodename[KNET_MAX_HOST_LEN]; uint8_t policy; unsigned int pmtudfreq = 0; if (vty->filemode) nl = file_newline; knet_vty_write(vty, "configure%s", nl); knet_vty_write(vty, " vty%s", nl); knet_vty_write(vty, " timeout %d%s", vty->idle_timeout, nl); knet_vty_write(vty, " exit%s", nl); while (knet_iface != NULL) { knet_vty_write(vty, " interface %s %u %u%s", tap_get_name(knet_iface->cfg_eth.tap), knet_iface->cfg_eth.node_id, knet_iface->cfg_ring.base_port, nl); if (!knet_iface->cfg_eth.auto_mtu) knet_vty_write(vty, " mtu %d%s", tap_get_mtu(knet_iface->cfg_eth.tap), nl); knet_handle_pmtud_getfreq(knet_iface->cfg_ring.knet_h, &pmtudfreq); if ((pmtudfreq > 0) && (pmtudfreq != 5)) knet_vty_write(vty, " pmtudfreq %d%s", pmtudfreq, nl); tap_get_ips(knet_iface->cfg_eth.tap, &ip_list, &ip_list_entries); if ((ip_list) && (ip_list_entries > 0)) { char *ipaddr = NULL, *prefix = NULL, *next = ip_list; for (i = 1; i <= ip_list_entries; i++) { ipaddr = next; prefix = ipaddr + strlen(ipaddr) + 1; next = prefix + strlen(prefix) + 1; knet_vty_write(vty, " ip %s %s%s", ipaddr, prefix, nl); } free(ip_list); ip_list = NULL; ip_list_entries = 0; } knet_vty_write(vty, " crypto %s %s %s%s", knet_iface->knet_handle_crypto_cfg.crypto_model, knet_iface->knet_handle_crypto_cfg.crypto_cipher_type, knet_iface->knet_handle_crypto_cfg.crypto_hash_type, nl); knet_host_get_host_list(knet_iface->cfg_ring.knet_h, host_ids, &host_ids_entries); for (j = 0; j < host_ids_entries; j++) { knet_host_get_name_by_host_id(knet_iface->cfg_ring.knet_h, host_ids[j], nodename); knet_vty_write(vty, " peer %s %u%s", nodename, host_ids[j], nl); knet_host_get_policy(knet_iface->cfg_ring.knet_h, host_ids[j], &policy); switch (policy) { case KNET_LINK_POLICY_PASSIVE: knet_vty_write(vty, " switch-policy passive%s", nl); break; case KNET_LINK_POLICY_ACTIVE: knet_vty_write(vty, " switch-policy active%s", nl); break; case KNET_LINK_POLICY_RR: knet_vty_write(vty, " switch-policy round-robin%s", nl); break; } knet_link_get_link_list(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids, &link_ids_entries); for (i = 0; i < link_ids_entries; i++) { uint8_t dynamic, transport; const char *transport_name; struct sockaddr_storage src_addr; struct sockaddr_storage dst_addr; if (!knet_link_get_config(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &transport, &src_addr, &dst_addr, &dynamic)) { transport_name = knet_handle_get_transport_name_by_id(knet_iface->cfg_ring.knet_h, transport); knet_link_get_status(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &status); if (status.enabled == 1) { uint8_t priority, pong_count; unsigned int precision; time_t interval, timeout; if (dynamic) { knet_vty_write(vty, " link %d %s dynamic %s%s", link_ids[i], status.src_ipaddr, transport_name, nl); } else { knet_vty_write(vty, " link %d %s %s %s%s", link_ids[i], status.src_ipaddr, status.dst_ipaddr, transport_name, nl); } knet_link_get_pong_count(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &pong_count); knet_vty_write(vty, " pong_count %u%s", pong_count, nl); knet_link_get_ping_timers(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &interval, &timeout, &precision); knet_vty_write(vty, " timers %llu %llu%s", (unsigned long long)interval, (unsigned long long)timeout, nl); knet_link_get_priority(knet_iface->cfg_ring.knet_h, host_ids[j], link_ids[i], &priority); knet_vty_write(vty, " priority %u%s", priority, nl); /* print link properties */ knet_vty_write(vty, " exit%s", nl); } } } knet_vty_write(vty, " exit%s", nl); } if (knet_iface->active) knet_vty_write(vty, " start%s", nl); knet_vty_write(vty, " exit%s", nl); knet_iface = knet_iface->next; } knet_vty_write(vty, " exit%sexit%s", nl, nl); return 0; } static int knet_cmd_show_conf(struct knet_vty *vty) { return knet_cmd_print_conf(vty); } static int knet_cmd_write_conf(struct knet_vty *vty) { int fd = 1, vty_sock, err = 0, backup = 1; char tempfile[PATH_MAX]; memset(tempfile, 0, sizeof(tempfile)); snprintf(tempfile, sizeof(tempfile), "%s.sav", knet_cfg_head.conffile); err = rename(knet_cfg_head.conffile, tempfile); if ((err < 0) && (errno != ENOENT)) { knet_vty_write(vty, "Unable to create backup config file %s %s", tempfile, telnet_newline); return -1; } if ((err < 0) && (errno == ENOENT)) backup = 0; fd = open(knet_cfg_head.conffile, O_RDWR | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { knet_vty_write(vty, "Error unable to open file%s", telnet_newline); return -1; } vty_sock = vty->vty_sock; vty->vty_sock = fd; vty->filemode = 1; knet_cmd_print_conf(vty); vty->vty_sock = vty_sock; vty->filemode = 0; close(fd); knet_vty_write(vty, "Configuration saved to %s%s", knet_cfg_head.conffile, telnet_newline); if (backup) knet_vty_write(vty, "Old configuration file has been stored in %s%s", tempfile, telnet_newline); return err; } static int knet_cmd_config(struct knet_vty *vty) { int err = 0; if (!vty->user_can_enable) { knet_vty_write(vty, "Error: user %s does not have enough privileges to perform config operations%s", vty->username, telnet_newline); return -1; } pthread_mutex_lock(&knet_vty_mutex); if (knet_vty_config >= 0) { knet_vty_write(vty, "Error: configuration is currently locked by user %s on vty(%d). Try again later%s", vty->username, knet_vty_config, telnet_newline); err = -1; goto out_clean; } vty->node = NODE_CONFIG; knet_vty_config = vty->conn_num; out_clean: pthread_mutex_unlock(&knet_vty_mutex); return err; } static int knet_cmd_logout(struct knet_vty *vty) { vty->got_epipe = 1; return 0; } static int knet_cmd_who(struct knet_vty *vty) { int conn_index; pthread_mutex_lock(&knet_vty_mutex); for(conn_index = 0; conn_index < KNET_VTY_TOTAL_MAX_CONN; conn_index++) { if (knet_vtys[conn_index].active) { knet_vty_write(vty, "User %s connected on vty(%d) from %s%s", knet_vtys[conn_index].username, knet_vtys[conn_index].conn_num, knet_vtys[conn_index].ip, telnet_newline); } } pthread_mutex_unlock(&knet_vty_mutex); return 0; } static int knet_cmd_help(struct knet_vty *vty) { knet_vty_write(vty, PACKAGE "d VTY provides advanced help feature.%s%s" "When you need help, anytime at the command line please press '?'.%s%s" "If nothing matches, the help list will be empty and you must backup%s" " until entering a '?' shows the available options.%s", telnet_newline, telnet_newline, telnet_newline, telnet_newline, telnet_newline, telnet_newline); return 0; } /* exported API to vty_cli.c */ int knet_vty_execute_cmd(struct knet_vty *vty) { const vty_node_cmds_t *cmds = NULL; char *cmd = NULL; int cmdlen = 0; int cmdoffset = 0; if (knet_vty_is_line_empty(vty)) return 0; cmds = get_cmds(vty, &cmd, &cmdlen, &cmdoffset); /* this will eventually disappear. keep it as safeguard for now */ if (cmds == NULL) { knet_vty_write(vty, "No commands associated to this node%s", telnet_newline); return 0; } return match_command(vty, cmds, cmd, cmdlen, cmdoffset, KNET_VTY_MATCH_EXEC); } void knet_close_down(void) { struct knet_vty *vty = &knet_vtys[0]; int err, loop = 0; vty->node = NODE_CONFIG; vty->vty_sock = 1; vty->user_can_enable = 1; vty->filemode = 1; vty->got_epipe = 0; while ((knet_cfg_head.knet_cfg) && (loop < 10)) { memset(vty->line, 0, sizeof(vty->line)); snprintf(vty->line, sizeof(vty->line) - 1, "no interface %s", tap_get_name(knet_cfg_head.knet_cfg->cfg_eth.tap)); vty->line_idx = strlen(vty->line); err = knet_vty_execute_cmd(vty); if (err != 0) { log_error("error shutting down: %s", vty->line); break; } loop++; } } int knet_read_conf(void) { int err = 0, len = 0, line = 0; struct knet_vty *vty = &knet_vtys[0]; FILE *file = NULL; file = fopen(knet_cfg_head.conffile, "r"); if ((file == NULL) && (errno != ENOENT)) { log_error("Unable to open config file for reading %s", knet_cfg_head.conffile); return -1; } if ((file == NULL) && (errno == ENOENT)) { log_info("Configuration file %s not found, starting with default empty config", knet_cfg_head.conffile); return 0; } vty->vty_sock = 1; vty->user_can_enable = 1; vty->filemode = 1; while(fgets(vty->line, sizeof(vty->line), file) != NULL) { line++; len = strlen(vty->line) - 1; memset(&vty->line[len], 0, 1); vty->line_idx = len; err = knet_vty_execute_cmd(vty); if (err != 0) { log_error("line[%d]: %s", line, vty->line); break; } } fclose(file); memset(vty, 0, sizeof(*vty)); return err; } void knet_vty_help(struct knet_vty *vty) { int idx = 0; const vty_node_cmds_t *cmds = NULL; char *cmd = NULL; int cmdlen = 0; int cmdoffset = 0; cmds = get_cmds(vty, &cmd, &cmdlen, &cmdoffset); /* this will eventually disappear. keep it as safeguard for now */ if (cmds == NULL) { knet_vty_write(vty, "No commands associated to this node%s", telnet_newline); return; } if (knet_vty_is_line_empty(vty) || cmd == NULL) { while (cmds[idx].cmd != NULL) { print_help(vty, cmds, idx); idx++; } return; } match_command(vty, cmds, cmd, cmdlen, cmdoffset, KNET_VTY_MATCH_HELP); } void knet_vty_tab_completion(struct knet_vty *vty) { const vty_node_cmds_t *cmds = NULL; char *cmd = NULL; int cmdlen = 0; int cmdoffset = 0; if (knet_vty_is_line_empty(vty)) return; knet_vty_write(vty, "%s", telnet_newline); cmds = get_cmds(vty, &cmd, &cmdlen, &cmdoffset); /* this will eventually disappear. keep it as safeguard for now */ if (cmds == NULL) { knet_vty_write(vty, "No commands associated to this node%s", telnet_newline); return; } match_command(vty, cmds, cmd, cmdlen, cmdoffset, KNET_VTY_MATCH_EXPAND); knet_vty_prompt(vty); knet_vty_write(vty, "%s", vty->line); } diff --git a/libknet/handle.c b/libknet/handle.c index 3783268e..8f7aa132 100644 --- a/libknet/handle.c +++ b/libknet/handle.c @@ -1,1414 +1,1414 @@ /* * Copyright (C) 2010-2015 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 #include #include #include "internals.h" #include "crypto.h" #include "compat.h" #include "common.h" #include "threads_common.h" #include "threads_heartbeat.h" #include "threads_pmtud.h" #include "threads_dsthandler.h" #include "threads_rx.h" #include "threads_tx.h" #include "transports.h" #include "logging.h" static pthread_mutex_t handle_config_mutex = PTHREAD_MUTEX_INITIALIZER; static int _init_locks(knet_handle_t knet_h) { int savederrno = 0; savederrno = pthread_rwlock_init(&knet_h->global_rwlock, NULL); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize list rwlock: %s", strerror(savederrno)); goto exit_fail; } knet_h->lock_init_done = 1; savederrno = pthread_mutex_init(&knet_h->pmtud_mutex, NULL); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize pmtud mutex: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_cond_init(&knet_h->pmtud_cond, NULL); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize pmtud conditional mutex: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_mutex_init(&knet_h->hb_mutex, NULL); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize hb_thread mutex: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_mutex_init(&knet_h->tx_mutex, NULL); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize tx_thread mutex: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_mutex_init(&knet_h->tx_seq_num_mutex, NULL); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize tx_seq_num_mutex mutex: %s", strerror(savederrno)); goto exit_fail; } return 0; exit_fail: errno = savederrno; return -1; } static void _destroy_locks(knet_handle_t knet_h) { knet_h->lock_init_done = 0; pthread_rwlock_destroy(&knet_h->global_rwlock); pthread_mutex_destroy(&knet_h->pmtud_mutex); pthread_cond_destroy(&knet_h->pmtud_cond); pthread_mutex_destroy(&knet_h->hb_mutex); pthread_mutex_destroy(&knet_h->tx_mutex); pthread_mutex_destroy(&knet_h->tx_seq_num_mutex); } static int _init_socks(knet_handle_t knet_h) { int savederrno = 0; if (_init_socketpair(knet_h, knet_h->hostsockfd)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize internal hostsockpair: %s", strerror(savederrno)); goto exit_fail; } if (_init_socketpair(knet_h, knet_h->dstsockfd)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to initialize internal dstsockpair: %s", strerror(savederrno)); goto exit_fail; } return 0; exit_fail: errno = savederrno; return -1; } static void _close_socks(knet_handle_t knet_h) { _close_socketpair(knet_h, knet_h->dstsockfd); _close_socketpair(knet_h, knet_h->hostsockfd); } static int _init_buffers(knet_handle_t knet_h) { int savederrno = 0; int i; size_t bufsize; for (i = 0; i < PCKT_FRAG_MAX; i++) { bufsize = ceil((float)KNET_MAX_PACKET_SIZE / (i + 1)) + KNET_HEADER_ALL_SIZE; knet_h->send_to_links_buf[i] = malloc(bufsize); if (!knet_h->send_to_links_buf[i]) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory datafd to link buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->send_to_links_buf[i], 0, bufsize); knet_h->recv_from_sock_buf[i] = malloc(KNET_DATABUFSIZE); if (!knet_h->recv_from_sock_buf[i]) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory for app to datafd buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->recv_from_sock_buf[i], 0, KNET_DATABUFSIZE); knet_h->recv_from_links_buf[i] = malloc(KNET_DATABUFSIZE); if (!knet_h->recv_from_links_buf[i]) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory for link to datafd buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->recv_from_links_buf[i], 0, KNET_DATABUFSIZE); } knet_h->pingbuf = malloc(KNET_HEADER_PING_SIZE); if (!knet_h->pingbuf) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory for hearbeat buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->pingbuf, 0, KNET_HEADER_PING_SIZE); knet_h->pmtudbuf = malloc(KNET_PMTUD_SIZE_V6); if (!knet_h->pmtudbuf) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory for pmtud buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->pmtudbuf, 0, KNET_PMTUD_SIZE_V6); for (i = 0; i < PCKT_FRAG_MAX; i++) { bufsize = ceil((float)KNET_MAX_PACKET_SIZE / (i + 1)) + KNET_HEADER_ALL_SIZE + KNET_DATABUFSIZE_CRYPT_PAD; knet_h->send_to_links_buf_crypt[i] = malloc(bufsize); if (!knet_h->send_to_links_buf_crypt[i]) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory for crypto datafd to link buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->send_to_links_buf_crypt[i], 0, bufsize); } knet_h->recv_from_links_buf_decrypt = malloc(KNET_DATABUFSIZE_CRYPT); if (!knet_h->recv_from_links_buf_decrypt) { savederrno = errno; log_err(knet_h, KNET_SUB_CRYPTO, "Unable to allocate memory for crypto link to datafd buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->recv_from_links_buf_decrypt, 0, KNET_DATABUFSIZE_CRYPT); knet_h->recv_from_links_buf_crypt = malloc(KNET_DATABUFSIZE_CRYPT); if (!knet_h->recv_from_links_buf_crypt) { savederrno = errno; log_err(knet_h, KNET_SUB_CRYPTO, "Unable to allocate memory for crypto link to datafd buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->recv_from_links_buf_crypt, 0, KNET_DATABUFSIZE_CRYPT); knet_h->pingbuf_crypt = malloc(KNET_DATABUFSIZE_CRYPT); if (!knet_h->pingbuf_crypt) { savederrno = errno; log_err(knet_h, KNET_SUB_CRYPTO, "Unable to allocate memory for crypto hearbeat buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->pingbuf_crypt, 0, KNET_DATABUFSIZE_CRYPT); knet_h->pmtudbuf_crypt = malloc(KNET_DATABUFSIZE_CRYPT); if (!knet_h->pmtudbuf_crypt) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to allocate memory for crypto pmtud buffer: %s", strerror(savederrno)); goto exit_fail; } memset(knet_h->pmtudbuf_crypt, 0, KNET_DATABUFSIZE_CRYPT); memset(knet_h->knet_transport_fd_tracker, KNET_MAX_TRANSPORTS, sizeof(knet_h->knet_transport_fd_tracker)); return 0; exit_fail: errno = savederrno; return -1; } static void _destroy_buffers(knet_handle_t knet_h) { int i; for (i = 0; i < PCKT_FRAG_MAX; i++) { free(knet_h->send_to_links_buf[i]); free(knet_h->recv_from_sock_buf[i]); free(knet_h->send_to_links_buf_crypt[i]); free(knet_h->recv_from_links_buf[i]); } free(knet_h->recv_from_links_buf_decrypt); free(knet_h->recv_from_links_buf_crypt); free(knet_h->pingbuf); free(knet_h->pingbuf_crypt); free(knet_h->pmtudbuf); free(knet_h->pmtudbuf_crypt); } static int _init_epolls(knet_handle_t knet_h) { struct epoll_event ev; int savederrno = 0; /* * even if the kernel does dynamic allocation with epoll_ctl * we need to reserve one extra for host to host communication */ knet_h->send_to_links_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); if (knet_h->send_to_links_epollfd < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to create epoll datafd to link fd: %s", strerror(savederrno)); goto exit_fail; } knet_h->recv_from_links_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS); if (knet_h->recv_from_links_epollfd < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to create epoll link to datafd fd: %s", strerror(savederrno)); goto exit_fail; } knet_h->dst_link_handler_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS); if (knet_h->dst_link_handler_epollfd < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to create epoll dst cache fd: %s", strerror(savederrno)); goto exit_fail; } if (_fdset_cloexec(knet_h->send_to_links_epollfd)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to set CLOEXEC on datafd to link epoll fd: %s", strerror(savederrno)); goto exit_fail; } if (_fdset_cloexec(knet_h->recv_from_links_epollfd)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to set CLOEXEC on link to datafd epoll fd: %s", strerror(savederrno)); goto exit_fail; } if (_fdset_cloexec(knet_h->dst_link_handler_epollfd)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to set CLOEXEC on dst cache epoll fd: %s", strerror(savederrno)); goto exit_fail; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = knet_h->hostsockfd[0]; if (epoll_ctl(knet_h->send_to_links_epollfd, EPOLL_CTL_ADD, knet_h->hostsockfd[0], &ev)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to add hostsockfd[0] to epoll pool: %s", strerror(savederrno)); goto exit_fail; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = knet_h->dstsockfd[0]; if (epoll_ctl(knet_h->dst_link_handler_epollfd, EPOLL_CTL_ADD, knet_h->dstsockfd[0], &ev)) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Unable to add dstsockfd[0] to epoll pool: %s", strerror(savederrno)); goto exit_fail; } return 0; exit_fail: errno = savederrno; return -1; } static void _close_epolls(knet_handle_t knet_h) { struct epoll_event ev; int i; memset(&ev, 0, sizeof(struct epoll_event)); for (i = 0; i < KNET_DATAFD_MAX; i++) { if (knet_h->sockfd[i].in_use) { epoll_ctl(knet_h->send_to_links_epollfd, EPOLL_CTL_DEL, knet_h->sockfd[i].sockfd[knet_h->sockfd[i].is_created], &ev); if (knet_h->sockfd[i].sockfd[knet_h->sockfd[i].is_created]) { _close_socketpair(knet_h, knet_h->sockfd[i].sockfd); } } } epoll_ctl(knet_h->send_to_links_epollfd, EPOLL_CTL_DEL, knet_h->hostsockfd[0], &ev); epoll_ctl(knet_h->dst_link_handler_epollfd, EPOLL_CTL_DEL, knet_h->dstsockfd[0], &ev); close(knet_h->send_to_links_epollfd); close(knet_h->recv_from_links_epollfd); close(knet_h->dst_link_handler_epollfd); } static int _start_transports(knet_handle_t knet_h) { int i, savederrno = 0, err = 0; for (i=0; itransport_ops[i] = get_udp_transport(); break; case KNET_TRANSPORT_SCTP: knet_h->transport_ops[i] = get_sctp_transport(); break; } if (knet_h->transport_ops[i]) { if (knet_h->transport_ops[i]->transport_init(knet_h) < 0) { savederrno = errno; log_err(knet_h, KNET_SUB_HANDLE, "Failed to allocate transport handle for %s: %s", knet_h->transport_ops[i]->transport_name, strerror(savederrno)); err = -1; goto out; } } } out: errno = savederrno; return err; } static void _stop_transports(knet_handle_t knet_h) { int i; for (i=0; itransport_ops[i]) { knet_h->transport_ops[i]->transport_free(knet_h); } } } static int _start_threads(knet_handle_t knet_h) { int savederrno = 0; savederrno = pthread_create(&knet_h->pmtud_link_handler_thread, 0, _handle_pmtud_link_thread, (void *) knet_h); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to start pmtud link thread: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_create(&knet_h->dst_link_handler_thread, 0, _handle_dst_link_handler_thread, (void *) knet_h); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to start dst cache thread: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_create(&knet_h->send_to_links_thread, 0, _handle_send_to_links_thread, (void *) knet_h); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to start datafd to link thread: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_create(&knet_h->recv_from_links_thread, 0, _handle_recv_from_links_thread, (void *) knet_h); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to start link to datafd thread: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_create(&knet_h->heartbt_thread, 0, _handle_heartbt_thread, (void *) knet_h); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to start heartbeat thread: %s", strerror(savederrno)); goto exit_fail; } return 0; exit_fail: errno = savederrno; return -1; } static void _stop_threads(knet_handle_t knet_h) { void *retval; /* * allow threads to catch on shutdown request * and release locks before we stop them. * this isn't the most efficent way to handle it * but it works good enough for now */ sleep(1); if (knet_h->heartbt_thread) { pthread_cancel(knet_h->heartbt_thread); pthread_join(knet_h->heartbt_thread, &retval); } if (knet_h->send_to_links_thread) { pthread_cancel(knet_h->send_to_links_thread); pthread_join(knet_h->send_to_links_thread, &retval); } if (knet_h->recv_from_links_thread) { pthread_cancel(knet_h->recv_from_links_thread); pthread_join(knet_h->recv_from_links_thread, &retval); } if (knet_h->dst_link_handler_thread) { pthread_cancel(knet_h->dst_link_handler_thread); pthread_join(knet_h->dst_link_handler_thread, &retval); } pthread_mutex_lock(&knet_h->pmtud_mutex); pthread_cond_signal(&knet_h->pmtud_cond); pthread_mutex_unlock(&knet_h->pmtud_mutex); sleep(1); if (knet_h->pmtud_link_handler_thread) { pthread_cancel(knet_h->pmtud_link_handler_thread); pthread_join(knet_h->pmtud_link_handler_thread, &retval); } } -knet_handle_t knet_handle_new(uint8_t host_id, - int log_fd, - uint8_t default_log_level) +knet_handle_t knet_handle_new(knet_node_id_t host_id, + int log_fd, + uint8_t default_log_level) { knet_handle_t knet_h; int savederrno = 0; struct rlimit cur; if (getrlimit(RLIMIT_NOFILE, &cur) < 0) { return NULL; } if ((log_fd < 0) || (log_fd >= cur.rlim_max)) { errno = EINVAL; return NULL; } /* * validate incoming request */ if ((log_fd) && (default_log_level > KNET_LOG_DEBUG)) { errno = EINVAL; return NULL; } /* * allocate handle */ knet_h = malloc(sizeof(struct knet_handle)); if (!knet_h) { errno = ENOMEM; return NULL; } memset(knet_h, 0, sizeof(struct knet_handle)); savederrno = pthread_mutex_lock(&handle_config_mutex); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get handle mutex lock: %s", strerror(savederrno)); errno = savederrno; goto exit_fail; } /* * copy config in place */ knet_h->host_id = host_id; knet_h->logfd = log_fd; if (knet_h->logfd > 0) { memset(&knet_h->log_levels, default_log_level, KNET_MAX_SUBSYSTEMS); } /* * set pmtud default timers */ knet_h->pmtud_interval = KNET_PMTUD_DEFAULT_INTERVAL; /* * init main locking structures */ if (_init_locks(knet_h)) { savederrno = errno; goto exit_fail; } /* * init sockets */ if (_init_socks(knet_h)) { savederrno = errno; goto exit_fail; } /* * allocate packet buffers */ if (_init_buffers(knet_h)) { savederrno = errno; goto exit_fail; } /* * create epoll fds */ if (_init_epolls(knet_h)) { savederrno = errno; goto exit_fail; } /* * start transports */ if (_start_transports(knet_h)) { savederrno = errno; goto exit_fail; } /* * start internal threads */ if (_start_threads(knet_h)) { savederrno = errno; goto exit_fail; } pthread_mutex_unlock(&handle_config_mutex); return knet_h; exit_fail: pthread_mutex_unlock(&handle_config_mutex); knet_handle_free(knet_h); errno = savederrno; return NULL; } int knet_handle_free(knet_handle_t knet_h) { int savederrno = 0; savederrno = pthread_mutex_lock(&handle_config_mutex); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get handle mutex lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h) { pthread_mutex_unlock(&handle_config_mutex); errno = EINVAL; return -1; } if (!knet_h->lock_init_done) { goto exit_nolock; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); pthread_mutex_unlock(&handle_config_mutex); errno = savederrno; return -1; } if (knet_h->host_head != NULL) { savederrno = EBUSY; log_err(knet_h, KNET_SUB_HANDLE, "Unable to free handle: host(s) or listener(s) are still active: %s", strerror(savederrno)); pthread_rwlock_unlock(&knet_h->global_rwlock); pthread_mutex_unlock(&handle_config_mutex); errno = savederrno; return -1; } knet_h->fini_in_progress = 1; pthread_rwlock_unlock(&knet_h->global_rwlock); _stop_threads(knet_h); _stop_transports(knet_h); _close_epolls(knet_h); _destroy_buffers(knet_h); _close_socks(knet_h); crypto_fini(knet_h); _destroy_locks(knet_h); exit_nolock: free(knet_h); knet_h = NULL; pthread_mutex_unlock(&handle_config_mutex); return 0; } int knet_handle_enable_sock_notify(knet_handle_t knet_h, void *sock_notify_fn_private_data, void (*sock_notify_fn) ( void *private_data, int datafd, int8_t channel, uint8_t tx_rx, int error, int errorno)) { int savederrno = 0, err = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!sock_notify_fn) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->sock_notify_fn_private_data = sock_notify_fn_private_data; knet_h->sock_notify_fn = sock_notify_fn; log_debug(knet_h, KNET_SUB_HANDLE, "sock_notify_fn enabled"); pthread_rwlock_unlock(&knet_h->global_rwlock); return err; } int knet_handle_add_datafd(knet_handle_t knet_h, int *datafd, int8_t *channel) { int err = 0, savederrno = 0; int i; struct epoll_event ev; if (!knet_h) { errno = EINVAL; return -1; } if (datafd == NULL) { errno = EINVAL; return -1; } if (channel == NULL) { errno = EINVAL; return -1; } if (*channel >= KNET_DATAFD_MAX) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h->sock_notify_fn) { log_err(knet_h, KNET_SUB_HANDLE, "Adding datafd requires sock notify callback enabled!"); savederrno = EINVAL; err = -1; goto out_unlock; } if (*datafd > 0) { for (i = 0; i < KNET_DATAFD_MAX; i++) { if ((knet_h->sockfd[i].in_use) && (knet_h->sockfd[i].sockfd[0] == *datafd)) { log_err(knet_h, KNET_SUB_HANDLE, "requested datafd: %d already exist in index: %d", *datafd, i); savederrno = EEXIST; err = -1; goto out_unlock; } } } /* * auto allocate a channel */ if (*channel < 0) { for (i = 0; i < KNET_DATAFD_MAX; i++) { if (!knet_h->sockfd[i].in_use) { *channel = i; break; } } if (*channel < 0) { savederrno = EBUSY; err = -1; goto out_unlock; } } else { if (knet_h->sockfd[*channel].in_use) { savederrno = EBUSY; err = -1; goto out_unlock; } } knet_h->sockfd[*channel].is_created = 0; knet_h->sockfd[*channel].is_socket = 0; knet_h->sockfd[*channel].has_error = 0; if (*datafd > 0) { int sockopt; socklen_t sockoptlen = sizeof(sockopt); if (_fdset_cloexec(*datafd)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_HANDLE, "Unable to set CLOEXEC on datafd: %s", strerror(savederrno)); goto out_unlock; } if (_fdset_nonblock(*datafd)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_HANDLE, "Unable to set NONBLOCK on datafd: %s", strerror(savederrno)); goto out_unlock; } knet_h->sockfd[*channel].sockfd[0] = *datafd; knet_h->sockfd[*channel].sockfd[1] = 0; if (!getsockopt(knet_h->sockfd[*channel].sockfd[0], SOL_SOCKET, SO_TYPE, &sockopt, &sockoptlen)) { knet_h->sockfd[*channel].is_socket = 1; } } else { if (_init_socketpair(knet_h, knet_h->sockfd[*channel].sockfd)) { savederrno = errno; err = -1; goto out_unlock; } knet_h->sockfd[*channel].is_created = 1; knet_h->sockfd[*channel].is_socket = 1; *datafd = knet_h->sockfd[*channel].sockfd[0]; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = knet_h->sockfd[*channel].sockfd[knet_h->sockfd[*channel].is_created]; if (epoll_ctl(knet_h->send_to_links_epollfd, EPOLL_CTL_ADD, knet_h->sockfd[*channel].sockfd[knet_h->sockfd[*channel].is_created], &ev)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_HANDLE, "Unable to add datafd %d to linkfd epoll pool: %s", knet_h->sockfd[*channel].sockfd[knet_h->sockfd[*channel].is_created], strerror(savederrno)); if (knet_h->sockfd[*channel].is_created) { _close_socketpair(knet_h, knet_h->sockfd[*channel].sockfd); } goto out_unlock; } knet_h->sockfd[*channel].in_use = 1; out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_handle_remove_datafd(knet_handle_t knet_h, int datafd) { int err = 0, savederrno = 0; int8_t channel = -1; int i; struct epoll_event ev; if (!knet_h) { errno = EINVAL; return -1; } if (datafd <= 0) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } for (i = 0; i < KNET_DATAFD_MAX; i++) { if ((knet_h->sockfd[i].in_use) && (knet_h->sockfd[i].sockfd[0] == datafd)) { channel = i; break; } } if (channel < 0) { savederrno = EINVAL; err = -1; goto out_unlock; } if (!knet_h->sockfd[channel].has_error) { 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)) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_HANDLE, "Unable to del datafd %d from linkfd epoll pool: %s", knet_h->sockfd[channel].sockfd[0], strerror(savederrno)); goto out_unlock; } } if (knet_h->sockfd[channel].is_created) { _close_socketpair(knet_h, knet_h->sockfd[channel].sockfd); } memset(&knet_h->sockfd[channel], 0, sizeof(struct knet_sock)); out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_handle_get_datafd(knet_handle_t knet_h, const int8_t channel, int *datafd) { int err = 0, savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if ((channel < 0) || (channel >= KNET_DATAFD_MAX)) { errno = EINVAL; return -1; } if (datafd == NULL) { 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; } if (!knet_h->sockfd[channel].in_use) { savederrno = EINVAL; err = -1; goto out_unlock; } *datafd = knet_h->sockfd[channel].sockfd[0]; out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_handle_get_channel(knet_handle_t knet_h, const int datafd, int8_t *channel) { int err = 0, savederrno = 0; int i; if (!knet_h) { errno = EINVAL; return -1; } if (datafd <= 0) { errno = EINVAL; return -1; } if (channel == NULL) { 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; } *channel = -1; for (i = 0; i < KNET_DATAFD_MAX; i++) { if ((knet_h->sockfd[i].in_use) && (knet_h->sockfd[i].sockfd[0] == datafd)) { *channel = i; break; } } if (*channel < 0) { savederrno = EINVAL; err = -1; goto out_unlock; } out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_handle_enable_filter(knet_handle_t knet_h, void *dst_host_filter_fn_private_data, int (*dst_host_filter_fn) ( void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_node_id, + knet_node_id_t this_host_id, + knet_node_id_t src_node_id, int8_t *channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries)) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->dst_host_filter_fn_private_data = dst_host_filter_fn_private_data; knet_h->dst_host_filter_fn = dst_host_filter_fn; if (knet_h->dst_host_filter_fn) { log_debug(knet_h, KNET_SUB_HANDLE, "dst_host_filter_fn enabled"); } else { log_debug(knet_h, KNET_SUB_HANDLE, "dst_host_filter_fn disabled"); } pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_setfwd(knet_handle_t knet_h, unsigned int enabled) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if ((enabled < 0) || (enabled > 1)) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->enabled = enabled; if (enabled) { log_debug(knet_h, KNET_SUB_HANDLE, "Data forwarding is enabled"); } else { log_debug(knet_h, KNET_SUB_HANDLE, "Data forwarding is disabled"); } pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_pmtud_getfreq(knet_handle_t knet_h, unsigned int *interval) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!interval) { 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; } *interval = knet_h->pmtud_interval; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_pmtud_setfreq(knet_handle_t knet_h, unsigned int interval) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if ((!interval) || (interval > 86400)) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->pmtud_interval = interval; log_debug(knet_h, KNET_SUB_HANDLE, "PMTUd interval set to: %u seconds", interval); pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_enable_pmtud_notify(knet_handle_t knet_h, void *pmtud_notify_fn_private_data, void (*pmtud_notify_fn) ( void *private_data, unsigned int data_mtu)) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->pmtud_notify_fn_private_data = pmtud_notify_fn_private_data; knet_h->pmtud_notify_fn = pmtud_notify_fn; if (knet_h->pmtud_notify_fn) { log_debug(knet_h, KNET_SUB_HANDLE, "pmtud_notify_fn enabled"); } else { log_debug(knet_h, KNET_SUB_HANDLE, "pmtud_notify_fn disabled"); } pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_pmtud_get(knet_handle_t knet_h, unsigned int *data_mtu) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!data_mtu) { 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; } *data_mtu = knet_h->data_mtu; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_handle_crypto(knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg) { int savederrno = 0; int err = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!knet_handle_crypto_cfg) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HANDLE, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } crypto_fini(knet_h); if ((!strncmp("none", knet_handle_crypto_cfg->crypto_model, 4)) || ((!strncmp("none", knet_handle_crypto_cfg->crypto_cipher_type, 4)) && (!strncmp("none", knet_handle_crypto_cfg->crypto_hash_type, 4)))) { log_debug(knet_h, KNET_SUB_CRYPTO, "crypto is not enabled"); err = 0; goto exit_unlock; } if (knet_handle_crypto_cfg->private_key_len < KNET_MIN_KEY_LEN) { log_debug(knet_h, KNET_SUB_CRYPTO, "private key len too short (min %u): %u", KNET_MIN_KEY_LEN, knet_handle_crypto_cfg->private_key_len); savederrno = EINVAL; err = -1; goto exit_unlock; } if (knet_handle_crypto_cfg->private_key_len > KNET_MAX_KEY_LEN) { log_debug(knet_h, KNET_SUB_CRYPTO, "private key len too long (max %u): %u", KNET_MAX_KEY_LEN, knet_handle_crypto_cfg->private_key_len); savederrno = EINVAL; err = -1; goto exit_unlock; } err = crypto_init(knet_h, knet_handle_crypto_cfg); if (err) { err = -2; } exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } ssize_t knet_recv(knet_handle_t knet_h, char *buff, const size_t buff_len, const int8_t channel) { int savederrno = 0; ssize_t err = 0; struct iovec iov_in; 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_HANDLE, "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_unlock; } memset(&iov_in, 0, sizeof(iov_in)); iov_in.iov_base = (void *)buff; iov_in.iov_len = buff_len; err = readv(knet_h->sockfd[channel].sockfd[0], &iov_in, 1); savederrno = errno; out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } ssize_t knet_send(knet_handle_t knet_h, const char *buff, const size_t buff_len, const int8_t channel) { int savederrno = 0; ssize_t err = 0; struct iovec iov_out[1]; 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_HANDLE, "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_unlock; } memset(iov_out, 0, sizeof(iov_out)); iov_out[0].iov_base = (void *)buff; iov_out[0].iov_len = buff_len; err = writev(knet_h->sockfd[channel].sockfd[0], iov_out, 1); savederrno = errno; out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } diff --git a/libknet/host.c b/libknet/host.c index 968c4324..29408ec2 100644 --- a/libknet/host.c +++ b/libknet/host.c @@ -1,698 +1,698 @@ /* * Copyright (C) 2010-2015 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 "host.h" #include "internals.h" #include "logging.h" static void _host_list_update(knet_handle_t knet_h) { struct knet_host *host; knet_h->host_ids_entries = 0; for (host = knet_h->host_head; host != NULL; host = host->next) { knet_h->host_ids[knet_h->host_ids_entries] = host->host_id; knet_h->host_ids_entries++; } } -int knet_host_add(knet_handle_t knet_h, uint8_t host_id) +int knet_host_add(knet_handle_t knet_h, knet_node_id_t host_id) { int savederrno = 0, err = 0; struct knet_host *host = NULL; uint8_t link_idx; if (!knet_h) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (knet_h->host_index[host_id]) { err = -1; savederrno = EEXIST; log_err(knet_h, KNET_SUB_HOST, "Unable to add host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } host = malloc(sizeof(struct knet_host)); if (!host) { err = -1; savederrno = errno; log_err(knet_h, KNET_SUB_HOST, "Unable to allocate memory for host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } memset(host, 0, sizeof(struct knet_host)); /* * set host_id */ host->host_id = host_id; /* * set default host->name to host_id for logging */ snprintf(host->name, KNET_MAX_HOST_LEN - 1, "%u", host_id); /* * initialize links internal data */ for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { host->link[link_idx].link_id = link_idx; } /* * add new host to the index */ knet_h->host_index[host_id] = host; /* * add new host to host list */ if (knet_h->host_head) { host->next = knet_h->host_head; } knet_h->host_head = host; _host_list_update(knet_h); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); if (err < 0) { free(host); } errno = savederrno; return err; } -int knet_host_remove(knet_handle_t knet_h, uint8_t host_id) +int knet_host_remove(knet_handle_t knet_h, knet_node_id_t host_id) { int savederrno = 0, err = 0; struct knet_host *host, *removed; uint8_t link_idx; if (!knet_h) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Unable to remove host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } /* * if links are configured we cannot release the host */ for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (host->link[link_idx].configured) { err = -1; savederrno = EBUSY; log_err(knet_h, KNET_SUB_HOST, "Unable to remove host %u, links are still configured: %s", host_id, strerror(savederrno)); goto exit_unlock; } } removed = NULL; /* * removing host from list */ if (knet_h->host_head->host_id == host_id) { removed = knet_h->host_head; knet_h->host_head = removed->next; } else { for (host = knet_h->host_head; host->next != NULL; host = host->next) { if (host->next->host_id == host_id) { removed = host->next; host->next = removed->next; break; } } } knet_h->host_index[host_id] = NULL; free(removed); _host_list_update(knet_h); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_host_set_name(knet_handle_t knet_h, uint8_t host_id, const char *name) +int knet_host_set_name(knet_handle_t knet_h, knet_node_id_t host_id, const char *name) { int savederrno = 0, err = 0; struct knet_host *host; if (!knet_h) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h->host_index[host_id]) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Unable to find host %u to set name: %s", host_id, strerror(savederrno)); goto exit_unlock; } if (!name) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Unable to set name for host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } if (strlen(name) >= KNET_MAX_HOST_LEN) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Requested name for host %u is too long: %s", host_id, strerror(savederrno)); goto exit_unlock; } for (host = knet_h->host_head; host != NULL; host = host->next) { if (!strncmp(host->name, name, KNET_MAX_HOST_LEN - 1)) { err = -1; savederrno = EEXIST; log_err(knet_h, KNET_SUB_HOST, "Duplicated name found on host_id %u", host->host_id); goto exit_unlock; } } snprintf(knet_h->host_index[host_id]->name, KNET_MAX_HOST_LEN - 1, "%s", name); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_host_get_name_by_host_id(knet_handle_t knet_h, uint8_t host_id, +int knet_host_get_name_by_host_id(knet_handle_t knet_h, knet_node_id_t host_id, char *name) { int savederrno = 0, err = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!name) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h->host_index[host_id]) { savederrno = EINVAL; err = -1; log_debug(knet_h, KNET_SUB_HOST, "Host %u not found", host_id); goto exit_unlock; } snprintf(name, KNET_MAX_HOST_LEN - 1, "%s", knet_h->host_index[host_id]->name); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_host_get_id_by_host_name(knet_handle_t knet_h, const char *name, - uint8_t *host_id) + knet_node_id_t *host_id) { int savederrno = 0, err = 0, found = 0; struct knet_host *host; if (!knet_h) { errno = EINVAL; return -1; } if (!name) { errno = EINVAL; return -1; } if (!host_id) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } for (host = knet_h->host_head; host != NULL; host = host->next) { if (!strncmp(name, host->name, KNET_MAX_HOST_LEN)) { found = 1; *host_id = host->host_id; break; } } if (!found) { savederrno = ENOENT; err = -1; } pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_host_get_host_list(knet_handle_t knet_h, - uint8_t *host_ids, size_t *host_ids_entries) + knet_node_id_t *host_ids, size_t *host_ids_entries) { int savederrno = 0, err = 0; if (!knet_h) { errno = EINVAL; return -1; } if ((!host_ids) || (!host_ids_entries)) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } memmove(host_ids, knet_h->host_ids, sizeof(knet_h->host_ids)); *host_ids_entries = knet_h->host_ids_entries; pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_host_set_policy(knet_handle_t knet_h, uint8_t host_id, +int knet_host_set_policy(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t policy) { int savederrno = 0, err = 0; uint8_t old_policy; if (!knet_h) { errno = EINVAL; return -1; } if (policy > KNET_LINK_POLICY_RR) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h->host_index[host_id]) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Unable to set name for host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } old_policy = knet_h->host_index[host_id]->link_handler_policy; knet_h->host_index[host_id]->link_handler_policy = policy; if (_host_dstcache_update_async(knet_h, knet_h->host_index[host_id])) { savederrno = errno; err = -1; knet_h->host_index[host_id]->link_handler_policy = old_policy; log_debug(knet_h, KNET_SUB_HOST, "Unable to update switch cache for host %u: %s", host_id, strerror(savederrno)); } log_debug(knet_h, KNET_SUB_HOST, "Host %u has new switching policy: %u", host_id, policy); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_host_get_policy(knet_handle_t knet_h, uint8_t host_id, +int knet_host_get_policy(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t *policy) { int savederrno = 0, err = 0; if (!knet_h) { errno = EINVAL; return -1; } if (!policy) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } if (!knet_h->host_index[host_id]) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Unable to get name for host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } *policy = knet_h->host_index[host_id]->link_handler_policy; exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_host_get_status(knet_handle_t knet_h, uint8_t host_id, +int knet_host_get_status(knet_handle_t knet_h, knet_node_id_t host_id, struct knet_host_status *status) { int savederrno = 0, err = 0; struct knet_host *host; if (!knet_h) { errno = EINVAL; return -1; } if (!status) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_HOST, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } memmove(status, &host->status, sizeof(struct knet_host_status)); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } int knet_host_enable_status_change_notify(knet_handle_t knet_h, void *host_status_change_notify_fn_private_data, void (*host_status_change_notify_fn) ( void *private_data, - uint8_t host_id, + knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external)) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_HOST, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->host_status_change_notify_fn_private_data = host_status_change_notify_fn_private_data; knet_h->host_status_change_notify_fn = host_status_change_notify_fn; if (knet_h->host_status_change_notify_fn) { log_debug(knet_h, KNET_SUB_HOST, "host_status_change_notify_fn enabled"); } else { log_debug(knet_h, KNET_SUB_HOST, "host_status_change_notify_fn disabled"); } pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int _send_host_info(knet_handle_t knet_h, const void *data, const size_t datalen) { if (knet_h->fini_in_progress) { return 0; } if (sendto(knet_h->hostsockfd[1], data, datalen, MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != datalen) { log_debug(knet_h, KNET_SUB_HOST, "Unable to write data to hostpipe"); return -1; } return 0; } static void _clear_cbuffers(struct knet_host *host, seq_num_t rx_seq_num) { int i; memset(host->circular_buffer, 0, KNET_CBUFFER_SIZE); host->rx_seq_num = rx_seq_num; memset(host->circular_buffer_defrag, 0, KNET_CBUFFER_SIZE); for (i = 0; i < KNET_MAX_LINK; i++) { memset(&host->defrag_buf[i], 0, sizeof(struct knet_host_defrag_buf)); } } /* * check if a given packet seq num is in the circular buffers * defrag_buf = 0 -> use normal cbuf 1 -> use the defrag buffer lookup */ int _seq_num_lookup(struct knet_host *host, seq_num_t seq_num, int defrag_buf, int clear_buf) { size_t i, j; /* circular buffer indexes */ seq_num_t seq_dist; char *dst_cbuf = host->circular_buffer; char *dst_cbuf_defrag = host->circular_buffer_defrag; seq_num_t *dst_seq_num = &host->rx_seq_num; if (clear_buf) { _clear_cbuffers(host, seq_num); } if (seq_num < *dst_seq_num) { seq_dist = (SEQ_MAX - seq_num) + *dst_seq_num; } else { seq_dist = *dst_seq_num - seq_num; } j = seq_num % KNET_CBUFFER_SIZE; if (seq_dist < KNET_CBUFFER_SIZE) { /* seq num is in ring buffer */ if (!defrag_buf) { return (dst_cbuf[j] == 0) ? 1 : 0; } else { return (dst_cbuf_defrag[j] == 0) ? 1 : 0; } } else if (seq_dist <= SEQ_MAX - KNET_CBUFFER_SIZE) { memset(dst_cbuf, 0, KNET_CBUFFER_SIZE); memset(dst_cbuf_defrag, 0, KNET_CBUFFER_SIZE); *dst_seq_num = seq_num; } /* cleaning up circular buffer */ i = (*dst_seq_num + 1) % KNET_CBUFFER_SIZE; if (i > j) { memset(dst_cbuf + i, 0, KNET_CBUFFER_SIZE - i); memset(dst_cbuf, 0, j + 1); memset(dst_cbuf_defrag + i, 0, KNET_CBUFFER_SIZE - i); memset(dst_cbuf_defrag, 0, j + 1); } else { memset(dst_cbuf + i, 0, j - i + 1); memset(dst_cbuf_defrag + i, 0, j - i + 1); } *dst_seq_num = seq_num; return 1; } void _seq_num_set(struct knet_host *host, seq_num_t seq_num, int defrag_buf) { if (!defrag_buf) { host->circular_buffer[seq_num % KNET_CBUFFER_SIZE] = 1; } else { host->circular_buffer_defrag[seq_num % KNET_CBUFFER_SIZE] = 1; } return; } int _host_dstcache_update_async(knet_handle_t knet_h, struct knet_host *host) { int savederrno = 0; - uint8_t host_id = host->host_id; + knet_node_id_t host_id = host->host_id; if (sendto(knet_h->dstsockfd[1], &host_id, sizeof(host_id), MSG_DONTWAIT | MSG_NOSIGNAL, NULL, 0) != sizeof(host_id)) { savederrno = errno; log_debug(knet_h, KNET_SUB_HOST, "Unable to write to dstpipefd[1]: %s", strerror(savederrno)); errno = savederrno; return -1; } return 0; } int _host_dstcache_update_sync(knet_handle_t knet_h, struct knet_host *host) { int link_idx; int best_priority = -1; int reachable = 0; host->active_link_entries = 0; for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if (host->link[link_idx].status.enabled != 1) /* link is not enabled */ continue; if (host->link[link_idx].status.connected != 1) /* link is not enabled */ continue; if (host->link[link_idx].has_valid_mtu != 1) /* link does not have valid MTU */ continue; if (host->link_handler_policy == KNET_LINK_POLICY_PASSIVE) { /* for passive we look for the only active link with higher priority */ if (host->link[link_idx].priority > best_priority) { host->active_links[0] = link_idx; best_priority = host->link[link_idx].priority; } host->active_link_entries = 1; } else { /* for RR and ACTIVE we need to copy all available links */ host->active_links[host->active_link_entries] = link_idx; host->active_link_entries++; } } if (host->link_handler_policy == KNET_LINK_POLICY_PASSIVE) { log_debug(knet_h, KNET_SUB_HOST, "host: %u (passive) best link: %u (pri: %u)", host->host_id, host->link[host->active_links[0]].link_id, host->link[host->active_links[0]].priority); } else { log_debug(knet_h, KNET_SUB_HOST, "host: %u has %u active links", host->host_id, host->active_link_entries); } /* no active links, we can clean the circular buffers and indexes */ if (!host->active_link_entries) { log_warn(knet_h, KNET_SUB_HOST, "host: %u has no active links", host->host_id); _clear_cbuffers(host, 0); } else { reachable = 1; } if (host->status.reachable != reachable) { host->status.reachable = reachable; if (knet_h->host_status_change_notify_fn) { knet_h->host_status_change_notify_fn( knet_h->host_status_change_notify_fn_private_data, host->host_id, host->status.reachable, host->status.remote, host->status.external); } } return 0; } diff --git a/libknet/internals.h b/libknet/internals.h index 2bd917e8..7c3405e5 100644 --- a/libknet/internals.h +++ b/libknet/internals.h @@ -1,462 +1,462 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __INTERNALS_H__ #define __INTERNALS_H__ /* * NOTE: you shouldn't need to include this header normally */ #include "libknet.h" #include "onwire.h" #include "compat.h" #define KNET_DATABUFSIZE KNET_MAX_PACKET_SIZE + KNET_HEADER_ALL_SIZE #define KNET_DATABUFSIZE_CRYPT_PAD 1024 #define KNET_DATABUFSIZE_CRYPT KNET_DATABUFSIZE + KNET_DATABUFSIZE_CRYPT_PAD #define KNET_RING_RCVBUFF 8388608 #define PCKT_FRAG_MAX UINT8_MAX #define KNET_EPOLL_MAX_EVENTS KNET_DATAFD_MAX typedef void *knet_transport_link_t; /* per link transport handle */ typedef void *knet_transport_t; /* per knet_h transport handle */ struct knet_transport_ops; /* Forward because of circular dependancy */ struct knet_link { /* required */ struct sockaddr_storage src_addr; struct sockaddr_storage dst_addr; /* configurable */ unsigned int dynamic; /* see KNET_LINK_DYN_ define above */ uint8_t priority; /* higher priority == preferred for A/P */ unsigned long long ping_interval; /* interval */ unsigned long long pong_timeout; /* timeout */ unsigned int latency_fix; /* precision */ uint8_t pong_count; /* how many ping/pong to send/receive before link is up */ /* status */ struct knet_link_status status; /* internals */ uint8_t link_id; uint8_t transport_type; /* #defined constant from API */ knet_transport_link_t transport_link; /* link_info_t from transport */ int outsock; unsigned int configured:1; /* set to 1 if src/dst have been configured transport initialized on this link*/ unsigned int transport_connected:1; /* set to 1 if lower level transport is connected */ unsigned int latency_exp; uint8_t received_pong; struct timespec ping_last; /* used by PMTUD thread as temp per-link variables and should always contain the onwire_len value! */ uint32_t proto_overhead; struct timespec pmtud_last; uint32_t last_ping_size; uint32_t last_good_mtu; uint32_t last_bad_mtu; uint32_t last_sent_mtu; uint32_t last_recv_mtu; uint8_t has_valid_mtu; }; #define KNET_CBUFFER_SIZE 4096 struct knet_host_defrag_buf { char buf[KNET_DATABUFSIZE]; uint8_t in_use; /* 0 buffer is free, 1 is in use */ seq_num_t pckt_seq; /* identify the pckt we are receiving */ uint8_t frag_recv; /* how many frags did we receive */ uint8_t frag_map[PCKT_FRAG_MAX];/* bitmap of what we received? */ uint8_t last_first; /* special case if we receive the last fragment first */ uint16_t frag_size; /* normal frag size (not the last one) */ uint16_t last_frag_size; /* the last fragment might not be aligned with MTU size */ struct timespec last_update; /* keep time of the last pckt */ }; struct knet_host { /* required */ - uint8_t host_id; + knet_node_id_t host_id; /* configurable */ uint8_t link_handler_policy; char name[KNET_MAX_HOST_LEN]; /* status */ struct knet_host_status status; /* internals */ char circular_buffer[KNET_CBUFFER_SIZE]; seq_num_t rx_seq_num; seq_num_t untimed_rx_seq_num; seq_num_t timed_rx_seq_num; uint8_t got_data; /* defrag/reassembly buffers */ struct knet_host_defrag_buf defrag_buf[KNET_MAX_LINK]; char circular_buffer_defrag[KNET_CBUFFER_SIZE]; /* link stuff */ struct knet_link link[KNET_MAX_LINK]; uint8_t active_link_entries; uint8_t active_links[KNET_MAX_LINK]; struct knet_host *next; }; struct knet_sock { int sockfd[2]; /* sockfd[0] will always be application facing * and sockfd[1] internal if sockpair has been created by knet */ int is_socket; /* check if it's a socket for recvmmsg usage */ int is_created; /* knet created this socket and has to clean up on exit/del */ int in_use; /* set to 1 if it's use, 0 if free */ int has_error; /* set to 1 if there were errors reading from the sock * and socket has been removed from epoll */ }; struct knet_fd_trackers { uint8_t transport; /* transport type (UDP/SCTP...) */ uint8_t data_type; /* internal use for transport to define what data are associated * to this fd */ void *data; /* pointer to the data */ }; #define KNET_MAX_FDS KNET_MAX_HOST * KNET_MAX_LINK * 4 struct knet_handle { - uint8_t host_id; + knet_node_id_t host_id; unsigned int enabled:1; struct knet_sock sockfd[KNET_DATAFD_MAX]; int logfd; uint8_t log_levels[KNET_MAX_SUBSYSTEMS]; int hostsockfd[2]; int dstsockfd[2]; int send_to_links_epollfd; int recv_from_links_epollfd; int dst_link_handler_epollfd; unsigned int pmtud_interval; unsigned int data_mtu; /* contains the max data size that we can send onwire * without frags */ struct knet_host *host_head; struct knet_host *host_index[KNET_MAX_HOST]; knet_transport_t transports[KNET_MAX_TRANSPORTS+1]; struct knet_transport_ops *transport_ops[KNET_MAX_TRANSPORTS+1]; struct knet_fd_trackers knet_transport_fd_tracker[KNET_MAX_FDS]; /* track status for each fd handled by transports */ - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; size_t host_ids_entries; struct knet_header *recv_from_sock_buf[PCKT_FRAG_MAX]; struct knet_header *send_to_links_buf[PCKT_FRAG_MAX]; struct knet_header *recv_from_links_buf[PCKT_FRAG_MAX]; struct knet_header *pingbuf; struct knet_header *pmtudbuf; pthread_t send_to_links_thread; pthread_t recv_from_links_thread; pthread_t heartbt_thread; pthread_t dst_link_handler_thread; pthread_t pmtud_link_handler_thread; int lock_init_done; pthread_rwlock_t global_rwlock; /* global config lock */ pthread_mutex_t pmtud_mutex; /* pmtud mutex to handle conditional send/recv + timeout */ pthread_cond_t pmtud_cond; /* conditional for above */ pthread_mutex_t tx_mutex; /* used to protect knet_send_sync and TX thread */ pthread_mutex_t hb_mutex; /* used to protect heartbeat thread and seq_num broadcasting */ struct crypto_instance *crypto_instance; uint16_t sec_header_size; uint16_t sec_block_size; uint16_t sec_hash_size; uint16_t sec_salt_size; unsigned char *send_to_links_buf_crypt[PCKT_FRAG_MAX]; unsigned char *recv_from_links_buf_crypt; unsigned char *recv_from_links_buf_decrypt; unsigned char *pingbuf_crypt; unsigned char *pmtudbuf_crypt; seq_num_t tx_seq_num; pthread_mutex_t tx_seq_num_mutex; void *dst_host_filter_fn_private_data; int (*dst_host_filter_fn) ( void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_node_id, + knet_node_id_t this_host_id, + knet_node_id_t src_node_id, int8_t *channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries); void *pmtud_notify_fn_private_data; void (*pmtud_notify_fn) ( void *private_data, unsigned int data_mtu); void *host_status_change_notify_fn_private_data; void (*host_status_change_notify_fn) ( void *private_data, - uint8_t host_id, + knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external); void *sock_notify_fn_private_data; void (*sock_notify_fn) ( void *private_data, int datafd, int8_t channel, uint8_t tx_rx, int error, int errorno); int fini_in_progress; }; /* * NOTE: every single operation must be implementend * for every protocol. */ typedef struct knet_transport_ops { /* * transport generic information */ const char *transport_name; const uint8_t transport_id; uint32_t transport_mtu_overhead; /* * transport init must allocate the new transport * and perform all internal initializations * (threads, lists, etc). */ int (*transport_init)(knet_handle_t knet_h); /* * transport free must releases _all_ resources * allocated by tranport_init */ int (*transport_free)(knet_handle_t knet_h); /* * link operations should take care of all the * sockets and epoll management for a given link/transport set * transport_link_disable should return err = -1 and errno = EBUSY * if listener is still in use, and any other errno in case * the link cannot be disabled. * * set_config/clear_config are invoked in global write lock context */ int (*transport_link_set_config)(knet_handle_t knet_h, struct knet_link *link); int (*transport_link_clear_config)(knet_handle_t knet_h, struct knet_link *link); /* * transport callback for incoming dynamic connections * this is called in global read lock context */ int (*transport_link_dyn_connect)(knet_handle_t knet_h, int sockfd, struct knet_link *link); /* * per transport error handling of recvmmsg * (see _handle_recv_from_links comments for details) */ /* * transport_rx_sock_error is invoked when recvmmsg returns <= 0 * * transport_rx_sock_error is invoked with both global_rdlock */ int (*transport_rx_sock_error)(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno); /* * transport_tx_sock_error is invoked with global_rwlock and * it's invoked when sendto or sendmmsg returns =< 0 * * it should return: * -1 on internal error * 0 ignore error and continue * 1 retry * any sleep or wait action should happen inside the transport code */ int (*transport_tx_sock_error)(knet_handle_t knet_h, int sockfd, int recv_err, int recv_errno); /* * this function is called on _every_ received packet * to verify if the packet is data or internal protocol error handling * * it should return: * -1 on error * 0 packet is not data and we should continue the packet process loop * 1 packet is not data and we should STOP the packet process loop * 2 packet is data and should be parsed as such * * transport_rx_is_data is invoked with both global_rwlock * and fd_tracker read lock (from RX thread) */ int (*transport_rx_is_data)(knet_handle_t knet_h, int sockfd, struct mmsghdr *msg); } knet_transport_ops_t; socklen_t sockaddr_len(const struct sockaddr_storage *ss); /** * This is a kernel style list implementation. * * @author Steven Dake */ struct knet_list_head { struct knet_list_head *next; struct knet_list_head *prev; }; /** * @def KNET_LIST_DECLARE() * Declare and initialize a list head. */ #define KNET_LIST_DECLARE(name) \ struct knet_list_head name = { &(name), &(name) } #define KNET_INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /** * Initialize the list entry. * * Points next and prev pointers to head. * @param head pointer to the list head */ static inline void knet_list_init(struct knet_list_head *head) { head->next = head; head->prev = head; } /** * Add this element to the list. * * @param element the new element to insert. * @param head pointer to the list head */ static inline void knet_list_add(struct knet_list_head *element, struct knet_list_head *head) { head->next->prev = element; element->next = head->next; element->prev = head; head->next = element; } /** * Add to the list (but at the end of the list). * * @param element pointer to the element to add * @param head pointer to the list head * @see knet_list_add() */ static inline void knet_list_add_tail(struct knet_list_head *element, struct knet_list_head *head) { head->prev->next = element; element->next = head; element->prev = head->prev; head->prev = element; } /** * Delete an entry from the list. * * @param _remove the list item to remove */ static inline void knet_list_del(struct knet_list_head *_remove) { _remove->next->prev = _remove->prev; _remove->prev->next = _remove->next; } /** * Replace old entry by new one * @param old: the element to be replaced * @param new: the new element to insert */ static inline void knet_list_replace(struct knet_list_head *old, struct knet_list_head *new) { new->next = old->next; new->next->prev = new; new->prev = old->prev; new->prev->next = new; } /** * Tests whether list is the last entry in list head * @param list: the entry to test * @param head: the head of the list * @return boolean true/false */ static inline int knet_list_is_last(const struct knet_list_head *list, const struct knet_list_head *head) { return list->next == head; } /** * A quick test to see if the list is empty (pointing to it's self). * @param head pointer to the list head * @return boolean true/false */ static inline int32_t knet_list_empty(const struct knet_list_head *head) { return head->next == head; } /** * Get the struct for this entry * @param ptr: the &struct list_head pointer. * @param type: the type of the struct this is embedded in. * @param member: the name of the list_struct within the struct. */ #define knet_list_entry(ptr,type,member)\ ((type *)((char *)(ptr)-(char*)(&((type *)0)->member))) /** * Get the first element from a list * @param ptr: the &struct list_head pointer. * @param type: the type of the struct this is embedded in. * @param member: the name of the list_struct within the struct. */ #define knet_list_first_entry(ptr, type, member) \ knet_list_entry((ptr)->next, type, member) /** * Iterate over a list * @param pos: the &struct list_head to use as a loop counter. * @param head: the head for your list. */ #define knet_list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * Iterate over a list backwards * @param pos: the &struct list_head to use as a loop counter. * @param head: the head for your list. */ #define knet_list_for_each_reverse(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * Iterate over a list safe against removal of list entry * @param pos: the &struct list_head to use as a loop counter. * @param n: another &struct list_head to use as temporary storage * @param head: the head for your list. */ #define knet_list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * Iterate over list of given type * @param pos: the type * to use as a loop counter. * @param head: the head for your list. * @param member: the name of the list_struct within the struct. */ #define knet_list_for_each_entry(pos, head, member) \ for (pos = knet_list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = knet_list_entry(pos->member.next, typeof(*pos), member)) #endif diff --git a/libknet/libknet.h b/libknet/libknet.h index 4ddfa48e..b5b0e867 100644 --- a/libknet/libknet.h +++ b/libknet/libknet.h @@ -1,1462 +1,1463 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __LIBKNET_H__ #define __LIBKNET_H__ #include #include /* * libknet limits */ /* * Maximum number of hosts */ -#define KNET_MAX_HOST 256 +typedef uint16_t knet_node_id_t; +#define KNET_MAX_HOST 65536 /* * Maximum number of links between 2 hosts */ #define KNET_MAX_LINK 8 /* * Maximum packet size that should be written to datafd * see knet_handle_new for details */ #define KNET_MAX_PACKET_SIZE 65536 /* * Buffers used for pretty logging * host is used to store both ip addresses and hostnames */ #define KNET_MAX_HOST_LEN 256 #define KNET_MAX_PORT_LEN 6 /* * Some notifications can be generated either on TX or RX */ #define KNET_NOTIFY_TX 0 #define KNET_NOTIFY_RX 1 typedef struct knet_handle *knet_handle_t; /* * Handle structs/API calls */ /* * knet_handle_new * * host_id - Each host in a knet is identified with a unique * ID. when creating a new handle local host_id * must be specified (0 to UINT16T_MAX are all valid). * It is the user's responsibility to check that the value * is unique, or bad things might happen. * * log_fd - Write file descriptor. If set to a value > 0, it will be used * to write log packets (see below) from libknet to the application. * Setting to 0 will disable logging from libknet. * It is possible to enable logging at any given time (see logging API * below). * Make sure to either read from this filedescriptor properly and/or * mark it O_NONBLOCK, otherwise if the fd becomes full, libknet could * block. * * default_log_level - * If logfd is specified, it will initialize all subsystems to log * at default_log_level value. (see logging API below) * * on success, a new knet_handle_t is returned. * on failure, NULL is returned and errno is set. */ -knet_handle_t knet_handle_new(uint8_t host_id, +knet_handle_t knet_handle_new(knet_node_id_t host_id, int log_fd, uint8_t default_log_level); /* * knet_handle_free * * knet_h - pointer to knet_handle_t * * Destroy a knet handle, free all resources * * knet_handle_free returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_free(knet_handle_t knet_h); /* * knet_handle_enable_sock_notify * * knet_h - pointer to knet_handle_t * * sock_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * sock_notify_fn * A callback function that is invoked every time * a socket in the datafd pool will report an error (-1) * or an end of read (0) (see socket.7). * This function MUST NEVER block or add substantial delays. * The callback is invoked in an internal unlocked area * to allow calls to knet_handle_add_datafd/knet_handle_remove_datafd * to swap/replace the bad fd. * if both err and errno are 0, it means that the socket * has received a 0 byte packet (EOF?). * The callback function must either remove the fd from knet * (by calling knet_handle_remove_fd()) or dup a new fd in its place. * Failure to do this can cause problems. * * knet_handle_enable_sock_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_enable_sock_notify(knet_handle_t knet_h, void *sock_notify_fn_private_data, void (*sock_notify_fn) ( void *private_data, int datafd, int8_t channel, uint8_t tx_rx, int error, int errorno)); /* sorry! can't call it errno ;) */ /* * knet_handle_add_datafd * * IMPORTANT: In order to add datafd to knet, knet_handle_enable_sock_notify * _MUST_ be set and be able to handle both errors (-1) and * 0 bytes read / write from the provided datafd. * On read error (< 0) from datafd, the socket is automatically * removed from polling to avoid spinning on dead sockets. * It is safe to call knet_handle_remove_datafd even on sockets * that have been removed. * * knet_h - pointer to knet_handle_t * * *datafd - read/write file descriptor. * knet will read data here to send to the other hosts * and will write data received from the network. * Each data packet can be of max size KNET_MAX_PACKET_SIZE! * Applications using knet_send/knet_recv will receive a * proper error if the packet size is not within boundaries. * Applications using their own functions to write to the * datafd should NOT write more than KNET_MAX_PACKET_SIZE. * * Please refer to handle.c on how to set up a socketpair. * * datafd can be 0, and knet_handle_add_datafd will create a properly * populated socket pair the same way as ping_test, or a value * higher than 0. A negative number will return an error. * On exit knet_handle_free will take care to cleanup the * socketpair only if they have been created by knet_handle_add_datafd. * * It is possible to pass either sockets or normal fds. * User provided datafd will be marked as non-blocking and close-on-exit. * * *channel - This value has the same effect of VLAN tagging. * A negative value will auto-allocate a channel. * Setting a value between 0 and 31 will try to allocate that * specific channel (unless already in use). * * It is possible to add up to 32 datafds but be aware that each * one of them must have a receiving end on the other host. * * Example: * hostA channel 0 will be delivered to datafd on hostB channel 0 * hostA channel 1 to hostB channel 1. * * Each channel must have a unique file descriptor. * * If your application could have 2 channels on one host and one * channel on another host, then you can use dst_host_filter * to manipulate channel values on TX and RX. * * knet_handle_add_datafd returns: * * 0 on success * *datafd will be populated with a socket if the original value was 0 * or if a specific fd was set, the value is untouched. * *channel will be populated with a channel number if the original value * was negative or the value is untouched if a specific channel * was requested. * * -1 on error and errno is set. * *datafd and *channel are untouched or empty. */ #define KNET_DATAFD_MAX 32 int knet_handle_add_datafd(knet_handle_t knet_h, int *datafd, int8_t *channel); /* * knet_handle_remove_datafd * * knet_h - pointer to knet_handle_t * * datafd - file descriptor to remove. * NOTE that if the socket/fd was created by knet_handle_add_datafd, * the socket will be closed by libknet. * * knet_handle_remove_datafd returns: * * 0 on success * * -1 on error and errno is set. */ int knet_handle_remove_datafd(knet_handle_t knet_h, int datafd); /* * knet_handle_enable_sock_notify * * knet_h - pointer to knet_handle_t * * sock_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * sock_notify_fn * A callback function that is invoked every time * a socket in the datafd pool will report an error (-1) * or an end of read (0) (see socket.7). * This function MUST NEVER block or add substantial delays. * The callback is invoked in an internal unlocked area * to allow calls to knet_handle_add_datafd/knet_handle_remove_datafd * to swap/replace the bad fd. * if both err and errno are 0, it means that the socket * has received a 0 byte packet (EOF?). * The callback function must either remove the fd from knet * (by calling knet_handle_remove_fd()) or dup a new fd in its place. * Failure to do this can cause problems. * * knet_handle_enable_sock_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_get_channel(knet_handle_t knet_h, const int datafd, int8_t *channel); /* * knet_handle_get_datafd * * knet_h - pointer to knet_handle_t * * channel - get the datafd associated to this channel * * *datafd - will contain the result * * knet_handle_get_datafd returns: * * 0 on success * and *datafd will contain the results * * -1 on error and errno is set. * and *datafd content is meaningless */ int knet_handle_get_datafd(knet_handle_t knet_h, const int8_t channel, int *datafd); /* * knet_recv * * knet_h - pointer to knet_handle_t * * buff - pointer to buffer to store the received data * * buff_len - buffer lenght * * knet_recv is a commodity function to wrap iovec operations * around a socket. It returns a call to readv(2). */ ssize_t knet_recv(knet_handle_t knet_h, char *buff, const size_t buff_len, const int8_t channel); /* * knet_send * * knet_h - pointer to knet_handle_t * * buff - pointer to the buffer of data to send * * buff_len - length of data to send * * knet_send is a commodity function to wrap iovec operations * around a socket. It returns a call to writev(2). */ ssize_t knet_send(knet_handle_t knet_h, const char *buff, const size_t buff_len, const int8_t channel); /* * knet_send_sync * * knet_h - pointer to knet_handle_t * * buff - pointer to the buffer of data to send * * buff_len - length of data to send * * channel - data channel to use (see knet_handle_add_datafd) * * All knet RX/TX operations are async for performance reasons. * There are applications that might need a sync version of data * transmission and receive errors in case of failure to deliver * to another host. * knet_send_sync bypasses the whole TX async layer and delivers * data directly to the link layer, and returns errors accordingly. * knet_send_sync allows to send only one packet to one host at * a time. It does NOT support multiple destinations or multicast * packets. Decision is still based on dst_host_filter_fn. * * knet_send_sync returns 0 on success and -1 on error. * * In addition to normal sendmmsg errors, knet_send_sync can fail * due to: * * ECANCELED - data forward is disabled * EFAULT - dst_host_filter fatal error * EINVAL - dst_host_filter did not provide * dst_host_ids_entries on unicast pckts * E2BIG - dst_host_filter did return more than one * dst_host_ids_entries on unicast pckts * ENOMSG - received unknown message type * EHOSTDOWN - unicast pckt cannot be delivered because * dest host is not connected yet * ECHILD - crypto failed * EAGAIN - sendmmsg was unable to send all messages and * there was no progress during retry */ int knet_send_sync(knet_handle_t knet_h, const char *buff, const size_t buff_len, const int8_t channel); /* * knet_handle_enable_filter * * knet_h - pointer to knet_handle_t * * dst_host_filter_fn_private_data * void pointer to data that can be used to identify * the callback. * * dst_host_filter_fn - * is a callback function that is invoked every time * a packet hits datafd (see knet_handle_new). * the function allows users to tell libknet where the * packet has to be delivered. * * const unsigned char *outdata - is a pointer to the * current packet * ssize_t outdata_len - lenght of the above data * uint8_t tx_rx - filter is called on tx or rx * (see defines below) - * uint8_t this_host_id - host_id processing the packet - * uint8_t src_host_id - host_id that generated the + * knet_node_id_t this_host_id - host_id processing the packet + * knet_node_id_t src_host_id - host_id that generated the * packet - * uint8_t *dst_host_ids - array of KNET_MAX_HOST uint8_t + * knet_node_id_t *dst_host_ids - array of KNET_MAX_HOST knet_node_id_t * where to store the destinations * size_t *dst_host_ids_entries - number of hosts to send the message * * dst_host_filter_fn should return * -1 on error, packet is discarded. * 0 packet is unicast and should be sent to dst_host_ids and there are * dst_host_ids_entries in the buffer. * 1 packet is broadcast/multicast and is sent all hosts. * contents of dst_host_ids and dst_host_ids_entries are ignored. * (see also kronosnetd/etherfilter.* for an example that filters based * on ether protocol) * * knet_handle_enable_filter returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_enable_filter(knet_handle_t knet_h, void *dst_host_filter_fn_private_data, int (*dst_host_filter_fn) ( void *private_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_host_id, + knet_node_id_t this_host_id, + knet_node_id_t src_host_id, int8_t *channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries)); /* * knet_handle_setfwd * * knet_h - pointer to knet_handle_t * * enable - set to 1 to allow data forwarding, 0 to disable data forwarding. * * knet_handle_setfwd returns: * * 0 on success * -1 on error and errno is set. * * By default data forwarding is off and no traffic will pass through knet until * it is set on. */ int knet_handle_setfwd(knet_handle_t knet_h, unsigned int enabled); /* * knet_handle_pmtud_setfreq * * knet_h - pointer to knet_handle_t * * interval - define the interval in seconds between PMTUd scans * range from 1 to 86400 (24h) * * knet_handle_pmtud_setfreq returns: * * 0 on success * -1 on error and errno is set. * * default interval is 60. */ #define KNET_PMTUD_DEFAULT_INTERVAL 60 int knet_handle_pmtud_setfreq(knet_handle_t knet_h, unsigned int interval); /* * knet_handle_pmtud_getfreq * * knet_h - pointer to knet_handle_t * * interval - pointer where to store the current interval value * * knet_handle_pmtud_setfreq returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_pmtud_getfreq(knet_handle_t knet_h, unsigned int *interval); /* * knet_handle_enable_pmtud_notify * * knet_h - pointer to knet_handle_t * * pmtud_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * pmtud_notify_fn * is a callback function that is invoked every time * a path MTU size change is detected. * The function allows libknet to notify the user * of data MTU, that's the max value that can be send * onwire without fragmentation. The data MTU will always * be lower than real link MTU because it accounts for * protocol overhead, knet packet header and (if configured) * crypto overhead, * This function MUST NEVER block or add substantial delays. * * knet_handle_enable_pmtud_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_enable_pmtud_notify(knet_handle_t knet_h, void *pmtud_notify_fn_private_data, void (*pmtud_notify_fn) ( void *private_data, unsigned int data_mtu)); /* * knet_handle_pmtud_get * * knet_h - pointer to knet_handle_t * * data_mtu - pointer where to store data_mtu (see above) * * knet_handle_pmtud_get returns: * * 0 on success * -1 on error and errno is set. */ int knet_handle_pmtud_get(knet_handle_t knet_h, unsigned int *data_mtu); /* * knet_handle_crypto * * knet_h - pointer to knet_handle_t * * knet_handle_crypto_cfg - * pointer to a knet_handle_crypto_cfg structure * * crypto_model should contain the model name. * Currently only "nss" is supported. * Setting to "none" will disable crypto. * * crypto_cipher_type * should contain the cipher algo name. * It can be set to "none" to disable * encryption. * Currently supported by "nss" model: * "3des", "aes128", "aes192" and "aes256". * * crypto_hash_type * should contain the hashing algo name. * It can be set to "none" to disable * hashing. * Currently supported by "nss" model: * "md5", "sha1", "sha256", "sha384" and "sha512". * * private_key will contain the private shared key. * It has to be at least KNET_MIN_KEY_LEN long. * * private_key_len * length of the provided private_key. * * Implementation notes/current limitations: * - enabling crypto, will increase latency as packets have * to processed. * - enabling crypto might reduce the overall throughtput * due to crypto data overhead. * - re-keying is not implemented yet. * - private/public key encryption/hashing is not currently * planned. * - crypto key must be the same for all hosts in the same * knet instance. * - it is safe to call knet_handle_crypto multiple times at runtime. * The last config will be used. * IMPORTANT: a call to knet_handle_crypto can fail due to: * 1) failure to obtain locking * 2) errors to initializing the crypto level. * This can happen even in subsequent calls to knet_handle_crypto. * A failure in crypto init, might leave your traffic unencrypted! * It's best to stop data forwarding (see above), change crypto config, * start forward again. * * knet_handle_crypto returns: * * 0 on success * -1 on error and errno is set. * -2 on crypto subsystem initialization error. No errno is provided at the moment (yet). */ #define KNET_MIN_KEY_LEN 1024 #define KNET_MAX_KEY_LEN 4096 struct knet_handle_crypto_cfg { char crypto_model[16]; char crypto_cipher_type[16]; char crypto_hash_type[16]; unsigned char private_key[KNET_MAX_KEY_LEN]; unsigned int private_key_len; }; int knet_handle_crypto(knet_handle_t knet_h, struct knet_handle_crypto_cfg *knet_handle_crypto_cfg); /* * host structs/API calls */ /* * knet_host_add * * knet_h - pointer to knet_handle_t * * host_id - each host in a knet is identified with a unique ID * (see also knet_handle_new documentation above) * * knet_host_add returns: * * 0 on success * -1 on error and errno is set. */ -int knet_host_add(knet_handle_t knet_h, uint8_t host_id); +int knet_host_add(knet_handle_t knet_h, knet_node_id_t host_id); /* * knet_host_remove * * knet_h - pointer to knet_handle_t * * host_id - each host in a knet is identified with a unique ID * (see also knet_handle_new documentation above) * * knet_host_remove returns: * * 0 on success * -1 on error and errno is set. */ -int knet_host_remove(knet_handle_t knet_h, uint8_t host_id); +int knet_host_remove(knet_handle_t knet_h, knet_node_id_t host_id); /* * knet_host_set_name * * knet_h - pointer to knet_handle_t * * host_id - see above * * name - this name will be used for pretty logging and eventually * search for hosts (see also get_name and get_id below). * Only up to KNET_MAX_HOST_LEN - 1 bytes will be accepted and * name has to be unique for each host. * * knet_host_set_name returns: * * 0 on success * -1 on error and errno is set. */ -int knet_host_set_name(knet_handle_t knet_h, uint8_t host_id, +int knet_host_set_name(knet_handle_t knet_h, knet_node_id_t host_id, const char *name); /* * knet_host_get_name_by_host_id * * knet_h - pointer to knet_handle_t * * host_id - see above * * name - pointer to a preallocated buffer of at least size KNET_MAX_HOST_LEN * where the current host name will be stored * (as set by knet_host_set_name or default by knet_host_add) * * knet_host_get_name_by_host_id returns: * * 0 on success * -1 on error and errno is set (name is left untouched) */ -int knet_host_get_name_by_host_id(knet_handle_t knet_h, uint8_t host_id, +int knet_host_get_name_by_host_id(knet_handle_t knet_h, knet_node_id_t host_id, char *name); /* * knet_host_get_id_by_host_name * * knet_h - pointer to knet_handle_t * * name - name to lookup, max len KNET_MAX_HOST_LEN * * host_id - where to store the result * * knet_host_get_id_by_host_name returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_get_id_by_host_name(knet_handle_t knet_h, const char *name, - uint8_t *host_id); + knet_node_id_t *host_id); /* * knet_host_get_host_list * * knet_h - pointer to knet_handle_t * * host_ids - array of at lest KNET_MAX_HOST size * * host_ids_entries - * number of entries writted in host_ids * * knet_host_get_host_list returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_get_host_list(knet_handle_t knet_h, - uint8_t *host_ids, size_t *host_ids_entries); + knet_node_id_t *host_ids, size_t *host_ids_entries); /* * define switching policies */ #define KNET_LINK_POLICY_PASSIVE 0 #define KNET_LINK_POLICY_ACTIVE 1 #define KNET_LINK_POLICY_RR 2 /* * knet_host_set_policy * * knet_h - pointer to knet_handle_t * * host_id - see above * * policy - there are currently 3 kind of simple switching policies * as defined above, based on link configuration. * KNET_LINK_POLICY_PASSIVE - the active link with the lowest * priority will be used. * if one or more active links share * the same priority, the one with * lowest link_id will be used. * * KNET_LINK_POLICY_ACTIVE - all active links will be used * simultaneously to send traffic. * link priority is ignored. * * KNET_LINK_POLICY_RR - round-robin policy, every packet * will be send on a different active * link. * * knet_host_set_policy returns: * * 0 on success * -1 on error and errno is set. */ -int knet_host_set_policy(knet_handle_t knet_h, uint8_t host_id, +int knet_host_set_policy(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t policy); /* * knet_host_get_policy * * knet_h - pointer to knet_handle_t * * host_id - see above * * policy - will contain the current configured switching policy. * Default is passive when creating a new host. * * knet_host_get_policy returns: * * 0 on success * -1 on error and errno is set. */ -int knet_host_get_policy(knet_handle_t knet_h, uint8_t host_id, +int knet_host_get_policy(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t *policy); /* * knet_host_enable_status_change_notify * * knet_h - pointer to knet_handle_t * * host_status_change_notify_fn_private_data * void pointer to data that can be used to identify * the callback. * * host_status_change_notify_fn * is a callback function that is invoked every time * there is a change in the host status. * host status is identified by: * - reachable, this host can send/receive data to/from host_id * - remote, 0 if the host_id is connected locally or 1 if * the there is one or more knet host(s) in between. * NOTE: re-switching is NOT currently implemented, * but this is ready for future and can avoid * an API/ABI breakage later on. * - external, 0 if the host_id is configured locally or 1 if * it has been added from remote nodes config. * NOTE: dynamic topology is NOT currently implemented, * but this is ready for future and can avoid * an API/ABI breakage later on. * This function MUST NEVER block or add substantial delays. * * knet_host_status_change_notify returns: * * 0 on success * -1 on error and errno is set. */ int knet_host_enable_status_change_notify(knet_handle_t knet_h, void *host_status_change_notify_fn_private_data, void (*host_status_change_notify_fn) ( void *private_data, - uint8_t host_id, + knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external)); /* * define host status structure for quick lookup * struct is in flux as more stats will be added soon * * reachable host_id can be seen either directly connected * or via another host_id * * remote 0 = node is connected locally, 1 is visible via * via another host_id * * external 0 = node is configured/known locally, * 1 host_id has been received via another host_id */ struct knet_host_status { uint8_t reachable; uint8_t remote; uint8_t external; /* add host statistics */ }; /* * knet_host_status_get * * knet_h - pointer to knet_handle_t * * status - pointer to knet_host_status struct (see above) * * knet_handle_pmtud_get returns: * * 0 on success * -1 on error and errno is set. */ -int knet_host_get_status(knet_handle_t knet_h, uint8_t host_id, +int knet_host_get_status(knet_handle_t knet_h, knet_node_id_t host_id, struct knet_host_status *status); /* * link structs/API calls * * every host allocated/managed by knet_host_* has * KNET_MAX_LINK structures to define the network * paths that connect 2 hosts. * * Each link is identified by a link_id that has a * values between 0 and KNET_MAX_LINK - 1. * * KNOWN LIMITATIONS: * * - let's assume the scenario where two hosts are connected * with any number of links. link_id must match on both sides. * If host_id 0 link_id 0 is configured to connect IP1 to IP2 and * host_id 0 link_id 1 is configured to connect IP3 to IP4, * host_id 1 link_id 0 _must_ connect IP2 to IP1 and likewise * host_id 1 link_id 1 _must_ connect IP4 to IP3. * We might be able to lift this restriction in future, by using * other data to determine src/dst link_id, but for now, deal with it. * * - */ /* * commodity functions to convert strings to sockaddr and viceversa */ /* * knet_strtoaddr * * host - IPaddr/hostname to convert * be aware only the first IP address will be returned * in case a hostname resolves to multiple IP * * port - port to connect to * * ss - sockaddr_storage where to store the converted data * * sslen - len of the sockaddr_storage * * knet_strtoaddr returns same error codes as getaddrinfo * */ int knet_strtoaddr(const char *host, const char *port, struct sockaddr_storage *ss, socklen_t sslen); /* * knet_addrtostr * * ss - sockaddr_storage to convert * * sslen - len of the sockaddr_storage * * host - IPaddr/hostname where to store data * (recommended size: KNET_MAX_HOST_LEN) * * port - port buffer where to store data * (recommended size: KNET_MAX_PORT_LEN) * * knet_strtoaddr returns same error codes as getnameinfo * */ int knet_addrtostr(const struct sockaddr_storage *ss, socklen_t sslen, char *addr_buf, size_t addr_buf_size, char *port_buf, size_t port_buf_size); /* * knet_handle_get_transport_list * * knet_h - pointer to knet_handle_t * * transport_list - an array of struct transport_info that must be * at least of size struct transport_info * KNET_MAX_TRANSPORTS * * transport_list_entries - pointer to a size_t where to store how many transports * are available in this build of libknet. * * knet_handle_get_transport_list returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_TRANSPORT_UDP 0 #define KNET_TRANSPORT_SCTP 1 #define KNET_MAX_TRANSPORTS 2 struct transport_info { const char *name; /* UDP/SCTP/etc... */ uint8_t id; /* value that can be used for link_set_config */ uint8_t properties; /* currently unused */ }; int knet_handle_get_transport_list(knet_handle_t knet_h, struct transport_info *transport_list, size_t *transport_list_entries); /* * knet_handle_get_transport_name_by_id * * knet_h - pointer to knet_handle_t * * transport - one of the above KNET_TRANSPORT_xxx constants * * knet_handle_get_transport_name_by_id returns: * * pointer to the name on success or * NULL on error and errno is set. */ const char *knet_handle_get_transport_name_by_id(knet_handle_t knet_h, uint8_t transport); /* * knet_handle_get_transport_id_by_name * * knet_h - pointer to knet_handle_t * * name - transport name (UDP/SCTP/etc) * * knet_handle_get_transport_name_by_id returns: * * KNET_MAX_TRANSPORTS on error and errno is set accordingly * KNET_TRANSPORT_xxx on success. */ uint8_t knet_handle_get_transport_id_by_name(knet_handle_t knet_h, const char *name); /* * knet_link_set_config * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * transport - one of the above KNET_TRANSPORT_xxx constants * * src_addr - sockaddr_storage that can be either IPv4 or IPv6 * * dst_addr - sockaddr_storage that can be either IPv4 or IPv6 * this can be null if we don't know the incoming * IP address/port and the link will remain quiet * till the node on the other end will initiate a * connection * * knet_link_set_config returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_set_config(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t transport, struct sockaddr_storage *src_addr, struct sockaddr_storage *dst_addr); /* * knet_link_get_config * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * transport - see above * * src_addr - sockaddr_storage that can be either IPv4 or IPv6 * * dst_addr - sockaddr_storage that can be either IPv4 or IPv6 * * dynamic - 0 if dst_addr is static or 1 if dst_addr is dynamic. * In case of 1, dst_addr can be NULL and it will be left * untouched. * * knet_link_get_config returns: * * 0 on success. * -1 on error and errno is set. */ -int knet_link_get_config(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *transport, struct sockaddr_storage *src_addr, struct sockaddr_storage *dst_addr, uint8_t *dynamic); /* * knet_link_clear_config * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * knet_link_clear_config returns: * * 0 on success. * -1 on error and errno is set. */ -int knet_link_clear_config(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id); +int knet_link_clear_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id); /* * knet_link_set_enable * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * enabled - 0 disable the link, 1 enable the link * * knet_link_set_enable returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_set_enable(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int enabled); /* * knet_link_get_enable * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * enabled - 0 disable the link, 1 enable the link * * knet_link_get_enable returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_get_enable(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int *enabled); /* * knet_link_set_ping_timers * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * interval - specify the ping interval * * timeout - if no pong is received within this time, * the link is declared dead * * precision - how many values of latency are used to calculate * the average link latency (see also get_status below) * * knet_link_set_ping_timers returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_LINK_DEFAULT_PING_INTERVAL 1000 /* 1 second */ #define KNET_LINK_DEFAULT_PING_TIMEOUT 2000 /* 2 seconds */ #define KNET_LINK_DEFAULT_PING_PRECISION 2048 /* samples */ -int knet_link_set_ping_timers(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, time_t interval, time_t timeout, unsigned int precision); /* * knet_link_get_ping_timers * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * interval - ping intervall * * timeout - if no pong is received within this time, * the link is declared dead * * precision - how many values of latency are used to calculate * the average link latency (see also get_status below) * * knet_link_get_ping_timers returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_get_ping_timers(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, time_t *interval, time_t *timeout, unsigned int *precision); /* * knet_link_set_pong_count * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * pong_count - how many valid ping/pongs before a link is marked UP. * default: 5, value should be > 0 * * knet_link_set_pong_count returns: * * 0 on success * -1 on error and errno is set. */ #define KNET_LINK_DEFAULT_PONG_COUNT 5 -int knet_link_set_pong_count(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t pong_count); /* * knet_link_get_pong_count * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * pong_count - see above * * knet_link_get_pong_count returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_get_pong_count(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *pong_count); /* * knet_link_set_priority * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * priority - specify the switching priority for this link * see also knet_host_set_policy * * knet_link_set_priority returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_set_priority(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t priority); /* * knet_link_get_priority * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * priority - gather the switching priority for this link * see also knet_host_set_policy * * knet_link_get_priority returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_get_priority(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *priority); /* * knet_link_get_link_list * * knet_h - pointer to knet_handle_t * * link_ids - array of at lest KNET_MAX_LINK size * with the list of configured links for a certain host. * * link_ids_entries - * number of entries contained in link_ids * * knet_link_get_link_list returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_get_link_list(knet_handle_t knet_h, uint8_t host_id, +int knet_link_get_link_list(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t *link_ids, size_t *link_ids_entries); /* * define link status structure for quick lookup * struct is in flux as more stats will be added soon * * src/dst_{ipaddr,port} strings are filled by * getnameinfo(3) when configuring the link. * if the link is dynamic (see knet_link_set_config) * dst_ipaddr/port will contain ipaddr/port of the currently * connected peer or "Unknown" if it was not possible * to determine the ipaddr/port at runtime. * * enabled see also knet_link_set/get_enable. * * connected the link is connected to a peer and ping/pong traffic * is flowing. * * dynconnected the link has dynamic ip on the other end, and * we can see the other host is sending pings to us. * * latency average latency of this link * see also knet_link_set/get_timeout. * * pong_last if the link is down, this value tells us how long * ago this link was active. A value of 0 means that the link * has never been active. */ struct knet_link_status { char src_ipaddr[KNET_MAX_HOST_LEN]; char src_port[KNET_MAX_PORT_LEN]; char dst_ipaddr[KNET_MAX_HOST_LEN]; char dst_port[KNET_MAX_PORT_LEN]; unsigned int enabled:1; /* link is configured and admin enabled for traffic */ unsigned int connected:1; /* link is connected for data (local view) */ unsigned int dynconnected:1; /* link has been activated by remote dynip */ unsigned long long latency; /* average latency computed by fix/exp */ struct timespec pong_last; unsigned int mtu; /* current detected MTU on this link */ unsigned int proto_overhead; /* contains the size of the IP protocol, knet headers and * crypto headers (if configured). This value is filled in * ONLY after the first PMTUd run on that given link, * and can change if link configuration or crypto configuration * changes at runtime. * WARNING: in general mtu + proto_overhead might or might * not match the output of ifconfig mtu due to crypto * requirements to pad packets to some specific boundaries. */ /* add link statistics */ }; /* * knet_link_get_status * * knet_h - pointer to knet_handle_t * * host_id - see above * * link_id - see above * * status - pointer to knet_link_status struct (see above) * * knet_link_get_status returns: * * 0 on success * -1 on error and errno is set. */ -int knet_link_get_status(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_status(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, struct knet_link_status *status); /* * logging structs/API calls */ /* * libknet is composed of several subsystems. In order * to easily distinguish log messages coming from different * places, each subsystem has its own ID. * * 0-19 config/management * 20-39 internal threads * 40-59 transports * 60-69 crypto implementations */ #define KNET_SUB_COMMON 0 /* common.c */ #define KNET_SUB_HANDLE 1 /* handle.c alloc/dealloc config changes */ #define KNET_SUB_HOST 2 /* host add/del/modify */ #define KNET_SUB_LISTENER 3 /* listeners add/del/modify... */ #define KNET_SUB_LINK 4 /* link add/del/modify */ #define KNET_SUB_TRANSPORT 5 /* Transport common */ #define KNET_SUB_CRYPTO 6 /* crypto.c config generic layer */ #define KNET_SUB_FILTER 19 /* allocated for users to log from dst_filter */ #define KNET_SUB_DSTCACHE 20 /* switching thread (destination cache handling) */ #define KNET_SUB_HEARTBEAT 21 /* heartbeat thread */ #define KNET_SUB_PMTUD 22 /* Path MTU Discovery thread */ #define KNET_SUB_TX 23 /* send to link thread */ #define KNET_SUB_RX 24 /* recv from link thread */ #define KNET_SUB_TRANSP_UDP 40 /* UDP Transport */ #define KNET_SUB_TRANSP_SCTP 41 /* SCTP Transport */ #define KNET_SUB_NSSCRYPTO 60 /* nsscrypto.c */ #define KNET_SUB_UNKNOWN 254 #define KNET_MAX_SUBSYSTEMS KNET_SUB_UNKNOWN + 1 /* * Convert between subsystem IDs and names */ /* * knet_log_get_subsystem_name * * return internal name of the subsystem or "common" */ const char *knet_log_get_subsystem_name(uint8_t subsystem); /* * knet_log_get_subsystem_id * * return internal ID of the subsystem or KNET_SUB_COMMON */ uint8_t knet_log_get_subsystem_id(const char *name); /* * 4 log levels are enough for everybody */ #define KNET_LOG_ERR 0 /* unrecoverable errors/conditions */ #define KNET_LOG_WARN 1 /* recoverable errors/conditions */ #define KNET_LOG_INFO 2 /* info, link up/down, config changes.. */ #define KNET_LOG_DEBUG 3 /* * Convert between log level values and names */ /* * knet_log_get_loglevel_name * * return internal name of the log level or "ERROR" for unknown values */ const char *knet_log_get_loglevel_name(uint8_t level); /* * knet_log_get_loglevel_id * * return internal log level ID or KNET_LOG_ERR for invalid names */ uint8_t knet_log_get_loglevel_id(const char *name); /* * every log message is composed by a text message (including a trailing \n) * and message level/subsystem IDs. * In order to make debugging easier it is possible to send those packets * straight to stdout/stderr (see knet_bench.c stdout option). */ #define KNET_MAX_LOG_MSG_SIZE 256 struct knet_log_msg { char msg[KNET_MAX_LOG_MSG_SIZE - (sizeof(uint8_t)*2)]; uint8_t subsystem; /* KNET_SUB_* */ uint8_t msglevel; /* KNET_LOG_* */ }; /* * knet_log_set_log_level * * knet_h - same as above * * subsystem - same as above * * level - same as above * * knet_log_set_loglevel allows fine control of log levels by subsystem. * See also knet_handle_new for defaults. * * knet_log_set_loglevel returns: * * 0 on success * -1 on error and errno is set. */ int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t level); /* * knet_log_get_log_level * * knet_h - same as above * * subsystem - same as above * * level - same as above * * knet_log_get_loglevel returns: * * 0 on success * -1 on error and errno is set. */ int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t *level); #endif diff --git a/libknet/link.c b/libknet/link.c index d221a1a7..141055ff 100644 --- a/libknet/link.c +++ b/libknet/link.c @@ -1,973 +1,973 @@ /* * Copyright (C) 2010-2015 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 "internals.h" #include "logging.h" #include "link.h" #include "transports.h" #include "host.h" -int _link_updown(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int _link_updown(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int enabled, unsigned int connected) { struct knet_link *link = &knet_h->host_index[host_id]->link[link_id]; if ((link->status.enabled == enabled) && (link->status.connected == connected)) return 0; link->status.enabled = enabled; link->status.connected = connected; _host_dstcache_update_async(knet_h, knet_h->host_index[host_id]); if ((link->status.dynconnected) && (!link->status.connected)) link->status.dynconnected = 0; return 0; } -int knet_link_set_config(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t transport, struct sockaddr_storage *src_addr, struct sockaddr_storage *dst_addr) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!src_addr) { errno = EINVAL; return -1; } if (transport >= KNET_MAX_TRANSPORTS) { errno = EINVAL; return -1; } if (!knet_h->transport_ops[transport]) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (link->configured != 0) { err =-1; savederrno = EBUSY; log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is currently configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } if (link->status.enabled != 0) { err =-1; savederrno = EBUSY; log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is currently in use: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } memmove(&link->src_addr, src_addr, sizeof(struct sockaddr_storage)); err = knet_addrtostr(src_addr, sizeof(struct sockaddr_storage), link->status.src_ipaddr, KNET_MAX_HOST_LEN, link->status.src_port, KNET_MAX_PORT_LEN); if (err) { if (err == EAI_SYSTEM) { savederrno = errno; log_warn(knet_h, KNET_SUB_LINK, "Unable to resolve host: %u link: %u source addr/port: %s", host_id, link_id, strerror(savederrno)); } else { savederrno = EINVAL; log_warn(knet_h, KNET_SUB_LINK, "Unable to resolve host: %u link: %u source addr/port: %s", host_id, link_id, gai_strerror(err)); } err = -1; goto exit_unlock; } if (!dst_addr) { link->dynamic = KNET_LINK_DYNIP; } else { link->dynamic = KNET_LINK_STATIC; memmove(&link->dst_addr, dst_addr, sizeof(struct sockaddr_storage)); err = knet_addrtostr(dst_addr, sizeof(struct sockaddr_storage), link->status.dst_ipaddr, KNET_MAX_HOST_LEN, link->status.dst_port, KNET_MAX_PORT_LEN); if (err) { if (err == EAI_SYSTEM) { savederrno = errno; log_warn(knet_h, KNET_SUB_LINK, "Unable to resolve host: %u link: %u destination addr/port: %s", host_id, link_id, strerror(savederrno)); } else { savederrno = EINVAL; log_warn(knet_h, KNET_SUB_LINK, "Unable to resolve host: %u link: %u destination addr/port: %s", host_id, link_id, gai_strerror(err)); } err = -1; goto exit_unlock; } } link->transport_type = transport; link->transport_connected = 0; link->proto_overhead = knet_h->transport_ops[link->transport_type]->transport_mtu_overhead; link->configured = 1; link->pong_count = KNET_LINK_DEFAULT_PONG_COUNT; link->has_valid_mtu = 0; link->ping_interval = KNET_LINK_DEFAULT_PING_INTERVAL * 1000; /* microseconds */ link->pong_timeout = KNET_LINK_DEFAULT_PING_TIMEOUT * 1000; /* microseconds */ link->latency_fix = KNET_LINK_DEFAULT_PING_PRECISION; link->latency_exp = KNET_LINK_DEFAULT_PING_PRECISION - \ ((link->ping_interval * KNET_LINK_DEFAULT_PING_PRECISION) / 8000000); if (knet_h->transport_ops[link->transport_type]->transport_link_set_config(knet_h, link) < 0) { savederrno = errno; err = -1; goto exit_unlock; } log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u is configured", host_id, link_id); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_config(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *transport, struct sockaddr_storage *src_addr, struct sockaddr_storage *dst_addr, uint8_t *dynamic) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!src_addr) { errno = EINVAL; return -1; } if (!dynamic) { errno = EINVAL; return -1; } if (!transport) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } if ((link->dynamic == KNET_LINK_STATIC) && (!dst_addr)) { savederrno = EINVAL; err = -1; goto exit_unlock; } memmove(src_addr, &link->src_addr, sizeof(struct sockaddr_storage)); *transport = link->transport_type; if (link->dynamic == KNET_LINK_STATIC) { *dynamic = 0; memmove(dst_addr, &link->dst_addr, sizeof(struct sockaddr_storage)); } else { *dynamic = 1; } exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_clear_config(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id) +int knet_link_clear_config(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (link->configured != 1) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } if (link->status.enabled != 0) { err = -1; savederrno = EBUSY; log_err(knet_h, KNET_SUB_LINK, "Host %u link %u is currently in use: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } if ((knet_h->transport_ops[link->transport_type]->transport_link_clear_config(knet_h, link) < 0) && (errno != EBUSY)) { savederrno = errno; err = -1; goto exit_unlock; } memset(link, 0, sizeof(struct knet_link)); link->link_id = link_id; log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u config has been wiped", host_id, link_id); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_set_enable(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int enabled) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (enabled > 1) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } if (link->status.enabled == enabled) { err = 0; goto exit_unlock; } err = _link_updown(knet_h, host_id, link_id, enabled, link->status.connected); savederrno = errno; if (enabled) { goto exit_unlock; } log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u is disabled", host_id, link_id); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_enable(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_enable(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, unsigned int *enabled) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!enabled) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } *enabled = link->status.enabled; exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_set_pong_count(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t pong_count) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (pong_count < 1) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } link->pong_count = pong_count; log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u pong count update: %u", host_id, link_id, link->pong_count); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_pong_count(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_pong_count(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *pong_count) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!pong_count) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } *pong_count = link->pong_count; exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_set_ping_timers(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, time_t interval, time_t timeout, unsigned int precision) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!interval) { errno = EINVAL; return -1; } if (!timeout) { errno = EINVAL; return -1; } if (!precision) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } link->ping_interval = interval * 1000; /* microseconds */ link->pong_timeout = timeout * 1000; /* microseconds */ link->latency_fix = precision; link->latency_exp = precision - \ ((link->ping_interval * precision) / 8000000); log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u timeout update - interval: %llu timeout: %llu precision: %d", host_id, link_id, link->ping_interval, link->pong_timeout, precision); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_ping_timers(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_ping_timers(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, time_t *interval, time_t *timeout, unsigned int *precision) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!interval) { errno = EINVAL; return -1; } if (!timeout) { errno = EINVAL; return -1; } if (!precision) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } *interval = link->ping_interval / 1000; /* microseconds */ *timeout = link->pong_timeout / 1000; *precision = link->latency_fix; exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_set_priority(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_set_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t priority) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; uint8_t old_priority; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } old_priority = link->priority; if (link->priority == priority) { err = 0; goto exit_unlock; } link->priority = priority; if (_host_dstcache_update_sync(knet_h, host)) { savederrno = errno; log_debug(knet_h, KNET_SUB_LINK, "Unable to update link priority (host: %u link: %u priority: %u): %s", host_id, link_id, link->priority, strerror(savederrno)); link->priority = old_priority; err = -1; goto exit_unlock; } log_debug(knet_h, KNET_SUB_LINK, "host: %u link: %u priority set to: %u", host_id, link_id, link->priority); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_priority(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_priority(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, uint8_t *priority) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!priority) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } *priority = link->priority; exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_link_list(knet_handle_t knet_h, uint8_t host_id, +int knet_link_get_link_list(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t *link_ids, size_t *link_ids_entries) { int savederrno = 0, err = 0, i, count = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (!link_ids) { errno = EINVAL; return -1; } if (!link_ids_entries) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } for (i = 0; i < KNET_MAX_LINK; i++) { link = &host->link[i]; if (!link->configured) { continue; } link_ids[count] = i; count++; } *link_ids_entries = count; exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } -int knet_link_get_status(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int knet_link_get_status(knet_handle_t knet_h, knet_node_id_t host_id, uint8_t link_id, struct knet_link_status *status) { int savederrno = 0, err = 0; struct knet_host *host; struct knet_link *link; if (!knet_h) { errno = EINVAL; return -1; } if (link_id >= KNET_MAX_LINK) { errno = EINVAL; return -1; } if (!status) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, KNET_SUB_LINK, "Unable to get read lock: %s", strerror(savederrno)); errno = savederrno; return -1; } host = knet_h->host_index[host_id]; if (!host) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "Unable to find host %u: %s", host_id, strerror(savederrno)); goto exit_unlock; } link = &host->link[link_id]; if (!link->configured) { err = -1; savederrno = EINVAL; log_err(knet_h, KNET_SUB_LINK, "host %u link %u is not configured: %s", host_id, link_id, strerror(savederrno)); goto exit_unlock; } memmove(status, &link->status, sizeof(struct knet_link_status)); exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); errno = savederrno; return err; } diff --git a/libknet/link.h b/libknet/link.h index 9b4cc798..698675ae 100644 --- a/libknet/link.h +++ b/libknet/link.h @@ -1,21 +1,21 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __LINK_H__ #define __LINK_H__ #include "internals.h" #define KNET_LINK_STATIC 0 /* link has static ip on both ends */ #define KNET_LINK_DYNIP 1 /* link has dynamic destination ip */ -int _link_updown(knet_handle_t knet_h, uint8_t host_id, uint8_t link_id, +int _link_updown(knet_handle_t knet_h, knet_node_id_t node_id, uint8_t link_id, unsigned int enabled, unsigned int connected); #endif diff --git a/libknet/onwire.h b/libknet/onwire.h index 3fc1f6ca..05e1ddd6 100644 --- a/libknet/onwire.h +++ b/libknet/onwire.h @@ -1,199 +1,201 @@ /* * Copyright (C) 2010-2015 Red Hat, Inc. All rights reserved. * * Authors: Fabio M. Di Nitto * Federico Simoncelli * * This software licensed under GPL-2.0+, LGPL-2.0+ */ #ifndef __ONWIRE_H__ #define __ONWIRE_H__ /* * data structures to define network packets. * Start from knet_header at the bottom */ #include +#include "libknet.h" + #if 0 /* * for future protocol extension (re-switching table calculation) */ struct knet_hinfo_link { uint8_t khl_link_id; uint8_t khl_link_dynamic; uint8_t khl_link_priority; uint64_t khl_link_latency; char khl_link_dst_ipaddr[KNET_MAX_HOST_LEN]; char khl_link_dst_port[KNET_MAX_PORT_LEN]; } __attribute__((packed)); struct knet_hinfo_link_table { - uint8_t khlt_node_id; + knet_node_id_t khlt_node_id; uint8_t khlt_local; /* we have this node connected locally */ struct knet_hinfo_link khlt_link[KNET_MAX_LINK]; /* info we send about each link in the node */ } __attribute__((packed)); struct link_table { - uint8_t khdt_host_entries; + knet_node_id_t khdt_host_entries; uint8_t khdt_host_maps[0]; /* array of knet_hinfo_link_table[khdt_host_entries] */ } __attribute__((packed)); #endif #define KNET_HOSTINFO_LINK_STATUS_DOWN 0 #define KNET_HOSTINFO_LINK_STATUS_UP 1 struct knet_hostinfo_payload_link_status { uint8_t khip_link_status_link_id; /* link id */ uint8_t khip_link_status_status; /* up/down status */ } __attribute__((packed)); /* * union to reference possible individual payloads */ union knet_hostinfo_payload { struct knet_hostinfo_payload_link_status knet_hostinfo_payload_link_status; } __attribute__((packed)); /* * due to the nature of knet_hostinfo, we are currently * sending those data as part of knet_header_payload_data.khp_data_userdata * and avoid a union that increses knet_header_payload_data size * unnecessarely. * This might change later on depending on how we implement * host info exchange */ #define KNET_HOSTINFO_TYPE_LINK_UP_DOWN 0 // UNUSED #define KNET_HOSTINFO_TYPE_LINK_TABLE 1 // NOT IMPLEMENTED #define KNET_HOSTINFO_UCAST 0 /* send info to a specific host */ #define KNET_HOSTINFO_BCAST 1 /* send info to all known / connected hosts */ struct knet_hostinfo { uint8_t khi_type; /* type of hostinfo we are sending */ uint8_t khi_bcast; /* hostinfo destination bcast/ucast */ - uint8_t khi_dst_node_id;/* used only if in ucast mode */ + knet_node_id_t khi_dst_node_id;/* used only if in ucast mode */ union knet_hostinfo_payload khi_payload; } __attribute__((packed)); #define KNET_HOSTINFO_ALL_SIZE sizeof(struct knet_hostinfo) #define KNET_HOSTINFO_SIZE (KNET_HOSTINFO_ALL_SIZE - sizeof(union knet_hostinfo_payload)) #define KNET_HOSTINFO_LINK_STATUS_SIZE (KNET_HOSTINFO_SIZE + sizeof(struct knet_hostinfo_payload_link_status)) #define khip_link_status_status khi_payload.knet_hostinfo_payload_link_status.khip_link_status_status #define khip_link_status_link_id khi_payload.knet_hostinfo_payload_link_status.khip_link_status_link_id /* * typedef uint64_t seq_num_t; * #define SEQ_MAX UINT64_MAX */ typedef uint16_t seq_num_t; #define SEQ_MAX UINT16_MAX struct knet_header_payload_data { seq_num_t khp_data_seq_num; /* pckt seq number used to deduplicate pkcts */ uint8_t khp_data_pad1; /* make sure to have space in the header to grow features */ uint8_t khp_data_pad2; uint8_t khp_data_bcast; /* data destination bcast/ucast */ uint8_t khp_data_frag_num; /* number of fragments of this pckt. 1 is not fragmented */ uint8_t khp_data_frag_seq; /* as above, indicates the frag sequence number */ int8_t khp_data_channel; /* transport channel data for localsock <-> knet <-> localsock mapping */ uint8_t khp_data_userdata[0]; /* pointer to the real user data */ } __attribute__((packed)); struct knet_header_payload_ping { uint8_t khp_ping_link; /* source link id */ uint32_t khp_ping_time[4]; /* ping timestamp */ seq_num_t khp_ping_seq_num; /* transport host seq_num */ uint8_t khp_ping_timed; /* timed pinged (1) or forced by seq_num (0) */ } __attribute__((packed)); /* taken from tracepath6 */ #define KNET_PMTUD_SIZE_V4 65535 #define KNET_PMTUD_SIZE_V6 KNET_PMTUD_SIZE_V4 /* These two get the protocol-specific overheads added to them */ #define KNET_PMTUD_OVERHEAD_V4 20 #define KNET_PMTUD_OVERHEAD_V6 40 #define KNET_PMTUD_MIN_MTU_V4 576 #define KNET_PMTUD_MIN_MTU_V6 1280 struct knet_header_payload_pmtud { uint8_t khp_pmtud_link; /* source link id */ uint16_t khp_pmtud_size; /* size of the current packet */ uint8_t khp_pmtud_data[0]; /* pointer to empty/random data/fill buffer */ } __attribute__((packed)); /* * union to reference possible individual payloads */ union knet_header_payload { struct knet_header_payload_data khp_data; /* pure data packet struct */ struct knet_header_payload_ping khp_ping; /* heartbeat packet struct */ struct knet_header_payload_pmtud khp_pmtud; /* Path MTU discovery packet struct */ } __attribute__((packed)); /* * starting point */ #define KNET_HEADER_VERSION 0x01 /* we currently support only one version */ #define KNET_HEADER_TYPE_DATA 0x00 /* pure data packet */ #define KNET_HEADER_TYPE_HOST_INFO 0x01 /* host status information pckt */ #define KNET_HEADER_TYPE_PMSK 0x80 /* packet mask */ #define KNET_HEADER_TYPE_PING 0x81 /* heartbeat */ #define KNET_HEADER_TYPE_PONG 0x82 /* reply to heartbeat */ #define KNET_HEADER_TYPE_PMTUD 0x83 /* Used to determine Path MTU */ #define KNET_HEADER_TYPE_PMTUD_REPLY 0x84 /* reply from remote host */ struct knet_header { uint8_t kh_version; /* pckt format/version */ uint8_t kh_type; /* from above defines. Tells what kind of pckt it is */ - uint8_t kh_node; /* host id of the source host for this pckt */ + knet_node_id_t kh_node; /* host id of the source host for this pckt */ uint8_t kh_pad1; /* make sure to have space in the header to grow features */ uint8_t kh_pad2; union knet_header_payload kh_payload; /* union of potential data struct based on kh_type */ } __attribute__((packed)); /* * commodoty defines to hide structure nesting * (needs review and cleanup) */ #define khp_data_seq_num kh_payload.khp_data.khp_data_seq_num #define khp_data_frag_num kh_payload.khp_data.khp_data_frag_num #define khp_data_frag_seq kh_payload.khp_data.khp_data_frag_seq #define khp_data_userdata kh_payload.khp_data.khp_data_userdata #define khp_data_bcast kh_payload.khp_data.khp_data_bcast #define khp_data_channel kh_payload.khp_data.khp_data_channel #define khp_ping_link kh_payload.khp_ping.khp_ping_link #define khp_ping_time kh_payload.khp_ping.khp_ping_time #define khp_ping_seq_num kh_payload.khp_ping.khp_ping_seq_num #define khp_ping_timed kh_payload.khp_ping.khp_ping_timed #define khp_pmtud_link kh_payload.khp_pmtud.khp_pmtud_link #define khp_pmtud_size kh_payload.khp_pmtud.khp_pmtud_size #define khp_pmtud_data kh_payload.khp_pmtud.khp_pmtud_data /* * extra defines to avoid mingling with sizeof() too much */ #define KNET_HEADER_ALL_SIZE sizeof(struct knet_header) #define KNET_HEADER_SIZE (KNET_HEADER_ALL_SIZE - sizeof(union knet_header_payload)) #define KNET_HEADER_PING_SIZE (KNET_HEADER_SIZE + sizeof(struct knet_header_payload_ping)) #define KNET_HEADER_PMTUD_SIZE (KNET_HEADER_SIZE + sizeof(struct knet_header_payload_pmtud)) #define KNET_HEADER_DATA_SIZE (KNET_HEADER_SIZE + sizeof(struct knet_header_payload_data)) #endif diff --git a/libknet/tests/api_knet_handle_enable_filter.c b/libknet/tests/api_knet_handle_enable_filter.c index 1db85d40..9f715037 100644 --- a/libknet/tests/api_knet_handle_enable_filter.c +++ b/libknet/tests/api_knet_handle_enable_filter.c @@ -1,158 +1,158 @@ /* * 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 "internals.h" #include "test-common.h" static int private_data; static int dhost_filter(void *pvt_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_host_id, + knet_node_id_t this_host_id, + knet_node_id_t src_host_id, int8_t *dst_channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { return 0; } static void test(void) { knet_handle_t knet_h; int logfds[2]; printf("Test knet_handle_enable_filter incorrect knet_h\n"); if ((!knet_handle_enable_filter(NULL, NULL, dhost_filter)) || (errno != EINVAL)) { printf("knet_handle_enable_filter accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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); } printf("Test knet_handle_enable_filter with no private_data\n"); if (knet_handle_enable_filter(knet_h, NULL, dhost_filter) < 0) { printf("knet_handle_enable_filter failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->dst_host_filter_fn_private_data != NULL) { printf("knet_handle_enable_filter failed to unset private_data"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_handle_enable_filter with private_data\n"); if (knet_handle_enable_filter(knet_h, &private_data, NULL) < 0) { printf("knet_handle_enable_filter failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->dst_host_filter_fn_private_data != &private_data) { printf("knet_handle_enable_filter failed to set private_data"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_handle_enable_filter with no dhost_filter fn\n"); if (knet_handle_enable_filter(knet_h, NULL, NULL) < 0) { printf("knet_handle_enable_filter failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->dst_host_filter_fn != NULL) { printf("knet_handle_enable_filter failed to unset dhost_filter fn"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_handle_enable_filter with dhost_filter fn\n"); if (knet_handle_enable_filter(knet_h, NULL, dhost_filter) < 0) { printf("knet_handle_enable_filter failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->dst_host_filter_fn != &dhost_filter) { printf("knet_handle_enable_filter failed to set dhost_filter fn"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); 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[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_handle_new.c b/libknet/tests/api_knet_handle_new.c index 70252c7d..e691eb5f 100644 --- a/libknet/tests/api_knet_handle_new.c +++ b/libknet/tests/api_knet_handle_new.c @@ -1,139 +1,139 @@ /* * 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 #include #include "libknet.h" #include "internals.h" #include "test-common.h" static void test(void) { knet_handle_t knet_h; struct rlimit cur; int logfds[2]; printf("Test knet_handle_new hostid 1, no logging\n"); knet_h = knet_handle_new(1, 0, 0); if (!knet_h) { printf("Unable to init knet_handle! err: %s\n", strerror(errno)); exit(FAIL); } if (knet_handle_free(knet_h) != 0) { printf("Unable to free knet_handle\n"); exit(FAIL); } printf("Test knet_handle_new hostid -1, no logging\n"); knet_h = knet_handle_new(-1, 0, 0); if (!knet_h) { printf("Unable to init knet_handle! err: %s\n", strerror(errno)); exit(FAIL); } /* - * -1 == uint8_t 255 + * -1 == knet_node_id_t 65535 */ - if (knet_h->host_id != 255) { + if (knet_h->host_id != 65535) { printf("host_id size might have changed!\n"); knet_handle_free(knet_h); exit(FAIL); } if (knet_handle_free(knet_h) != 0) { printf("Unable to free knet_handle\n"); exit(FAIL); } if (getrlimit(RLIMIT_NOFILE, &cur) < 0) { printf("Unable to get current fd limit: %s\n", strerror(errno)); exit(SKIP); } /* * passing a bad fd and it should fail */ printf("Test knet_handle_new hostid 1, incorrect log_fd (-1)\n"); knet_h = knet_handle_new(1, -1, 0); if ((!knet_h) && (errno != EINVAL)) { printf("knet_handle_new returned incorrect errno on incorrect log_fd\n"); exit(FAIL); } if (knet_h) { printf("knet_handle_new accepted an incorrect (-1) log_fd\n"); knet_handle_free(knet_h); exit(FAIL); } /* * passing a bad fd and it should fail */ printf("Test knet_handle_new hostid 1, incorrect log_fd (max_fd + 1)\n"); knet_h = knet_handle_new(1, (int) cur.rlim_max, 0); if ((knet_h) || (errno != EINVAL)) { printf("knet_handle_new accepted an incorrect (max_fd + 1) log_fd or returned incorrect errno on incorrect log_fd: %s\n", strerror(errno)); knet_handle_free(knet_h); exit(FAIL); } setup_logpipes(logfds); printf("Test knet_handle_new hostid 1, proper log_fd, invalid log level (DEBUG + 1)\n"); knet_h = knet_handle_new(1, logfds[1], KNET_LOG_DEBUG + 1); if ((knet_h) || (errno != EINVAL)) { printf("knet_handle_new accepted an incorrect log level or returned incorrect errno on incorrect log level: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } printf("Test knet_handle_new hostid 1, proper log_fd, proper log level (DEBUG)\n"); 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); } knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); } int main(int argc, char *argv[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_host_add.c b/libknet/tests/api_knet_host_add.c index b7d62ceb..b135ab40 100644 --- a/libknet/tests/api_knet_host_add.c +++ b/libknet/tests/api_knet_host_add.c @@ -1,114 +1,114 @@ /* * 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 "test-common.h" static void test(void) { knet_handle_t knet_h; int logfds[2]; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; size_t host_ids_entries; printf("Test knet_host_add incorrect knet_h\n"); if ((!knet_host_add(NULL, 1)) || (errno != EINVAL)) { printf("knet_host_add accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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_host_add with hostid 1\n"); if (knet_host_add(knet_h, 1) < 0) { printf("knet_host_add failed error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test verify host_id 1 is in the host list\n"); if (knet_host_get_host_list(knet_h, host_ids, &host_ids_entries) < 0) { printf("Unable to get host list: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (host_ids_entries != 1) { printf("Too many hosts?\n"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (host_ids[0] != 1) { printf("Unable to find host id 1 in host list\n"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_add adding host 1 again\n"); if ((!knet_host_add(knet_h, 1)) || (errno != EEXIST)) { printf("knet_host_add accepted duplicated host_id or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); 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[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_host_enable_status_change_notify.c b/libknet/tests/api_knet_host_enable_status_change_notify.c index 4d809809..dc0b1be1 100644 --- a/libknet/tests/api_knet_host_enable_status_change_notify.c +++ b/libknet/tests/api_knet_host_enable_status_change_notify.c @@ -1,152 +1,152 @@ /* * 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 "internals.h" #include "test-common.h" static int private_data; static void host_notify(void *priv_data, - uint8_t host_id, + knet_node_id_t host_id, uint8_t reachable, uint8_t remote, uint8_t external) { return; } static void test(void) { knet_handle_t knet_h; int logfds[2]; printf("Test knet_host_enable_status_change_notify incorrect knet_h\n"); if ((!knet_host_enable_status_change_notify(NULL, NULL, host_notify)) || (errno != EINVAL)) { printf("knet_host_enable_status_change_notify accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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); } printf("Test knet_host_enable_status_change_notify with no private_data\n"); if (knet_host_enable_status_change_notify(knet_h, NULL, host_notify) < 0) { printf("knet_host_enable_status_change_notify failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->host_status_change_notify_fn_private_data != NULL) { printf("knet_host_enable_status_change_notify failed to unset private_data"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_enable_status_change_notify with private_data\n"); if (knet_host_enable_status_change_notify(knet_h, &private_data, NULL) < 0) { printf("knet_host_enable_status_change_notify failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->host_status_change_notify_fn_private_data != &private_data) { printf("knet_host_enable_status_change_notify failed to set private_data"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_enable_status_change_notify with no host_notify fn\n"); if (knet_host_enable_status_change_notify(knet_h, NULL, NULL) < 0) { printf("knet_host_enable_status_change_notify failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->host_status_change_notify_fn != NULL) { printf("knet_host_enable_status_change_notify failed to unset host_notify fn"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_enable_status_change_notify with host_notify fn\n"); if (knet_host_enable_status_change_notify(knet_h, NULL, host_notify) < 0) { printf("knet_host_enable_status_change_notify failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_h->host_status_change_notify_fn != &host_notify) { printf("knet_host_enable_status_change_notify failed to set host_notify fn"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); } int main(int argc, char *argv[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_host_get_host_list.c b/libknet/tests/api_knet_host_get_host_list.c index 2cb6a78d..b644bade 100644 --- a/libknet/tests/api_knet_host_get_host_list.c +++ b/libknet/tests/api_knet_host_get_host_list.c @@ -1,148 +1,148 @@ /* * 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 "test-common.h" static void test(void) { knet_handle_t knet_h; int logfds[2]; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; size_t host_ids_entries; printf("Test knet_host_get_host_list incorrect knet_h\n"); if ((!knet_host_get_host_list(NULL, host_ids, &host_ids_entries)) || (errno != EINVAL)) { printf("knet_host_get_host_list accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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_host_get_host_list incorrect host_ids\n"); if ((!knet_host_get_host_list(knet_h, NULL, &host_ids_entries)) || (errno != EINVAL)) { printf("knet_host_get_host_list accepted invalid host_ids or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_get_host_list incorrect host_ids_entries\n"); if ((!knet_host_get_host_list(knet_h, host_ids, NULL)) || (errno != EINVAL)) { printf("knet_host_get_host_list accepted invalid host_ids or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_get_host_list with one host\n"); if (knet_host_add(knet_h, 1) < 0) { printf("knet_host_add failed error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_host_get_host_list(knet_h, host_ids, &host_ids_entries) < 0) { printf("Unable to get host list: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (host_ids_entries != 1) { printf("Too many hosts?\n"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (host_ids[0] != 1) { printf("Unable to find host id 1 in host list\n"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_get_host_list with zero hosts\n"); if (knet_host_remove(knet_h, 1) < 0) { printf("knet_host_remove failed error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_host_get_host_list(knet_h, host_ids, &host_ids_entries) < 0) { printf("Unable to get host list: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (host_ids_entries != 0) { printf("Too many hosts?\n"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); } int main(int argc, char *argv[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_host_get_id_by_host_name.c b/libknet/tests/api_knet_host_get_id_by_host_name.c index 071dc938..fcb88a4a 100644 --- a/libknet/tests/api_knet_host_get_id_by_host_name.c +++ b/libknet/tests/api_knet_host_get_id_by_host_name.c @@ -1,120 +1,120 @@ /* * 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 "internals.h" #include "test-common.h" static void test(void) { knet_handle_t knet_h; int logfds[2]; - uint8_t host_id; + knet_node_id_t host_id; printf("Test knet_host_get_id_by_host_name incorrect knet_h\n"); if ((!knet_host_get_id_by_host_name(NULL, "1", &host_id)) || (errno != EINVAL)) { printf("knet_host_get_id_by_host_name accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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_host_get_id_by_host_name with incorrect name 1\n"); if ((!knet_host_get_id_by_host_name(knet_h, NULL, &host_id)) || (errno != EINVAL)) { printf("knet_host_get_id_by_host_name accepted invalid name or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_get_id_by_host_name with incorrect host_id\n"); if ((!knet_host_get_id_by_host_name(knet_h, "1", NULL)) || (errno != EINVAL)) { printf("knet_host_get_id_by_host_name accepted invalid host_id or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_get_id_by_host_name with incorrect values for name\n"); if (knet_host_add(knet_h, 1) < 0) { printf("knet_host_add failed error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if ((!knet_host_get_id_by_host_name(knet_h, "test", &host_id)) || (errno != ENOENT)) { printf("knet_host_get_id_by_host_name returned invalid host_id or returned incorrect error: %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); } flush_logs(logfds[0], stdout); printf("Test knet_host_get_id_by_host_name with correct values\n"); if (knet_host_get_id_by_host_name(knet_h, "1", &host_id) < 0) { printf("knet_host_get_id_by_host_name could not get id for known name: %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); } flush_logs(logfds[0], stdout); 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[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_host_remove.c b/libknet/tests/api_knet_host_remove.c index 55e0d89b..e5798eff 100644 --- a/libknet/tests/api_knet_host_remove.c +++ b/libknet/tests/api_knet_host_remove.c @@ -1,169 +1,169 @@ /* * 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 "netutils.h" #include "test-common.h" static void test(void) { knet_handle_t knet_h; int logfds[2]; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; size_t host_ids_entries; struct sockaddr_storage ss; printf("Test knet_host_add incorrect knet_h\n"); if ((!knet_host_remove(NULL, 1)) || (errno != EINVAL)) { printf("knet_host_remove accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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_host_remove with unconfigured host_id\n"); if ((!knet_host_remove(knet_h, 1)) || (errno != EINVAL)) { printf("knet_host_remove accepted invalid host_id or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); if (knet_host_add(knet_h, 1) < 0) { printf("Unable to add host_id 1: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_host_remove with configured host_id and links\n"); memset(&ss, 0, sizeof(struct sockaddr_storage)); if (knet_strtoaddr("127.0.0.0", "50000", &ss, sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert str to sockaddr: %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_config(knet_h, 1, 0, KNET_TRANSPORT_UDP, &ss, NULL) < 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("Unable to enable 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_host_remove(knet_h, 1)) || (errno != EBUSY)) { printf("knet_host_remove accepted invalid request to remove host with link enabled or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_link_set_enable(knet_h, 1, 0, 0) < 0) { printf("Unable to disable 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_clear_config(knet_h, 1, 0) < 0) { printf("Unable to clear link config: %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); } flush_logs(logfds[0], stdout); printf("Test knet_host_remove with configured host_id (no links)\n"); if (knet_host_remove(knet_h, 1) < 0) { printf("knet_host_remove didn't remove host_id 1: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_host_get_host_list(knet_h, host_ids, &host_ids_entries) < 0) { printf("Unable to get host list: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (host_ids_entries) { printf("Too many hosts?\n"); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); } int main(int argc, char *argv[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/api_knet_send_sync.c b/libknet/tests/api_knet_send_sync.c index fe8654e6..904d4c29 100644 --- a/libknet/tests/api_knet_send_sync.c +++ b/libknet/tests/api_knet_send_sync.c @@ -1,396 +1,396 @@ /* * 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 "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 int dhost_filter_ret = 0; static int dhost_filter(void *pvt_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_host_id, + knet_node_id_t this_host_id, + knet_node_id_t src_host_id, int8_t *dst_channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { dst_host_ids[0] = 0; /* * fatal fault */ if (dhost_filter_ret < 0) { return -1; } /* * trigger EINVAL * no ids found */ if (dhost_filter_ret == 0) { *dst_host_ids_entries = 0; return 0; } /* * send correct info back */ if (dhost_filter_ret == 1) { dst_host_ids[0] = 1; *dst_host_ids_entries = 1; return 0; } /* * trigger E2BIG * mcast destinations */ if (dhost_filter_ret == 2) { dst_host_ids[0] = 1; *dst_host_ids_entries = 2; return 0; } /* * return mcast */ if (dhost_filter_ret == 3) { return 1; } return dhost_filter_ret; } static void test(void) { knet_handle_t knet_h; int logfds[2]; int datafd = 0; int8_t channel = 0; char send_buff[KNET_MAX_PACKET_SIZE]; struct sockaddr_storage lo; 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)); printf("Test knet_send_sync incorrect knet_h\n"); if ((!knet_send_sync(NULL, send_buff, KNET_MAX_PACKET_SIZE, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid knet_h or returned incorrect error: %s\n", strerror(errno)); exit(FAIL); } 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); } printf("Test knet_send_sync with no send_buff\n"); if ((!knet_send_sync(knet_h, NULL, KNET_MAX_PACKET_SIZE, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid send_buff or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with invalid send_buff len (0)\n"); if ((!knet_send_sync(knet_h, send_buff, 0, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid send_buff len (0) or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with invalid send_buff len (> KNET_MAX_PACKET_SIZE)\n"); if ((!knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE + 1, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid send_buff len (> KNET_MAX_PACKET_SIZE) or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with invalid channel (-1)\n"); channel = -1; if ((!knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid channel (-1) or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with invalid channel (KNET_DATAFD_MAX)\n"); channel = KNET_DATAFD_MAX; if ((!knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid channel (KNET_DATAFD_MAX) or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with unconfigured channel\n"); channel = 0; if ((!knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel)) || (errno != EINVAL)) { printf("knet_send_sync accepted invalid unconfigured channel or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with data forwarding disabled\n"); 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_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) == sizeof(send_buff)) || (errno != ECANCELED)) { printf("knet_send_sync didn't detect datafwd disabled or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with broken dst_host_filter\n"); if (knet_handle_setfwd(knet_h, 1) < 0) { printf("knet_handle_setfwd failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } if (knet_handle_enable_filter(knet_h, NULL, dhost_filter) < 0) { printf("knet_handle_enable_filter failed: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } dhost_filter_ret = -1; if ((knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) == sizeof(send_buff)) || (errno != EFAULT)) { printf("knet_send_sync didn't detect fatal error from dst_host_filter or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with dst_host_filter returning no host_ids_entries\n"); dhost_filter_ret = 0; if ((knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) == sizeof(send_buff)) || (errno != EINVAL)) { printf("knet_send_sync didn't detect 0 host_ids from dst_host_filter or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with host down\n"); dhost_filter_ret = 1; if ((knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) == sizeof(send_buff)) || (errno != EHOSTDOWN)) { printf("knet_send_sync didn't detect hostdown or returned incorrect error: %s\n", strerror(errno)); knet_handle_free(knet_h); flush_logs(logfds[0], stdout); close_logpipes(logfds); exit(FAIL); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with dst_host_filter returning too many host_ids_entries\n"); 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) { 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); } while(knet_h->host_index[1]->status.reachable != 1) { printf("waiting host to be reachable\n"); sleep(1); } dhost_filter_ret = 2; if ((knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) == sizeof(send_buff)) || (errno != E2BIG)) { printf("knet_send_sync didn't detect 2+ host_ids from dst_host_filter or returned incorrect error: %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); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with dst_host_filter returning mcast packets\n"); dhost_filter_ret = 3; if ((knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) == sizeof(send_buff)) || (errno != E2BIG)) { printf("knet_send_sync didn't detect mcast packet from dst_host_filter or returned incorrect error: %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); } flush_logs(logfds[0], stdout); printf("Test knet_send_sync with valid data\n"); dhost_filter_ret = 1; if (knet_send_sync(knet_h, send_buff, KNET_MAX_PACKET_SIZE, channel) < 0) { printf("knet_send_sync failed: %d %s\n", errno, 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); 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[]) { need_root(); test(); return PASS; } diff --git a/libknet/tests/knet_bench.c b/libknet/tests/knet_bench.c index 2d862e92..527725c4 100644 --- a/libknet/tests/knet_bench.c +++ b/libknet/tests/knet_bench.c @@ -1,663 +1,663 @@ /* * 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 #include #include "libknet.h" #include "internals.h" #include "netutils.h" #include "test-common.h" #define MAX_NODES 128 static int senderid = -1; static knet_handle_t knet_h; static int datafd = 0; static int8_t channel = 0; static int globallistener = 0; static int continous = 0; static struct sockaddr_storage allv4; static struct sockaddr_storage allv6; static int broadcast_test = 1; static pthread_t rx_thread = (pthread_t)NULL; static char *rx_buf[PCKT_FRAG_MAX]; static int shutdown_in_progress = 0; static pthread_mutex_t shutdown_mutex = PTHREAD_MUTEX_INITIALIZER; #define TEST_PING 0 #define TEST_PING_AND_DATA 1 #define TEST_PERF 2 static int test_type = TEST_PING; struct node { int nodeid; int links; struct sockaddr_storage address[KNET_MAX_LINK]; }; static void print_help(void) { printf("knet_bench usage:\n"); printf(" -h print this help (no really)\n"); printf(" -d enable debug logs (default INFO)\n"); printf(" -c [implementation]:[crypto]:[hashing] crypto configuration. (default disabled)\n"); printf(" Example: -c nss:aes128:sha1\n"); printf(" -p [active|passive|rr] (default: passive)\n"); printf(" -P [udp|sctp] (default: udp) protocol (transport) to use\n"); printf(" -t [nodeid] This nodeid (required)\n"); printf(" -n [nodeid],[link1_ip_addr],[link2_..] Other nodes information (at least one required)\n"); printf(" Example: -t 1,192.168.8.1,3ffe::8:1,..\n"); printf(" can be repeated up to %d and should contain also the localnode info\n", MAX_NODES); printf(" -b [port] baseport (default: 50000)\n"); printf(" -l enable global listener on 0.0.0.0/:: (default: off, incompatible with -o)\n"); printf(" -o enable baseport offset per nodeid\n"); printf(" -w dont wait for all nodes to be up before starting the test (default: wait)\n"); printf(" -T [ping|ping_data|perf] test type (default: ping)\n"); printf(" ping: will wait for all hosts to join the knet network, sleep 5 seconds and quit\n"); printf(" ping_data: will wait for all hosts to join the knet network, sends some data to all nodes and quit\n"); printf(" perf: will wait for all hosts to join the knet network, perform a series of benchmarks and quit\n"); printf(" -s nodeid that will generate traffic for benchmarks\n"); printf(" -C repeat the test continously (default: off)\n"); } static void parse_nodes(char *nodesinfo[MAX_NODES], int onidx, int port, struct node nodes[MAX_NODES], int thisnodeid, int *thisidx) { int i; char *temp = NULL; char port_str[10]; memset(port_str, 0, sizeof(port_str)); sprintf(port_str, "%d", port); for (i = 0; i < onidx; i++) { nodes[i].nodeid = atoi(strtok(nodesinfo[i], ",")); if ((nodes[i].nodeid < 0) || (nodes[i].nodeid > KNET_MAX_HOST)) { printf("Invalid nodeid: %d (0 - %d)\n", nodes[i].nodeid, KNET_MAX_HOST); exit(FAIL); } if (thisnodeid == nodes[i].nodeid) { *thisidx = i; } while((temp = strtok(NULL, ","))) { if (nodes[i].links == KNET_MAX_LINK) { printf("Too many links configured. Max %d\n", KNET_MAX_LINK); exit(FAIL); } if (knet_strtoaddr(temp, port_str, &nodes[i].address[nodes[i].links], sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert %s to sockaddress\n", temp); exit(FAIL); } nodes[i].links++; } } if (knet_strtoaddr("0.0.0.0", port_str, &allv4, sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert 0.0.0.0 to sockaddress\n"); exit(FAIL); } if (knet_strtoaddr("::", port_str, &allv6, sizeof(struct sockaddr_storage)) < 0) { printf("Unable to convert :: to sockaddress\n"); exit(FAIL); } for (i = 1; i < onidx; i++) { if (nodes[0].links != nodes[i].links) { printf("knet_bench does not support unbalanced link configuration\n"); exit(FAIL); } } return; } static int private_data; static void sock_notify(void *pvt_data, int local_datafd, int8_t local_channel, uint8_t tx_rx, int error, int errorno) { printf("Error (%d - %d - %s) from socket: %d\n", error, errorno, strerror(errno), local_datafd); return; } static void setup_knet(int argc, char *argv[]) { int logfd; int rv; char *cryptocfg = NULL, *policystr = NULL, *protostr = NULL; char *othernodeinfo[MAX_NODES]; struct node nodes[MAX_NODES]; int thisnodeid = -1; int thisidx = -1; int onidx = 0; int debug = KNET_LOG_INFO; int port = 50000, portoffset = 0; int thisport = 0, otherport = 0; int thisnewport = 0, othernewport = 0; struct sockaddr_in *so_in; struct sockaddr_in6 *so_in6; struct sockaddr_storage *src; int i, link_idx, allnodesup = 0; int policy = KNET_LINK_POLICY_PASSIVE, policyfound = 0; int protocol = KNET_TRANSPORT_UDP, protofound = 0; int wait = 1; struct knet_handle_crypto_cfg knet_handle_crypto_cfg; char *cryptomodel = NULL, *cryptotype = NULL, *cryptohash = NULL; memset(nodes, 0, sizeof(nodes)); while ((rv = getopt(argc, argv, "CT:s:ldowb:t:n:c:p:P:h")) != EOF) { switch(rv) { case 'h': print_help(); exit(PASS); break; case 'd': debug = KNET_LOG_DEBUG; break; case 'c': if (cryptocfg) { printf("Error: -c can only be specified once\n"); exit(FAIL); } cryptocfg = optarg; break; case 'p': if (policystr) { printf("Error: -p can only be specified once\n"); exit(FAIL); } policystr = optarg; if (!strcmp(policystr, "active")) { policy = KNET_LINK_POLICY_ACTIVE; policyfound = 1; } if (!strcmp(policystr, "rr")) { policy = KNET_LINK_POLICY_RR; policyfound = 1; } if (!strcmp(policystr, "passive")) { policy = KNET_LINK_POLICY_PASSIVE; policyfound = 1; } if (!policyfound) { printf("Error: invalid policy %s specified. -p accepts active|passive|rr\n", policystr); exit(FAIL); } break; case 'P': if (protostr) { printf("Error: -P can only be specified once\n"); exit(FAIL); } protostr = optarg; if (!strcmp(protostr, "udp")) { protocol = KNET_TRANSPORT_UDP; protofound = 1; } if (!strcmp(protostr, "sctp")) { protocol = KNET_TRANSPORT_SCTP; protofound = 1; } if (!protofound) { printf("Error: invalid protocol %s specified. -P accepts udp|sctp\n", policystr); exit(FAIL); } break; case 't': if (thisnodeid >= 0) { printf("Error: -t can only be specified once\n"); exit(FAIL); } thisnodeid = atoi(optarg); if ((thisnodeid < 0) || (thisnodeid > 65536)) { printf("Error: -t nodeid out of range %d (1 - 65536)\n", thisnodeid); exit(FAIL); } break; case 'n': if (onidx == MAX_NODES) { printf("Error: too many other nodes. Max %d\n", MAX_NODES); exit(FAIL); } othernodeinfo[onidx] = optarg; onidx++; break; case 'b': port = atoi(optarg); if ((port < 1) || (port > 65536)) { printf("Error: port %d out of range (1 - 65536)\n", port); exit(FAIL); } case 'o': if (globallistener) { printf("Error: -l cannot be used with -o\n"); exit(FAIL); } portoffset = 1; break; case 'l': if (portoffset) { printf("Error: -o cannot be used with -l\n"); exit(FAIL); } globallistener = 1; break; case 'w': wait = 0; break; case 's': if (senderid >= 0) { printf("Error: -s can only be specified once\n"); exit(FAIL); } senderid = atoi(optarg); if ((senderid < 0) || (senderid > 65536)) { printf("Error: -s nodeid out of range %d (1 - 65536)\n", senderid); exit(FAIL); } break; case 'T': if (!strcmp("ping", optarg)) { test_type = TEST_PING; } if (!strcmp("ping_data", optarg)) { test_type = TEST_PING_AND_DATA; } if (!strcmp("perf", optarg)) { test_type = TEST_PERF; } break; case 'C': continous = 1; break; default: break; } } if (thisnodeid < 0) { printf("Who am I?!? missing -t from command line?\n"); exit(FAIL); } if (onidx < 1) { printf("no other nodes configured?!? missing -n from command line\n"); exit(FAIL); } parse_nodes(othernodeinfo, onidx, port, nodes, thisnodeid, &thisidx); if (thisidx < 0) { printf("no config for this node found\n"); exit(FAIL); } if (senderid >= 0) { for (i=0; i < onidx; i++) { if (senderid == nodes[i].nodeid) { break; } } if (i == onidx) { printf("Unable to find senderid in nodelist\n"); exit(FAIL); } } if ((test_type == TEST_PERF) && (senderid < 0)) { printf("Error: performance test requires -s to be set (for now)\n"); exit(FAIL); } logfd = start_logging(stdout); knet_h = knet_handle_new(thisnodeid, logfd, debug); if (!knet_h) { printf("Unable to knet_handle_new: %s\n", strerror(errno)); exit(FAIL); } if (cryptocfg) { memset(&knet_handle_crypto_cfg, 0, sizeof(knet_handle_crypto_cfg)); cryptomodel = strtok(cryptocfg, ":"); cryptotype = strtok(NULL, ":"); cryptohash = strtok(NULL, ":"); if (cryptomodel) { strncpy(knet_handle_crypto_cfg.crypto_model, cryptomodel, sizeof(knet_handle_crypto_cfg.crypto_model) - 1); } if (cryptotype) { strncpy(knet_handle_crypto_cfg.crypto_cipher_type, cryptotype, sizeof(knet_handle_crypto_cfg.crypto_cipher_type) - 1); } if (cryptohash) { strncpy(knet_handle_crypto_cfg.crypto_hash_type, cryptohash, sizeof(knet_handle_crypto_cfg.crypto_hash_type) - 1); } knet_handle_crypto_cfg.private_key_len = KNET_MAX_KEY_LEN; if (knet_handle_crypto(knet_h, &knet_handle_crypto_cfg)) { printf("Unable to init crypto\n"); 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); 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); exit(FAIL); } for (i=0; i < onidx; i++) { if (i == thisidx) { continue; } if (knet_host_add(knet_h, nodes[i].nodeid) < 0) { printf("knet_host_add failed: %s\n", strerror(errno)); exit(FAIL); } if (knet_host_set_policy(knet_h, nodes[i].nodeid, policy) < 0) { printf("knet_host_set_policy failed: %s\n", strerror(errno)); exit(FAIL); } for (link_idx = 0; link_idx < nodes[i].links; link_idx++) { if (portoffset) { if (nodes[thisidx].address[link_idx].ss_family == AF_INET) { so_in = (struct sockaddr_in *)&nodes[thisidx].address[link_idx]; thisport = ntohs(so_in->sin_port); thisnewport = thisport + nodes[i].nodeid; so_in->sin_port = (htons(thisnewport)); so_in = (struct sockaddr_in *)&nodes[i].address[link_idx]; otherport = ntohs(so_in->sin_port); othernewport = otherport + nodes[thisidx].nodeid; so_in->sin_port = (htons(othernewport)); } else { so_in6 = (struct sockaddr_in6 *)&nodes[thisidx].address[link_idx]; thisport = ntohs(so_in6->sin6_port); thisnewport = thisport + nodes[i].nodeid; so_in6->sin6_port = (htons(thisnewport)); so_in6 = (struct sockaddr_in6 *)&nodes[i].address[link_idx]; otherport = ntohs(so_in6->sin6_port); othernewport = otherport + nodes[thisidx].nodeid; so_in6->sin6_port = (htons(othernewport)); } } if (!globallistener) { src = &nodes[thisidx].address[link_idx]; } else { if (nodes[thisidx].address[link_idx].ss_family == AF_INET) { src = &allv4; } else { src = &allv6; } } if (knet_link_set_config(knet_h, nodes[i].nodeid, link_idx, protocol, src, &nodes[i].address[link_idx]) < 0) { printf("Unable to configure link: %s\n", strerror(errno)); exit(FAIL); } if (portoffset) { if (nodes[thisidx].address[link_idx].ss_family == AF_INET) { so_in = (struct sockaddr_in *)&nodes[thisidx].address[link_idx]; so_in->sin_port = (htons(thisport)); so_in = (struct sockaddr_in *)&nodes[i].address[link_idx]; so_in->sin_port = (htons(otherport)); } else { so_in6 = (struct sockaddr_in6 *)&nodes[thisidx].address[link_idx]; so_in6->sin6_port = (htons(thisport)); so_in6 = (struct sockaddr_in6 *)&nodes[i].address[link_idx]; so_in6->sin6_port = (htons(otherport)); } } if (knet_link_set_enable(knet_h, nodes[i].nodeid, link_idx, 1) < 0) { printf("knet_link_set_enable failed: %s\n", strerror(errno)); exit(FAIL); } } } if (knet_handle_setfwd(knet_h, 1) < 0) { printf("knet_handle_setfwd failed: %s\n", strerror(errno)); exit(FAIL); } if (wait) { while(!allnodesup) { allnodesup = 1; for (i=0; i < onidx; i++) { if (i == thisidx) { continue; } if(knet_h->host_index[nodes[i].nodeid]->status.reachable != 1) { printf("waiting host %d to be reachable\n", nodes[i].nodeid); allnodesup = 0; } } if (!allnodesup) { sleep(1); } } sleep(1); } } static int ping_dst_host_filter(void *pvt_data, const unsigned char *outdata, ssize_t outdata_len, uint8_t tx_rx, - uint8_t this_host_id, - uint8_t src_host_id, + knet_node_id_t this_host_id, + knet_node_id_t src_host_id, int8_t *dst_channel, - uint8_t *dst_host_ids, + knet_node_id_t *dst_host_ids, size_t *dst_host_ids_entries) { if (broadcast_test) { return 1; } if (tx_rx == KNET_NOTIFY_TX) { - memmove(&dst_host_ids[0], outdata, 1); + memmove(&dst_host_ids[0], outdata, 2); } else { dst_host_ids[0] = this_host_id; } *dst_host_ids_entries = 1; return 0; } static void *_rx_thread(void *args) { fd_set rfds; ssize_t len; struct timeval tv; struct sockaddr_storage address[PCKT_FRAG_MAX]; struct mmsghdr msg[PCKT_FRAG_MAX]; struct iovec iov_in[PCKT_FRAG_MAX]; int i, msg_recv; for (i = 0; i < PCKT_FRAG_MAX; i++) { rx_buf[i] = malloc(KNET_MAX_PACKET_SIZE); if (!rx_buf[i]) { printf("RXT: Unable to malloc!\n"); return NULL; } memset(rx_buf[i], 0, KNET_MAX_PACKET_SIZE); iov_in[i].iov_base = (void *)rx_buf[i]; iov_in[i].iov_len = KNET_MAX_PACKET_SIZE; memset(&msg[i].msg_hdr, 0, sizeof(struct msghdr)); msg[i].msg_hdr.msg_name = &address[i]; msg[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); msg[i].msg_hdr.msg_iov = &iov_in[i]; msg[i].msg_hdr.msg_iovlen = 1; } select_loop: tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(datafd, &rfds); len = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if (len < 0) { printf("RXT: Unable select over datafd\nHALTING RX THREAD!\n"); return NULL; } if (!len) { printf("RXT: No data for the past 5 seconds\n"); } if (FD_ISSET(datafd, &rfds)) { msg_recv = recvmmsg(datafd, msg, PCKT_FRAG_MAX, MSG_DONTWAIT | MSG_NOSIGNAL, NULL); if (msg_recv < 0) { printf("RXT: error from recvmmsg: %s\n", strerror(errno)); } for (i = 0; i < msg_recv; i++) { if (msg[i].msg_len == 0) { printf("RXT: received 0 bytes message?\n"); } if (test_type == TEST_PING_AND_DATA) { printf("received %lu bytes message: %s\n", (long)msg[i].msg_len, (char *)msg[i].msg_hdr.msg_iov->iov_base); } /* * do stats here */ } } goto select_loop; return NULL; } static void setup_data_txrx_common(void) { if (!rx_thread) { if (knet_handle_enable_filter(knet_h, NULL, ping_dst_host_filter)) { printf("Unable to enable dst_host_filter: %s\n", strerror(errno)); exit(FAIL); } printf("Setting up rx thread\n"); if (pthread_create(&rx_thread, 0, _rx_thread, NULL)) { printf("Unable to start rx thread\n"); exit(FAIL); } } } static void stop_rx_thread(void) { void *retval; int i; if (rx_thread) { printf("Shutting down rx thread\n"); pthread_cancel(rx_thread); pthread_join(rx_thread, &retval); for (i = 0; i < PCKT_FRAG_MAX; i ++) { free(rx_buf[i]); } } } static void send_ping_data(void) { const char *buf = "Hello world!\x0"; ssize_t len = strlen(buf); if (knet_send(knet_h, buf, len, channel) != len) { printf("Error sending hello world: %s\n", strerror(errno)); } sleep(1); } static void cleanup_all(void) { if (pthread_mutex_lock(&shutdown_mutex)) { return; } if (shutdown_in_progress) { pthread_mutex_unlock(&shutdown_mutex); return; } shutdown_in_progress = 1; pthread_mutex_unlock(&shutdown_mutex); if (rx_thread) { stop_rx_thread(); } knet_handle_stop(knet_h); } static void sigint_handler(int signum) { printf("Cleaning up... got signal: %d\n", signum); cleanup_all(); exit(PASS); } int main(int argc, char *argv[]) { if (signal(SIGINT, sigint_handler) == SIG_ERR) { printf("Unable to configure SIGINT handler\n"); exit(FAIL); } need_root(); setup_knet(argc, argv); restart: switch(test_type) { default: case TEST_PING: /* basic ping, no data */ sleep(5); break; case TEST_PING_AND_DATA: setup_data_txrx_common(); send_ping_data(); break; case TEST_PERF: setup_data_txrx_common(); break; } if (continous) { goto restart; } cleanup_all(); return PASS; } diff --git a/libknet/tests/test-common.c b/libknet/tests/test-common.c index 94cf25be..3e51d8f3 100644 --- a/libknet/tests/test-common.c +++ b/libknet/tests/test-common.c @@ -1,434 +1,434 @@ /* * Copyright (C) 2016 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 #include #include #include "libknet.h" #include "test-common.h" static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; static int log_init = 0; static pthread_mutex_t log_thread_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t log_thread; static int log_thread_init = 0; static int log_fds[2]; struct log_thread_data { int logfd; FILE *std; }; static struct log_thread_data data; static pthread_mutex_t shutdown_mutex = PTHREAD_MUTEX_INITIALIZER; static int shutdown_in_progress = 0; static int _read_pipe(int fd, char **file, size_t *length) { char buf[4096]; int n; int done = 0; *file = NULL; *length = 0; memset(buf, 0, sizeof(buf)); while (!done) { n = read(fd, buf, sizeof(buf)); if (n < 0) { if (errno == EINTR) continue; if (*file) free(*file); return n; } if (n == 0 && (!*length)) return 0; if (n == 0) done = 1; if (*file) *file = realloc(*file, (*length) + n + done); else *file = malloc(n + done); if (!*file) return -1; memcpy((*file) + (*length), buf, n); *length += (done + n); } /* Null terminator */ (*file)[(*length) - 1] = 0; return 0; } int execute_shell(const char *command, char **error_string) { pid_t pid; int status, err = 0; int fd[2]; size_t size = 0; if ((command == NULL) || (!error_string)) { errno = EINVAL; return FAIL; } *error_string = NULL; err = pipe(fd); if (err) goto out_clean; pid = fork(); if (pid < 0) { err = pid; goto out_clean; } if (pid) { /* parent */ close(fd[1]); err = _read_pipe(fd[0], error_string, &size); if (err) goto out_clean0; waitpid(pid, &status, 0); if (!WIFEXITED(status)) { err = -1; goto out_clean0; } if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { err = WEXITSTATUS(status); goto out_clean0; } goto out_clean0; } else { /* child */ close(0); close(1); close(2); close(fd[0]); dup2(fd[1], 1); dup2(fd[1], 2); close(fd[1]); execlp("/bin/sh", "/bin/sh", "-c", command, NULL); exit(FAIL); } out_clean: close(fd[1]); out_clean0: close(fd[0]); return err; } int is_memcheck(void) { char *val; val = getenv("KNETMEMCHECK"); if (val) { if (!strncmp(val, "yes", 3)) { return 1; } } return 0; } int is_helgrind(void) { char *val; val = getenv("KNETHELGRIND"); if (val) { if (!strncmp(val, "yes", 3)) { return 1; } } return 0; } int need_root(void) { if (geteuid() != 0) { printf("This test requires root privileges\n"); exit(SKIP); } return PASS; } void set_scheduler(int policy) { struct sched_param sched_param; int err; err = sched_get_priority_max(policy); if (err < 0) { printf("Could not get maximum scheduler priority\n"); exit(FAIL); } sched_param.sched_priority = err; err = sched_setscheduler(0, policy, &sched_param); if (err < 0) { printf("Could not set priority\n"); exit(FAIL); } return; } int setup_logpipes(int *logfds) { if (pipe2(logfds, O_CLOEXEC | O_NONBLOCK) < 0) { printf("Unable to setup logging pipe\n"); exit(FAIL); } return PASS; } void close_logpipes(int *logfds) { close(logfds[0]); logfds[0] = 0; close(logfds[1]); logfds[1] = 0; } void flush_logs(int logfd, FILE *std) { struct knet_log_msg msg; size_t bytes_read; int len; next: len = 0; bytes_read = 0; memset(&msg, 0, sizeof(struct knet_log_msg)); while (bytes_read < sizeof(struct knet_log_msg)) { len = read(logfd, &msg + bytes_read, sizeof(struct knet_log_msg) - bytes_read); if (len <= 0) { return; } bytes_read += len; } if (len > 0) { fprintf(std, "knet logs: [%s] %s: %s\n", knet_log_get_loglevel_name(msg.msglevel), knet_log_get_subsystem_name(msg.subsystem), msg.msg); goto next; } } static void *_logthread(void *args) { fd_set rfds; ssize_t len; struct timeval tv; select_loop: tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO(&rfds); FD_SET(data.logfd, &rfds); len = select(FD_SETSIZE, &rfds, NULL, NULL, &tv); if (len < 0) { fprintf(data.std, "Unable select over logfd!\nHALTING LOGTHREAD!\n"); return NULL; } if (!len) { fprintf(data.std, "knet logs: No logs in the last 5 seconds\n"); } if (FD_ISSET(data.logfd, &rfds)) { flush_logs(data.logfd, data.std); } goto select_loop; return NULL; } int start_logthread(int logfd, FILE *std) { int savederrno = 0; savederrno = pthread_mutex_lock(&log_thread_mutex); if (savederrno) { printf("Unable to get log_thread mutex lock\n"); return -1; } if (!log_thread_init) { data.logfd = logfd; data.std = std; savederrno = pthread_create(&log_thread, 0, _logthread, NULL); if (savederrno) { printf("Unable to start logging thread: %s\n", strerror(savederrno)); pthread_mutex_unlock(&log_thread_mutex); return -1; } log_thread_init = 1; } pthread_mutex_unlock(&log_thread_mutex); return 0; } int stop_logthread(void) { int savederrno = 0; void *retval; savederrno = pthread_mutex_lock(&log_thread_mutex); if (savederrno) { printf("Unable to get log_thread mutex lock\n"); return -1; } if (log_thread_init) { pthread_cancel(log_thread); pthread_join(log_thread, &retval); log_thread_init = 0; } pthread_mutex_unlock(&log_thread_mutex); return 0; } static void stop_logging(void) { stop_logthread(); flush_logs(log_fds[0], stdout); close_logpipes(log_fds); } int start_logging(FILE *std) { int savederrno = 0; savederrno = pthread_mutex_lock(&log_mutex); if (savederrno) { printf("Unable to get log_mutex lock\n"); return -1; } if (!log_init) { setup_logpipes(log_fds); if (atexit(&stop_logging) != 0) { printf("Unable to register atexit handler to stop logging: %s\n", strerror(errno)); exit(FAIL); } if (start_logthread(log_fds[0], std) < 0) { exit(FAIL); } log_init = 1; } pthread_mutex_unlock(&log_mutex); return log_fds[1]; } int knet_handle_stop(knet_handle_t knet_h) { int i, j, savederrno; - uint8_t host_ids[KNET_MAX_HOST]; + knet_node_id_t host_ids[KNET_MAX_HOST]; uint8_t link_ids[KNET_MAX_LINK]; size_t host_ids_entries = 0, link_ids_entries = 0; struct knet_link_status status; savederrno = pthread_mutex_lock(&shutdown_mutex); if (savederrno) { printf("Unable to get shutdown mutex lock\n"); return -1; } if (shutdown_in_progress) { pthread_mutex_unlock(&shutdown_mutex); errno = EINVAL; return -1; } shutdown_in_progress = 1; pthread_mutex_unlock(&shutdown_mutex); if (!knet_h) { errno = EINVAL; return -1; } if (knet_host_get_host_list(knet_h, host_ids, &host_ids_entries) < 0) { printf("knet_host_get_host_list failed: %s\n", strerror(errno)); return -1; } for (i = 0; i < host_ids_entries; i++) { if (knet_link_get_link_list(knet_h, host_ids[i], link_ids, &link_ids_entries)) { printf("knet_link_get_link_list failed: %s\n", strerror(errno)); return -1; } for (j = 0; j < link_ids_entries; j++) { if (knet_link_get_status(knet_h, host_ids[i], link_ids[j], &status)) { printf("knet_link_get_status failed: %s\n", strerror(errno)); return -1; } if (status.enabled) { if (knet_link_set_enable(knet_h, host_ids[i], j, 0)) { printf("knet_link_set_enable failed: %s\n", strerror(errno)); return -1; } } knet_link_clear_config(knet_h, host_ids[i], j); } if (knet_host_remove(knet_h, host_ids[i]) < 0) { printf("knet_host_remove failed: %s\n", strerror(errno)); return -1; } } if (knet_handle_free(knet_h)) { printf("knet_handle_free failed: %s\n", strerror(errno)); return -1; } return 0; } diff --git a/libknet/threads_dsthandler.c b/libknet/threads_dsthandler.c index c855cc5c..3b1612cc 100644 --- a/libknet/threads_dsthandler.c +++ b/libknet/threads_dsthandler.c @@ -1,61 +1,61 @@ /* * Copyright (C) 2010-2015 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 "host.h" #include "compat.h" #include "logging.h" #include "threads_common.h" #include "threads_dsthandler.h" static void _handle_dst_link_updates(knet_handle_t knet_h) { - uint8_t host_id; + knet_node_id_t host_id; struct knet_host *host; if (recv(knet_h->dstsockfd[0], &host_id, sizeof(host_id), MSG_DONTWAIT | MSG_NOSIGNAL) != sizeof(host_id)) { log_debug(knet_h, KNET_SUB_DSTCACHE, "Short read on dstsockfd"); return; } if (pthread_rwlock_wrlock(&knet_h->global_rwlock) != 0) { log_debug(knet_h, KNET_SUB_DSTCACHE, "Unable to get read lock"); return; } host = knet_h->host_index[host_id]; if (!host) { log_debug(knet_h, KNET_SUB_DSTCACHE, "Unable to find host: %u", host_id); goto out_unlock; } _host_dstcache_update_sync(knet_h, host); out_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); return; } void *_handle_dst_link_handler_thread(void *data) { knet_handle_t knet_h = (knet_handle_t) data; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; while (!shutdown_in_progress(knet_h)) { if (epoll_wait(knet_h->dst_link_handler_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1) >= 1) _handle_dst_link_updates(knet_h); } return NULL; } diff --git a/libknet/threads_heartbeat.c b/libknet/threads_heartbeat.c index c4f48405..77727245 100644 --- a/libknet/threads_heartbeat.c +++ b/libknet/threads_heartbeat.c @@ -1,167 +1,167 @@ /* * Copyright (C) 2010-2015 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 "crypto.h" #include "link.h" #include "logging.h" #include "threads_common.h" #include "threads_heartbeat.h" static void _link_down(knet_handle_t knet_h, struct knet_host *dst_host, struct knet_link *dst_link) { memset(&dst_link->pmtud_last, 0, sizeof(struct timespec)); dst_link->received_pong = 0; dst_link->status.pong_last.tv_nsec = 0; if (dst_link->status.connected == 1) { log_info(knet_h, KNET_SUB_LINK, "host: %u link: %u is down", dst_host->host_id, dst_link->link_id); _link_updown(knet_h, dst_host->host_id, dst_link->link_id, dst_link->status.enabled, 0); } } static void _handle_check_each(knet_handle_t knet_h, struct knet_host *dst_host, struct knet_link *dst_link, int timed) { int err = 0, savederrno = 0; int len; ssize_t outlen = KNET_HEADER_PING_SIZE; struct timespec clock_now, pong_last; unsigned long long diff_ping; unsigned char *outbuf = (unsigned char *)knet_h->pingbuf; if (dst_link->transport_connected == 0) { _link_down(knet_h, dst_host, dst_link); return; } /* caching last pong to avoid race conditions */ pong_last = dst_link->status.pong_last; if (clock_gettime(CLOCK_MONOTONIC, &clock_now) != 0) { log_debug(knet_h, KNET_SUB_HEARTBEAT, "Unable to get monotonic clock"); return; } timespec_diff(dst_link->ping_last, clock_now, &diff_ping); if ((diff_ping >= (dst_link->ping_interval * 1000llu)) || (!timed)) { memmove(&knet_h->pingbuf->khp_ping_time[0], &clock_now, sizeof(struct timespec)); knet_h->pingbuf->khp_ping_link = dst_link->link_id; if (pthread_mutex_lock(&knet_h->tx_seq_num_mutex)) { log_debug(knet_h, KNET_SUB_HEARTBEAT, "Unable to get seq mutex lock"); return; } knet_h->pingbuf->khp_ping_seq_num = htons(knet_h->tx_seq_num); pthread_mutex_unlock(&knet_h->tx_seq_num_mutex); knet_h->pingbuf->khp_ping_timed = timed; if (knet_h->crypto_instance) { if (crypto_encrypt_and_sign(knet_h, (const unsigned char *)knet_h->pingbuf, outlen, knet_h->pingbuf_crypt, &outlen) < 0) { log_debug(knet_h, KNET_SUB_HEARTBEAT, "Unable to crypto ping packet"); return; } outbuf = knet_h->pingbuf_crypt; } retry: len = sendto(dst_link->outsock, outbuf, outlen, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) &dst_link->dst_addr, sizeof(struct sockaddr_storage)); dst_link->ping_last = clock_now; if (len != outlen) { err = knet_h->transport_ops[dst_link->transport_type]->transport_tx_sock_error(knet_h, dst_link->outsock, len, savederrno); switch(err) { case -1: /* unrecoverable error */ log_debug(knet_h, KNET_SUB_HEARTBEAT, "Unable to send ping (sock: %d) packet (sendto): %d %s. recorded src ip: %s src port: %s dst ip: %s dst port: %s", dst_link->outsock, errno, strerror(errno), dst_link->status.src_ipaddr, dst_link->status.src_port, dst_link->status.dst_ipaddr, dst_link->status.dst_port); break; case 0: break; case 1: goto retry; break; } } else { dst_link->last_ping_size = outlen; } } timespec_diff(pong_last, clock_now, &diff_ping); if ((pong_last.tv_nsec) && (diff_ping >= (dst_link->pong_timeout * 1000llu))) { _link_down(knet_h, dst_host, dst_link); } } void _send_pings(knet_handle_t knet_h, int timed) { struct knet_host *dst_host; int link_idx; if (pthread_mutex_lock(&knet_h->hb_mutex)) { log_debug(knet_h, KNET_SUB_HEARTBEAT, "Unable to get hb mutex lock"); return; } for (dst_host = knet_h->host_head; dst_host != NULL; dst_host = dst_host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { if ((dst_host->link[link_idx].status.enabled != 1) || ((dst_host->link[link_idx].dynamic == KNET_LINK_DYNIP) && (dst_host->link[link_idx].status.dynconnected != 1))) continue; _handle_check_each(knet_h, dst_host, &dst_host->link[link_idx], timed); } } pthread_mutex_unlock(&knet_h->hb_mutex); } void *_handle_heartbt_thread(void *data) { knet_handle_t knet_h = (knet_handle_t) data; /* preparing ping buffer */ knet_h->pingbuf->kh_version = KNET_HEADER_VERSION; knet_h->pingbuf->kh_type = KNET_HEADER_TYPE_PING; - knet_h->pingbuf->kh_node = knet_h->host_id; + knet_h->pingbuf->kh_node = htons(knet_h->host_id); while (!shutdown_in_progress(knet_h)) { usleep(KNET_THREADS_TIMERES); if (pthread_rwlock_rdlock(&knet_h->global_rwlock) != 0) { log_debug(knet_h, KNET_SUB_HEARTBEAT, "Unable to get read lock"); continue; } _send_pings(knet_h, 1); pthread_rwlock_unlock(&knet_h->global_rwlock); } return NULL; } diff --git a/libknet/threads_pmtud.c b/libknet/threads_pmtud.c index 3f375a52..0df8b4fe 100644 --- a/libknet/threads_pmtud.c +++ b/libknet/threads_pmtud.c @@ -1,404 +1,404 @@ /* * Copyright (C) 2010-2015 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 "crypto.h" #include "link.h" #include "host.h" #include "logging.h" #include "threads_common.h" #include "threads_pmtud.h" static int _handle_check_link_pmtud(knet_handle_t knet_h, struct knet_host *dst_host, struct knet_link *dst_link) { int err, ret, savederrno, mutex_retry_limit, failsafe; ssize_t onwire_len; /* current packet onwire size */ ssize_t overhead_len; /* onwire packet overhead (protocol based) */ ssize_t max_mtu_len; /* max mtu for protocol */ ssize_t data_len; /* how much data we can send in the packet * generally would be onwire_len - overhead_len * needs to be adjusted for crypto */ ssize_t pad_len; /* crypto packet pad size, needs to move into crypto.c callbacks */ int len; /* len of what we were able to sendto onwire */ struct timespec ts; unsigned char *outbuf = (unsigned char *)knet_h->pmtudbuf; mutex_retry_limit = 0; failsafe = 0; pad_len = 0; dst_link->last_bad_mtu = 0; knet_h->pmtudbuf->khp_pmtud_link = dst_link->link_id; switch (dst_link->dst_addr.ss_family) { case AF_INET6: max_mtu_len = KNET_PMTUD_SIZE_V6; overhead_len = KNET_PMTUD_OVERHEAD_V6 + dst_link->proto_overhead; dst_link->last_good_mtu = dst_link->last_ping_size + overhead_len; break; case AF_INET: max_mtu_len = KNET_PMTUD_SIZE_V4; overhead_len = KNET_PMTUD_OVERHEAD_V4 + dst_link->proto_overhead; dst_link->last_good_mtu = dst_link->last_ping_size + overhead_len; break; default: log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD aborted, unknown protocol"); return -1; break; } /* * discovery starts from the top because kernel will * refuse to send packets > current iface mtu. * this saves us some time and network bw. */ onwire_len = max_mtu_len; restart: /* * prevent a race when interface mtu is changed _exactly_ during * the discovery process and it's complex to detect. Easier * to wait the next loop. * 30 is not an arbitrary value. To bisect from 576 to 128000 doesn't * take more than 18/19 steps. */ if (failsafe == 30) { log_err(knet_h, KNET_SUB_PMTUD, "Aborting PMTUD process: Too many attempts. MTU might have changed during discovery."); return -1; } else { failsafe++; } data_len = onwire_len - overhead_len; if (knet_h->crypto_instance) { if (knet_h->sec_block_size) { pad_len = knet_h->sec_block_size - (data_len % knet_h->sec_block_size); if (pad_len == knet_h->sec_block_size) { pad_len = 0; } data_len = data_len + pad_len; } data_len = data_len + (knet_h->sec_hash_size + knet_h->sec_salt_size + knet_h->sec_block_size); if (knet_h->sec_block_size) { while (data_len + overhead_len >= max_mtu_len) { data_len = data_len - knet_h->sec_block_size; } } if (dst_link->last_bad_mtu) { while (data_len + overhead_len >= dst_link->last_bad_mtu) { data_len = data_len - (knet_h->sec_hash_size + knet_h->sec_salt_size + knet_h->sec_block_size); } } if (data_len < (knet_h->sec_hash_size + knet_h->sec_salt_size + knet_h->sec_block_size) + 1) { log_debug(knet_h, KNET_SUB_PMTUD, "Aborting PMTUD process: link mtu smaller than crypto header detected (link might have been disconnected)"); return -1; } onwire_len = data_len + overhead_len; knet_h->pmtudbuf->khp_pmtud_size = onwire_len; if (crypto_encrypt_and_sign(knet_h, (const unsigned char *)knet_h->pmtudbuf, data_len - (knet_h->sec_hash_size + knet_h->sec_salt_size + knet_h->sec_block_size), knet_h->pmtudbuf_crypt, &data_len) < 0) { log_debug(knet_h, KNET_SUB_PMTUD, "Unable to crypto pmtud packet"); return -1; } outbuf = knet_h->pmtudbuf_crypt; } else { knet_h->pmtudbuf->khp_pmtud_size = onwire_len; } /* link has gone down, aborting pmtud */ if (dst_link->status.connected != 1) { log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD detected host (%u) link (%u) has been disconnected", dst_host->host_id, dst_link->link_id); return -1; } if (dst_link->transport_connected != 1) { log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD detected host (%u) link (%u) has been disconnected", dst_host->host_id, dst_link->link_id); return -1; } if (pthread_mutex_lock(&knet_h->pmtud_mutex) != 0) { log_debug(knet_h, KNET_SUB_PMTUD, "Unable to get mutex lock"); return -1; } retry: len = sendto(dst_link->outsock, outbuf, data_len, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) &dst_link->dst_addr, sizeof(struct sockaddr_storage)); savederrno = errno; err = knet_h->transport_ops[dst_link->transport_type]->transport_tx_sock_error(knet_h, dst_link->outsock, len, savederrno); switch(err) { case -1: /* unrecoverable error */ log_debug(knet_h, KNET_SUB_PMTUD, "Unable to send pmtu packet (sendto): %d %s", savederrno, strerror(savederrno)); pthread_mutex_unlock(&knet_h->pmtud_mutex); return -1; case 0: /* ignore error and continue */ break; case 1: /* retry to send those same data */ goto retry; break; } if (len != data_len) { if (savederrno == EMSGSIZE) { dst_link->last_bad_mtu = onwire_len; } else { log_debug(knet_h, KNET_SUB_PMTUD, "Unable to send pmtu packet len: %zu err: %s", onwire_len, strerror(savederrno)); } } else { dst_link->last_sent_mtu = onwire_len; dst_link->last_recv_mtu = 0; if (clock_gettime(CLOCK_REALTIME, &ts) < 0) { log_debug(knet_h, KNET_SUB_PMTUD, "Unable to get current time: %s", strerror(errno)); pthread_mutex_unlock(&knet_h->pmtud_mutex); return -1; } /* * Set an artibrary 2 seconds timeout to receive a PMTUd reply * perhaps this should be configurable but: * 1) too short timeout can cause instability since MTU value * influeces link status * 2) too high timeout slows down the MTU detection process for * small MTU * * Another option is to make the PMTUd process less influent * in link status detection but that could cause data packet loss * without link up/down changes */ ts.tv_sec += 2; ret = pthread_cond_timedwait(&knet_h->pmtud_cond, &knet_h->pmtud_mutex, &ts); if (shutdown_in_progress(knet_h)) { pthread_mutex_unlock(&knet_h->pmtud_mutex); log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD aborted. shutdown in progress"); return -1; } if ((ret != 0) && (ret != ETIMEDOUT)) { pthread_mutex_unlock(&knet_h->pmtud_mutex); if (mutex_retry_limit == 3) { log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD aborted, unable to get mutex lock"); return -1; } mutex_retry_limit++; goto restart; } if ((dst_link->last_recv_mtu != onwire_len) || (ret)) { dst_link->last_bad_mtu = onwire_len; } else { int found_mtu = 0; if (knet_h->sec_block_size) { if ((onwire_len + knet_h->sec_block_size >= max_mtu_len) || ((dst_link->last_bad_mtu) && (dst_link->last_bad_mtu <= (onwire_len + knet_h->sec_block_size)))) { found_mtu = 1; } } else { if ((onwire_len == max_mtu_len) || ((dst_link->last_bad_mtu) && (dst_link->last_bad_mtu == (onwire_len + 1)))) { found_mtu = 1; } } if (found_mtu) { /* * account for IP overhead, knet headers and crypto in PMTU calculation */ dst_link->status.mtu = onwire_len - dst_link->status.proto_overhead; pthread_mutex_unlock(&knet_h->pmtud_mutex); return 0; } dst_link->last_good_mtu = onwire_len; } } onwire_len = (dst_link->last_good_mtu + dst_link->last_bad_mtu) / 2; pthread_mutex_unlock(&knet_h->pmtud_mutex); goto restart; } static int _handle_check_pmtud(knet_handle_t knet_h, struct knet_host *dst_host, struct knet_link *dst_link, unsigned int *min_mtu) { uint8_t saved_valid_pmtud; unsigned int saved_pmtud; struct timespec clock_now; unsigned long long diff_pmtud, interval; interval = knet_h->pmtud_interval * 1000000000llu; /* nanoseconds */ if (clock_gettime(CLOCK_MONOTONIC, &clock_now) != 0) { log_debug(knet_h, KNET_SUB_PMTUD, "Unable to get monotonic clock"); return 0; } timespec_diff(dst_link->pmtud_last, clock_now, &diff_pmtud); if (diff_pmtud < interval) { *min_mtu = dst_link->status.mtu; return dst_link->has_valid_mtu; } switch (dst_link->dst_addr.ss_family) { case AF_INET6: dst_link->status.proto_overhead = KNET_PMTUD_OVERHEAD_V6 + dst_link->proto_overhead + KNET_HEADER_ALL_SIZE + knet_h->sec_header_size; break; case AF_INET: dst_link->status.proto_overhead = KNET_PMTUD_OVERHEAD_V4 + dst_link->proto_overhead + KNET_HEADER_ALL_SIZE + knet_h->sec_header_size; break; } saved_pmtud = dst_link->status.mtu; saved_valid_pmtud = dst_link->has_valid_mtu; log_debug(knet_h, KNET_SUB_PMTUD, "Starting PMTUD for host: %u link: %u", dst_host->host_id, dst_link->link_id); if (_handle_check_link_pmtud(knet_h, dst_host, dst_link) < 0) { dst_link->has_valid_mtu = 0; } else { dst_link->has_valid_mtu = 1; switch (dst_link->dst_addr.ss_family) { case AF_INET6: if (((dst_link->status.mtu + dst_link->status.proto_overhead) < KNET_PMTUD_MIN_MTU_V6) || ((dst_link->status.mtu + dst_link->status.proto_overhead) > KNET_PMTUD_SIZE_V6)) { log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD detected an IPv6 MTU out of bound value (%u) for host: %u link: %u.", dst_link->status.mtu + dst_link->status.proto_overhead, dst_host->host_id, dst_link->link_id); dst_link->has_valid_mtu = 0; } break; case AF_INET: if (((dst_link->status.mtu + dst_link->status.proto_overhead) < KNET_PMTUD_MIN_MTU_V4) || ((dst_link->status.mtu + dst_link->status.proto_overhead) > KNET_PMTUD_SIZE_V4)) { log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD detected an IPv4 MTU out of bound value (%u) for host: %u link: %u.", dst_link->status.mtu + dst_link->status.proto_overhead, dst_host->host_id, dst_link->link_id); dst_link->has_valid_mtu = 0; } break; } if (dst_link->has_valid_mtu) { if ((saved_pmtud) && (saved_pmtud != dst_link->status.mtu)) { log_info(knet_h, KNET_SUB_PMTUD, "PMTUD link change for host: %u link: %u from %u to %u", dst_host->host_id, dst_link->link_id, saved_pmtud, dst_link->status.mtu); } log_debug(knet_h, KNET_SUB_PMTUD, "PMTUD completed for host: %u link: %u current link mtu: %u", dst_host->host_id, dst_link->link_id, dst_link->status.mtu); if (dst_link->status.mtu < *min_mtu) { *min_mtu = dst_link->status.mtu; } dst_link->pmtud_last = clock_now; } } if (saved_valid_pmtud != dst_link->has_valid_mtu) { _host_dstcache_update_sync(knet_h, dst_host); } return dst_link->has_valid_mtu; } void *_handle_pmtud_link_thread(void *data) { knet_handle_t knet_h = (knet_handle_t) data; struct knet_host *dst_host; struct knet_link *dst_link; int link_idx; unsigned int min_mtu, have_mtu; unsigned int lower_mtu; knet_h->data_mtu = KNET_PMTUD_MIN_MTU_V4 - KNET_HEADER_ALL_SIZE - knet_h->sec_header_size; /* preparing pmtu buffer */ knet_h->pmtudbuf->kh_version = KNET_HEADER_VERSION; knet_h->pmtudbuf->kh_type = KNET_HEADER_TYPE_PMTUD; - knet_h->pmtudbuf->kh_node = knet_h->host_id; + knet_h->pmtudbuf->kh_node = htons(knet_h->host_id); while (!shutdown_in_progress(knet_h)) { usleep(KNET_THREADS_TIMERES); if (pthread_rwlock_rdlock(&knet_h->global_rwlock) != 0) { log_debug(knet_h, KNET_SUB_PMTUD, "Unable to get read lock"); continue; } lower_mtu = KNET_PMTUD_SIZE_V4; min_mtu = KNET_PMTUD_SIZE_V4 - KNET_HEADER_ALL_SIZE - knet_h->sec_header_size; have_mtu = 0; for (dst_host = knet_h->host_head; dst_host != NULL; dst_host = dst_host->next) { for (link_idx = 0; link_idx < KNET_MAX_LINK; link_idx++) { dst_link = &dst_host->link[link_idx]; if ((dst_link->status.enabled != 1) || (dst_link->status.connected != 1) || (!dst_link->last_ping_size) || ((dst_link->dynamic == KNET_LINK_DYNIP) && (dst_link->status.dynconnected != 1))) continue; if (_handle_check_pmtud(knet_h, dst_host, dst_link, &min_mtu)) { have_mtu = 1; if (min_mtu < lower_mtu) { lower_mtu = min_mtu; } } } } if (have_mtu) { if (knet_h->data_mtu != lower_mtu) { knet_h->data_mtu = lower_mtu; log_info(knet_h, KNET_SUB_PMTUD, "Global data MTU changed to: %u", knet_h->data_mtu); if (knet_h->pmtud_notify_fn) { knet_h->pmtud_notify_fn(knet_h->pmtud_notify_fn_private_data, knet_h->data_mtu); } } } pthread_rwlock_unlock(&knet_h->global_rwlock); } return NULL; } diff --git a/libknet/threads_rx.c b/libknet/threads_rx.c index 65feab4d..93e8ba7f 100644 --- a/libknet/threads_rx.c +++ b/libknet/threads_rx.c @@ -1,725 +1,727 @@ /* * Copyright (C) 2010-2017 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 "compat.h" #include "crypto.h" #include "host.h" #include "link.h" #include "logging.h" #include "transports.h" #include "threads_common.h" #include "threads_heartbeat.h" #include "threads_rx.h" #include "netutils.h" /* * RECV */ /* * return 1 if a > b * return -1 if b > a * return 0 if they are equal */ static inline int timecmp(struct timespec a, struct timespec b) { if (a.tv_sec != b.tv_sec) { if (a.tv_sec > b.tv_sec) { return 1; } else { return -1; } } else { if (a.tv_nsec > b.tv_nsec) { return 1; } else if (a.tv_nsec < b.tv_nsec) { return -1; } else { return 0; } } } /* * this functions needs to return an index (0 to 7) * to a knet_host_defrag_buf. (-1 on errors) */ static int find_pckt_defrag_buf(knet_handle_t knet_h, struct knet_header *inbuf) { struct knet_host *src_host = knet_h->host_index[inbuf->kh_node]; int i, oldest; /* * check if there is a buffer already in use handling the same seq_num */ for (i = 0; i < KNET_MAX_LINK; i++) { if (src_host->defrag_buf[i].in_use) { if (src_host->defrag_buf[i].pckt_seq == inbuf->khp_data_seq_num) { return i; } } } /* * If there is no buffer that's handling the current seq_num * either it's new or it's been reclaimed already. * check if it's been reclaimed/seen before using the defrag circular * buffer. If the pckt has been seen before, the buffer expired (ETIME) * and there is no point to try to defrag it again. */ if (!_seq_num_lookup(src_host, inbuf->khp_data_seq_num, 1, 0)) { errno = ETIME; return -1; } /* * register the pckt as seen */ _seq_num_set(src_host, inbuf->khp_data_seq_num, 1); /* * see if there is a free buffer */ for (i = 0; i < KNET_MAX_LINK; i++) { if (!src_host->defrag_buf[i].in_use) { return i; } } /* * at this point, there are no free buffers, the pckt is new * and we need to reclaim a buffer, and we will take the one * with the oldest timestamp. It's as good as any. */ oldest = 0; for (i = 0; i < KNET_MAX_LINK; i++) { if (timecmp(src_host->defrag_buf[i].last_update, src_host->defrag_buf[oldest].last_update) < 0) { oldest = i; } } src_host->defrag_buf[oldest].in_use = 0; return oldest; } static int pckt_defrag(knet_handle_t knet_h, struct knet_header *inbuf, ssize_t *len) { struct knet_host_defrag_buf *defrag_buf; int defrag_buf_idx; defrag_buf_idx = find_pckt_defrag_buf(knet_h, inbuf); if (defrag_buf_idx < 0) { if (errno == ETIME) { log_debug(knet_h, KNET_SUB_RX, "Defrag buffer expired"); } return 1; } defrag_buf = &knet_h->host_index[inbuf->kh_node]->defrag_buf[defrag_buf_idx]; /* * if the buf is not is use, then make sure it's clean */ if (!defrag_buf->in_use) { memset(defrag_buf, 0, sizeof(struct knet_host_defrag_buf)); defrag_buf->in_use = 1; defrag_buf->pckt_seq = inbuf->khp_data_seq_num; } /* * update timestamp on the buffer */ clock_gettime(CLOCK_MONOTONIC, &defrag_buf->last_update); /* * check if we already received this fragment */ if (defrag_buf->frag_map[inbuf->khp_data_frag_seq]) { /* * if we have received this fragment and we didn't clear the buffer * it means that we don't have all fragments yet */ return 1; } /* * we need to handle the last packet with gloves due to its different size */ if (inbuf->khp_data_frag_seq == inbuf->khp_data_frag_num) { defrag_buf->last_frag_size = *len; /* * in the event when the last packet arrives first, * we still don't know the offset vs the other fragments (based on MTU), * so we store the fragment at the end of the buffer where it's safe * and take a copy of the len so that we can restore its offset later. * remember we can't use the local MTU for this calculation because pMTU * can be asymettric between the same hosts. */ if (!defrag_buf->frag_size) { defrag_buf->last_first = 1; memmove(defrag_buf->buf + (KNET_MAX_PACKET_SIZE - *len), inbuf->khp_data_userdata, *len); } } else { defrag_buf->frag_size = *len; } memmove(defrag_buf->buf + ((inbuf->khp_data_frag_seq - 1) * defrag_buf->frag_size), inbuf->khp_data_userdata, *len); defrag_buf->frag_recv++; defrag_buf->frag_map[inbuf->khp_data_frag_seq] = 1; /* * check if we received all the fragments */ if (defrag_buf->frag_recv == inbuf->khp_data_frag_num) { /* * special case the last pckt */ if (defrag_buf->last_first) { memmove(defrag_buf->buf + ((inbuf->khp_data_frag_num - 1) * defrag_buf->frag_size), defrag_buf->buf + (KNET_MAX_PACKET_SIZE - defrag_buf->last_frag_size), defrag_buf->last_frag_size); } /* * recalculate packet lenght */ *len = ((inbuf->khp_data_frag_num - 1) * defrag_buf->frag_size) + defrag_buf->last_frag_size; /* * copy the pckt back in the user data */ memmove(inbuf->khp_data_userdata, defrag_buf->buf, *len); /* * free this buffer */ defrag_buf->in_use = 0; return 0; } return 1; } static void _parse_recv_from_links(knet_handle_t knet_h, int sockfd, const struct mmsghdr *msg) { int err = 0, savederrno = 0; ssize_t outlen; struct knet_host *src_host; struct knet_link *src_link; unsigned long long latency_last; - uint8_t dst_host_ids[KNET_MAX_HOST]; + knet_node_id_t dst_host_ids[KNET_MAX_HOST]; size_t dst_host_ids_entries = 0; int bcast = 1; struct timespec recvtime; struct knet_header *inbuf = msg->msg_hdr.msg_iov->iov_base; unsigned char *outbuf = (unsigned char *)msg->msg_hdr.msg_iov->iov_base; ssize_t len = msg->msg_len; struct knet_hostinfo *knet_hostinfo; struct iovec iov_out[1]; int8_t channel; struct sockaddr_storage pckt_src; seq_num_t recv_seq_num; int wipe_bufs = 0; if (knet_h->crypto_instance) { if (crypto_authenticate_and_decrypt(knet_h, (unsigned char *)inbuf, len, knet_h->recv_from_links_buf_decrypt, &outlen) < 0) { log_debug(knet_h, KNET_SUB_RX, "Unable to decrypt/auth packet"); return; } len = outlen; inbuf = (struct knet_header *)knet_h->recv_from_links_buf_decrypt; } if (len < (KNET_HEADER_SIZE + 1)) { log_debug(knet_h, KNET_SUB_RX, "Packet is too short: %ld", len); return; } if (inbuf->kh_version != KNET_HEADER_VERSION) { log_debug(knet_h, KNET_SUB_RX, "Packet version does not match"); return; } + inbuf->kh_node = ntohs(inbuf->kh_node); src_host = knet_h->host_index[inbuf->kh_node]; if (src_host == NULL) { /* host not found */ log_debug(knet_h, KNET_SUB_RX, "Unable to find source host for this packet"); return; } src_link = NULL; if ((inbuf->kh_type & KNET_HEADER_TYPE_PMSK) != 0) { src_link = src_host->link + (inbuf->khp_ping_link % KNET_MAX_LINK); if (src_link->dynamic == KNET_LINK_DYNIP) { /* * cpyaddrport will only copy address and port of the incoming * packet and strip extra bits such as flow and scopeid */ cpyaddrport(&pckt_src, msg->msg_hdr.msg_name); if (cmpaddr(&src_link->dst_addr, sockaddr_len(&src_link->dst_addr), &pckt_src, sockaddr_len(&pckt_src)) != 0) { log_debug(knet_h, KNET_SUB_RX, "host: %u link: %u appears to have changed ip address", src_host->host_id, src_link->link_id); memmove(&src_link->dst_addr, &pckt_src, sizeof(struct sockaddr_storage)); if (knet_addrtostr(&src_link->dst_addr, sockaddr_len(msg->msg_hdr.msg_name), src_link->status.dst_ipaddr, KNET_MAX_HOST_LEN, src_link->status.dst_port, KNET_MAX_PORT_LEN) != 0) { log_debug(knet_h, KNET_SUB_RX, "Unable to resolve ???"); snprintf(src_link->status.dst_ipaddr, KNET_MAX_HOST_LEN - 1, "Unknown!!!"); snprintf(src_link->status.dst_port, KNET_MAX_PORT_LEN - 1, "??"); } else { log_info(knet_h, KNET_SUB_RX, "host: %u link: %u new connection established from: %s %s", src_host->host_id, src_link->link_id, src_link->status.dst_ipaddr, src_link->status.dst_port); } } /* * transport has already accepted the connection here * otherwise we would not be receiving packets */ knet_h->transport_ops[src_link->transport_type]->transport_link_dyn_connect(knet_h, sockfd, src_link); } } switch (inbuf->kh_type) { case KNET_HEADER_TYPE_HOST_INFO: case KNET_HEADER_TYPE_DATA: /* * TODO: should we accept data even if we can't reply to the other node? * how would that work with SCTP and guaranteed delivery? */ if (!src_host->status.reachable) { log_debug(knet_h, KNET_SUB_RX, "Source host %u not reachable yet", src_host->host_id); //return; } inbuf->khp_data_seq_num = ntohs(inbuf->khp_data_seq_num); channel = inbuf->khp_data_channel; src_host->got_data = 1; if (!_seq_num_lookup(src_host, inbuf->khp_data_seq_num, 0, 0)) { if (src_host->link_handler_policy != KNET_LINK_POLICY_ACTIVE) { log_debug(knet_h, KNET_SUB_RX, "Packet has already been delivered"); } return; } if (inbuf->khp_data_frag_num > 1) { /* * len as received from the socket also includes extra stuff * that the defrag code doesn't care about. So strip it * here and readd only for repadding once we are done * defragging */ len = len - KNET_HEADER_DATA_SIZE; if (pckt_defrag(knet_h, inbuf, &len)) { return; } len = len + KNET_HEADER_DATA_SIZE; } if (inbuf->kh_type == KNET_HEADER_TYPE_DATA) { if (knet_h->enabled != 1) /* data forward is disabled */ break; if (knet_h->dst_host_filter_fn) { int host_idx; int found = 0; bcast = knet_h->dst_host_filter_fn( knet_h->dst_host_filter_fn_private_data, (const unsigned char *)inbuf->khp_data_userdata, len - KNET_HEADER_DATA_SIZE, KNET_NOTIFY_RX, knet_h->host_id, inbuf->kh_node, &channel, dst_host_ids, &dst_host_ids_entries); if (bcast < 0) { log_debug(knet_h, KNET_SUB_RX, "Error from dst_host_filter_fn: %d", bcast); return; } if ((!bcast) && (!dst_host_ids_entries)) { log_debug(knet_h, KNET_SUB_RX, "Message is unicast but no dst_host_ids_entries"); return; } /* check if we are dst for this packet */ if (!bcast) { for (host_idx = 0; host_idx < dst_host_ids_entries; host_idx++) { if (dst_host_ids[host_idx] == knet_h->host_id) { found = 1; break; } } if (!found) { log_debug(knet_h, KNET_SUB_RX, "Packet is not for us"); return; } } } } if (inbuf->kh_type == KNET_HEADER_TYPE_DATA) { if (!knet_h->sockfd[channel].in_use) { log_debug(knet_h, KNET_SUB_RX, "received packet for channel %d but there is no local sock connected", channel); return; } memset(iov_out, 0, sizeof(iov_out)); iov_out[0].iov_base = (void *) inbuf->khp_data_userdata; iov_out[0].iov_len = len - KNET_HEADER_DATA_SIZE; outlen = writev(knet_h->sockfd[channel].sockfd[knet_h->sockfd[channel].is_created], iov_out, 1); if (outlen <= 0) { knet_h->sock_notify_fn(knet_h->sock_notify_fn_private_data, knet_h->sockfd[channel].sockfd[0], channel, KNET_NOTIFY_RX, outlen, errno); return; } if (outlen == iov_out[0].iov_len) { _seq_num_set(src_host, inbuf->khp_data_seq_num, 0); } } else { /* HOSTINFO */ knet_hostinfo = (struct knet_hostinfo *)inbuf->khp_data_userdata; if (knet_hostinfo->khi_bcast == KNET_HOSTINFO_UCAST) { bcast = 0; + knet_hostinfo->khi_dst_node_id = ntohs(knet_hostinfo->khi_dst_node_id); } if (!_seq_num_lookup(src_host, inbuf->khp_data_seq_num, 0, 0)) { return; } _seq_num_set(src_host, inbuf->khp_data_seq_num, 0); switch(knet_hostinfo->khi_type) { case KNET_HOSTINFO_TYPE_LINK_UP_DOWN: break; case KNET_HOSTINFO_TYPE_LINK_TABLE: break; default: log_warn(knet_h, KNET_SUB_RX, "Receiving unknown host info message from host %u", src_host->host_id); break; } } break; case KNET_HEADER_TYPE_PING: outlen = KNET_HEADER_PING_SIZE; inbuf->kh_type = KNET_HEADER_TYPE_PONG; - inbuf->kh_node = knet_h->host_id; + inbuf->kh_node = htons(knet_h->host_id); recv_seq_num = ntohs(inbuf->khp_ping_seq_num); wipe_bufs = 0; if (!inbuf->khp_ping_timed) { /* * we might be receiving this message from all links, but we want * to process it only the first time */ if (recv_seq_num != src_host->untimed_rx_seq_num) { /* * cache the untimed seq num */ src_host->untimed_rx_seq_num = recv_seq_num; /* * if the host has received data in between * untimed ping, then we don't need to wipe the bufs */ if (src_host->got_data) { src_host->got_data = 0; wipe_bufs = 0; } else { wipe_bufs = 1; } } _seq_num_lookup(src_host, recv_seq_num, 0, wipe_bufs); } else { /* * pings always arrives in bursts over all the link * catch the first of them to cache the seq num and * avoid duplicate processing */ if (recv_seq_num != src_host->timed_rx_seq_num) { src_host->timed_rx_seq_num = recv_seq_num; if (recv_seq_num == 0) { _seq_num_lookup(src_host, recv_seq_num, 0, 1); } } } if (knet_h->crypto_instance) { if (crypto_encrypt_and_sign(knet_h, (const unsigned char *)inbuf, len, knet_h->recv_from_links_buf_crypt, &outlen) < 0) { log_debug(knet_h, KNET_SUB_RX, "Unable to encrypt pong packet"); break; } outbuf = knet_h->recv_from_links_buf_crypt; } retry_pong: len = sendto(src_link->outsock, outbuf, outlen, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) &src_link->dst_addr, sizeof(struct sockaddr_storage)); savederrno = errno; if (len != outlen) { err = knet_h->transport_ops[src_link->transport_type]->transport_tx_sock_error(knet_h, src_link->outsock, len, savederrno); switch(err) { case -1: /* unrecoverable error */ log_debug(knet_h, KNET_SUB_RX, "Unable to send pong reply (sock: %d) packet (sendto): %d %s. recorded src ip: %s src port: %s dst ip: %s dst port: %s", src_link->outsock, errno, strerror(errno), src_link->status.src_ipaddr, src_link->status.src_port, src_link->status.dst_ipaddr, src_link->status.dst_port); break; case 0: /* ignore error and continue */ break; case 1: /* retry to send those same data */ goto retry_pong; break; } } break; case KNET_HEADER_TYPE_PONG: clock_gettime(CLOCK_MONOTONIC, &src_link->status.pong_last); memmove(&recvtime, &inbuf->khp_ping_time[0], sizeof(struct timespec)); timespec_diff(recvtime, src_link->status.pong_last, &latency_last); src_link->status.latency = ((src_link->status.latency * src_link->latency_exp) + ((latency_last / 1000llu) * (src_link->latency_fix - src_link->latency_exp))) / src_link->latency_fix; if (src_link->status.latency < src_link->pong_timeout) { if (!src_link->status.connected) { if (src_link->received_pong >= src_link->pong_count) { log_info(knet_h, KNET_SUB_RX, "host: %u link: %u is up", src_host->host_id, src_link->link_id); _link_updown(knet_h, src_host->host_id, src_link->link_id, src_link->status.enabled, 1); } else { src_link->received_pong++; log_debug(knet_h, KNET_SUB_RX, "host: %u link: %u received pong: %u", src_host->host_id, src_link->link_id, src_link->received_pong); } } } break; case KNET_HEADER_TYPE_PMTUD: outlen = KNET_HEADER_PMTUD_SIZE; inbuf->kh_type = KNET_HEADER_TYPE_PMTUD_REPLY; - inbuf->kh_node = knet_h->host_id; + inbuf->kh_node = htons(knet_h->host_id); if (knet_h->crypto_instance) { if (crypto_encrypt_and_sign(knet_h, (const unsigned char *)inbuf, len, knet_h->recv_from_links_buf_crypt, &outlen) < 0) { log_debug(knet_h, KNET_SUB_RX, "Unable to encrypt PMTUd reply packet"); break; } outbuf = knet_h->recv_from_links_buf_crypt; } retry_pmtud: len = sendto(src_link->outsock, outbuf, outlen, MSG_DONTWAIT | MSG_NOSIGNAL, (struct sockaddr *) &src_link->dst_addr, sizeof(struct sockaddr_storage)); if (len != outlen) { err = knet_h->transport_ops[src_link->transport_type]->transport_tx_sock_error(knet_h, src_link->outsock, len, savederrno); switch(err) { case -1: /* unrecoverable error */ log_debug(knet_h, KNET_SUB_RX, "Unable to send PMTUd reply (sock: %d) packet (sendto): %d %s. recorded src ip: %s src port: %s dst ip: %s dst port: %s", src_link->outsock, errno, strerror(errno), src_link->status.src_ipaddr, src_link->status.src_port, src_link->status.dst_ipaddr, src_link->status.dst_port); break; case 0: /* ignore error and continue */ break; case 1: /* retry to send those same data */ goto retry_pmtud; break; } } break; case KNET_HEADER_TYPE_PMTUD_REPLY: if (pthread_mutex_lock(&knet_h->pmtud_mutex) != 0) { log_debug(knet_h, KNET_SUB_RX, "Unable to get mutex lock"); break; } src_link->last_recv_mtu = inbuf->khp_pmtud_size; pthread_cond_signal(&knet_h->pmtud_cond); pthread_mutex_unlock(&knet_h->pmtud_mutex); break; default: return; } } static void _handle_recv_from_links(knet_handle_t knet_h, int sockfd, struct mmsghdr *msg) { int err, savederrno; int i, msg_recv, transport; if (pthread_rwlock_rdlock(&knet_h->global_rwlock) != 0) { log_debug(knet_h, KNET_SUB_RX, "Unable to get global read lock"); return; } if (_is_valid_fd(knet_h, sockfd) < 1) { /* * this is normal if a fd got an event and before we grab the read lock * and the link is removed by another thread */ goto exit_unlock; } transport = knet_h->knet_transport_fd_tracker[sockfd].transport; /* * reset msg_namelen to buffer size because after recvmmsg * each msg_namelen will contain sizeof sockaddr_in or sockaddr_in6 */ for (i = 0; i < PCKT_FRAG_MAX; i++) { msg[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); } msg_recv = recvmmsg(sockfd, msg, PCKT_FRAG_MAX, MSG_DONTWAIT | MSG_NOSIGNAL, NULL); savederrno = errno; /* * WARNING: man page for recvmmsg is wrong. Kernel implementation here: * recvmmsg can return: * -1 on error * 0 if the previous run of recvmmsg recorded an error on the socket * N number of messages (see exception below). * * If there is an error from recvmsg after receiving a frame or more, the recvmmsg * loop is interrupted, error recorded in the socket (getsockopt(SO_ERROR) and * it will be visibile in the next run. * * Need to be careful how we handle errors at this stage. * * error messages need to be handled on a per transport/protocol base * at this point we have different layers of error handling * - msg_recv < 0 -> error from this run * msg_recv = 0 -> error from previous run and error on socket needs to be cleared * - per-transport message data * example: msg[i].msg_hdr.msg_flags & MSG_NOTIFICATION or msg_len for SCTP == EOF, * but for UDP it is perfectly legal to receive a 0 bytes message.. go figure * - NOTE: on SCTP MSG_NOTIFICATION we get msg_recv == PCKT_FRAG_MAX messages and no * errno set. That means the error api needs to be able to abort the loop below. */ if (msg_recv <= 0) { knet_h->transport_ops[transport]->transport_rx_sock_error(knet_h, sockfd, msg_recv, savederrno); goto exit_unlock; } for (i = 0; i < msg_recv; i++) { err = knet_h->transport_ops[transport]->transport_rx_is_data(knet_h, sockfd, &msg[i]); /* * TODO: make this section silent once we are confident * all protocols packet handlers are good */ switch(err) { case -1: /* on error */ log_debug(knet_h, KNET_SUB_RX, "Transport reported error parsing packet"); goto exit_unlock; break; case 0: /* packet is not data and we should continue the packet process loop */ log_debug(knet_h, KNET_SUB_RX, "Transport reported no data, continue"); break; case 1: /* packet is not data and we should STOP the packet process loop */ log_debug(knet_h, KNET_SUB_RX, "Transport reported no data, stop"); goto exit_unlock; break; case 2: /* packet is data and should be parsed as such */ _parse_recv_from_links(knet_h, sockfd, &msg[i]); break; } } exit_unlock: pthread_rwlock_unlock(&knet_h->global_rwlock); } void *_handle_recv_from_links_thread(void *data) { int i, nev; knet_handle_t knet_h = (knet_handle_t) data; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; struct sockaddr_storage address[PCKT_FRAG_MAX]; struct mmsghdr msg[PCKT_FRAG_MAX]; struct iovec iov_in[PCKT_FRAG_MAX]; memset(&msg, 0, sizeof(msg)); for (i = 0; i < PCKT_FRAG_MAX; i++) { iov_in[i].iov_base = (void *)knet_h->recv_from_links_buf[i]; iov_in[i].iov_len = KNET_DATABUFSIZE; memset(&msg[i].msg_hdr, 0, sizeof(struct msghdr)); msg[i].msg_hdr.msg_name = &address[i]; msg[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); msg[i].msg_hdr.msg_iov = &iov_in[i]; msg[i].msg_hdr.msg_iovlen = 1; } while (!shutdown_in_progress(knet_h)) { nev = epoll_wait(knet_h->recv_from_links_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1); for (i = 0; i < nev; i++) { _handle_recv_from_links(knet_h, events[i].data.fd, msg); } } return NULL; } diff --git a/libknet/threads_tx.c b/libknet/threads_tx.c index 2eb2df0a..147618d5 100644 --- a/libknet/threads_tx.c +++ b/libknet/threads_tx.c @@ -1,608 +1,609 @@ /* * Copyright (C) 2010-2017 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 "compat.h" #include "crypto.h" #include "host.h" #include "link.h" #include "logging.h" #include "transports.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 mmsghdr *msg, int msgs_to_send) { int link_idx, msg_idx, sent_msgs, prev_sent, progress; int err = 0, savederrno = 0; struct mmsghdr *cur; for (link_idx = 0; link_idx < dst_host->active_link_entries; link_idx++) { sent_msgs = 0; prev_sent = 0; progress = 1; msg_idx = 0; while (msg_idx < msgs_to_send) { msg[msg_idx].msg_hdr.msg_name = &dst_host->link[dst_host->active_links[link_idx]].dst_addr; msg_idx++; } retry: cur = &msg[prev_sent]; sent_msgs = sendmmsg(dst_host->link[dst_host->active_links[link_idx]].outsock, cur, msgs_to_send - prev_sent, MSG_DONTWAIT | MSG_NOSIGNAL); savederrno = errno; err = knet_h->transport_ops[dst_host->link[dst_host->active_links[link_idx]].transport_type]->transport_tx_sock_error(knet_h, dst_host->link[dst_host->active_links[link_idx]].outsock, sent_msgs, savederrno); switch(err) { case -1: /* unrecoverable error */ goto out_unlock; break; case 0: /* ignore error and continue */ break; case 1: /* retry to send those same data */ 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, int buf_idx, ssize_t inlen, int8_t channel, int is_sync) { ssize_t outlen, frag_len; struct knet_host *dst_host; - uint8_t dst_host_ids_temp[KNET_MAX_HOST]; + knet_node_id_t dst_host_ids_temp[KNET_MAX_HOST]; size_t dst_host_ids_entries_temp = 0; - uint8_t dst_host_ids[KNET_MAX_HOST]; + 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]; uint8_t frag_idx; unsigned int temp_data_mtu; int host_idx; int send_mcast = 0; struct knet_header *inbuf; int savederrno = 0; int err = 0; seq_num_t tx_seq_num; struct mmsghdr msg[PCKT_FRAG_MAX]; int msgs_to_send, msg_idx; inbuf = knet_h->recv_from_sock_buf[buf_idx]; 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; } } 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 unrechable 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->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->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 mininum 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; } /* * 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 (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].iov_base = (void *)knet_h->send_to_links_buf[frag_idx]; /* * set the len */ if (frag_len > temp_data_mtu) { iov_out[frag_idx].iov_len = temp_data_mtu + KNET_HEADER_DATA_SIZE; } else { iov_out[frag_idx].iov_len = frag_len + KNET_HEADER_DATA_SIZE; } /* * 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; memmove(knet_h->send_to_links_buf[frag_idx]->khp_data_userdata, inbuf->khp_data_userdata + (temp_data_mtu * frag_idx), iov_out[frag_idx].iov_len - KNET_HEADER_DATA_SIZE); frag_len = frag_len - temp_data_mtu; frag_idx++; } } else { iov_out[frag_idx].iov_base = (void *)inbuf; iov_out[frag_idx].iov_len = frag_len + KNET_HEADER_DATA_SIZE; } if (knet_h->crypto_instance) { frag_idx = 0; while (frag_idx < inbuf->khp_data_frag_num) { if (crypto_encrypt_and_sign( knet_h, (const unsigned char *)iov_out[frag_idx].iov_base, iov_out[frag_idx].iov_len, knet_h->send_to_links_buf_crypt[frag_idx], &outlen) < 0) { log_debug(knet_h, KNET_SUB_TX, "Unable to encrypt packet"); savederrno = ECHILD; err = -1; goto out_unlock; } iov_out[frag_idx].iov_base = knet_h->send_to_links_buf_crypt[frag_idx]; iov_out[frag_idx].iov_len = outlen; frag_idx++; } } 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]; msg[msg_idx].msg_hdr.msg_iovlen = 1; 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[0]->kh_type = KNET_HEADER_TYPE_DATA; memmove(knet_h->recv_from_sock_buf[0]->khp_data_userdata, buff, buff_len); err = _parse_recv_from_sock(knet_h, 0, 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, int sockfd, int8_t channel, struct mmsghdr *msg, int type) { ssize_t inlen = 0; struct iovec iov_in; int msg_recv, i; int savederrno = 0, docallback = 0; if ((channel >= 0) && (channel < KNET_DATAFD_MAX) && (!knet_h->sockfd[channel].is_socket)) { memset(&iov_in, 0, sizeof(iov_in)); iov_in.iov_base = (void *)knet_h->recv_from_sock_buf[0]->khp_data_userdata; iov_in.iov_len = KNET_MAX_PACKET_SIZE; inlen = readv(sockfd, &iov_in, 1); if (inlen <= 0) { savederrno = errno; docallback = 1; goto out; } msg_recv = 1; knet_h->recv_from_sock_buf[0]->kh_type = type; _parse_recv_from_sock(knet_h, 0, inlen, channel, 0); } else { msg_recv = recvmmsg(sockfd, msg, PCKT_FRAG_MAX, MSG_DONTWAIT | MSG_NOSIGNAL, NULL); if (msg_recv < 0) { inlen = msg_recv; savederrno = errno; docallback = 1; goto out; } for (i = 0; i < msg_recv; i++) { inlen = msg[i].msg_len; if (inlen == 0) { savederrno = 0; docallback = 1; goto out; break; } knet_h->recv_from_sock_buf[i]->kh_type = type; _parse_recv_from_sock(knet_h, i, inlen, channel, 0); } } out: if (inlen < 0) { struct epoll_event ev; 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; } } 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]; struct sockaddr_storage address[PCKT_FRAG_MAX]; struct mmsghdr msg[PCKT_FRAG_MAX]; struct iovec iov_in[PCKT_FRAG_MAX]; int i, nev, type; int8_t channel; memset(&msg, 0, sizeof(struct mmsghdr)); /* preparing data buffer */ for (i = 0; i < PCKT_FRAG_MAX; i++) { iov_in[i].iov_base = (void *)knet_h->recv_from_sock_buf[i]->khp_data_userdata; iov_in[i].iov_len = KNET_MAX_PACKET_SIZE; memset(&msg[i].msg_hdr, 0, sizeof(struct msghdr)); msg[i].msg_hdr.msg_name = &address[i]; msg[i].msg_hdr.msg_namelen = sizeof(struct sockaddr_storage); msg[i].msg_hdr.msg_iov = &iov_in[i]; msg[i].msg_hdr.msg_iovlen = 1; knet_h->recv_from_sock_buf[i]->kh_version = KNET_HEADER_VERSION; knet_h->recv_from_sock_buf[i]->khp_data_frag_seq = 0; - knet_h->recv_from_sock_buf[i]->kh_node = knet_h->host_id; + knet_h->recv_from_sock_buf[i]->kh_node = htons(knet_h->host_id); 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 = knet_h->host_id; + 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, -1); 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 (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, events[i].data.fd, channel, &msg[0], type); pthread_mutex_unlock(&knet_h->tx_mutex); } pthread_rwlock_unlock(&knet_h->global_rwlock); } return NULL; }