From 969496f15e1e0359e26c2c6e995ad4ef82720f86 Mon Sep 17 00:00:00 2001 From: Manuel Bentele Date: Fri, 16 Oct 2020 17:15:49 +0200 Subject: [BUILD] rewrite CMake build system to track changes of source files This change restructures the source code directories, separates shared form non-shared application code and adds CMake dependencies. These dependencies allow the tracking of changes and trigger a rebuild of those build targets where changed files are involved. WARNING: Note that the support of the DNBD3_SERVER_AFL build option is not supported yet. Thus, the option should be never turned on. --- inc/dnbd3/config.h | 43 ++++++++++ inc/dnbd3/config/client.h | 36 ++++++++ inc/dnbd3/config/server.h | 62 ++++++++++++++ inc/dnbd3/shared/crc32.h | 9 ++ inc/dnbd3/shared/fdsignal.h | 57 +++++++++++++ inc/dnbd3/shared/log.h | 65 +++++++++++++++ inc/dnbd3/shared/protocol.h | 156 ++++++++++++++++++++++++++++++++++ inc/dnbd3/shared/serialize.h | 40 +++++++++ inc/dnbd3/shared/sockhelper.h | 120 ++++++++++++++++++++++++++ inc/dnbd3/shared/timing.h | 162 +++++++++++++++++++++++++++++++++++ inc/dnbd3/types.h | 190 ++++++++++++++++++++++++++++++++++++++++++ inc/dnbd3/version.h.in | 9 ++ 12 files changed, 949 insertions(+) create mode 100644 inc/dnbd3/config.h create mode 100644 inc/dnbd3/config/client.h create mode 100644 inc/dnbd3/config/server.h create mode 100644 inc/dnbd3/shared/crc32.h create mode 100644 inc/dnbd3/shared/fdsignal.h create mode 100644 inc/dnbd3/shared/log.h create mode 100644 inc/dnbd3/shared/protocol.h create mode 100644 inc/dnbd3/shared/serialize.h create mode 100644 inc/dnbd3/shared/sockhelper.h create mode 100644 inc/dnbd3/shared/timing.h create mode 100644 inc/dnbd3/types.h create mode 100644 inc/dnbd3/version.h.in (limited to 'inc') diff --git a/inc/dnbd3/config.h b/inc/dnbd3/config.h new file mode 100644 index 0000000..50336af --- /dev/null +++ b/inc/dnbd3/config.h @@ -0,0 +1,43 @@ +/* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Johann Latocha + * + * This file may be licensed under the terms of of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +// +++++ Network +++++ +// Default port +#define PORT 5003 +#define RPC_PORT (PORT+1) + +// No serialized payload allowed exceeding this many bytes (so actual data from client->server is not affected by this limit!) +#define MAX_PAYLOAD 1000 + +// Protocol version should be increased whenever new features/messages are added, +// so either the client or server can run in compatibility mode, or they can +// cancel the connection right away if the protocol has changed too much +#define PROTOCOL_VERSION 3 +// 2017-10-16: Update to v3: Change header to support request hop-counting + +#define NUMBER_SERVERS 8 // Number of alt servers per image/device + +// +++++ Block Device +++++ +#define DNBD3_BLOCK_SIZE ((uint64_t)4096) // NEVER CHANGE THIS OR THE WORLD WILL END! + +#endif /* CONFIG_H_ */ diff --git a/inc/dnbd3/config/client.h b/inc/dnbd3/config/client.h new file mode 100644 index 0000000..f35f673 --- /dev/null +++ b/inc/dnbd3/config/client.h @@ -0,0 +1,36 @@ +#ifndef _CLIENTCONFIG_H_ +#define _CLIENTCONFIG_H_ + +// Which is the minimum protocol version the client expects from the server +#define MIN_SUPPORTED_SERVER 2 + +// in seconds if not stated otherwise (MS = milliseconds) +#define SOCKET_TIMEOUT_CLIENT_DATA 2 +#define SOCKET_TIMEOUT_CLIENT_DISCOVERY 1 + +#define RTT_THRESHOLD_FACTOR(us) (((us) * 2) / 3) // 2/3 = current to best must be 33% worse +#define RTT_ABSOLUTE_THRESHOLD (80000) // Or 80ms worse +#define RTT_UNREACHABLE 0x7FFFFFFul // Use this value for timeout/unreachable as RTT. Don't set too high or you might get overflows. 0x7FFFFFF = 134 seconds +// This must be a power of two: +#define RTT_BLOCK_SIZE 4096 + +#define STARTUP_MODE_DURATION 30 +// Interval of several repeating tasks (in seconds) +#define TIMER_INTERVAL_PROBE_STARTUP 4 +#define TIMER_INTERVAL_PROBE_NORMAL 22 +#define TIMER_INTERVAL_PROBE_PANIC 2 +#define TIMER_INTERVAL_KEEPALIVE_PACKET 6 + +// Expect a keepalive response every X seconds +#define SOCKET_KEEPALIVE_TIMEOUT 8 + +// Number of unsuccessful alt_server probes before read errors are reported to the block layer +// (ALL servers will be probed this many times) +// Set to 0 to disable +#define PROBE_COUNT_TIMEOUT 0 + +// ++ Kernel module ++ +#define DEFAULT_READ_AHEAD_KB 512 +#define NUMBER_DEVICES 8 + +#endif diff --git a/inc/dnbd3/config/server.h b/inc/dnbd3/config/server.h new file mode 100644 index 0000000..b6eee2c --- /dev/null +++ b/inc/dnbd3/config/server.h @@ -0,0 +1,62 @@ +#ifndef _SERVERCONFIG_H_ +#define _SERVERCONFIG_H_ + +#include + +// +++++ Performance/memory related +#define SERVER_MAX_CLIENTS 4000 +#define SERVER_MAX_IMAGES 5000 +#define SERVER_MAX_ALTS 50 +// +++++ Uplink handling (proxy mode) +#define SERVER_GLOBAL_DUP_TIME 6 // How many seconds to wait before changing global fail counter again +#define SERVER_BAD_UPLINK_MIN 10 // Thresold for fails at which we start ignoring the server occasionally +#define SERVER_BAD_UPLINK_MAX 20 // Hard block server if it failed this many times +#define SERVER_BAD_UPLINK_LOCAL_BLOCK 10 // If a server didn't supply the requested image this many times, block it for some time +#define SERVER_BAD_UPLINK_IGNORE 180 // How many seconds is a server ignored +#define UPLINK_MAX_QUEUE 500 // Maximum number of queued requests per uplink +#define UPLINK_MAX_CLIENTS_PER_REQUEST 32 // Maximum number of clients that can attach to one uplink request +#define SERVER_UPLINK_QUEUELEN_THRES 900 // Threshold where we start dropping incoming clients +#define SERVER_MAX_PENDING_ALT_CHECKS 500 // Length of queue for pending alt checks requested by uplinks + +// Wait a maximum of 5 minutes before saving cache map (if data was received at all) +#define CACHE_MAP_MAX_SAVE_DELAY 300 +// If more than 500MB have been received from uplink without saving cache map, do so +#define CACHE_MAP_MAX_UNSAVED_BYTES ((uint64_t)500 * 1000 * 1000) + +// Time in ms to wait for a read/write call to complete on an uplink connection +#define SOCKET_TIMEOUT_UPLINK 5000 +// Same for client connections. Be a bit more liberal here +#define SOCKET_TIMEOUT_CLIENT 15000 +// When waiting for the next request header from client, allow the timeout from above +// to expire this many times. This allows for greater idle times without also increasing +// the timeout for cases where we wait for additional data or are actively sending a reply +#define SOCKET_TIMEOUT_CLIENT_RETRIES 3 + +#define SERVER_UPLINK_KEEPALIVE_INTERVAL 10 // (Seconds) Send keep-alive if nothing else is happening on the uplink +#define SERVER_UPLINK_IDLE_TIMEOUT 1800 // (Seconds) Timeout after which we tear down an uplink connection if no blocks needed to be fetched + +// +++++ Other magic constants +#define SERVER_RTT_PROBES 5 // How many probes to average over +#define SERVER_RTT_INTERVAL_INIT 5 // Initial interval between probes +#define SERVER_RTT_INTERVAL_MAX 45 // Maximum interval between probes +#define SERVER_RTT_MAX_UNREACH 10 // If no server was reachable this many times, stop RTT measurements for a while +#define SERVER_RTT_INTERVAL_FAILED 180 // Interval to use if no uplink server is reachable for above many times + +#define SERVER_REMOTE_IMAGE_CHECK_CACHETIME 120 // 2 minutes + +// Which is the minimum protocol version the server expects from the client +#define MIN_SUPPORTED_CLIENT 2 +// Same for when we're a proxy talking to another server +#define MIN_SUPPORTED_SERVER 2 + +// Length of comment fields (for alt server etc.) +#define COMMENT_LENGTH 120 + +#define RTT_THRESHOLD_FACTOR(us) (((us) * 2) / 3) // 2/3 = current to best must be 33% worse +#define RTT_UNREACHABLE 0x7FFFFFFu // Use this value for timeout/unreachable as RTT. Don't set too high or you might get overflows. 0x7FFFFFF = 134 seconds + +// How many seconds have to pass after the last client disconnected until the imagefd is closed +#define UNUSED_FD_TIMEOUT 3600 + +#endif + diff --git a/inc/dnbd3/shared/crc32.h b/inc/dnbd3/shared/crc32.h new file mode 100644 index 0000000..00b8bdd --- /dev/null +++ b/inc/dnbd3/shared/crc32.h @@ -0,0 +1,9 @@ +#ifndef _CRC32_H_ +#define _CRC32_H_ + +#include + +uint32_t crc32(uint32_t crc, const uint8_t *buf, size_t len); + +#endif + diff --git a/inc/dnbd3/shared/fdsignal.h b/inc/dnbd3/shared/fdsignal.h new file mode 100644 index 0000000..960a2a9 --- /dev/null +++ b/inc/dnbd3/shared/fdsignal.h @@ -0,0 +1,57 @@ +#ifndef _FD_SIGNAL_H_ +#define _FD_SIGNAL_H_ + +#define SIGNAL_OK (0) +#define SIGNAL_TIMEOUT (-2) +#define SIGNAL_ERROR (-1) + +typedef struct _dnbd3_signal dnbd3_signal_t; + +/** + * Create a new signal, nonblocking. + * @return NULL on error, pointer to dnbd3_signal_t on success. + */ +dnbd3_signal_t* signal_new(); + +/** + * Create a new signal, blocking. + * @return NULL on error, pointer to dnbd3_signal_t on success. + */ +dnbd3_signal_t* signal_newBlocking(); + +/** + * Trigger the given signal, so a wait or clear call will succeed. + * @return SIGNAL_OK on success, SIGNAL_ERROR on error + */ +int signal_call(const dnbd3_signal_t* const signal); + +/** + * Wait for given signal, with an optional timeout. + * If timeout == 0, just poll once. + * If timeout < 0, wait forever. + * @return > 0 telling how many times the signal was called, + * SIGNAL_TIMEOUT if the timeout was reached, + * SIGNAL_ERROR if some error occured + */ +int signal_wait(const dnbd3_signal_t* const signal, int timeoutMs); + +/** + * Clears any pending signals on this signal. + * @return number of signals that were pending, + * SIGNAL_ERROR if some error occured + */ +int signal_clear(const dnbd3_signal_t* const signal); + +/** + * Close the given signal. + */ +void signal_close(const dnbd3_signal_t* const signal); + +/** + * Get a file descriptor for the given signal that can be + * waited on using poll or similar. + * @return -1 if the signal is invalid + */ +int signal_getWaitFd(const dnbd3_signal_t* const signal); + +#endif diff --git a/inc/dnbd3/shared/log.h b/inc/dnbd3/shared/log.h new file mode 100644 index 0000000..5b1e8f7 --- /dev/null +++ b/inc/dnbd3/shared/log.h @@ -0,0 +1,65 @@ +/* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Simon Rettberg + * + * This file may be licensed under the terms of of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LOG_H_ +#define LOG_H_ + +#include +#include + +typedef unsigned int logmask_t; +#define LOG_ERROR ((logmask_t)1) // Fatal error, server will terminate +#define LOG_WARNING ((logmask_t)2) // Major issue, something is broken but keep running +#define LOG_MINOR ((logmask_t)4) // Minor issue, more of a hickup than serious problem +#define LOG_INFO ((logmask_t)8) // Informational message +#define LOG_DEBUG1 ((logmask_t)16) // Debug information, use this for non-spammy stuff +#define LOG_DEBUG2 ((logmask_t)32) // Use this for debug messages that will show up a lot + + +/** + * Check if cansoleMask | fileMask has all of mask set. + */ +bool log_hasMask(const logmask_t mask); + +void log_setFileMask(logmask_t mask); + +void log_setConsoleMask(logmask_t mask); + +void log_setConsoleTimestamps(bool on); + +/** + * Open or reopen the log file. If path is NULL and the + * function was called with a path before, the same path + * will be used again. + */ +bool log_openLogFile(const char *path); + +/** + * Add a line to the log + */ +void logadd(const logmask_t mask, const char *text, ...) + __attribute__ ((format (printf, 2, 3))); + +/** + * Return last size bytes of log. + */ +ssize_t log_fetch(char *buffer, int size); + +#endif /* LOG_H_ */ diff --git a/inc/dnbd3/shared/protocol.h b/inc/dnbd3/shared/protocol.h new file mode 100644 index 0000000..1dd47f8 --- /dev/null +++ b/inc/dnbd3/shared/protocol.h @@ -0,0 +1,156 @@ +#ifndef _PROTOCOL_H_ +#define _PROTOCOL_H_ + +#include +#include +#include + +#include +#include +#include +#include + +// Client tells server that it is another server +#define FLAGS8_SERVER (1) +// Client (which is a proxy) tells server that it has background-replication enabled +#define FLAGS8_BG_REP (2) + +// 2017-10-16: We now support hop-counting, macro to pass hop count conditinally to a function +#define COND_HOPCOUNT(vers,hopcount) ( (vers) >= 3 ? (hopcount) : 0 ) + +// 2017-11-02: Macro to set flags in select image message properly if we're a server, as BG_REP depends on global var +#define SI_SERVER_FLAGS ( (uint8_t)( (_pretendClient ? 0 : FLAGS8_SERVER) | (_backgroundReplication == BGR_FULL ? FLAGS8_BG_REP : 0) ) ) + +#define REPLY_OK (0) +#define REPLY_ERRNO (-1) +#define REPLY_AGAIN (-2) +#define REPLY_INTR (-3) +#define REPLY_CLOSED (-4) +#define REPLY_INCOMPLETE (-5) +#define REPLY_WRONGMAGIC (-6) + +static inline int dnbd3_read_reply(int sock, dnbd3_reply_t *reply, bool wait) +{ + ssize_t ret = recv( sock, reply, sizeof(*reply), (wait ? MSG_WAITALL : MSG_DONTWAIT) | MSG_NOSIGNAL ); + if ( ret == 0 ) return REPLY_CLOSED; + if ( ret < 0 ) { + if ( errno == EAGAIN || errno == EWOULDBLOCK ) return REPLY_AGAIN; + if ( errno == EINTR ) return REPLY_INTR; + return REPLY_ERRNO; + } + if ( !wait && ret != sizeof(*reply) ) ret += recv( sock, ((char*)reply) + ret, sizeof(*reply) - ret, MSG_WAITALL | MSG_NOSIGNAL ); + if ( ret != sizeof(*reply) ) return REPLY_INCOMPLETE; + fixup_reply( *reply ); + if ( reply->magic != dnbd3_packet_magic ) return REPLY_WRONGMAGIC; + return REPLY_OK; +} + +static inline bool dnbd3_get_reply(int sock, dnbd3_reply_t *reply) +{ + int ret; + do { + ret = dnbd3_read_reply( sock, reply, true ); + } while ( ret == REPLY_INTR ); + return ret == REPLY_OK; +} + +static inline bool dnbd3_select_image(int sock, const char *name, uint16_t rid, uint8_t flags8) +{ + serialized_buffer_t serialized; + dnbd3_request_t request; + struct iovec iov[2]; + serializer_reset_write( &serialized ); + serializer_put_uint16( &serialized, PROTOCOL_VERSION ); + serializer_put_string( &serialized, name ); + serializer_put_uint16( &serialized, rid ); + serializer_put_uint8( &serialized, flags8 ); + const ssize_t len = serializer_get_written_length( &serialized ); + request.magic = dnbd3_packet_magic; + request.cmd = CMD_SELECT_IMAGE; + request.size = (uint32_t)len; + request.handle = 0; + request.offset = 0; + fixup_request( request ); + iov[0].iov_base = &request; + iov[0].iov_len = sizeof(request); + iov[1].iov_base = &serialized; + iov[1].iov_len = len; + ssize_t ret; + do { + ret = writev( sock, iov, 2 ); + } while ( ret == -1 && errno == EINTR ); + return ret == len + (ssize_t)sizeof(request); +} + +static inline bool dnbd3_get_block(int sock, uint64_t offset, uint32_t size, uint64_t handle, uint8_t hopCount) +{ + dnbd3_request_t request; + request.magic = dnbd3_packet_magic; + request.handle = handle; + request.cmd = CMD_GET_BLOCK; + // When writing before "fixup", we can get away with assigning to offset instead of offset_small if we + // do it before assigning to .hops. Faster on 64bit machines (so, on everything) + request.offset = offset; + request.hops = hopCount; + request.size = size; + fixup_request( request ); + return sock_sendAll( sock, &request, sizeof(request), 2 ) == (ssize_t)sizeof(request); +} + +static inline bool dnbd3_get_crc32(int sock, uint32_t *master, void *buffer, size_t *bufferLen) +{ + dnbd3_request_t request; + dnbd3_reply_t reply; + request.magic = dnbd3_packet_magic; + request.handle = 0; + request.cmd = CMD_GET_CRC32; + request.offset = 0; + request.size = 0; + fixup_request( request ); + if ( sock_sendAll( sock, &request, sizeof(request), 2 ) != (ssize_t)sizeof(request) ) return false; + if ( !dnbd3_get_reply( sock, &reply ) ) return false; + if ( reply.size == 0 ) { + *bufferLen = 0; + return true; + } + if ( reply.size < 4 ) return false; + reply.size -= 4; + if ( reply.cmd != CMD_GET_CRC32 || reply.size > *bufferLen ) return false; + *bufferLen = reply.size; + if ( sock_recv( sock, master, sizeof(uint32_t) ) != (ssize_t)sizeof(uint32_t) ) return false; + return sock_recv( sock, buffer, reply.size ) == (ssize_t)reply.size; +} + +/** + * Pass a full serialized_buffer_t and a socket fd. Parsed data will be returned in further arguments. + * Note that all strings will point into the passed buffer, so there's no need to free them. + * This function will also read the header for you, as this message can only occur during connection, + * where no unrequested messages could arrive inbetween. + */ +static inline bool dnbd3_select_image_reply(serialized_buffer_t *buffer, int sock, uint16_t *protocol_version, char **name, uint16_t *rid, + uint64_t *imageSize) +{ + errno = 0; + dnbd3_reply_t reply; + if ( !dnbd3_get_reply( sock, &reply ) ) { + return false; + } + errno = 0; + if ( reply.cmd != CMD_SELECT_IMAGE || reply.size < 3 || reply.size > MAX_PAYLOAD ) { + return false; + } + // receive reply payload + ssize_t ret = sock_recv( sock, buffer, reply.size ); + if ( ret != (ssize_t)reply.size ) { + return false; + } + // handle/check reply payload + serializer_reset_read( buffer, reply.size ); + *protocol_version = serializer_get_uint16( buffer ); + *name = serializer_get_string( buffer ); + *rid = serializer_get_uint16( buffer ); + *imageSize = serializer_get_uint64( buffer ); + return true; +} + +#endif diff --git a/inc/dnbd3/shared/serialize.h b/inc/dnbd3/shared/serialize.h new file mode 100644 index 0000000..a7a1ced --- /dev/null +++ b/inc/dnbd3/shared/serialize.h @@ -0,0 +1,40 @@ +#ifndef SERIALIZER_H_ +#define SERIALIZER_H_ + +#include +#include + +typedef struct +{ + char buffer[MAX_PAYLOAD]; // This MUST be the first member or send_reply() will blow up + char *buffer_end; + char *buffer_pointer; +} serialized_buffer_t; + +void serializer_reset_read(serialized_buffer_t *buffer, size_t data_len); + +void serializer_reset_write(serialized_buffer_t *buffer); + +uint32_t serializer_get_written_length(serialized_buffer_t *buffer); + +// + +uint8_t serializer_get_uint8(serialized_buffer_t *buffer); + +uint16_t serializer_get_uint16(serialized_buffer_t *buffer); + +uint64_t serializer_get_uint64(serialized_buffer_t *buffer); + +char *serializer_get_string(serialized_buffer_t *buffer); + +// + +void serializer_put_uint8(serialized_buffer_t *buffer, uint8_t value); + +void serializer_put_uint16(serialized_buffer_t *buffer, uint16_t value); + +void serializer_put_uint64(serialized_buffer_t *buffer, uint64_t value); + +void serializer_put_string(serialized_buffer_t *buffer, const char *value); + +#endif diff --git a/inc/dnbd3/shared/sockhelper.h b/inc/dnbd3/shared/sockhelper.h new file mode 100644 index 0000000..5c7d903 --- /dev/null +++ b/inc/dnbd3/shared/sockhelper.h @@ -0,0 +1,120 @@ +#ifndef SOCKHELPER_H_ +#define SOCKHELPER_H_ + +/* + * Helper functions for dealing with sockets. These functions should + * abstract from the IP version by using getaddrinfo() and thelike. + */ + +#include +#include +#include +#include + +typedef struct _poll_list poll_list_t; + +/** + * Connect to given dnbd3_host_t. + * @param addr - address of host to connect to + * @param connect_ms - timeout in milliseconds after which the connection attempt fails + * @param rw_ms - read/write timeout in milliseconds to apply on successful connect + * @return socket file descriptor, or -1 on error + */ +int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms); + +/** + * Resolve/parse given address and put the result(s) into passed dnbd3_host_t array, + * but only up to count entries. + * @return Number of items added to array + */ +int sock_resolveToDnbd3Host(const char * const address, dnbd3_host_t * const dest, const int count); + +bool sock_sockaddrToDnbd3(struct sockaddr* sa, dnbd3_host_t *host); + +void sock_setTimeout(const int sockfd, const int milliseconds); + +size_t sock_printHost(const dnbd3_host_t * const host, char *output, const size_t len); + +size_t sock_printable(const struct sockaddr * const addr, const socklen_t addrLen, char *output, const size_t len); + +/** + * Create new poll list. + */ +poll_list_t* sock_newPollList(); + +/** + * Delete a poll list, closing all sockets first if necessary. + */ +void sock_destroyPollList(poll_list_t *list); + +/** + * Listen on all interfaces/available IP addresses, using the given protocol. + * IPv4 and IPv6 are supported. + * @param protocol_family PF_INET or PF_INET6 + * @param port port to listen on + * @return true if any listen call was successful + */ +bool sock_listenAny(poll_list_t* list, uint16_t port); + +/** + * Listen on a specific address and port. + * @param bind_addr human readable address to bind to for listening + * @param port to listen on + */ +bool sock_listen(poll_list_t* list, char* bind_addr, uint16_t port); + +/** + * Asynchroneously connect to multiple hosts. + * This can be called multiple times with varying timeouts. Calling it + * the first time on an empty list is identical to sock_connect(). On + * consecutive calls, more nonblocking sockets in connecting state will + * be added to the list, and on each of these calls, all the pending + * sockets will be checked for successful connection (or error), respecting + * the passed timeout. + * host can be NULL to just wait on the sockets already in the list. + * If at least one socket completed the connection + * within the given timeout, it will be removed from the list and + * returned. On error or timeout, -1 is returned. If there are no more sockets + * in the list, -2 is returned. + */ +int sock_multiConnect(poll_list_t* list, const dnbd3_host_t* host, int connect_ms, int rw_ms); + +/** + * This is a multi-socket version of accept. Pass in an array of listening sockets. + * If any of the sockets has an incoming connection, accept it and return the new socket's fd. + * On error, return -1, just like accept(). + * @param sockets array of listening socket fds + * @param socket_count number of sockets in that array + * @return fd of new client socket, -1 on error + */ +int sock_accept(poll_list_t *list, struct sockaddr_storage *addr, socklen_t *length_ptr); + +void sock_set_nonblock(int sock); + +void sock_set_block(int sock); + +/** + * Add given socket to array. Take an existing empty slot ( == -1) if available, + * append to end otherwise. Updates socket count variable passed by reference. + * + * @param poll_list_t list the poll list to add the socket to + * @param sock socket fd to add + * @param wantRead whether to set the EPOLLIN flag + * @param wantWrite whether to set the EPOLLOUT flag + * @return true on success, false iff the array is already full or socket is < 0 + */ +bool sock_append(poll_list_t *list, const int sock, bool wantRead, bool wantWrite); + +/** + * Send the whole buffer, calling write() multiple times if neccessary. + * Give up after calling write() maxtries times. + * Set maxtries < 0 to try infinitely. + */ +ssize_t sock_sendAll(const int sock, const void *buffer, const size_t len, int maxtries); + +/** + * Send given buffer, repeatedly calling recv on partial send or EINTR. + */ +ssize_t sock_recv(const int sock, void *buffer, const size_t len); + +#endif /* SOCKHELPER_H_ */ diff --git a/inc/dnbd3/shared/timing.h b/inc/dnbd3/shared/timing.h new file mode 100644 index 0000000..f23bfeb --- /dev/null +++ b/inc/dnbd3/shared/timing.h @@ -0,0 +1,162 @@ +#ifndef _D_TIMING_H +#define _D_TIMING_H + +#ifndef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 199309L +#endif + +#include +#include +#include + +#ifdef CLOCK_MONOTONIC_RAW +#define BEST_CLOCK_SOURCE CLOCK_MONOTONIC_RAW +#else +#define BEST_CLOCK_SOURCE CLOCK_MONOTONIC +#endif + +typedef struct timespec ticks; + +extern struct timespec basetime; + +/** + * Assign src to dst while adding secs seconds. + */ +#define timing_set(dst,src,secs) do { (dst)->tv_sec = (src)->tv_sec + (secs); (dst)->tv_nsec = (src)->tv_nsec; } while (0) + +/** + * Define variable now, initialize to timing_get. + */ +#define declare_now ticks now; timing_get( &now ) + +/** + * Call this once to calibrate on startup. + * Although overflows of CLOCK_MONOTONIC(_RAW) should + * by definition never happen, we still have a fixed size + * int that could at some point. By forcing the counter + * to start at 0 on startup the point of overflow + * will be very far in the future (decades for 32bit time_t, + * end of universe for 64bit). + */ +void timing_setBase(); + +/** + * Internal, do not use. Moved to another function + * to prevent inlining of error handling code, which + * should be very unlikely to ever trigger. + */ +_Noreturn void timing_abort(); + +/** + * Get current time. Shortcut for clock_gettime with error check. + */ +static inline void timing_get(ticks* retval) +{ + if ( clock_gettime( BEST_CLOCK_SOURCE, retval ) == -1 ) timing_abort(); + retval->tv_sec -= basetime.tv_sec; +} + +/** + * Get a ticks instance somewhere in the future. + * Useful for timeouts. + */ +static inline void timing_gets(ticks* retval, int32_t addSeconds) +{ + timing_get( retval ); + retval->tv_sec += addSeconds; +} + +static inline void timing_addSeconds(ticks* retval, ticks* base, int32_t addSeconds) +{ + retval->tv_sec = base->tv_sec + addSeconds; + retval->tv_nsec = base->tv_nsec; +} + +/** + * Check whether given timeout is reached. + * Might trigger up to one second early. + */ +static inline bool timing_reached(const ticks* timeout, const ticks* now) +{ + return now->tv_sec >= timeout->tv_sec; +} +#define timing_1le2(one,two) timing_reached(one,two) + +/** + * Precise check whether given timeout has been reached. + */ +static inline bool timing_reachedPrecise(const ticks* timeout, const ticks* now) +{ + return now->tv_sec > timeout->tv_sec + || (now->tv_sec == timeout->tv_sec && now->tv_nsec > timeout->tv_nsec); +} + +/** + * Shortcut for above. Useful if not used in loop. + * Might trigger up to one second early. + */ +static inline bool timing_isReached(const ticks* timeout) +{ + ticks now; + timing_get( &now ); + return timing_reached( timeout, &now ); +} +/** + * Shortcut for above. Useful if not used in loop. + */ +static inline bool timing_isReachedPrecise(const ticks* timeout) +{ + ticks now; + timing_get( &now ); + return timing_reachedPrecise( timeout, &now ); +} + + +/** + * Get difference between two ticks, rounded down to seconds. + * Make sure you pass the arguments in the proper order. If + * end is before start, 0 will always be returned. + */ +static inline uint32_t timing_diff(const ticks *start, const ticks *end) +{ + if ( end->tv_sec <= start->tv_sec ) return 0; + return (uint32_t)( ( end->tv_sec - start->tv_sec ) + + ( start->tv_nsec > end->tv_nsec ? -1 : 0 ) ); +} + +/** + * Get difference between two ticks, rounded down to milliseconds. + * Same as above; passing arguments in reverse will always return 0. + */ +static inline uint64_t timing_diffMs(const ticks *start, const ticks *end) +{ + if ( end->tv_sec < start->tv_sec ) return 0; + uint64_t diff = (uint64_t)( end->tv_sec - start->tv_sec ) * 1000; + if ( start->tv_nsec >= end->tv_nsec ) { + if ( diff == 0 ) return 0; + diff -= (start->tv_nsec - end->tv_nsec) / 1000000; + } else { + diff += (end->tv_nsec - start->tv_nsec) / 1000000; + } + return diff; +} + +/** + * Get difference between two ticks, rounded down to microseconds. + * Same as above; passing arguments in reverse will always return 0. + */ +static inline uint64_t timing_diffUs(const ticks *start, const ticks *end) +{ + if ( end->tv_sec < start->tv_sec ) return 0; + uint64_t diff = (uint64_t)( end->tv_sec - start->tv_sec ) * 1000000; + if ( start->tv_nsec >= end->tv_nsec ) { + if ( diff == 0 ) return 0; + diff -= ( start->tv_nsec - end->tv_nsec ) / 1000; + } else { + diff += ( end->tv_nsec - start->tv_nsec ) / 1000; + } + return diff; +} + + +#endif diff --git a/inc/dnbd3/types.h b/inc/dnbd3/types.h new file mode 100644 index 0000000..c7f335b --- /dev/null +++ b/inc/dnbd3/types.h @@ -0,0 +1,190 @@ +/* + * This file is part of the Distributed Network Block Device 3 + * + * Copyright(c) 2011-2012 Johann Latocha + * + * This file may be licensed under the terms of of the + * GNU General Public License Version 2 (the ``GPL''). + * + * Software distributed under the License is distributed + * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See the GPL for the specific language + * governing rights and limitations. + * + * You should have received a copy of the GPL along with this + * program. If not, go to http://www.gnu.org/licenses/gpl.html + * or write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef TYPES_H_ +#define TYPES_H_ + +#include +#ifdef DNBD3_KERNEL_MODULE +#include +#include +#else +#include +#include +#include +#include +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +#ifdef __GNUC__ +#define UNUSED __attribute__ ((unused)) +#else +#error "Please add define for your compiler for UNUSED, or define to nothing for your compiler if not supported" +#endif + +#if defined(__GNUC__) && __GNUC__ >= 3 +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifdef __linux__ +#define HAVE_THREAD_NAMES +#endif + +#ifdef __FreeBSD__ +#ifndef MSG_MORE +#define MSG_MORE 0 +#endif +#ifndef POLLRDHUP +#define POLLRDHUP 0x2000 +#endif +#include +#endif + +#ifdef DNBD3_SERVER_AFL +#define send(a,b,c,d) write(a,b,c) +#define recv(a,b,c,d) read(a,b,c) +#endif + + +// ioctl +#define DNBD3_MAGIC 'd' +#define IOCTL_OPEN _IO(0xab, 1) +#define IOCTL_CLOSE _IO(0xab, 2) +#define IOCTL_SWITCH _IO(0xab, 3) +#define IOCTL_ADD_SRV _IO(0xab, 4) +#define IOCTL_REM_SRV _IO(0xab, 5) + +#if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define dnbd3_packet_magic ((uint16_t)( (0x73 << 8) | (0x72) )) +// Flip bytes around on big endian when putting stuff on the net +#define net_order_64(a) ((uint64_t)((((a) & 0xFFull) << 56) | (((a) & 0xFF00ull) << 40) | (((a) & 0xFF0000ull) << 24) | (((a) & 0xFF000000ull) << 8) | (((a) & 0xFF00000000ull) >> 8) | (((a) & 0xFF0000000000ull) >> 24) | (((a) & 0xFF000000000000ull) >> 40) | (((a) & 0xFF00000000000000ull) >> 56))) +#define net_order_32(a) ((uint32_t)((((a) & (uint32_t)0xFF) << 24) | (((a) & (uint32_t)0xFF00) << 8) | (((a) & (uint32_t)0xFF0000) >> 8) | (((a) & (uint32_t)0xFF000000) >> 24))) +#define net_order_16(a) ((uint16_t)((((a) & (uint16_t)0xFF) << 8) | (((a) & (uint16_t)0xFF00) >> 8))) +#define fixup_request(a) do { \ + (a).cmd = net_order_16((a).cmd); \ + (a).size = net_order_32((a).size); \ + (a).offset = net_order_64((a).offset); \ +} while (0) +#define fixup_reply(a) do { \ + (a).cmd = net_order_16((a).cmd); \ + (a).size = net_order_32((a).size); \ +} while (0) +#define ENDIAN_MODE "Big Endian" +#define DNBD3_BIG_ENDIAN +#elif defined(__LITTLE_ENDIAN__) || (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || defined(__i386__) || defined(__i386) || defined(__x86_64) +#define dnbd3_packet_magic ((uint16_t)( (0x73) | (0x72 << 8) )) +// Make little endian our network byte order as probably 99.999% of machines this will be used on are LE +#define net_order_64(a) (a) +#define net_order_32(a) (a) +#define net_order_16(a) (a) +#define fixup_request(a) while(0) +#define fixup_reply(a) while(0) +#define ENDIAN_MODE "Little Endian" +#define DNBD3_LITTLE_ENDIAN +#else +#error "Unknown Endianness" +#endif + +typedef uint8_t dnbd3_af; + +static const dnbd3_af HOST_NONE = (dnbd3_af)0; +static const dnbd3_af HOST_IP4 = (dnbd3_af)2; +static const dnbd3_af HOST_IP6 = (dnbd3_af)10; + +typedef struct __attribute__((packed)) dnbd3_host_t +{ + uint8_t addr[16]; // 16byte (network representation, so it can be directly passed to socket functions) + uint16_t port; // 2byte (network representation, so it can be directly passed to socket functions) + dnbd3_af type; // 1byte (ip version. HOST_IP4 or HOST_IP6. 0 means this struct is empty and should be ignored) +} dnbd3_host_t; + +typedef struct __attribute__((packed)) +{ + uint16_t len; + dnbd3_host_t host; + uint16_t imgnamelen; + char *imgname; + int rid; + int read_ahead_kb; + uint8_t use_server_provided_alts; +} dnbd3_ioctl_t; + +// network +#define CMD_GET_BLOCK 1 +#define CMD_SELECT_IMAGE 2 +#define CMD_GET_SERVERS 3 +#define CMD_ERROR 4 +#define CMD_KEEPALIVE 5 +#define CMD_LATEST_RID 6 +#define CMD_SET_CLIENT_MODE 7 +#define CMD_GET_CRC32 8 + +#define DNBD3_REQUEST_SIZE 24 +typedef struct __attribute__((packed)) +{ + uint16_t magic; // 2byte + uint16_t cmd; // 2byte + uint32_t size; // 4byte + union { + struct { +#ifdef DNBD3_LITTLE_ENDIAN + uint64_t offset_small:56; // 7byte + uint8_t hops; // 1byte +#elif defined(DNBD3_BIG_ENDIAN) + uint8_t hops; // 1byte + uint64_t offset_small:56; // 7byte +#endif + }; + uint64_t offset; // 8byte + }; + uint64_t handle; // 8byte +} dnbd3_request_t; +_Static_assert( sizeof(dnbd3_request_t) == DNBD3_REQUEST_SIZE, "dnbd3_request_t is messed up" ); + +#define DNBD3_REPLY_SIZE 16 +typedef struct __attribute__((packed)) +{ + uint16_t magic; // 2byte + uint16_t cmd; // 2byte + uint32_t size; // 4byte + uint64_t handle; // 8byte +} dnbd3_reply_t; +_Static_assert( sizeof(dnbd3_reply_t) == DNBD3_REPLY_SIZE, "dnbd3_reply_t is messed up" ); + +typedef struct __attribute__((packed)) +{ + dnbd3_host_t host; + uint8_t failures; // 1byte (number of times server has been consecutively unreachable) +} dnbd3_server_entry_t; + +#endif /* TYPES_H_ */ diff --git a/inc/dnbd3/version.h.in b/inc/dnbd3/version.h.in new file mode 100644 index 0000000..4a88cf0 --- /dev/null +++ b/inc/dnbd3/version.h.in @@ -0,0 +1,9 @@ +/* + * AUTOGENERATED: DO NOT EDIT THIS FILE + */ +#ifndef VERSION_H_ +#define VERSION_H_ + +#define DNBD3_VERSION "@DNBD3_VERSION@" + +#endif /* VERSION_H_ */ -- cgit v1.2.3-55-g7522