From 871c3e3281d15ce876fcd85a02228b4e70453bac Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 28 Jul 2022 12:07:38 -0400 Subject: [PATCH] changeset for windows port * wintun vpn platform for windows * bundle config snippets into nsis installer for exit node, keyfile persisting, reduced hops mode. * use wintun for vpn platform * isolate all windows platform specific code into their own compilation units and libraries * split up internal libraries into more specific components * rename liblokinet.a target to liblokinet-amalgum.a to elimiate ambiguity with liblokinet.so * DNS platform for win32 * rename llarp/ev/ev_libuv.{c,h}pp to llarp/ev/libuv.{c,h}pp as the old name was idiotic * split up net platform into win32 and posix specific compilation units * rename lokinet_init.c to easter_eggs.cpp as that is what they are for and it does not need to be a c compilation target * add cmake option STRIP_SYMBOLS for seperating out debug symbols for windows builds * intercept dns traffic on all interfaces on windows using windivert and feed it into lokinet --- .drone.jsonnet | 2 +- CMakeLists.txt | 32 +- cmake/enable_lto.cmake | 3 + cmake/gui.cmake | 54 +- cmake/installer.cmake | 35 +- cmake/win32.cmake | 60 +- cmake/win32_installer_deps.cmake | 29 +- contrib/android-configure.sh | 1 + contrib/ci/drone-static-upload.sh | 11 +- contrib/configs/00-debug-log.ini | 2 + contrib/configs/00-exit.ini | 5 + contrib/configs/00-keyfile.ini | 5 + contrib/windows.sh | 9 +- daemon/CMakeLists.txt | 24 +- daemon/lokinet.cpp | 12 +- jni/CMakeLists.txt | 2 +- llarp/CMakeLists.txt | 99 ++- llarp/apple/CMakeLists.txt | 12 +- llarp/apple/vpn_interface.hpp | 2 +- llarp/config/config.cpp | 29 +- llarp/config/config.hpp | 3 + llarp/constants/net.hpp | 7 + llarp/constants/platform.hpp | 31 +- llarp/dns/multi_platform.hpp | 21 - llarp/dns/nm_platform.cpp | 2 +- llarp/dns/nm_platform.hpp | 2 +- llarp/dns/null_platform.hpp | 13 - .../dns/{multi_platform.cpp => platform.cpp} | 6 +- llarp/dns/platform.hpp | 37 +- llarp/dns/sd_platform.cpp | 8 +- llarp/dns/sd_platform.hpp | 2 +- llarp/dns/server.cpp | 104 ++- llarp/dns/server.hpp | 74 +- llarp/dns/win32_platform.cpp | 51 ++ llarp/dns/win32_platform.hpp | 8 + llarp/ev/ev.cpp | 11 +- llarp/ev/ev.hpp | 9 +- llarp/ev/{ev_libuv.cpp => libuv.cpp} | 16 +- llarp/ev/{ev_libuv.hpp => libuv.hpp} | 0 llarp/exit/endpoint.cpp | 45 +- llarp/exit/endpoint.hpp | 6 +- llarp/exit/session.cpp | 27 +- llarp/handlers/exit.cpp | 63 +- llarp/handlers/exit.hpp | 3 +- llarp/handlers/null.hpp | 2 +- llarp/handlers/tun.cpp | 241 +++--- llarp/handlers/tun.hpp | 21 +- llarp/lokinet_shared.cpp | 2 +- llarp/net/address_info.hpp | 12 +- llarp/net/bogon_ranges.hpp | 36 + llarp/net/interface_info.hpp | 21 + llarp/net/ip_address.cpp | 2 +- llarp/net/ip_address.hpp | 60 +- llarp/net/ip_packet.cpp | 216 +++--- llarp/net/ip_packet.hpp | 144 +++- llarp/net/ip_range.cpp | 11 + llarp/net/ip_range.hpp | 37 +- llarp/net/net.hpp | 178 ++++- llarp/net/net_bits.hpp | 8 - llarp/net/{net.cpp => posix.cpp} | 256 ++----- llarp/net/sock_addr.cpp | 26 +- llarp/net/sock_addr.hpp | 13 +- llarp/net/win32.cpp | 216 ++++++ llarp/path/path.cpp | 4 +- llarp/path/transit_hop.cpp | 8 +- llarp/quic/client.cpp | 2 +- llarp/quic/connection.cpp | 8 +- llarp/quic/endpoint.cpp | 2 +- llarp/quic/tunnel.cpp | 2 +- llarp/router/abstractrouter.hpp | 8 +- llarp/router/route_poker.cpp | 264 +++---- llarp/router/route_poker.hpp | 44 +- llarp/router/router.cpp | 76 +- llarp/router/router.hpp | 23 +- llarp/router_contact.cpp | 4 +- llarp/rpc/rpc_server.cpp | 8 +- llarp/service/endpoint.cpp | 9 +- llarp/service/endpoint.hpp | 9 + llarp/service/pendingbuffer.hpp | 34 +- llarp/service/sendcontext.cpp | 1 + llarp/util/buffer.cpp | 8 + llarp/util/buffer.hpp | 143 ++-- llarp/util/easter_eggs.cpp | 39 + llarp/util/lokinet_init.c | 35 - llarp/util/lokinet_init.h | 9 - llarp/util/str.cpp | 24 + llarp/util/str.hpp | 5 +- llarp/vpn/android.hpp | 42 +- llarp/vpn/i_packet_io.hpp | 35 + llarp/vpn/linux.hpp | 138 ++-- llarp/vpn/packet_intercept.hpp | 27 + llarp/vpn/packet_router.cpp | 33 +- llarp/vpn/packet_router.hpp | 30 +- llarp/vpn/platform.cpp | 4 +- llarp/{ev/vpn.hpp => vpn/platform.hpp} | 93 ++- llarp/vpn/win32.hpp | 687 +++--------------- llarp/win32/dll.cpp | 22 + llarp/win32/dll.hpp | 30 + llarp/win32/exception.cpp | 39 + llarp/win32/exception.hpp | 25 +- llarp/win32/exec.cpp | 57 ++ llarp/win32/exec.hpp | 46 ++ llarp/win32/guid.hpp | 27 + llarp/win32/handle.hpp | 19 + llarp/win32/windivert.cpp | 194 +++++ llarp/win32/windivert.hpp | 30 + llarp/win32/wintun.cpp | 411 +++++++++++ llarp/win32/wintun.hpp | 34 + pybind/CMakeLists.txt | 2 +- pybind/llarp/config.cpp | 2 +- test/CMakeLists.txt | 11 +- test/mocks/mock_network.hpp | 54 +- test/mocks/mock_vpn.hpp | 20 +- test/net/test_llarp_net.cpp | 26 +- win32-setup/extra_install.nsis | 12 +- win32-setup/extra_preinstall.nsis | 4 +- win32-setup/extra_uninstall.nsis | 12 +- 117 files changed, 3338 insertions(+), 2076 deletions(-) create mode 100644 contrib/configs/00-debug-log.ini create mode 100644 contrib/configs/00-exit.ini create mode 100644 contrib/configs/00-keyfile.ini create mode 100644 llarp/constants/net.hpp rename llarp/dns/{multi_platform.cpp => platform.cpp} (78%) create mode 100644 llarp/dns/win32_platform.cpp create mode 100644 llarp/dns/win32_platform.hpp rename llarp/ev/{ev_libuv.cpp => libuv.cpp} (97%) rename llarp/ev/{ev_libuv.hpp => libuv.hpp} (100%) create mode 100644 llarp/net/bogon_ranges.hpp create mode 100644 llarp/net/interface_info.hpp rename llarp/net/{net.cpp => posix.cpp} (67%) create mode 100644 llarp/net/win32.cpp create mode 100644 llarp/util/easter_eggs.cpp delete mode 100644 llarp/util/lokinet_init.c create mode 100644 llarp/vpn/i_packet_io.hpp create mode 100644 llarp/vpn/packet_intercept.hpp rename llarp/{ev/vpn.hpp => vpn/platform.hpp} (58%) create mode 100644 llarp/win32/dll.cpp create mode 100644 llarp/win32/dll.hpp create mode 100644 llarp/win32/exception.cpp create mode 100644 llarp/win32/exec.cpp create mode 100644 llarp/win32/exec.hpp create mode 100644 llarp/win32/guid.hpp create mode 100644 llarp/win32/handle.hpp create mode 100644 llarp/win32/windivert.cpp create mode 100644 llarp/win32/windivert.hpp create mode 100644 llarp/win32/wintun.cpp create mode 100644 llarp/win32/wintun.hpp diff --git a/.drone.jsonnet b/.drone.jsonnet index 2ec9ac4aa..d03f1baec 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -146,7 +146,7 @@ local windows_cross_pipeline(name, 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool', 'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix', 'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix', - 'VERBOSE=1 JOBS=' + jobs + ' ./contrib/windows.sh ' + ci_mirror_opts, + 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts, ] + extra_cmds, }, ], diff --git a/CMakeLists.txt b/CMakeLists.txt index 8498e480e..1496b2a25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ option(WITH_HIVE "build simulation stubs" OFF) option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF) option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP}) option(WITH_PEERSTATS "build with experimental peerstats db support" OFF) +option(STRIP_SYMBOLS "strip off all debug symbols into an external archive for all executables built" OFF) + include(cmake/enable_lto.cmake) @@ -81,6 +83,12 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo) endif() +set(debug OFF) +if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") + set(debug ON) + add_definitions(-DLOKINET_DEBUG) +endif() + if(BUILD_STATIC_DEPS AND STATIC_LINK) message(STATUS "we are building static deps so we won't build shared libs") set(BUILD_SHARED_LIBS OFF CACHE BOOL "") @@ -171,11 +179,18 @@ if(NOT TARGET sodium) export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake) endif() -if(NOT APPLE) - add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla) +if(APPLE) + add_compile_options(-Wno-deprecated-declarations) +else() + add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werror=vla) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-unknown-warning-option) endif() + if(debug) + add_compile_options(-Wdeprecated-declarations) + else() + add_compile_options(-Wno-deprecated-declarations) + endif() endif() if(XSAN) @@ -186,11 +201,6 @@ if(XSAN) message(STATUS "Doing a ${XSAN} sanitizer build") endif() -if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]") - add_definitions(-DLOKINET_DEBUG) -endif() - - include(cmake/coverage.cmake) # these vars are set by the cmake toolchain spec @@ -317,6 +327,10 @@ if(NOT TARGET uninstall) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() -if(BUILD_PACKAGE) - include(cmake/installer.cmake) +if(BUILD_PACKAGE AND NOT APPLE) + include(cmake/installer.cmake) +endif() + +if(TARGET package) + add_dependencies(package assemble_gui) endif() diff --git a/cmake/enable_lto.cmake b/cmake/enable_lto.cmake index c315c6cff..dd81906bb 100644 --- a/cmake/enable_lto.cmake +++ b/cmake/enable_lto.cmake @@ -2,6 +2,9 @@ include(CheckIPOSupported) option(WITH_LTO "enable lto on compile time" ON) if(WITH_LTO) + if(WIN32) + message(FATAL_ERROR "LTO not supported on win32 targets, please set -DWITH_LTO=OFF") + endif() check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error) if(IPO_ENABLED) message(STATUS "LTO enabled") diff --git a/cmake/gui.cmake b/cmake/gui.cmake index be27a9688..fcb81b2fd 100644 --- a/cmake/gui.cmake +++ b/cmake/gui.cmake @@ -8,25 +8,30 @@ elseif(WIN32) set(default_gui_target win32) endif() +if(WIN32) + option(GUI_EXE "path to an externally built lokinet gui.exe" OFF) +endif() + option(BUILD_GUI "build electron gui from 'gui' submodule source" ${default_build_gui}) set(GUI_YARN_TARGET "${default_gui_target}" CACHE STRING "yarn target for building the GUI") set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn build command") + if (BUILD_GUI) message(STATUS "Building lokinet-gui") + # allow manually specifying yarn with -DYARN= if(NOT YARN) - find_program(YARN NAMES yarn yarnpkg REQUIRED) + find_program(YARN NAMES yarnpkg yarn REQUIRED) endif() message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}") - set(wine_env) - if(WIN32) - set(wine_env USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix") - endif() - add_custom_target(lokinet-gui - COMMAND ${YARN} install --frozen-lockfile && - ${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} - WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") + + if(NOT WIN32) + add_custom_target(lokinet-gui + COMMAND ${YARN} install --frozen-lockfile && + ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") + endif() if(APPLE) add_custom_target(assemble_gui ALL @@ -38,14 +43,13 @@ if (BUILD_GUI) COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns" COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/" COMMAND /usr/libexec/PlistBuddy - -c "Delete :CFBundleDisplayName" - -c "Add :LSHasLocalizedDisplayName bool true" - -c "Add :CFBundleDevelopmentRegion string en" - -c "Set :CFBundleShortVersionString ${lokinet_VERSION}" - -c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}" - "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist" + -c "Delete :CFBundleDisplayName" + -c "Add :LSHasLocalizedDisplayName bool true" + -c "Add :CFBundleDevelopmentRegion string en" + -c "Set :CFBundleShortVersionString ${lokinet_VERSION}" + -c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}" + "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist" ) - elseif(WIN32) file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui") option(GUI_ZIP_FILE "custom lokinet gui for windows from zip file" OFF) @@ -53,20 +57,28 @@ if (BUILD_GUI) message(STATUS "using custom lokinet gui from ${GUI_ZIP_FILE}") execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE} WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) + add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true") + elseif(GUI_EXE) + message(STATUS "using custom lokinet gui executable: ${GUI_EXE}") + execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GUI_EXE}" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe") + add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true") else() add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" - DEPENDS lokinet lokinet-gui - # FIXME: we really shouldn't be building inside the source directory but this is npm... + COMMAND ${YARN} install --frozen-lockfile && + USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all WINEPREFIX="${PROJECT_BINARY_DIR}/wineprefix" ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" - ) - add_custom_target(assemble_gui ALL - DEPENDS ${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe) + WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") endif() + add_custom_target(assemble_gui ALL COMMAND "true" DEPENDS "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe") else() message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform") endif() else() message(STATUS "not building gui") endif() + +if(NOT TARGET assemble_gui) + add_custom_target(assemble_gui COMMAND "true") +endif() diff --git a/cmake/installer.cmake b/cmake/installer.cmake index 60e39f69b..503fd15dd 100644 --- a/cmake/installer.cmake +++ b/cmake/installer.cmake @@ -5,11 +5,42 @@ set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE") if(WIN32) include(cmake/win32_installer_deps.cmake) + install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-exit.ini DESTINATION share/conf.d COMPONENT exit_configs) + install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-keyfile.ini DESTINATION share/conf.d COMPONENT keyfile_configs) + install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-debug-log.ini DESTINATION share/conf.d COMPONENT debug_configs) + get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) + list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified" "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs") + list(APPEND CPACK_COMPONENTS_ALL "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs") elseif(APPLE) set(CPACK_GENERATOR DragNDrop;ZIP) + get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) + list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") endif() - -# This must always be last! include(CPack) +if(WIN32) + cpack_add_component(lokinet + DISPLAY_NAME "lokinet" + DESCRIPTION "core required lokinet files" + REQUIRED) + + cpack_add_component(gui + DISPLAY_NAME "lokinet gui" + DESCRIPTION "electron based control panel for lokinet") + + cpack_add_component(exit_configs + DISPLAY_NAME "auto-enable exit" + DESCRIPTION "automatically enable usage of exit.loki as an exit node\n" + DISABLED) + + cpack_add_component(keyfile_configs + DISPLAY_NAME "persist address" + DESCRIPTION "persist .loki address across restarts of lokinet\nnot recommended when enabling exit nodes" + DISABLED) + + cpack_add_component(debug_configs + DISPLAY_NAME "debug logging" + DESCRIPTION "enable debug spew log level by default" + DISABLED) +endif() diff --git a/cmake/win32.cmake b/cmake/win32.cmake index 73cda29b3..ae3bdb6d2 100644 --- a/cmake/win32.cmake +++ b/cmake/win32.cmake @@ -1,32 +1,52 @@ if(NOT WIN32) return() endif() +if (NOT STATIC_LINK) + message(FATAL_ERROR "windows requires static builds (thanks balmer)") +endif() enable_language(RC) -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - -if(NOT MSVC_VERSION) - add_compile_options($<$:-Wno-bad-function-cast>) - add_compile_options($<$:-Wno-cast-function-type>) - add_compile_options($<$:-fpermissive>) - # unlike unix where you get a *single* compiler ID string in .comment - # GNU ld sees fit to merge *all* the .ident sections in object files - # to .r[o]data section one after the other! - add_compile_options(-fno-ident -Wa,-mbig-obj) - link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv) - # the minimum windows version, set to 6 rn because supporting older windows is hell - set(_winver 0x0600) - add_definitions(-DWINVER=${_winver} -D_WIN32_WINNT=${_winver}) -endif() +option(WITH_WINDOWS_32 "build 32 bit windows" OFF) + +# unlike unix where you get a *single* compiler ID string in .comment +# GNU ld sees fit to merge *all* the .ident sections in object files +# to .r[o]data section one after the other! +add_compile_options(-fno-ident -Wa,-mbig-obj) +# the minimum windows version, set to 6 rn because supporting older windows is hell +set(_winver 0x0600) +add_definitions(-D_WIN32_WINNT=${_winver}) if(EMBEDDED_CFG) link_libatomic() endif() -add_definitions(-DWIN32_LEAN_AND_MEAN -DWIN32) +set(WINTUN_VERSION 0.14.1 CACHE STRING "wintun version") +set(WINTUN_MIRROR https://www.wintun.net/builds + CACHE STRING "wintun mirror(s)") +set(WINTUN_SOURCE wintun-${WINTUN_VERSION}.zip) +set(WINTUN_HASH SHA256=07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51 + CACHE STRING "wintun source hash") -if (NOT STATIC_LINK AND NOT MSVC) - message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll") - message("for release builds, turn on STATIC_LINK in cmake options") -endif() +set(WINDIVERT_VERSION 2.2.0-A CACHE STRING "windivert version") +set(WINDIVERT_MIRROR https://reqrypt.org/download + CACHE STRING "windivert mirror(s)") +set(WINDIVERT_SOURCE WinDivert-${WINDIVERT_VERSION}.zip) +set(WINDIVERT_HASH SHA256=2a7630aac0914746fbc565ac862fa096e3e54233883ac52d17c83107496b7a7f + CACHE STRING "windivert source hash") + +set(WINTUN_URL ${WINTUN_MIRROR}/${WINTUN_SOURCE} + CACHE STRING "wintun download url") +set(WINDIVERT_URL ${WINDIVERT_MIRROR}/${WINDIVERT_SOURCE} + CACHE STRING "windivert download url") + +message(STATUS "Downloading wintun from ${WINTUN_URL}") +file(DOWNLOAD ${WINTUN_URL} ${CMAKE_BINARY_DIR}/wintun.zip EXPECTED_HASH ${WINTUN_HASH}) +message(STATUS "Downloading windivert from ${WINDIVERT_URL}") +file(DOWNLOAD ${WINDIVERT_URL} ${CMAKE_BINARY_DIR}/windivert.zip EXPECTED_HASH ${WINDIVERT_HASH}) + +execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/wintun.zip + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/windivert.zip + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/cmake/win32_installer_deps.cmake b/cmake/win32_installer_deps.cmake index f20034212..4461c5ec4 100644 --- a/cmake/win32_installer_deps.cmake +++ b/cmake/win32_installer_deps.cmake @@ -1,11 +1,3 @@ -set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe") -set(TUNTAP_EXE "${CMAKE_BINARY_DIR}/tuntap-install.exe") -set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed") - -file(DOWNLOAD - ${TUNTAP_URL} - ${TUNTAP_EXE}) - if(NOT BUILD_GUI) if(NOT GUI_ZIP_URL) set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip") @@ -25,9 +17,19 @@ if(NOT BUILD_GUI) message(FATAL_ERROR "Downloaded gui archive from ${GUI_ZIP_URL} does not contain gui/lokinet-gui.exe!") endif() endif() - install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui) -install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap) + +if(WITH_WINDOWS_32) + install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/x86/wintun.dll DESTINATION bin COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.sys DESTINATION lib COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.dll DESTINATION bin COMPONENT lokinet) +else() + install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/amd64/wintun.dll DESTINATION bin COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert64.sys DESTINATION lib COMPONENT lokinet) + install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert.dll DESTINATION bin COMPONENT lokinet) +endif() + +set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed") install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed) set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet") @@ -35,7 +37,6 @@ set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico") set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin") set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) - function(read_nsis_file filename outvar) file(STRINGS "${filename}" _outvar) list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\") @@ -51,9 +52,9 @@ read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_ set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}") -set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") +set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}") set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}") +set(CPACK_NSIS_MODIFY_PATH ON) -get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) -list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") +set(CPACK_NSIS_COMPRESSOR "/SOLID lzma") diff --git a/contrib/android-configure.sh b/contrib/android-configure.sh index 2d669cae2..637bbe7ba 100755 --- a/contrib/android-configure.sh +++ b/contrib/android-configure.sh @@ -34,6 +34,7 @@ for abi in $build_abis; do -DBUILD_TESTING=OFF \ -DBUILD_LIBLOKINET=OFF \ -DWITH_TESTS=OFF \ + -DWITH_BOOTSTRAP=OFF \ -DNATIVE_BUILD=OFF \ -DSTATIC_LINK=ON \ -DWITH_SYSTEMD=OFF \ diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh index 6180b516f..80a87d5a9 100755 --- a/contrib/ci/drone-static-upload.sh +++ b/contrib/ci/drone-static-upload.sh @@ -34,8 +34,11 @@ else fi mkdir -v "$base" -if [ -e build-windows ]; then - cp -av build-windows/lokinet-*.exe "$base" +if [ -e build/win32 ]; then + # save debug symbols + cp -av build/win32/daemon/debug-symbols.tar.xz "$base-debug-symbols.tar.xz" + # save installer + cp -av build/win32/*.exe "$base" # zipit up yo archive="$base.zip" zip -r "$archive" "$base" @@ -77,6 +80,10 @@ $mkdirs put $archive $upload_to SFTP +if [ -e "$base-debug-symbols.tar.xz" ] ; then + sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<<"put $base-debug-symbols.tar.xz $upload_to" +fi + set +o xtrace echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n" diff --git a/contrib/configs/00-debug-log.ini b/contrib/configs/00-debug-log.ini new file mode 100644 index 000000000..73dd5bee6 --- /dev/null +++ b/contrib/configs/00-debug-log.ini @@ -0,0 +1,2 @@ +[logging] +level=debug \ No newline at end of file diff --git a/contrib/configs/00-exit.ini b/contrib/configs/00-exit.ini new file mode 100644 index 000000000..f9f2b8550 --- /dev/null +++ b/contrib/configs/00-exit.ini @@ -0,0 +1,5 @@ +# +# "suggested" default exit node config +# +[network] +exit-node=exit.loki \ No newline at end of file diff --git a/contrib/configs/00-keyfile.ini b/contrib/configs/00-keyfile.ini new file mode 100644 index 000000000..cfdf515e2 --- /dev/null +++ b/contrib/configs/00-keyfile.ini @@ -0,0 +1,5 @@ +# +# persist .loki address in a private key file in the data dir +# +[network] +keyfile=lokinet-addr.privkey \ No newline at end of file diff --git a/contrib/windows.sh b/contrib/windows.sh index ff380aebd..d06adab84 100755 --- a/contrib/windows.sh +++ b/contrib/windows.sh @@ -6,9 +6,8 @@ set -e set +x - - root="$(readlink -f $(dirname $0)/../)" -cd "$root" -./contrib/windows-configure.sh . build-windows "$@" -make package -j${JOBS:-$(nproc)} -C build-windows +mkdir -p $root/build/win32 +$root/contrib/windows-configure.sh $root $root/build/win32 "$@" +make package -j${JOBS:-$(nproc)} -C $root/build/win32 + diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index db864b57e..5372fe46d 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -46,15 +46,27 @@ if(WITH_BOOTSTRAP) endif() endif() +# cmake interface library for bunch of cmake hacks to fix final link order +add_library(hax_and_shims_for_cmake INTERFACE) +if(WIN32) + target_link_libraries(hax_and_shims_for_cmake INTERFACE uvw oxenmq::oxenmq -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv) +endif() + foreach(exe ${exetargets}) - if(WIN32 AND NOT MSVC_VERSION) + if(WIN32) target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc) + target_compile_options(${exe} PRIVATE $<$:-fpermissive>) target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00) - target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") target_link_directories(${exe} PRIVATE /usr/local/lib) endif() - target_link_libraries(${exe} PUBLIC liblokinet) + target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake) + if(STRIP_SYMBOLS) + add_custom_command(TARGET ${exe} + POST_BUILD + COMMAND ${CMAKE_OBJCOPY} ARGS --only-keep-debug $ $.debug + COMMAND ${CMAKE_STRIP} ARGS --strip-all $) + endif() target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}") if(should_install) if(APPLE) @@ -71,3 +83,9 @@ endforeach() if(SETCAP) install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)") endif() + +if(STRIP_SYMBOLS) + add_custom_target(symbols ALL + COMMAND ${CMAKE_COMMAND} -E tar cJf ${CMAKE_CURRENT_BINARY_DIR}/debug-symbols.tar.xz $.debug + DEPENDS lokinet) +endif() diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index 53655c860..8ff06fdfd 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -101,7 +101,7 @@ install_win32_daemon() // Create the service schService = CreateService( schSCManager, // SCM database - "lokinet", // name of service + strdup("lokinet"), // name of service "Lokinet for Windows", // service name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type @@ -134,10 +134,10 @@ insert_description() SC_HANDLE schSCManager; SC_HANDLE schService; SERVICE_DESCRIPTION sd; - LPTSTR szDesc = + LPTSTR szDesc = strdup( "LokiNET is a free, open source, private, " "decentralized, \"market based sybil resistant\" " - "and IP based onion routing network"; + "and IP based onion routing network"); // Get a handle to the SCM database. schSCManager = OpenSCManager( NULL, // local computer @@ -270,7 +270,7 @@ run_main_context(std::optional confFile, const llarp::RuntimeOptions o } catch (std::exception& ex) { - llarp::LogError("failed to start up lokinet: {}", ex.what()); + llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what())); exit_code.set_value(1); return; } @@ -367,7 +367,7 @@ main(int argc, char* argv[]) return lokinet_main(argc, argv); #else SERVICE_TABLE_ENTRY DispatchTable[] = { - {"lokinet", (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; + {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; if (lstrcmpi(argv[1], "--win32-daemon") == 0) { start_as_daemon = true; @@ -680,7 +680,7 @@ win32_daemon_entry(DWORD argc, LPTSTR* argv) ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); // SCM clobbers startup args, regenerate them here argc = 2; - argv[1] = "c:/programdata/lokinet/lokinet.ini"; + argv[1] = strdup("c:/programdata/lokinet/lokinet.ini"); argv[2] = nullptr; lokinet_main(argc, argv); } diff --git a/jni/CMakeLists.txt b/jni/CMakeLists.txt index 000e25aa0..ef24506a3 100644 --- a/jni/CMakeLists.txt +++ b/jni/CMakeLists.txt @@ -2,4 +2,4 @@ add_library(lokinet-android SHARED lokinet_config.cpp lokinet_daemon.cpp) -target_link_libraries(lokinet-android liblokinet) +target_link_libraries(lokinet-android lokinet-amalgum) diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index f80bdb77c..f033721d5 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -8,7 +8,7 @@ add_library(lokinet-util util/fs.cpp util/json.cpp util/logging/buffer.cpp - util/lokinet_init.c + util/easter_eggs.cpp util/mem.cpp util/str.cpp util/thread/queue_manager.cpp @@ -32,12 +32,11 @@ add_library(lokinet-platform STATIC # for networking ev/ev.cpp - ev/ev_libuv.cpp + ev/libuv.cpp net/ip.cpp net/ip_address.cpp net/ip_packet.cpp net/ip_range.cpp - net/net.cpp net/net_int.cpp net/sock_addr.cpp vpn/packet_router.cpp @@ -58,34 +57,66 @@ endif() if (WIN32) target_sources(lokinet-platform PRIVATE - win32/win32_inet.c - win32/win32_intrnl.c) - - target_link_libraries(lokinet-platform PUBLIC iphlpapi) + net/win32.cpp + win32/exec.cpp) + add_library(lokinet-win32 STATIC + win32/dll.cpp + win32/exception.cpp) + add_library(lokinet-wintun STATIC + win32/wintun.cpp) + add_library(lokinet-windivert STATIC + win32/windivert.cpp) + + # wintun and windivert are privated linked by lokinet-platform + # this is so their details do not leak out to deps of lokinet-platform + # wintun and windivert still need things from lokinet-platform + target_compile_options(lokinet-wintun PUBLIC -I${CMAKE_BINARY_DIR}/wintun/include/) + target_compile_options(lokinet-windivert PUBLIC -I${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/include/) + target_include_directories(lokinet-windivert PUBLIC ${PROJECT_SOURCE_DIR}) + target_link_libraries(lokinet-wintun PUBLIC lokinet-platform lokinet-util lokinet-config) + target_link_libraries(lokinet-win32 PUBLIC lokinet-util) + target_link_libraries(lokinet-windivert PUBLIC oxen-logging) + target_link_libraries(lokinet-windivert PRIVATE lokinet-win32) + target_link_libraries(lokinet-platform PRIVATE lokinet-win32 lokinet-wintun lokinet-windivert) +else() + target_sources(lokinet-platform PRIVATE + net/posix.cpp) endif() + if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") target_include_directories(lokinet-platform SYSTEM PUBLIC /usr/local/include) endif() -add_library(liblokinet +add_library(lokinet-dns STATIC - config/config.cpp - config/definition.cpp - config/ini.cpp - config/key_manager.cpp - dns/message.cpp dns/name.cpp - dns/multi_platform.cpp - dns/nm_platform.cpp - dns/sd_platform.cpp + dns/platform.cpp dns/question.cpp dns/rr.cpp dns/serialize.cpp dns/server.cpp - dns/srv_data.cpp + dns/srv_data.cpp) + +if(WITH_SYSTEMD) + target_sources(lokinet-dns PRIVATE dns/nm_platform.cpp dns/sd_platform.cpp) +endif() + +target_link_libraries(lokinet-dns PUBLIC lokinet-platform uvw) +target_link_libraries(lokinet-dns PRIVATE libunbound lokinet-config) + +add_library(lokinet-config + STATIC + config/config.cpp + config/definition.cpp + config/ini.cpp + config/key_manager.cpp) +target_link_libraries(lokinet-config PUBLIC lokinet-dns lokinet-platform oxenmq::oxenmq) + +add_library(lokinet-amalgum + STATIC consensus/table.cpp consensus/reachability_testing.cpp @@ -115,7 +146,7 @@ add_library(liblokinet dht/taglookup.cpp endpoint_base.cpp - + exit/context.cpp exit/endpoint.cpp exit/exit_messages.cpp @@ -170,7 +201,7 @@ add_library(liblokinet router/rc_gossiper.cpp router/router.cpp router/route_poker.cpp - + routing/dht_message.cpp routing/message_parser.cpp routing/path_confirm_message.cpp @@ -204,42 +235,42 @@ add_library(liblokinet service/tag.cpp ) -if(WITH_PEERSTATS_BACKEND) - target_compile_definitions(liblokinet PRIVATE -DLOKINET_PEERSTATS_BACKEND) - target_link_libraries(liblokinet PUBLIC sqlite_orm) -endif() - -set_target_properties(liblokinet PROPERTIES OUTPUT_NAME lokinet) -enable_lto(lokinet-util lokinet-platform liblokinet) - -if(TRACY_ROOT) - target_sources(liblokinet PRIVATE ${TRACY_ROOT}/TracyClient.cpp) +if(WITH_PEERSTATS_BACKEND) + target_compile_definitions(lokinet-amalgum PRIVATE -DLOKINET_PEERSTATS_BACKEND) + target_link_libraries(lokinet-amalgum PUBLIC sqlite_orm) endif() if(WITH_HIVE) - target_sources(liblokinet PRIVATE + target_sources(lokinet-amalgum PRIVATE tooling/router_hive.cpp tooling/hive_router.cpp tooling/hive_context.cpp ) endif() -target_link_libraries(liblokinet PUBLIC +# TODO: make libunbound hidden behind a feature flag like sqlite for embedded lokinet +target_link_libraries(lokinet-amalgum PRIVATE libunbound) + +target_link_libraries(lokinet-amalgum PUBLIC cxxopts oxenc::oxenc lokinet-platform + lokinet-config + lokinet-dns lokinet-util lokinet-cryptography ngtcp2_static oxenmq::oxenmq) -target_link_libraries(liblokinet PRIVATE libunbound) + +enable_lto(lokinet-util lokinet-platform lokinet-dns lokinet-config lokinet-amalgum) + pkg_check_modules(CRYPT libcrypt IMPORTED_TARGET) if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING) add_definitions(-DHAVE_CRYPT) add_library(libcrypt INTERFACE) target_link_libraries(libcrypt INTERFACE PkgConfig::CRYPT) - target_link_libraries(liblokinet PRIVATE libcrypt) + target_link_libraries(lokinet-amalgum PRIVATE libcrypt) message(STATUS "using libcrypt ${CRYPT_VERSION}") endif() @@ -247,7 +278,7 @@ endif() if(BUILD_LIBLOKINET) include(GNUInstallDirs) add_library(lokinet-shared SHARED lokinet_shared.cpp) - target_link_libraries(lokinet-shared PUBLIC liblokinet) + target_link_libraries(lokinet-shared PUBLIC lokinet-amalgum) if(WIN32) set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "") endif() diff --git a/llarp/apple/CMakeLists.txt b/llarp/apple/CMakeLists.txt index 71d6c6651..8dd561ef7 100644 --- a/llarp/apple/CMakeLists.txt +++ b/llarp/apple/CMakeLists.txt @@ -18,12 +18,9 @@ target_sources(lokinet-platform PRIVATE vpn_platform.cpp vpn_interface.cpp route add_executable(lokinet-extension MACOSX_BUNDLE PacketTunnelProvider.m DNSTrampoline.m - ) +) + enable_lto(lokinet-extension) -target_link_libraries(lokinet-extension PRIVATE - liblokinet - ${COREFOUNDATION} - ${NETEXT}) # -fobjc-arc enables automatic reference counting for objective-C code # -e _NSExtensionMain because the appex has that instead of a `main` function entry point, of course. @@ -43,6 +40,11 @@ else() set(product_type com.apple.product-type.app-extension) endif() +target_link_libraries(lokinet-extension PRIVATE + lokinet-amalgum + ${COREFOUNDATION} + ${NETEXT}) + set_target_properties(lokinet-extension PROPERTIES BUNDLE TRUE BUNDLE_EXTENSION ${bundle_ext} diff --git a/llarp/apple/vpn_interface.hpp b/llarp/apple/vpn_interface.hpp index 762227f91..07b4de5ab 100644 --- a/llarp/apple/vpn_interface.hpp +++ b/llarp/apple/vpn_interface.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include #include diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 787fcdd83..195b96f4d 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -140,14 +140,14 @@ namespace llarp "this setting specifies the public IP at which this router is reachable. When", "provided the public-port option must also be specified.", }, - [this](std::string arg) { + [this, net = params.Net_ptr()](std::string arg) { if (arg.empty()) return; nuint32_t addr{}; if (not addr.FromString(arg)) throw std::invalid_argument{fmt::format("{} is not a valid IPv4 address", arg)}; - if (IsIPv4Bogon(addr)) + if (net->IsBogonIP(addr)) throw std::invalid_argument{ fmt::format("{} is not a publicly routable ip address", addr)}; @@ -648,6 +648,7 @@ namespace llarp throw std::invalid_argument{ fmt::format("[network]:ip6-range invalid value: '{}'", arg)}; }); + // TODO: could be useful for snodes in the future, but currently only implemented for clients: conf.defineOption( "network", @@ -813,6 +814,30 @@ namespace llarp } }); + conf.defineOption( + "dns", + "l3-intercept", + Default{ + platform::is_windows or platform::is_android + or (platform::is_macos and not platform::is_apple_sysex)}, + Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface " + "instead of binding a local udp socket"}, + AssignmentAcceptor(m_raw_dns)); + + conf.defineOption( + "dns", + "query-bind", +#ifdef __APPLE__ + Default{"127.0.0.1:1253"}, +#endif +#ifdef _WIN32 + Default{"0.0.0.0:0"}, +#endif + Comment{ + "Address to bind to for sending upstream DNS requests.", + }, + [this](std::string arg) { m_QueryBind = SockAddr{arg}; }); + conf.defineOption( "dns", "bind", diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 198599dab..54dbcc443 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -155,9 +155,12 @@ namespace llarp struct DnsConfig { + bool m_raw_dns; std::vector m_bind; std::vector m_upstreamDNS; std::vector m_hostfiles; + std::optional m_QueryBind; + std::unordered_multimap m_ExtraOpts; void diff --git a/llarp/constants/net.hpp b/llarp/constants/net.hpp new file mode 100644 index 000000000..9b6c6c4b3 --- /dev/null +++ b/llarp/constants/net.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace llarp::constants +{ + constexpr auto udp_header_bytes = 8; + constexpr auto ip_header_min_bytes = 20; +} // namespace llarp::constants diff --git a/llarp/constants/platform.hpp b/llarp/constants/platform.hpp index 6ca98f655..64b48c67e 100644 --- a/llarp/constants/platform.hpp +++ b/llarp/constants/platform.hpp @@ -11,8 +11,9 @@ namespace llarp::platform false #endif ; - /// we have systemd ? - inline constexpr bool has_systemd = + + /// building with systemd enabled ? + inline constexpr bool with_systemd = #ifdef WITH_SYSTEMD true #else @@ -47,6 +48,15 @@ namespace llarp::platform #endif ; + /// are we building as an apple system extension + inline constexpr bool is_apple_sysex = +#ifdef MACOS_SYSTEM_EXTENSION + true +#else + false +#endif + ; + /// are we an android platform ? inline constexpr bool is_android = #ifdef ANDROID @@ -65,9 +75,26 @@ namespace llarp::platform #endif ; + /// are we running with pybind simulation mode enabled? + inline constexpr bool is_simulation = +#ifdef LOKINET_HIVE + true +#else + false +#endif + ; + /// do we have systemd support ? + // on cross compiles sometimes weird permutations of target and host make this value not correct, + // this ensures it always is + inline constexpr bool has_systemd = is_linux and with_systemd and not(is_android or is_windows); + + /// are we using macos ? + inline constexpr bool is_macos = is_apple and not is_iphone; + /// are we a mobile phone ? inline constexpr bool is_mobile = is_android or is_iphone; /// does this platform support native ipv6 ? + // TODO: make windows support ipv6 inline constexpr bool supports_ipv6 = not is_windows; } // namespace llarp::platform diff --git a/llarp/dns/multi_platform.hpp b/llarp/dns/multi_platform.hpp index 8f05d7ed7..8b1378917 100644 --- a/llarp/dns/multi_platform.hpp +++ b/llarp/dns/multi_platform.hpp @@ -1,22 +1 @@ -#pragma once -#include "platform.hpp" -#include - -namespace llarp::dns -{ - /// a collection of dns platforms that are tried in order when setting dns - class Multi_Platform : public I_Platform - { - std::vector> m_Impls; - - public: - /// add a platform to be owned - void - add_impl(std::unique_ptr impl); - - /// try all owned platforms to set the resolver, throws if none of them work - void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; - }; -} // namespace llarp::dns diff --git a/llarp/dns/nm_platform.cpp b/llarp/dns/nm_platform.cpp index 79d2a0fc3..30fa981d8 100644 --- a/llarp/dns/nm_platform.cpp +++ b/llarp/dns/nm_platform.cpp @@ -13,7 +13,7 @@ using namespace std::literals; namespace llarp::dns::nm { void - Platform::set_resolver(std::string, llarp::SockAddr, bool) + Platform::set_resolver(unsigned int, llarp::SockAddr, bool) { // todo: implement me eventually } diff --git a/llarp/dns/nm_platform.hpp b/llarp/dns/nm_platform.hpp index e41e858cb..742b050c6 100644 --- a/llarp/dns/nm_platform.hpp +++ b/llarp/dns/nm_platform.hpp @@ -17,7 +17,7 @@ namespace llarp::dns virtual ~Platform() = default; void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + set_resolver(unsigned int index, llarp::SockAddr dns, bool global) override; }; }; // namespace nm using NM_Platform_t = std::conditional_t; diff --git a/llarp/dns/null_platform.hpp b/llarp/dns/null_platform.hpp index 2b31ee8a2..8b1378917 100644 --- a/llarp/dns/null_platform.hpp +++ b/llarp/dns/null_platform.hpp @@ -1,14 +1 @@ -#pragma once -#include "platform.hpp" -namespace llarp::dns -{ - /// a dns platform does silently does nothing, successfully - class Null_Platform : public I_Platform - { - public: - void - set_resolver(std::string, llarp::SockAddr, bool) override - {} - }; -} // namespace llarp::dns diff --git a/llarp/dns/multi_platform.cpp b/llarp/dns/platform.cpp similarity index 78% rename from llarp/dns/multi_platform.cpp rename to llarp/dns/platform.cpp index bf4b0f887..c63e42683 100644 --- a/llarp/dns/multi_platform.cpp +++ b/llarp/dns/platform.cpp @@ -1,4 +1,4 @@ -#include "multi_platform.hpp" +#include "platform.hpp" namespace llarp::dns { @@ -9,14 +9,14 @@ namespace llarp::dns } void - Multi_Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global) + Multi_Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool global) { size_t fails{0}; for (const auto& ptr : m_Impls) { try { - ptr->set_resolver(ifname, dns, global); + ptr->set_resolver(index, dns, global); } catch (std::exception& ex) { diff --git a/llarp/dns/platform.hpp b/llarp/dns/platform.hpp index 25402db26..46fea1c13 100644 --- a/llarp/dns/platform.hpp +++ b/llarp/dns/platform.hpp @@ -3,9 +3,12 @@ #include #include #include - #include +#ifndef _WIN32 +#include +#endif + namespace llarp::dns { /// sets dns settings in a platform dependant way @@ -18,13 +21,39 @@ namespace llarp::dns /// throws if unsupported or fails. /// /// - /// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0. - /// Typically tun_endpoint.GetIfName(). + /// \param if_index -- the interface index to which we add the DNS servers, this can be gotten + /// from the interface name e.g. lokitun0 (Typically tun_endpoint.GetIfName().) and then put + /// through if_nametoindex(). /// \param dns -- the listening address of the lokinet DNS server /// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode /// addresses (false). virtual void - set_resolver(std::string if_name, llarp::SockAddr dns, bool global) = 0; + set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) = 0; + }; + + /// a dns platform does silently does nothing, successfully + class Null_Platform : public I_Platform + { + public: + ~Null_Platform() override = default; + void + set_resolver(unsigned int, llarp::SockAddr, bool) override + {} }; + /// a collection of dns platforms that are tried in order when setting dns + class Multi_Platform : public I_Platform + { + std::vector> m_Impls; + + public: + ~Multi_Platform() override = default; + /// add a platform to be owned + void + add_impl(std::unique_ptr impl); + + /// try all owned platforms to set the resolver, throws if none of them work + void + set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override; + }; } // namespace llarp::dns diff --git a/llarp/dns/sd_platform.cpp b/llarp/dns/sd_platform.cpp index f867441f1..1e4ae3d2b 100644 --- a/llarp/dns/sd_platform.cpp +++ b/llarp/dns/sd_platform.cpp @@ -13,14 +13,8 @@ using namespace std::literals; namespace llarp::dns::sd { void - Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global) + Platform::set_resolver(unsigned int if_ndx, llarp::SockAddr dns, bool global) { - unsigned int if_ndx = if_nametoindex(ifname.c_str()); - if (if_ndx == 0) - { - throw std::runtime_error{"No such interface '" + ifname + "'"}; - } - linux::DBUS _dbus{ "org.freedesktop.resolve1", "/org/freedesktop/resolve1", diff --git a/llarp/dns/sd_platform.hpp b/llarp/dns/sd_platform.hpp index 0c83c2557..532e73a15 100644 --- a/llarp/dns/sd_platform.hpp +++ b/llarp/dns/sd_platform.hpp @@ -16,7 +16,7 @@ namespace llarp::dns virtual ~Platform() = default; void - set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override; + set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override; }; } // namespace sd using SD_Platform_t = diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index 0d652074f..41632e6e4 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -9,9 +9,9 @@ #include #include -#include "multi_platform.hpp" #include "sd_platform.hpp" #include "nm_platform.hpp" +#include "win32_platform.hpp" namespace llarp::dns { @@ -37,7 +37,7 @@ namespace llarp::dns m_udp = loop->make_udp([&](auto&, SockAddr src, llarp::OwnedBuffer buf) { if (src == m_LocalAddr) return; - if (not m_DNS.MaybeHandlePacket(weak_from_this(), m_LocalAddr, src, std::move(buf))) + if (not m_DNS.MaybeHandlePacket(shared_from_this(), m_LocalAddr, src, std::move(buf))) { LogWarn("did not handle dns packet from ", src, " to ", m_LocalAddr); } @@ -83,7 +83,7 @@ namespace llarp::dns class Query : public QueryJob_Base { std::weak_ptr parent; - std::weak_ptr src; + std::shared_ptr src; SockAddr resolverAddr; SockAddr askerAddr; @@ -91,7 +91,7 @@ namespace llarp::dns explicit Query( std::weak_ptr parent_, Message query, - std::weak_ptr pktsrc, + std::shared_ptr pktsrc, SockAddr toaddr, SockAddr fromaddr) : QueryJob_Base{std::move(query)} @@ -118,6 +118,8 @@ namespace llarp::dns std::shared_ptr m_Poller; #endif + std::optional m_LocalAddr; + struct ub_result_deleter { void @@ -127,6 +129,12 @@ namespace llarp::dns } }; + const net::Platform* + Net_ptr() const + { + return m_Loop.lock()->Net_ptr(); + } + static void Callback(void* data, int err, ub_result* _result) { @@ -186,6 +194,12 @@ namespace llarp::dns return "unbound"; } + virtual std::optional + GetLocalAddr() const override + { + return m_LocalAddr; + } + void Up(const llarp::DnsConfig& conf) { @@ -220,23 +234,39 @@ namespace llarp::dns throw std::runtime_error{ fmt::format("cannot use {} as upstream dns: {}", str, ub_strerror(err))}; } -#ifdef __APPLE__ - // On Apple, we configure a localhost resolver to trampoline requests through the tunnel - // to the actual upstream (because the network extension itself cannot route through the - // tunnel using normal sockets but instead we "get" to use Apple's interfaces, hurray). - if (hoststr == "127.0.0.1") + } + + if (auto maybe_addr = conf.m_QueryBind) + { + SockAddr addr{*maybe_addr}; + std::string host{addr.hostString()}; + + if (addr.getPort() == 0) { - // Not at all clear why this is needed but without it we get "send failed: Can't - // assign requested address" when unbound tries to connect to the localhost address - // using a source address of 0.0.0.0. Yay apple. - SetOpt("outgoing-interface:", hoststr.c_str()); - // The trampoline expects just a single source port (and sends everything back to it) - SetOpt("outgoing-range:", "1"); - SetOpt("outgoing-port-avoid:", "0-65535"); - SetOpt("outgoing-port-permit:", "1253"); + // unbound manages their own sockets because of COURSE it does. so we find an open port + // on our system and use it so we KNOW what it is before giving it to unbound to + // explicitly bind to JUST that port. + + addrinfo hints{}; + addrinfo* result{nullptr}; + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_family = AF_INET; + if (auto err = getaddrinfo(host.c_str(), nullptr, &hints, &result)) + throw std::invalid_argument{strerror(err)}; + addr.setPort(net::port_t{reinterpret_cast(result->ai_addr)->sin_port}); + freeaddrinfo(result); } -#endif + m_LocalAddr = addr; + + LogInfo(fmt::format("sening dns queries from {}:{}", host, addr.getPort())); + // set up query bind port if needed + SetOpt("outgoing-interface:", host); + SetOpt("outgoing-range:", "1"); + SetOpt("outgoing-port-avoid:", "0-65535"); + SetOpt("outgoing-port-permit:", std::to_string(addr.getPort())); } + // set async ub_ctx_async(m_ctx.get(), 1); // setup mainloop @@ -339,7 +369,7 @@ namespace llarp::dns bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const Message& query, const SockAddr& to, const SockAddr& from) override @@ -388,13 +418,10 @@ namespace llarp::dns void Query::SendReply(llarp::OwnedBuffer replyBuf) const { - auto packet_src = src.lock(); - auto parent_ptr = parent.lock(); - - if (packet_src and parent_ptr) + if (auto ptr = parent.lock()) { - parent_ptr->call([packet_src, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] { - packet_src->SendTo(to, from, OwnedBuffer::copy_from(buf)); + ptr->call([this, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] { + src->SendTo(to, from, OwnedBuffer::copy_from(buf)); }); } else @@ -402,13 +429,22 @@ namespace llarp::dns } } // namespace libunbound - Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif) + Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif) : m_Loop{std::move(loop)} , m_Config{std::move(conf)} , m_Platform{CreatePlatform()} - , m_NetifName{std::move(netif)} + , m_NetIfIndex{std::move(netif)} {} + std::vector> + Server::GetAllResolvers() const + { + std::vector> all; + for (const auto& res : m_Resolvers) + all.push_back(res); + return all; + } + void Server::Start() { @@ -433,6 +469,10 @@ namespace llarp::dns plat->add_impl(std::make_unique()); plat->add_impl(std::make_unique()); } + if constexpr (llarp::platform::is_windows) + { + plat->add_impl(std::make_unique()); + } return plat; } @@ -531,19 +571,16 @@ namespace llarp::dns Server::SetDNSMode(bool all_queries) { if (auto maybe_addr = FirstBoundPacketSourceAddr()) - m_Platform->set_resolver(m_NetifName, *maybe_addr, all_queries); + m_Platform->set_resolver(m_NetIfIndex, *maybe_addr, all_queries); } bool Server::MaybeHandlePacket( - std::weak_ptr src, + std::shared_ptr ptr, const SockAddr& to, const SockAddr& from, llarp::OwnedBuffer buf) { - auto ptr = src.lock(); - if (not ptr) - return false; // dont process to prevent feedback loop if (ptr->WouldLoop(to, from)) { @@ -557,6 +594,7 @@ namespace llarp::dns LogWarn("invalid dns message format from ", from, " to dns listener on ", to); return false; } + auto& msg = *maybe; // we don't provide a DoH resolver because it requires verified TLS // TLS needs X509/ASN.1-DER and opting into the Root CA Cabal @@ -581,7 +619,7 @@ namespace llarp::dns if (auto res_ptr = resolver.lock()) { LogDebug("check resolver ", res_ptr->ResolverName(), " for dns from ", from, " to ", to); - if (res_ptr->MaybeHookDNS(src, msg, to, from)) + if (res_ptr->MaybeHookDNS(ptr, msg, to, from)) return true; } } diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index 88ca86385..3202d02af 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -67,17 +67,62 @@ namespace llarp::dns BoundOn() const = 0; }; + /// a packet source which will override the sendto function of an wrapped packet source to + /// construct a raw ip packet as a reply + class PacketSource_Wrapper : public PacketSource_Base + { + std::weak_ptr m_Wrapped; + std::function m_WritePacket; + + public: + explicit PacketSource_Wrapper( + std::weak_ptr wrapped, std::function write_packet) + : m_Wrapped{wrapped}, m_WritePacket{write_packet} + {} + + bool + WouldLoop(const SockAddr& to, const SockAddr& from) const override + { + if (auto ptr = m_Wrapped.lock()) + return ptr->WouldLoop(to, from); + return true; + } + + void + SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override + { + m_WritePacket(net::IPPacket::make_udp(to, from, std::move(buf))); + } + + /// stop reading packets and end operation + void + Stop() override + { + if (auto ptr = m_Wrapped.lock()) + ptr->Stop(); + } + + /// returns the sockaddr we are bound on if applicable + std::optional + BoundOn() const override + { + if (auto ptr = m_Wrapped.lock()) + return ptr->BoundOn(); + return std::nullopt; + } + }; + /// non complex implementation of QueryJob_Base for use in things that /// only ever called on the mainloop thread class QueryJob : public QueryJob_Base, std::enable_shared_from_this { - std::weak_ptr src; + std::shared_ptr src; const SockAddr resolver; const SockAddr asker; public: explicit QueryJob( - std::weak_ptr source, + std::shared_ptr source, const Message& query, const SockAddr& to_, const SockAddr& from_) @@ -87,8 +132,7 @@ namespace llarp::dns void SendReply(llarp::OwnedBuffer replyBuf) const override { - if (auto ptr = src.lock()) - ptr->SendTo(asker, resolver, std::move(replyBuf)); + src->SendTo(asker, resolver, std::move(replyBuf)); } }; @@ -119,6 +163,13 @@ namespace llarp::dns return Rank() > other.Rank(); } + /// get local socket address that queries are sent from + virtual std::optional + GetLocalAddr() const + { + return std::nullopt; + } + /// get printable name virtual std::string_view ResolverName() const = 0; @@ -134,7 +185,7 @@ namespace llarp::dns /// returns true if we consumed this query and it should not be processed again virtual bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const Message& query, const SockAddr& to, const SockAddr& from) = 0; @@ -167,7 +218,8 @@ namespace llarp::dns public: virtual ~Server() = default; - explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif_name); + + explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif_index); /// returns all sockaddr we have from all of our PacketSources std::vector @@ -205,16 +257,18 @@ namespace llarp::dns virtual std::shared_ptr MakeDefaultResolver(); - /// feed a packet buffer from a packet source + std::vector> + GetAllResolvers() const; + + /// feed a packet buffer from a packet source. /// returns true if we decided to process the packet and consumed it /// returns false if we dont want to process the packet bool MaybeHandlePacket( - std::weak_ptr pktsource, + std::shared_ptr pktsource, const SockAddr& resolver, const SockAddr& from, llarp::OwnedBuffer buf); - /// set which dns mode we are in. /// true for intercepting all queries. false for just .loki and .snode void @@ -226,7 +280,7 @@ namespace llarp::dns std::shared_ptr m_Platform; private: - const std::string m_NetifName; + const unsigned int m_NetIfIndex; std::set, ComparePtr>> m_OwnedResolvers; std::set, CompareWeakPtr> m_Resolvers; diff --git a/llarp/dns/win32_platform.cpp b/llarp/dns/win32_platform.cpp new file mode 100644 index 000000000..6c268b05c --- /dev/null +++ b/llarp/dns/win32_platform.cpp @@ -0,0 +1,51 @@ +#include "win32_platform.hpp" +#include + +namespace llarp::dns::win32 +{ + void + Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool) + { +#ifdef _WIN32 + + // clear any previous dns settings + m_UndoDNS.clear(); + + auto interfaces = m_Loop->Net_ptr()->AllNetworkInterfaces(); + // remove dns + { + std::vector jobs; + for (const auto& ent : interfaces) + { + if (ent.index == index) + continue; + jobs.emplace_back( + "netsh.exe", fmt::format("interface ipv4 delete dns \"{}\" all", ent.name)); + jobs.emplace_back( + "netsh.exe", fmt::format("interface ipv6 delete dns \"{}\" all", ent.name)); + } + } + // add new dns + { + std::vector jobs; + for (const auto& ent : interfaces) + { + if (ent.index == index) + continue; + jobs.emplace_back( + "netsh.exe", + fmt::format("interface ipv4 add dns \"{}\" {} validate=no", ent.name, dns.asIPv4())); + jobs.emplace_back( + "netsh.exe", + fmt::format("interface ipv6 add dns \"{}\" {} validate=no", ent.name, dns.asIPv6())); + m_UndoDNS.emplace_back("netsh.exe", fmt::format("", index)); + } + m_UndoDNS.emplace_back("netsh.exe", "winsock reset"); + } + // flush dns + llarp::win32::Exec("ipconfig.exe", "/flushdns"); + +#endif + } + +} // namespace llarp::dns::win32 diff --git a/llarp/dns/win32_platform.hpp b/llarp/dns/win32_platform.hpp new file mode 100644 index 000000000..cb57a206c --- /dev/null +++ b/llarp/dns/win32_platform.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "platform.hpp" + +namespace llarp::dns +{ + // TODO: implement me + using Win32_Platform_t = Null_Platform; +} // namespace llarp::dns diff --git a/llarp/ev/ev.cpp b/llarp/ev/ev.cpp index 9c86b8cdd..8170c8a82 100644 --- a/llarp/ev/ev.cpp +++ b/llarp/ev/ev.cpp @@ -6,8 +6,8 @@ #include #include -// We libuv now -#include "ev_libuv.hpp" +#include "libuv.hpp" +#include namespace llarp { @@ -16,4 +16,11 @@ namespace llarp { return std::make_shared(queueLength); } + + const net::Platform* + EventLoop::Net_ptr() const + { + return net::Platform::Default_ptr(); + } + } // namespace llarp diff --git a/llarp/ev/ev.hpp b/llarp/ev/ev.hpp index 898d526a3..54e9046f5 100644 --- a/llarp/ev/ev.hpp +++ b/llarp/ev/ev.hpp @@ -4,7 +4,7 @@ #include #include #include - +#include #include #include #include @@ -28,8 +28,10 @@ namespace llarp namespace net { + class Platform; + struct IPPacket; - } + } // namespace net /// distinct event loop waker upper; used to idempotently schedule a task on the next event loop /// @@ -184,6 +186,9 @@ namespace llarp virtual ~EventLoop() = default; + virtual const net::Platform* + Net_ptr() const; + using UDPReceiveFunc = std::function; // Constructs a UDP socket that can be used for sending and/or receiving diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/libuv.cpp similarity index 97% rename from llarp/ev/ev_libuv.cpp rename to llarp/ev/libuv.cpp index 3f4784b94..75e370522 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/libuv.cpp @@ -1,12 +1,12 @@ -#include "ev_libuv.hpp" -#include "vpn.hpp" +#include "libuv.hpp" #include #include #include +#include + #include #include -#include -#include "ev.hpp" +#include #include @@ -251,19 +251,21 @@ namespace llarp::uv using event_t = uvw::PrepareEvent; auto handle = m_Impl->resource(); #endif + if (!handle) return false; handle->on([netif = std::move(netif), handler = std::move(handler)]( const event_t&, [[maybe_unused]] auto& handle) { - for (auto pkt = netif->ReadNextPacket(); pkt.sz > 0; pkt = netif->ReadNextPacket()) + for (auto pkt = netif->ReadNextPacket(); true; pkt = netif->ReadNextPacket()) { - LogDebug("got packet ", pkt.sz); + if (pkt.empty()) + return; if (handler) handler(std::move(pkt)); // on windows/apple, vpn packet io does not happen as an io action that wakes up the event // loop thus, we must manually wake up the event loop when we get a packet on our interface. - // on linux this is a nop + // on linux/android this is a nop netif->MaybeWakeUpperLayers(); } }); diff --git a/llarp/ev/ev_libuv.hpp b/llarp/ev/libuv.hpp similarity index 100% rename from llarp/ev/ev_libuv.hpp rename to llarp/ev/libuv.hpp diff --git a/llarp/exit/endpoint.cpp b/llarp/exit/endpoint.cpp index 919a4c705..4bbad541c 100644 --- a/llarp/exit/endpoint.cpp +++ b/llarp/exit/endpoint.cpp @@ -110,7 +110,7 @@ namespace llarp bool Endpoint::QueueOutboundTraffic( - PathID_t path, ManagedBuffer buf, uint64_t counter, service::ProtocolType t) + PathID_t path, std::vector buf, uint64_t counter, service::ProtocolType t) { const service::ConvoTag tag{path.as_array()}; if (t == service::ProtocolType::QUIC) @@ -118,18 +118,19 @@ namespace llarp auto quic = m_Parent->GetQUICTunnel(); if (not quic) return false; - quic->receive_packet(tag, buf.underlying); + m_TxRate += buf.size(); + quic->receive_packet(tag, std::move(buf)); m_LastActive = m_Parent->Now(); - m_TxRate += buf.underlying.sz; return true; } // queue overflow if (m_UpstreamQueue.size() > MaxUpstreamQueueSize) return false; - llarp::net::IPPacket pkt; - if (!pkt.Load(buf.underlying)) + llarp::net::IPPacket pkt{std::move(buf)}; + if (pkt.empty()) return false; + if (pkt.IsV6() && m_Parent->SupportsV6()) { huint128_t dst; @@ -152,24 +153,19 @@ namespace llarp { return false; } - m_UpstreamQueue.emplace(pkt, counter); - m_TxRate += buf.underlying.sz; + m_TxRate += pkt.size(); + m_UpstreamQueue.emplace(std::move(pkt), counter); m_LastActive = m_Parent->Now(); return true; } bool - Endpoint::QueueInboundTraffic(ManagedBuffer buf, service::ProtocolType type) + Endpoint::QueueInboundTraffic(std::vector buf, service::ProtocolType type) { - llarp::net::IPPacket pkt{}; - if (type == service::ProtocolType::QUIC) + if (type != service::ProtocolType::QUIC) { - pkt.sz = std::min(buf.underlying.sz, sizeof(pkt.buf)); - std::copy_n(buf.underlying.base, pkt.sz, pkt.buf); - } - else - { - if (!pkt.Load(buf.underlying)) + llarp::net::IPPacket pkt{std::move(buf)}; + if (pkt.empty()) return false; huint128_t src; @@ -181,11 +177,11 @@ namespace llarp pkt.UpdateIPv6Address(src, m_IP); else pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP))); + + buf = pkt.steal(); } - const auto _pktbuf = pkt.ConstBuffer(); - auto& pktbuf = _pktbuf.underlying; - const uint8_t queue_idx = pktbuf.sz / llarp::routing::ExitPadSize; + const uint8_t queue_idx = buf.size() / llarp::routing::ExitPadSize; if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end()) m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{}); auto& queue = m_DownstreamQueues.at(queue_idx); @@ -193,17 +189,17 @@ namespace llarp { queue.emplace_back(); queue.back().protocol = type; - return queue.back().PutBuffer(pktbuf, m_Counter++); + return queue.back().PutBuffer(std::move(buf), m_Counter++); } auto& msg = queue.back(); - if (msg.Size() + pktbuf.sz > llarp::routing::ExitPadSize) + if (msg.Size() + buf.size() > llarp::routing::ExitPadSize) { queue.emplace_back(); queue.back().protocol = type; - return queue.back().PutBuffer(pktbuf, m_Counter++); + return queue.back().PutBuffer(std::move(buf), m_Counter++); } msg.protocol = type; - return msg.PutBuffer(pktbuf, m_Counter++); + return msg.PutBuffer(std::move(buf), m_Counter++); } bool @@ -212,7 +208,8 @@ namespace llarp // flush upstream queue while (m_UpstreamQueue.size()) { - m_Parent->QueueOutboundTraffic(m_UpstreamQueue.top().pkt); + m_Parent->QueueOutboundTraffic( + const_cast(m_UpstreamQueue.top().pkt).steal()); m_UpstreamQueue.pop(); } // flush downstream queue diff --git a/llarp/exit/endpoint.hpp b/llarp/exit/endpoint.hpp index 50b1d138b..b6fc4b13e 100644 --- a/llarp/exit/endpoint.hpp +++ b/llarp/exit/endpoint.hpp @@ -58,7 +58,7 @@ namespace llarp /// queue traffic from service node / internet to be transmitted bool - QueueInboundTraffic(ManagedBuffer buff, service::ProtocolType t); + QueueInboundTraffic(std::vector data, service::ProtocolType t); /// flush inbound and outbound traffic queues bool @@ -68,7 +68,7 @@ namespace llarp /// does ip rewrite here bool QueueOutboundTraffic( - PathID_t txid, ManagedBuffer pkt, uint64_t counter, service::ProtocolType t); + PathID_t txid, std::vector data, uint64_t counter, service::ProtocolType t); /// update local path id and cascade information to parent /// return true if success @@ -122,7 +122,7 @@ namespace llarp struct UpstreamBuffer { - UpstreamBuffer(const llarp::net::IPPacket& p, uint64_t c) : pkt(p), counter(c) + UpstreamBuffer(llarp::net::IPPacket p, uint64_t c) : pkt{std::move(p)}, counter(c) {} llarp::net::IPPacket pkt; diff --git a/llarp/exit/session.cpp b/llarp/exit/session.cpp index b2370002a..1ae5870c3 100644 --- a/llarp/exit/session.cpp +++ b/llarp/exit/session.cpp @@ -213,8 +213,8 @@ namespace llarp if (m_WritePacket) { - llarp::net::IPPacket pkt; - if (!pkt.Load(buf)) + llarp::net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return false; m_LastUse = m_router->Now(); m_Downstream.emplace(counter, pkt); @@ -235,9 +235,7 @@ namespace llarp BaseSession::QueueUpstreamTraffic( llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t) { - const auto pktbuf = pkt.ConstBuffer(); - const llarp_buffer_t& buf = pktbuf; - auto& queue = m_Upstream[buf.sz / N]; + auto& queue = m_Upstream[pkt.size() / N]; // queue overflow if (queue.size() >= MaxUpstreamQueueLength) return false; @@ -245,18 +243,18 @@ namespace llarp { queue.emplace_back(); queue.back().protocol = t; - return queue.back().PutBuffer(buf, m_Counter++); + return queue.back().PutBuffer(std::move(pkt), m_Counter++); } auto& back = queue.back(); // pack to nearest N - if (back.Size() + buf.sz > N) + if (back.Size() + pkt.size() > N) { queue.emplace_back(); queue.back().protocol = t; - return queue.back().PutBuffer(buf, m_Counter++); + return queue.back().PutBuffer(std::move(pkt), m_Counter++); } back.protocol = t; - return back.PutBuffer(buf, m_Counter++); + return back.PutBuffer(std::move(pkt), m_Counter++); } bool @@ -333,7 +331,7 @@ namespace llarp while (m_Downstream.size()) { if (m_WritePacket) - m_WritePacket(m_Downstream.top().second.ConstBuffer()); + m_WritePacket(const_cast(m_Downstream.top().second).steal()); m_Downstream.pop(); } } @@ -369,8 +367,8 @@ namespace llarp void SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) { - net::IPPacket pkt; - if (not pkt.Load(buf)) + net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return; pkt.ZeroAddresses(); QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t); @@ -379,9 +377,10 @@ namespace llarp void ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) { - net::IPPacket pkt; - if (not pkt.Load(buf)) + net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return; + pkt.ZeroSourceAddress(); QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t); } diff --git a/llarp/handlers/exit.cpp b/llarp/handlers/exit.cpp index c14245bbc..df508141c 100644 --- a/llarp/handlers/exit.cpp +++ b/llarp/handlers/exit.cpp @@ -109,7 +109,7 @@ namespace llarp { if (not itr->second->LooksDead(Now())) { - if (itr->second->QueueInboundTraffic(ManagedBuffer{payload}, type)) + if (itr->second->QueueInboundTraffic(payload.copy(), type)) return true; } } @@ -117,10 +117,10 @@ namespace llarp if (not m_Router->PathToRouterAllowed(*rid)) return false; - ObtainSNodeSession(*rid, [data = payload.copy(), type](auto session) { + ObtainSNodeSession(*rid, [pkt = payload.copy(), type](auto session) mutable { if (session and session->IsReady()) { - session->SendPacketToRemote(data, type); + session->SendPacketToRemote(std::move(pkt), type); } }); } @@ -209,7 +209,7 @@ namespace llarp bool ExitEndpoint::MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) @@ -374,20 +374,24 @@ namespace llarp { while (not m_InetToNetwork.empty()) { - net::IPPacket pkt{m_InetToNetwork.top()}; - m_InetToNetwork.pop(); + auto& top = m_InetToNetwork.top(); - PubKey pk; + // get a session by public key + std::optional maybe_pk; { - auto itr = m_IPToKey.find(pkt.dstv6()); - if (itr == m_IPToKey.end()) - { - // drop - LogWarn(Name(), " dropping packet, has no session at ", pkt.dstv6()); - continue; - } - pk = itr->second; + auto itr = m_IPToKey.find(top.dstv6()); + if (itr != m_IPToKey.end()) + maybe_pk = itr->second; } + + auto buf = const_cast(top).steal(); + m_InetToNetwork.pop(); + // we have no session for public key so drop + if (not maybe_pk) + continue; // we are in a while loop + + const auto& pk = *maybe_pk; + // check if this key is a service node if (m_SNodeKeys.count(pk)) { @@ -397,13 +401,14 @@ namespace llarp auto itr = m_SNodeSessions.find(pk); if (itr != m_SNodeSessions.end()) { - itr->second->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::TrafficV4); + itr->second->SendPacketToRemote(std::move(buf), service::ProtocolType::TrafficV4); + // we are in a while loop continue; } } - auto tryFlushingTraffic = [&](exit::Endpoint* const ep) -> bool { - if (!ep->QueueInboundTraffic( - ManagedBuffer{pkt.Buffer()}, service::ProtocolType::TrafficV4)) + auto tryFlushingTraffic = + [this, buf = std::move(buf), pk](exit::Endpoint* const ep) -> bool { + if (!ep->QueueInboundTraffic(buf, service::ProtocolType::TrafficV4)) { LogWarn( Name(), @@ -451,13 +456,14 @@ namespace llarp m_IPToKey[ip] = us; m_IPActivity[ip] = std::numeric_limits::max(); m_SNodeKeys.insert(us); + if (m_ShouldInitTun) { vpn::InterfaceInfo info; info.ifname = m_ifname; - info.addrs.emplace(m_OurRange); + info.addrs.emplace_back(m_OurRange); - m_NetIf = GetRouter()->GetVPNPlatform()->ObtainInterface(std::move(info), m_Router); + m_NetIf = GetRouter()->GetVPNPlatform()->CreateInterface(std::move(info), m_Router); if (not m_NetIf) { llarp::LogError("Could not create interface"); @@ -471,7 +477,12 @@ namespace llarp } GetRouter()->loop()->add_ticker([this] { Flush(); }); +#ifndef _WIN32 + m_Resolver = std::make_shared( + m_Router->loop(), m_DNSConf, if_nametoindex(m_ifname.c_str())); m_Resolver->Start(); + +#endif } return true; } @@ -596,7 +607,7 @@ namespace llarp rc.srvRecords.clear(); for (auto& record : srvRecords) rc.srvRecords.emplace_back(record); - // set the version to 1 because we have srv records + // set the verssion to 1 because we have srv records rc.version = 1; return rc; }); @@ -651,8 +662,8 @@ namespace llarp bool ExitEndpoint::QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from) { - net::IPPacket pkt; - if (!pkt.Load(buf)) + net::IPPacket pkt{buf.view()}; + if (pkt.empty()) return false; // rewrite ip if (m_UseV6) @@ -708,6 +719,9 @@ namespace llarp return true; } */ + + m_DNSConf = dnsConfig; + if (networkConfig.m_endpointType == "null") { m_ShouldInitTun = false; @@ -743,7 +757,6 @@ namespace llarp return llarp::SockAddr{ifaddr, huint16_t{port}}; }); } - m_Resolver = std::make_shared(m_Router->loop(), dnsConfig, m_ifname); } huint128_t diff --git a/llarp/handlers/exit.hpp b/llarp/handlers/exit.hpp index 9984a3431..66bb09784 100644 --- a/llarp/handlers/exit.hpp +++ b/llarp/handlers/exit.hpp @@ -32,7 +32,7 @@ namespace llarp bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) override; @@ -245,6 +245,7 @@ namespace llarp /// internet to llarp packet queue PacketQueue_t m_InetToNetwork; bool m_UseV6; + DnsConfig m_DNSConf; }; } // namespace handlers } // namespace llarp diff --git a/llarp/handlers/null.hpp b/llarp/handlers/null.hpp index e0464b041..cd97fa82d 100644 --- a/llarp/handlers/null.hpp +++ b/llarp/handlers/null.hpp @@ -17,7 +17,7 @@ namespace llarp::handlers , m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) { var::visit( [&pkt](auto&& from) { - LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes"); + LogError("unhandled traffic from: ", from, " of ", pkt.size(), " bytes"); }, from); }}} diff --git a/llarp/handlers/tun.cpp b/llarp/handlers/tun.cpp index 2d1ab4b8f..cf0ae5dc0 100644 --- a/llarp/handlers/tun.cpp +++ b/llarp/handlers/tun.cpp @@ -25,8 +25,7 @@ #include #include #include -#include - +#include #include #include @@ -37,7 +36,7 @@ namespace llarp { bool TunEndpoint::MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) @@ -46,21 +45,25 @@ namespace llarp return false; auto job = std::make_shared(source, query, to, from); - if (not HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); })) + if (HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); })) + Router()->TriggerPump(); + else job->Cancel(); return true; } - // Intercepts DNS IP packets going to an IP on the tun interface; this is currently used on - // Android and macOS where binding to a low port isn't possible - // because of OS restrictions, but a tun interface *is* available. + + /// Intercepts DNS IP packets on platforms where binding to a low port isn't viable. + /// (windows/macos/ios/android ... aka everything that is not linux... funny that) class DnsInterceptor : public dns::PacketSource_Base { - public: - TunEndpoint* const m_Endpoint; + std::function m_Reply; + net::ipaddr_t m_OurIP; llarp::DnsConfig m_Config; - explicit DnsInterceptor(TunEndpoint* ep, llarp::DnsConfig conf) - : m_Endpoint{ep}, m_Config{conf} + public: + explicit DnsInterceptor( + std::function reply, net::ipaddr_t our_ip, llarp::DnsConfig conf) + : m_Reply{std::move(reply)}, m_OurIP{std::move(our_ip)}, m_Config{std::move(conf)} {} virtual ~DnsInterceptor() = default; @@ -68,17 +71,11 @@ namespace llarp void SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override { - const auto pkt = net::IPPacket::UDP( - from.getIPv4(), - ToNet(huint16_t{from.getPort()}), - to.getIPv4(), - ToNet(huint16_t{to.getPort()}), - buf); - - if (pkt.sz == 0) + auto pkt = net::IPPacket::make_udp(from, to, std::move(buf)); + + if (pkt.empty()) return; - m_Endpoint->HandleWriteIPPacket( - pkt.ConstBuffer(), net::ExpandV4(from.asIPv4()), net::ExpandV4(to.asIPv4()), 0); + m_Reply(std::move(pkt)); } void @@ -90,92 +87,131 @@ namespace llarp return std::nullopt; } -#ifdef ANDROID bool - WouldLoop(const SockAddr&, const SockAddr&) const override + WouldLoop(const SockAddr& to, const SockAddr& from) const override { +#ifdef __APPLE__ + (void)from; + // DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data + // through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual + // upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver + // won't work for us. However when active the mac also only queries the main tunnel IP for + // DNS, so we consider anything else to be upstream-bound DNS to let it through the tunnel. + return to.asIPv6() != m_OurIP(); +#else + if (auto maybe_addr = m_Config.m_QueryBind) + { + const auto& addr = *maybe_addr; + // omit traffic to and from our dns socket + return addr == to or addr == from; + } return false; - } #endif - -#ifdef __APPLE__ - // DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data - // through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual - // upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver, - // above, won't work for us). However when active the mac also only queries the main tunnel - // IP for DNS, so we consider anything else to be upstream-bound DNS to let it through the - // tunnel. - bool - WouldLoop(const SockAddr& to, const SockAddr&) const override - { - return to.asIPv6() != m_Endpoint->GetIfAddr(); } -#endif }; -#if defined(ANDROID) || defined(__APPLE__) class TunDNS : public dns::Server { + std::optional m_QueryBind; + net::ipaddr_t m_OurIP; TunEndpoint* const m_Endpoint; public: - std::weak_ptr PacketSource; + std::shared_ptr PacketSource; virtual ~TunDNS() = default; + explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf) - : dns::Server{ep->Router()->loop(), conf, ep->GetIfName()}, m_Endpoint{ep} + : dns::Server{ep->Router()->loop(), conf, 0} + , m_QueryBind{conf.m_QueryBind} + , m_OurIP{ToNet(ep->GetIfAddr())} + , m_Endpoint{ep} {} std::shared_ptr MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override { - auto ptr = std::make_shared(m_Endpoint, conf); + auto ptr = std::make_shared( + [ep = m_Endpoint](auto pkt) { + ep->HandleWriteIPPacket(pkt.ConstBuffer(), pkt.srcv6(), pkt.dstv6(), 0); + }, + m_OurIP, + conf); PacketSource = ptr; return ptr; } - - std::shared_ptr - MakeDefaultResolver() override - { - // android will not cache dns via unbound it only intercepts .loki - return nullptr; - } }; -#endif TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent) : service::Endpoint{r, parent} { - m_PacketRouter = std::make_unique( + m_PacketRouter = std::make_shared( [this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); }); } void TunEndpoint::SetupDNS() { -#if defined(ANDROID) || defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION)) - auto dns = std::make_shared(this, m_DnsConfig); - m_DNS = dns; - m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) { - const size_t ip_header_size = (pkt.Header()->ihl * 4); - - const uint8_t* ptr = pkt.buf + ip_header_size; - const auto dst = ToNet(pkt.dstv4()); - const auto src = ToNet(pkt.srcv4()); - const SockAddr laddr{src, nuint16_t{*reinterpret_cast(ptr)}}; - const SockAddr raddr{dst, nuint16_t{*reinterpret_cast(ptr + 2)}}; - - OwnedBuffer buf{pkt.sz - (8 + ip_header_size)}; - std::copy_n(ptr + 8, buf.sz, buf.buf.get()); - - if (dns->MaybeHandlePacket(dns->PacketSource, raddr, laddr, std::move(buf))) - return; + const auto& info = GetVPNInterface()->Info(); + if (m_DnsConfig.m_raw_dns) + { + auto dns = std::make_shared(this, m_DnsConfig); + if (auto vpn = Router()->GetVPNPlatform()) + { + // get the first local address we know of + std::optional localaddr; + for (auto res : dns->GetAllResolvers()) + { + if (localaddr) + continue; + if (auto ptr = res.lock()) + localaddr = ptr->GetLocalAddr(); + } + if (platform::is_windows) + { + auto dns_io = vpn->create_packet_io(0); + LogInfo("doing dns queries from ", *localaddr); + Router()->loop()->add_ticker( + [r = Router(), dns_io, handler = m_PacketRouter, src = localaddr]() { + net::IPPacket pkt = dns_io->ReadNextPacket(); + while (not pkt.empty()) + { + // reinject if for upstream dns + if (src and pkt.src() == *src) + { + LogInfo("reinject dns"); + std::function reply{std::move(pkt.reply)}; + reply(std::move(pkt)); + } + else + { + LogInfo("got dns packet from ", pkt.src(), " of size ", pkt.size(), "B"); + handler->HandleIPPacket(std::move(pkt)); + } + pkt = dns_io->ReadNextPacket(); + } + }); + m_RawDNS = dns_io; + } + } + m_DNS = dns; - HandleGotUserPacket(std::move(pkt)); - }); -#else - m_DNS = std::make_shared(Loop(), m_DnsConfig, GetIfName()); -#endif + m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) { + auto dns_pkt_src = dns->PacketSource; + if (const auto& reply = pkt.reply) + dns_pkt_src = std::make_shared(dns_pkt_src, reply); + if (dns->MaybeHandlePacket( + std::move(dns_pkt_src), pkt.dst(), pkt.src(), *pkt.L4OwnedBuffer())) + return; + + HandleGotUserPacket(std::move(pkt)); + }); + } + else + m_DNS = std::make_shared(Loop(), m_DnsConfig, info.index); + + if (m_RawDNS) + m_RawDNS->Start(); m_DNS->AddResolver(weak_from_this()); m_DNS->Start(); } @@ -969,45 +1005,39 @@ namespace llarp } vpn::InterfaceInfo info; - info.addrs.emplace(m_OurRange); + info.addrs.emplace_back(m_OurRange); if (m_BaseV6Address) { IPRange v6range = m_OurRange; v6range.addr = (*m_BaseV6Address) | m_OurRange.addr; LogInfo(Name(), " using v6 range: ", v6range); - info.addrs.emplace(v6range, AF_INET6); + info.addrs.emplace_back(v6range, AF_INET6); } info.ifname = m_IfName; - LogInfo(Name(), " setting up dns..."); - SetupDNS(); - - if (auto maybe_addr = m_DNS->FirstBoundPacketSourceAddr()) - info.dnsaddr = maybe_addr->asIPv4(); - LogInfo(Name(), " setting up network..."); try { - m_NetIf = Router()->GetVPNPlatform()->ObtainInterface(std::move(info), Router()); + m_NetIf = Router()->GetVPNPlatform()->CreateInterface(std::move(info), Router()); } catch (std::exception& ex) { LogError(Name(), " failed to set up network interface: ", ex.what()); - } - if (not m_NetIf) - { - LogError(Name(), " failed to obtain network interface"); return false; } - m_IfName = m_NetIf->IfName(); + + m_IfName = m_NetIf->Info().ifname; LogInfo(Name(), " got network interface ", m_IfName); - if (not Router()->loop()->add_network_interface(m_NetIf, [this](net::IPPacket pkt) { - m_PacketRouter->HandleIPPacket(std::move(pkt)); - })) + auto handle_packet = [netif = m_NetIf, pkt_router = m_PacketRouter](auto pkt) { + pkt.reply = [netif](auto pkt) { netif->WritePacket(std::move(pkt)); }; + pkt_router->HandleIPPacket(std::move(pkt)); + }; + + if (not Router()->loop()->add_network_interface(m_NetIf, std::move(handle_packet))) { LogError(Name(), " failed to add network interface"); return false; @@ -1025,8 +1055,9 @@ namespace llarp } } - m_router->routePoker().SetDNSMode(false); - + LogInfo(Name(), " setting up dns..."); + SetupDNS(); + Loop()->call_soon([this]() { m_router->routePoker()->SetDNSMode(false); }); return HasAddress(ourAddr); } @@ -1060,9 +1091,13 @@ namespace llarp bool TunEndpoint::Stop() { + // stop vpn tunnel + if (m_NetIf) + m_NetIf->Stop(); + if (m_RawDNS) + m_RawDNS->Stop(); // save address map if applicable -#ifndef ANDROID - if (m_PersistAddrMapFile) + if (m_PersistAddrMapFile and not platform::is_android) { const auto& file = *m_PersistAddrMapFile; LogInfo(Name(), " saving address map to ", file); @@ -1082,7 +1117,6 @@ namespace llarp maybe->write(data.data(), data.size()); } } -#endif if (m_DNS) m_DNS->Stop(); return llarp::service::Endpoint::Stop(); @@ -1096,12 +1130,18 @@ namespace llarp // is it already mapped? return the mapping if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end()) return itr->second; + + const auto& net = m_router->Net(); + const bool is_bogon = net.IsBogonIP(ip); // build up our candidates to choose + std::unordered_set candidates; for (const auto& entry : m_ExitMap.FindAllEntries(ip)) { - // make sure it is allowed by the range if the ip is a bogon - if (not IsBogon(ip) or entry.first.BogonContains(ip)) + // in the event the exit's range is a bogon range, make sure the ip is located in that range + // to allow it + if ((is_bogon and net.IsBogonRange(entry.first) and entry.first.Contains(ip)) + or entry.first.Contains(ip)) candidates.emplace(entry.second); } // no candidates? bail. @@ -1163,13 +1203,20 @@ namespace llarp return; } + std::function extra_cb; + if (not HasFlowToService(addr)) + { + extra_cb = [poker = Router()->routePoker()]() { poker->Up(); }; + } pkt.ZeroSourceAddress(); MarkAddressOutbound(addr); EnsurePathToService( addr, - [pkt, this](service::Address addr, service::OutboundContext* ctx) { + [pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) { if (ctx) { + if (extra_cb) + extra_cb(); ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit); Router()->TriggerPump(); return; diff --git a/llarp/handlers/tun.hpp b/llarp/handlers/tun.hpp index e8e4ad22a..3c254a9e3 100644 --- a/llarp/handlers/tun.hpp +++ b/llarp/handlers/tun.hpp @@ -2,22 +2,20 @@ #include #include -#include #include #include #include #include +#include +#include #include #include +#include #include - #include #include -#include -#include - namespace llarp { namespace handlers @@ -29,6 +27,12 @@ namespace llarp TunEndpoint(AbstractRouter* r, llarp::service::Context* parent); ~TunEndpoint() override; + vpn::NetworkInterface* + GetVPNInterface() override + { + return m_NetIf.get(); + } + int Rank() const override { @@ -43,7 +47,7 @@ namespace llarp bool MaybeHookDNS( - std::weak_ptr source, + std::shared_ptr source, const dns::Message& query, const SockAddr& to, const SockAddr& from) override; @@ -306,7 +310,7 @@ namespace llarp std::shared_ptr m_NetIf; - std::unique_ptr m_PacketRouter; + std::shared_ptr m_PacketRouter; std::optional m_TrafficPolicy; /// ranges we advetise as reachable @@ -316,6 +320,9 @@ namespace llarp /// a file to load / store the ephemeral address map to std::optional m_PersistAddrMapFile; + + /// for raw packet dns + std::shared_ptr m_RawDNS; }; } // namespace handlers diff --git a/llarp/lokinet_shared.cpp b/llarp/lokinet_shared.cpp index de8f48528..7d4a0f085 100644 --- a/llarp/lokinet_shared.cpp +++ b/llarp/lokinet_shared.cpp @@ -954,7 +954,7 @@ extern "C" dstport, llarp_buffer_t{reinterpret_cast(ptr), len}); - if (pkt.sz == 0) + if (pkt.empty()) return EINVAL; std::promise ret; ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() { diff --git a/llarp/net/address_info.hpp b/llarp/net/address_info.hpp index cd5698886..73d41939e 100644 --- a/llarp/net/address_info.hpp +++ b/llarp/net/address_info.hpp @@ -50,9 +50,19 @@ namespace llarp fromSockAddr(const SockAddr& address); /// get this as an explicit v4 or explicit v6 - std::variant + net::ipaddr_t IP() const; + /// get this as an v4 or throw if it is not one + inline net::ipv4addr_t + IPv4() const + { + auto ip = IP(); + if (auto* ptr = std::get_if(&ip)) + return *ptr; + throw std::runtime_error{"no ipv4 address found in address info"}; + } + std::string ToString() const; }; diff --git a/llarp/net/bogon_ranges.hpp b/llarp/net/bogon_ranges.hpp new file mode 100644 index 000000000..d11512672 --- /dev/null +++ b/llarp/net/bogon_ranges.hpp @@ -0,0 +1,36 @@ +#pragma once +#include "ip_range.hpp" + +namespace llarp +{ + static constexpr std::array bogonRanges_v6 = { + // zero + IPRange{huint128_t{0}, netmask_ipv6_bits(128)}, + // loopback + IPRange{huint128_t{1}, netmask_ipv6_bits(128)}, + // yggdrasil + IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)}, + // multicast + IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, + // local + IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, + // local + IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}}; + + static constexpr std::array bogonRanges_v4 = { + IPRange::FromIPv4(0, 0, 0, 0, 8), + IPRange::FromIPv4(10, 0, 0, 0, 8), + IPRange::FromIPv4(100, 64, 0, 0, 10), + IPRange::FromIPv4(127, 0, 0, 0, 8), + IPRange::FromIPv4(169, 254, 0, 0, 16), + IPRange::FromIPv4(172, 16, 0, 0, 12), + IPRange::FromIPv4(192, 0, 0, 0, 24), + IPRange::FromIPv4(192, 0, 2, 0, 24), + IPRange::FromIPv4(192, 88, 99, 0, 24), + IPRange::FromIPv4(192, 168, 0, 0, 16), + IPRange::FromIPv4(198, 18, 0, 0, 15), + IPRange::FromIPv4(198, 51, 100, 0, 24), + IPRange::FromIPv4(203, 0, 113, 0, 24), + IPRange::FromIPv4(224, 0, 0, 0, 4), + IPRange::FromIPv4(240, 0, 0, 0, 4)}; +} // namespace llarp diff --git a/llarp/net/interface_info.hpp b/llarp/net/interface_info.hpp new file mode 100644 index 000000000..c5d897264 --- /dev/null +++ b/llarp/net/interface_info.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +#include "ip_range.hpp" + +namespace llarp::net +{ + /// info about a network interface lokinet does not own + struct InterfaceInfo + { + /// human readable name of interface + std::string name; + /// interface's index + int index; + /// the addresses owned by this interface + std::vector addrs; + /// a gateway we can use if it exists + std::optional gateway; + }; +} // namespace llarp::net diff --git a/llarp/net/ip_address.cpp b/llarp/net/ip_address.cpp index 818df7ac8..3503ff7c7 100644 --- a/llarp/net/ip_address.cpp +++ b/llarp/net/ip_address.cpp @@ -127,7 +127,7 @@ namespace llarp SockAddr addr(m_ipAddress); const auto* addr6 = static_cast(addr); const uint8_t* raw = addr6->sin6_addr.s6_addr; - return IsIPv4Bogon(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15])); + return IPRange::V4MappedRange().Contains(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15])); } std::string diff --git a/llarp/net/ip_address.hpp b/llarp/net/ip_address.hpp index d86c77314..5be44071e 100644 --- a/llarp/net/ip_address.hpp +++ b/llarp/net/ip_address.hpp @@ -19,14 +19,12 @@ namespace llarp /// As a convenience, it can produce a SockAddr for dealing with network libraries which depend /// sockaddr structs. However, it does not keep this as a member variable and isn't responsible /// for its lifetime/memory/etc. - /// - /// TODO: IPv6 is not currently supported. - struct IpAddress + struct [[deprecated("use llarp::SockAddr instead")]] IpAddress { /// Empty constructor. IpAddress() = default; /// move construtor - IpAddress(IpAddress&&) = default; + IpAddress(IpAddress &&) = default; /// copy construct IpAddress(const IpAddress&); @@ -56,80 +54,64 @@ namespace llarp /// @param addr is an SockAddr to initialize from. IpAddress(const SockAddr& addr); - IpAddress& - operator=(const sockaddr& other); + IpAddress& operator=(const sockaddr& other); /// move assignment - IpAddress& - operator=(IpAddress&& other); + IpAddress& operator=(IpAddress&& other); /// copy assignment - IpAddress& - operator=(const IpAddress& other); + IpAddress& operator=(const IpAddress& other); /// Return the port. Returns -1 if no port has been provided. /// /// @return the port, if present - std::optional - getPort() const; + std::optional getPort() const; /// Return true if we have a port set otherwise return false - bool - hasPort() const; + bool hasPort() const; /// Set the port. /// /// @param port - void - setPort(std::optional port); + void setPort(std::optional port); /// Set the IP address. Follows the same logic as the constructor with the same signature, but /// doesn't overwrite the port if the port isn't present in the string. - void - setAddress(std::string_view str); - void - setAddress(std::string_view str, std::optional port); + void setAddress(std::string_view str); + void setAddress(std::string_view str, std::optional port); /// Returns true if this is an IPv4 address (or an IPv6 address representing an IPv4 address) /// /// TODO: could return an int (e.g. 4 or 6) or an enum /// /// @return true if this is an IPv4 address, false otherwise - bool - isIPv4(); + bool isIPv4(); /// Returns true if this represents a valid IpAddress, false otherwise. /// /// @return whether or not this IpAddress is empty - bool - isEmpty() const; + bool isEmpty() const; /// Creates an instance of SockAddr representing this IpAddress. /// /// @return an instance of a SockAddr created from this IpAddress - SockAddr - createSockAddr() const; + SockAddr createSockAddr() const; /// Returns true if this IpAddress is a bogon, false otherwise /// /// @return whether or not this IpAddress is a bogon - bool - isBogon() const; + bool isBogon() const; /// Returns a string representing this IpAddress /// /// @return string representation of this IpAddress - std::string - ToString() const; + std::string ToString() const; - std::string - toHost() const; + std::string toHost() const; - huint32_t - toIP() const; + huint32_t toIP() const; - huint128_t - toIP6() const; + huint128_t toIP6() const; // TODO: other utility functions left over from Addr which may be useful // IsBogon() const; @@ -137,11 +119,9 @@ namespace llarp // std::hash // to string / stream / etc - bool - operator<(const IpAddress& other) const; + bool operator<(const IpAddress& other) const; - bool - operator==(const IpAddress& other) const; + bool operator==(const IpAddress& other) const; private: bool m_empty = true; diff --git a/llarp/net/ip_packet.cpp b/llarp/net/ip_packet.cpp index d6e339ee5..9f822b818 100644 --- a/llarp/net/ip_packet.cpp +++ b/llarp/net/ip_packet.cpp @@ -1,6 +1,6 @@ #include "ip_packet.hpp" #include "ip.hpp" - +#include #include #include #include @@ -75,7 +75,6 @@ namespace llarp::net } throw std::invalid_argument{"no such ip protocol: '" + data + "'"}; } - inline static uint32_t* in6_uint32_ptr(in6_addr& addr) { @@ -106,30 +105,54 @@ namespace llarp::net return ExpandV4(dstv4()); } - bool - IPPacket::Load(const llarp_buffer_t& pkt) + IPPacket::IPPacket(byte_view_t view) + { + if (view.size() < MinSize) + { + _buf.resize(0); + return; + } + _buf.resize(view.size()); + std::copy_n(view.data(), size(), data()); + } + + IPPacket::IPPacket(size_t sz) + { + if (sz and sz < MinSize) + throw std::invalid_argument{"buffer size is too small to hold an ip packet"}; + _buf.resize(sz); + } + + SockAddr + IPPacket::src() const + { + auto port = SrcPort(); + if (IsV4()) + return SockAddr{ToNet(srcv4()), *port}; + else + return SockAddr{ToNet(srcv6()), *port}; + } + + SockAddr + IPPacket::dst() const { - if (pkt.sz > sizeof(buf) or pkt.sz == 0) - return false; - sz = pkt.sz; - std::copy_n(pkt.base, sz, buf); - return true; + auto port = DstPort(); + if (IsV4()) + return SockAddr{ToNet(dstv4()), *port}; + else + return SockAddr{ToNet(dstv6()), *port}; } - ManagedBuffer - IPPacket::ConstBuffer() const + IPPacket::IPPacket(std::vector&& stolen) : _buf{stolen} { - const byte_t* ptr = buf; - llarp_buffer_t b(ptr, sz); - return ManagedBuffer(b); + if (size() < MinSize) + _buf.resize(0); } - ManagedBuffer - IPPacket::Buffer() + byte_view_t + IPPacket::view() const { - byte_t* ptr = buf; - llarp_buffer_t b(ptr, sz); - return ManagedBuffer(b); + return byte_view_t{data(), size()}; } std::optional @@ -139,7 +162,7 @@ namespace llarp::net { case IPProtocol::TCP: case IPProtocol::UDP: - return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4) + 2)}; + return nuint16_t{*reinterpret_cast(data() + (Header()->ihl * 4) + 2)}; default: return std::nullopt; } @@ -152,7 +175,7 @@ namespace llarp::net { case IPProtocol::TCP: case IPProtocol::UDP: - return nuint16_t{*reinterpret_cast(buf + (Header()->ihl * 4))}; + return nuint16_t{*reinterpret_cast(data() + (Header()->ihl * 4))}; default: return std::nullopt; } @@ -397,7 +420,8 @@ namespace llarp::net auto oSrcIP = nuint32_t{hdr->saddr}; auto oDstIP = nuint32_t{hdr->daddr}; - + auto* buf = data(); + auto sz = size(); // L4 checksum auto ihs = size_t(hdr->ihl * 4); if (ihs <= sz) @@ -435,7 +459,7 @@ namespace llarp::net IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional flowlabel) { const size_t ihs = 4 + 4 + 16 + 16; - + const auto sz = size(); // XXX should've been checked at upper level? if (sz <= ihs) return; @@ -459,11 +483,11 @@ namespace llarp::net const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr); // TODO IPv6 header options - auto pld = buf + ihs; + auto* pld = data() + ihs; auto psz = sz - ihs; size_t fragoff = 0; - auto nextproto = hdr->proto; + auto nextproto = hdr->protocol; for (;;) { switch (nextproto) @@ -554,31 +578,27 @@ namespace llarp::net if (IsV4()) { constexpr auto icmp_Header_size = 8; - constexpr auto ip_Header_size = 20; - net::IPPacket pkt{}; - auto* pkt_Header = pkt.Header(); + auto ip_Header_size = Header()->ihl * 4; + auto pkt_size = (icmp_Header_size + ip_Header_size) * 2; + net::IPPacket pkt{static_cast(pkt_size)}; + auto* pkt_Header = pkt.Header(); pkt_Header->version = 4; pkt_Header->ihl = 0x05; pkt_Header->tos = 0; pkt_Header->check = 0; - pkt_Header->tot_len = ntohs(icmp_Header_size + ip_Header_size); + pkt_Header->tot_len = ntohs(pkt_size); pkt_Header->saddr = Header()->daddr; pkt_Header->daddr = Header()->saddr; pkt_Header->protocol = 1; // ICMP - pkt_Header->ttl = 1; + pkt_Header->ttl = Header()->ttl; pkt_Header->frag_off = htons(0b0100000000000000); - // size pf ip header - const size_t l3_HeaderSize = Header()->ihl * 4; - // size of l4 packet to reflect back - const size_t l4_PacketSize = 8; - pkt_Header->tot_len += ntohs(l4_PacketSize + l3_HeaderSize); uint16_t* checksum; - uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4); + uint8_t* itr = pkt.data() + ip_Header_size; uint8_t* icmp_begin = itr; // type 'destination unreachable' *itr++ = 3; - // code 'Destination host unknown error' + // code 'Destination host unknown error' *itr++ = 7; // checksum + unused oxenc::write_host_as_big(0, itr); @@ -588,14 +608,13 @@ namespace llarp::net oxenc::write_host_as_big(1500, itr); itr += 2; // copy ip header and first 8 bytes of datagram for icmp rject - std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr); - itr += l4_PacketSize + l3_HeaderSize; + std::copy_n(data(), ip_Header_size + icmp_Header_size, itr); + itr += ip_Header_size + icmp_Header_size; // calculate checksum of ip header - pkt_Header->check = ipchksum(pkt.buf, pkt_Header->ihl * 4); + pkt_Header->check = ipchksum(pkt.data(), ip_Header_size); const auto icmp_size = std::distance(icmp_begin, itr); // calculate icmp checksum *checksum = ipchksum(icmp_begin, icmp_size); - pkt.sz = ntohs(pkt_Header->tot_len); return pkt; } return std::nullopt; @@ -614,56 +633,85 @@ namespace llarp::net return std::nullopt; // check for invalid size - if (sz < (hdr->ihl * 4) + l4_HeaderSize) + if (size() < (hdr->ihl * 4) + l4_HeaderSize) return std::nullopt; - const uint8_t* ptr = buf + ((hdr->ihl * 4) + l4_HeaderSize); - return std::make_pair(reinterpret_cast(ptr), std::distance(ptr, buf + sz)); + const uint8_t* ptr = data() + ((hdr->ihl * 4) + l4_HeaderSize); + return std::make_pair(reinterpret_cast(ptr), std::distance(ptr, data() + size())); } - IPPacket - IPPacket::UDP( - nuint32_t srcaddr, - nuint16_t srcport, - nuint32_t dstaddr, - nuint16_t dstport, - const llarp_buffer_t& buf) + namespace { - net::IPPacket pkt; - - if (buf.sz + 28 > sizeof(pkt.buf)) + IPPacket + make_ip4_udp( + net::ipv4addr_t srcaddr, + net::port_t srcport, + net::ipv4addr_t dstaddr, + net::port_t dstport, + std::vector udp_data) { - pkt.sz = 0; + constexpr auto pkt_overhead = constants::udp_header_bytes + constants::ip_header_min_bytes; + net::IPPacket pkt{udp_data.size() + pkt_overhead}; + + auto* hdr = pkt.Header(); + pkt.data()[1] = 0; + hdr->version = 4; + hdr->ihl = 5; + hdr->tot_len = htons(pkt_overhead + udp_data.size()); + hdr->protocol = 0x11; // udp + hdr->ttl = 64; + hdr->frag_off = htons(0b0100000000000000); + + hdr->saddr = srcaddr.n; + hdr->daddr = dstaddr.n; + + // make udp packet + uint8_t* ptr = pkt.data() + constants::ip_header_min_bytes; + std::memcpy(ptr, &srcport.n, 2); + ptr += 2; + std::memcpy(ptr, &dstport.n, 2); + ptr += 2; + oxenc::write_host_as_big( + static_cast(udp_data.size() + constants::udp_header_bytes), ptr); + ptr += 2; + oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum + ptr += 2; + std::copy_n(udp_data.data(), udp_data.size(), ptr); + + hdr->check = 0; + hdr->check = net::ipchksum(pkt.data(), 20); return pkt; } - auto* hdr = pkt.Header(); - pkt.buf[1] = 0; - hdr->version = 4; - hdr->ihl = 5; - hdr->tot_len = htons(buf.sz + 28); - hdr->protocol = 0x11; // udp - hdr->ttl = 64; - hdr->frag_off = htons(0b0100000000000000); - - hdr->saddr = srcaddr.n; - hdr->daddr = dstaddr.n; - - // make udp packet - uint8_t* ptr = pkt.buf + 20; - std::memcpy(ptr, &srcport.n, 2); - ptr += 2; - std::memcpy(ptr, &dstport.n, 2); - ptr += 2; - oxenc::write_host_as_big(static_cast(buf.sz + 8), ptr); - ptr += 2; - oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum - ptr += 2; - std::copy_n(buf.base, buf.sz, ptr); - - hdr->check = 0; - hdr->check = net::ipchksum(pkt.buf, 20); - pkt.sz = 28 + buf.sz; - return pkt; + } // namespace + IPPacket + IPPacket::make_udp( + net::ipaddr_t srcaddr, + net::port_t srcport, + net::ipaddr_t dstaddr, + net::port_t dstport, + std::vector udp_data) + { + auto getfam = [](auto&& v) { + if (std::holds_alternative(v)) + return AF_INET; + else if (std::holds_alternative(v)) + return AF_INET6; + else + return AF_UNSPEC; + }; + auto fam = getfam(srcaddr); + if (fam != getfam(dstaddr)) + return net::IPPacket{size_t{}}; + if (fam == AF_INET) + { + return make_ip4_udp( + *std::get_if(&srcaddr), + srcport, + *std::get_if(&dstaddr), + dstport, + std::move(udp_data)); + } + // TODO: ipv6 + return net::IPPacket{size_t{}}; } - } // namespace llarp::net diff --git a/llarp/net/ip_packet.hpp b/llarp/net/ip_packet.hpp index a9db47904..14418d05f 100644 --- a/llarp/net/ip_packet.hpp +++ b/llarp/net/ip_packet.hpp @@ -69,7 +69,7 @@ namespace llarp::net } preamble; uint16_t payload_len; - uint8_t proto; + uint8_t protocol; uint8_t hoplimit; in6_addr srcaddr; in6_addr dstaddr; @@ -109,34 +109,119 @@ namespace llarp::net /// an Packet struct IPPacket { - static constexpr size_t MaxSize = 1500; + static constexpr size_t _max_size = 1500; llarp_time_t timestamp; - size_t sz; + std::vector _buf; - alignas(ip_header) byte_t buf[MaxSize]; + public: + IPPacket() : IPPacket{size_t{}} + {} + /// create an ip packet buffer of all zeros of size sz + explicit IPPacket(size_t sz); + /// create an ip packet from a view + explicit IPPacket(byte_view_t); + /// create an ip packet from a vector we then own + IPPacket(std::vector&&); - static IPPacket + ~IPPacket() = default; + + static constexpr size_t MaxSize = _max_size; + static constexpr size_t MinSize = 20; + + [[deprecated("deprecated because of llarp_buffer_t")]] static IPPacket UDP(nuint32_t srcaddr, nuint16_t srcport, nuint32_t dstaddr, nuint16_t dstport, - const llarp_buffer_t& data); + const llarp_buffer_t& data) + { + return make_udp(srcaddr, srcport, dstaddr, dstport, data.copy()); + } + + static IPPacket + make_udp( + net::ipaddr_t srcaddr, + net::port_t srcport, + net::ipaddr_t dstaddr, + net::port_t dstport, + std::vector udp_body); + + static inline IPPacket + make_udp(SockAddr src, SockAddr dst, std::variant> udp_body) + { + if (auto* vec = std::get_if>(&udp_body)) + return make_udp(src.getIP(), src.port(), dst.getIP(), dst.port(), std::move(*vec)); + else if (auto* buf = std::get_if(&udp_body)) + return make_udp(src, dst, buf->copy()); + else + return net::IPPacket{size_t{}}; + } + + [[deprecated("deprecated because of llarp_buffer_t")]] inline bool + Load(const llarp_buffer_t& buf) + { + _buf = buf.copy(); + if (size() >= MinSize) + return true; + _buf.resize(0); + return false; + } - ManagedBuffer - Buffer(); + [[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t + ConstBuffer() const + { + return llarp_buffer_t{_buf}; + } - ManagedBuffer - ConstBuffer() const; + /// steal the underlying vector + inline std::vector + steal() + { + std::vector buf; + buf.resize(0); + std::swap(_buf, buf); + return buf; + } - bool - Load(const llarp_buffer_t& buf); + inline byte_t* + data() + { + return _buf.data(); + } + + inline const byte_t* + data() const + { + return _buf.data(); + } + + constexpr size_t + capacity() const + { + return _max_size; + } + + inline size_t + size() const + { + return _buf.size(); + } + + inline bool + empty() const + { + return _buf.empty(); + } + + byte_view_t + view() const; struct CompareSize { bool operator()(const IPPacket& left, const IPPacket& right) { - return left.sz < right.sz; + return left.size() < right.size(); } }; @@ -152,25 +237,25 @@ namespace llarp::net inline ip_header* Header() { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline const ip_header* Header() const { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline ipv6_header* HeaderV6() { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline const ipv6_header* HeaderV6() const { - return reinterpret_cast(&buf[0]); + return reinterpret_cast(data()); } inline int @@ -179,6 +264,15 @@ namespace llarp::net return Header()->version; } + inline byte_t + protocol() const + { + if (IsV4()) + return Header()->protocol; + else + return HeaderV6()->protocol; + } + inline bool IsV4() const { @@ -226,6 +320,12 @@ namespace llarp::net huint128_t dst4to6Lan() const; + SockAddr + src() const; + + SockAddr + dst() const; + /// get destination port if applicable std::optional DstPort() const; @@ -238,6 +338,14 @@ namespace llarp::net std::optional> L4Data() const; + inline std::optional + L4OwnedBuffer() const + { + if (auto data = L4Data()) + return OwnedBuffer{reinterpret_cast(data->first), data->second}; + return std::nullopt; + } + void UpdateIPv4Address(nuint32_t src, nuint32_t dst); @@ -256,6 +364,8 @@ namespace llarp::net /// make an icmp unreachable reply packet based of this ip packet std::optional MakeICMPUnreachable() const; + + std::function reply; }; /// generate ip checksum diff --git a/llarp/net/ip_range.cpp b/llarp/net/ip_range.cpp index 08d234584..ea3fe858e 100644 --- a/llarp/net/ip_range.cpp +++ b/llarp/net/ip_range.cpp @@ -90,4 +90,15 @@ namespace llarp return addr.ToString(); } + std::string + IPRange::NetmaskString() const + { + if (IsV4()) + { + const huint32_t mask = net::TruncateV6(netmask_bits); + return mask.ToString(); + } + return netmask_bits.ToString(); + } + } // namespace llarp diff --git a/llarp/net/ip_range.hpp b/llarp/net/ip_range.hpp index e89e54865..201473852 100644 --- a/llarp/net/ip_range.hpp +++ b/llarp/net/ip_range.hpp @@ -9,10 +9,6 @@ namespace llarp { - /// forward declare - bool - IsBogon(huint128_t ip); - struct IPRange { using Addr_t = huint128_t; @@ -25,6 +21,12 @@ namespace llarp : addr{std::move(address)}, netmask_bits{std::move(netmask)} {} + static constexpr IPRange + V4MappedRange() + { + return IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)}; + } + static constexpr IPRange FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask) { @@ -42,8 +44,7 @@ namespace llarp constexpr bool IsV4() const { - constexpr auto ipv4_map = IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)}; - return ipv4_map.Contains(addr); + return V4MappedRange().Contains(addr); } /// get address family @@ -55,27 +56,6 @@ namespace llarp return AF_INET6; } - /// return true if we intersect with a bogon range - bool - BogonRange() const - { - // special case for 0.0.0.0/0 - if (IsV4() and netmask_bits == netmask_ipv6_bits(96)) - return false; - // special case for ::/0 - if (netmask_bits == huint128_t{0}) - return false; - return IsBogon(addr) or IsBogon(HighestAddr()); - } - - /// return true if we intersect with a bogon range *and* we contain the given address - template - bool - BogonContains(Addr&& addr) const - { - return BogonRange() and Contains(std::forward(addr)); - } - /// return the number of bits set in the hostmask constexpr int HostmaskBits() const @@ -147,6 +127,9 @@ namespace llarp std::string BaseAddressString() const; + std::string + NetmaskString() const; + bool FromString(std::string str); diff --git a/llarp/net/net.hpp b/llarp/net/net.hpp index d8bd77aaa..e54a7ee8e 100644 --- a/llarp/net/net.hpp +++ b/llarp/net/net.hpp @@ -9,6 +9,8 @@ #include #include +#include "interface_info.hpp" + #include #include // for itoa #include @@ -22,50 +24,73 @@ #include #include #include -#define inet_aton(x, y) inet_pton(AF_INET, x, y) #endif #ifndef _WIN32 #include #endif -bool -operator==(const sockaddr& a, const sockaddr& b); - -bool -operator==(const sockaddr_in& a, const sockaddr_in& b); +#include "bogon_ranges.hpp" -bool -operator==(const sockaddr_in6& a, const sockaddr_in6& b); - -bool -operator<(const sockaddr_in6& a, const sockaddr_in6& b); +namespace llarp +{ + inline bool + operator==(const in_addr& a, const in_addr& b) + { + return memcmp(&a, &b, sizeof(in_addr)) == 0; + } -bool -operator<(const in6_addr& a, const in6_addr& b); + inline bool + operator==(const in6_addr& a, const in6_addr& b) + { + return memcmp(&a, &b, sizeof(in6_addr)) == 0; + } -bool -operator==(const in6_addr& a, const in6_addr& b); + inline bool + operator==(const sockaddr_in& a, const sockaddr_in& b) + { + return a.sin_port == b.sin_port and a.sin_addr.s_addr == b.sin_addr.s_addr; + } -namespace llarp -{ - bool - IsIPv4Bogon(const huint32_t& addr); + inline bool + operator==(const sockaddr_in6& a, const sockaddr_in6& b) + { + return a.sin6_port == b.sin6_port and a.sin6_addr == b.sin6_addr; + } inline bool - IsIPv4Bogon(const nuint32_t& addr) + operator==(const sockaddr& a, const sockaddr& b) { - return IsIPv4Bogon(ToHost(addr)); + if (a.sa_family != b.sa_family) + return false; + switch (a.sa_family) + { + case AF_INET: + return reinterpret_cast(a) == reinterpret_cast(b); + case AF_INET6: + return reinterpret_cast(a) == reinterpret_cast(b); + default: + return false; + } } - bool - IsBogon(const in6_addr& addr); + inline bool + operator<(const in_addr& a, const in_addr& b) + { + return memcmp(&a, &b, sizeof(in_addr)) < 0; + } - bool - IsBogon(const huint128_t addr); + inline bool + operator<(const in6_addr& a, const in6_addr& b) + { + return memcmp(&a, &b, sizeof(in6_addr)) < 0; + } - bool - IsBogonRange(const in6_addr& host, const in6_addr& mask); + inline bool + operator<(const sockaddr_in6& a, const sockaddr_in6& b) + { + return a.sin6_addr < b.sin6_addr or a.sin6_port < b.sin6_port; + } namespace net { @@ -86,8 +111,27 @@ namespace llarp virtual std::optional AllInterfaces(SockAddr pubaddr) const = 0; - virtual SockAddr - Wildcard(int af = AF_INET) const = 0; + inline SockAddr + Wildcard(int af = AF_INET) const + { + if (af == AF_INET) + { + sockaddr_in addr{}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(0); + return SockAddr{addr}; + } + if (af == AF_INET6) + { + sockaddr_in6 addr6{}; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(0); + addr6.sin6_addr = IN6ADDR_ANY_INIT; + return SockAddr{addr6}; + } + throw std::invalid_argument{fmt::format("{} is not a valid address family")}; + } inline SockAddr WildcardWithPort(port_t port, int af = AF_INET) const @@ -104,12 +148,24 @@ namespace llarp HasInterfaceAddress(ipaddr_t ip) const = 0; /// return true if ip is considered a loopback address - virtual bool - IsLoopbackAddress(ipaddr_t ip) const = 0; + inline bool + IsLoopbackAddress(ipaddr_t ip) const + { + return var::visit( + [loopback6 = IPRange{huint128_t{uint128_t{0UL, 1UL}}, netmask_ipv6_bits(128)}, + loopback4 = IPRange::FromIPv4(127, 0, 0, 0, 8)](auto&& ip) { + const auto h_ip = ToHost(ip); + return loopback4.Contains(h_ip) or loopback6.Contains(h_ip); + }, + ip); + } /// return true if ip is considered a wildcard address - virtual bool - IsWildcardAddress(ipaddr_t ip) const = 0; + inline bool + IsWildcardAddress(ipaddr_t ip) const + { + return var::visit([](auto&& ip) { return not ip.n; }, ip); + } virtual std::optional GetBestNetIF(int af = AF_INET) const = 0; @@ -143,11 +199,63 @@ namespace llarp return std::nullopt; } - virtual bool - IsBogon(const SockAddr& addr) const = 0; + inline bool + IsBogon(const SockAddr& addr) const + { + return IsBogonIP(addr.asIPv6()); + } + + inline bool + IsBogonRange(const IPRange& range) const + { + // special case for 0.0.0.0/0 + if (range.IsV4() and range.netmask_bits == netmask_ipv6_bits(96)) + return false; + // special case for ::/0 + if (IsWildcardAddress(ToNet(range.netmask_bits))) + return false; + return IsBogonIP(range.addr) or IsBogonIP(range.HighestAddr()); + } + + inline bool + IsBogonIP(const net::ipaddr_t& addr) const + { + return IsBogonIP(var::visit( + [](auto&& ip) { + if constexpr (std::is_same_v>) + return ExpandV4(ToHost(ip)); + else + return ToHost(ip); + }, + addr)); + } + inline bool + IsBogonIP(const huint128_t& addr) const + { + if (not IPRange::V4MappedRange().Contains(addr)) + { + for (const auto& v6_range : bogonRanges_v6) + { + if (v6_range.Contains(addr)) + return true; + } + return false; + } + const auto v4_addr = net::TruncateV6(addr); + for (const auto& v4_range : bogonRanges_v4) + { + if (v4_range.Contains(v4_addr)) + return true; + } + return false; + } virtual std::optional GetInterfaceIndex(ipaddr_t ip) const = 0; + + /// returns a vector holding all of our network interfaces + virtual std::vector + AllNetworkInterfaces() const = 0; }; } // namespace net diff --git a/llarp/net/net_bits.hpp b/llarp/net/net_bits.hpp index c9c2c9c97..d36297593 100644 --- a/llarp/net/net_bits.hpp +++ b/llarp/net/net_bits.hpp @@ -40,14 +40,6 @@ namespace llarp // IPv4 mapped address live at ::ffff:0:0/96 constexpr std::array ipv4_map_prefix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; - constexpr bool - ipv6_is_mapped_ipv4(const in6_addr& addr) - { - for (size_t i = 0; i < ipv4_map_prefix.size(); i++) - if (addr.s6_addr[i] != ipv4_map_prefix[i]) - return false; - return true; - } namespace net { inline auto diff --git a/llarp/net/net.cpp b/llarp/net/posix.cpp similarity index 67% rename from llarp/net/net.cpp rename to llarp/net/posix.cpp index 419a718fd..aaff8ece0 100644 --- a/llarp/net/net.cpp +++ b/llarp/net/posix.cpp @@ -1,19 +1,9 @@ #include "net.hpp" - #include "net_if.hpp" #include #include -#ifdef ANDROID -#include -#endif - -#ifndef _WIN32 #include -#ifndef ANDROID -#include -#endif -#endif #include "ip.hpp" #include "ip_range.hpp" @@ -23,116 +13,15 @@ #ifdef ANDROID #include #else -#ifdef _WIN32 -#include -#include -#else #include #endif -#endif #include #include #include -bool -operator==(const sockaddr& a, const sockaddr& b) -{ - if (a.sa_family != b.sa_family) - return false; - switch (a.sa_family) - { - case AF_INET: - return *((const sockaddr_in*)&a) == *((const sockaddr_in*)&b); - case AF_INET6: - return *((const sockaddr_in6*)&a) == *((const sockaddr_in6*)&b); - default: - return false; - } -} - -bool -operator<(const sockaddr_in6& a, const sockaddr_in6& b) -{ - return a.sin6_addr < b.sin6_addr || a.sin6_port < b.sin6_port; -} - -bool -operator<(const in6_addr& a, const in6_addr& b) -{ - return memcmp(&a, &b, sizeof(in6_addr)) < 0; -} - -bool -operator==(const in6_addr& a, const in6_addr& b) -{ - return memcmp(&a, &b, sizeof(in6_addr)) == 0; -} - -bool -operator==(const sockaddr_in& a, const sockaddr_in& b) -{ - return a.sin_port == b.sin_port && a.sin_addr.s_addr == b.sin_addr.s_addr; -} - -bool -operator==(const sockaddr_in6& a, const sockaddr_in6& b) -{ - return a.sin6_port == b.sin6_port && a.sin6_addr == b.sin6_addr; -} - namespace llarp::net { - class Platform_Base : public llarp::net::Platform - { - public: - bool - IsLoopbackAddress(ipaddr_t ip) const override - { - return var::visit( - [loopback6 = IPRange{huint128_t{uint128_t{0UL, 1UL}}, netmask_ipv6_bits(128)}, - loopback4 = IPRange::FromIPv4(127, 0, 0, 0, 8)](auto&& ip) { - const auto h_ip = ToHost(ip); - return loopback4.Contains(h_ip) or loopback6.Contains(h_ip); - }, - ip); - } - - SockAddr - Wildcard(int af) const override - { - if (af == AF_INET) - { - sockaddr_in addr{}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(0); - return SockAddr{addr}; - } - if (af == AF_INET6) - { - sockaddr_in6 addr6{}; - addr6.sin6_family = AF_INET6; - addr6.sin6_port = htons(0); - addr6.sin6_addr = IN6ADDR_ANY_INIT; - return SockAddr{addr6}; - } - throw std::invalid_argument{fmt::format("{} is not a valid address family")}; - } - - bool - IsBogon(const llarp::SockAddr& addr) const override - { - return llarp::IsBogon(addr.asIPv6()); - } - - bool - IsWildcardAddress(ipaddr_t ip) const override - { - return var::visit([](auto&& ip) { return not ip.n; }, ip); - } - }; - #ifdef _WIN32 class Platform_Impl : public Platform_Base { @@ -293,11 +182,30 @@ namespace llarp::net { return GetInterfaceIndex(ip) != std::nullopt; } + + std::vector + AllNetworkInterfaces() const override + { + std::vector all; + iter_adapters([&all](auto* a) { + auto& cur = all.emplace_back(); + cur.index = a->IfIndex; + cur.name = a->AdapterName; + for (auto* addr = a->FirstUnicastAddress; addr and addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + cur.addrs.emplace_back( + saddr.asIPv6(), + ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family)); + } + }); + return all; + } }; #else - class Platform_Impl : public Platform_Base + class Platform_Impl : public Platform { template void @@ -469,6 +377,33 @@ namespace llarp::net }); return found; } + std::vector + AllNetworkInterfaces() const override + { + std::unordered_map ifmap; + iter_all([&ifmap](auto* i) { + if (i == nullptr or i->ifa_addr == nullptr) + return; + + const auto fam = i->ifa_addr->sa_family; + if (fam != AF_INET and fam != AF_INET6) + return; + + auto& ent = ifmap[i->ifa_name]; + if (ent.name.empty()) + { + ent.name = i->ifa_name; + ent.index = if_nametoindex(i->ifa_name); + } + SockAddr addr{*i->ifa_addr}; + SockAddr mask{*i->ifa_netmask}; + ent.addrs.emplace_back(addr.asIPv6(), mask.asIPv6()); + }); + std::vector all; + for (auto& [name, ent] : ifmap) + all.emplace_back(std::move(ent)); + return all; + } }; #endif @@ -480,100 +415,3 @@ namespace llarp::net return &g_plat; } } // namespace llarp::net - -namespace llarp -{ -#if !defined(TESTNET) - static constexpr std::array bogonRanges_v6 = { - // zero - IPRange{huint128_t{0}, netmask_ipv6_bits(128)}, - // loopback - IPRange{huint128_t{1}, netmask_ipv6_bits(128)}, - // yggdrasil - IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)}, - // multicast - IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, - // local - IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}, - // local - IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}}; - - static constexpr std::array bogonRanges_v4 = { - IPRange::FromIPv4(0, 0, 0, 0, 8), - IPRange::FromIPv4(10, 0, 0, 0, 8), - IPRange::FromIPv4(100, 64, 0, 0, 10), - IPRange::FromIPv4(127, 0, 0, 0, 8), - IPRange::FromIPv4(169, 254, 0, 0, 16), - IPRange::FromIPv4(172, 16, 0, 0, 12), - IPRange::FromIPv4(192, 0, 0, 0, 24), - IPRange::FromIPv4(192, 0, 2, 0, 24), - IPRange::FromIPv4(192, 88, 99, 0, 24), - IPRange::FromIPv4(192, 168, 0, 0, 16), - IPRange::FromIPv4(198, 18, 0, 0, 15), - IPRange::FromIPv4(198, 51, 100, 0, 24), - IPRange::FromIPv4(203, 0, 113, 0, 24), - IPRange::FromIPv4(224, 0, 0, 0, 4), - IPRange::FromIPv4(240, 0, 0, 0, 4)}; - -#endif - - bool - IsBogon(const in6_addr& addr) - { -#if defined(TESTNET) - (void)addr; - return false; -#else - if (not ipv6_is_mapped_ipv4(addr)) - { - const auto ip = net::In6ToHUInt(addr); - for (const auto& range : bogonRanges_v6) - { - if (range.Contains(ip)) - return true; - } - return false; - } - return IsIPv4Bogon( - ipaddr_ipv4_bits(addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15])); -#endif - } - - bool - IsBogon(const huint128_t ip) - { - const nuint128_t netIP{ntoh128(ip.h)}; - in6_addr addr{}; - std::copy_n((const uint8_t*)&netIP.n, 16, &addr.s6_addr[0]); - return IsBogon(addr); - } - - bool - IsBogonRange(const in6_addr& host, const in6_addr&) - { - // TODO: implement me - return IsBogon(host); - } - -#if !defined(TESTNET) - bool - IsIPv4Bogon(const huint32_t& addr) - { - for (const auto& bogon : bogonRanges_v4) - { - if (bogon.Contains(addr)) - { - return true; - } - } - return false; - } -#else - bool - IsIPv4Bogon(const huint32_t&) - { - return false; - } -#endif - -} // namespace llarp diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp index 7f66e0aff..2e9595bc8 100644 --- a/llarp/net/sock_addr.cpp +++ b/llarp/net/sock_addr.cpp @@ -1,7 +1,9 @@ #include "sock_addr.hpp" +#include "ip_range.hpp" #include "address_info.hpp" #include "ip.hpp" #include "net_bits.hpp" +#include "net.hpp" #include #include #include @@ -11,17 +13,6 @@ namespace llarp { - bool - operator==(const in6_addr& lh, const in6_addr& rh) - { - return memcmp(&lh, &rh, sizeof(in6_addr)) == 0; - } - - bool - operator<(const in6_addr& lh, const in6_addr& rh) - { - return memcmp(&lh, &rh, sizeof(in6_addr)) < 0; - } /// shared utility functions /// @@ -156,7 +147,7 @@ namespace llarp init(); memcpy(&m_addr, &other, sizeof(sockaddr_in6)); - if (ipv6_is_mapped_ipv4(other.sin6_addr)) + if (IPRange::V4MappedRange().Contains(asIPv6())) { setIPv4( other.sin6_addr.s6_addr[12], @@ -179,9 +170,8 @@ namespace llarp SockAddr::operator=(const in6_addr& other) { init(); - memcpy(&m_addr.sin6_addr.s6_addr, &other.s6_addr, sizeof(m_addr.sin6_addr.s6_addr)); - if (ipv6_is_mapped_ipv4(other)) + if (IPRange::V4MappedRange().Contains(asIPv6())) { setIPv4(other.s6_addr[12], other.s6_addr[13], other.s6_addr[14], other.s6_addr[15]); m_addr4.sin_port = m_addr.sin6_port; @@ -335,7 +325,7 @@ namespace llarp bool SockAddr::isIPv4() const { - return ipv6_is_mapped_ipv4(m_addr.sin6_addr); + return IPRange::V4MappedRange().Contains(asIPv6()); } bool SockAddr::isIPv6() const @@ -438,10 +428,10 @@ namespace llarp setPort(ToNet(port)); } - uint16_t - SockAddr::getPort() const + net::port_t + SockAddr::port() const { - return ntohs(m_addr.sin6_port); + return net::port_t{m_addr.sin6_port}; } } // namespace llarp diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index 82ea367c7..7634c6e23 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -141,9 +141,16 @@ namespace llarp setPort(huint16_t{port}); } - /// port is always returned in native (host) order - uint16_t - getPort() const; + /// get the port of this sockaddr in network order + net::port_t + port() const; + + /// port is always returned in host order + inline uint16_t + getPort() const + { + return ToHost(port()).h; + } /// True if this stores an IPv6 address, false if IPv4. bool diff --git a/llarp/net/win32.cpp b/llarp/net/win32.cpp new file mode 100644 index 000000000..4f166fb84 --- /dev/null +++ b/llarp/net/win32.cpp @@ -0,0 +1,216 @@ +#include "net.hpp" + +#include "net_if.hpp" +#include +#include + +#include "ip.hpp" +#include "ip_range.hpp" +#include +#include + +#include +#include + +#include +#include +#include + +namespace llarp::net +{ + class Platform_Impl : public Platform + { + /// visit all adapters (not addresses). windows serves net info per adapter unlink posix which + /// gives a list of all distinct addresses. + template + void + iter_adapters(Visit_t&& visit, int af = AF_UNSPEC) const + { + ULONG sz{}; + GetAdaptersAddresses(af, 0, nullptr, nullptr, &sz); + auto ptr = std::make_unique(sz); + auto* addrs = reinterpret_cast(ptr.get()); + + if (auto err = GetAdaptersAddresses(af, 0, nullptr, addrs, &sz); err != ERROR_SUCCESS) + throw llarp::win32::error{err, "GetAdaptersAddresses()"}; + + for (auto* addr = addrs; addr->Next; addr = addr->Next) + visit(addr); + } + + template + bool + adapter_has_ip(adapter_t* a, ipaddr_t ip) const + { + for (auto* addr = a->FirstUnicastAddress; addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + LogDebug(fmt::format("'{}' has address '{}'", a->AdapterName, saddr)); + if (saddr.getIP() == ip) + return true; + } + return false; + } + + template + bool + adapter_has_fam(adapter_t* a, int af) const + { + for (auto* addr = a->FirstUnicastAddress; addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + if (saddr.Family() == af) + return true; + } + return false; + } + + public: + std::optional + GetInterfaceIndex(ipaddr_t ip) const override + { + std::optional found; + int af{AF_INET}; + if (std::holds_alternative(ip)) + af = AF_INET6; + iter_adapters( + [&found, ip, this](auto* adapter) { + if (found) + return; + + LogDebug(fmt::format( + "visit adapter looking for '{}': '{}' idx={}", + ip, + adapter->AdapterName, + adapter->IfIndex)); + if (adapter_has_ip(adapter, ip)) + { + found = adapter->IfIndex; + } + }, + af); + return found; + } + + std::optional + GetInterfaceAddr(std::string_view name, int af) const override + { + std::optional found; + iter_adapters([name = std::string{name}, af, &found, this](auto* a) { + if (found) + return; + if (std::string{a->AdapterName} != name) + return; + + if (adapter_has_fam(a, af)) + found = SockAddr{*a->FirstUnicastAddress->Address.lpSockaddr}; + }); + return found; + } + + std::optional + AllInterfaces(SockAddr fallback) const override + { + // windows seems to not give a shit about source address + return fallback.isIPv6() ? SockAddr{"[::]"} : SockAddr{"0.0.0.0"}; + } + + std::optional + FindFreeTun() const override + { + return "lokitun0"; + } + + std::optional + GetBestNetIF(int) const override + { + // TODO: implement me ? + return std::nullopt; + } + + std::optional + FindFreeRange() const override + { + std::list currentRanges; + iter_adapters([¤tRanges](auto* i) { + for (auto* addr = i->FirstUnicastAddress; addr and addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + currentRanges.emplace_back( + saddr.asIPv6(), + ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family)); + } + }); + + auto ownsRange = [¤tRanges](const IPRange& range) -> bool { + for (const auto& ownRange : currentRanges) + { + if (ownRange * range) + return true; + } + return false; + }; + // generate possible ranges to in order of attempts + std::list possibleRanges; + for (byte_t oct = 16; oct < 32; ++oct) + { + possibleRanges.emplace_back(IPRange::FromIPv4(172, oct, 0, 1, 16)); + } + for (byte_t oct = 0; oct < 255; ++oct) + { + possibleRanges.emplace_back(IPRange::FromIPv4(10, oct, 0, 1, 16)); + } + for (byte_t oct = 0; oct < 255; ++oct) + { + possibleRanges.emplace_back(IPRange::FromIPv4(192, 168, oct, 1, 24)); + } + // for each possible range pick the first one we don't own + for (const auto& range : possibleRanges) + { + if (not ownsRange(range)) + return range; + } + return std::nullopt; + } + + std::string + LoopbackInterfaceName() const override + { + // todo: implement me? does windows even have a loopback? + return ""; + } + + bool + HasInterfaceAddress(ipaddr_t ip) const override + { + return GetInterfaceIndex(ip) != std::nullopt; + } + + std::vector + AllNetworkInterfaces() const override + { + std::vector all; + iter_adapters([&all](auto* a) { + auto& cur = all.emplace_back(); + cur.index = a->IfIndex; + cur.name = a->AdapterName; + for (auto* addr = a->FirstUnicastAddress; addr and addr->Next; addr = addr->Next) + { + SockAddr saddr{*addr->Address.lpSockaddr}; + cur.addrs.emplace_back( + saddr.asIPv6(), + ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family)); + } + }); + return all; + } + }; + + const Platform_Impl g_plat{}; + + const Platform* + Platform::Default_ptr() + { + return &g_plat; + } +} // namespace llarp::net diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index 516a6d02d..0790c1238 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -877,7 +877,7 @@ namespace llarp auto self = shared_from_this(); bool result = true; for (const auto& hook : m_ObtainedExitHooks) - result &= hook(self, B); + result = hook(self, B) and result; m_ObtainedExitHooks.clear(); return result; } @@ -897,7 +897,7 @@ namespace llarp for (const auto& pkt : msg.X) { if (pkt.size() <= 8) - return false; + continue; auto counter = oxenc::load_big_to_host(pkt.data()); if (m_ExitTrafficHandler( self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol)) diff --git a/llarp/path/transit_hop.cpp b/llarp/path/transit_hop.cpp index 903326260..4efe3540f 100644 --- a/llarp/path/transit_hop.cpp +++ b/llarp/path/transit_hop.cpp @@ -397,11 +397,9 @@ namespace llarp if (pkt.size() <= 8) continue; auto counter = oxenc::load_big_to_host(pkt.data()); - sent &= endpoint->QueueOutboundTraffic( - info.rxID, - ManagedBuffer(llarp_buffer_t(pkt.data() + 8, pkt.size() - 8)), - counter, - msg.protocol); + llarp_buffer_t buf{pkt.data() + 8, pkt.size() - 8}; + sent = + endpoint->QueueOutboundTraffic(info.rxID, buf.copy(), counter, msg.protocol) and sent; } return sent; } diff --git a/llarp/quic/client.cpp b/llarp/quic/client.cpp index 8cf9d7f80..007e8e8b6 100644 --- a/llarp/quic/client.cpp +++ b/llarp/quic/client.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include diff --git a/llarp/quic/connection.cpp b/llarp/quic/connection.cpp index 71b84cfd4..f9567f4ac 100644 --- a/llarp/quic/connection.cpp +++ b/llarp/quic/connection.cpp @@ -333,13 +333,11 @@ namespace llarp::quic extern "C" inline void ngtcp_trace_logger([[maybe_unused]] void* user_data, const char* fmt, ...) { + std::array buf{}; va_list ap; va_start(ap, fmt); - if (char* msg; vasprintf(&msg, fmt, ap) >= 0) - { - LogTrace{msg}; - std::free(msg); - } + if (vsnprintf(buf.data(), buf.size(), fmt, ap) >= 0) + LogTrace(fmt::format("{}", buf.data())); va_end(ap); } #endif diff --git a/llarp/quic/endpoint.cpp b/llarp/quic/endpoint.cpp index 037011883..4fa35434a 100644 --- a/llarp/quic/endpoint.cpp +++ b/llarp/quic/endpoint.cpp @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include diff --git a/llarp/quic/tunnel.cpp b/llarp/quic/tunnel.cpp index 480d14177..9b09e5e2f 100644 --- a/llarp/quic/tunnel.cpp +++ b/llarp/quic/tunnel.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/llarp/router/abstractrouter.hpp b/llarp/router/abstractrouter.hpp index 69b4ccf4a..4826947a6 100644 --- a/llarp/router/abstractrouter.hpp +++ b/llarp/router/abstractrouter.hpp @@ -181,8 +181,8 @@ namespace llarp virtual ILinkManager& linkManager() = 0; - virtual RoutePoker& - routePoker() = 0; + virtual const std::shared_ptr& + routePoker() const = 0; virtual I_RCLookupHandler& rcLookupHandler() = 0; @@ -215,6 +215,10 @@ namespace llarp virtual void Stop() = 0; + /// indicate we are about to sleep for a while + virtual void + Freeze() = 0; + /// thaw from long sleep or network changed event virtual void Thaw() = 0; diff --git a/llarp/router/route_poker.cpp b/llarp/router/route_poker.cpp index 352ac1625..1c55e38ad 100644 --- a/llarp/router/route_poker.cpp +++ b/llarp/router/route_poker.cpp @@ -7,83 +7,93 @@ namespace llarp { + namespace + { + auto logcat = log::Cat("route-poker"); + } + void - RoutePoker::AddRoute(huint32_t ip) + RoutePoker::AddRoute(net::ipv4addr_t ip) { - m_PokedRoutes[ip] = m_CurrentGateway; - if (m_CurrentGateway.h == 0) - { - llarp::LogDebug("RoutePoker::AddRoute no current gateway, cannot enable route."); - } - else if (m_Enabled or m_Enabling) + bool has_existing = m_PokedRoutes.count(ip); + // set up route and apply as needed + auto& gw = m_PokedRoutes[ip]; + if (m_CurrentGateway) { - llarp::LogInfo( - "RoutePoker::AddRoute enabled, enabling route to ", ip, " via ", m_CurrentGateway); - EnableRoute(ip, m_CurrentGateway); + // remove existing mapping as needed + if (has_existing) + DisableRoute(ip, gw); + // update and add new mapping + gw = *m_CurrentGateway; + EnableRoute(ip, gw); } else - { - llarp::LogDebug("RoutePoker::AddRoute disabled, not enabling route."); - } + gw = net::ipv4addr_t{}; } void - RoutePoker::DisableRoute(huint32_t ip, huint32_t gateway) + RoutePoker::DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway) { - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - route.DelRoute(ip, gateway); + if (ip.n and gateway.n and IsEnabled()) + { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + route.DelRoute(ip, gateway); + } } void - RoutePoker::EnableRoute(huint32_t ip, huint32_t gateway) + RoutePoker::EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway) { - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - route.AddRoute(ip, gateway); + if (ip.n and gateway.n and IsEnabled()) + { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + route.AddRoute(ip, gateway); + } } void - RoutePoker::DelRoute(huint32_t ip) + RoutePoker::DelRoute(net::ipv4addr_t ip) { const auto itr = m_PokedRoutes.find(ip); if (itr == m_PokedRoutes.end()) return; - if (m_Enabled) - DisableRoute(itr->first, itr->second); + DisableRoute(itr->first, itr->second); m_PokedRoutes.erase(itr); } void - RoutePoker::Init(AbstractRouter* router, bool enable) + RoutePoker::Start(AbstractRouter* router) { m_Router = router; - m_Enabled = enable; - m_CurrentGateway = {0}; + if (m_Router->IsServiceNode()) + return; + + m_Router->loop()->call_every(100ms, weak_from_this(), [this]() { Update(); }); } void RoutePoker::DeleteAllRoutes() { // DelRoute will check enabled, so no need here - for (const auto& [ip, gateway] : m_PokedRoutes) - DelRoute(ip); + for (const auto& item : m_PokedRoutes) + DelRoute(item.first); } void RoutePoker::DisableAllRoutes() { for (const auto& [ip, gateway] : m_PokedRoutes) + { DisableRoute(ip, gateway); + } } void - RoutePoker::EnableAllRoutes() + RoutePoker::RefreshAllRoutes() { - for (auto& [ip, gateway] : m_PokedRoutes) - { - gateway = m_CurrentGateway; - EnableRoute(ip, m_CurrentGateway); - } + for (const auto& item : m_PokedRoutes) + AddRoute(item.first); } RoutePoker::~RoutePoker() @@ -91,149 +101,145 @@ namespace llarp if (not m_Router or not m_Router->GetVPNPlatform()) return; - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + auto& route = m_Router->GetVPNPlatform()->RouteManager(); for (const auto& [ip, gateway] : m_PokedRoutes) { - if (gateway.h) + if (gateway.n and ip.n) route.DelRoute(ip, gateway); } route.DelBlackhole(); } - std::optional - RoutePoker::GetDefaultGateway() const + bool + RoutePoker::IsEnabled() const { if (not m_Router) - throw std::runtime_error("Attempting to use RoutePoker before calling Init"); + throw std::runtime_error{"Attempting to use RoutePoker before calling Init"}; + if (m_Router->IsServiceNode()) + return false; + if (const auto& conf = m_Router->GetConfig()) + return conf->network.m_EnableRoutePoker; - const auto ep = m_Router->hiddenServiceContext().GetDefault(); - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - const auto gateways = route.GetGatewaysNotOnInterface(ep->GetIfName()); - if (gateways.empty()) - { - return std::nullopt; - } - if (auto* ptr = std::get_if(&gateways[0])) - { - return huint32_t{*ptr}; - } - return std::nullopt; + throw std::runtime_error{"Attempting to use RoutePoker with router with no config set"}; } void RoutePoker::Update() { if (not m_Router) - throw std::runtime_error("Attempting to use RoutePoker before calling Init"); + throw std::runtime_error{"Attempting to use RoutePoker before calling Init"}; - // check for network - const auto maybe = GetDefaultGateway(); - if (not maybe.has_value()) - { -#ifndef ANDROID - LogError("Network is down"); -#endif - // mark network lost - m_HasNetwork = false; + // ensure we have an endpoint + auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (ep == nullptr) + return; + // ensure we have a vpn platform + auto* platform = m_Router->GetVPNPlatform(); + if (platform == nullptr) + return; + // ensure we have a vpn interface + auto* vpn = ep->GetVPNInterface(); + if (vpn == nullptr) return; + + auto& route = platform->RouteManager(); + + // find current gateways + auto gateways = route.GetGatewaysNotOnInterface(*vpn); + std::optional next_gw; + for (auto& gateway : gateways) + { + if (auto* gw_ptr = std::get_if(&gateway)) + next_gw = *gw_ptr; } - const huint32_t gateway = *maybe; - const bool gatewayChanged = m_CurrentGateway.h != 0 and m_CurrentGateway != gateway; + auto is_equal = [](auto lhs, auto rhs) { + if (lhs == std::nullopt and rhs == std::nullopt) + return true; + if (lhs and rhs) + return *lhs == *rhs; + return false; + }; - if (m_CurrentGateway != gateway) + // update current gateway and apply state chnages as needed + if (not is_equal(m_CurrentGateway, next_gw)) { - LogInfo("found default gateway: ", gateway); - m_CurrentGateway = gateway; - if (m_Enabling) + if (next_gw and m_CurrentGateway) { - EnableAllRoutes(); - Up(); + log::info(logcat, "default gateway changed from {} to {}", *m_CurrentGateway, *next_gw); + m_CurrentGateway = next_gw; + m_Router->Thaw(); + if (m_Router->HasClientExit()) + Up(); + else + RefreshAllRoutes(); + } + else if (m_CurrentGateway) + { + log::warning(logcat, "default gateway {} has gone away", *m_CurrentGateway); + m_CurrentGateway = next_gw; + m_Router->Freeze(); + } + else if (next_gw) + { + log::info(logcat, "default gateway found at {}", *next_gw); + m_CurrentGateway = next_gw; } - } - // revive network connectitivity on gateway change or network wakeup - if (gatewayChanged or not m_HasNetwork) - { - LogInfo("our network changed, thawing router state"); - m_Router->Thaw(); - m_HasNetwork = true; } } void RoutePoker::SetDNSMode(bool exit_mode_on) const { - if (auto dns_server = m_Router->hiddenServiceContext().GetDefault()->DNS()) + auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (not ep) + return; + if (auto dns_server = ep->DNS()) dns_server->SetDNSMode(exit_mode_on); } void - RoutePoker::Enable() + RoutePoker::Up() { - if (m_Enabled) - return; - - if (m_Router->GetConfig()->network.m_EnableRoutePoker) + if (IsEnabled()) { - m_Enabling = true; - Update(); - m_Enabling = false; - m_Enabled = true; + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + + // black hole all routes if enabled + if (m_Router->GetConfig()->network.m_BlackholeRoutes) + route.AddBlackhole(); + + // explicit route pokes for first hops + m_Router->ForEachPeer( + [this](auto session, auto) { AddRoute(session->GetRemoteEndpoint().getIPv4()); }, false); + // add default route + const auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (auto* vpn = ep->GetVPNInterface()) + route.AddDefaultRouteViaInterface(*vpn); } - SetDNSMode(true); } - void - RoutePoker::Disable() - { - if (not m_Enabled) - return; - - DisableAllRoutes(); - m_Enabled = false; - - SetDNSMode(false); - } - - void - RoutePoker::Up() - { - if (not m_Router->GetConfig()->network.m_EnableRoutePoker) - return; - - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - - // black hole all routes if enabled - if (m_Router->GetConfig()->network.m_BlackholeRoutes) - route.AddBlackhole(); - - // explicit route pokes for first hops - m_Router->ForEachPeer( - [&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().asIPv4()); }, - false); - // add default route - const auto ep = m_Router->hiddenServiceContext().GetDefault(); - route.AddDefaultRouteViaInterface(ep->GetIfName()); - } - void RoutePoker::Down() { - if (not m_Router->GetConfig()->network.m_EnableRoutePoker) - return; - // unpoke routes for first hops m_Router->ForEachPeer( - [&](auto session, auto) mutable { DelRoute(session->GetRemoteEndpoint().asIPv4()); }, - false); + [this](auto session, auto) { DelRoute(session->GetRemoteEndpoint().getIPv4()); }, false); + // remove default route - const auto ep = m_Router->hiddenServiceContext().GetDefault(); - vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); - route.DelDefaultRouteViaInterface(ep->GetIfName()); - // delete route blackhole - route.DelBlackhole(); + if (IsEnabled()) + { + vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); + const auto ep = m_Router->hiddenServiceContext().GetDefault(); + if (auto* vpn = ep->GetVPNInterface()) + route.DelDefaultRouteViaInterface(*vpn); + + // delete route blackhole + route.DelBlackhole(); + } + SetDNSMode(false); } } // namespace llarp diff --git a/llarp/router/route_poker.hpp b/llarp/router/route_poker.hpp index 17969a2b5..7473617e1 100644 --- a/llarp/router/route_poker.hpp +++ b/llarp/router/route_poker.hpp @@ -10,32 +10,19 @@ namespace llarp { struct AbstractRouter; - struct RoutePoker + struct RoutePoker : public std::enable_shared_from_this { void - AddRoute(huint32_t ip); + AddRoute(net::ipv4addr_t ip); void - DelRoute(huint32_t ip); + DelRoute(net::ipv4addr_t ip); void - Init(AbstractRouter* router, bool enable = false); + Start(AbstractRouter* router); ~RoutePoker(); - void - Update(); - - // sets stored routes and causes AddRoute to actually - // set routes rather than just storing them - void - Enable(); - - // unsets stored routes, and causes AddRoute to simply - // remember the desired routes rather than setting them. - void - Disable(); - /// explicitly put routes up void Up(); @@ -50,6 +37,12 @@ namespace llarp SetDNSMode(bool using_exit_mode) const; private: + void + Update(); + + bool + IsEnabled() const; + void DeleteAllRoutes(); @@ -57,25 +50,18 @@ namespace llarp DisableAllRoutes(); void - EnableAllRoutes(); + RefreshAllRoutes(); void - EnableRoute(huint32_t ip, huint32_t gateway); + EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway); void - DisableRoute(huint32_t ip, huint32_t gateway); - - std::optional - GetDefaultGateway() const; + DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway); - std::unordered_map m_PokedRoutes; - huint32_t m_CurrentGateway; + std::unordered_map m_PokedRoutes; - bool m_Enabled = false; - bool m_Enabling = false; + std::optional m_CurrentGateway; AbstractRouter* m_Router = nullptr; - - bool m_HasNetwork = true; }; } // namespace llarp diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 62f17e9e5..bb9e0f514 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -48,22 +48,21 @@ namespace llarp static auto logcat = log::Cat("router"); Router::Router(EventLoop_ptr loop, std::shared_ptr vpnPlatform) - : ready(false) - , m_lmq(std::make_shared()) - , _loop(std::move(loop)) - , _vpnPlatform(std::move(vpnPlatform)) - , paths(this) - , _exitContext(this) - , _dht(llarp_dht_context_new(this)) - , m_DiskThread(m_lmq->add_tagged_thread("disk")) - , inbound_link_msg_parser(this) - , _hiddenServiceContext(this) - , m_RPCServer(new rpc::RpcServer(m_lmq, this)) -#ifdef LOKINET_HIVE - , _randomStartDelay(std::chrono::milliseconds((llarp::randint() % 1250) + 2000)) -#else - , _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10)) -#endif + : ready{false} + , m_lmq{std::make_shared()} + , _loop{std::move(loop)} + , _vpnPlatform{std::move(vpnPlatform)} + , paths{this} + , _exitContext{this} + , _dht{llarp_dht_context_new(this)} + , m_DiskThread{m_lmq->add_tagged_thread("disk")} + , inbound_link_msg_parser{this} + , _hiddenServiceContext{this} + , m_RoutePoker{std::make_shared()} + , m_RPCServer{new rpc::RpcServer{m_lmq, this}} + , _randomStartDelay{ + platform::is_simulation ? std::chrono::milliseconds{(llarp::randint() % 1250) + 2000} + : 0s} { m_keyManager = std::make_shared(); // for lokid, so we don't close the connection when syncing the whitelist @@ -219,10 +218,22 @@ namespace llarp } return inbound_link_msg_parser.ProcessFrom(session, buf); } + void + Router::Freeze() + { + if (IsServiceNode()) + return; + linkManager().ForEachPeer([](auto peer) { + if (peer) + peer->Close(); + }); + } void Router::Thaw() { + if (IsServiceNode()) + return; // get pubkeys we are connected to std::unordered_set peerPubkeys; linkManager().ForEachPeer([&peerPubkeys](auto peer) { @@ -420,8 +431,6 @@ namespace llarp if (not EnsureIdentity()) throw std::runtime_error("EnsureIdentity() failed"); - - m_RoutePoker.Init(this); return true; } @@ -1003,19 +1012,6 @@ namespace llarp _linkManager.CheckPersistingSessions(now); - if (not isSvcNode) - { - if (HasClientExit()) - { - m_RoutePoker.Enable(); - } - else - { - m_RoutePoker.Disable(); - } - m_RoutePoker.Update(); - } - size_t connected = NumberOfConnectedRouters(); if (not isSvcNode) { @@ -1123,7 +1119,7 @@ namespace llarp if (const auto maybe = nodedb()->Get(remote); maybe.has_value()) { for (const auto& addr : maybe->addrs) - m_RoutePoker.DelRoute(addr.toIpAddress().toIP()); + m_RoutePoker->DelRoute(addr.IPv4()); } } @@ -1242,7 +1238,7 @@ namespace llarp throw std::runtime_error{"cannot override public ip, it is already set"}; ai.fromSockAddr(*_ourAddress); } - if (RouterContact::BlockBogons && IsBogon(ai.ip)) + if (RouterContact::BlockBogons && Net().IsBogon(ai.ip)) throw std::runtime_error{var::visit( [](auto&& ip) { return "cannot use " + ip.ToString() @@ -1254,12 +1250,6 @@ namespace llarp } }); - if (ExitEnabled() and IsServiceNode()) - { - LogError("exit mode not supported while service node"); - return false; - } - if (IsServiceNode() and not _rc.IsPublicRouter()) { LogError("we are configured as relay but have no reachable addresses"); @@ -1345,6 +1335,7 @@ namespace llarp LogInfo("have ", _nodedb->NumLoaded(), " routers"); _loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); }); + m_RoutePoker->Start(this); _running.store(true); _startedAt = Now(); #if defined(WITH_SYSTEMD) @@ -1677,11 +1668,8 @@ namespace llarp [this](llarp::RouterContact rc) { if (IsServiceNode()) return; - llarp::LogTrace( - "Before connect, outbound link adding route to (", - rc.addrs[0].toIpAddress().toIP(), - ") via gateway."); - m_RoutePoker.AddRoute(rc.addrs[0].toIpAddress().toIP()); + for (const auto& addr : rc.addrs) + m_RoutePoker->AddRoute(addr.IPv4()); }, util::memFn(&Router::ConnectionEstablished, this), util::memFn(&AbstractRouter::CheckRenegotiateValid, this), diff --git a/llarp/router/router.hpp b/llarp/router/router.hpp index 2b280a4c2..8f56024ea 100644 --- a/llarp/router/router.hpp +++ b/llarp/router/router.hpp @@ -274,27 +274,13 @@ namespace llarp /// bootstrap RCs BootstrapList bootstrapRCList; - bool - ExitEnabled() const - { - return false; // FIXME - have to fix the FIXME because FIXME - throw std::runtime_error("FIXME: this needs to be derived from config"); - /* - // TODO: use equal_range ? - auto itr = netConfig.find("exit"); - if (itr == netConfig.end()) - return false; - return IsTrueValue(itr->second.c_str()); - */ - } - - RoutePoker& - routePoker() override + const std::shared_ptr& + routePoker() const override { return m_RoutePoker; } - RoutePoker m_RoutePoker; + std::shared_ptr m_RoutePoker; void TriggerPump() override; @@ -401,6 +387,9 @@ namespace llarp bool StartRpcServer() override; + void + Freeze() override; + void Thaw() override; diff --git a/llarp/router_contact.cpp b/llarp/router_contact.cpp index b40339a59..f565255d2 100644 --- a/llarp/router_contact.cpp +++ b/llarp/router_contact.cpp @@ -485,9 +485,11 @@ namespace llarp if (IsExpired(now) and not allowExpired) return false; + // TODO: make net* overridable + const auto* net = net::Platform::Default_ptr(); for (const auto& a : addrs) { - if (IsBogon(a.ip) && BlockBogons) + if (net->IsBogon(a.ip) && BlockBogons) { llarp::LogError("invalid address info: ", a); return false; diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 175ccdf91..f688dfa46 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -516,8 +516,8 @@ namespace llarp::rpc { auto mapExit = [=](service::Address addr) mutable { ep->MapExitRange(range, addr); - r->routePoker().Enable(); - r->routePoker().Up(); + + r->routePoker()->Up(); bool shouldSendAuth = false; if (token.has_value()) { @@ -531,7 +531,7 @@ namespace llarp::rpc reply(CreateJSONError("we dont have an exit?")); }; auto onBadResult = [r, reply, ep, range](std::string reason) { - r->routePoker().Down(); + r->routePoker()->Down(); ep->UnmapExitRange(range); reply(CreateJSONError(reason)); }; @@ -614,7 +614,7 @@ namespace llarp::rpc } else if (not map) { - r->routePoker().Down(); + r->routePoker()->Down(); ep->UnmapExitRange(range); reply(CreateJSONResponse("OK")); } diff --git a/llarp/service/endpoint.cpp b/llarp/service/endpoint.cpp index 47599397a..1395dadd1 100644 --- a/llarp/service/endpoint.cpp +++ b/llarp/service/endpoint.cpp @@ -37,7 +37,6 @@ #include #include -#include #include #include @@ -124,8 +123,7 @@ namespace llarp // add supported ethertypes if (HasIfAddr()) { - const auto ourIP = net::HUIntToIn6(GetIfAddr()); - if (ipv6_is_mapped_ipv4(ourIP)) + if (IPRange::V4MappedRange().Contains(GetIfAddr())) { introSet().supportedProtocols.push_back(ProtocolType::TrafficV4); } @@ -2074,6 +2072,11 @@ namespace llarp LogInfo(Name(), " map ", range, " to exit at ", exit); m_ExitMap.Insert(range, exit); } + bool + Endpoint::HasFlowToService(Address addr) const + { + return HasOutboundConvo(addr) or HasInboundConvo(addr); + } void Endpoint::UnmapExitRange(IPRange range) diff --git a/llarp/service/endpoint.hpp b/llarp/service/endpoint.hpp index 64b8b68ab..00a2a3c68 100644 --- a/llarp/service/endpoint.hpp +++ b/llarp/service/endpoint.hpp @@ -174,6 +174,12 @@ namespace llarp return nullptr; }; + virtual vpn::NetworkInterface* + GetVPNInterface() + { + return nullptr; + } + bool PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override; @@ -355,6 +361,9 @@ namespace llarp bool HasPathToSNode(const RouterID remote) const; + bool + HasFlowToService(const Address remote) const; + void PutSenderFor(const ConvoTag& tag, const ServiceInfo& info, bool inbound) override; diff --git a/llarp/service/pendingbuffer.hpp b/llarp/service/pendingbuffer.hpp index ed9a71522..827b2fab1 100644 --- a/llarp/service/pendingbuffer.hpp +++ b/llarp/service/pendingbuffer.hpp @@ -2,31 +2,23 @@ #include "protocol.hpp" #include - -#include -#include #include -namespace llarp +namespace llarp::service { - namespace service + struct PendingBuffer { - struct PendingBuffer - { - std::vector payload; - ProtocolType protocol; + std::vector payload; + ProtocolType protocol; - PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload(buf.sz), protocol(t) - { - std::copy(buf.base, buf.base + buf.sz, std::back_inserter(payload)); - } + inline llarp_buffer_t + Buffer() + { + return llarp_buffer_t{payload}; + } - ManagedBuffer - Buffer() - { - return ManagedBuffer{llarp_buffer_t(payload)}; - } - }; - } // namespace service + PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload{buf.copy()}, protocol{t} + {} + }; -} // namespace llarp +} // namespace llarp::service diff --git a/llarp/service/sendcontext.cpp b/llarp/service/sendcontext.cpp index 16b524c16..20fb744a0 100644 --- a/llarp/service/sendcontext.cpp +++ b/llarp/service/sendcontext.cpp @@ -5,6 +5,7 @@ #include "endpoint.hpp" #include #include +#include namespace llarp { diff --git a/llarp/util/buffer.cpp b/llarp/util/buffer.cpp index 15cfca265..05c4162ea 100644 --- a/llarp/util/buffer.cpp +++ b/llarp/util/buffer.cpp @@ -132,6 +132,14 @@ operator==(const llarp_buffer_t& buff, std::string_view data) { return std::string_view{reinterpret_cast(buff.cur), buff.size_left()} == data; } + +llarp::byte_view_t +llarp_buffer_t::view() const +{ + return {base, sz}; + +} + namespace llarp { std::vector diff --git a/llarp/util/buffer.hpp b/llarp/util/buffer.hpp index 08010616f..5d5899209 100644 --- a/llarp/util/buffer.hpp +++ b/llarp/util/buffer.hpp @@ -15,59 +15,17 @@ #include #include #include +#include -/** - * buffer.h - * - * generic memory buffer - * - * TODO: replace usage of these with std::span (via a backport until we move to C++20). That's a - * fairly big job, though, as llarp_buffer_t is currently used a bit differently (i.e. maintains - * both start and current position, plus has some value reading/writing methods). - */ - -/** - llarp_buffer_t represents a region of memory that is ONLY - valid in the current scope. - - make sure to follow the rules: - - ALWAYS copy the contents of the buffer if that data is to be used outside the - current scope. - - ALWAYS pass a llarp_buffer_t * if you plan on modifying the data associated - with the buffer - - ALWAYS pass a llarp_buffer_t * if you plan on advancing the stream position - - ALWAYS pass a const llarp_buffer_t & if you are doing a read only operation - that does not modify the buffer - - ALWAYS pass a const llarp_buffer_t & if you don't want to advance the stream - position - - ALWAYS bail out of the current operation if you run out of space in a buffer - - ALWAYS assume the pointers in the buffer are stack allocated memory - (yes even if you know they are not) - - NEVER malloc() the pointers in the buffer when using it - - NEVER realloc() the pointers in the buffer when using it - - NEVER free() the pointers in the buffer when using it - - NEVER use llarp_buffer_t ** (double pointers) - - NEVER use llarp_buffer_t ** (double pointers) - - ABSOLUTELY NEVER USE DOUBLE POINTERS. - - */ - -struct ManagedBuffer; +namespace llarp +{ + using byte_view_t = std::basic_string_view; +} -struct llarp_buffer_t +/// TODO: replace usage of these with std::span (via a backport until we move to C++20). That's a +/// fairly big job, though, as llarp_buffer_t is currently used a bit differently (i.e. maintains +/// both start and current position, plus has some value reading/writing methods). +struct [[deprecated("this type is stupid, use something else")]] llarp_buffer_t { /// starting memory address byte_t* base{nullptr}; @@ -76,26 +34,22 @@ struct llarp_buffer_t /// max size of buffer size_t sz{0}; - byte_t - operator[](size_t x) + byte_t operator[](size_t x) { return *(this->base + x); } llarp_buffer_t() = default; - llarp_buffer_t(byte_t* b, byte_t* c, size_t s) : base(b), cur(c), sz(s) + llarp_buffer_t(byte_t * b, byte_t * c, size_t s) : base(b), cur(c), sz(s) {} - llarp_buffer_t(const ManagedBuffer&) = delete; - llarp_buffer_t(ManagedBuffer&&) = delete; - /// Construct referencing some 1-byte, trivially copyable (e.g. char, unsigned char, byte_t) /// pointer type and a buffer size. template < typename T, typename = std::enable_if_t>> - llarp_buffer_t(T* buf, size_t _sz) + llarp_buffer_t(T * buf, size_t _sz) : base(reinterpret_cast(const_cast*>(buf))) , cur(base) , sz(_sz) @@ -105,82 +59,73 @@ struct llarp_buffer_t template < typename T, typename = std::void_t().data() + std::declval().size())>> - llarp_buffer_t(T&& t) : llarp_buffer_t{t.data(), t.size()} + llarp_buffer_t(T && t) : llarp_buffer_t{t.data(), t.size()} {} - byte_t* - begin() + byte_t* begin() { return base; } - byte_t* - begin() const + byte_t* begin() const { return base; } - byte_t* - end() + byte_t* end() { return base + sz; } - byte_t* - end() const + byte_t* end() const { return base + sz; } - size_t - size_left() const; + size_t size_left() const; template - bool - read_into(OutputIt begin, OutputIt end); + bool read_into(OutputIt begin, OutputIt end); template - bool - write(InputIt begin, InputIt end); + bool write(InputIt begin, InputIt end); #ifndef _WIN32 - bool - writef(const char* fmt, ...) __attribute__((format(printf, 2, 3))); + bool writef(const char* fmt, ...) __attribute__((format(printf, 2, 3))); #elif defined(__MINGW64__) || defined(__MINGW32__) - bool - writef(const char* fmt, ...) __attribute__((__format__(__MINGW_PRINTF_FORMAT, 2, 3))); + bool writef(const char* fmt, ...) __attribute__((__format__(__MINGW_PRINTF_FORMAT, 2, 3))); #else - bool - writef(const char* fmt, ...); + bool writef(const char* fmt, ...); #endif - bool - put_uint16(uint16_t i); - bool - put_uint32(uint32_t i); + bool put_uint16(uint16_t i); + bool put_uint32(uint32_t i); - bool - put_uint64(uint64_t i); + bool put_uint64(uint64_t i); - bool - read_uint16(uint16_t& i); - bool - read_uint32(uint32_t& i); + bool read_uint16(uint16_t & i); + bool read_uint32(uint32_t & i); - bool - read_uint64(uint64_t& i); + bool read_uint64(uint64_t & i); - size_t - read_until(char delim, byte_t* result, size_t resultlen); + size_t read_until(char delim, byte_t* result, size_t resultlen); /// make a copy of this buffer - std::vector - copy() const; + std::vector copy() const; + + /// get a read only view over the entire region + llarp::byte_view_t view() const; + + bool operator==(std::string_view data) const + { + return std::string_view{reinterpret_cast(base), sz} == data; + } private: friend struct ManagedBuffer; llarp_buffer_t(const llarp_buffer_t&) = default; - llarp_buffer_t(llarp_buffer_t&&) = default; + llarp_buffer_t(llarp_buffer_t &&) = default; }; + bool operator==(const llarp_buffer_t& buff, std::string_view data); @@ -214,7 +159,7 @@ llarp_buffer_t::write(InputIt begin, InputIt end) /** Provide a copyable/moveable wrapper around `llarp_buffer_t`. */ -struct ManagedBuffer +struct [[deprecated("deprecated along with llarp_buffer_t")]] ManagedBuffer { llarp_buffer_t underlying; @@ -223,7 +168,7 @@ struct ManagedBuffer explicit ManagedBuffer(const llarp_buffer_t& b) : underlying(b) {} - ManagedBuffer(ManagedBuffer&&) = default; + ManagedBuffer(ManagedBuffer &&) = default; ManagedBuffer(const ManagedBuffer&) = default; operator const llarp_buffer_t&() const @@ -234,6 +179,8 @@ struct ManagedBuffer namespace llarp { + using byte_view_t = std::basic_string_view; + // Wrapper around a std::unique_ptr that owns its own memory and is also implicitly // convertible to a llarp_buffer_t. struct OwnedBuffer diff --git a/llarp/util/easter_eggs.cpp b/llarp/util/easter_eggs.cpp new file mode 100644 index 000000000..c3a658db9 --- /dev/null +++ b/llarp/util/easter_eggs.cpp @@ -0,0 +1,39 @@ +#include "lokinet_init.h" + +#if defined(_WIN32) +#include +#include +#include + +#define WineKiller \ + DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit + +struct WineKiller +{ + WineKiller() + { + if (auto hntdll = GetModuleHandle("ntdll.dll")) + { + if (GetProcAddress(hntdll, "wine_get_version")) + { + static const char* text = + "dont run lokinet in wine like wtf man we support linux and pretty " + "much every flavour of BSD, and even some flavours of unix system " + "5.x.\nThis Program Will now crash lmao."; + static const char* title = "srsly fam wtf"; + MessageBoxA(NULL, text, title, MB_ICONHAND); + abort(); + } + } + } +}; + +// i heckin love static initalization +WineKiller lmao{}; +#endif + +extern "C" int +Lokinet_INIT(void) +{ + return 0; +} diff --git a/llarp/util/lokinet_init.c b/llarp/util/lokinet_init.c deleted file mode 100644 index 62fb3f7b2..000000000 --- a/llarp/util/lokinet_init.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "lokinet_init.h" -#if defined(_WIN32) -#include -#include -#include - -int -Lokinet_INIT(void) -{ - static const char*(CDECL * pwine_get_version)(void); - HMODULE hntdll = GetModuleHandle("ntdll.dll"); - if (hntdll) - { - pwine_get_version = (void*)GetProcAddress(hntdll, "wine_get_version"); - if (pwine_get_version) - { - static const char* text = - "dont run lokinet in wine like wtf man we support linux and pretty " - "much every flavour of BSD, and even some flavours of unix system " - "5.x.\nThis Program Will now crash lmao."; - static const char* title = "srsly fam wtf"; - MessageBoxA(NULL, text, title, MB_ICONHAND); - abort(); - } - } - return 0; -} -#else -int -Lokinet_INIT(void) -{ - return 0; -} - -#endif diff --git a/llarp/util/lokinet_init.h b/llarp/util/lokinet_init.h index 42154facc..5fd0a73de 100644 --- a/llarp/util/lokinet_init.h +++ b/llarp/util/lokinet_init.h @@ -3,15 +3,6 @@ #ifdef __cplusplus extern "C" { -#endif - -#ifndef Lokinet_INIT -#if defined(_WIN32) -#define Lokinet_INIT \ - DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit -#else -#define Lokinet_INIT _lokinet_non_shit_platform_INIT -#endif #endif int diff --git a/llarp/util/str.cpp b/llarp/util/str.cpp index 3ffa8eeda..56ca034cf 100644 --- a/llarp/util/str.cpp +++ b/llarp/util/str.cpp @@ -7,6 +7,12 @@ #include #include +#ifdef _WIN32 +#include +#include +#include +#endif + namespace llarp { bool @@ -168,4 +174,22 @@ namespace llarp dur.count()); } + std::wstring + to_wide(std::string data) + { + std::wstring buf; + buf.resize(data.size()); +#ifdef _WIN32 + // win32 specific codepath because balmer made windows so that man may suffer + if (MultiByteToWideChar(CP_UTF8, 0, data.c_str(), data.size(), buf.data(), buf.size()) == 0) + throw win32::error{GetLastError(), "failed to convert string to wide string"}; + +#else + // this dumb but probably works (i guess?) + std::transform( + data.begin(), data.end(), buf.begin(), [](const auto& ch) -> wchar_t { return ch; }); +#endif + return buf; + } + } // namespace llarp diff --git a/llarp/util/str.hpp b/llarp/util/str.hpp index cb31b8412..eed00482e 100644 --- a/llarp/util/str.hpp +++ b/llarp/util/str.hpp @@ -5,7 +5,6 @@ #include #include #include - #include namespace llarp @@ -141,4 +140,8 @@ namespace llarp std::string friendly_duration(std::chrono::nanoseconds dur); + /// convert a "normal" string into a wide string + std::wstring + to_wide(std::string data); + } // namespace llarp diff --git a/llarp/vpn/android.hpp b/llarp/vpn/android.hpp index 043249b0d..3364e75fa 100644 --- a/llarp/vpn/android.hpp +++ b/llarp/vpn/android.hpp @@ -3,7 +3,7 @@ #include #include -#include +#include "platform.hpp" #include "common.hpp" #include @@ -12,10 +12,9 @@ namespace llarp::vpn class AndroidInterface : public NetworkInterface { const int m_fd; - const InterfaceInfo m_Info; // likely 100% ignored on android, at least for now public: - AndroidInterface(InterfaceInfo info, int fd) : m_fd(fd), m_Info(info) + AndroidInterface(InterfaceInfo info, int fd) : NetworkInterface{std::move(info)}, m_fd{fd} { if (m_fd == -1) throw std::runtime_error( @@ -37,38 +36,34 @@ namespace llarp::vpn net::IPPacket ReadNextPacket() override { - net::IPPacket pkt{}; - const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf)); - if (sz >= 0) - pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)}); - return pkt; + std::vector pkt; + pkt.reserve(net::IPPacket::MaxSize); + const auto n = read(m_fd, pkt.data(), pkt.capacity()); + pkt.resize(std::min(std::max(ssize_t{}, n), static_cast(pkt.capacity()))); + return net::IPPacket{std::move(pkt)}; } bool WritePacket(net::IPPacket pkt) override { - const auto sz = write(m_fd, pkt.buf, pkt.sz); + const auto sz = write(m_fd, pkt.data(), pkt.size()); if (sz <= 0) return false; - return sz == static_cast(pkt.sz); - } - - std::string - IfName() const override - { - return m_Info.ifname; + return sz == static_cast(pkt.size()); } }; class AndroidRouteManager : public IRouteManager { - void AddRoute(IPVariant_t, IPVariant_t) override{}; + void AddRoute(net::ipaddr_t, net::ipaddr_t) override{}; - void DelRoute(IPVariant_t, IPVariant_t) override{}; + void DelRoute(net::ipaddr_t, net::ipaddr_t) override{}; - void AddDefaultRouteViaInterface(std::string) override{}; + void + AddDefaultRouteViaInterface(NetworkInterface&) override{}; - void DelDefaultRouteViaInterface(std::string) override{}; + void + DelDefaultRouteViaInterface(NetworkInterface&) override{}; void AddRouteViaInterface(NetworkInterface&, IPRange) override{}; @@ -76,9 +71,10 @@ namespace llarp::vpn void DelRouteViaInterface(NetworkInterface&, IPRange) override{}; - std::vector GetGatewaysNotOnInterface(std::string) override + std::vector + GetGatewaysNotOnInterface(NetworkInterface&) override { - return std::vector{}; + return std::vector{}; }; }; @@ -88,7 +84,7 @@ namespace llarp::vpn AndroidRouteManager _routeManager{}; public: - AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD) + AndroidPlatform(llarp::Context* ctx) : fd{ctx->androidFD} {} std::shared_ptr diff --git a/llarp/vpn/i_packet_io.hpp b/llarp/vpn/i_packet_io.hpp new file mode 100644 index 000000000..5789b193c --- /dev/null +++ b/llarp/vpn/i_packet_io.hpp @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include + +namespace llarp::vpn +{ + class I_Packet_IO + { + public: + virtual ~I_Packet_IO() = default; + + /// start any platform specific operations before running + virtual void + Start(){}; + + /// stop operation and tear down anything that Start() set up. + virtual void + Stop(){}; + + /// read next ip packet, return an empty packet if there are none ready. + virtual net::IPPacket + ReadNextPacket() = 0; + + /// write a packet to the interface + /// returns false if we dropped it + virtual bool + WritePacket(net::IPPacket pkt) = 0; + + /// get pollable fd for reading + virtual int + PollFD() const = 0; + }; + +} // namespace llarp::vpn diff --git a/llarp/vpn/linux.hpp b/llarp/vpn/linux.hpp index 44b5ae220..5fc06d674 100644 --- a/llarp/vpn/linux.hpp +++ b/llarp/vpn/linux.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include "platform.hpp" #include #include #include @@ -36,11 +36,10 @@ namespace llarp::vpn class LinuxInterface : public NetworkInterface { const int m_fd; - const InterfaceInfo m_Info; public: LinuxInterface(InterfaceInfo info) - : NetworkInterface{}, m_fd{::open("/dev/net/tun", O_RDWR)}, m_Info{std::move(info)} + : NetworkInterface{std::move(info)}, m_fd{::open("/dev/net/tun", O_RDWR)} { if (m_fd == -1) @@ -60,7 +59,7 @@ namespace llarp::vpn control.ioctl(SIOCGIFFLAGS, &ifr); const int flags = ifr.ifr_flags; control.ioctl(SIOCGIFINDEX, &ifr); - const int ifindex = ifr.ifr_ifindex; + m_Info.index = ifr.ifr_ifindex; for (const auto& ifaddr : m_Info.addrs) { @@ -79,7 +78,7 @@ namespace llarp::vpn { ifr6.addr = net::HUIntToIn6(ifaddr.range.addr); ifr6.prefixlen = llarp::bits::count_bits(ifaddr.range.netmask_bits); - ifr6.ifindex = ifindex; + ifr6.ifindex = m_Info.index; try { IOCTL{AF_INET6}.ioctl(SIOCSIFADDR, &ifr6); @@ -108,30 +107,29 @@ namespace llarp::vpn net::IPPacket ReadNextPacket() override { - net::IPPacket pkt; - const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf)); - if (sz >= 0) - pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)}); - else if (errno == EAGAIN || errno == EWOULDBLOCK) - pkt.sz = 0; - else + std::vector pkt; + pkt.resize(net::IPPacket::MaxSize); + const auto sz = read(m_fd, pkt.data(), pkt.capacity()); + if (errno) + { + if (errno == EAGAIN or errno == EWOULDBLOCK) + { + errno = 0; + return net::IPPacket{}; + } throw std::error_code{errno, std::system_category()}; + } + pkt.resize(sz); return pkt; } bool WritePacket(net::IPPacket pkt) override { - const auto sz = write(m_fd, pkt.buf, pkt.sz); + const auto sz = write(m_fd, pkt.data(), pkt.size()); if (sz <= 0) return false; - return sz == static_cast(pkt.sz); - } - - std::string - IfName() const override - { - return m_Info.ifname; + return sz == static_cast(pkt.size()); } }; @@ -182,19 +180,18 @@ namespace llarp::vpn unsigned char bitlen; unsigned char data[sizeof(struct in6_addr)]; - _inet_addr(huint32_t addr, size_t bits = 32) + _inet_addr(net::ipv4addr_t addr, size_t bits = 32) { family = AF_INET; bitlen = bits; - oxenc::write_host_as_big(addr.h, data); + std::memcpy(data, &addr.n, 4); } - _inet_addr(huint128_t addr, size_t bits = 128) + _inet_addr(net::ipv6addr_t addr, size_t bits = 128) { family = AF_INET6; bitlen = bits; - const nuint128_t net = ToNet(addr); - std::memcpy(data, &net, 16); + std::memcpy(data, &addr.n, 16); } }; @@ -290,76 +287,68 @@ namespace llarp::vpn } void - DefaultRouteViaInterface(std::string ifname, int cmd, int flags) + DefaultRouteViaInterface(NetworkInterface& vpn, int cmd, int flags) { - int if_idx = if_nametoindex(ifname.c_str()); - const auto maybe = Net().GetInterfaceAddr(ifname); + const auto& info = vpn.Info(); + + const auto maybe = Net().GetInterfaceAddr(info.ifname); if (not maybe) throw std::runtime_error{"we dont have our own network interface?"}; - const _inet_addr gateway{maybe->asIPv4()}; - const _inet_addr lower{ipaddr_ipv4_bits(0, 0, 0, 0), 1}; - const _inet_addr upper{ipaddr_ipv4_bits(128, 0, 0, 0), 1}; + const _inet_addr gateway{maybe->getIPv4()}; + const _inet_addr lower{ToNet(ipaddr_ipv4_bits(0, 0, 0, 0)), 1}; + const _inet_addr upper{ToNet(ipaddr_ipv4_bits(128, 0, 0, 0)), 1}; - Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, if_idx); - Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, if_idx); + Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, info.index); + Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, info.index); - if (const auto maybe6 = Net().GetInterfaceIPv6Address(ifname)) + if (const auto maybe6 = Net().GetInterfaceIPv6Address(info.ifname)) { - const _inet_addr gateway6{*maybe6, 128}; + const _inet_addr gateway6{ToNet(*maybe6), 128}; for (const std::string str : {"::", "4000::", "8000::", "c000::"}) { - huint128_t _hole{}; - _hole.FromString(str); - const _inet_addr hole6{_hole, 2}; - Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, if_idx); + const _inet_addr hole6{net::ipv6addr_t::from_string(str), 2}; + Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, info.index); } } } void - RouteViaInterface(int cmd, int flags, std::string ifname, IPRange range) + RouteViaInterface(int cmd, int flags, NetworkInterface& vpn, IPRange range) { - int if_idx = if_nametoindex(ifname.c_str()); + const auto& info = vpn.Info(); if (range.IsV4()) { - const auto maybe = Net().GetInterfaceAddr(ifname); + const auto maybe = Net().GetInterfaceAddr(info.ifname); if (not maybe) throw std::runtime_error{"we dont have our own network interface?"}; - const _inet_addr gateway{maybe->asIPv4()}; + const auto gateway = var::visit([](auto&& ip) { return _inet_addr{ip}; }, maybe->getIP()); const _inet_addr addr{ - net::TruncateV6(range.addr), bits::count_bits(net::TruncateV6(range.netmask_bits))}; + ToNet(net::TruncateV6(range.addr)), + bits::count_bits(net::TruncateV6(range.netmask_bits))}; - Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); + Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index); } else { - const auto maybe = Net().GetInterfaceIPv6Address(ifname); + const auto maybe = Net().GetInterfaceIPv6Address(info.ifname); if (not maybe) throw std::runtime_error{"we dont have our own network interface?"}; - const _inet_addr gateway{*maybe, 128}; - const _inet_addr addr{range.addr, bits::count_bits(range.netmask_bits)}; - Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); + const _inet_addr gateway{ToNet(*maybe), 128}; + const _inet_addr addr{ToNet(range.addr), bits::count_bits(range.netmask_bits)}; + Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index); } } void - Route(int cmd, int flags, IPVariant_t ip, IPVariant_t gateway) + Route(int cmd, int flags, net::ipaddr_t ip, net::ipaddr_t gateway) { - // do bullshit double std::visit because lol variants - std::visit( - [gateway, cmd, flags, this](auto&& ip) { - const _inet_addr toAddr{ip}; - std::visit( - [toAddr, cmd, flags, this](auto&& gateway) { - const _inet_addr gwAddr{gateway}; - Route(cmd, flags, toAddr, gwAddr, GatewayMode::eFirstHop, 0); - }, - gateway); - }, - ip); + auto _ip = var::visit([](auto&& i) { return _inet_addr{i}; }, ip); + auto _gw = var::visit([](auto&& i) { return _inet_addr{i}; }, gateway); + + Route(cmd, flags, _ip, _gw, GatewayMode::eFirstHop, 0); } public: @@ -375,45 +364,46 @@ namespace llarp::vpn } void - AddRoute(IPVariant_t ip, IPVariant_t gateway) override + AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { Route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ip, gateway); } void - DelRoute(IPVariant_t ip, IPVariant_t gateway) override + DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { Route(RTM_DELROUTE, 0, ip, gateway); } void - AddDefaultRouteViaInterface(std::string ifname) override + AddDefaultRouteViaInterface(NetworkInterface& vpn) override { - DefaultRouteViaInterface(ifname, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL); + DefaultRouteViaInterface(vpn, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL); } void - DelDefaultRouteViaInterface(std::string ifname) override + DelDefaultRouteViaInterface(NetworkInterface& vpn) override { - DefaultRouteViaInterface(ifname, RTM_DELROUTE, 0); + DefaultRouteViaInterface(vpn, RTM_DELROUTE, 0); } void AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn.IfName(), range); + RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn, range); } void DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - RouteViaInterface(RTM_DELROUTE, 0, vpn.IfName(), range); + RouteViaInterface(RTM_DELROUTE, 0, vpn, range); } - std::vector - GetGatewaysNotOnInterface(std::string ifname) override + std::vector + GetGatewaysNotOnInterface(NetworkInterface& vpn) override { - std::vector gateways{}; + const auto& ifname = vpn.Info().ifname; + std::vector gateways{}; std::ifstream inf{"/proc/net/route"}; for (std::string line; std::getline(inf, line);) { @@ -425,7 +415,7 @@ namespace llarp::vpn { huint32_t x{}; oxenc::from_hex(ip.begin(), ip.end(), reinterpret_cast(&x.h)); - gateways.emplace_back(x); + gateways.emplace_back(net::ipv4addr_t::from_host(x.h)); } } } diff --git a/llarp/vpn/packet_intercept.hpp b/llarp/vpn/packet_intercept.hpp new file mode 100644 index 000000000..7150ae54a --- /dev/null +++ b/llarp/vpn/packet_intercept.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include + +namespace llarp::vpn +{ + using PacketSendFunc_t = std::function)>; + using PacketInterceptFunc_t = std::function, PacketSendFunc_t)>; + + class I_PacketIntercepter + { + public: + virtual ~I_PacketIntercepter() = default; + + /// start intercepting packets and call a callback for each one we get + /// the callback passes in an ip packet and a function that we can use to send an ip packet to + /// its origin + virtual void + start(PacketInterceptFunc_t f) = 0; + + /// stop intercepting packets + virtual void + stop() = 0; + }; + +} // namespace llarp::vpn diff --git a/llarp/vpn/packet_router.cpp b/llarp/vpn/packet_router.cpp index 54c1839e9..7a618022c 100644 --- a/llarp/vpn/packet_router.cpp +++ b/llarp/vpn/packet_router.cpp @@ -4,14 +4,15 @@ namespace llarp::vpn { struct UDPPacketHandler : public Layer4Handler { - PacketHandlerFunc m_BaseHandler; - std::unordered_map m_LocalPorts; + PacketHandlerFunc_t m_BaseHandler; + std::unordered_map m_LocalPorts; - explicit UDPPacketHandler(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)} + explicit UDPPacketHandler(PacketHandlerFunc_t baseHandler) + : m_BaseHandler{std::move(baseHandler)} {} void - AddSubHandler(nuint16_t localport, PacketHandlerFunc handler) override + AddSubHandler(nuint16_t localport, PacketHandlerFunc_t handler) override { m_LocalPorts.emplace(localport, std::move(handler)); } @@ -19,12 +20,15 @@ namespace llarp::vpn void HandleIPPacket(llarp::net::IPPacket pkt) override { - const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2; - const nuint16_t dstPort{*reinterpret_cast(ptr)}; - if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end()) + auto dstport = pkt.DstPort(); + if (not dstport) { - itr->second(std::move(pkt)); + m_BaseHandler(std::move(pkt)); + return; } + + if (auto itr = m_LocalPorts.find(*dstport); itr != m_LocalPorts.end()) + itr->second(std::move(pkt)); else m_BaseHandler(std::move(pkt)); } @@ -32,9 +36,9 @@ namespace llarp::vpn struct GenericLayer4Handler : public Layer4Handler { - PacketHandlerFunc m_BaseHandler; + PacketHandlerFunc_t m_BaseHandler; - explicit GenericLayer4Handler(PacketHandlerFunc baseHandler) + explicit GenericLayer4Handler(PacketHandlerFunc_t baseHandler) : m_BaseHandler{std::move(baseHandler)} {} @@ -45,7 +49,8 @@ namespace llarp::vpn } }; - PacketRouter::PacketRouter(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)} + PacketRouter::PacketRouter(PacketHandlerFunc_t baseHandler) + : m_BaseHandler{std::move(baseHandler)} {} void @@ -53,15 +58,13 @@ namespace llarp::vpn { const auto proto = pkt.Header()->protocol; if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end()) - { itr->second->HandleIPPacket(std::move(pkt)); - } else m_BaseHandler(std::move(pkt)); } void - PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc func) + PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func) { constexpr byte_t udp_proto = 0x11; @@ -73,7 +76,7 @@ namespace llarp::vpn } void - PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc func) + PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc_t func) { m_IPProtoHandler[proto] = std::make_unique(std::move(func)); } diff --git a/llarp/vpn/packet_router.hpp b/llarp/vpn/packet_router.hpp index ee0721a05..635df7925 100644 --- a/llarp/vpn/packet_router.hpp +++ b/llarp/vpn/packet_router.hpp @@ -6,25 +6,18 @@ namespace llarp::vpn { - using PacketHandlerFunc = std::function; + using PacketHandlerFunc_t = std::function; - struct Layer4Handler - { - virtual ~Layer4Handler() = default; + struct Layer4Handler; - virtual void - HandleIPPacket(llarp::net::IPPacket pkt) = 0; - - virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){}; - }; class PacketRouter { - PacketHandlerFunc m_BaseHandler; + PacketHandlerFunc_t m_BaseHandler; std::unordered_map> m_IPProtoHandler; public: /// baseHandler will be called if no other handlers matches a packet - explicit PacketRouter(PacketHandlerFunc baseHandler); + explicit PacketRouter(PacketHandlerFunc_t baseHandler); /// feed in an ip packet for handling void @@ -32,14 +25,25 @@ namespace llarp::vpn /// add a non udp packet handler using ip protocol proto void - AddIProtoHandler(uint8_t proto, PacketHandlerFunc func); + AddIProtoHandler(uint8_t proto, PacketHandlerFunc_t func); /// helper that adds a udp packet handler for UDP destinted for localport void - AddUDPHandler(huint16_t localport, PacketHandlerFunc func); + AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func); /// remove a udp handler that is already set up by bound port void RemoveUDPHandler(huint16_t localport); }; + + struct Layer4Handler + { + virtual ~Layer4Handler() = default; + + virtual void + HandleIPPacket(llarp::net::IPPacket pkt) = 0; + + virtual void AddSubHandler(nuint16_t, PacketHandlerFunc_t){}; + }; + } // namespace llarp::vpn diff --git a/llarp/vpn/platform.cpp b/llarp/vpn/platform.cpp index ef5a30295..757894b61 100644 --- a/llarp/vpn/platform.cpp +++ b/llarp/vpn/platform.cpp @@ -1,5 +1,5 @@ -#include +#include "platform.hpp" #ifdef _WIN32 #include "win32.hpp" @@ -28,7 +28,7 @@ namespace llarp::vpn (void)ctx; std::shared_ptr plat; #ifdef _WIN32 - plat = std::make_shared(); + plat = std::make_shared(ctx); #endif #ifdef __linux__ #ifdef ANDROID diff --git a/llarp/ev/vpn.hpp b/llarp/vpn/platform.hpp similarity index 58% rename from llarp/ev/vpn.hpp rename to llarp/vpn/platform.hpp index 4488010eb..a29841138 100644 --- a/llarp/ev/vpn.hpp +++ b/llarp/vpn/platform.hpp @@ -2,10 +2,12 @@ #include #include -#include - #include +#include "i_packet_io.hpp" + +#include + namespace llarp { struct Context; @@ -30,36 +32,40 @@ namespace llarp::vpn struct InterfaceInfo { std::string ifname; + unsigned int index; huint32_t dnsaddr; - std::set addrs; + std::vector addrs; + + /// get address number N + inline net::ipaddr_t + operator[](size_t idx) const + { + const auto& range = addrs[idx].range; + if (range.IsV4()) + return ToNet(net::TruncateV6(range.addr)); + return ToNet(range.addr); + } }; /// a vpn network interface - class NetworkInterface + class NetworkInterface : public I_Packet_IO { + protected: + InterfaceInfo m_Info; + public: - NetworkInterface() = default; + NetworkInterface(InterfaceInfo info) : m_Info{std::move(info)} + {} NetworkInterface(const NetworkInterface&) = delete; NetworkInterface(NetworkInterface&&) = delete; virtual ~NetworkInterface() = default; - /// get pollable fd for reading - virtual int - PollFD() const = 0; - - /// the interface's name - virtual std::string - IfName() const = 0; - - /// read next ip packet, return an empty packet if there are none ready. - virtual net::IPPacket - ReadNextPacket() = 0; - - /// write a packet to the interface - /// returns false if we dropped it - virtual bool - WritePacket(net::IPPacket pkt) = 0; + const InterfaceInfo& + Info() const + { + return m_Info; + } /// idempotently wake up the upper layers as needed (platform dependant) virtual void @@ -69,8 +75,6 @@ namespace llarp::vpn class IRouteManager { public: - using IPVariant_t = std::variant; - IRouteManager() = default; IRouteManager(const IRouteManager&) = delete; IRouteManager(IRouteManager&&) = delete; @@ -86,16 +90,16 @@ namespace llarp::vpn } virtual void - AddRoute(IPVariant_t ip, IPVariant_t gateway) = 0; + AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0; virtual void - DelRoute(IPVariant_t ip, IPVariant_t gateway) = 0; + DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0; virtual void - AddDefaultRouteViaInterface(std::string ifname) = 0; + AddDefaultRouteViaInterface(NetworkInterface& vpn) = 0; virtual void - DelDefaultRouteViaInterface(std::string ifname) = 0; + DelDefaultRouteViaInterface(NetworkInterface& vpn) = 0; virtual void AddRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; @@ -103,8 +107,8 @@ namespace llarp::vpn virtual void DelRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; - virtual std::vector - GetGatewaysNotOnInterface(std::string ifname) = 0; + virtual std::vector + GetGatewaysNotOnInterface(NetworkInterface& vpn) = 0; virtual void AddBlackhole(){}; @@ -117,20 +121,43 @@ namespace llarp::vpn /// responsible for obtaining vpn interfaces class Platform { + protected: + /// get a new network interface fully configured given the interface info + /// blocks until ready, throws on error + virtual std::shared_ptr + ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0; + public: Platform() = default; Platform(const Platform&) = delete; Platform(Platform&&) = delete; virtual ~Platform() = default; - /// get a new network interface fully configured given the interface info - /// blocks until ready, throws on error - virtual std::shared_ptr - ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0; + /// create and start a network interface + inline std::shared_ptr + CreateInterface(InterfaceInfo info, AbstractRouter* router) + { + if (auto netif = ObtainInterface(std::move(info), router)) + { + netif->Start(); + return netif; + } + return nullptr; + } /// get owned ip route manager for managing routing table virtual IRouteManager& RouteManager() = 0; + + /// create a packet io that will read (and optionally write) packets on a network interface the + /// lokinet process does not own + /// @param index the interface index of the network interface to use or 0 for all + /// interfaces on the system + virtual std::shared_ptr + create_packet_io(unsigned int) + { + throw std::runtime_error{"raw packet io is unimplemented"}; + } }; /// create native vpn platform diff --git a/llarp/vpn/win32.hpp b/llarp/vpn/win32.hpp index 89771353f..a08e28813 100644 --- a/llarp/vpn/win32.hpp +++ b/llarp/vpn/win32.hpp @@ -5,662 +5,191 @@ #include #include #include -#include #include - +#include +#include +#include +#include #include -// DDK macros -#define CTL_CODE(DeviceType, Function, Method, Access) \ - (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)) -#define FILE_DEVICE_UNKNOWN 0x00000022 -#define FILE_ANY_ACCESS 0x00000000 -#define METHOD_BUFFERED 0 - -/* From OpenVPN tap driver, common.h */ -#define TAP_CONTROL_CODE(request, method) \ - CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) -#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED) -#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE(2, METHOD_BUFFERED) -#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE(3, METHOD_BUFFERED) -#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE(4, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE(5, METHOD_BUFFERED) -#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE(7, METHOD_BUFFERED) -#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE(8, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE(9, METHOD_BUFFERED) -#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE(10, METHOD_BUFFERED) - -/* Windows registry crap */ -#define MAX_KEY_LENGTH 255 -#define MAX_VALUE_NAME 16383 -#define NETWORK_ADAPTERS \ - "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-" \ - "08002BE10318}" +#include "platform.hpp" -typedef unsigned long IPADDR; - -namespace llarp::vpn +namespace llarp::win32 { - static char* - reg_query(char* key_name) - { - HKEY adapters, adapter; - DWORD i, ret, len; - char* deviceid = nullptr; - DWORD sub_keys = 0; - - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(key_name), 0, KEY_READ, &adapters); - if (ret != ERROR_SUCCESS) - { - return nullptr; - } - - ret = RegQueryInfoKey( - adapters, NULL, NULL, NULL, &sub_keys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) - { - return nullptr; - } - - if (sub_keys <= 0) - { - return nullptr; - } - - /* Walk througt all adapters */ - for (i = 0; i < sub_keys; i++) - { - char new_key[MAX_KEY_LENGTH]; - char data[256]; - TCHAR key[MAX_KEY_LENGTH]; - DWORD keylen = MAX_KEY_LENGTH; - - /* Get the adapter key name */ - ret = RegEnumKeyEx(adapters, i, key, &keylen, NULL, NULL, NULL, NULL); - if (ret != ERROR_SUCCESS) - { - continue; - } - - /* Append it to NETWORK_ADAPTERS and open it */ - snprintf(new_key, sizeof new_key, "%s\\%s", key_name, key); - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(new_key), 0, KEY_READ, &adapter); - if (ret != ERROR_SUCCESS) - { - continue; - } - - /* Check its values */ - len = sizeof data; - ret = RegQueryValueEx(adapter, "ComponentId", NULL, NULL, (LPBYTE)data, &len); - if (ret != ERROR_SUCCESS) - { - /* This value doesn't exist in this adaptater tree */ - goto clean; - } - /* If its a tap adapter, its all good */ - if (strncmp(data, "tap0901", 7) == 0) - { - DWORD type; - - len = sizeof data; - ret = RegQueryValueEx(adapter, "NetCfgInstanceId", NULL, &type, (LPBYTE)data, &len); - if (ret != ERROR_SUCCESS) - { - goto clean; - } - deviceid = strdup(data); - break; - } - clean: - RegCloseKey(adapter); - } - RegCloseKey(adapters); - return deviceid; - } - - template - void - ForEachWIN32Interface(Visit visit) - { -#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x)) -#define FREE(x) HeapFree(GetProcessHeap(), 0, (x)) - MIB_IPFORWARDTABLE* pIpForwardTable; - DWORD dwSize = 0; - DWORD dwRetVal = 0; - - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE)); - if (pIpForwardTable == nullptr) - return; - - if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) - { - FREE(pIpForwardTable); - pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize); - if (pIpForwardTable == nullptr) - { - return; - } - } - - if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR) - { - for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++) - { - visit(&pIpForwardTable->table[i]); - } - } - FREE(pIpForwardTable); -#undef MALLOC -#undef FREE - } - - std::optional - GetInterfaceIndex(huint32_t ip) - { - std::optional ret = std::nullopt; - ForEachWIN32Interface([&ret, n = ToNet(ip)](auto* iface) { - if (ret.has_value()) - return; - if (iface->dwForwardNextHop == n.n) - { - ret = iface->dwForwardIfIndex; - } - }); - return ret; - } - namespace { - std::wstring - get_win_sys_path() + template + std::string + ip_to_string(T ip) { - wchar_t win_sys_path[MAX_PATH] = {0}; - const wchar_t* default_sys_path = L"C:\\Windows\\system32"; - - if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path))) - { - wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path)); - win_sys_path[_countof(win_sys_path) - 1] = L'\0'; - } - return win_sys_path; + return var::visit([](auto&& ip) { return ip.ToString(); }, ip); } } // namespace - class Win32Interface final : public NetworkInterface + using namespace llarp::vpn; + class VPNPlatform : public Platform, public IRouteManager { - std::atomic m_Run; - HANDLE m_Device, m_IOCP; - std::vector m_Threads; - thread::Queue m_ReadQueue; - - InterfaceInfo m_Info; + llarp::Context* const _ctx; + const int m_Metric{2}; - AbstractRouter* const _router; - - static std::string - NetSHCommand() + const auto& + Net() const { - std::wstring wcmd = get_win_sys_path() + L"\\netsh.exe"; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); + return _ctx->router->Net(); } - static void - NetSH(std::string commands) + void + Route(std::string ip, std::string gw, std::string cmd) { - commands = NetSHCommand() + " interface IPv6 " + commands; - LogInfo("exec: ", commands); - ::system(commands.c_str()); + llarp::win32::Exec( + "route.exe", + fmt::format("{} {} MASK 255.255.255.255 {} METRIC {}", cmd, ip, gw, m_Metric)); } - public: - static std::string - RouteCommand() + void + DefaultRouteViaInterface(NetworkInterface& vpn, std::string cmd) { - std::wstring wcmd = get_win_sys_path() + L"\\route.exe"; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); + // route hole for loopback bacause god is dead on windows + llarp::win32::Exec( + "route.exe", fmt::format("{} 127.0.0.0 MASK 255.0.0.0 0.0.0.0 METRIC {}", cmd, m_Metric)); + // set up ipv4 routes + auto lower = RouteViaInterface(vpn, "0.0.0.0", "128.0.0.0", cmd); + auto upper = RouteViaInterface(vpn, "128.0.0.0", "128.0.0.0", cmd); } - Win32Interface(InterfaceInfo info, AbstractRouter* router) - : m_ReadQueue{1024}, m_Info{std::move(info)}, _router{router} + OneShotExec + RouteViaInterface(NetworkInterface& vpn, std::string addr, std::string mask, std::string cmd) { - DWORD len; - - const auto device_id = reg_query(NETWORK_ADAPTERS); - if (device_id == nullptr) - { - LogError("cannot query registry"); - throw std::invalid_argument{"cannot query registery"}; - } - const auto fname = fmt::format(R"(\\.\Global\{}.tap)", device_id); - m_Device = CreateFile( - fname.c_str(), - GENERIC_WRITE | GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0); - if (m_Device == INVALID_HANDLE_VALUE) + const auto& info = vpn.Info(); + auto index = info.index; + if (index == 0) { - LogError("failed to open device"); - throw std::invalid_argument{"cannot open " + fname}; + if (auto maybe_idx = net::Platform::Default_ptr()->GetInterfaceIndex(info[0])) + index = *maybe_idx; } - LogInfo("putting interface up..."); - ULONG flag = 1; - // put the interface up - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_SET_MEDIA_STATUS, - &flag, - sizeof(flag), - &flag, - sizeof(flag), - &len, - nullptr)) + auto ifaddr = ip_to_string(info[0]); + // this changes the last 1 to a 0 so that it routes over the interface + // this is required because windows is idiotic af + ifaddr.back()--; + if (index) { - LogError("cannot up interface up"); - throw std::invalid_argument{"cannot put interface up"}; + return OneShotExec{ + "route.exe", + fmt::format( + "{} {} MASK {} {} IF {} METRIC {}", cmd, addr, mask, ifaddr, info.index, m_Metric)}; } - - LogInfo("setting addresses"); - huint32_t ip{}; - // set ipv4 addresses - for (const auto& ifaddr : m_Info.addrs) - { - if (ifaddr.fam == AF_INET) - { - IPADDR sock[3]{}; - const nuint32_t addr = xhtonl(net::TruncateV6(ifaddr.range.addr)); - ip = net::TruncateV6(ifaddr.range.addr); - m_Info.ifname = ip.ToString(); - const nuint32_t mask = xhtonl(net::TruncateV6(ifaddr.range.netmask_bits)); - LogInfo("address ", addr, " netmask ", mask); - sock[0] = addr.n; - sock[1] = addr.n & mask.n; - sock[2] = mask.n; - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_TUN, - &sock, - sizeof(sock), - &sock, - sizeof(sock), - &len, - nullptr)) - { - LogError("cannot set address"); - throw std::invalid_argument{"cannot configure tun interface address"}; - } - - IPADDR ep[4]{}; - - ep[0] = addr.n; - ep[1] = mask.n; - ep[2] = (addr.n | ~mask.n) - htonl(1); - ep[3] = 31536000; - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_DHCP_MASQ, - ep, - sizeof(ep), - ep, - sizeof(ep), - &len, - nullptr)) - { - LogError("cannot set dhcp masq"); - throw std::invalid_argument{"Cannot configure tun interface dhcp"}; - } -#pragma pack(push) -#pragma pack(1) - struct opt - { - uint8_t dhcp_opt; - uint8_t length; - uint32_t value; - } dns, gateway; -#pragma pack(pop) - - const nuint32_t dnsaddr{xhtonl(m_Info.dnsaddr)}; - dns.dhcp_opt = 6; - dns.length = 4; - dns.value = dnsaddr.n; - - gateway.dhcp_opt = 3; - gateway.length = 4; - gateway.value = addr.n + htonl(1); - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_DHCP_SET_OPT, - &gateway, - sizeof(gateway), - &gateway, - sizeof(gateway), - &len, - nullptr)) - { - LogError("cannot set gateway"); - throw std::invalid_argument{"cannot set tun gateway"}; - } - - if (not DeviceIoControl( - m_Device, - TAP_IOCTL_CONFIG_DHCP_SET_OPT, - &dns, - sizeof(dns), - &dns, - sizeof(dns), - &len, - nullptr)) - { - LogError("cannot set dns"); - throw std::invalid_argument{"cannot set tun dns"}; - } - } - } - // set ipv6 addresses - for (const auto& ifaddr : m_Info.addrs) + else { - if (ifaddr.fam == AF_INET6) - { - const auto maybe = GetInterfaceIndex(ip); - if (maybe.has_value()) - { - NetSH( - "add address interface=" + std::to_string(*maybe) + " " + ifaddr.range.ToString()); - } - } + return OneShotExec{ + "route.exe", + fmt::format("{} {} MASK {} {} METRIC {}", cmd, addr, mask, ifaddr, m_Metric)}; } } - ~Win32Interface() - { - ULONG flag = 0; - DWORD len; - // put the interface down - DeviceIoControl( - m_Device, - TAP_IOCTL_SET_MEDIA_STATUS, - &flag, - sizeof(flag), - &flag, - sizeof(flag), - &len, - nullptr); - m_Run = false; - CloseHandle(m_IOCP); - // close the handle - CloseHandle(m_Device); - // close the reader threads - for (auto& thread : m_Threads) - thread.join(); - } - - virtual void - MaybeWakeUpperLayers() const override - { - _router->TriggerPump(); - } - - int - PollFD() const override - { - return -1; - } - - std::string - IfName() const override - { - return m_Info.ifname; - } - - void - Start() - { - m_Run = true; - const auto numThreads = std::thread::hardware_concurrency(); - // allocate packets - for (size_t idx = 0; idx < numThreads; ++idx) - m_Packets.emplace_back(new asio_evt_pkt{true}); - - // create completion port - m_IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0); - // attach the handle or some shit - CreateIoCompletionPort(m_Device, m_IOCP, 0, 0); - // spawn reader threads - for (size_t idx = 0; idx < numThreads; ++idx) - m_Threads.emplace_back([this, idx]() { ReadLoop(idx); }); - } - - net::IPPacket - ReadNextPacket() override - { - if (m_ReadQueue.empty()) - return net::IPPacket{}; - - return m_ReadQueue.popFront(); - } - - struct asio_evt_pkt - { - explicit asio_evt_pkt(bool _read) : read{_read} - {} + std::shared_ptr _wintun; - OVERLAPPED hdr = {0, 0, 0, 0, nullptr}; // must be first, since this is part of the IO call - bool read; - net::IPPacket pkt; + WinDivert_API m_WinDivert{}; - void - Read(HANDLE dev) - { - ReadFile(dev, pkt.buf, sizeof(pkt.buf), nullptr, &hdr); - } - }; + public: + VPNPlatform(const VPNPlatform&) = delete; + VPNPlatform(VPNPlatform&&) = delete; - std::vector> m_Packets; + VPNPlatform(llarp::Context* ctx) : Platform{}, _ctx{ctx}, _wintun{WintunContext_new()} + {} - bool - WritePacket(net::IPPacket pkt) - { - LogDebug("write packet ", pkt.sz); - asio_evt_pkt* ev = new asio_evt_pkt{false}; - std::copy_n(pkt.buf, pkt.sz, ev->pkt.buf); - ev->pkt.sz = pkt.sz; - WriteFile(m_Device, ev->pkt.buf, ev->pkt.sz, nullptr, &ev->hdr); - return true; - } + virtual ~VPNPlatform() = default; void - ReadLoop(size_t packetIndex) + AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { - auto& ev = m_Packets[packetIndex]; - ev->Read(m_Device); - while (m_Run) - { - DWORD size; - ULONG_PTR user; - OVERLAPPED* ovl = nullptr; - if (not GetQueuedCompletionStatus(m_IOCP, &size, &user, &ovl, 1000)) - continue; - asio_evt_pkt* pkt = (asio_evt_pkt*)ovl; - LogDebug("got iocp event size=", size, " read=", pkt->read); - if (pkt->read) - { - pkt->pkt.sz = size; - m_ReadQueue.pushBack(pkt->pkt); - pkt->Read(m_Device); - } - else - delete pkt; - } + Route(ip_to_string(ip), ip_to_string(gateway), "ADD"); } - }; - class Win32RouteManager : public IRouteManager - { void - Execute(std::string cmd) const + DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override { - llarp::LogInfo("exec: ", cmd); - ::system(cmd.c_str()); - } - - static std::string - PowerShell() - { - std::wstring wcmd = - get_win_sys_path() + L"\\WindowsPowerShell\\v1.0\\powershell.exe -Command "; - - using convert_type = std::codecvt_utf8; - std::wstring_convert converter; - return converter.to_bytes(wcmd); - } - - static std::string - RouteCommand() - { - return Win32Interface::RouteCommand(); + Route(ip_to_string(ip), ip_to_string(gateway), "DELETE"); } void - Route(IPVariant_t ip, IPVariant_t gateway, std::string cmd) + AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - Execute(fmt::format( - "{} {} {} MASK 255.255.255.255 {} METRIC 2", RouteCommand(), cmd, ip, gateway)); + RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "ADD"); } void - DefaultRouteViaInterface(std::string ifname, std::string cmd) + DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override { - // poke hole for loopback bacause god is dead on windows - Execute(RouteCommand() + " " + cmd + " 127.0.0.0 MASK 255.0.0.0 0.0.0.0"); - - huint32_t ip{}; - ip.FromString(ifname); - const auto ipv6 = net::ExpandV4Lan(ip); - - Execute(RouteCommand() + " " + cmd + " ::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " " + cmd + " 4000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " " + cmd + " 8000::/2 " + ipv6.ToString()); - Execute(RouteCommand() + " " + cmd + " c000::/2 " + ipv6.ToString()); - - ifname.back()++; - Execute(RouteCommand() + " " + cmd + " 0.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2"); - Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2"); + RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "DELETE"); } - void - RouteViaInterface(std::string ifname, IPRange range, std::string cmd) + std::vector + GetGatewaysNotOnInterface(NetworkInterface& vpn) override { - if (range.IsV4()) - { - const huint32_t addr4 = net::TruncateV6(range.addr); - const huint32_t mask4 = net::TruncateV6(range.netmask_bits); - Execute( - RouteCommand() + " " + cmd + " " + addr4.ToString() + " MASK " + mask4.ToString() + " " - + ifname); - } - else + std::vector gateways; + + auto idx = vpn.Info().index; + using UInt_t = decltype(idx); + for (const auto& iface : Net().AllNetworkInterfaces()) { - Execute( - RouteCommand() + " " + cmd + range.addr.ToString() + " MASK " - + range.netmask_bits.ToString() + " " + ifname); + if (static_cast(iface.index) == idx) + continue; + if (iface.gateway) + gateways.emplace_back(*iface.gateway); } - } - - public: - void - AddRoute(IPVariant_t ip, IPVariant_t gateway) override - { - Route(ip, gateway, "ADD"); + return gateways; } void - DelRoute(IPVariant_t ip, IPVariant_t gateway) override + AddDefaultRouteViaInterface(NetworkInterface& vpn) override { - Route(ip, gateway, "DELETE"); - } + // kill ipv6 + llarp::win32::Exec( + "WindowsPowerShell\\v1.0\\powershell.exe", + "-Command (Disable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)"); - void - AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override - { - RouteViaInterface(vpn.IfName(), range, "ADD"); + DefaultRouteViaInterface(vpn, "ADD"); + llarp::win32::Exec("ipconfig.exe", "/flushdns"); } void - DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override + DelDefaultRouteViaInterface(NetworkInterface& vpn) override { - RouteViaInterface(vpn.IfName(), range, "DELETE"); - } + // restore ipv6 + llarp::win32::Exec( + "WindowsPowerShell\\v1.0\\powershell.exe", + "-Command (Enable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)"); - std::vector - GetGatewaysNotOnInterface(std::string ifname) override - { - std::vector gateways; - ForEachWIN32Interface([&](auto w32interface) { - struct in_addr gateway, interface_addr; - gateway.S_un.S_addr = (u_long)w32interface->dwForwardDest; - interface_addr.S_un.S_addr = (u_long)w32interface->dwForwardNextHop; - std::string interface_name{inet_ntoa(interface_addr)}; - if ((!gateway.S_un.S_addr) and interface_name != ifname) - { - llarp::LogTrace( - "Win32 find gateway: Adding gateway (", interface_name, ") to list of gateways."); - huint32_t x{}; - if (x.FromString(interface_name)) - gateways.push_back(x); - } - }); - return gateways; + DefaultRouteViaInterface(vpn, "DELETE"); + llarp::win32::Exec("netsh.exe", "winsock reset"); + llarp::win32::Exec("ipconfig.exe", "/flushdns"); } - void - AddDefaultRouteViaInterface(std::string ifname) override + std::shared_ptr + ObtainInterface(InterfaceInfo info, AbstractRouter* router) override { - // kill ipv6 - Execute(PowerShell() + R"(Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); - DefaultRouteViaInterface(ifname, "ADD"); + return WintunInterface_new(_wintun, std::move(info), router); } - void - DelDefaultRouteViaInterface(std::string ifname) override + std::shared_ptr + create_packet_io(unsigned int ifindex) override { - // restore ipv6 - Execute(PowerShell() + R"(Enable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); - DefaultRouteViaInterface(ifname, "DELETE"); + // we only want do this on all interfaes with windivert + if (ifindex) + throw std::invalid_argument{ + "cannot create packet io on explicitly specified interface, not currently supported on " + "windows (yet)"}; + return m_WinDivert.make_intercepter( + "outbound and ( udp.DstPort == 53 or tcp.DstPort == 53 )", + [router = _ctx->router] { router->TriggerPump(); }); } - }; - - class Win32Platform : public Platform - { - Win32RouteManager _routeManager{}; - - public: - std::shared_ptr - ObtainInterface(InterfaceInfo info, AbstractRouter* router) override - { - auto netif = std::make_shared(std::move(info), router); - netif->Start(); - return netif; - }; IRouteManager& RouteManager() override { - return _routeManager; + return *this; } }; -} // namespace llarp::vpn +} // namespace llarp::win32 diff --git a/llarp/win32/dll.cpp b/llarp/win32/dll.cpp new file mode 100644 index 000000000..650617a52 --- /dev/null +++ b/llarp/win32/dll.cpp @@ -0,0 +1,22 @@ +#include "dll.hpp" +#include +#include + +namespace llarp::win32 +{ + namespace + { + auto cat = log::Cat("win32-dll"); + } + DLL::DLL(std::string dll) : m_Handle{LoadLibraryA(dll.c_str())} + { + if (not m_Handle) + throw win32::error{fmt::format("failed to load '{}'", dll)}; + log::info(cat, "loaded '{}'", dll); + } + + DLL::~DLL() + { + FreeLibrary(m_Handle); + } +} // namespace llarp::win32 diff --git a/llarp/win32/dll.hpp b/llarp/win32/dll.hpp new file mode 100644 index 000000000..ab7360aa0 --- /dev/null +++ b/llarp/win32/dll.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +#include "exception.hpp" +#include + +namespace llarp::win32 +{ + class DLL + { + const HMODULE m_Handle; + + protected: + /// given a name of a function pointer find it and put it into `func` + /// throws if the function does not exist in the DLL we openned. + template + void + init(std::string name, Func_t& func) + { + auto ptr = GetProcAddress(m_Handle, name.c_str()); + if (not ptr) + throw win32::error{fmt::format("function '{}' not found", name)}; + func = reinterpret_cast(ptr); + } + + public: + DLL(std::string dll); + + virtual ~DLL(); + }; +} // namespace llarp::win32 diff --git a/llarp/win32/exception.cpp b/llarp/win32/exception.cpp new file mode 100644 index 000000000..4b74e0584 --- /dev/null +++ b/llarp/win32/exception.cpp @@ -0,0 +1,39 @@ +#include "windows.h" +#include "exception.hpp" +#include +#include + +namespace llarp::win32 + +{ + error::error(std::string msg) : error{GetLastError(), std::move(msg)} + {} + error::error(DWORD err, std::string msg) + : std::runtime_error{fmt::format("{}: {} (code={})", msg, error_to_string(err), err)} + {} + std::string + error_to_string(DWORD err) + { + // mostly yoinked from https://stackoverflow.com/a/45565001 + LPTSTR psz{nullptr}; + const DWORD cchMsg = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER, + nullptr, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&psz), + 0, + nullptr); + + if (cchMsg <= 0) + { + // cannot get message for error, reset the last error here so it does not propagate + ::SetLastError(0); + return "unknown error"; + } + + auto deleter = [](void* p) { ::LocalFree(p); }; + std::unique_ptr ptrBuffer{psz, deleter}; + return std::string{ptrBuffer.get(), cchMsg}; + } +} // namespace llarp::win32 diff --git a/llarp/win32/exception.hpp b/llarp/win32/exception.hpp index 893fcc741..38663061c 100644 --- a/llarp/win32/exception.hpp +++ b/llarp/win32/exception.hpp @@ -1,31 +1,18 @@ #pragma once - -#include -#include #include +#include #include -#include - namespace llarp::win32 { - namespace - { - inline std::string - error_to_string(DWORD err) - { - std::array buffer{}; - - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, &err, 0, 0, buffer.data(), buffer.size(), nullptr); - return std::string{buffer.data()}; - } - } // namespace + std::string + error_to_string(DWORD err); class error : public std::runtime_error { public: - error(DWORD err, std::string msg) - : std::runtime_error{fmt::format("{}: {}", msg, error_to_string(err))} - {} + error(std::string msg); + virtual ~error() = default; + explicit error(DWORD err, std::string msg); }; } // namespace llarp::win32 diff --git a/llarp/win32/exec.cpp b/llarp/win32/exec.cpp new file mode 100644 index 000000000..bce7f02d7 --- /dev/null +++ b/llarp/win32/exec.cpp @@ -0,0 +1,57 @@ +#include "exec.hpp" +#include "exception.hpp" +#include + +#include + +namespace llarp::win32 +{ + namespace + { + auto logcat = log::Cat("win32:exec"); + + /// get the directory for system32 which contains all the executables we use + std::string + SystemExeDir() + { + std::array path{}; + + if (GetSystemDirectoryA(path.data(), path.size())) + return path.data(); + + return "C:\\Windows\\system32"; + } + + } // namespace + + void + Exec(std::string exe, std::string args) + { + OneShotExec{exe, args}; + } + + OneShotExec::OneShotExec(std::string cmd, std::chrono::milliseconds timeout) + : _si{}, _pi{}, _timeout{timeout.count()} + { + log::info(logcat, "exec: {}", cmd); + if (not CreateProcessA( + nullptr, cmd.data(), nullptr, nullptr, false, 0, nullptr, nullptr, &_si, &_pi)) + throw win32::error(GetLastError(), "failed to execute subprocess"); + } + + OneShotExec::~OneShotExec() + { + WaitForSingleObject(_pi.hProcess, _timeout); + CloseHandle(_pi.hProcess); + CloseHandle(_pi.hThread); + } + + OneShotExec::OneShotExec(std::string cmd, std::string args, std::chrono::milliseconds timeout) + : OneShotExec{fmt::format("{}\\{} {}", SystemExeDir(), cmd, args), timeout} + {} + + DeferExec::~DeferExec() + { + OneShotExec{std::move(_exe), std::move(_args), std::move(_timeout)}; + } +} // namespace llarp::win32 diff --git a/llarp/win32/exec.hpp b/llarp/win32/exec.hpp new file mode 100644 index 000000000..f77e009df --- /dev/null +++ b/llarp/win32/exec.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +namespace llarp::win32 +{ + /// RAII wrapper for calling a subprocess in win32 for parallel executuion in the same scope + /// destructor blocks until timeout has been hit of the execution of the subprocses finished + class OneShotExec + { + STARTUPINFO _si; + PROCESS_INFORMATION _pi; + const DWORD _timeout; + + OneShotExec(std::string cmd, std::chrono::milliseconds timeout); + + public: + /// construct a call to an exe in system32 with args, will resolve the full path of the exe to + /// prevent path injection + explicit OneShotExec(std::string exe, std::string args, std::chrono::milliseconds timeout = 5s); + + ~OneShotExec(); + }; + + /// a wrapper for OneShotExec that calls the thing we want on destruction + class DeferExec + { + std::string _exe; + std::string _args; + std::chrono::milliseconds _timeout; + + public: + explicit DeferExec(std::string exe, std::string args, std::chrono::milliseconds timeout = 5s) + : _exe{std::move(exe)}, _args{std::move(args)}, _timeout{std::move(timeout)} + {} + + ~DeferExec(); + }; + + /// simple wrapper to run a single command in a blocking way + void + Exec(std::string exe, std::string args); + +} // namespace llarp::win32 diff --git a/llarp/win32/guid.hpp b/llarp/win32/guid.hpp new file mode 100644 index 000000000..83c926523 --- /dev/null +++ b/llarp/win32/guid.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +namespace llarp::win32 +{ + /// @brief given a container of data hash it and make it into a GUID so we have a way to + /// deterministically generate GUIDs + template + inline GUID + MakeDeterministicGUID(Data data) + { + ShortHash h{}; + auto hash = [&h](auto data) { CryptoManager::instance()->shorthash(h, data); }; + + if constexpr (std::is_same_v) + hash(llarp_buffer_t{reinterpret_cast(data.data()), data.size()}); + else + hash(llarp_buffer_t{data}); + GUID guid{}; + std::copy_n( + h.begin(), std::min(sizeof(GUID), sizeof(ShortHash)), reinterpret_cast(&guid)); + return guid; + } +} // namespace llarp::win32 diff --git a/llarp/win32/handle.hpp b/llarp/win32/handle.hpp new file mode 100644 index 000000000..d97caf7b6 --- /dev/null +++ b/llarp/win32/handle.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include "exception.hpp" + +namespace llarp::win32 +{ + inline void + ensure_handle_is_valid(HANDLE h) + { + BY_HANDLE_FILE_INFORMATION info{}; + if (GetFileInformationByHandle(h, &info)) + return; + if (auto err = GetLastError()) + { + SetLastError(0); + throw llarp::win32::error{err, "handle validity check failed"}; + } + } +} // namespace llarp::win32 diff --git a/llarp/win32/windivert.cpp b/llarp/win32/windivert.cpp new file mode 100644 index 000000000..4e4e4d3f4 --- /dev/null +++ b/llarp/win32/windivert.cpp @@ -0,0 +1,194 @@ +#include +#include +#include "windivert.hpp" +#include "dll.hpp" +#include "handle.hpp" +#include +#include +#include +extern "C" +{ +#include +} +namespace L = llarp::log; + +namespace llarp::win32 +{ + namespace + { + auto cat = L::Cat("windivert"); + } + + using WD_Open_Func_t = decltype(&::WinDivertOpen); + using WD_Close_Func_t = decltype(&::WinDivertClose); + using WD_Shutdown_Func_t = decltype(&::WinDivertShutdown); + using WD_Send_Func_t = decltype(&::WinDivertSend); + using WD_Recv_Func_t = decltype(&::WinDivertRecv); + using WD_IP4_Format_Func_t = decltype(&::WinDivertHelperFormatIPv4Address); + using WD_IP6_Format_Func_t = decltype(&::WinDivertHelperFormatIPv6Address); + + struct WinDivertDLL : DLL + { + WD_Open_Func_t open; + WD_Close_Func_t close; + WD_Shutdown_Func_t shutdown; + WD_Send_Func_t send; + WD_Recv_Func_t recv; + WD_IP4_Format_Func_t format_ip4; + WD_IP6_Format_Func_t format_ip6; + + WinDivertDLL() : DLL{"WinDivert.dll"} + { + init("WinDivertOpen", open); + init("WinDivertClose", close); + init("WinDivertShutdown", shutdown); + init("WinDivertSend", send); + init("WinDivertRecv", recv); + init("WinDivertHelperFormatIPv4Address", format_ip4); + init("WinDivertHelperFormatIPv6Address", format_ip6); + L::debug(cat, "loaded windivert functions"); + } + + virtual ~WinDivertDLL() = default; + }; + + struct WD_Packet + { + std::vector pkt; + WINDIVERT_ADDRESS addr; + }; + + class WinDivert_IO : public llarp::vpn::I_Packet_IO + { + const std::shared_ptr m_WinDivert; + std::function m_Wake; + + HANDLE m_Handle; + std::thread m_Runner; + thread::Queue m_RecvQueue; + + public: + WinDivert_IO( + std::shared_ptr api, std::string filter_spec, std::function wake) + : m_WinDivert{api}, m_Wake{wake}, m_RecvQueue{recv_queue_size} + { + L::info(cat, "load windivert with filterspec: '{}'", filter_spec); + + m_Handle = m_WinDivert->open(filter_spec.c_str(), WINDIVERT_LAYER_NETWORK, 0, 0); + if (auto err = GetLastError()) + throw win32::error{err, "cannot open windivert handle"}; + } + + ~WinDivert_IO() + { + m_WinDivert->close(m_Handle); + } + + std::optional + recv_packet() const + { + WINDIVERT_ADDRESS addr{}; + std::vector pkt; + pkt.resize(1500); // net::IPPacket::MaxSize + UINT sz{}; + if (not m_WinDivert->recv(m_Handle, pkt.data(), pkt.size(), &sz, &addr)) + { + auto err = GetLastError(); + if (err and err != ERROR_BROKEN_PIPE) + throw win32::error{ + err, fmt::format("failed to receive packet from windivert (code={})", err)}; + else if (err) + SetLastError(0); + return std::nullopt; + } + L::info(cat, "got packet of size {}B", sz); + pkt.resize(sz); + return WD_Packet{std::move(pkt), std::move(addr)}; + } + + void + send_packet(const WD_Packet& w_pkt) const + { + const auto& pkt = w_pkt.pkt; + const auto* addr = &w_pkt.addr; + L::info(cat, "send dns packet of size {}B", pkt.size()); + UINT sz{}; + if (m_WinDivert->send(m_Handle, pkt.data(), pkt.size(), &sz, addr)) + return; + throw win32::error{"windivert send failed"}; + } + + virtual int + PollFD() const + { + return -1; + } + + virtual bool WritePacket(net::IPPacket) override + { + return false; + } + + virtual net::IPPacket + ReadNextPacket() override + { + auto w_pkt = recv_packet(); + if (not w_pkt) + return net::IPPacket{}; + net::IPPacket pkt{std::move(w_pkt->pkt)}; + pkt.reply = [this, addr = std::move(w_pkt->addr)](auto pkt) { + send_packet(WD_Packet{pkt.steal(), addr}); + }; + return pkt; + } + + virtual void + Start() override + { + L::info(cat, "starting windivert"); + if (m_Runner.joinable()) + throw std::runtime_error{"windivert thread is already running"}; + + auto read_loop = [this]() { + log::debug(cat, "windivert read loop start"); + while (true) + { + // in the read loop, read packets until they stop coming in + // each packet is sent off + if (auto maybe_pkt = recv_packet()) + m_RecvQueue.pushBack(std::move(*maybe_pkt)); + else // leave loop on read fail + break; + } + log::debug(cat, "windivert read loop end"); + }; + + m_Runner = std::thread{std::move(read_loop)}; + } + + virtual void + Stop() override + { + L::info(cat, "stopping windivert"); + m_WinDivert->shutdown(m_Handle, WINDIVERT_SHUTDOWN_BOTH); + m_Runner.join(); + } + }; + + WinDivert_API::WinDivert_API() : m_Impl{std::make_shared()} + {} + + std::string + WinDivert_API::format_ip(uint32_t ip) const + { + std::array buf{}; + m_Impl->format_ip4(ip, buf.data(), buf.size()); + return buf.data(); + } + + std::shared_ptr + WinDivert_API::make_intercepter(std::string filter_spec, std::function wake) const + { + return std::make_shared(m_Impl, filter_spec, wake); + } +} // namespace llarp::win32 diff --git a/llarp/win32/windivert.hpp b/llarp/win32/windivert.hpp new file mode 100644 index 000000000..36fbaffea --- /dev/null +++ b/llarp/win32/windivert.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include +#include + +namespace llarp::win32 +{ + struct WinDivertDLL; + + class WinDivert_API + { + std::shared_ptr m_Impl; + + public: + WinDivert_API(); + + /// format an ipv4 in host order to string such that a windivert filter spec can understand it + std::string + format_ip(uint32_t ip) const; + + /// create a packet intercepter that uses windivert. + /// filter_spec describes the kind of traffic we wish to intercept. + /// pass in a callable that wakes up the main event loop. + /// we hide all implementation details from other compilation units to prevent issues with + /// linkage that may arrise. + std::shared_ptr + make_intercepter(std::string filter_spec, std::function wakeup) const; + }; +} // namespace llarp::win32 diff --git a/llarp/win32/wintun.cpp b/llarp/win32/wintun.cpp new file mode 100644 index 000000000..bf8554891 --- /dev/null +++ b/llarp/win32/wintun.cpp @@ -0,0 +1,411 @@ +extern "C" +{ +#include +} + +#include +#include "wintun.hpp" +#include "exception.hpp" +#include "dll.hpp" +#include "guid.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace llarp::win32 +{ + namespace + { + auto logcat = log::Cat("wintun"); + constexpr auto PoolName = "lokinet"; + } // namespace + + using Adapter_ptr = std::shared_ptr<_WINTUN_ADAPTER>; + + struct PacketWrapper + { + BYTE* data; + DWORD size; + WINTUN_SESSION_HANDLE session; + WINTUN_RELEASE_RECEIVE_PACKET_FUNC* release; + /// copy our data into an ip packet struct + net::IPPacket + copy() const + { + net::IPPacket pkt{size}; + std::copy_n(data, size, pkt.data()); + return pkt; + } + + ~PacketWrapper() + { + release(session, data); + } + }; + + class WintunDLL : public DLL + { + public: + WINTUN_CREATE_ADAPTER_FUNC* create_adapter; + WINTUN_OPEN_ADAPTER_FUNC* open_adapter; + WINTUN_CLOSE_ADAPTER_FUNC* close_adapter; + + WINTUN_START_SESSION_FUNC* start_session; + WINTUN_END_SESSION_FUNC* end_session; + + WINTUN_GET_ADAPTER_LUID_FUNC* get_adapter_LUID; + WINTUN_GET_READ_WAIT_EVENT_FUNC* get_adapter_handle; + + WINTUN_RECEIVE_PACKET_FUNC* read_packet; + WINTUN_RELEASE_RECEIVE_PACKET_FUNC* release_read; + + WINTUN_ALLOCATE_SEND_PACKET_FUNC* alloc_write; + WINTUN_SEND_PACKET_FUNC* write_packet; + + WINTUN_SET_LOGGER_FUNC* set_logger; + WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC* get_version; + /// read out all the wintun function pointers from a library handle + WintunDLL() : DLL{"wintun.dll"} + { + init("WintunGetRunningDriverVersion", get_version); + init("WintunCreateAdapter", create_adapter); + init("WintunOpenAdapter", open_adapter); + init("WintunCloseAdapter", close_adapter); + init("WintunStartSession", start_session); + init("WintunEndSession", end_session); + init("WintunGetAdapterLUID", get_adapter_LUID); + init("WintunReceivePacket", read_packet); + init("WintunReleaseReceivePacket", release_read); + init("WintunSendPacket", write_packet); + init("WintunAllocateSendPacket", alloc_write); + init("WintunSetLogger", set_logger); + init("WintunGetReadWaitEvent", get_adapter_handle); + + log::info(logcat, fmt::format("wintun version {0:x} loaded", get_version())); + } + + /// autovivify a wintun adapter handle + [[nodiscard]] auto + make_adapter(std::string adapter_name, std::string tunnel_name) const + { + auto adapter_name_wide = to_wide(adapter_name); + if (auto _impl = open_adapter(adapter_name_wide.c_str())) + { + log::info(logcat, "opened existing adapter: '{}'", adapter_name); + return _impl; + } + if (auto err = GetLastError()) + { + log::info( + logcat, "did not open existing adapter '{}': {}", adapter_name, error_to_string(err)); + SetLastError(0); + } + const auto guid = + llarp::win32::MakeDeterministicGUID(fmt::format("{}|{}", adapter_name, tunnel_name)); + log::info(logcat, "creating adapter: '{}' on pool '{}'", adapter_name, tunnel_name); + auto tunnel_name_wide = to_wide(tunnel_name); + if (auto _impl = create_adapter(adapter_name_wide.c_str(), tunnel_name_wide.c_str(), &guid)) + return _impl; + throw win32::error{"failed to create wintun adapter"}; + } + }; + + class WintunAdapter + { + WINTUN_CLOSE_ADAPTER_FUNC* _close_adapter; + WINTUN_GET_ADAPTER_LUID_FUNC* _get_adapter_LUID; + + WINTUN_GET_READ_WAIT_EVENT_FUNC* _get_handle; + WINTUN_START_SESSION_FUNC* _start_session; + WINTUN_END_SESSION_FUNC* _end_session; + + WINTUN_ADAPTER_HANDLE _handle; + + [[nodiscard]] auto + get_adapter_LUID() const + { + NET_LUID _uid{}; + _get_adapter_LUID(_handle, &_uid); + return _uid; + } + + public: + WintunAdapter(const WintunDLL& dll, std::string name) + : _close_adapter{dll.close_adapter} + , _get_adapter_LUID{dll.get_adapter_LUID} + , _get_handle{dll.get_adapter_handle} + , _start_session{dll.start_session} + , _end_session{dll.end_session} + { + _handle = dll.make_adapter(std::move(name), PoolName); + if (_handle == nullptr) + throw std::runtime_error{"failed to create wintun adapter"}; + } + + /// put adapter up + void + Up(const vpn::InterfaceInfo& info) const + { + const auto luid = get_adapter_LUID(); + for (const auto& addr : info.addrs) + { + // TODO: implement ipv6 + if (addr.fam != AF_INET) + continue; + MIB_UNICASTIPADDRESS_ROW AddressRow; + InitializeUnicastIpAddressEntry(&AddressRow); + AddressRow.InterfaceLuid = luid; + + AddressRow.Address.Ipv4.sin_family = AF_INET; + AddressRow.Address.Ipv4.sin_addr.S_un.S_addr = ToNet(net::TruncateV6(addr.range.addr)).n; + AddressRow.OnLinkPrefixLength = addr.range.HostmaskBits(); + AddressRow.DadState = IpDadStatePreferred; + + if (auto err = CreateUnicastIpAddressEntry(&AddressRow); err != ERROR_SUCCESS) + throw error{err, fmt::format("cannot set address '{}'", addr.range)}; + LogDebug(fmt::format("added address: '{}'", addr.range)); + } + } + + /// put adapter down and close it + void + Down() const + { + _close_adapter(_handle); + } + + /// auto vivify a wintun session handle and read handle off of our adapter + [[nodiscard]] std::pair + session() const + { + if (auto impl = _start_session(_handle, WINTUN_MAX_RING_CAPACITY)) + { + if (auto handle = _get_handle(impl)) + return {impl, handle}; + _end_session(impl); + } + return {nullptr, nullptr}; + } + }; + + class WintunSession + { + WINTUN_END_SESSION_FUNC* _end_session; + WINTUN_RECEIVE_PACKET_FUNC* _recv_pkt; + WINTUN_RELEASE_RECEIVE_PACKET_FUNC* _release_pkt; + WINTUN_ALLOCATE_SEND_PACKET_FUNC* _alloc_write; + WINTUN_SEND_PACKET_FUNC* _write_pkt; + WINTUN_SESSION_HANDLE _impl; + HANDLE _handle; + + public: + WintunSession(const WintunDLL& dll) + : _end_session{dll.end_session} + , _recv_pkt{dll.read_packet} + , _release_pkt{dll.release_read} + , _alloc_write{dll.alloc_write} + , _write_pkt{dll.write_packet} + , _impl{nullptr} + , _handle{nullptr} + {} + + void + Start(const std::shared_ptr& adapter) + { + if (auto [impl, handle] = adapter->session(); impl and handle) + { + _impl = impl; + _handle = handle; + return; + } + throw error{GetLastError(), "could not create wintun session"}; + } + + void + Stop() const + { + _end_session(_impl); + } + + void + WaitFor(std::chrono::milliseconds dur) + { + WaitForSingleObject(_handle, dur.count()); + } + + /// read a unique pointer holding a packet read from wintun, returns the packet if we read one + /// and a bool, set to true if our adapter is now closed + [[nodiscard]] std::pair, bool> + ReadPacket() const + { + // typedef so the return statement fits on 1 line :^D + using Pkt_ptr = std::unique_ptr; + DWORD sz; + if (auto* ptr = _recv_pkt(_impl, &sz)) + return {Pkt_ptr{new PacketWrapper{ptr, sz, _impl, _release_pkt}}, false}; + const auto err = GetLastError(); + if (err == ERROR_NO_MORE_ITEMS or err == ERROR_HANDLE_EOF) + { + SetLastError(0); + return {nullptr, err == ERROR_HANDLE_EOF}; + } + throw error{err, "failed to read packet"}; + } + + /// write an ip packet to the interface, return 2 bools, first is did we write the packet, + /// second if we are terminating + std::pair + WritePacket(net::IPPacket pkt) const + { + if (auto* buf = _alloc_write(_impl, pkt.size())) + { + std::copy_n(pkt.data(), pkt.size(), buf); + _write_pkt(_impl, buf); + return {true, false}; + } + const auto err = GetLastError(); + if (err == ERROR_BUFFER_OVERFLOW or err == ERROR_HANDLE_EOF) + { + SetLastError(0); + return {err != ERROR_BUFFER_OVERFLOW, err == ERROR_HANDLE_EOF}; + } + throw error{err, "failed to write packet"}; + } + }; + + class WintunInterface : public vpn::NetworkInterface + { + AbstractRouter* const _router; + std::shared_ptr _adapter; + std::shared_ptr _session; + thread::Queue _recv_queue; + thread::Queue _send_queue; + std::thread _recv_thread; + std::thread _send_thread; + + static inline constexpr size_t packet_queue_length = 1024; + + public: + WintunInterface(const WintunDLL& dll, vpn::InterfaceInfo info, AbstractRouter* router) + : vpn::NetworkInterface{std::move(info)} + , _router{router} + , _adapter{std::make_shared(dll, m_Info.ifname)} + , _session{std::make_shared(dll)} + , _recv_queue{packet_queue_length} + , _send_queue{packet_queue_length} + {} + + void + Start() override + { + m_Info.index = 0; + // put the adapter and set addresses + _adapter->Up(m_Info); + // start up io session + _session->Start(_adapter); + + // start read packet loop + _recv_thread = std::thread{[session = _session, this]() { + do + { + // read all our packets this iteration + bool more{true}; + do + { + auto [pkt, done] = session->ReadPacket(); + // bail if we are closing + if (done) + return; + if (pkt) + _recv_queue.pushBack(pkt->copy()); + else + more = false; + } while (more); + // wait for more packets + session->WaitFor(5s); + } while (true); + }}; + // start write packet loop + _send_thread = std::thread{[this, session = _session]() { + do + { + if (auto maybe = _send_queue.popFrontWithTimeout(100ms)) + { + auto [written, done] = session->WritePacket(std::move(*maybe)); + if (done) + return; + } + } while (_send_queue.enabled()); + }}; + } + + void + Stop() override + { + // end writing packets + _send_queue.disable(); + _send_thread.join(); + // end reading packets + _session->Stop(); + _recv_thread.join(); + // close session + _session.reset(); + // put adapter down + _adapter->Down(); + _adapter.reset(); + } + + net::IPPacket + ReadNextPacket() override + { + net::IPPacket pkt{}; + if (auto maybe_pkt = _recv_queue.tryPopFront()) + pkt = std::move(*maybe_pkt); + return pkt; + } + + bool + WritePacket(net::IPPacket pkt) override + { + return _send_queue.tryPushBack(std::move(pkt)) == thread::QueueReturn::Success; + } + + int + PollFD() const override + { + return -1; + } + + void + MaybeWakeUpperLayers() const override + { + _router->TriggerPump(); + } + }; + + struct WintunContext + { + WintunDLL dll{}; + }; + + std::shared_ptr + WintunContext_new() + { + return std::make_shared(); + } + + std::shared_ptr + WintunInterface_new( + std::shared_ptr const& ctx, + const llarp::vpn::InterfaceInfo& info, + llarp::AbstractRouter* r) + { + return std::static_pointer_cast( + std::make_shared(ctx->dll, info, r)); + } + +} // namespace llarp::win32 diff --git a/llarp/win32/wintun.hpp b/llarp/win32/wintun.hpp new file mode 100644 index 000000000..9bc5c0f52 --- /dev/null +++ b/llarp/win32/wintun.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include + +namespace llarp +{ + struct AbstractRouter; +} + +namespace llarp::vpn +{ + struct InterfaceInfo; + class NetworkInterface; +} // namespace llarp::vpn + +namespace llarp::win32 +{ + /// holds all wintun implementation, including function pointers we fetch out of the wintun + /// library forward declared to hide wintun from other compilation units + class WintunContext; + + std::shared_ptr + WintunContext_new(); + + /// makes a new vpn interface with a wintun context given info and a router pointer + std::shared_ptr + WintunInterface_new( + const std::shared_ptr&, + const vpn::InterfaceInfo& info, + AbstractRouter* router); + +} // namespace llarp::win32 diff --git a/pybind/CMakeLists.txt b/pybind/CMakeLists.txt index c8444599a..471e92319 100644 --- a/pybind/CMakeLists.txt +++ b/pybind/CMakeLists.txt @@ -16,5 +16,5 @@ pybind11_add_module(pyllarp MODULE llarp/tooling/router_event.cpp llarp/service/address.cpp ) -target_link_libraries(pyllarp PUBLIC liblokinet) +target_link_libraries(pyllarp PUBLIC lokinet-amalgum) target_include_directories(pyllarp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/pybind/llarp/config.cpp b/pybind/llarp/config.cpp index 4ec4c0131..8b686959d 100644 --- a/pybind/llarp/config.cpp +++ b/pybind/llarp/config.cpp @@ -74,7 +74,7 @@ namespace llarp self.OutboundLinks.emplace_back(std::move(_addr)); }) .def("addInboundLink", [](LinksConfig& self, std::string _addr) { - self.InboundLinks.emplace_back(std::move(_addr)); + self.InboundListenAddrs.emplace_back(std::move(_addr)); }); py::class_(mod, "ApiConfig") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cac731775..c11104139 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ if (WITH_HIVE) - add_custom_target(hive_build DEPENDS liblokinet pyllarp) + add_custom_target(hive_build DEPENDS lokinet-amalgum pyllarp) add_custom_target(hive ${CMAKE_COMMAND} -E env PYTHONPATH="$ENV{PYTHONPATH}:${CMAKE_BINARY_DIR}/pybind" ${PYTHON_EXECUTABLE} -m pytest @@ -9,7 +9,6 @@ if (WITH_HIVE) endif() add_subdirectory(Catch2) - add_executable(testAll # helpers check_main.cpp @@ -19,7 +18,7 @@ add_executable(testAll config/test_llarp_config_ini.cpp config/test_llarp_config_output.cpp config/test_llarp_config_values.cpp - crypto/test_llarp_crypto_types.cpp + crypto/test_llarp_crypto_types.cpp crypto/test_llarp_crypto.cpp crypto/test_llarp_key_manager.cpp dns/test_llarp_dns_dns.cpp @@ -46,14 +45,16 @@ add_executable(testAll test_llarp_encrypted_frame.cpp test_llarp_router_contact.cpp) -if(WITH_PEERSTATS) + +if(WITH_PEERSTATS_BACKEND) target_sources(testAll PRIVATE peerstats/test_peer_db.cpp peerstats/test_peer_types.cpp) endif() -target_link_libraries(testAll PUBLIC liblokinet Catch2::Catch2) +target_link_libraries(testAll PUBLIC lokinet-amalgum Catch2::Catch2) target_include_directories(testAll PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + if(WIN32) target_sources(testAll PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/win32/test.rc") target_link_libraries(testAll PUBLIC ws2_32 iphlpapi shlwapi) diff --git a/test/mocks/mock_network.hpp b/test/mocks/mock_network.hpp index 54e92a44a..4653e053b 100644 --- a/test/mocks/mock_network.hpp +++ b/test/mocks/mock_network.hpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace mocks @@ -15,7 +15,7 @@ namespace mocks std::optional _addr; public: - MockUDPHandle(Network* net, llarp::UDPHandle::ReceiveFunc recv) + MockUDPHandle(Network* net, llarp::UDPHandle::ReceiveFunc recv) : llarp::UDPHandle{recv}, _net{net} {} @@ -54,6 +54,12 @@ namespace mocks , _snode{snode} {} + const llarp::net::Platform* + Net_ptr() const override + { + return this; + } + void run() override { @@ -80,7 +86,7 @@ namespace mocks GetBestNetIF(int af) const override { for (const auto& [k, range] : _network_interfaces) - if (range.Family() == af and not range.BogonRange()) + if (range.Family() == af and not IsBogonRange(range)) return k; return std::nullopt; } @@ -116,29 +122,6 @@ namespace mocks return m_Default->AllInterfaces(fallback); } - llarp::SockAddr - Wildcard(int af) const override - { - return m_Default->Wildcard(af); - } - - bool - IsBogon(const llarp::SockAddr& addr) const override - { - return m_Default->IsBogon(addr); - } - bool - IsLoopbackAddress(llarp::net::ipaddr_t ip) const override - { - return m_Default->IsLoopbackAddress(ip); - } - - bool - IsWildcardAddress(llarp::net::ipaddr_t ip) const override - { - return m_Default->IsWildcardAddress(ip); - } - std::optional GetInterfaceIndex(llarp::net::ipaddr_t ip) const override { @@ -188,6 +171,25 @@ namespace mocks return name; throw std::runtime_error{"no loopback interface?"}; } + + std::vector + AllNetworkInterfaces() const override + { + std::map _addrs; + for(const auto & [ifname, range] : _network_interfaces) + { + auto & ent = _addrs[ifname]; + ent.name = ifname; + ent.addrs.emplace_back(range); + } + std::vector infos; + for(const auto & [name, info] : _addrs) + { + infos.emplace_back(info); + infos.back().index = infos.size(); + } + return infos; + } }; bool diff --git a/test/mocks/mock_vpn.hpp b/test/mocks/mock_vpn.hpp index 538102226..6841bdd7d 100644 --- a/test/mocks/mock_vpn.hpp +++ b/test/mocks/mock_vpn.hpp @@ -9,7 +9,7 @@ namespace mocks int _pipes[2]; public: - MockInterface(llarp::vpn::InterfaceInfo) : llarp::vpn::NetworkInterface{} + MockInterface(llarp::vpn::InterfaceInfo info) : llarp::vpn::NetworkInterface{std::move(info)} { if (pipe(_pipes)) throw std::runtime_error{strerror(errno)}; @@ -26,12 +26,6 @@ namespace mocks return _pipes[0]; }; - std::string - IfName() const override - { - return "ligma"; - }; - llarp::net::IPPacket ReadNextPacket() override { @@ -64,13 +58,13 @@ namespace mocks return &_net; }; - void AddRoute(IPVariant_t, IPVariant_t) override{}; + void AddRoute(llarp::net::ipaddr_t, llarp::net::ipaddr_t) override{}; - void DelRoute(IPVariant_t, IPVariant_t) override{}; + void DelRoute(llarp::net::ipaddr_t, llarp::net::ipaddr_t) override{}; - void AddDefaultRouteViaInterface(std::string) override{}; + void AddDefaultRouteViaInterface(llarp::vpn::NetworkInterface&) override{}; - void DelDefaultRouteViaInterface(std::string) override{}; + void DelDefaultRouteViaInterface(llarp::vpn::NetworkInterface&) override{}; void AddRouteViaInterface(llarp::vpn::NetworkInterface&, llarp::IPRange) override{}; @@ -78,9 +72,9 @@ namespace mocks void DelRouteViaInterface(llarp::vpn::NetworkInterface&, llarp::IPRange) override{}; - std::vector GetGatewaysNotOnInterface(std::string) override + std::vector GetGatewaysNotOnInterface(llarp::vpn::NetworkInterface&) override { - return std::vector{}; + return std::vector{}; }; /// get owned ip route manager for managing routing table diff --git a/test/net/test_llarp_net.cpp b/test/net/test_llarp_net.cpp index c383684c1..b65f8ce73 100644 --- a/test/net/test_llarp_net.cpp +++ b/test/net/test_llarp_net.cpp @@ -6,6 +6,16 @@ #include +namespace +{ + template + bool IsBogon(T ip) + { + return llarp::net::Platform::Default_ptr()->IsBogon(ip); + } +} + + TEST_CASE("In6Addr") { llarp::huint128_t ip; @@ -83,30 +93,30 @@ TEST_CASE("Bogon") { SECTION("Bogon_10_8") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(10, 40, 11, 6))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(10, 40, 11, 6))); } SECTION("Bogon_192_168_16") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(192, 168, 1, 111))); } SECTION("Bogon_127_8") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(127, 0, 0, 1))); } SECTION("Bogon_0_8") { - REQUIRE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(0, 0, 0, 0))); + REQUIRE(IsBogon(llarp::ipaddr_ipv4_bits(0, 0, 0, 0))); } SECTION("Non-bogon") { - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(1, 1, 1, 1))); - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(8, 8, 6, 6))); - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(141, 55, 12, 99))); - REQUIRE_FALSE(llarp::IsIPv4Bogon(llarp::ipaddr_ipv4_bits(79, 12, 3, 4))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(1, 1, 1, 1))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(8, 8, 6, 6))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(141, 55, 12, 99))); + REQUIRE_FALSE(IsBogon(llarp::ipaddr_ipv4_bits(79, 12, 3, 4))); } } diff --git a/win32-setup/extra_install.nsis b/win32-setup/extra_install.nsis index ec07fb540..a00e002d3 100644 --- a/win32-setup/extra_install.nsis +++ b/win32-setup/extra_install.nsis @@ -1,7 +1,13 @@ -ifFileExists $INSTDIR\bin\tuntap-install.exe 0 +2 -ExecWait '$INSTDIR\bin\tuntap-install.exe /S' ExecWait '$INSTDIR\bin\lokinet.exe --install' -ExecWait 'sc failure lokinet reset= 60 actions= restart/1000' ExecWait '$INSTDIR\bin\lokinet.exe -g C:\ProgramData\lokinet\lokinet.ini' CopyFiles '$INSTDIR\share\bootstrap.signed' C:\ProgramData\lokinet\bootstrap.signed +ifFileExists $INSTDIR\share\conf.d 0 +3 +CreateDirectory C:\ProgramData\lokinet\conf.d +CopyFiles '$INSTDIR\share\conf.d\*.ini' C:\ProgramData\lokinet\conf.d + +IfFileExists $INSTDIR\bin\WinDivert64.sys +2 0 +CopyFiles '$INSTDIR\lib\WinDivert64.sys' '$INSTDIR\bin\WinDivert64.sys' + +IfFileExists $INSTDIR\bin\WinDivert.sys +2 0 +CopyFiles '$INSTDIR\lib\WinDivert.sys' '$INSTDIR\bin\WinDivert.sys' \ No newline at end of file diff --git a/win32-setup/extra_preinstall.nsis b/win32-setup/extra_preinstall.nsis index de3904f6f..1e6637d3d 100644 --- a/win32-setup/extra_preinstall.nsis +++ b/win32-setup/extra_preinstall.nsis @@ -1,6 +1,6 @@ +ExecWait 'taskkill /f /t /im lokinet-gui.exe' + IfFileExists $INSTDIR\bin\lokinet.exe 0 +3 ExecWait 'net stop lokinet' ExecWait '$INSTDIR\bin\lokinet.exe --remove' -IfFileExists $INSTDIR\share\gui\lokinet.exe 0 +2 -ExecWait 'taskkill /f /t /im lokinet-gui.exe' diff --git a/win32-setup/extra_uninstall.nsis b/win32-setup/extra_uninstall.nsis index ea8664219..5f24cc341 100644 --- a/win32-setup/extra_uninstall.nsis +++ b/win32-setup/extra_uninstall.nsis @@ -1,5 +1,13 @@ -ExecWait 'net stop lokinet' ExecWait 'taskkill /f /t /im lokinet-gui.exe' +ExecWait 'net stop lokinet' +ExecWait 'sc stop windivert' ExecWait '$INSTDIR\bin\lokinet.exe --remove' -RMDir /r /REBOOTOK C:\ProgramData\lokinet +IfFileExists '$INSTDIR\bin\WinDivert64.sys' 0 +2 +Delete /REBOOTOK '$INSTDIR\bin\WinDivert64.sys' + +IfFileExists '$INSTDIR\bin\WinDivert.sys' 0 +2 +Delete /REBOOTOK '$INSTDIR\bin\WinDivert.sys' + +RMDir /r /REBOOTOK C:\ProgramData\lokinet +RMDir /r /REBOOTOK '$INSTDIR\share\conf.d'