diff --git a/libknet/libknet.h b/libknet/libknet.h index 7beb2537..a930d597 100644 --- a/libknet/libknet.h +++ b/libknet/libknet.h @@ -1,1344 +1,1344 @@ /* * 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 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 64 #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(uint16_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) * uint16_t this_host_id - host_id processing the packet * uint16_t src_host_id - host_id that generated the * packet * uint16_t *dst_host_ids - array of KNET_MAX_HOST uint16_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, uint16_t this_host_id, uint16_t src_host_id, int8_t *channel, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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. * * - */ #define KNET_TRANSPORT_UDP 0 #define KNET_TRANSPORT_SCTP 1 #define KNET_MAX_TRANSPORTS 2 /* * 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, uint16_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, uint16_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_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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, uint16_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_SCTP_LINK_T 41 /* SCTP 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 ping_test.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/logging.c b/libknet/logging.c index 8630322d..70341d8c 100644 --- a/libknet/logging.c +++ b/libknet/logging.c @@ -1,246 +1,246 @@ /* * 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 #include #include #include #include "internals.h" #include "logging.h" struct pretty_names { const char *name; uint8_t val; }; static struct pretty_names subsystem_names[] = { { "common", KNET_SUB_COMMON }, { "handle", KNET_SUB_HANDLE }, { "host", KNET_SUB_HOST }, { "listener", KNET_SUB_LISTENER }, { "link", KNET_SUB_LINK }, { "transport", KNET_SUB_TRANSPORT }, { "crypto", KNET_SUB_CRYPTO }, { "filter", KNET_SUB_FILTER }, { "dstcache", KNET_SUB_DSTCACHE }, { "heartbeat", KNET_SUB_HEARTBEAT }, { "pmtud", KNET_SUB_PMTUD }, { "tx", KNET_SUB_TX }, { "rx", KNET_SUB_RX }, { "udp", KNET_SUB_TRANSP_UDP }, - { "sctp_t", KNET_SUB_SCTP_LINK_T }, + { "sctp", KNET_SUB_TRANSP_SCTP }, { "nsscrypto", KNET_SUB_NSSCRYPTO }, { "unknown", KNET_SUB_UNKNOWN } /* unknown MUST always be last in this array */ }; const char *knet_log_get_subsystem_name(uint8_t subsystem) { unsigned int i; for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) { if (subsystem_names[i].val == KNET_SUB_UNKNOWN) { break; } if (subsystem_names[i].val == subsystem) { return subsystem_names[i].name; } } return "unknown"; } uint8_t knet_log_get_subsystem_id(const char *name) { unsigned int i; for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) { if (subsystem_names[i].val == KNET_SUB_UNKNOWN) { break; } if (strcasecmp(name, subsystem_names[i].name) == 0) { return subsystem_names[i].val; } } return KNET_SUB_UNKNOWN; } static int is_valid_subsystem(uint8_t subsystem) { unsigned int i; for (i = 0; i < KNET_MAX_SUBSYSTEMS; i++) { if ((subsystem != KNET_SUB_UNKNOWN) && (subsystem_names[i].val == KNET_SUB_UNKNOWN)) { break; } if (subsystem_names[i].val == subsystem) { return 0; } } return -1; } static struct pretty_names loglevel_names[] = { { "ERROR", KNET_LOG_ERR }, { "WARNING", KNET_LOG_WARN }, { "info", KNET_LOG_INFO }, { "debug", KNET_LOG_DEBUG } }; const char *knet_log_get_loglevel_name(uint8_t level) { unsigned int i; for (i = 0; i <= KNET_LOG_DEBUG; i++) { if (loglevel_names[i].val == level) { return loglevel_names[i].name; } } return "ERROR"; } uint8_t knet_log_get_loglevel_id(const char *name) { unsigned int i; for (i = 0; i <= KNET_LOG_DEBUG; i++) { if (strcasecmp(name, loglevel_names[i].name) == 0) { return loglevel_names[i].val; } } return KNET_LOG_ERR; } int knet_log_set_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t level) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (is_valid_subsystem(subsystem) < 0) { errno = EINVAL; return -1; } if (level > KNET_LOG_DEBUG) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_wrlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, subsystem, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } knet_h->log_levels[subsystem] = level; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } int knet_log_get_loglevel(knet_handle_t knet_h, uint8_t subsystem, uint8_t *level) { int savederrno = 0; if (!knet_h) { errno = EINVAL; return -1; } if (is_valid_subsystem(subsystem) < 0) { errno = EINVAL; return -1; } if (!level) { errno = EINVAL; return -1; } savederrno = pthread_rwlock_rdlock(&knet_h->global_rwlock); if (savederrno) { log_err(knet_h, subsystem, "Unable to get write lock: %s", strerror(savederrno)); errno = savederrno; return -1; } *level = knet_h->log_levels[subsystem]; pthread_rwlock_unlock(&knet_h->global_rwlock); return 0; } void log_msg(knet_handle_t knet_h, uint8_t subsystem, uint8_t msglevel, const char *fmt, ...) { va_list ap; struct knet_log_msg msg; size_t byte_cnt = 0; int len, err; if ((!knet_h) || (subsystem == KNET_MAX_SUBSYSTEMS) || (msglevel > knet_h->log_levels[subsystem])) return; /* * most logging calls will take place with locking in place. * if we get an EINVAL and locking is initialized, then * we are getting a real error and we need to stop */ err = pthread_rwlock_tryrdlock(&knet_h->global_rwlock); if ((err == EAGAIN) && (knet_h->lock_init_done)) return; if (knet_h->logfd <= 0) goto out_unlock; memset(&msg, 0, sizeof(struct knet_log_msg)); msg.subsystem = subsystem; msg.msglevel = msglevel; va_start(ap, fmt); vsnprintf(msg.msg, sizeof(msg.msg) - 2, fmt, ap); va_end(ap); len = strlen(msg.msg); msg.msg[len+1] = '\n'; while (byte_cnt < sizeof(struct knet_log_msg)) { len = write(knet_h->logfd, &msg, sizeof(struct knet_log_msg) - byte_cnt); if (len <= 0) return; byte_cnt += len; } out_unlock: /* * unlock only if we are holding the lock */ if (!err) pthread_rwlock_unlock(&knet_h->global_rwlock); return; } diff --git a/libknet/transport_sctp.c b/libknet/transport_sctp.c index ba24487f..79ae75fe 100644 --- a/libknet/transport_sctp.c +++ b/libknet/transport_sctp.c @@ -1,706 +1,706 @@ #include "config.h" #include #include #include #include #include #include #include #include #include "host.h" #include "link.h" #include "logging.h" #include "common.h" #include "transports.h" #ifdef HAVE_NETINET_SCTP_H #include /* * https://en.wikipedia.org/wiki/SCTP_packet_structure */ #define KNET_PMTUD_SCTP_OVERHEAD_COMMON 12 #define KNET_PMTUD_SCTP_OVERHEAD_DATA_CHUNK 16 #define KNET_PMTUD_SCTP_OVERHEAD KNET_PMTUD_SCTP_OVERHEAD_COMMON + KNET_PMTUD_SCTP_OVERHEAD_DATA_CHUNK /* Time to sleep before reconnection attempts. in microseconds */ #define KNET_SCTP_SLEEP_TIME 1000000 #define MAX_ACCEPTED_SOCKS 256 typedef struct sctp_handle_info { knet_handle_t knet_handle; int connect_epollfd; int listen_epollfd; pthread_t connect_thread; pthread_t listen_thread; pthread_rwlock_t links_list_lock; struct knet_list_head links_list; } sctp_handle_info_t; typedef struct sctp_link_info { knet_transport_t transport; knet_handle_t knet_handle; struct knet_link *link; int sendrecv_sock; int listen_sock; int accepted_socks[MAX_ACCEPTED_SOCKS]; struct sockaddr_storage dst_address; struct knet_list_head list; int on_epoll; } sctp_link_info_t; static int _configure_sctp_socket(knet_handle_t knet_h, int sock, struct sockaddr_storage *address, const char *type) { int err = 0; int value; int savederrno; struct sctp_event_subscribe events; if (_configure_transport_socket(knet_h, sock, address, type) < 0) { err = -1; goto exit_error; } value = 1; if (setsockopt(sock, SOL_SCTP, SCTP_NODELAY, &value, sizeof(value)) < 0) { savederrno = errno; err = -1; log_err(knet_h, KNET_SUB_TRANSPORT, "Unable to set sctp nodelay: %s", strerror(savederrno)); goto exit_error; } /* Events we want notifications for */ memset(&events, 0, sizeof (events)); events.sctp_data_io_event = 1; events.sctp_association_event = 1; events.sctp_send_failure_event = 1; events.sctp_address_event = 1; events.sctp_peer_error_event = 1; events.sctp_shutdown_event = 1; if (setsockopt(sock, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof (events)) < 0) { savederrno = errno; err = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to enable %s events: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to enable %s events: %s", type, strerror(savederrno)); goto exit_error; } err = 0; exit_error: return err; } /* Listener received a new connection */ static void _handle_incoming_sctp(sctp_handle_info_t *handle_info, sctp_link_info_t *info) { knet_handle_t knet_h = handle_info->knet_handle; int new_fd; int i; struct epoll_event ev; struct sockaddr_storage ss; socklen_t sock_len = sizeof(ss); new_fd = accept(info->listen_sock, (struct sockaddr *)&ss, &sock_len); if (new_fd < 0) { - log_warn(knet_h, KNET_SUB_SCTP_LINK_T, "Incoming: accept error: %s", strerror(errno)); + log_warn(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accept error: %s", strerror(errno)); return; } if (_fdset_cloexec(new_fd)) { - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "Incoming: unable to set cloexec opts: %s", strerror(errno)); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to set cloexec opts: %s", strerror(errno)); return; } /* Keep a track of all accepted FDs */ for (i=0; iaccepted_socks[i] == -1) { info->accepted_socks[i] = new_fd; break; } } if (i == MAX_ACCEPTED_SOCKS) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Incoming: too many connections!"); + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: too many connections!"); close(new_fd); return; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.fd = new_fd; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, new_fd, &ev)) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Incoming: unable to add accepted socket %d to epoll pool: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: unable to add accepted socket %d to epoll pool: %s", new_fd, strerror(errno)); info->accepted_socks[i] = -1; close(new_fd); } else { char *print_str[2]; _transport_addrtostr((struct sockaddr *)&ss, sizeof(ss), print_str); - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "Incoming: accepted new fd %d for %s (listen fd: %d). index: %d", new_fd, print_str[0], info->listen_sock, i); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Incoming: accepted new fd %d for %s (listen fd: %d). index: %d", new_fd, print_str[0], info->listen_sock, i); _transport_addrtostr_free(print_str); } } static int _create_connect_socket(knet_handle_t knet_h, sctp_handle_info_t *handle_info, sctp_link_info_t *info, int do_close) { int sendrecv_sock; int savederrno = EINVAL; struct epoll_event ev; char *print_str[2]; memset(&ev, 0, sizeof(struct epoll_event)); if (do_close || info->sendrecv_sock != -1) { if (info->on_epoll) { ev.events = EPOLLOUT; ev.data.ptr = info; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, info->sendrecv_sock, &ev)) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to remove connected socket from the epoll pool: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from the epoll pool: %s", strerror(errno)); } } close(info->sendrecv_sock); info->on_epoll = 0; sendrecv_sock = socket(info->dst_address.ss_family, SOCK_STREAM, IPPROTO_SCTP); if (sendrecv_sock < 0) { savederrno = errno; sendrecv_sock = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to create send/recv socket: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create send/recv socket: %s", strerror(savederrno)); goto exit_error; } if (_configure_sctp_socket(knet_h, sendrecv_sock, &info->dst_address, "send/recv") < 0) { /* Error already reported */ goto exit_error; } } else { sendrecv_sock = info->sendrecv_sock; } if (connect(sendrecv_sock, (struct sockaddr *)&info->dst_address, sizeof(struct sockaddr_storage)) < 0) { if (errno != EINPROGRESS && errno != EISCONN) { savederrno = errno; sendrecv_sock = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to connect SCTP socket: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to connect SCTP socket: %s", strerror(savederrno)); goto exit_error; } } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLOUT; ev.data.ptr = info; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_ADD, sendrecv_sock, &ev)) { savederrno = errno; sendrecv_sock = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to add send/recv to epoll pool: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add send/recv to epoll pool: %s", strerror(savederrno)); goto exit_error; } info->on_epoll = 1; _transport_addrtostr((struct sockaddr *)&info->dst_address, sizeof(struct sockaddr_storage), print_str); - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "New connect attempt to %s on fd %d", print_str[0], sendrecv_sock); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "New connect attempt to %s on fd %d", print_str[0], sendrecv_sock); _transport_addrtostr_free(print_str); exit_error: return sendrecv_sock; } /* Connect completed or failed */ static void _handle_connected_sctp(sctp_handle_info_t *handle_info, sctp_link_info_t *info) { knet_handle_t knet_h = handle_info->knet_handle; struct epoll_event ev; int err; char *print_str[2]; unsigned int status, len = sizeof(status); int fd = info->sendrecv_sock; err = getsockopt(fd, SOL_SOCKET, SO_ERROR, &status, &len); if (err || status) { if (err) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP getsockopt() on connecting socket %d failed: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP getsockopt() on connecting socket %d failed: %s", fd, strerror(errno)); } else { _transport_addrtostr((struct sockaddr *)&info->dst_address, sizeof(struct sockaddr_storage), print_str); - log_info(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP connect on %d to %s failed: %s", + log_info(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect on %d to %s failed: %s", fd, print_str[0], strerror(status)); _transport_addrtostr_free(print_str); /* Retry connect */ usleep(KNET_SCTP_SLEEP_TIME); /* No need to create a new socket if connect failed, * just retry connect */ info->sendrecv_sock = _create_connect_socket(knet_h, handle_info, info, 0); } return; } /* Connected - Remove us from the connect epoll */ ev.events = EPOLLOUT; ev.data.ptr = info; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, fd, &ev)) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to remove connected socket %d from epoll pool: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket %d from epoll pool: %s", fd, strerror(errno)); } info->on_epoll = 0; ev.events = EPOLLIN; ev.data.fd = fd; if (epoll_ctl(knet_h->recv_from_links_epollfd, EPOLL_CTL_ADD, fd, &ev)) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to add connected socket to epoll pool: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add connected socket to epoll pool: %s", strerror(errno)); } _transport_addrtostr((struct sockaddr *)&info->dst_address, sizeof(struct sockaddr_storage), print_str); - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP handler fd %d now connected to %s", fd, print_str[0]); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP handler fd %d now connected to %s", fd, print_str[0]); _transport_addrtostr_free(print_str); } static void *_sctp_listen_thread(void *data) { int i, nev; sctp_handle_info_t *handle_info = (sctp_handle_info_t*) data; knet_handle_t knet_h = handle_info->knet_handle; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; while (!knet_h->fini_in_progress) { nev = epoll_wait(handle_info->listen_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1); if (knet_h->fini_in_progress) { break; } if (nev < 0) { - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP listen handler EPOLL ERROR: %s", strerror(errno)); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP listen handler EPOLL ERROR: %s", strerror(errno)); continue; } /* Sort out which FD has an incoming connection */ for (i = 0; i < nev; i++) { _handle_incoming_sctp(handle_info, events[i].data.ptr); } } return NULL; } static void *_sctp_connect_thread(void *data) { int i, nev; sctp_handle_info_t *handle_info = (sctp_handle_info_t*) data; knet_handle_t knet_h = handle_info->knet_handle; struct epoll_event events[KNET_EPOLL_MAX_EVENTS]; while (!knet_h->fini_in_progress) { nev = epoll_wait(handle_info->connect_epollfd, events, KNET_EPOLL_MAX_EVENTS, -1); if (knet_h->fini_in_progress) { break; } if (nev < 0) { - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP connect handler EPOLL ERROR: %s", strerror(errno)); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP connect handler EPOLL ERROR: %s", strerror(errno)); continue; } /* Sort out which FD has a connection */ for (i = 0; i < nev; i++) { _handle_connected_sctp(handle_info, events[i].data.ptr); } } return NULL; } /* * EOF on the socket, find the link and set it waiting for connect() again * Returns -1 if the fd is not known to us. * The fd is already removed from the main epoll by the time we get here. */ static int sctp_handle_fd_eof(knet_handle_t knet_h, int sock_fd) { sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; sctp_link_info_t *info; int ret = -1; int i; /* Not us */ if (!handle_info) { return ret; } pthread_rwlock_rdlock(&handle_info->links_list_lock); knet_list_for_each_entry(info, &handle_info->links_list, list) { if (sock_fd == info->sendrecv_sock) { pthread_rwlock_unlock(&handle_info->links_list_lock); - log_info(knet_h, KNET_SUB_SCTP_LINK_T, "Restarting connect for closed socket %d", sock_fd); + log_info(knet_h, KNET_SUB_TRANSP_SCTP, "Restarting connect for closed socket %d", sock_fd); /* Restart the connect() attempts */ info->sendrecv_sock = _create_connect_socket(knet_h, handle_info, info, 1); info->link->outsock = info->sendrecv_sock; return 0; } /* Accepted socket - just close it */ for (i=0; iaccepted_socks[i]) { - log_info(knet_h, KNET_SUB_SCTP_LINK_T, "Closing accepted socket %d", sock_fd); + log_info(knet_h, KNET_SUB_TRANSP_SCTP, "Closing accepted socket %d", sock_fd); close(sock_fd); info->accepted_socks[i] = -1; pthread_rwlock_unlock(&handle_info->links_list_lock); return 0; } } } pthread_rwlock_unlock(&handle_info->links_list_lock); - log_info(knet_h, KNET_SUB_SCTP_LINK_T, "Cannot find link_info for EOF socket %d", sock_fd); + log_info(knet_h, KNET_SUB_TRANSP_SCTP, "Cannot find link_info for EOF socket %d", sock_fd); return -1; } static int sctp_handle_allocate(knet_handle_t knet_h, knet_transport_t *transport) { sctp_handle_info_t *handle_info; int savederrno; handle_info = malloc(sizeof(sctp_handle_info_t)); if (!handle_info) { return -1; } handle_info->knet_handle = knet_h; knet_list_init(&handle_info->links_list); pthread_rwlock_init(&handle_info->links_list_lock, NULL); handle_info->listen_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); if (handle_info->listen_epollfd < 0) { savederrno = errno; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to create epoll listen fd: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll listen fd: %s", strerror(savederrno)); goto exit_fail; } handle_info->connect_epollfd = epoll_create(KNET_EPOLL_MAX_EVENTS + 1); if (handle_info->connect_epollfd < 0) { savederrno = errno; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to create epoll connect fd: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create epoll connect fd: %s", strerror(savederrno)); goto exit_fail; } /* Start connect & listener threads */ savederrno = pthread_create(&handle_info->listen_thread, NULL, _sctp_listen_thread, handle_info); if (savederrno) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to start sctp listen thread: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp listen thread: %s", strerror(savederrno)); goto exit_fail; } savederrno = pthread_create(&handle_info->connect_thread, NULL, _sctp_connect_thread, handle_info); if (savederrno) { - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to start sctp connect thread: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to start sctp connect thread: %s", strerror(savederrno)); goto exit_fail; } *transport = handle_info; return 0; exit_fail: errno = savederrno; return -1; } static int sctp_handle_free(knet_handle_t knet_h, knet_transport_t transport) { sctp_handle_info_t *handle_info; void *thread_status; if (!transport) { errno = EINVAL; return -1; } handle_info = transport; if (handle_info->listen_thread) { pthread_cancel(handle_info->listen_thread); pthread_join(handle_info->listen_thread, &thread_status); } if (handle_info->connect_thread) { pthread_cancel(handle_info->connect_thread); pthread_join(handle_info->connect_thread, &thread_status); } free(handle_info); return 0; } static int sctp_link_listener_start(knet_handle_t knet_h, knet_transport_link_t transport_link, uint8_t link_id, struct sockaddr_storage *address, struct sockaddr_storage *dst_address) { int listen_sock; int savederrno = EINVAL; struct epoll_event ev; int err; sctp_link_info_t *info; sctp_handle_info_t *handle_info; char *print_str[2]; info = (sctp_link_info_t *)transport_link; handle_info = info->transport; listen_sock = socket(address->ss_family, SOCK_STREAM, IPPROTO_SCTP); if (listen_sock < 0) { savederrno = errno; err = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to create listener socket: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to create listener socket: %s", strerror(savederrno)); goto exit_error; } if (_configure_sctp_socket(knet_h, listen_sock, address, "listener") < 0) { /* Error already reported */ goto exit_error; } if (bind(listen_sock, (struct sockaddr *)address, sizeof(struct sockaddr_storage)) < 0) { savederrno = errno; err = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to bind listener socket: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to bind listener socket: %s", strerror(savederrno)); goto exit_error; } if (listen(listen_sock, 5) < 0) { savederrno = errno; err = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to listen on listener socket: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to listen on listener socket: %s", strerror(savederrno)); goto exit_error; } memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.ptr = info; if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_ADD, listen_sock, &ev)) { savederrno = errno; err = -1; - log_err(knet_h, KNET_SUB_SCTP_LINK_T, "Unable to add listener to epoll pool: %s", + log_err(knet_h, KNET_SUB_TRANSP_SCTP, "Unable to add listener to epoll pool: %s", strerror(savederrno)); goto exit_error; } info->listen_sock = listen_sock; _transport_addrtostr((struct sockaddr *)address, sizeof(struct sockaddr_storage), print_str); - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "Listening on fd %d for %s", listen_sock, print_str[0]); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "Listening on fd %d for %s", listen_sock, print_str[0]); _transport_addrtostr_free(print_str); return 0; exit_error: errno = savederrno; return err; } static int sctp_link_allocate(knet_handle_t knet_h, knet_transport_t transport, struct knet_link *link, knet_transport_link_t *transport_link, uint8_t link_id, struct sockaddr_storage *address, struct sockaddr_storage *dst_address, int *send_sock) { int savederrno = EINVAL; int err; int i; sctp_link_info_t *info; sctp_handle_info_t *handle_info; info = malloc(sizeof(sctp_link_info_t)); if (!info) { return -1; } info->knet_handle = knet_h; memcpy(&info->dst_address, dst_address, sizeof(struct sockaddr_storage)); handle_info = transport; info->link = link; info->on_epoll = 0; info->sendrecv_sock = -1; for (i=0; i< MAX_ACCEPTED_SOCKS; i++) { info->accepted_socks[i] = -1; } info->sendrecv_sock = _create_connect_socket(knet_h, handle_info, info, 1); if (info->sendrecv_sock == -1) { free(info); err = -1; goto exit_error; } info->transport = transport; pthread_rwlock_wrlock(&handle_info->links_list_lock); knet_list_add(&info->list, &handle_info->links_list); pthread_rwlock_unlock(&handle_info->links_list_lock); *transport_link = (knet_transport_link_t *)info; *send_sock = info->sendrecv_sock; return 0; exit_error: errno = savederrno; return err; } static int sctp_link_free(knet_transport_link_t transport) { sctp_link_info_t *info = (sctp_link_info_t *)transport; sctp_handle_info_t *handle_info = info->transport; int i; struct epoll_event ev; memset(&ev, 0, sizeof(struct epoll_event)); if (info->on_epoll) { ev.events = EPOLLOUT; ev.data.ptr = info; if (epoll_ctl(handle_info->connect_epollfd, EPOLL_CTL_DEL, info->sendrecv_sock, &ev)) { - log_err(handle_info->knet_handle, KNET_SUB_SCTP_LINK_T, "Unable to remove connected socket from the epoll pool: %s", + log_err(handle_info->knet_handle, KNET_SUB_TRANSP_SCTP, "Unable to remove connected socket from the epoll pool: %s", strerror(errno)); } } ev.events = EPOLLIN; ev.data.ptr = info; if (epoll_ctl(handle_info->listen_epollfd, EPOLL_CTL_DEL, info->listen_sock, &ev)) { - log_err(handle_info->knet_handle, KNET_SUB_SCTP_LINK_T, "Unable to add listener to epoll pool: %s", + log_err(handle_info->knet_handle, KNET_SUB_TRANSP_SCTP, "Unable to add listener to epoll pool: %s", strerror(errno)); } close(info->sendrecv_sock); close(info->listen_sock); for (i=0; i< MAX_ACCEPTED_SOCKS; i++) { if (info->accepted_socks[i] > -1) { close(info->accepted_socks[i]); } } pthread_rwlock_wrlock(&handle_info->links_list_lock); knet_list_del(&info->list); pthread_rwlock_unlock(&handle_info->links_list_lock); /* Remove from epoll */ free(transport); return 0; } static int sctp_handle_fd_notification(knet_handle_t knet_h, int sockfd, struct iovec *iov, size_t iovlen) { struct sctp_assoc_change *sac; union sctp_notification *snp; sctp_link_info_t *info; sctp_handle_info_t *handle_info = knet_h->transports[KNET_TRANSPORT_SCTP]; char *print_str[2]; int i; /* Find the link associated with this fd */ pthread_rwlock_rdlock(&handle_info->links_list_lock); knet_list_for_each_entry(info, &handle_info->links_list, list) { if (sockfd == info->sendrecv_sock) { for (i=0; i< iovlen; i++) { snp = iov[i].iov_base; switch (snp->sn_header.sn_type) { case SCTP_ASSOC_CHANGE: sac = &snp->sn_assoc_change; if (sac->sac_state == SCTP_COMM_LOST) { _transport_addrtostr((struct sockaddr *)&info->dst_address, sizeof(struct sockaddr_storage), print_str); - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP shutdown, reconnecting sock %d to %s", sockfd, print_str[0]); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP shutdown, reconnecting sock %d to %s", sockfd, print_str[0]); _transport_addrtostr_free(print_str); _create_connect_socket(knet_h, handle_info, info, 1); } break; case SCTP_SEND_FAILED: break; case SCTP_PEER_ADDR_CHANGE: break; case SCTP_REMOTE_ERROR: break; case SCTP_SHUTDOWN_EVENT: _transport_addrtostr((struct sockaddr *)&info->dst_address, sizeof(struct sockaddr_storage), print_str); - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "SCTP shutdown, reconnecting sock %d to %s", sockfd, print_str[0]); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "SCTP shutdown, reconnecting sock %d to %s", sockfd, print_str[0]); _transport_addrtostr_free(print_str); _create_connect_socket(knet_h, handle_info, info, 1); break; default: - log_debug(knet_h, KNET_SUB_SCTP_LINK_T, "unknown SCTP event type: %hu\n", snp->sn_header.sn_type); + log_debug(knet_h, KNET_SUB_TRANSP_SCTP, "unknown SCTP event type: %hu\n", snp->sn_header.sn_type); break; } pthread_rwlock_unlock(&handle_info->links_list_lock); return 0; } } } pthread_rwlock_unlock(&handle_info->links_list_lock); return -1; } static int sctp_link_get_mtu_overhead(knet_transport_t transport) { return KNET_PMTUD_SCTP_OVERHEAD; } static knet_transport_ops_t sctp_transport_ops = { .handle_allocate = sctp_handle_allocate, .handle_free = sctp_handle_free, .handle_fd_eof = sctp_handle_fd_eof, .handle_fd_notification = sctp_handle_fd_notification, .link_allocate = sctp_link_allocate, .link_listener_start = sctp_link_listener_start, .link_free = sctp_link_free, .link_get_mtu_overhead = sctp_link_get_mtu_overhead, .transport_name = "SCTP", }; knet_transport_ops_t *get_sctp_transport() { return &sctp_transport_ops; } #else // HAVE_NETINET_SCTP_H knet_transport_ops_t *get_sctp_transport() { return NULL; } #endif