mirror of https://github.com/oxen-io/lokinet
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
This commit is contained in:
parent
e981c9f899
commit
871c3e3281
|
@ -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,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -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)
|
||||
if(BUILD_PACKAGE AND NOT APPLE)
|
||||
include(cmake/installer.cmake)
|
||||
endif()
|
||||
|
||||
if(TARGET package)
|
||||
add_dependencies(package assemble_gui)
|
||||
endif()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
if(NOT WIN32)
|
||||
add_custom_target(lokinet-gui
|
||||
COMMAND ${YARN} install --frozen-lockfile &&
|
||||
${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
|
||||
${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
add_custom_target(assemble_gui ALL
|
||||
|
@ -45,7 +50,6 @@ if (BUILD_GUI)
|
|||
-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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
option(WITH_WINDOWS_32 "build 32 bit windows" OFF)
|
||||
|
||||
if(NOT MSVC_VERSION)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-bad-function-cast>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-cast-function-type>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-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()
|
||||
# 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})
|
||||
|
|
|
@ -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 "\\\\" "\\\\\\\\")
|
||||
|
@ -54,6 +55,6 @@ set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}")
|
|||
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")
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[logging]
|
||||
level=debug
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# "suggested" default exit node config
|
||||
#
|
||||
[network]
|
||||
exit-node=exit.loki
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# persist .loki address in a private key file in the data dir
|
||||
#
|
||||
[network]
|
||||
keyfile=lokinet-addr.privkey
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 $<$<COMPILE_LANGUAGE:CXX>:-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 $<TARGET_FILE:${exe}> $<TARGET_FILE:${exe}>.debug
|
||||
COMMAND ${CMAKE_STRIP} ARGS --strip-all $<TARGET_FILE:${exe}>)
|
||||
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 $<TARGET_FILE:lokinet>.debug
|
||||
DEPENDS lokinet)
|
||||
endif()
|
||||
|
|
|
@ -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<fs::path> 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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
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)
|
||||
|
||||
target_link_libraries(lokinet-platform PUBLIC iphlpapi)
|
||||
# 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
|
||||
|
||||
|
@ -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)
|
||||
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()
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp.hpp>
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <memory>
|
||||
|
||||
|
|
|
@ -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<std::string>(
|
||||
"network",
|
||||
|
@ -813,6 +814,30 @@ namespace llarp
|
|||
}
|
||||
});
|
||||
|
||||
conf.defineOption<bool>(
|
||||
"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<std::string>(
|
||||
"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<std::string>(
|
||||
"dns",
|
||||
"bind",
|
||||
|
|
|
@ -155,9 +155,12 @@ namespace llarp
|
|||
|
||||
struct DnsConfig
|
||||
{
|
||||
bool m_raw_dns;
|
||||
std::vector<SockAddr> m_bind;
|
||||
std::vector<SockAddr> m_upstreamDNS;
|
||||
std::vector<fs::path> m_hostfiles;
|
||||
std::optional<SockAddr> m_QueryBind;
|
||||
|
||||
std::unordered_multimap<std::string, std::string> m_ExtraOpts;
|
||||
|
||||
void
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -1,22 +1 @@
|
|||
#pragma once
|
||||
|
||||
#include "platform.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
/// a collection of dns platforms that are tried in order when setting dns
|
||||
class Multi_Platform : public I_Platform
|
||||
{
|
||||
std::vector<std::unique_ptr<I_Platform>> m_Impls;
|
||||
|
||||
public:
|
||||
/// add a platform to be owned
|
||||
void
|
||||
add_impl(std::unique_ptr<I_Platform> 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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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<false, nm::Platform, Null_Platform>;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
|
@ -3,9 +3,12 @@
|
|||
#include <memory>
|
||||
#include <llarp/net/sock_addr.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <net/if.h>
|
||||
#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<std::unique_ptr<I_Platform>> m_Impls;
|
||||
|
||||
public:
|
||||
~Multi_Platform() override = default;
|
||||
/// add a platform to be owned
|
||||
void
|
||||
add_impl(std::unique_ptr<I_Platform> 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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
#include <unbound.h>
|
||||
#include <uvw.hpp>
|
||||
|
||||
#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<Resolver> parent;
|
||||
std::weak_ptr<PacketSource_Base> src;
|
||||
std::shared_ptr<PacketSource_Base> src;
|
||||
SockAddr resolverAddr;
|
||||
SockAddr askerAddr;
|
||||
|
||||
|
@ -91,7 +91,7 @@ namespace llarp::dns
|
|||
explicit Query(
|
||||
std::weak_ptr<Resolver> parent_,
|
||||
Message query,
|
||||
std::weak_ptr<PacketSource_Base> pktsrc,
|
||||
std::shared_ptr<PacketSource_Base> pktsrc,
|
||||
SockAddr toaddr,
|
||||
SockAddr fromaddr)
|
||||
: QueryJob_Base{std::move(query)}
|
||||
|
@ -118,6 +118,8 @@ namespace llarp::dns
|
|||
std::shared_ptr<uvw::PollHandle> m_Poller;
|
||||
#endif
|
||||
|
||||
std::optional<SockAddr> 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<SockAddr>
|
||||
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)
|
||||
{
|
||||
// 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)
|
||||
SockAddr addr{*maybe_addr};
|
||||
std::string host{addr.hostString()};
|
||||
|
||||
if (addr.getPort() == 0)
|
||||
{
|
||||
// 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<sockaddr_in*>(result->ai_addr)->sin_port});
|
||||
freeaddrinfo(result);
|
||||
}
|
||||
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:", "1253");
|
||||
}
|
||||
#endif
|
||||
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<PacketSource_Base> source,
|
||||
std::shared_ptr<PacketSource_Base> 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<std::weak_ptr<Resolver_Base>>
|
||||
Server::GetAllResolvers() const
|
||||
{
|
||||
std::vector<std::weak_ptr<Resolver_Base>> 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<SD_Platform_t>());
|
||||
plat->add_impl(std::make_unique<NM_Platform_t>());
|
||||
}
|
||||
if constexpr (llarp::platform::is_windows)
|
||||
{
|
||||
plat->add_impl(std::make_unique<Win32_Platform_t>());
|
||||
}
|
||||
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<PacketSource_Base> src,
|
||||
std::shared_ptr<PacketSource_Base> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<PacketSource_Base> m_Wrapped;
|
||||
std::function<void(net::IPPacket)> m_WritePacket;
|
||||
|
||||
public:
|
||||
explicit PacketSource_Wrapper(
|
||||
std::weak_ptr<PacketSource_Base> wrapped, std::function<void(net::IPPacket)> 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<SockAddr>
|
||||
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<QueryJob>
|
||||
{
|
||||
std::weak_ptr<PacketSource_Base> src;
|
||||
std::shared_ptr<PacketSource_Base> src;
|
||||
const SockAddr resolver;
|
||||
const SockAddr asker;
|
||||
|
||||
public:
|
||||
explicit QueryJob(
|
||||
std::weak_ptr<PacketSource_Base> source,
|
||||
std::shared_ptr<PacketSource_Base> 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<SockAddr>
|
||||
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<PacketSource_Base> source,
|
||||
std::shared_ptr<PacketSource_Base> 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<SockAddr>
|
||||
|
@ -205,16 +257,18 @@ namespace llarp::dns
|
|||
virtual std::shared_ptr<Resolver_Base>
|
||||
MakeDefaultResolver();
|
||||
|
||||
/// feed a packet buffer from a packet source
|
||||
std::vector<std::weak_ptr<Resolver_Base>>
|
||||
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<PacketSource_Base> pktsource,
|
||||
std::shared_ptr<PacketSource_Base> 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<I_Platform> m_Platform;
|
||||
|
||||
private:
|
||||
const std::string m_NetifName;
|
||||
const unsigned int m_NetIfIndex;
|
||||
std::set<std::shared_ptr<Resolver_Base>, ComparePtr<std::shared_ptr<Resolver_Base>>>
|
||||
m_OwnedResolvers;
|
||||
std::set<std::weak_ptr<Resolver_Base>, CompareWeakPtr<Resolver_Base>> m_Resolvers;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#include "win32_platform.hpp"
|
||||
#include <llarp/net/net.hpp>
|
||||
|
||||
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<llarp::win32::OneShotExec> 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<llarp::win32::OneShotExec> 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
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
#include "platform.hpp"
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
// TODO: implement me
|
||||
using Win32_Platform_t = Null_Platform;
|
||||
} // namespace llarp::dns
|
|
@ -6,8 +6,8 @@
|
|||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
// We libuv now
|
||||
#include "ev_libuv.hpp"
|
||||
#include "libuv.hpp"
|
||||
#include <llarp/net/net.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -16,4 +16,11 @@ namespace llarp
|
|||
{
|
||||
return std::make_shared<llarp::uv::Loop>(queueLength);
|
||||
}
|
||||
|
||||
const net::Platform*
|
||||
EventLoop::Net_ptr() const
|
||||
{
|
||||
return net::Platform::Default_ptr();
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <llarp/util/time.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
#include <llarp/constants/evloop.hpp>
|
||||
|
||||
#include <llarp/net/interface_info.hpp>
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
@ -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<void(UDPHandle&, SockAddr src, llarp::OwnedBuffer buf)>;
|
||||
|
||||
// Constructs a UDP socket that can be used for sending and/or receiving
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "ev_libuv.hpp"
|
||||
#include "vpn.hpp"
|
||||
#include "libuv.hpp"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
|
||||
#include <llarp/util/exceptions.hpp>
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <cstring>
|
||||
#include "ev.hpp"
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
#include <uvw.hpp>
|
||||
|
||||
|
@ -251,19 +251,21 @@ namespace llarp::uv
|
|||
using event_t = uvw::PrepareEvent;
|
||||
auto handle = m_Impl->resource<uvw::PrepareHandle>();
|
||||
#endif
|
||||
|
||||
if (!handle)
|
||||
return false;
|
||||
|
||||
handle->on<event_t>([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();
|
||||
}
|
||||
});
|
|
@ -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<byte_t> 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<byte_t> 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)));
|
||||
}
|
||||
const auto _pktbuf = pkt.ConstBuffer();
|
||||
auto& pktbuf = _pktbuf.underlying;
|
||||
|
||||
const uint8_t queue_idx = pktbuf.sz / llarp::routing::ExitPadSize;
|
||||
buf = pkt.steal();
|
||||
}
|
||||
|
||||
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<net::IPPacket&>(m_UpstreamQueue.top().pkt).steal());
|
||||
m_UpstreamQueue.pop();
|
||||
}
|
||||
// flush downstream queue
|
||||
|
|
|
@ -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<byte_t> 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<byte_t> 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;
|
||||
|
|
|
@ -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<net::IPPacket&>(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);
|
||||
}
|
||||
|
|
|
@ -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<dns::PacketSource_Base> source,
|
||||
std::shared_ptr<dns::PacketSource_Base> 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<PubKey> 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<net::IPPacket&>(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<llarp_time_t>::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<dns::Server>(
|
||||
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<dns::Server>(m_Router->loop(), dnsConfig, m_ifname);
|
||||
}
|
||||
|
||||
huint128_t
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace llarp
|
|||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::weak_ptr<dns::PacketSource_Base> source,
|
||||
std::shared_ptr<dns::PacketSource_Base> 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
|
||||
|
|
|
@ -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);
|
||||
}}}
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
#include <llarp/rpc/endpoint_rpc.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/dns/srv_data.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#include <llarp/constants/net.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#include <oxenc/bt.h>
|
||||
|
@ -37,7 +36,7 @@ namespace llarp
|
|||
{
|
||||
bool
|
||||
TunEndpoint::MaybeHookDNS(
|
||||
std::weak_ptr<dns::PacketSource_Base> source,
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from)
|
||||
|
@ -46,21 +45,25 @@ namespace llarp
|
|||
return false;
|
||||
|
||||
auto job = std::make_shared<dns::QueryJob>(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<void(net::IPPacket)> 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<void(net::IPPacket)> 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);
|
||||
auto pkt = net::IPPacket::make_udp(from, to, std::move(buf));
|
||||
|
||||
if (pkt.sz == 0)
|
||||
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
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#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,
|
||||
// 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
|
||||
// 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)
|
||||
{
|
||||
return to.asIPv6() != m_Endpoint->GetIfAddr();
|
||||
const auto& addr = *maybe_addr;
|
||||
// omit traffic to and from our dns socket
|
||||
return addr == to or addr == from;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(ANDROID) || defined(__APPLE__)
|
||||
class TunDNS : public dns::Server
|
||||
{
|
||||
std::optional<SockAddr> m_QueryBind;
|
||||
net::ipaddr_t m_OurIP;
|
||||
TunEndpoint* const m_Endpoint;
|
||||
|
||||
public:
|
||||
std::weak_ptr<dns::PacketSource_Base> PacketSource;
|
||||
std::shared_ptr<dns::PacketSource_Base> 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<dns::PacketSource_Base>
|
||||
MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override
|
||||
{
|
||||
auto ptr = std::make_shared<DnsInterceptor>(m_Endpoint, conf);
|
||||
auto ptr = std::make_shared<DnsInterceptor>(
|
||||
[ep = m_Endpoint](auto pkt) {
|
||||
ep->HandleWriteIPPacket(pkt.ConstBuffer(), pkt.srcv6(), pkt.dstv6(), 0);
|
||||
},
|
||||
m_OurIP,
|
||||
conf);
|
||||
PacketSource = ptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<dns::Resolver_Base>
|
||||
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<vpn::PacketRouter>(
|
||||
m_PacketRouter = std::make_shared<vpn::PacketRouter>(
|
||||
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
|
||||
}
|
||||
|
||||
void
|
||||
TunEndpoint::SetupDNS()
|
||||
{
|
||||
#if defined(ANDROID) || defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION))
|
||||
const auto& info = GetVPNInterface()->Info();
|
||||
if (m_DnsConfig.m_raw_dns)
|
||||
{
|
||||
auto dns = std::make_shared<TunDNS>(this, m_DnsConfig);
|
||||
if (auto vpn = Router()->GetVPNPlatform())
|
||||
{
|
||||
// get the first local address we know of
|
||||
std::optional<SockAddr> 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<void(net::IPPacket)> 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;
|
||||
|
||||
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<const uint16_t*>(ptr)}};
|
||||
const SockAddr raddr{dst, nuint16_t{*reinterpret_cast<const uint16_t*>(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)))
|
||||
auto dns_pkt_src = dns->PacketSource;
|
||||
if (const auto& reply = pkt.reply)
|
||||
dns_pkt_src = std::make_shared<dns::PacketSource_Wrapper>(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<dns::Server>(Loop(), m_DnsConfig, GetIfName());
|
||||
#endif
|
||||
}
|
||||
else
|
||||
m_DNS = std::make_shared<dns::Server>(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<service::Address> 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<void(void)> 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;
|
||||
|
|
|
@ -2,21 +2,19 @@
|
|||
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/net/ip.hpp>
|
||||
#include <llarp/net/ip_packet.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/service/endpoint.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
#include <llarp/vpn/packet_router.hpp>
|
||||
|
||||
#include <future>
|
||||
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <llarp/service/protocol_type.hpp>
|
||||
#include <llarp/util/priority_queue.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
#include <llarp/vpn/packet_router.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -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<dns::PacketSource_Base> source,
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override;
|
||||
|
@ -306,7 +310,7 @@ namespace llarp
|
|||
|
||||
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
|
||||
|
||||
std::unique_ptr<vpn::PacketRouter> m_PacketRouter;
|
||||
std::shared_ptr<vpn::PacketRouter> m_PacketRouter;
|
||||
|
||||
std::optional<net::TrafficPolicy> 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<fs::path> m_PersistAddrMapFile;
|
||||
|
||||
/// for raw packet dns
|
||||
std::shared_ptr<vpn::I_Packet_IO> m_RawDNS;
|
||||
};
|
||||
|
||||
} // namespace handlers
|
||||
|
|
|
@ -954,7 +954,7 @@ extern "C"
|
|||
dstport,
|
||||
llarp_buffer_t{reinterpret_cast<const uint8_t*>(ptr), len});
|
||||
|
||||
if (pkt.sz == 0)
|
||||
if (pkt.empty())
|
||||
return EINVAL;
|
||||
std::promise<int> ret;
|
||||
ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() {
|
||||
|
|
|
@ -50,9 +50,19 @@ namespace llarp
|
|||
fromSockAddr(const SockAddr& address);
|
||||
|
||||
/// get this as an explicit v4 or explicit v6
|
||||
std::variant<nuint32_t, nuint128_t>
|
||||
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<net::ipv4addr_t>(&ip))
|
||||
return *ptr;
|
||||
throw std::runtime_error{"no ipv4 address found in address info"};
|
||||
}
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<IPRange> addrs;
|
||||
/// a gateway we can use if it exists
|
||||
std::optional<ipaddr_t> gateway;
|
||||
};
|
||||
} // namespace llarp::net
|
|
@ -127,7 +127,7 @@ namespace llarp
|
|||
SockAddr addr(m_ipAddress);
|
||||
const auto* addr6 = static_cast<const sockaddr_in6*>(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
|
||||
|
|
|
@ -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<uint16_t>
|
||||
getPort() const;
|
||||
std::optional<uint16_t> 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<uint16_t> port);
|
||||
void setPort(std::optional<uint16_t> 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<uint16_t> port);
|
||||
void setAddress(std::string_view str);
|
||||
void setAddress(std::string_view str, std::optional<uint16_t> 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;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "ip_packet.hpp"
|
||||
#include "ip.hpp"
|
||||
|
||||
#include <llarp/constants/net.hpp>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/mem.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
|
@ -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 (pkt.sz > sizeof(buf) or pkt.sz == 0)
|
||||
return false;
|
||||
sz = pkt.sz;
|
||||
std::copy_n(pkt.base, sz, buf);
|
||||
return true;
|
||||
if (view.size() < MinSize)
|
||||
{
|
||||
_buf.resize(0);
|
||||
return;
|
||||
}
|
||||
_buf.resize(view.size());
|
||||
std::copy_n(view.data(), size(), data());
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
IPPacket::ConstBuffer() const
|
||||
IPPacket::IPPacket(size_t sz)
|
||||
{
|
||||
const byte_t* ptr = buf;
|
||||
llarp_buffer_t b(ptr, sz);
|
||||
return ManagedBuffer(b);
|
||||
if (sz and sz < MinSize)
|
||||
throw std::invalid_argument{"buffer size is too small to hold an ip packet"};
|
||||
_buf.resize(sz);
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
IPPacket::Buffer()
|
||||
SockAddr
|
||||
IPPacket::src() const
|
||||
{
|
||||
byte_t* ptr = buf;
|
||||
llarp_buffer_t b(ptr, sz);
|
||||
return ManagedBuffer(b);
|
||||
auto port = SrcPort();
|
||||
if (IsV4())
|
||||
return SockAddr{ToNet(srcv4()), *port};
|
||||
else
|
||||
return SockAddr{ToNet(srcv6()), *port};
|
||||
}
|
||||
|
||||
SockAddr
|
||||
IPPacket::dst() const
|
||||
{
|
||||
auto port = DstPort();
|
||||
if (IsV4())
|
||||
return SockAddr{ToNet(dstv4()), *port};
|
||||
else
|
||||
return SockAddr{ToNet(dstv6()), *port};
|
||||
}
|
||||
|
||||
IPPacket::IPPacket(std::vector<byte_t>&& stolen) : _buf{stolen}
|
||||
{
|
||||
if (size() < MinSize)
|
||||
_buf.resize(0);
|
||||
}
|
||||
|
||||
byte_view_t
|
||||
IPPacket::view() const
|
||||
{
|
||||
return byte_view_t{data(), size()};
|
||||
}
|
||||
|
||||
std::optional<nuint16_t>
|
||||
|
@ -139,7 +162,7 @@ namespace llarp::net
|
|||
{
|
||||
case IPProtocol::TCP:
|
||||
case IPProtocol::UDP:
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(buf + (Header()->ihl * 4) + 2)};
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(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<const uint16_t*>(buf + (Header()->ihl * 4))};
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(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<nuint32_t> 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,28 +578,24 @@ 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<size_t>(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'
|
||||
|
@ -588,14 +608,13 @@ namespace llarp::net
|
|||
oxenc::write_host_as_big<uint16_t>(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,33 +633,31 @@ 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<const char*>(ptr), std::distance(ptr, buf + sz));
|
||||
const uint8_t* ptr = data() + ((hdr->ihl * 4) + l4_HeaderSize);
|
||||
return std::make_pair(reinterpret_cast<const char*>(ptr), std::distance(ptr, data() + size()));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
IPPacket
|
||||
IPPacket::UDP(
|
||||
nuint32_t srcaddr,
|
||||
nuint16_t srcport,
|
||||
nuint32_t dstaddr,
|
||||
nuint16_t dstport,
|
||||
const llarp_buffer_t& buf)
|
||||
make_ip4_udp(
|
||||
net::ipv4addr_t srcaddr,
|
||||
net::port_t srcport,
|
||||
net::ipv4addr_t dstaddr,
|
||||
net::port_t dstport,
|
||||
std::vector<byte_t> udp_data)
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
constexpr auto pkt_overhead = constants::udp_header_bytes + constants::ip_header_min_bytes;
|
||||
net::IPPacket pkt{udp_data.size() + pkt_overhead};
|
||||
|
||||
if (buf.sz + 28 > sizeof(pkt.buf))
|
||||
{
|
||||
pkt.sz = 0;
|
||||
return pkt;
|
||||
}
|
||||
auto* hdr = pkt.Header();
|
||||
pkt.buf[1] = 0;
|
||||
pkt.data()[1] = 0;
|
||||
hdr->version = 4;
|
||||
hdr->ihl = 5;
|
||||
hdr->tot_len = htons(buf.sz + 28);
|
||||
hdr->tot_len = htons(pkt_overhead + udp_data.size());
|
||||
hdr->protocol = 0x11; // udp
|
||||
hdr->ttl = 64;
|
||||
hdr->frag_off = htons(0b0100000000000000);
|
||||
|
@ -649,21 +666,52 @@ namespace llarp::net
|
|||
hdr->daddr = dstaddr.n;
|
||||
|
||||
// make udp packet
|
||||
uint8_t* ptr = pkt.buf + 20;
|
||||
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<uint16_t>(buf.sz + 8), ptr);
|
||||
oxenc::write_host_as_big(
|
||||
static_cast<uint16_t>(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(buf.base, buf.sz, ptr);
|
||||
std::copy_n(udp_data.data(), udp_data.size(), ptr);
|
||||
|
||||
hdr->check = 0;
|
||||
hdr->check = net::ipchksum(pkt.buf, 20);
|
||||
pkt.sz = 28 + buf.sz;
|
||||
hdr->check = net::ipchksum(pkt.data(), 20);
|
||||
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<byte_t> udp_data)
|
||||
{
|
||||
auto getfam = [](auto&& v) {
|
||||
if (std::holds_alternative<net::ipv4addr_t>(v))
|
||||
return AF_INET;
|
||||
else if (std::holds_alternative<net::ipv6addr_t>(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<net::ipv4addr_t>(&srcaddr),
|
||||
srcport,
|
||||
*std::get_if<net::ipv4addr_t>(&dstaddr),
|
||||
dstport,
|
||||
std::move(udp_data));
|
||||
}
|
||||
// TODO: ipv6
|
||||
return net::IPPacket{size_t{}};
|
||||
}
|
||||
} // namespace llarp::net
|
||||
|
|
|
@ -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<byte_t> _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<byte_t>&&);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
Buffer();
|
||||
static IPPacket
|
||||
make_udp(
|
||||
net::ipaddr_t srcaddr,
|
||||
net::port_t srcport,
|
||||
net::ipaddr_t dstaddr,
|
||||
net::port_t dstport,
|
||||
std::vector<byte_t> udp_body);
|
||||
|
||||
ManagedBuffer
|
||||
ConstBuffer() const;
|
||||
static inline IPPacket
|
||||
make_udp(SockAddr src, SockAddr dst, std::variant<OwnedBuffer, std::vector<byte_t>> udp_body)
|
||||
{
|
||||
if (auto* vec = std::get_if<std::vector<byte_t>>(&udp_body))
|
||||
return make_udp(src.getIP(), src.port(), dst.getIP(), dst.port(), std::move(*vec));
|
||||
else if (auto* buf = std::get_if<OwnedBuffer>(&udp_body))
|
||||
return make_udp(src, dst, buf->copy());
|
||||
else
|
||||
return net::IPPacket{size_t{}};
|
||||
}
|
||||
|
||||
bool
|
||||
Load(const llarp_buffer_t& buf);
|
||||
[[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;
|
||||
}
|
||||
|
||||
[[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t
|
||||
ConstBuffer() const
|
||||
{
|
||||
return llarp_buffer_t{_buf};
|
||||
}
|
||||
|
||||
/// steal the underlying vector
|
||||
inline std::vector<byte_t>
|
||||
steal()
|
||||
{
|
||||
std::vector<byte_t> buf;
|
||||
buf.resize(0);
|
||||
std::swap(_buf, buf);
|
||||
return 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<ip_header*>(&buf[0]);
|
||||
return reinterpret_cast<ip_header*>(data());
|
||||
}
|
||||
|
||||
inline const ip_header*
|
||||
Header() const
|
||||
{
|
||||
return reinterpret_cast<const ip_header*>(&buf[0]);
|
||||
return reinterpret_cast<const ip_header*>(data());
|
||||
}
|
||||
|
||||
inline ipv6_header*
|
||||
HeaderV6()
|
||||
{
|
||||
return reinterpret_cast<ipv6_header*>(&buf[0]);
|
||||
return reinterpret_cast<ipv6_header*>(data());
|
||||
}
|
||||
|
||||
inline const ipv6_header*
|
||||
HeaderV6() const
|
||||
{
|
||||
return reinterpret_cast<const ipv6_header*>(&buf[0]);
|
||||
return reinterpret_cast<const ipv6_header*>(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<nuint16_t>
|
||||
DstPort() const;
|
||||
|
@ -238,6 +338,14 @@ namespace llarp::net
|
|||
std::optional<std::pair<const char*, size_t>>
|
||||
L4Data() const;
|
||||
|
||||
inline std::optional<OwnedBuffer>
|
||||
L4OwnedBuffer() const
|
||||
{
|
||||
if (auto data = L4Data())
|
||||
return OwnedBuffer{reinterpret_cast<const byte_t*>(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<IPPacket>
|
||||
MakeICMPUnreachable() const;
|
||||
|
||||
std::function<void(net::IPPacket)> reply;
|
||||
};
|
||||
|
||||
/// generate ip checksum
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <typename Addr>
|
||||
bool
|
||||
BogonContains(Addr&& addr) const
|
||||
{
|
||||
return BogonRange() and Contains(std::forward<Addr>(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);
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include <llarp/util/mem.hpp>
|
||||
#include <llarp/util/bits.hpp>
|
||||
|
||||
#include "interface_info.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <cstdlib> // for itoa
|
||||
#include <vector>
|
||||
|
@ -22,50 +24,73 @@
|
|||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <wspiapi.h>
|
||||
#define inet_aton(x, y) inet_pton(AF_INET, x, y)
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
bool
|
||||
operator==(const sockaddr& a, const sockaddr& b);
|
||||
|
||||
bool
|
||||
operator==(const sockaddr_in& a, const sockaddr_in& b);
|
||||
|
||||
bool
|
||||
operator==(const sockaddr_in6& a, const sockaddr_in6& b);
|
||||
|
||||
bool
|
||||
operator<(const sockaddr_in6& a, const sockaddr_in6& b);
|
||||
|
||||
bool
|
||||
operator<(const in6_addr& a, const in6_addr& b);
|
||||
|
||||
bool
|
||||
operator==(const in6_addr& a, const in6_addr& b);
|
||||
#include "bogon_ranges.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
bool
|
||||
IsIPv4Bogon(const huint32_t& addr);
|
||||
|
||||
inline bool
|
||||
IsIPv4Bogon(const nuint32_t& addr)
|
||||
operator==(const in_addr& a, const in_addr& b)
|
||||
{
|
||||
return IsIPv4Bogon(ToHost(addr));
|
||||
return memcmp(&a, &b, sizeof(in_addr)) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
IsBogon(const in6_addr& addr);
|
||||
inline bool
|
||||
operator==(const in6_addr& a, const in6_addr& b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(in6_addr)) == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
IsBogon(const huint128_t addr);
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
IsBogonRange(const in6_addr& host, const in6_addr& mask);
|
||||
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
|
||||
operator==(const sockaddr& a, const sockaddr& b)
|
||||
{
|
||||
if (a.sa_family != b.sa_family)
|
||||
return false;
|
||||
switch (a.sa_family)
|
||||
{
|
||||
case AF_INET:
|
||||
return reinterpret_cast<const sockaddr_in&>(a) == reinterpret_cast<const sockaddr_in&>(b);
|
||||
case AF_INET6:
|
||||
return reinterpret_cast<const sockaddr_in6&>(a) == reinterpret_cast<const sockaddr_in6&>(b);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator<(const in_addr& a, const in_addr& b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(in_addr)) < 0;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator<(const in6_addr& a, const in6_addr& b)
|
||||
{
|
||||
return memcmp(&a, &b, sizeof(in6_addr)) < 0;
|
||||
}
|
||||
|
||||
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<SockAddr>
|
||||
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<std::string>
|
||||
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<net::ipv4addr_t, std::decay_t<decltype(ip)>>)
|
||||
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<int>
|
||||
GetInterfaceIndex(ipaddr_t ip) const = 0;
|
||||
|
||||
/// returns a vector holding all of our network interfaces
|
||||
virtual std::vector<InterfaceInfo>
|
||||
AllNetworkInterfaces() const = 0;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -40,14 +40,6 @@ namespace llarp
|
|||
// IPv4 mapped address live at ::ffff:0:0/96
|
||||
constexpr std::array<uint8_t, 12> 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
|
||||
|
|
|
@ -1,19 +1,9 @@
|
|||
#include "net.hpp"
|
||||
|
||||
#include "net_if.hpp"
|
||||
#include <stdexcept>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <llarp/android/ifaddrs.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#ifndef ANDROID
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "ip.hpp"
|
||||
#include "ip_range.hpp"
|
||||
|
@ -23,116 +13,15 @@
|
|||
#ifdef ANDROID
|
||||
#include <llarp/android/ifaddrs.h>
|
||||
#else
|
||||
#ifdef _WIN32
|
||||
#include <iphlpapi.h>
|
||||
#include <llarp/win32/exception.hpp>
|
||||
#else
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
|
||||
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<InterfaceInfo>
|
||||
AllNetworkInterfaces() const override
|
||||
{
|
||||
std::vector<InterfaceInfo> 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 <typename Visit_t>
|
||||
void
|
||||
|
@ -469,6 +377,33 @@ namespace llarp::net
|
|||
});
|
||||
return found;
|
||||
}
|
||||
std::vector<InterfaceInfo>
|
||||
AllNetworkInterfaces() const override
|
||||
{
|
||||
std::unordered_map<std::string, InterfaceInfo> 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<InterfaceInfo> 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
|
|
@ -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 <llarp/util/str.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/util/mem.hpp>
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
#include "net.hpp"
|
||||
|
||||
#include "net_if.hpp"
|
||||
#include <stdexcept>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#include "ip.hpp"
|
||||
#include "ip_range.hpp"
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
#include <iphlpapi.h>
|
||||
#include <llarp/win32/exception.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
|
||||
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 <typename Visit_t>
|
||||
void
|
||||
iter_adapters(Visit_t&& visit, int af = AF_UNSPEC) const
|
||||
{
|
||||
ULONG sz{};
|
||||
GetAdaptersAddresses(af, 0, nullptr, nullptr, &sz);
|
||||
auto ptr = std::make_unique<byte_t[]>(sz);
|
||||
auto* addrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(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 <typename adapter_t>
|
||||
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 <typename adapter_t>
|
||||
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<int>
|
||||
GetInterfaceIndex(ipaddr_t ip) const override
|
||||
{
|
||||
std::optional<int> found;
|
||||
int af{AF_INET};
|
||||
if (std::holds_alternative<ipv6addr_t>(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<llarp::SockAddr>
|
||||
GetInterfaceAddr(std::string_view name, int af) const override
|
||||
{
|
||||
std::optional<SockAddr> 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<SockAddr>
|
||||
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<std::string>
|
||||
FindFreeTun() const override
|
||||
{
|
||||
return "lokitun0";
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
GetBestNetIF(int) const override
|
||||
{
|
||||
// TODO: implement me ?
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<IPRange>
|
||||
FindFreeRange() const override
|
||||
{
|
||||
std::list<IPRange> 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<IPRange> 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<InterfaceInfo>
|
||||
AllNetworkInterfaces() const override
|
||||
{
|
||||
std::vector<InterfaceInfo> 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
|
|
@ -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<uint64_t>(pkt.data());
|
||||
if (m_ExitTrafficHandler(
|
||||
self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol))
|
||||
|
|
|
@ -397,11 +397,9 @@ namespace llarp
|
|||
if (pkt.size() <= 8)
|
||||
continue;
|
||||
auto counter = oxenc::load_big_to_host<uint64_t>(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;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <oxenc/variant.h>
|
||||
#include <llarp/service/address.hpp>
|
||||
#include <llarp/service/endpoint.hpp>
|
||||
#include <llarp/ev/ev_libuv.hpp>
|
||||
#include <llarp/ev/libuv.hpp>
|
||||
|
||||
#include <variant>
|
||||
|
||||
|
|
|
@ -333,13 +333,11 @@ namespace llarp::quic
|
|||
extern "C" inline void
|
||||
ngtcp_trace_logger([[maybe_unused]] void* user_data, const char* fmt, ...)
|
||||
{
|
||||
std::array<char, 2048> 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
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <llarp/crypto/crypto.hpp>
|
||||
#include <llarp/util/logging/buffer.hpp>
|
||||
#include <llarp/service/endpoint.hpp>
|
||||
#include <llarp/ev/ev_libuv.hpp>
|
||||
#include <llarp/ev/libuv.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/util/logging/buffer.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/ev/ev_libuv.hpp>
|
||||
#include <llarp/ev/libuv.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
|
|
@ -181,8 +181,8 @@ namespace llarp
|
|||
virtual ILinkManager&
|
||||
linkManager() = 0;
|
||||
|
||||
virtual RoutePoker&
|
||||
routePoker() = 0;
|
||||
virtual const std::shared_ptr<RoutePoker>&
|
||||
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;
|
||||
|
|
|
@ -7,83 +7,93 @@
|
|||
|
||||
namespace llarp
|
||||
{
|
||||
void
|
||||
RoutePoker::AddRoute(huint32_t ip)
|
||||
namespace
|
||||
{
|
||||
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)
|
||||
{
|
||||
llarp::LogInfo(
|
||||
"RoutePoker::AddRoute enabled, enabling route to ", ip, " via ", m_CurrentGateway);
|
||||
EnableRoute(ip, m_CurrentGateway);
|
||||
}
|
||||
else
|
||||
{
|
||||
llarp::LogDebug("RoutePoker::AddRoute disabled, not enabling route.");
|
||||
}
|
||||
auto logcat = log::Cat("route-poker");
|
||||
}
|
||||
|
||||
void
|
||||
RoutePoker::DisableRoute(huint32_t ip, huint32_t gateway)
|
||||
RoutePoker::AddRoute(net::ipv4addr_t ip)
|
||||
{
|
||||
bool has_existing = m_PokedRoutes.count(ip);
|
||||
// set up route and apply as needed
|
||||
auto& gw = m_PokedRoutes[ip];
|
||||
if (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
|
||||
gw = net::ipv4addr_t{};
|
||||
}
|
||||
|
||||
void
|
||||
RoutePoker::DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t 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)
|
||||
{
|
||||
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);
|
||||
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,117 +101,108 @@ 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<huint32_t>
|
||||
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<huint32_t>(&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;
|
||||
}
|
||||
const huint32_t gateway = *maybe;
|
||||
|
||||
const bool gatewayChanged = m_CurrentGateway.h != 0 and m_CurrentGateway != gateway;
|
||||
auto& route = platform->RouteManager();
|
||||
|
||||
if (m_CurrentGateway != gateway)
|
||||
// find current gateways
|
||||
auto gateways = route.GetGatewaysNotOnInterface(*vpn);
|
||||
std::optional<net::ipv4addr_t> next_gw;
|
||||
for (auto& gateway : gateways)
|
||||
{
|
||||
LogInfo("found default gateway: ", gateway);
|
||||
m_CurrentGateway = gateway;
|
||||
if (m_Enabling)
|
||||
{
|
||||
EnableAllRoutes();
|
||||
Up();
|
||||
if (auto* gw_ptr = std::get_if<net::ipv4addr_t>(&gateway))
|
||||
next_gw = *gw_ptr;
|
||||
}
|
||||
}
|
||||
// revive network connectitivity on gateway change or network wakeup
|
||||
if (gatewayChanged or not m_HasNetwork)
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// update current gateway and apply state chnages as needed
|
||||
if (not is_equal(m_CurrentGateway, next_gw))
|
||||
{
|
||||
LogInfo("our network changed, thawing router state");
|
||||
if (next_gw and m_CurrentGateway)
|
||||
{
|
||||
log::info(logcat, "default gateway changed from {} to {}", *m_CurrentGateway, *next_gw);
|
||||
m_CurrentGateway = next_gw;
|
||||
m_Router->Thaw();
|
||||
m_HasNetwork = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (m_Enabled)
|
||||
return;
|
||||
|
||||
if (m_Router->GetConfig()->network.m_EnableRoutePoker)
|
||||
{
|
||||
m_Enabling = true;
|
||||
Update();
|
||||
m_Enabling = false;
|
||||
m_Enabled = true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (IsEnabled())
|
||||
{
|
||||
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
|
||||
|
||||
// black hole all routes if enabled
|
||||
|
@ -210,30 +211,35 @@ namespace llarp
|
|||
|
||||
// explicit route pokes for first hops
|
||||
m_Router->ForEachPeer(
|
||||
[&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().asIPv4()); },
|
||||
false);
|
||||
[this](auto session, auto) { AddRoute(session->GetRemoteEndpoint().getIPv4()); }, false);
|
||||
// add default route
|
||||
const auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
route.AddDefaultRouteViaInterface(ep->GetIfName());
|
||||
if (auto* vpn = ep->GetVPNInterface())
|
||||
route.AddDefaultRouteViaInterface(*vpn);
|
||||
}
|
||||
SetDNSMode(true);
|
||||
}
|
||||
|
||||
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);
|
||||
// remove default route
|
||||
const auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
|
||||
[this](auto session, auto) { DelRoute(session->GetRemoteEndpoint().getIPv4()); }, false);
|
||||
|
||||
// remove default route
|
||||
|
||||
if (IsEnabled())
|
||||
{
|
||||
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
|
||||
const auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
if (auto* vpn = ep->GetVPNInterface())
|
||||
route.DelDefaultRouteViaInterface(*vpn);
|
||||
|
||||
route.DelDefaultRouteViaInterface(ep->GetIfName());
|
||||
// delete route blackhole
|
||||
route.DelBlackhole();
|
||||
}
|
||||
SetDNSMode(false);
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -10,32 +10,19 @@ namespace llarp
|
|||
{
|
||||
struct AbstractRouter;
|
||||
|
||||
struct RoutePoker
|
||||
struct RoutePoker : public std::enable_shared_from_this<RoutePoker>
|
||||
{
|
||||
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);
|
||||
DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway);
|
||||
|
||||
std::optional<huint32_t>
|
||||
GetDefaultGateway() const;
|
||||
std::unordered_map<net::ipv4addr_t, net::ipv4addr_t> m_PokedRoutes;
|
||||
|
||||
std::unordered_map<huint32_t, huint32_t> m_PokedRoutes;
|
||||
huint32_t m_CurrentGateway;
|
||||
|
||||
bool m_Enabled = false;
|
||||
bool m_Enabling = false;
|
||||
std::optional<net::ipv4addr_t> m_CurrentGateway;
|
||||
|
||||
AbstractRouter* m_Router = nullptr;
|
||||
|
||||
bool m_HasNetwork = true;
|
||||
};
|
||||
} // namespace llarp
|
||||
|
|
|
@ -48,22 +48,21 @@ namespace llarp
|
|||
static auto logcat = log::Cat("router");
|
||||
|
||||
Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform)
|
||||
: ready(false)
|
||||
, m_lmq(std::make_shared<oxenmq::OxenMQ>())
|
||||
, _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<oxenmq::OxenMQ>()}
|
||||
, _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<RoutePoker>()}
|
||||
, 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<KeyManager>();
|
||||
// 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<RouterID> 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),
|
||||
|
|
|
@ -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>&
|
||||
routePoker() const override
|
||||
{
|
||||
return m_RoutePoker;
|
||||
}
|
||||
|
||||
RoutePoker m_RoutePoker;
|
||||
std::shared_ptr<RoutePoker> m_RoutePoker;
|
||||
|
||||
void
|
||||
TriggerPump() override;
|
||||
|
@ -401,6 +387,9 @@ namespace llarp
|
|||
bool
|
||||
StartRpcServer() override;
|
||||
|
||||
void
|
||||
Freeze() override;
|
||||
|
||||
void
|
||||
Thaw() override;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
|
||||
#include <llarp/quic/server.hpp>
|
||||
#include <llarp/quic/tunnel.hpp>
|
||||
#include <llarp/ev/ev_libuv.hpp>
|
||||
#include <uvw.hpp>
|
||||
#include <variant>
|
||||
|
||||
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -2,31 +2,23 @@
|
|||
|
||||
#include "protocol.hpp"
|
||||
#include <llarp/util/buffer.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::service
|
||||
{
|
||||
namespace service
|
||||
{
|
||||
struct PendingBuffer
|
||||
{
|
||||
std::vector<byte_t> 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));
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
inline llarp_buffer_t
|
||||
Buffer()
|
||||
{
|
||||
return ManagedBuffer{llarp_buffer_t(payload)};
|
||||
return llarp_buffer_t{payload};
|
||||
}
|
||||
};
|
||||
} // namespace service
|
||||
|
||||
} // namespace llarp
|
||||
PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload{buf.copy()}, protocol{t}
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace llarp::service
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "endpoint.hpp"
|
||||
#include <utility>
|
||||
#include <unordered_set>
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
|
|
@ -132,6 +132,14 @@ operator==(const llarp_buffer_t& buff, std::string_view data)
|
|||
{
|
||||
return std::string_view{reinterpret_cast<const char*>(buff.cur), buff.size_left()} == data;
|
||||
}
|
||||
|
||||
llarp::byte_view_t
|
||||
llarp_buffer_t::view() const
|
||||
{
|
||||
return {base, sz};
|
||||
|
||||
}
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
std::vector<byte_t>
|
||||
|
|
|
@ -15,59 +15,17 @@
|
|||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
namespace llarp
|
||||
{
|
||||
using byte_view_t = std::basic_string_view<byte_t>;
|
||||
}
|
||||
|
||||
/**
|
||||
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;
|
||||
|
||||
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<sizeof(T) == 1 and std::is_trivially_copyable_v<T>>>
|
||||
llarp_buffer_t(T* buf, size_t _sz)
|
||||
llarp_buffer_t(T * buf, size_t _sz)
|
||||
: base(reinterpret_cast<byte_t*>(const_cast<std::remove_const_t<T>*>(buf)))
|
||||
, cur(base)
|
||||
, sz(_sz)
|
||||
|
@ -105,82 +59,73 @@ struct llarp_buffer_t
|
|||
template <
|
||||
typename T,
|
||||
typename = std::void_t<decltype(std::declval<T>().data() + std::declval<T>().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 <typename OutputIt>
|
||||
bool
|
||||
read_into(OutputIt begin, OutputIt end);
|
||||
bool read_into(OutputIt begin, OutputIt end);
|
||||
|
||||
template <typename InputIt>
|
||||
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<byte_t>
|
||||
copy() const;
|
||||
std::vector<byte_t> 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<const char*>(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<byte_t>;
|
||||
|
||||
// Wrapper around a std::unique_ptr<byte_t[]> that owns its own memory and is also implicitly
|
||||
// convertible to a llarp_buffer_t.
|
||||
struct OwnedBuffer
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
#include "lokinet_init.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#include "lokinet_init.h"
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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
|
|
@ -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
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <stringapiset.h>
|
||||
#include <llarp/win32/exception.hpp>
|
||||
#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
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <charconv>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
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
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include "platform.hpp"
|
||||
#include "common.hpp"
|
||||
#include <llarp.hpp>
|
||||
|
||||
|
@ -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<byte_t> 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<ssize_t>(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<ssize_t>(pkt.sz);
|
||||
}
|
||||
|
||||
std::string
|
||||
IfName() const override
|
||||
{
|
||||
return m_Info.ifname;
|
||||
return sz == static_cast<ssize_t>(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<IPVariant_t> GetGatewaysNotOnInterface(std::string) override
|
||||
std::vector<net::ipaddr_t>
|
||||
GetGatewaysNotOnInterface(NetworkInterface&) override
|
||||
{
|
||||
return std::vector<IPVariant_t>{};
|
||||
return std::vector<net::ipaddr_t>{};
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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<NetworkInterface>
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <llarp/net/ip_packet.hpp>
|
||||
#include <llarp/util/types.hpp>
|
||||
|
||||
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
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include "platform.hpp"
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -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<byte_t> 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<ssize_t>(pkt.sz);
|
||||
}
|
||||
|
||||
std::string
|
||||
IfName() const override
|
||||
{
|
||||
return m_Info.ifname;
|
||||
return sz == static_cast<ssize_t>(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<IPVariant_t>
|
||||
GetGatewaysNotOnInterface(std::string ifname) override
|
||||
std::vector<net::ipaddr_t>
|
||||
GetGatewaysNotOnInterface(NetworkInterface& vpn) override
|
||||
{
|
||||
std::vector<IPVariant_t> gateways{};
|
||||
const auto& ifname = vpn.Info().ifname;
|
||||
std::vector<net::ipaddr_t> 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<char*>(&x.h));
|
||||
gateways.emplace_back(x);
|
||||
gateways.emplace_back(net::ipv4addr_t::from_host(x.h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <llarp/util/types.hpp>
|
||||
|
||||
namespace llarp::vpn
|
||||
{
|
||||
using PacketSendFunc_t = std::function<void(std::vector<byte_t>)>;
|
||||
using PacketInterceptFunc_t = std::function<void(std::vector<byte_t>, 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
|
|
@ -4,14 +4,15 @@ namespace llarp::vpn
|
|||
{
|
||||
struct UDPPacketHandler : public Layer4Handler
|
||||
{
|
||||
PacketHandlerFunc m_BaseHandler;
|
||||
std::unordered_map<nuint16_t, PacketHandlerFunc> m_LocalPorts;
|
||||
PacketHandlerFunc_t m_BaseHandler;
|
||||
std::unordered_map<nuint16_t, PacketHandlerFunc_t> 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<const uint16_t*>(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<GenericLayer4Handler>(std::move(func));
|
||||
}
|
||||
|
|
|
@ -6,7 +6,35 @@
|
|||
|
||||
namespace llarp::vpn
|
||||
{
|
||||
using PacketHandlerFunc = std::function<void(llarp::net::IPPacket)>;
|
||||
using PacketHandlerFunc_t = std::function<void(llarp::net::IPPacket)>;
|
||||
|
||||
struct Layer4Handler;
|
||||
|
||||
class PacketRouter
|
||||
{
|
||||
PacketHandlerFunc_t m_BaseHandler;
|
||||
std::unordered_map<uint8_t, std::unique_ptr<Layer4Handler>> m_IPProtoHandler;
|
||||
|
||||
public:
|
||||
/// baseHandler will be called if no other handlers matches a packet
|
||||
explicit PacketRouter(PacketHandlerFunc_t baseHandler);
|
||||
|
||||
/// feed in an ip packet for handling
|
||||
void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt);
|
||||
|
||||
/// add a non udp packet handler using ip protocol proto
|
||||
void
|
||||
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_t func);
|
||||
|
||||
/// remove a udp handler that is already set up by bound port
|
||||
void
|
||||
RemoveUDPHandler(huint16_t localport);
|
||||
};
|
||||
|
||||
struct Layer4Handler
|
||||
{
|
||||
|
@ -15,31 +43,7 @@ namespace llarp::vpn
|
|||
virtual void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt) = 0;
|
||||
|
||||
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){};
|
||||
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc_t){};
|
||||
};
|
||||
class PacketRouter
|
||||
{
|
||||
PacketHandlerFunc m_BaseHandler;
|
||||
std::unordered_map<uint8_t, std::unique_ptr<Layer4Handler>> m_IPProtoHandler;
|
||||
|
||||
public:
|
||||
/// baseHandler will be called if no other handlers matches a packet
|
||||
explicit PacketRouter(PacketHandlerFunc baseHandler);
|
||||
|
||||
/// feed in an ip packet for handling
|
||||
void
|
||||
HandleIPPacket(llarp::net::IPPacket pkt);
|
||||
|
||||
/// add a non udp packet handler using ip protocol proto
|
||||
void
|
||||
AddIProtoHandler(uint8_t proto, PacketHandlerFunc func);
|
||||
|
||||
/// helper that adds a udp packet handler for UDP destinted for localport
|
||||
void
|
||||
AddUDPHandler(huint16_t localport, PacketHandlerFunc func);
|
||||
|
||||
/// remove a udp handler that is already set up by bound port
|
||||
void
|
||||
RemoveUDPHandler(huint16_t localport);
|
||||
};
|
||||
} // namespace llarp::vpn
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include "platform.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "win32.hpp"
|
||||
|
@ -28,7 +28,7 @@ namespace llarp::vpn
|
|||
(void)ctx;
|
||||
std::shared_ptr<Platform> plat;
|
||||
#ifdef _WIN32
|
||||
plat = std::make_shared<vpn::Win32Platform>();
|
||||
plat = std::make_shared<llarp::win32::VPNPlatform>(ctx);
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#ifdef ANDROID
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
#include <llarp/net/ip_range.hpp>
|
||||
#include <llarp/net/ip_packet.hpp>
|
||||
#include <set>
|
||||
|
||||
#include <oxenc/variant.h>
|
||||
|
||||
#include "i_packet_io.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
struct Context;
|
||||
|
@ -30,36 +32,40 @@ namespace llarp::vpn
|
|||
struct InterfaceInfo
|
||||
{
|
||||
std::string ifname;
|
||||
unsigned int index;
|
||||
huint32_t dnsaddr;
|
||||
std::set<InterfaceAddress> addrs;
|
||||
std::vector<InterfaceAddress> 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<huint32_t, huint128_t>;
|
||||
|
||||
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<IPVariant_t>
|
||||
GetGatewaysNotOnInterface(std::string ifname) = 0;
|
||||
virtual std::vector<net::ipaddr_t>
|
||||
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<NetworkInterface>
|
||||
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<NetworkInterface>
|
||||
ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0;
|
||||
/// create and start a network interface
|
||||
inline std::shared_ptr<NetworkInterface>
|
||||
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<I_Packet_IO>
|
||||
create_packet_io(unsigned int)
|
||||
{
|
||||
throw std::runtime_error{"raw packet io is unimplemented"};
|
||||
}
|
||||
};
|
||||
|
||||
/// create native vpn platform
|
|
@ -5,662 +5,191 @@
|
|||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
|
||||
#include <llarp/win32/exec.hpp>
|
||||
#include <llarp/win32/windivert.hpp>
|
||||
#include <llarp/win32/wintun.hpp>
|
||||
#include <llarp.hpp>
|
||||
#include <fmt/std.h>
|
||||
|
||||
// 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
|
||||
#include "platform.hpp"
|
||||
|
||||
/* 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}"
|
||||
|
||||
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 <typename Visit>
|
||||
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<int>
|
||||
GetInterfaceIndex(huint32_t ip)
|
||||
{
|
||||
std::optional<int> 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 <typename T>
|
||||
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<bool> m_Run;
|
||||
HANDLE m_Device, m_IOCP;
|
||||
std::vector<std::thread> m_Threads;
|
||||
thread::Queue<net::IPPacket> m_ReadQueue;
|
||||
llarp::Context* const _ctx;
|
||||
const int m_Metric{2};
|
||||
|
||||
InterfaceInfo m_Info;
|
||||
|
||||
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<wchar_t>;
|
||||
std::wstring_convert<convert_type, wchar_t> converter;
|
||||
return converter.to_bytes(wcmd);
|
||||
}
|
||||
|
||||
static void
|
||||
NetSH(std::string commands)
|
||||
{
|
||||
commands = NetSHCommand() + " interface IPv6 " + commands;
|
||||
LogInfo("exec: ", commands);
|
||||
::system(commands.c_str());
|
||||
}
|
||||
|
||||
public:
|
||||
static std::string
|
||||
RouteCommand()
|
||||
{
|
||||
std::wstring wcmd = get_win_sys_path() + L"\\route.exe";
|
||||
|
||||
using convert_type = std::codecvt_utf8<wchar_t>;
|
||||
std::wstring_convert<convert_type, wchar_t> converter;
|
||||
return converter.to_bytes(wcmd);
|
||||
}
|
||||
|
||||
Win32Interface(InterfaceInfo info, AbstractRouter* router)
|
||||
: m_ReadQueue{1024}, m_Info{std::move(info)}, _router{router}
|
||||
{
|
||||
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)
|
||||
{
|
||||
LogError("failed to open device");
|
||||
throw std::invalid_argument{"cannot open " + fname};
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
LogError("cannot up interface up");
|
||||
throw std::invalid_argument{"cannot put interface up"};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~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;
|
||||
return _ctx->router->Net();
|
||||
}
|
||||
|
||||
void
|
||||
Start()
|
||||
Route(std::string ip, std::string gw, std::string cmd)
|
||||
{
|
||||
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); });
|
||||
llarp::win32::Exec(
|
||||
"route.exe",
|
||||
fmt::format("{} {} MASK 255.255.255.255 {} METRIC {}", cmd, ip, gw, m_Metric));
|
||||
}
|
||||
|
||||
net::IPPacket
|
||||
ReadNextPacket() override
|
||||
void
|
||||
DefaultRouteViaInterface(NetworkInterface& vpn, std::string cmd)
|
||||
{
|
||||
if (m_ReadQueue.empty())
|
||||
return net::IPPacket{};
|
||||
|
||||
return m_ReadQueue.popFront();
|
||||
// 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);
|
||||
}
|
||||
|
||||
struct asio_evt_pkt
|
||||
OneShotExec
|
||||
RouteViaInterface(NetworkInterface& vpn, std::string addr, std::string mask, std::string cmd)
|
||||
{
|
||||
explicit asio_evt_pkt(bool _read) : read{_read}
|
||||
const auto& info = vpn.Info();
|
||||
auto index = info.index;
|
||||
if (index == 0)
|
||||
{
|
||||
if (auto maybe_idx = net::Platform::Default_ptr()->GetInterfaceIndex(info[0]))
|
||||
index = *maybe_idx;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return OneShotExec{
|
||||
"route.exe",
|
||||
fmt::format(
|
||||
"{} {} MASK {} {} IF {} METRIC {}", cmd, addr, mask, ifaddr, info.index, m_Metric)};
|
||||
}
|
||||
else
|
||||
{
|
||||
return OneShotExec{
|
||||
"route.exe",
|
||||
fmt::format("{} {} MASK {} {} METRIC {}", cmd, addr, mask, ifaddr, m_Metric)};
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<WintunContext> _wintun;
|
||||
|
||||
WinDivert_API m_WinDivert{};
|
||||
|
||||
public:
|
||||
VPNPlatform(const VPNPlatform&) = delete;
|
||||
VPNPlatform(VPNPlatform&&) = delete;
|
||||
|
||||
VPNPlatform(llarp::Context* ctx) : Platform{}, _ctx{ctx}, _wintun{WintunContext_new()}
|
||||
{}
|
||||
|
||||
OVERLAPPED hdr = {0, 0, 0, 0, nullptr}; // must be first, since this is part of the IO call
|
||||
bool read;
|
||||
net::IPPacket pkt;
|
||||
virtual ~VPNPlatform() = default;
|
||||
|
||||
void
|
||||
Read(HANDLE dev)
|
||||
AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
|
||||
{
|
||||
ReadFile(dev, pkt.buf, sizeof(pkt.buf), nullptr, &hdr);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<asio_evt_pkt>> m_Packets;
|
||||
|
||||
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;
|
||||
Route(ip_to_string(ip), ip_to_string(gateway), "ADD");
|
||||
}
|
||||
|
||||
void
|
||||
ReadLoop(size_t packetIndex)
|
||||
DelRoute(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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Win32RouteManager : public IRouteManager
|
||||
{
|
||||
void
|
||||
Execute(std::string cmd) const
|
||||
{
|
||||
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<wchar_t>;
|
||||
std::wstring_convert<convert_type, wchar_t> converter;
|
||||
return converter.to_bytes(wcmd);
|
||||
}
|
||||
|
||||
static std::string
|
||||
RouteCommand()
|
||||
{
|
||||
return Win32Interface::RouteCommand();
|
||||
}
|
||||
|
||||
void
|
||||
Route(IPVariant_t ip, IPVariant_t gateway, std::string cmd)
|
||||
{
|
||||
Execute(fmt::format(
|
||||
"{} {} {} MASK 255.255.255.255 {} METRIC 2", RouteCommand(), cmd, ip, gateway));
|
||||
}
|
||||
|
||||
void
|
||||
DefaultRouteViaInterface(std::string ifname, std::string cmd)
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
|
||||
void
|
||||
RouteViaInterface(std::string ifname, IPRange range, std::string cmd)
|
||||
{
|
||||
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
|
||||
{
|
||||
Execute(
|
||||
RouteCommand() + " " + cmd + range.addr.ToString() + " MASK "
|
||||
+ range.netmask_bits.ToString() + " " + ifname);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
AddRoute(IPVariant_t ip, IPVariant_t gateway) override
|
||||
{
|
||||
Route(ip, gateway, "ADD");
|
||||
}
|
||||
|
||||
void
|
||||
DelRoute(IPVariant_t ip, IPVariant_t gateway) override
|
||||
{
|
||||
Route(ip, gateway, "DELETE");
|
||||
Route(ip_to_string(ip), ip_to_string(gateway), "DELETE");
|
||||
}
|
||||
|
||||
void
|
||||
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override
|
||||
{
|
||||
RouteViaInterface(vpn.IfName(), range, "ADD");
|
||||
RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "ADD");
|
||||
}
|
||||
|
||||
void
|
||||
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override
|
||||
{
|
||||
RouteViaInterface(vpn.IfName(), range, "DELETE");
|
||||
RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "DELETE");
|
||||
}
|
||||
|
||||
std::vector<IPVariant_t>
|
||||
GetGatewaysNotOnInterface(std::string ifname) override
|
||||
std::vector<net::ipaddr_t>
|
||||
GetGatewaysNotOnInterface(NetworkInterface& vpn) override
|
||||
{
|
||||
std::vector<IPVariant_t> 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)
|
||||
std::vector<net::ipaddr_t> gateways;
|
||||
|
||||
auto idx = vpn.Info().index;
|
||||
using UInt_t = decltype(idx);
|
||||
for (const auto& iface : Net().AllNetworkInterfaces())
|
||||
{
|
||||
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);
|
||||
if (static_cast<UInt_t>(iface.index) == idx)
|
||||
continue;
|
||||
if (iface.gateway)
|
||||
gateways.emplace_back(*iface.gateway);
|
||||
}
|
||||
});
|
||||
return gateways;
|
||||
}
|
||||
|
||||
void
|
||||
AddDefaultRouteViaInterface(std::string ifname) override
|
||||
AddDefaultRouteViaInterface(NetworkInterface& vpn) override
|
||||
{
|
||||
// kill ipv6
|
||||
Execute(PowerShell() + R"(Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)");
|
||||
DefaultRouteViaInterface(ifname, "ADD");
|
||||
llarp::win32::Exec(
|
||||
"WindowsPowerShell\\v1.0\\powershell.exe",
|
||||
"-Command (Disable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
|
||||
|
||||
DefaultRouteViaInterface(vpn, "ADD");
|
||||
llarp::win32::Exec("ipconfig.exe", "/flushdns");
|
||||
}
|
||||
|
||||
void
|
||||
DelDefaultRouteViaInterface(std::string ifname) override
|
||||
DelDefaultRouteViaInterface(NetworkInterface& vpn) override
|
||||
{
|
||||
// restore ipv6
|
||||
Execute(PowerShell() + R"(Enable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)");
|
||||
DefaultRouteViaInterface(ifname, "DELETE");
|
||||
llarp::win32::Exec(
|
||||
"WindowsPowerShell\\v1.0\\powershell.exe",
|
||||
"-Command (Enable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
|
||||
|
||||
DefaultRouteViaInterface(vpn, "DELETE");
|
||||
llarp::win32::Exec("netsh.exe", "winsock reset");
|
||||
llarp::win32::Exec("ipconfig.exe", "/flushdns");
|
||||
}
|
||||
};
|
||||
|
||||
class Win32Platform : public Platform
|
||||
{
|
||||
Win32RouteManager _routeManager{};
|
||||
|
||||
public:
|
||||
std::shared_ptr<NetworkInterface>
|
||||
ObtainInterface(InterfaceInfo info, AbstractRouter* router) override
|
||||
{
|
||||
auto netif = std::make_shared<Win32Interface>(std::move(info), router);
|
||||
netif->Start();
|
||||
return netif;
|
||||
};
|
||||
return WintunInterface_new(_wintun, std::move(info), router);
|
||||
}
|
||||
|
||||
std::shared_ptr<I_Packet_IO>
|
||||
create_packet_io(unsigned int ifindex) override
|
||||
{
|
||||
// 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(); });
|
||||
}
|
||||
|
||||
IRouteManager&
|
||||
RouteManager() override
|
||||
{
|
||||
return _routeManager;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llarp::vpn
|
||||
} // namespace llarp::win32
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
#include "dll.hpp"
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
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
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "exception.hpp"
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
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 <typename Func_t>
|
||||
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<Func_t&>(ptr);
|
||||
}
|
||||
|
||||
public:
|
||||
DLL(std::string dll);
|
||||
|
||||
virtual ~DLL();
|
||||
};
|
||||
} // namespace llarp::win32
|
|
@ -0,0 +1,39 @@
|
|||
#include "windows.h"
|
||||
#include "exception.hpp"
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <array>
|
||||
|
||||
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<LPTSTR>(&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<TCHAR, decltype(deleter)> ptrBuffer{psz, deleter};
|
||||
return std::string{ptrBuffer.get(), cchMsg};
|
||||
}
|
||||
} // namespace llarp::win32
|
|
@ -1,31 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
namespace llarp::win32
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline std::string
|
||||
error_to_string(DWORD err)
|
||||
{
|
||||
std::array<CHAR, 512> 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
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue