diff options
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/CMakeLists.txt | 28 | ||||
-rw-r--r-- | src/shared/crc32.c | 238 | ||||
-rw-r--r-- | src/shared/crc32.h | 9 | ||||
-rw-r--r-- | src/shared/fdsignal.c | 4 | ||||
-rw-r--r-- | src/shared/fdsignal.h | 57 | ||||
-rw-r--r-- | src/shared/log.c | 36 | ||||
-rw-r--r-- | src/shared/log.h | 65 | ||||
-rw-r--r-- | src/shared/protocol.h | 159 | ||||
-rw-r--r-- | src/shared/serialize.c | 99 | ||||
-rw-r--r-- | src/shared/sockhelper.c | 42 | ||||
-rw-r--r-- | src/shared/sockhelper.h | 120 | ||||
-rw-r--r-- | src/shared/timing.c | 2 | ||||
-rw-r--r-- | src/shared/timing.h | 162 |
13 files changed, 362 insertions, 659 deletions
diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt new file mode 100644 index 0000000..a1bd49a --- /dev/null +++ b/src/shared/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-shared + LANGUAGES C) + +# find atomic library required by dnbd3-shared +find_package(Stdatomic REQUIRED) +find_package(Libatomic REQUIRED) + +# add compile option to get POLLRDHUP support for signals +add_definitions(-D_GNU_SOURCE) + +set(DNBD3_SHARED_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/crc32.c + ${CMAKE_CURRENT_SOURCE_DIR}/fdsignal.c + ${CMAKE_CURRENT_SOURCE_DIR}/log.c + ${CMAKE_CURRENT_SOURCE_DIR}/serialize.c + ${CMAKE_CURRENT_SOURCE_DIR}/sockhelper.c + ${CMAKE_CURRENT_SOURCE_DIR}/timing.c) +set(DNBD3_SHARED_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/fdsignal.inc/eventfd.c + ${CMAKE_CURRENT_SOURCE_DIR}/fdsignal.inc/pipe64.c + ${CMAKE_CURRENT_SOURCE_DIR}/fdsignal.inc/pipe_malloc.c) + +add_library(dnbd3-shared STATIC ${DNBD3_SHARED_SOURCE_FILES}) +target_include_directories(dnbd3-shared PUBLIC ${PROJECT_INCLUDE_DIR}) + +add_linter(dnbd3-shared-lint "${DNBD3_SHARED_SOURCE_FILES}" "${DNBD3_SHARED_HEADER_FILES}") +add_linter_fix(dnbd3-shared-lint-fix "${DNBD3_SHARED_SOURCE_FILES}" "${DNBD3_SHARED_HEADER_FILES}") diff --git a/src/shared/crc32.c b/src/shared/crc32.c index db941d3..6cf9a18 100644 --- a/src/shared/crc32.c +++ b/src/shared/crc32.c @@ -38,24 +38,23 @@ */ -#include "../types.h" +#include <dnbd3/types.h> #include <stddef.h> -#define FAR +#if defined(__x86_64__) || defined(__amd64__) +#include <emmintrin.h> +#include <smmintrin.h> +#include <wmmintrin.h> +#include <stdatomic.h> +#define zalign(n) __attribute__((aligned(n))) +#endif + #define OF(args) args -#define local static /* Definitions for doing the crc four data bytes at a time. */ -#if !defined(NOBYFOUR) -# define BYFOUR -#endif -#ifdef BYFOUR -# define TBLS 8 -#else -# define TBLS 1 -#endif /* BYFOUR */ +#define TBLS 8 -local const uint32_t crc_table[TBLS][256] = +static const uint32_t crc_table[TBLS][256] = { { 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, @@ -110,7 +109,6 @@ local const uint32_t crc_table[TBLS][256] = 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U, 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, 0x2d02ef8dU -#ifdef BYFOUR }, { 0x00000000U, 0x191b3141U, 0x32366282U, 0x2b2d53c3U, 0x646cc504U, @@ -489,38 +487,159 @@ local const uint32_t crc_table[TBLS][256] = 0x95e6b8b1U, 0x7b490da3U, 0x1e2eb11bU, 0x483ed243U, 0x2d596efbU, 0xc3f6dbe9U, 0xa6916751U, 0x1fa9b0ccU, 0x7ace0c74U, 0x9461b966U, 0xf10605deU -#endif } }; -#ifdef NO_ENDIAN -// Currently not in use, always use the BYFOUR method with known endianness -/* ========================================================================= */ -#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) -#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 +#define PCLMUL_MIN_LEN 64 +#define PCLMUL_ALIGN 16 +#define PCLMUL_ALIGN_MASK 15 -/* ========================================================================= */ -uint32_t crc32(crc, buf, len) - uint32_t crc; - const uint8_t *buf; - size_t len; +#if defined(__x86_64__) || defined(__amd64__) +/* crc32_simd.c + * + * Copyright 2017 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the Chromium source repository LICENSE file. + * + * crc32_sse42_simd_(): compute the crc32 of the buffer, where the buffer + * length must be at least 64, and a multiple of 16. Based on: + * + * "Fast CRC Computation for Generic Polynomials Using PCLMULQDQ Instruction" + * V. Gopal, E. Ozturk, et al., 2009, http://intel.ly/2ySEwL0 + */ +static uint32_t +__attribute__((target("pclmul,sse4.1"))) +crc32pclmul(uint32_t crc, const uint8_t *buf, size_t len) { - if (buf == NULL) return 0; + /* + * Definitions of the bit-reflected domain constants k1,k2,k3, etc and + * the CRC32+Barrett polynomials given at the end of the paper. + */ + static const uint64_t zalign(16) k1k2[] = { 0x0154442bd4, 0x01c6e41596 }; + static const uint64_t zalign(16) k3k4[] = { 0x01751997d0, 0x00ccaa009e }; + static const uint64_t zalign(16) k5k0[] = { 0x0163cd6124, 0x0000000000 }; + static const uint64_t zalign(16) poly[] = { 0x01db710641, 0x01f7011641 }; + + __m128i x0, x1, x2, x3, x4, x5, x6, x7, x8, y5, y6, y7, y8; + + /* + * There's at least one block of 64. + */ + x1 = _mm_loadu_si128((__m128i *)(buf + 0x00)); + x2 = _mm_loadu_si128((__m128i *)(buf + 0x10)); + x3 = _mm_loadu_si128((__m128i *)(buf + 0x20)); + x4 = _mm_loadu_si128((__m128i *)(buf + 0x30)); + + x1 = _mm_xor_si128(x1, _mm_cvtsi32_si128(crc)); + + x0 = _mm_load_si128((__m128i *)k1k2); + + buf += 64; + len -= 64; - crc = crc ^ 0xffffffffU; - while (len >= 8) { - DO8; - len -= 8; + /* + * Parallel fold blocks of 64, if any. + */ + while (len >= 64) + { + x5 = _mm_clmulepi64_si128(x1, x0, 0x00); + x6 = _mm_clmulepi64_si128(x2, x0, 0x00); + x7 = _mm_clmulepi64_si128(x3, x0, 0x00); + x8 = _mm_clmulepi64_si128(x4, x0, 0x00); + + x1 = _mm_clmulepi64_si128(x1, x0, 0x11); + x2 = _mm_clmulepi64_si128(x2, x0, 0x11); + x3 = _mm_clmulepi64_si128(x3, x0, 0x11); + x4 = _mm_clmulepi64_si128(x4, x0, 0x11); + + y5 = _mm_loadu_si128((__m128i *)(buf + 0x00)); + y6 = _mm_loadu_si128((__m128i *)(buf + 0x10)); + y7 = _mm_loadu_si128((__m128i *)(buf + 0x20)); + y8 = _mm_loadu_si128((__m128i *)(buf + 0x30)); + + x1 = _mm_xor_si128(x1, x5); + x2 = _mm_xor_si128(x2, x6); + x3 = _mm_xor_si128(x3, x7); + x4 = _mm_xor_si128(x4, x8); + + x1 = _mm_xor_si128(x1, y5); + x2 = _mm_xor_si128(x2, y6); + x3 = _mm_xor_si128(x3, y7); + x4 = _mm_xor_si128(x4, y8); + + buf += 64; + len -= 64; } - if (len) do { - DO1; - } while (--len); - return crc ^ 0xffffffffU; + + /* + * Fold into 128-bits. + */ + x0 = _mm_load_si128((__m128i *)k3k4); + + x5 = _mm_clmulepi64_si128(x1, x0, 0x00); + x1 = _mm_clmulepi64_si128(x1, x0, 0x11); + x1 = _mm_xor_si128(x1, x2); + x1 = _mm_xor_si128(x1, x5); + + x5 = _mm_clmulepi64_si128(x1, x0, 0x00); + x1 = _mm_clmulepi64_si128(x1, x0, 0x11); + x1 = _mm_xor_si128(x1, x3); + x1 = _mm_xor_si128(x1, x5); + + x5 = _mm_clmulepi64_si128(x1, x0, 0x00); + x1 = _mm_clmulepi64_si128(x1, x0, 0x11); + x1 = _mm_xor_si128(x1, x4); + x1 = _mm_xor_si128(x1, x5); + + /* + * Single fold blocks of 16, if any. + */ + while (len >= 16) + { + x2 = _mm_loadu_si128((__m128i *)buf); + + x5 = _mm_clmulepi64_si128(x1, x0, 0x00); + x1 = _mm_clmulepi64_si128(x1, x0, 0x11); + x1 = _mm_xor_si128(x1, x2); + x1 = _mm_xor_si128(x1, x5); + + buf += 16; + len -= 16; + } + + /* + * Fold 128-bits to 64-bits. + */ + x2 = _mm_clmulepi64_si128(x1, x0, 0x10); + x3 = _mm_setr_epi32(~0, 0, ~0, 0); + x1 = _mm_srli_si128(x1, 8); + x1 = _mm_xor_si128(x1, x2); + + x0 = _mm_loadl_epi64((__m128i*)k5k0); + + x2 = _mm_srli_si128(x1, 4); + x1 = _mm_and_si128(x1, x3); + x1 = _mm_clmulepi64_si128(x1, x0, 0x00); + x1 = _mm_xor_si128(x1, x2); + + /* + * Barret reduce to 32-bits. + */ + x0 = _mm_load_si128((__m128i*)poly); + + x2 = _mm_and_si128(x1, x3); + x2 = _mm_clmulepi64_si128(x2, x0, 0x10); + x2 = _mm_and_si128(x2, x3); + x2 = _mm_clmulepi64_si128(x2, x0, 0x00); + x1 = _mm_xor_si128(x1, x2); + + /* + * Return the crc32. + */ + return _mm_extract_epi32(x1, 1); } #endif -#ifdef BYFOUR - /* This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit integer pointer type. This violates the strict aliasing rule, where a @@ -533,7 +652,7 @@ uint32_t crc32(crc, buf, len) writes to the buffer that is passed to these routines. */ -#ifdef LITTLE_ENDIAN +#ifdef DNBD3_LITTLE_ENDIAN /* ========================================================================= */ #define DOLIT4 c ^= *buf4++; \ c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ @@ -547,25 +666,36 @@ uint32_t crc32(crc, buf, len) size_t len; { if (buf == NULL) return 0; - register uint32_t c; - register const uint32_t FAR *buf4; + uint32_t c; c = ~crc; - while (len && ((uintptr_t)buf & 3)) { + while (len && ((uintptr_t)buf & PCLMUL_ALIGN_MASK)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } - - buf4 = (const uint32_t FAR *)(const void FAR *)buf; - while (len >= 32) { - DOLIT32; - len -= 32; - } - while (len >= 4) { - DOLIT4; - len -= 4; +#if defined(__x86_64__) || defined(__amd64__) + static atomic_int pclmul = -1; + if (pclmul == -1) { + pclmul = __builtin_cpu_supports("pclmul") && __builtin_cpu_supports("sse4.1"); } - buf = (const uint8_t FAR *)buf4; + if (pclmul && len >= PCLMUL_MIN_LEN) { + c = crc32pclmul(c, buf, len & ~PCLMUL_ALIGN_MASK); + buf += len & ~PCLMUL_ALIGN_MASK; + len &= PCLMUL_ALIGN_MASK; + } else +#endif + do { + const uint32_t *buf4 = (const uint32_t *)(const void *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const uint8_t *)buf4; + } while (0); if (len) do { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); @@ -575,7 +705,7 @@ uint32_t crc32(crc, buf, len) } #endif -#ifdef BIG_ENDIAN +#ifdef DNBD3_BIG_ENDIAN /* ========================================================================= */ #define DOBIG4 c ^= *buf4++; \ c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ @@ -590,7 +720,7 @@ uint32_t crc32(crc, buf, len) { if (buf == NULL) return 0; register uint32_t c; - register const uint32_t FAR *buf4; + register const uint32_t *buf4; c = ~net_order_32(crc); while (len && ((uintptr_t)buf & 3)) { @@ -598,7 +728,7 @@ uint32_t crc32(crc, buf, len) len--; } - buf4 = (const uint32_t FAR *)(const void FAR *)buf; + buf4 = (const uint32_t *)(const void *)buf; while (len >= 32) { DOBIG32; len -= 32; @@ -607,7 +737,7 @@ uint32_t crc32(crc, buf, len) DOBIG4; len -= 4; } - buf = (const uint8_t FAR *)buf4; + buf = (const uint8_t *)buf4; if (len) do { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); @@ -617,5 +747,3 @@ uint32_t crc32(crc, buf, len) } #endif -#endif /* BYFOUR */ - diff --git a/src/shared/crc32.h b/src/shared/crc32.h deleted file mode 100644 index 00b8bdd..0000000 --- a/src/shared/crc32.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _CRC32_H_ -#define _CRC32_H_ - -#include <stdint.h> - -uint32_t crc32(uint32_t crc, const uint8_t *buf, size_t len); - -#endif - diff --git a/src/shared/fdsignal.c b/src/shared/fdsignal.c index 5e5cf7f..1db59bd 100644 --- a/src/shared/fdsignal.c +++ b/src/shared/fdsignal.c @@ -1,6 +1,6 @@ -#include "fdsignal.h" +#include <dnbd3/shared/fdsignal.h> -#if defined(linux) || defined(__linux) || defined(__linux__) +#if defined(__linux__) //#warning "Using eventfd based signalling" #include "fdsignal.inc/eventfd.c" #elif __SIZEOF_INT__ == 4 && __SIZEOF_POINTER__ == 8 diff --git a/src/shared/fdsignal.h b/src/shared/fdsignal.h deleted file mode 100644 index 960a2a9..0000000 --- a/src/shared/fdsignal.h +++ /dev/null @@ -1,57 +0,0 @@ -#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/src/shared/log.c b/src/shared/log.c index 055acb4..3a4739d 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -3,7 +3,7 @@ * * Copyright(c) 2011-2012 Simon Rettberg * - * This file may be licensed under the terms of of the + * This file may be licensed under the terms of the * GNU General Public License Version 2 (the ``GPL''). * * Software distributed under the License is distributed @@ -18,7 +18,7 @@ * */ -#include "log.h" +#include <dnbd3/shared/log.h> #include <stdarg.h> #include <pthread.h> #include <stdlib.h> @@ -36,6 +36,7 @@ static _Atomic logmask_t maskCon = 15; static char *logFile = NULL; static int logFd = -1; +static FILE *logOutStream; static bool consoleTimestamps = false; @@ -43,6 +44,10 @@ static bool consoleTimestamps = false; static int writeLevel(char *buffer, logmask_t level); +void log_init(void) { + logOutStream = stdout; +} + bool log_hasMask(const logmask_t mask) { return ( ( maskFile | maskCon ) & mask ) == mask; @@ -63,6 +68,15 @@ void log_setConsoleTimestamps(bool on) consoleTimestamps = on; } +int log_setConsoleOutputStream(FILE *outputStream) +{ + if ( outputStream != stdout && outputStream != stderr ) + return -EINVAL; + + logOutStream = outputStream; + return 0; +} + bool log_openLogFile(const char *path) { pthread_mutex_lock( &logLock ); @@ -93,10 +107,10 @@ void logadd(const logmask_t mask, const char *fmt, ...) struct tm timeinfo; char buffer[LINE_LEN]; bool toFile = maskFile & mask; - bool toStdout = maskCon & mask; + bool toOutStream = maskCon & mask; size_t offset; - if ( toFile || ( toStdout && consoleTimestamps ) ) { + if ( toFile || ( toOutStream && consoleTimestamps ) ) { time( &rawtime ); localtime_r( &rawtime, &timeinfo ); offset = strftime( buffer, LINE_LEN, "[%d.%m. %H:%M:%S] ", &timeinfo ); @@ -134,15 +148,11 @@ void logadd(const logmask_t mask, const char *fmt, ...) } pthread_mutex_unlock( &logLock ); } - if ( toStdout ) { - if ( consoleTimestamps ) stdoutLine = buffer; -#ifdef AFL_MODE - fputs( stdoutLine, stderr ); - fflush( stderr ); -#else - fputs( stdoutLine, stdout ); - fflush( stdout ); -#endif + if ( toOutStream ) { + if ( consoleTimestamps ) + stdoutLine = buffer; + fputs( stdoutLine, logOutStream ); + fflush( logOutStream ); } } diff --git a/src/shared/log.h b/src/shared/log.h deleted file mode 100644 index 5b1e8f7..0000000 --- a/src/shared/log.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 <stdbool.h> -#include <unistd.h> - -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/src/shared/protocol.h b/src/shared/protocol.h deleted file mode 100644 index 92dbe11..0000000 --- a/src/shared/protocol.h +++ /dev/null @@ -1,159 +0,0 @@ -#ifndef _PROTOCOL_H_ -#define _PROTOCOL_H_ - -#include "sockhelper.h" - -#include "../types.h" -#include "../serialize.h" - -#include <errno.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/uio.h> - -// 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 ( (_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; -#ifdef _DEBUG - request.handle = 0; - request.offset = 0; -#endif - 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/src/shared/serialize.c b/src/shared/serialize.c new file mode 100644 index 0000000..1f7cddd --- /dev/null +++ b/src/shared/serialize.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <dnbd3/shared/serialize.h> +#include <dnbd3/types.h> + +void serializer_reset_read(serialized_buffer_t *buffer, size_t data_len) +{ + buffer->buffer_end = buffer->buffer + MIN(MAX_PAYLOAD, data_len); + buffer->buffer_pointer = buffer->buffer; +} + +void serializer_reset_write(serialized_buffer_t *buffer) +{ + buffer->buffer_end = buffer->buffer + MAX_PAYLOAD; + buffer->buffer_pointer = buffer->buffer; +} + +uint8_t serializer_get_uint8(serialized_buffer_t *buffer) +{ + if (buffer->buffer_pointer + 1 > buffer->buffer_end) + return 0; + return (uint8_t)*buffer->buffer_pointer++; +} + +uint16_t serializer_get_uint16(serialized_buffer_t *buffer) +{ + uint16_t ret; + + if (buffer->buffer_pointer + 2 > buffer->buffer_end) + return 0; + memcpy(&ret, buffer->buffer_pointer, 2); + buffer->buffer_pointer += 2; + return net_order_16(ret); +} + +uint64_t serializer_get_uint64(serialized_buffer_t *buffer) +{ + uint64_t ret; + + if (buffer->buffer_pointer + 8 > buffer->buffer_end) + return 0; + memcpy(&ret, buffer->buffer_pointer, 8); + buffer->buffer_pointer += 8; + return net_order_64(ret); +} + +char *serializer_get_string(serialized_buffer_t *buffer) +{ + char *ptr = buffer->buffer_pointer, *start = buffer->buffer_pointer; + + if (ptr >= buffer->buffer_end) + return NULL; + while (ptr < buffer->buffer_end && *ptr) + ++ptr; + // String did not terminate within buffer (possibly corrupted/malicious packet) + if (*ptr) + return NULL; + buffer->buffer_pointer = ptr + 1; + return start; +} + +void serializer_put_uint8(serialized_buffer_t *buffer, uint8_t value) +{ + if (buffer->buffer_pointer + 1 > buffer->buffer_end) + return; + *buffer->buffer_pointer++ = (char)value; +} + +void serializer_put_uint16(serialized_buffer_t *buffer, uint16_t value) +{ + if (buffer->buffer_pointer + 2 > buffer->buffer_end) + return; + value = net_order_16(value); + memcpy(buffer->buffer_pointer, &value, 2); + buffer->buffer_pointer += 2; +} + +void serializer_put_uint64(serialized_buffer_t *buffer, uint64_t value) +{ + if (buffer->buffer_pointer + 8 > buffer->buffer_end) + return; + value = net_order_64(value); + memcpy(buffer->buffer_pointer, &value, 8); + buffer->buffer_pointer += 8; +} + +void serializer_put_string(serialized_buffer_t *buffer, const char *value) +{ + const size_t len = strlen(value) + 1; + + if (buffer->buffer_pointer + len > buffer->buffer_end) + return; + memcpy(buffer->buffer_pointer, value, len); + buffer->buffer_pointer += len; +} + +uint32_t serializer_get_written_length(serialized_buffer_t *buffer) +{ + return (uint32_t)(buffer->buffer_pointer - buffer->buffer); +} diff --git a/src/shared/sockhelper.c b/src/shared/sockhelper.c index ab34aa1..5096320 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -1,6 +1,8 @@ -#include "sockhelper.h" -#include "log.h" +#include <dnbd3/shared/sockhelper.h> +#include <dnbd3/shared/log.h> +#include <dnbd3/types.h> #include <arpa/inet.h> // inet_ntop +#include <netinet/tcp.h> #include <netdb.h> #include <stdio.h> #include <unistd.h> @@ -19,8 +21,7 @@ struct _poll_list { int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const int rw_ms) { // TODO: Move out of here, this unit should contain general socket functions - // TODO: Abstract away from sockaddr_in* like the rest of the functions here do, - // so WITH_IPV6 can finally be removed as everything is transparent. b- but how? + // TODO: Abstract away from sockaddr_in* like the rest of the functions here struct sockaddr_storage ss; int proto, addrlen; memset( &ss, 0, sizeof ss ); @@ -32,9 +33,7 @@ int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const in addr4->sin_port = addr->port; proto = PF_INET; addrlen = sizeof *addr4; - } -#ifdef WITH_IPV6 - else if ( addr->type == HOST_IP6 ) { + } else if ( addr->type == HOST_IP6 ) { // Set host (IPv6) struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)&ss; addr6->sin6_family = AF_INET6; @@ -42,10 +41,9 @@ int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const in addr6->sin6_port = addr->port; proto = PF_INET6; addrlen = sizeof *addr6; - } -#endif - else { + } else { logadd( LOG_DEBUG1, "Unsupported address type: %d\n", (int)addr->type ); + errno = EAFNOSUPPORT; return -1; } int client_sock = socket( proto, SOCK_STREAM, IPPROTO_TCP ); @@ -56,9 +54,13 @@ int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const in } else { sock_setTimeout( client_sock, connect_ms ); } + // NODELAY makes sense for the client side, which should be all users in this code base + int e2 = 1; + setsockopt( client_sock, IPPROTO_TCP, TCP_NODELAY, (void *)&e2, sizeof(e2) ); for ( int i = 0; i < 5; ++i ) { int ret = connect( client_sock, (struct sockaddr *)&ss, addrlen ); - if ( ret != -1 || errno == EINPROGRESS || errno == EISCONN ) break; + e2 = errno; + if ( ret != -1 || ( connect_ms == -1 && errno == EINPROGRESS ) || errno == EISCONN ) break; if ( errno == EINTR ) { // http://www.madore.org/~david/computers/connect-intr.html #ifdef __linux__ @@ -67,21 +69,26 @@ int sock_connect(const dnbd3_host_t * const addr, const int connect_ms, const in struct pollfd unix_really_sucks = { .fd = client_sock, .events = POLLOUT | POLLIN }; while ( i-- > 0 ) { int pr = poll( &unix_really_sucks, 1, connect_ms == 0 ? -1 : connect_ms ); + e2 = errno; if ( pr == 1 && ( unix_really_sucks.revents & POLLOUT ) ) break; if ( pr == -1 && errno == EINTR ) continue; close( client_sock ); + errno = e2; return -1; } - sockaddr_storage junk; + struct sockaddr_storage junk; socklen_t more_junk = sizeof(junk); if ( getpeername( client_sock, (struct sockaddr*)&junk, &more_junk ) == -1 ) { + e2 = errno; close( client_sock ); + errno = e2; return -1; } break; #endif } // EINTR close( client_sock ); + errno = e2; return -1; } if ( connect_ms != -1 && connect_ms != rw_ms ) { @@ -157,7 +164,7 @@ bool sock_sockaddrToDnbd3(struct sockaddr* sa, dnbd3_host_t *host) memcpy( host->addr, &addr4->sin_addr, 4 ); return true; } -#ifdef WITH_IPV6 + if ( sa->sa_family == AF_INET6 ) { // Set host (IPv6) struct sockaddr_in6 *addr6 = (struct sockaddr_in6*)sa; @@ -166,7 +173,7 @@ bool sock_sockaddrToDnbd3(struct sockaddr* sa, dnbd3_host_t *host) memcpy( host->addr, &addr6->sin6_addr, 16 ); return true; } -#endif + return false; } @@ -234,7 +241,10 @@ size_t sock_printable(const struct sockaddr * const addr, const socklen_t addrLe outlen = snprintf( output, len, "[%s]:%s", host, port ); } } - if ( outlen <= 0 ) return 0; + if ( outlen <= 0 ) { + output[0] = '\0'; + return 0; + } return MIN( (size_t)outlen, len-1 ); } @@ -338,7 +348,7 @@ int sock_multiConnect(poll_list_t* list, const dnbd3_host_t* host, int connect_m if ( i != list->count ) list->entry[i] = list->entry[list->count]; if ( fd != -1 ) { sock_set_block( fd ); - if ( rw_ms != -1 && rw_ms != connect_ms ) { + if ( rw_ms != -1 ) { sock_setTimeout( fd, rw_ms ); } return fd; diff --git a/src/shared/sockhelper.h b/src/shared/sockhelper.h deleted file mode 100644 index 8d70789..0000000 --- a/src/shared/sockhelper.h +++ /dev/null @@ -1,120 +0,0 @@ -#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 "../types.h" -#include <stdint.h> -#include <sys/socket.h> -#include <string.h> - -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/src/shared/timing.c b/src/shared/timing.c index 4ca1002..bdb8388 100644 --- a/src/shared/timing.c +++ b/src/shared/timing.c @@ -1,4 +1,4 @@ -#include "timing.h" +#include <dnbd3/shared/timing.h> #include <errno.h> #include <stdlib.h> #include <stdio.h> diff --git a/src/shared/timing.h b/src/shared/timing.h deleted file mode 100644 index f3d8802..0000000 --- a/src/shared/timing.h +++ /dev/null @@ -1,162 +0,0 @@ -#ifndef _D_TIMING_H -#define _D_TIMING_H - -#ifndef _POSIX_C_SOURCE -#define _POSIX_C_SOURCE 199309L -#endif - -#include <time.h> -#include <stdint.h> -#include <stdbool.h> - -#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 |