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. --- .gitignore | 2 - CMakeLists.txt | 378 +++++++++++------------------------------- Kbuild.in | 2 - build.sh | 6 - cmake/GenerateVersion.cmake | 15 ++ cmake/Kernel.cmake | 57 +++++++ cmake/KernelVersion.cmake | 12 ++ cmake/ProjectVersion.cmake | 27 +++ get-version.sh | 24 --- 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 + pack.sh | 6 - src/CMakeLists.txt | 13 ++ src/bench/CMakeLists.txt | 13 ++ src/bench/connection.c | 10 +- src/bench/connection.h | 2 +- src/bench/helper.h | 2 +- src/bench/main.c | 6 +- src/bench/serialize.c | 5 - src/client/CMakeLists.txt | 14 ++ src/client/client.c | 10 +- src/clientconfig.h | 36 ---- src/config.h | 43 ----- src/fuse/CMakeLists.txt | 14 ++ src/fuse/connection.c | 10 +- src/fuse/connection.h | 4 +- src/fuse/helper.h | 2 +- src/fuse/main.c | 10 +- src/fuse/serialize.c | 5 - src/kernel/CMakeLists.txt | 48 ++++++ src/kernel/Kbuild | 5 + src/kernel/blk.c | 2 +- src/kernel/blk.h | 2 +- src/kernel/core.c | 91 ---------- src/kernel/dnbd3.h | 89 ---------- src/kernel/dnbd3_main.c | 93 +++++++++++ src/kernel/dnbd3_main.h | 88 ++++++++++ src/kernel/net.c | 4 +- src/kernel/net.h | 2 +- src/kernel/serialize.c | 1 + src/kernel/serialize_kmod.c | 5 - src/kernel/sysfs.h | 2 +- src/serialize.c | 84 ---------- src/serialize.h | 40 ----- src/server/CMakeLists.txt | 66 ++++++++ src/server/altservers.c | 6 +- src/server/fuse.c | 10 +- src/server/globals.c | 2 +- src/server/globals.h | 8 +- src/server/helper.h | 4 +- src/server/image.c | 6 +- src/server/locks.c | 4 +- src/server/locks.h | 6 +- src/server/net.c | 16 +- src/server/rpc.c | 12 +- src/server/serialize.c | 5 - src/server/server.c | 18 +- src/server/server.h | 2 +- src/server/threadpool.c | 4 +- src/server/threadpool.h | 2 +- src/server/uplink.c | 14 +- src/server/uplink.h | 2 +- src/serverconfig.h | 62 ------- src/shared/CMakeLists.txt | 15 ++ src/shared/crc32.c | 2 +- src/shared/crc32.h | 9 - src/shared/fdsignal.c | 2 +- src/shared/fdsignal.h | 57 ------- src/shared/log.c | 4 +- src/shared/log.h | 65 -------- src/shared/protocol.h | 157 ------------------ src/shared/serialize.c | 83 ++++++++++ src/shared/sockhelper.c | 20 +-- src/shared/sockhelper.h | 120 -------------- src/shared/timing.c | 2 +- src/shared/timing.h | 162 ------------------ src/types.h | 185 --------------------- src/version.c.in | 3 - src/version.h | 26 --- 89 files changed, 1721 insertions(+), 1673 deletions(-) delete mode 100644 Kbuild.in delete mode 100755 build.sh create mode 100644 cmake/GenerateVersion.cmake create mode 100644 cmake/Kernel.cmake create mode 100644 cmake/KernelVersion.cmake create mode 100644 cmake/ProjectVersion.cmake delete mode 100755 get-version.sh 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 delete mode 100755 pack.sh create mode 100644 src/CMakeLists.txt create mode 100644 src/bench/CMakeLists.txt delete mode 100644 src/bench/serialize.c create mode 100644 src/client/CMakeLists.txt delete mode 100644 src/clientconfig.h delete mode 100644 src/config.h create mode 100644 src/fuse/CMakeLists.txt delete mode 100644 src/fuse/serialize.c create mode 100644 src/kernel/CMakeLists.txt create mode 100644 src/kernel/Kbuild delete mode 100644 src/kernel/core.c delete mode 100644 src/kernel/dnbd3.h create mode 100644 src/kernel/dnbd3_main.c create mode 100644 src/kernel/dnbd3_main.h create mode 120000 src/kernel/serialize.c delete mode 100644 src/kernel/serialize_kmod.c delete mode 100644 src/serialize.c delete mode 100644 src/serialize.h create mode 100644 src/server/CMakeLists.txt delete mode 100644 src/server/serialize.c delete mode 100644 src/serverconfig.h create mode 100644 src/shared/CMakeLists.txt delete mode 100644 src/shared/crc32.h delete mode 100644 src/shared/fdsignal.h delete mode 100644 src/shared/log.h delete mode 100644 src/shared/protocol.h create mode 100644 src/shared/serialize.c delete mode 100644 src/shared/sockhelper.h delete mode 100644 src/shared/timing.h delete mode 100644 src/types.h delete mode 100644 src/version.c.in delete mode 100644 src/version.h diff --git a/.gitignore b/.gitignore index 38ae262..6617c58 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,3 @@ build/ *.swp .autotools .idea -/version.txt - diff --git a/CMakeLists.txt b/CMakeLists.txt index 079fd9b..a1a91aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,279 +1,103 @@ -################################################################################ -# GENERAL # -################################################################################ - -PROJECT(dnbd3 C) -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12) -IF (CMAKE_BUILD_TYPE STREQUAL "") - SET(CMAKE_BUILD_TYPE Debug) -ENDIF() - -SET(CMAKE_INSTALL_PREFIX "/usr/local" CACHE PATH "Path prefix for system installation") -OPTION(BUILD_FUSE_CLIENT "Build dnbd3 fuse client" ON) -OPTION(BUILD_SERVER "Build dnbd3 server" ON) -OPTION(BUILD_SERVER_FUSE "Enable FUSE-Integration for dnbd3 server" OFF) -OPTION(BUILD_STRESSTEST "Build dnbd3 stress testing tool" OFF) -SET(EXTRA_C_FLAGS "" CACHE STRING "Additional options to pass to compiler") -OPTION(DEBUG_LOCKS "Add lock debugging code to dnbd3-server" OFF) -OPTION(DEBUG_THREADS "Add thread debugging code to dnbd3-server" OFF) - -OPTION(SERVER_FOR_AFL "Build dnbd3-server for usage with afl-fuzz" OFF) - -# Is there a non-retarded way to check if build type is debug or release? -# When specifying, it is case insensitive, so DeBuG would also enable debug builds, -# but in cmake, we can only do case sensitive matches... :/ -string( TOLOWER "${CMAKE_BUILD_TYPE}" bt_lower ) -if (NOT bt_lower MATCHES "^(debug|release)$") - message( FATAL_ERROR "Build type needs to be either Debug or Release" ) -endif() - -message( "Build Type selected: ${CMAKE_BUILD_TYPE}" ) - -IF(CMAKE_SYSTEM_NAME MATCHES "BSD") - message("Detected *BSD System: disable build of Kernel Module.") - SET(BUILD_KERNEL_MODULE False) -ELSE() - OPTION(BUILD_KERNEL_MODULE "Build the dnbd3 Linux kernel module" ON) -ENDIF() - -INCLUDE(CheckCCompilerFlag) -macro (TRY_ADD_FLAG _FLAG) - UNSET(TMP_TEST CACHE) - CHECK_C_COMPILER_FLAG("${_FLAG}" TMP_TEST) - if (TMP_TEST) - message(":-) Compiler supports ${_FLAG}") - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FLAG}") - else() - message(":-( Compiler does not support ${_FLAG}") - endif() - -endmacro() - -# Common for gcc and clang -#SET(CMAKE_EXE_LINKER_FLAGS "-Wl,-z,relro,-z,now,-z,defs -pie") -SET(CMAKE_C_FLAGS_RELEASE "-DNDEBUG") -SET(CMAKE_C_FLAGS "-O2 -std=c11 -Wno-multichar -fno-strict-aliasing -D_GNU_SOURCE -D_FORTIFY_SOURCE=2 ${EXTRA_C_FLAGS}") -# Hardening. Try as much as is possible. -#TRY_ADD_FLAG("-mmitigate-rop") -#TRY_ADD_FLAG("-fstack-protector-strong") -#TRY_ADD_FLAG("-fstack-clash-protection") -#TRY_ADD_FLAG("-Wformat") -#TRY_ADD_FLAG("-Wformat-security") -#TRY_ADD_FLAG("-Werror=format-security") -if(CMAKE_C_COMPILER MATCHES "clang") - message( "Using clang flags." ) - SET(CMAKE_C_FLAGS_DEBUG " -Og -fno-omit-frame-pointer -g -Wall -Wextra -Wpedantic -Wno-unused-result -D_DEBUG") -elseif (CMAKE_C_COMPILER MATCHES "(cc-)|(cc$)") - message( "Using (g)cc flags." ) - SET(CMAKE_C_FLAGS_DEBUG " -Og -g -Wall -Wextra -Wpedantic -Wconversion -Wno-sign-conversion -D_DEBUG") -else() - message( FATAL_ERROR "Could not determine compiler type." ) -endif() - -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") - -ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64) -ADD_DEFINITIONS(-DWITH_IPV6) -ADD_DEFINITIONS(-DBUILD_TYPE=${CMAKE_BUILD_TYPE}) -if (DEBUG_LOCKS) - ADD_DEFINITIONS(-DDEBUG_LOCKS) -ENDIF() -if (DEBUG_THREADS) - ADD_DEFINITIONS(-DDEBUG_THREADS) -ENDIF() - -FIND_PACKAGE(Threads) -if(BUILD_SERVER_FUSE OR BUILD_FUSE_CLIENT) - FIND_PACKAGE(Fuse) - message( " FUSE: ${FUSE_VERSION} " ) -endif() - -SET(DO_ABORT False) - -message( " *************************************************" ) -if(BUILD_FUSE_CLIENT) - if(NOT FUSE_FOUND) - message( " *** No fuse dev libs found, can't build dnbd3-fuse" ) - SET(DO_ABORT True) - endif() - if(NOT THREADS_FOUND) - message( " *** No threads found, can't build dnbd3-fuse" ) - SET(DO_ABORT True) - endif() -endif() -if(BUILD_SERVER) - FIND_PACKAGE(Jansson) - if(NOT THREADS_FOUND) - message( " *** No threads found, can't build dnbd3-server" ) - SET(DO_ABORT True) - endif() - if(NOT JANSSON_FOUND) - message( " *** No jansson lib found, can't build dnbd3-server" ) - SET(DO_ABORT True) - endif() - if(BUILD_SERVER_FUSE AND NOT FUSE_FOUND) - message( " *** No fuse dev libs found, can't build dnbd3-server with fuse support" ) - SET(DO_ABORT True) - endif() -endif() -if(BUILD_STRESSTEST) - if(NOT THREADS_FOUND) - message( " *** No threads found, can't build dnbd3-bench" ) - SET(DO_ABORT True) - endif() -endif() -message( " *************************************************" ) -if(DO_ABORT) - message( FATAL_ERROR "Aborting." ) -endif() - -#SET(FUSE_INCLUDE_DIR "") -#SET(JANSSON_INCLUDE_DIR "") - -################################################################################ -# VERSION HEADER # -################################################################################ - -FILE(WRITE ${CMAKE_BINARY_DIR}/version.cmake -"EXECUTE_PROCESS( - COMMAND \${CMD} - OUTPUT_VARIABLE VERSION - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - CONFIGURE_FILE(\${SRC} \${DST} @ONLY) -") -ADD_CUSTOM_COMMAND(OUTPUT fake-file.c ${CMAKE_BINARY_DIR}/generated/version.c - COMMAND - ${CMAKE_COMMAND} -D SRC=${CMAKE_SOURCE_DIR}/src/version.c.in - -D DST=${CMAKE_BINARY_DIR}/generated/version.c - -D CMD=${CMAKE_SOURCE_DIR}/get-version.sh - -P ${CMAKE_BINARY_DIR}/version.cmake -) -ADD_CUSTOM_TARGET(version ALL DEPENDS fake-file.c) - -################################################################################ -# CLIENT # -################################################################################ - -if(BUILD_KERNEL_MODULE) - FILE(GLOB_RECURSE CLIENT_SRCS src/client/*.c) - ADD_EXECUTABLE(dnbd3-client ${CMAKE_BINARY_DIR}/generated/version.c ${CLIENT_SRCS}) - TARGET_LINK_LIBRARIES(dnbd3-client) - ADD_DEPENDENCIES(dnbd3-client version) - INSTALL(TARGETS dnbd3-client RUNTIME DESTINATION sbin) -ENDIF() - - -################################################################################ -# SERVER # -################################################################################ - -if(BUILD_SERVER) - IF(SERVER_FOR_AFL) - message(" ######################## Building server for AFL mode - will be useless otherwise!") - ADD_DEFINITIONS(-DAFL_MODE) - ENDIF() - FILE(GLOB SERVER_SRCS src/server/*.c src/shared/*.c src/server/picohttpparser/*.c) - ADD_EXECUTABLE(dnbd3-server ${CMAKE_BINARY_DIR}/generated/version.c ${SERVER_SRCS}) - TARGET_INCLUDE_DIRECTORIES(dnbd3-server PRIVATE ${JANSSON_INCLUDE_DIR}) - TARGET_LINK_LIBRARIES(dnbd3-server ${CMAKE_THREAD_LIBS_INIT} ${JANSSON_LIBRARIES}) - if(BUILD_SERVER_FUSE) - TARGET_LINK_LIBRARIES(dnbd3-server ${FUSE_LIBRARIES}) - ADD_DEFINITIONS(-DBUILD_SERVER_FUSE) - TARGET_INCLUDE_DIRECTORIES(dnbd3-server PRIVATE ${FUSE_INCLUDE_DIRS}) - endif() - if(UNIX AND NOT APPLE) - target_link_libraries(dnbd3-server rt) - endif() - ADD_DEPENDENCIES(dnbd3-server version) - INSTALL(TARGETS dnbd3-server RUNTIME DESTINATION sbin) +cmake_minimum_required(VERSION 3.10) + +# include CMake macros +set(PROJECT_MODULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + ${PROJECT_MODULES_DIR}) + +# define root CMake project +project(dnbd3 + DESCRIPTION "dnbd3 Linux kernel module, server, clients and utilities" + LANGUAGES C) + +# define project options to define build configuration +OPTION(DNBD3_KERNEL_MODULE "Build the dnbd3 Linux kernel module" ON) +OPTION(DNBD3_SERVER_FUSE "Enable FUSE-Integration for dnbd3-server" OFF) +OPTION(DNBD3_SERVER_AFL "Build dnbd3-server for usage with afl-fuzz" OFF) +OPTION(DNBD3_SERVER_DEBUG_LOCKS "Add lock debugging code to dnbd3-server" OFF) +OPTION(DNBD3_SERVER_DEBUG_THREADS "Add thread debugging code to dnbd3-server" OFF) +OPTION(DNBD3_RELEASE_HARDENING "Harden dnbd3 programs in Release build configuration" OFF) + +# set supported build configurations +set(CMAKE_CONFIGURATION_TYPES Debug Release) + +# set compilation in debug mode as default configuration +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) + message(STATUS "Build type is not set. Defaulting to ${CMAKE_BUILD_TYPE} build!") endif() - - -################################################################################ -# FUSE # -################################################################################ - -if(BUILD_FUSE_CLIENT) - FILE(GLOB FUSE_SRCS src/fuse/*.c src/shared/*.c) - ADD_EXECUTABLE(dnbd3-fuse ${FUSE_SRCS}) - TARGET_INCLUDE_DIRECTORIES(dnbd3-fuse PRIVATE ${FUSE_INCLUDE_DIRS}) - TARGET_LINK_LIBRARIES(dnbd3-fuse ${CMAKE_THREAD_LIBS_INIT} ${FUSE_LIBRARIES}) - ADD_DEPENDENCIES(dnbd3-fuse version) - INSTALL(TARGETS dnbd3-fuse RUNTIME DESTINATION bin) -endif() - -################################################################################ -# STRESSTEST # -################################################################################ - -if(BUILD_STRESSTEST) - FILE(GLOB BENCH_SRCS src/bench/*.c src/shared/*.c) - ADD_EXECUTABLE(dnbd3-bench ${BENCH_SRCS}) - TARGET_LINK_LIBRARIES(dnbd3-bench ${CMAKE_THREAD_LIBS_INIT}) - ADD_DEPENDENCIES(dnbd3-bench version) - INSTALL(TARGETS dnbd3-bench RUNTIME DESTINATION bin) -endif() - -################################################################################ -# MODULE # -################################################################################ - -IF(BUILD_KERNEL_MODULE) - SET(MODULE_NAME dnbd3) - SET(MODULE_FILE ${MODULE_NAME}.ko) - FILE(GLOB MODULE_SOURCE_FILES src/kernel/*.c src/serialize.c) - FILE(GLOB MODULE_HEADER_FILES src/kernel/*.h src/*.h) - - SET(KERNEL_DIR "" CACHE PATH "Path to kernel sources to compile against") - IF(KERNEL_DIR STREQUAL "") - SET(KERNEL_DIR "/lib/modules/${CMAKE_SYSTEM_VERSION}/build") - ENDIF() - - SET(KERNEL_C_FLAGS "" CACHE STRING "Additional C flags to be used for building the kernel module") - IF(CMAKE_BUILD_TYPE MATCHES Debug) - SET(KERNEL_C_FLAGS "-g -DDEBUG") - ENDIF() - - SET(KBUILD_COMMAND ${CMAKE_MAKE_PROGRAM} -C ${KERNEL_DIR} - M=${CMAKE_BINARY_DIR} modules - EXTRA_CFLAGS=${KERNEL_C_FLAGS} - ) - - CONFIGURE_FILE(Kbuild.in ${CMAKE_BINARY_DIR}/Kbuild) - - FOREACH(MODULE_SOURCE_FILE ${MODULE_SOURCE_FILES}) - CONFIGURE_FILE(${MODULE_SOURCE_FILE} ${CMAKE_BINARY_DIR} COPYONLY) - ENDFOREACH( MODULE_SOURCE_FILE ) - - FOREACH(MODULE_HEADER_FILE ${MODULE_HEADER_FILES}) - CONFIGURE_FILE(${MODULE_HEADER_FILE} ${CMAKE_BINARY_DIR} COPYONLY) - ENDFOREACH( MODULE_HEADER_FILE ) - - ADD_CUSTOM_COMMAND( - OUTPUT ${CMAKE_BINARY_DIR}/${MODULE_FILE} - COMMAND ${KBUILD_COMMAND} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS ${MODULE_SOURCE_FILES} Kbuild.in - VERBATIM - ) - - ADD_CUSTOM_TARGET(${MODULE_NAME} ALL DEPENDS ${CMAKE_BINARY_DIR}/${MODULE_FILE}) - - INSTALL(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.ko - DESTINATION /lib/modules/${CMAKE_SYSTEM_VERSION}/kernel/drivers/block - PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ - ) - - INSTALL(CODE "EXECUTE_PROCESS(COMMAND depmod -a)") -ENDIF() - - -# -# Other install files -# - -FILE(GLOB conf_files "${CMAKE_CURRENT_SOURCE_DIR}/conf/*") -INSTALL(FILES ${conf_files} DESTINATION /etc/dnbd3-server/sample/) - +# disable build of the dnbd3 Linux kernel module on a BSD system +if(CMAKE_SYSTEM_NAME MATCHES "BSD") + message(STATUS "Detected BSD System: Disable build of the dnbd3 Linux kernel module") + set(DNBD3_KERNEL_MODULE OFF) +endif(CMAKE_SYSTEM_NAME MATCHES "BSD") + +# set Linux kernel directories +set(KERNEL_BUILD_DIR "/lib/modules/${CMAKE_SYSTEM_VERSION}/build" + CACHE PATH "Path to Linux kernel modules to compile against") +set(KERNEL_INSTALL_DIR "/lib/modules/${CMAKE_SYSTEM_VERSION}/extra" + CACHE PATH "Path to install Linux kernel modules") + +# search for required packages +find_package(Git) +find_package(Threads) +find_package(Fuse) + +# abort if a required package is not available +if(NOT GIT_FOUND) + message(FATAL_ERROR "No Git found, can't determine dnbd3 project version number!") +endif(NOT GIT_FOUND) +if(NOT THREADS_FOUND) + message(FATAL_ERROR "No threads found, can't build dnbd3 project!") +endif(NOT THREADS_FOUND) +if(NOT FUSE_FOUND) + message(FATAL_ERROR "No Fuse found, can't build dnbd3 project!") +endif(NOT FUSE_FOUND) + +# include project version related macros +include(KernelVersion) +# include project version related macros +include(ProjectVersion) + +# get Linux kernel version from path +get_kernel_version(LINUX_KERNEL_VERSION ${KERNEL_BUILD_DIR}) + +# set include directories +set(PROJECT_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc) +set(PROJECT_INCLUDE_TMP_DIR ${CMAKE_CURRENT_BINARY_DIR}/inc) +include_directories(${PROJECT_INCLUDE_DIR}) + +# generate project version C header file from template +# exposes dnbd3-generate-version and dnbd3-version target +gen_project_version(${PROJECT_INCLUDE_DIR}/dnbd3/version.h.in ${PROJECT_INCLUDE_TMP_DIR}/dnbd3/version.h) + +# print configured settings +message(STATUS "Path to Linux kernel modules to compile against is " ${KERNEL_BUILD_DIR}) +message(STATUS "Path to install Linux kernel modules is " ${KERNEL_INSTALL_DIR}) + +# add compile option to handle files greater than 2GB on a 32bit system +add_definitions(-D_FILE_OFFSET_BITS=64) + +# define global C flags for compilation +set(CMAKE_C_FLAGS "-std=c11") + +# enable all error warnings in Debug build configuration +set(CMAKE_C_FLAGS_DEBUG "-Wall -Wextra -Wpedantic -Wconversion -Wformat -Wformat-security -Werror=format-security") +set(CMAKE_C_FLAGS_RELEASE "-Wno-error") + +# set compilation optimization +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -Og -DDEBUG") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -DNDEBUG") + +if(DNBD3_RELEASE_HARDENING) + # harden Release builds with specific C flags + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2 -fstack-protector-strong -fstack-clash-protection -mfunction-return=thunk -mindirect-branch=thunk") +endif(DNBD3_RELEASE_HARDENING) + +# add all dnbd3 related projects from the source code directory +add_subdirectory(src) + +# install config files into sample directory +file(GLOB DNBD3_CONF_FILES "${CMAKE_CURRENT_SOURCE_DIR}/conf/*") +install(FILES ${DNBD3_CONF_FILES} DESTINATION /etc/dnbd3-server/sample) diff --git a/Kbuild.in b/Kbuild.in deleted file mode 100644 index 667cee0..0000000 --- a/Kbuild.in +++ /dev/null @@ -1,2 +0,0 @@ -obj-m := ${MODULE_NAME}.o -${MODULE_NAME}-objs += core.o blk.o net.o sysfs.o utils.o serialize_kmod.o \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100755 index 6726a86..0000000 --- a/build.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -mkdir -p build -cd build/ -cmake .. -make diff --git a/cmake/GenerateVersion.cmake b/cmake/GenerateVersion.cmake new file mode 100644 index 0000000..e336489 --- /dev/null +++ b/cmake/GenerateVersion.cmake @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2020 Manuel Bentele +# + +# get Git short hash and tag of latest repository commit +execute_process(COMMAND git describe HEAD + OUTPUT_VARIABLE DNBD3_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE) +if(DNBD3_VERSION STREQUAL "") + set(DNBD3_VERSION "unknown") +endif(DNBD3_VERSION STREQUAL "") + +# write dnbd3 version into a new C source file based on the specified version template +configure_file(${VERSION_INPUT_FILE} ${VERSION_OUTPUT_FILE}) diff --git a/cmake/Kernel.cmake b/cmake/Kernel.cmake new file mode 100644 index 0000000..232c298 --- /dev/null +++ b/cmake/Kernel.cmake @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# CMake macros to build and install Linux kernel modules +# Copyright (C) 2020 Manuel Bentele +# + +# macro to define kernel module targets +macro(add_kernel_module MODULE_NAME KERNEL_BUILD_DIR KERNEL_INSTALL_DIR MODULE_MACRO MODULE_SOURCE_FILES MODULE_HEADER_FILES BUILD_SOURCE_FILE) + # create directory for kernel module + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}) + # copy build source file + get_filename_component(BUILD_SOURCE_FILENAME ${BUILD_SOURCE_FILE} NAME) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${BUILD_SOURCE_FILENAME} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BUILD_SOURCE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME} + DEPENDS ${BUILD_SOURCE_FILE}) + set(BUILD_SOURCE_FILE_PREPARED ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${BUILD_SOURCE_FILENAME}) + # copy source files + foreach(MODULE_SOURCE_FILE ${MODULE_SOURCE_FILES}) + get_filename_component(MODULE_SOURCE_FILENAME ${MODULE_SOURCE_FILE} NAME) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_SOURCE_FILENAME} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MODULE_SOURCE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME} + DEPENDS ${MODULE_SOURCE_FILE}) + set(MODULE_SOURCE_FILES_PREPARED ${MODULE_SOURCE_FILES_PREPARED} + ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_SOURCE_FILENAME}) + endforeach() + # copy header files + foreach(MODULE_HEADER_FILE ${MODULE_HEADER_FILES}) + get_filename_component(MODULE_HEADER_FILENAME ${MODULE_HEADER_FILE} NAME) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_HEADER_FILENAME} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${MODULE_HEADER_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME} + DEPENDS ${MODULE_HEADER_FILE}) + set(MODULE_HEADER_FILES_PREPARED ${MODULE_HEADER_FILES_PREPARED} + ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_HEADER_FILENAME}) + endforeach() + # check if module depends on another module + if(NOT ${ARGV7} STREQUAL "") + set(MODULE_EXTRA_SYMBOLS ${CMAKE_CURRENT_BINARY_DIR}/${ARGV7}/Module.symvers) + endif() + # define build command + set(MODULE_BUILD_COMMAND ${CMAKE_MAKE_PROGRAM} ${MODULE_MACRO} + -C ${KERNEL_BUILD_DIR} + M=${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME} modules + EXTRA_CFLAGS=${KERNEL_C_FLAGS} + KBUILD_EXTRA_SYMBOLS=${MODULE_EXTRA_SYMBOLS}) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_NAME}.ko + COMMAND ${MODULE_BUILD_COMMAND} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME} + COMMENT "Build kernel module ${MODULE_NAME}" + DEPENDS ${BUILD_SOURCE_FILE_PREPARED} ${MODULE_HEADER_FILES_PREPARED} ${MODULE_SOURCE_FILES_PREPARED} + VERBATIM) + add_custom_target(${MODULE_NAME} ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_NAME}.ko ${ARGV7}) + # install kernel module + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_NAME}/${MODULE_NAME}.ko + DESTINATION ${KERNEL_INSTALL_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + COMPONENT kernel) +endmacro(add_kernel_module) diff --git a/cmake/KernelVersion.cmake b/cmake/KernelVersion.cmake new file mode 100644 index 0000000..1ded072 --- /dev/null +++ b/cmake/KernelVersion.cmake @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2020 Manuel Bentele +# + +# get Linux kernel version from KERNEL_BUILD_DIR string +macro(get_kernel_version LINUX_KERNEL_VERSION KERNEL_BUILD_DIR) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LINUX_KERNEL_VERSION ${KERNEL_BUILD_DIR}) + if(LINUX_KERNEL_VERSION STREQUAL "") + set(LINUX_KERNEL_VERSION "unknown") + endif(LINUX_KERNEL_VERSION STREQUAL "") +endmacro(get_kernel_version LINUX_KERNEL_VERSION KERNEL_BUILD_DIR) diff --git a/cmake/ProjectVersion.cmake b/cmake/ProjectVersion.cmake new file mode 100644 index 0000000..108505d --- /dev/null +++ b/cmake/ProjectVersion.cmake @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (C) 2020 Manuel Bentele +# + +macro(gen_project_version VERSION_INPUT_FILE VERSION_OUTPUT_FILE) + get_filename_component(VERSION_OUTPUT_FILENAME ${VERSION_OUTPUT_FILE} NAME) + # command that will trigger a rebuild of version.c every time + add_custom_command(OUTPUT regenerate-version-file + COMMAND ${CMAKE_COMMAND} -E sleep 0 + COMMENT "Trigger generating ${VERSION_OUTPUT_FILENAME}") + + # call the GenerateVersion.cmake file to generate the version.c file + add_custom_command(OUTPUT ${VERSION_OUTPUT_FILE} + COMMAND ${CMAKE_COMMAND} -D VERSION_INPUT_FILE=${VERSION_INPUT_FILE} + -D VERSION_OUTPUT_FILE=${VERSION_OUTPUT_FILE} + -P ${PROJECT_MODULES_DIR}/GenerateVersion.cmake + COMMENT "Generating ${VERSION_OUTPUT_FILENAME}" + DEPENDS regenerate-version-file) + add_custom_target(dnbd3-generate-version DEPENDS ${VERSION_OUTPUT_FILE}) + + # create target to expose project version + add_library(dnbd3-version INTERFACE) + target_include_directories(dnbd3-version INTERFACE ${PROJECT_INCLUDE_TMP_DIR}) + add_dependencies(dnbd3-version dnbd3-generate-version) + +endmacro(gen_project_version VERSION_INPUT_FILE VERSION_OUTPUT_FILE) diff --git a/get-version.sh b/get-version.sh deleted file mode 100755 index 49c7024..0000000 --- a/get-version.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh - -# Always create version string for repository this script lies in, -# not the cwd... Makes usage easier in cmake -ARG0="$0" -SELF="$(readlink -f "${ARG0}")" -ROOT_DIR="$(dirname "${SELF}")" -cd "$ROOT_DIR" - -if [ -d .git ]; then - [ -n "$(git diff HEAD)" ] && MODDED='+MOD' - name="$( git describe )" - [ -z "$name" ] && name="$( git rev-parse --short HEAD )" - printf "%s\n" "$name$MODDED, branch $(git rev-parse --abbrev-ref HEAD), built $(date +%Y-%m-%d)" - exit 0 -fi - -if [ -f "version.txt" ]; then - cat "version.txt" - exit 0 -fi - -echo "-unknown-" - 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_ */ diff --git a/pack.sh b/pack.sh deleted file mode 100755 index 9cbe5c4..0000000 --- a/pack.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -./get-version.sh > version.txt -tar ckzf dnbd3.tar.gz src cmake CMakeLists.txt get-version.sh version.txt -rm -- version.txt - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..6093164 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-src) + +add_subdirectory(bench) +add_subdirectory(client) +add_subdirectory(fuse) +if(DNBD3_KERNEL_MODULE) + add_subdirectory(kernel) +endif(DNBD3_KERNEL_MODULE) +add_subdirectory(server) +add_subdirectory(shared) diff --git a/src/bench/CMakeLists.txt b/src/bench/CMakeLists.txt new file mode 100644 index 0000000..d8a5dc9 --- /dev/null +++ b/src/bench/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-bench) + +# add compile option to enable enhanced POSIX pthread features +add_compile_options(-D_GNU_SOURCE) + +add_executable(dnbd3-bench ${CMAKE_CURRENT_SOURCE_DIR}/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/helper.c + ${CMAKE_CURRENT_SOURCE_DIR}/main.c) +target_link_libraries(dnbd3-bench dnbd3-version dnbd3-shared ${CMAKE_THREAD_LIBS_INIT}) +install(TARGETS dnbd3-bench RUNTIME DESTINATION bin) diff --git a/src/bench/connection.c b/src/bench/connection.c index 65f1757..974bc8a 100644 --- a/src/bench/connection.c +++ b/src/bench/connection.c @@ -1,10 +1,10 @@ #include "connection.h" #include "helper.h" -#include "../config.h" -#include "../shared/protocol.h" -#include "../shared/fdsignal.h" -#include "../shared/sockhelper.h" -#include "../shared/log.h" +#include +#include +#include +#include +#include #include #include diff --git a/src/bench/connection.h b/src/bench/connection.h index 770bf0d..422c93e 100644 --- a/src/bench/connection.h +++ b/src/bench/connection.h @@ -1,7 +1,7 @@ #ifndef _CONNECTION_H_ #define _CONNECTION_H_ -#include "../shared/fdsignal.h" +#include #include #include #include "helper.h" diff --git a/src/bench/helper.h b/src/bench/helper.h index e0c0262..53f32bf 100644 --- a/src/bench/helper.h +++ b/src/bench/helper.h @@ -1,7 +1,7 @@ #ifndef IMAGEHELPER_H #define IMAGEHELPER_H -#include "../types.h" +#include #include #include diff --git a/src/bench/main.c b/src/bench/main.c index f8c55c3..89c09f3 100644 --- a/src/bench/main.c +++ b/src/bench/main.c @@ -4,8 +4,9 @@ #include "connection.h" #include "helper.h" -#include "../shared/protocol.h" -#include "../shared/log.h" +#include +#include +#include #include #include @@ -19,6 +20,7 @@ static void printUsage(char *argv0, int exitCode) { + printf( "Version: %s\n", DNBD3_VERSION ); printf( "Usage: %s [--debug] --host --image [--rid revision]\n", argv0 ); printf( "Or: %s [-d] -h -i [-r revision]\n", argv0 ); printf( " -h --host List of space separated hosts to use\n" ); diff --git a/src/bench/serialize.c b/src/bench/serialize.c deleted file mode 100644 index 4934132..0000000 --- a/src/bench/serialize.c +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include -#include - -#include "../serialize.c" diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt new file mode 100644 index 0000000..a7c1eb4 --- /dev/null +++ b/src/client/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-client) + +# suppress -Wmultichar warnings +add_compile_options(-Wno-multichar) + +# add compile option to enable enhanced BSD netdb features +add_compile_options(-D_GNU_SOURCE) + +add_executable(dnbd3-client ${CMAKE_CURRENT_SOURCE_DIR}/client.c) +target_link_libraries(dnbd3-client dnbd3-version dnbd3-shared) +install(TARGETS dnbd3-client RUNTIME DESTINATION bin) diff --git a/src/client/client.c b/src/client/client.c index 37f0558..33737fa 100644 --- a/src/client/client.c +++ b/src/client/client.c @@ -18,9 +18,9 @@ * */ -#include "../clientconfig.h" -#include "../types.h" -#include "../version.h" +#include +#include +#include #include #include @@ -643,7 +643,7 @@ static int dnbd3_daemon_send(int argc, char **argv) static void dnbd3_print_help(char *argv_0) { - printf( "Version: %s\n\n", VERSION_STRING ); + printf( "Version: %s\n", DNBD3_VERSION ); printf( "\nUsage: %s\n" "\t-h -i [-r ] -d [-a ] || -c -d \n\n", argv_0 ); printf( "Start the DNBD3 client.\n" ); @@ -665,6 +665,6 @@ static void dnbd3_print_help(char *argv_0) void dnbd3_print_version() { - printf( "Version: %s\n", VERSION_STRING ); + printf( "dnbd3-client version: %s\n", DNBD3_VERSION ); exit( EXIT_SUCCESS ); } diff --git a/src/clientconfig.h b/src/clientconfig.h deleted file mode 100644 index f35f673..0000000 --- a/src/clientconfig.h +++ /dev/null @@ -1,36 +0,0 @@ -#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/src/config.h b/src/config.h deleted file mode 100644 index 50336af..0000000 --- a/src/config.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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/src/fuse/CMakeLists.txt b/src/fuse/CMakeLists.txt new file mode 100644 index 0000000..367356a --- /dev/null +++ b/src/fuse/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-fuse) + +# add compile option to enable enhanced POSIX pthread features +add_compile_options(-D_GNU_SOURCE) + +add_executable(dnbd3-fuse ${CMAKE_CURRENT_SOURCE_DIR}/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/helper.c + ${CMAKE_CURRENT_SOURCE_DIR}/main.c) +target_include_directories(dnbd3-fuse PRIVATE ${FUSE_INCLUDE_DIRS}) +target_link_libraries(dnbd3-fuse dnbd3-version dnbd3-shared ${FUSE_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +install(TARGETS dnbd3-fuse RUNTIME DESTINATION bin) diff --git a/src/fuse/connection.c b/src/fuse/connection.c index 7bd8018..25ea219 100644 --- a/src/fuse/connection.c +++ b/src/fuse/connection.c @@ -1,10 +1,10 @@ #include "connection.h" #include "helper.h" -#include "../clientconfig.h" -#include "../shared/protocol.h" -#include "../shared/fdsignal.h" -#include "../shared/sockhelper.h" -#include "../shared/log.h" +#include +#include +#include +#include +#include #include #include diff --git a/src/fuse/connection.h b/src/fuse/connection.h index be0115e..dde6da9 100644 --- a/src/fuse/connection.h +++ b/src/fuse/connection.h @@ -1,8 +1,8 @@ #ifndef _CONNECTION_H_ #define _CONNECTION_H_ -#include "../shared/fdsignal.h" -#include "../shared/timing.h" +#include +#include #include #include #include diff --git a/src/fuse/helper.h b/src/fuse/helper.h index 65cca2c..b1fa513 100644 --- a/src/fuse/helper.h +++ b/src/fuse/helper.h @@ -1,7 +1,7 @@ #ifndef IMAGEHELPER_H #define IMAGEHELPER_H -#include "../types.h" +#include #include #include diff --git a/src/fuse/main.c b/src/fuse/main.c index f303c9c..cf9efbe 100644 --- a/src/fuse/main.c +++ b/src/fuse/main.c @@ -10,11 +10,12 @@ #include "connection.h" #include "helper.h" -#include "../shared/protocol.h" -#include "../shared/log.h" +#include +#include +#include #define FUSE_USE_VERSION 30 -#include "../config.h" +#include #include #include #include @@ -272,7 +273,8 @@ static struct fuse_lowlevel_ops image_oper = { static void printVersion() { char *arg[] = { "foo", "-V" }; - printf( "DNBD3-Fuse Version 1.2.3.4, protocol version %d\n", (int)PROTOCOL_VERSION ); + printf( "dnbd3-fuse version: %s\n", DNBD3_VERSION ); + printf( "Protocol version: %d\n", (int)PROTOCOL_VERSION ); struct fuse_args args = FUSE_ARGS_INIT( 2, arg ); fuse_parse_cmdline( &args, NULL, NULL, NULL ); exit( 0 ); diff --git a/src/fuse/serialize.c b/src/fuse/serialize.c deleted file mode 100644 index 4934132..0000000 --- a/src/fuse/serialize.c +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include -#include - -#include "../serialize.c" diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt new file mode 100644 index 0000000..bf1eb3a --- /dev/null +++ b/src/kernel/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-kernel-module) + +# include macros to define Linux kernel build targets +include(Kernel) + +# set C flags for a Linux kernel module +set(KERNEL_C_FLAGS "-DDNBD3_KERNEL_MODULE -I ${PROJECT_INCLUDE_TMP_DIR}" + CACHE STRING "C flags to be used for building the dnbd3 kernel module") +# set C flags for the debug mode of a Linux kernel module +set(KERNEL_C_FLAGS_DEBUG "-g -DDEBUG" + CACHE STRING "Additional C flags to be used for building the dnbd3 kernel module in debug mode") + +# append include directories to the C flags +get_property(KERNEL_INCLUDE_DIRS DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES) +foreach(KERNEL_INCLUDE_DIR ${KERNEL_INCLUDE_DIRS}) + set(KERNEL_C_FLAGS "${KERNEL_C_FLAGS} -I ${KERNEL_INCLUDE_DIR}") +endforeach(KERNEL_INCLUDE_DIR ${KERNEL_INCLUDE_DIRS}) + +# append debug C flags if debug mode is enabled +if(CMAKE_BUILD_TYPE MATCHES Debug) + set(KERNEL_C_FLAGS "${KERNEL_C_FLAGS} ${KERNEL_C_FLAGS_DEBUG}") +endif(CMAKE_BUILD_TYPE MATCHES Debug) + +# dnbd3 Linux kernel module +set(KERNEL_MODULE_DNBD3_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/blk.c + ${CMAKE_CURRENT_SOURCE_DIR}/dnbd3_main.c + ${CMAKE_CURRENT_SOURCE_DIR}/net.c + ${CMAKE_CURRENT_SOURCE_DIR}/serialize.c + ${CMAKE_CURRENT_SOURCE_DIR}/sysfs.c + ${CMAKE_CURRENT_SOURCE_DIR}/utils.c + ${PROJECT_VERSION_FILE}) +set(KERNEL_MODULE_DNBD3_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/blk.h + ${CMAKE_CURRENT_SOURCE_DIR}/dnbd3_main.h + ${CMAKE_CURRENT_SOURCE_DIR}/net.h + ${CMAKE_CURRENT_SOURCE_DIR}/sysfs.h + ${CMAKE_CURRENT_SOURCE_DIR}/utils.h) +add_kernel_module(dnbd3 "${KERNEL_BUILD_DIR}" + "${KERNEL_INSTALL_DIR}" + "CONFIG_BLK_DEV_DNBD3=m" + "${KERNEL_MODULE_DNBD3_SOURCE_FILES}" + "${KERNEL_MODULE_DNBD3_HEADER_FILES}" + ${CMAKE_CURRENT_SOURCE_DIR}/Kbuild) + +# add dependency to generate ${PROJECT_VERSION_FILE} before dnbd3.ko is built +add_dependencies(dnbd3 dnbd3-generate-version) diff --git a/src/kernel/Kbuild b/src/kernel/Kbuild new file mode 100644 index 0000000..385a5ff --- /dev/null +++ b/src/kernel/Kbuild @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Linux kernel module dnbd3 +obj-$(CONFIG_BLK_DEV_DNBD3) := dnbd3.o +dnbd3-y += dnbd3_main.o blk.o net.o serialize.o sysfs.o utils.o diff --git a/src/kernel/blk.c b/src/kernel/blk.c index 2c46b67..00c3f8f 100644 --- a/src/kernel/blk.c +++ b/src/kernel/blk.c @@ -18,7 +18,7 @@ * */ -#include "clientconfig.h" +#include #include "blk.h" #include "net.h" #include "sysfs.h" diff --git a/src/kernel/blk.h b/src/kernel/blk.h index 3377406..5f7f2db 100644 --- a/src/kernel/blk.h +++ b/src/kernel/blk.h @@ -21,7 +21,7 @@ #ifndef BLK_H_ #define BLK_H_ -#include "dnbd3.h" +#include "dnbd3_main.h" #define REQ_TYPE_SPECIAL REQ_TYPE_DRV_PRIV diff --git a/src/kernel/core.c b/src/kernel/core.c deleted file mode 100644 index 6e7d052..0000000 --- a/src/kernel/core.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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. - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "clientconfig.h" -#include "dnbd3.h" -#include "blk.h" - -int major; -static unsigned int max_devs = NUMBER_DEVICES; -static dnbd3_device_t *dnbd3_devices; - -struct device *dnbd3_device_to_dev(dnbd3_device_t *dev) -{ - return disk_to_dev(dev->disk); -} - -static int __init dnbd3_init(void) -{ - int i; - - dnbd3_devices = kcalloc(max_devs, sizeof(*dnbd3_devices), GFP_KERNEL); - if (!dnbd3_devices) - return -ENOMEM; - - // initialize block device - if ((major = register_blkdev(0, "dnbd3")) == 0) - { - pr_err("register_blkdev failed\n"); - return -EIO; - } - - pr_info("kernel module loaded\n"); - pr_debug("machine type " ENDIAN_MODE "\n"); - - // add MAX_NUMBER_DEVICES devices - for (i = 0; i < max_devs; i++) - { - if (dnbd3_blk_add_device(&dnbd3_devices[i], i) != 0) - { - pr_err("dnbd3_blk_add_device failed\n"); - return -EIO; // TODO: delete all devices added so far. it could happen that it's not the first one that fails. also call unregister_blkdev and free memory - } - } - - pr_info("init successful (%i devices)\n", max_devs); - - return 0; -} - -static void __exit dnbd3_exit(void) -{ - int i; - - for (i = 0; i < max_devs; i++) - { - dnbd3_blk_del_device(&dnbd3_devices[i]); - } - - unregister_blkdev(major, "dnbd3"); - kfree(dnbd3_devices); - - pr_info("exit kernel module\n"); -} - -module_init(dnbd3_init); -module_exit(dnbd3_exit); - -MODULE_DESCRIPTION("Distributed Network Block Device 3"); -MODULE_LICENSE("GPL"); - -module_param(max_devs, int, 0444); -MODULE_PARM_DESC(max_devs, "number of network block devices to initialize (default: 8)"); diff --git a/src/kernel/dnbd3.h b/src/kernel/dnbd3.h deleted file mode 100644 index 4e06d70..0000000 --- a/src/kernel/dnbd3.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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 DNBD_H_ -#define DNBD_H_ - -#include -#include -#include -#include -#include -#include - -#define KERNEL_MODULE -#include "config.h" -#include "types.h" -#include "serialize.h" - -extern int major; - -typedef struct -{ - dnbd3_host_t host; - unsigned long rtts[4]; // Last four round trip time measurements in µs - uint16_t protocol_version; // dnbd3 protocol version of this server - uint8_t failures; // How many times the server was unreachable -} dnbd3_server_t; - -typedef struct -{ - // block - struct gendisk *disk; - struct blk_mq_tag_set tag_set; - struct request_queue *queue; - spinlock_t blk_lock; - - // sysfs - struct kobject kobj; - - // network - char *imgname; - struct socket *sock; - dnbd3_server_t cur_server, initial_server; - unsigned long cur_rtt; - serialized_buffer_t payload_buffer; - dnbd3_server_t alt_servers[NUMBER_SERVERS]; // array of alt servers - int new_servers_num; // number of new alt servers that are waiting to be copied to above array - dnbd3_server_entry_t new_servers[NUMBER_SERVERS]; // pending new alt servers - uint8_t discover, panic, disconnecting, update_available, panic_count; - uint8_t use_server_provided_alts; - uint16_t rid; - uint32_t heartbeat_count; - uint64_t reported_size; - // server switch - struct socket *better_sock; - - // process - struct task_struct * thread_send; - struct task_struct * thread_receive; - struct task_struct *thread_discover; - struct timer_list hb_timer; - wait_queue_head_t process_queue_send; - wait_queue_head_t process_queue_receive; - wait_queue_head_t process_queue_discover; - struct list_head request_queue_send; - struct list_head request_queue_receive; - -} dnbd3_device_t; - -extern inline struct device *dnbd3_device_to_dev(dnbd3_device_t *dev); - -#endif /* DNBD_H_ */ diff --git a/src/kernel/dnbd3_main.c b/src/kernel/dnbd3_main.c new file mode 100644 index 0000000..27e8eed --- /dev/null +++ b/src/kernel/dnbd3_main.c @@ -0,0 +1,93 @@ +/* + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "dnbd3_main.h" +#include "blk.h" + +int major; +static unsigned int max_devs = NUMBER_DEVICES; +static dnbd3_device_t *dnbd3_devices; + +struct device *dnbd3_device_to_dev(dnbd3_device_t *dev) +{ + return disk_to_dev(dev->disk); +} + +static int __init dnbd3_init(void) +{ + int i; + + dnbd3_devices = kcalloc(max_devs, sizeof(*dnbd3_devices), GFP_KERNEL); + if (!dnbd3_devices) + return -ENOMEM; + + // initialize block device + if ((major = register_blkdev(0, "dnbd3")) == 0) + { + pr_err("register_blkdev failed\n"); + return -EIO; + } + + pr_info("kernel module in version %s loaded\n", DNBD3_VERSION); + pr_debug("machine type " ENDIAN_MODE "\n"); + + // add MAX_NUMBER_DEVICES devices + for (i = 0; i < max_devs; i++) + { + if (dnbd3_blk_add_device(&dnbd3_devices[i], i) != 0) + { + pr_err("dnbd3_blk_add_device failed\n"); + return -EIO; // TODO: delete all devices added so far. it could happen that it's not the first one that fails. also call unregister_blkdev and free memory + } + } + + pr_info("init successful (%i devices)\n", max_devs); + + return 0; +} + +static void __exit dnbd3_exit(void) +{ + int i; + + for (i = 0; i < max_devs; i++) + { + dnbd3_blk_del_device(&dnbd3_devices[i]); + } + + unregister_blkdev(major, "dnbd3"); + kfree(dnbd3_devices); + + pr_info("exit kernel module\n"); +} + +module_init(dnbd3_init); +module_exit(dnbd3_exit); + +MODULE_DESCRIPTION("Distributed Network Block Device 3"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DNBD3_VERSION); + +module_param(max_devs, int, 0444); +MODULE_PARM_DESC(max_devs, "number of network block devices to initialize (default: 8)"); diff --git a/src/kernel/dnbd3_main.h b/src/kernel/dnbd3_main.h new file mode 100644 index 0000000..124426b --- /dev/null +++ b/src/kernel/dnbd3_main.h @@ -0,0 +1,88 @@ +/* + * 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 DNBD_H_ +#define DNBD_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern int major; + +typedef struct +{ + dnbd3_host_t host; + unsigned long rtts[4]; // Last four round trip time measurements in µs + uint16_t protocol_version; // dnbd3 protocol version of this server + uint8_t failures; // How many times the server was unreachable +} dnbd3_server_t; + +typedef struct +{ + // block + struct gendisk *disk; + struct blk_mq_tag_set tag_set; + struct request_queue *queue; + spinlock_t blk_lock; + + // sysfs + struct kobject kobj; + + // network + char *imgname; + struct socket *sock; + dnbd3_server_t cur_server, initial_server; + unsigned long cur_rtt; + serialized_buffer_t payload_buffer; + dnbd3_server_t alt_servers[NUMBER_SERVERS]; // array of alt servers + int new_servers_num; // number of new alt servers that are waiting to be copied to above array + dnbd3_server_entry_t new_servers[NUMBER_SERVERS]; // pending new alt servers + uint8_t discover, panic, disconnecting, update_available, panic_count; + uint8_t use_server_provided_alts; + uint16_t rid; + uint32_t heartbeat_count; + uint64_t reported_size; + // server switch + struct socket *better_sock; + + // process + struct task_struct * thread_send; + struct task_struct * thread_receive; + struct task_struct *thread_discover; + struct timer_list hb_timer; + wait_queue_head_t process_queue_send; + wait_queue_head_t process_queue_receive; + wait_queue_head_t process_queue_discover; + struct list_head request_queue_send; + struct list_head request_queue_receive; + +} dnbd3_device_t; + +extern inline struct device *dnbd3_device_to_dev(dnbd3_device_t *dev); + +#endif /* DNBD_H_ */ diff --git a/src/kernel/net.c b/src/kernel/net.c index b387c1d..46c369a 100644 --- a/src/kernel/net.c +++ b/src/kernel/net.c @@ -18,12 +18,12 @@ * */ -#include "clientconfig.h" +#include #include "net.h" #include "blk.h" #include "utils.h" -#include "serialize.h" +#include #include #include diff --git a/src/kernel/net.h b/src/kernel/net.h index 8a2bc22..e7accd0 100644 --- a/src/kernel/net.h +++ b/src/kernel/net.h @@ -21,7 +21,7 @@ #ifndef NET_H_ #define NET_H_ -#include "dnbd3.h" +#include "dnbd3_main.h" #define init_msghdr(h) do { \ h.msg_name = NULL; \ diff --git a/src/kernel/serialize.c b/src/kernel/serialize.c new file mode 120000 index 0000000..5a4e4ac --- /dev/null +++ b/src/kernel/serialize.c @@ -0,0 +1 @@ +../shared/serialize.c \ No newline at end of file diff --git a/src/kernel/serialize_kmod.c b/src/kernel/serialize_kmod.c deleted file mode 100644 index 50746df..0000000 --- a/src/kernel/serialize_kmod.c +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include - -#define KERNEL_MODULE -#include "serialize.c" diff --git a/src/kernel/sysfs.h b/src/kernel/sysfs.h index 0a747a5..a90840b 100644 --- a/src/kernel/sysfs.h +++ b/src/kernel/sysfs.h @@ -21,7 +21,7 @@ #ifndef SYSFS_H_ #define SYSFS_H_ -#include "dnbd3.h" +#include "dnbd3_main.h" void dnbd3_sysfs_init(dnbd3_device_t *dev); diff --git a/src/serialize.c b/src/serialize.c deleted file mode 100644 index 0bc0dcd..0000000 --- a/src/serialize.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "serialize.h" -#include "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; - if (*ptr) return NULL; // String did not terminate within buffer (possibly corrupted/malicious packet) - 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/serialize.h b/src/serialize.h deleted file mode 100644 index 1b73531..0000000 --- a/src/serialize.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef SERIALIZER_H_ -#define SERIALIZER_H_ - -// Careful with includes - this is used in kernel module too -#include "config.h" - -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/src/server/CMakeLists.txt b/src/server/CMakeLists.txt new file mode 100644 index 0000000..47dda53 --- /dev/null +++ b/src/server/CMakeLists.txt @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-server) + +# find Jansson package required by the dnbd3-server +find_package(Jansson) +if(NOT JANSSON_FOUND) + message(FATAL_ERROR "*** No jansson lib found, can't build dnbd3-server!") +endif(NOT JANSSON_FOUND) + +# add compile option to enable enhanced POSIX features +add_definitions(-D_GNU_SOURCE) + +# export project build type to source code +add_definitions(-DBUILD_TYPE=${CMAKE_BUILD_TYPE}) + +if(DNBD3_SERVER_AFL) + # build dnbd3-server with AFL support + message(STATUS "Building dnbd3-server with AFL support") + add_definitions(-DDNBD3_SERVER_AFL) +endif(DNBD3_SERVER_AFL) + +add_executable(dnbd3-server ${CMAKE_CURRENT_SOURCE_DIR}/altservers.c + ${CMAKE_CURRENT_SOURCE_DIR}/fileutil.c + ${CMAKE_CURRENT_SOURCE_DIR}/fuse.c + ${CMAKE_CURRENT_SOURCE_DIR}/globals.c + ${CMAKE_CURRENT_SOURCE_DIR}/helper.c + ${CMAKE_CURRENT_SOURCE_DIR}/image.c + ${CMAKE_CURRENT_SOURCE_DIR}/ini.c + ${CMAKE_CURRENT_SOURCE_DIR}/integrity.c + ${CMAKE_CURRENT_SOURCE_DIR}/locks.c + ${CMAKE_CURRENT_SOURCE_DIR}/net.c + ${CMAKE_CURRENT_SOURCE_DIR}/picohttpparser/picohttpparser.c + ${CMAKE_CURRENT_SOURCE_DIR}/reference.c + ${CMAKE_CURRENT_SOURCE_DIR}/rpc.c + ${CMAKE_CURRENT_SOURCE_DIR}/server.c + ${CMAKE_CURRENT_SOURCE_DIR}/threadpool.c + ${CMAKE_CURRENT_SOURCE_DIR}/uplink.c + ${CMAKE_CURRENT_SOURCE_DIR}/urldecode.c) +target_include_directories(dnbd3-server PRIVATE ${JANSSON_INCLUDE_DIR}) +target_link_libraries(dnbd3-server dnbd3-version dnbd3-shared ${CMAKE_THREAD_LIBS_INIT} ${JANSSON_LIBRARIES}) + +if(DNBD3_SERVER_FUSE) + # include Fuse headers and link with Fuse library + target_compile_options(dnbd3-server PRIVATE DNBD3_SERVER_FUSE) + target_include_directories(dnbd3-server PRIVATE ${FUSE_INCLUDE_DIRS}) + target_link_libraries(dnbd3-server ${FUSE_LIBRARIES}) +endif(DNBD3_SERVER_FUSE) + +if(UNIX AND NOT APPLE) + # link dnbd3-server with librt if server is compiled for a Unix system + target_link_libraries(dnbd3-server rt) +endif(UNIX AND NOT APPLE) + +if(DNBD3_SERVER_DEBUG_LOCKS) + # enable debugging of locks used in the dnbd3-server + target_compile_options(dnbd3-server DNBD3_SERVER_DEBUG_LOCKS) +endif(DNBD3_SERVER_DEBUG_LOCKS) + +if(DNBD3_SERVER_DEBUG_THREADS) + # enable debugging of threads used in the dnbd3-server + target_compile_options(dnbd3-server DNBD3_SERVER_DEBUG_THREADS) +endif(DNBD3_SERVER_DEBUG_THREADS) + +install(TARGETS dnbd3-server RUNTIME DESTINATION bin) diff --git a/src/server/altservers.c b/src/server/altservers.c index 838a475..4b87c3e 100644 --- a/src/server/altservers.c +++ b/src/server/altservers.c @@ -5,9 +5,9 @@ #include "helper.h" #include "image.h" #include "fileutil.h" -#include "../shared/protocol.h" -#include "../shared/timing.h" -#include "../serverconfig.h" +#include +#include +#include #include "reference.h" #include diff --git a/src/server/fuse.c b/src/server/fuse.c index e2ebf23..12913a6 100644 --- a/src/server/fuse.c +++ b/src/server/fuse.c @@ -1,8 +1,8 @@ #include "fuse.h" -#include "../types.h" -#include "../shared/log.h" +#include +#include -#ifndef BUILD_SERVER_FUSE +#ifndef DNBD3_SERVER_FUSE // bool dfuse_init(const char *opts UNUSED, const char *dir UNUSED) @@ -23,7 +23,7 @@ static char nullbytes[DNBD3_BLOCK_SIZE]; // FUSE ENABLED #define FUSE_USE_VERSION 30 // -#include "../config.h" +#include #include "locks.h" #include "threadpool.h" #include "image.h" @@ -658,4 +658,4 @@ static void cleanupFuse() fuseChannel = NULL; } -#endif +#endif // DNBD3_SERVER_FUSE diff --git a/src/server/globals.c b/src/server/globals.c index 9914f89..f689705 100644 --- a/src/server/globals.c +++ b/src/server/globals.c @@ -1,7 +1,7 @@ #include "globals.h" #include "ini.h" #include "locks.h" -#include "../shared/log.h" +#include #include #include #include diff --git a/src/server/globals.h b/src/server/globals.h index 8807019..12805ed 100644 --- a/src/server/globals.h +++ b/src/server/globals.h @@ -1,9 +1,9 @@ #ifndef _GLOBALS_H_ #define _GLOBALS_H_ -#include "../types.h" -#include "../shared/fdsignal.h" -#include "../serverconfig.h" +#include +#include +#include #include #include #include @@ -36,7 +36,7 @@ typedef struct _dnbd3_queue_entry uint64_t from; // First byte offset of requested block (ie. 4096) uint64_t to; // Last byte + 1 of requested block (ie. 8192, if request len is 4096, resulting in bytes 4096-8191) dnbd3_queue_client_t *clients; -#ifdef _DEBUG +#ifdef DEBUG ticks entered; // When this request entered the queue (for debugging) #endif uint8_t hopCount; // How many hops this request has already taken across proxies diff --git a/src/server/helper.h b/src/server/helper.h index 102cb36..3e1b661 100644 --- a/src/server/helper.h +++ b/src/server/helper.h @@ -2,8 +2,8 @@ #define HELPER_H_ #include "server.h" -#include "../shared/log.h" -#include "../types.h" +#include +#include #include #include #include diff --git a/src/server/image.c b/src/server/image.c index 7a6e3f4..de6217f 100644 --- a/src/server/image.c +++ b/src/server/image.c @@ -5,9 +5,9 @@ #include "locks.h" #include "integrity.h" #include "altservers.h" -#include "../shared/protocol.h" -#include "../shared/timing.h" -#include "../shared/crc32.h" +#include +#include +#include #include "reference.h" #include diff --git a/src/server/locks.c b/src/server/locks.c index 4d9cde6..3be73b3 100644 --- a/src/server/locks.c +++ b/src/server/locks.c @@ -7,9 +7,9 @@ #include "locks.h" #include "helper.h" -#include "../shared/timing.h" +#include -#ifdef DEBUG_LOCKS +#ifdef DNBD3_SERVER_DEBUG_LOCKS #define MAXLOCKS (SERVER_MAX_CLIENTS * 2 + SERVER_MAX_ALTS + 200 + SERVER_MAX_IMAGES) #define MAXTHREADS (SERVER_MAX_CLIENTS + 100) #define MAXLPT 20 diff --git a/src/server/locks.h b/src/server/locks.h index ff27a33..f495f7b 100644 --- a/src/server/locks.h +++ b/src/server/locks.h @@ -28,7 +28,7 @@ // -#ifdef DEBUG_LOCKS +#ifdef DNBD3_SERVER_DEBUG_LOCKS #define mutex_init( lock, prio ) debug_mutex_init( #lock, __FILE__, __LINE__, lock, prio) #define mutex_lock( lock ) debug_mutex_lock( #lock, __FILE__, __LINE__, lock, false) @@ -57,7 +57,7 @@ void debug_dump_lock_stats(); #endif -#ifdef DEBUG_THREADS +#ifdef DNBD3_SERVER_DEBUG_THREADS extern int debugThreadCount; #define thread_create(thread,attr,routine,arg) (logadd( LOG_THREAD CREATE, "%d @ %s:%d\n", debugThreadCount, __FILE__, (int)__LINE__), debug_thread_create(thread, attr, routine, arg)) @@ -101,6 +101,6 @@ static inline int debug_thread_join(pthread_t thread, void **value_ptr) #define thread_detach(thread) pthread_detach( thread ) #define thread_join(thread,value) pthread_join( thread, value ) -#endif +#endif /* DNBD3_SERVER_DEBUG_THREADS */ #endif /* LOCKS_H_ */ diff --git a/src/server/net.c b/src/server/net.c index c33a12e..4cd9773 100644 --- a/src/server/net.c +++ b/src/server/net.c @@ -26,10 +26,10 @@ #include "altservers.h" #include "reference.h" -#include "../shared/sockhelper.h" -#include "../shared/timing.h" -#include "../shared/protocol.h" -#include "../serialize.h" +#include +#include +#include +#include #include @@ -63,7 +63,7 @@ static void uplinkCallback(void *data, uint64_t handle, uint64_t start, uint32_t static inline bool recv_request_header(int sock, dnbd3_request_t *request) { ssize_t ret, fails = 0; -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL sock = 0; #endif // Read request header from socket @@ -90,7 +90,7 @@ static inline bool recv_request_header(int sock, dnbd3_request_t *request) static inline bool recv_request_payload(int sock, uint32_t size, serialized_buffer_t *payload) { -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL sock = 0; #endif if ( size == 0 ) { @@ -160,7 +160,7 @@ void* net_handleNewConnection(void *clientPtr) // Await data from client. Since this is a fresh connection, we expect data right away sock_setTimeout( client->sock, _clientTimeout ); do { -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL const int ret = (int)recv( 0, &request, sizeof(request), MSG_WAITALL ); #else const int ret = (int)recv( client->sock, &request, sizeof(request), MSG_WAITALL ); @@ -396,7 +396,7 @@ void* net_handleNewConnection(void *clientPtr) // TODO: Should we consider EOPNOTSUPP on BSD for sendfile and fallback to read/write? // Linux would set EINVAL or ENOSYS instead, which it unfortunately also does for a couple of other failures :/ // read/write would kill performance anyways so a fallback would probably be of little use either way. -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL char buf[1000]; size_t cnt = realBytes - done; if ( cnt > 1000 ) { diff --git a/src/server/rpc.c b/src/server/rpc.c index 0002661..ab08395 100644 --- a/src/server/rpc.c +++ b/src/server/rpc.c @@ -5,8 +5,8 @@ #include "locks.h" #include "image.h" #include "altservers.h" -#include "../shared/sockhelper.h" -#include "../version.h" +#include +#include #include "fileutil.h" #include "picohttpparser/picohttpparser.h" #include "urldecode.h" @@ -175,7 +175,7 @@ void rpc_sendStatsJson(int sock, dnbd3_host_t* host, const void* data, const int // Reaching here means partial request or parse error if ( pret == -2 ) { // Partial, keep reading prevLen = hoff; -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL ssize_t ret = recv( 0, headerBuf + hoff, sizeof(headerBuf) - hoff, 0 ); #else ssize_t ret = recv( sock, headerBuf + hoff, sizeof(headerBuf) - hoff, 0 ); @@ -311,7 +311,7 @@ static bool handleStatus(int sock, int permissions, struct field *fields, size_t "runId", randomRunId ); } if ( version ) { - json_object_set_new( statisticsJson, "version", json_string( VERSION_STRING ) ); + json_object_set_new( statisticsJson, "version", json_string( DNBD3_VERSION ) ); json_object_set_new( statisticsJson, "build", json_string( TOSTRING( BUILD_TYPE ) ) ); } if ( space ) { @@ -411,7 +411,7 @@ static bool sendReply(int sock, const char *status, const char *ctype, const cha if ( keepAlive == HTTP_CLOSE ) { // Wait for flush shutdown( sock, SHUT_WR ); -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL sock = 0; #endif // Don't wait too long in case other side ignores the shutdown @@ -459,7 +459,7 @@ static int getacl(dnbd3_host_t *host) if ( aclRules[i].bitMask != 0 && aclRules[i].host[aclRules[i].bytes] != ( host->addr[aclRules[i].bytes] & aclRules[i].bitMask ) ) continue; return aclRules[i].permissions; } -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL return 0x7fffff; #else return 0; diff --git a/src/server/serialize.c b/src/server/serialize.c deleted file mode 100644 index 4934132..0000000 --- a/src/server/serialize.c +++ /dev/null @@ -1,5 +0,0 @@ -#include -#include -#include - -#include "../serialize.c" diff --git a/src/server/server.c b/src/server/server.c index 9dd9f81..8aefece 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -31,9 +31,9 @@ #include "rpc.h" #include "fuse.h" -#include "../version.h" -#include "../shared/sockhelper.h" -#include "../shared/timing.h" +#include +#include +#include #include #include @@ -105,11 +105,11 @@ static void queueJobInternal(job_t *job); */ void dnbd3_printHelp(char *argv_0) { - printf( "Version: %s\n\n", VERSION_STRING ); + printf( "Version: %s\n\n", DNBD3_VERSION ); printf( "Usage: %s [OPTIONS]...\n", argv_0 ); printf( "Start the DNBD3 server\n" ); printf( "-c or --config Configuration directory (default /etc/dnbd3-server/)\n" ); -#ifdef BUILD_SERVER_FUSE +#ifdef DNBD3_SERVER_WITH_FUSE printf( "-m or --mount FUSE mount point\n "); #endif printf( "-n or --nodaemon Start server in foreground\n" ); @@ -130,7 +130,7 @@ void dnbd3_printHelp(char *argv_0) */ void dnbd3_printVersion() { - printf( "Version: %s\n", VERSION_STRING ); + printf( "dnbd3-server version: %s\n", DNBD3_VERSION ); exit( 0 ); } @@ -227,7 +227,7 @@ int main(int argc, char *argv[]) _configDir = strdup( optarg ); break; case 'm': -#ifndef BUILD_SERVER_FUSE +#ifndef DNBD3_SERVER_WITH_FUSE fprintf( stderr, "FUSE support not enabled at build time.\n" ); return 8; #endif @@ -290,7 +290,7 @@ int main(int argc, char *argv[]) timing_setBase(); timing_get( &startupTime ); -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL // ###### AFL // image_serverStartup(); @@ -365,7 +365,7 @@ int main(int argc, char *argv[]) logadd( LOG_INFO, "DNBD3 server starting...." ); logadd( LOG_INFO, "Machine type: " ENDIAN_MODE ); logadd( LOG_INFO, "Build Type: " TOSTRING( BUILD_TYPE ) ); - logadd( LOG_INFO, "Version: %s", VERSION_STRING ); + logadd( LOG_INFO, "Version: %s", DNBD3_VERSION ); if ( altservers_load() < 0 ) { logadd( LOG_WARNING, "Could not load alt-servers. Does the file exist in %s?", _configDir ); diff --git a/src/server/server.h b/src/server/server.h index a026eb6..93ab925 100644 --- a/src/server/server.h +++ b/src/server/server.h @@ -22,7 +22,7 @@ #define SERVER_H_ #include "globals.h" -#include "../types.h" +#include uint32_t dnbd3_serverUptime(); void server_addJob(void *(*startRoutine)(void *), void *arg, int delaySecs, int intervalSecs); diff --git a/src/server/threadpool.c b/src/server/threadpool.c index 4ebefcb..cf39d5c 100644 --- a/src/server/threadpool.c +++ b/src/server/threadpool.c @@ -63,7 +63,7 @@ bool threadpool_run(void *(*startRoutine)(void *), void *arg, const char *name) logadd( LOG_MINOR, "Cannot submit work to threadpool while shutting down!" ); return false; } -#ifdef _DEBUG +#ifdef DEBUG if ( unlikely( startRoutine == NULL ) ) { logadd( LOG_ERROR, "Trying to queue work for thread pool with NULL startRoutine" ); return false; // Or bail out!? @@ -123,7 +123,7 @@ keep_going:; logadd( LOG_DEBUG1, "Unexpected return value %d for signal_wait in threadpool worker!", ret ); continue; } -#ifdef _DEBUG +#ifdef DEBUG if ( entry->startRoutine == NULL ) { logadd( LOG_ERROR, "Worker woke up but has no work to do!" ); exit( 1 ); diff --git a/src/server/threadpool.h b/src/server/threadpool.h index d8a526e..c30d44f 100644 --- a/src/server/threadpool.h +++ b/src/server/threadpool.h @@ -1,7 +1,7 @@ #ifndef _THREADPOOL_H_ #define _THREADPOOL_H_ -#include "../types.h" +#include /** * Initialize the thread pool. This must be called before using diff --git a/src/server/uplink.c b/src/server/uplink.c index cb4f956..1fded60 100644 --- a/src/server/uplink.c +++ b/src/server/uplink.c @@ -4,10 +4,10 @@ #include "image.h" #include "altservers.h" #include "net.h" -#include "../shared/sockhelper.h" -#include "../shared/protocol.h" -#include "../shared/timing.h" -#include "../shared/crc32.h" +#include +#include +#include +#include #include "threadpool.h" #include "reference.h" @@ -358,7 +358,7 @@ static bool uplink_requestInternal(dnbd3_uplink_t *uplink, void *data, uplink_ca request->handle = ++uplink->queueId; request->from = start & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1); request->to = (end + DNBD3_BLOCK_SIZE - 1) & ~(uint64_t)(DNBD3_BLOCK_SIZE - 1); -#ifdef _DEBUG +#ifdef DEBUG timing_get( &request->entered ); #endif request->hopCount = hops; @@ -650,7 +650,7 @@ static void* uplink_mainloop(void *data) } timing_set( &nextAltCheck, &now, (discoverFailCount < SERVER_RTT_MAX_UNREACH) ? altCheckInterval : SERVER_RTT_INTERVAL_FAILED ); } -#ifdef _DEBUG +#ifdef DEBUG if ( uplink->current.fd != -1 && !uplink->shutdown ) { bool resend = false; ticks deadline; @@ -662,7 +662,7 @@ static void* uplink_mainloop(void *data) " (from %" PRIu64 " to %" PRIu64 ", sent: %d) %s:%d", it->from, it->to, (int)it->sent, PIMG(uplink->image) ); it->entered = now; -#ifdef _DEBUG_RESEND_STARVING +#ifdef DEBUG_RESEND_STARVING it->sent = false; resend = true; #endif diff --git a/src/server/uplink.h b/src/server/uplink.h index 6cd69ea..b6037d6 100644 --- a/src/server/uplink.h +++ b/src/server/uplink.h @@ -2,7 +2,7 @@ #define _UPLINK_H_ #include "globals.h" -#include "../types.h" +#include void uplink_globalsInit(); diff --git a/src/serverconfig.h b/src/serverconfig.h deleted file mode 100644 index 31708de..0000000 --- a/src/serverconfig.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef _SERVERCONFIG_H_ -#define _SERVERCONFIG_H_ - -#include "config.h" - -// +++++ 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/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt new file mode 100644 index 0000000..804d27c --- /dev/null +++ b/src/shared/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(dnbd3-shared) + +# add compile option to get POLLRDHUP support for signals +add_compile_options(-D_GNU_SOURCE) + +add_library(dnbd3-shared STATIC ${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) +target_include_directories(dnbd3-shared PUBLIC ${PROJECT_INCLUDE_DIR}) diff --git a/src/shared/crc32.c b/src/shared/crc32.c index c3e566f..6cf9a18 100644 --- a/src/shared/crc32.c +++ b/src/shared/crc32.c @@ -38,7 +38,7 @@ */ -#include "../types.h" +#include #include #if defined(__x86_64__) || defined(__amd64__) 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 - -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 087b6f1..1db59bd 100644 --- a/src/shared/fdsignal.c +++ b/src/shared/fdsignal.c @@ -1,4 +1,4 @@ -#include "fdsignal.h" +#include #if defined(__linux__) //#warning "Using eventfd based signalling" 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..f2cab85 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -18,7 +18,7 @@ * */ -#include "log.h" +#include #include #include #include @@ -136,7 +136,7 @@ void logadd(const logmask_t mask, const char *fmt, ...) } if ( toStdout ) { if ( consoleTimestamps ) stdoutLine = buffer; -#ifdef AFL_MODE +#ifdef DNBD3_SERVER_AFL fputs( stdoutLine, stderr ); fflush( stderr ); #else 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 -#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/src/shared/protocol.h b/src/shared/protocol.h deleted file mode 100644 index 05fd2bf..0000000 --- a/src/shared/protocol.h +++ /dev/null @@ -1,157 +0,0 @@ -#ifndef _PROTOCOL_H_ -#define _PROTOCOL_H_ - -#include "sockhelper.h" - -#include "../types.h" -#include "../serialize.h" - -#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/src/shared/serialize.c b/src/shared/serialize.c new file mode 100644 index 0000000..648b719 --- /dev/null +++ b/src/shared/serialize.c @@ -0,0 +1,83 @@ +#include +#include + +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; + if (*ptr) return NULL; // String did not terminate within buffer (possibly corrupted/malicious packet) + 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 4ff93a6..f218d5a 100644 --- a/src/shared/sockhelper.c +++ b/src/shared/sockhelper.c @@ -1,5 +1,6 @@ -#include "sockhelper.h" -#include "log.h" +#include +#include +#include #include // inet_ntop #include #include @@ -19,8 +20,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 +32,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,9 +40,7 @@ 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; @@ -165,7 +161,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; @@ -174,7 +170,7 @@ bool sock_sockaddrToDnbd3(struct sockaddr* sa, dnbd3_host_t *host) memcpy( host->addr, &addr6->sin6_addr, 16 ); return true; } -#endif + return false; } 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 -#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/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 #include #include #include diff --git a/src/shared/timing.h b/src/shared/timing.h deleted file mode 100644 index f23bfeb..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 -#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/src/types.h b/src/types.h deleted file mode 100644 index 83416f4..0000000 --- a/src/types.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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 "config.h" -#ifndef KERNEL_MODULE -#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 AFL_MODE -#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/src/version.c.in b/src/version.c.in deleted file mode 100644 index ab937a2..0000000 --- a/src/version.c.in +++ /dev/null @@ -1,3 +0,0 @@ - -const char * VERSION_STRING = "@VERSION@"; - diff --git a/src/version.h b/src/version.h deleted file mode 100644 index 1c17442..0000000 --- a/src/version.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 VERSION_H_ -#define VERSION_H_ - -extern const char *VERSION_STRING; - -#endif /* VERSION_H_ */ -- cgit v1.2.3-55-g7522