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
pull/1969/head
Jeff 2 years ago committed by Jeff Becker
parent e981c9f899
commit 871c3e3281
No known key found for this signature in database
GPG Key ID: 025C02EE3A092F2D

@ -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', '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-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', '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, ] + 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(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_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
option(WITH_PEERSTATS "build with experimental peerstats db support" OFF) 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) include(cmake/enable_lto.cmake)
@ -81,6 +83,12 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo) set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif() 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) if(BUILD_STATIC_DEPS AND STATIC_LINK)
message(STATUS "we are building static deps so we won't build shared libs") message(STATUS "we are building static deps so we won't build shared libs")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "") set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
@ -171,11 +179,18 @@ if(NOT TARGET sodium)
export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake) export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake)
endif() endif()
if(NOT APPLE) if(APPLE)
add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla) 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") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-unknown-warning-option) add_compile_options(-Wno-unknown-warning-option)
endif() endif()
if(debug)
add_compile_options(-Wdeprecated-declarations)
else()
add_compile_options(-Wno-deprecated-declarations)
endif()
endif() endif()
if(XSAN) if(XSAN)
@ -186,11 +201,6 @@ if(XSAN)
message(STATUS "Doing a ${XSAN} sanitizer build") message(STATUS "Doing a ${XSAN} sanitizer build")
endif() endif()
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
add_definitions(-DLOKINET_DEBUG)
endif()
include(cmake/coverage.cmake) include(cmake/coverage.cmake)
# these vars are set by the cmake toolchain spec # 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) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif() endif()
if(BUILD_PACKAGE) if(BUILD_PACKAGE AND NOT APPLE)
include(cmake/installer.cmake) include(cmake/installer.cmake)
endif()
if(TARGET package)
add_dependencies(package assemble_gui)
endif() endif()

@ -2,6 +2,9 @@
include(CheckIPOSupported) include(CheckIPOSupported)
option(WITH_LTO "enable lto on compile time" ON) option(WITH_LTO "enable lto on compile time" ON)
if(WITH_LTO) 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) check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error)
if(IPO_ENABLED) if(IPO_ENABLED)
message(STATUS "LTO enabled") message(STATUS "LTO enabled")

@ -8,25 +8,30 @@ elseif(WIN32)
set(default_gui_target win32) set(default_gui_target win32)
endif() 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}) 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_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") set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn build command")
if (BUILD_GUI) if (BUILD_GUI)
message(STATUS "Building lokinet-gui") message(STATUS "Building lokinet-gui")
# allow manually specifying yarn with -DYARN=
if(NOT YARN) if(NOT YARN)
find_program(YARN NAMES yarn yarnpkg REQUIRED) find_program(YARN NAMES yarnpkg yarn REQUIRED)
endif() endif()
message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}") message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}")
set(wine_env)
if(WIN32)
set(wine_env USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix")
endif()
add_custom_target(lokinet-gui
COMMAND ${YARN} install --frozen-lockfile && if(NOT WIN32)
${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET} add_custom_target(lokinet-gui
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui") COMMAND ${YARN} install --frozen-lockfile &&
${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
endif()
if(APPLE) if(APPLE)
add_custom_target(assemble_gui ALL add_custom_target(assemble_gui ALL
@ -38,14 +43,13 @@ if (BUILD_GUI)
COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns" COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns"
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/" COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/"
COMMAND /usr/libexec/PlistBuddy COMMAND /usr/libexec/PlistBuddy
-c "Delete :CFBundleDisplayName" -c "Delete :CFBundleDisplayName"
-c "Add :LSHasLocalizedDisplayName bool true" -c "Add :LSHasLocalizedDisplayName bool true"
-c "Add :CFBundleDevelopmentRegion string en" -c "Add :CFBundleDevelopmentRegion string en"
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}" -c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}" -c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
"${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
) )
elseif(WIN32) elseif(WIN32)
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui") file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui")
option(GUI_ZIP_FILE "custom lokinet gui for windows from zip file" OFF) 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}") message(STATUS "using custom lokinet gui from ${GUI_ZIP_FILE}")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE} execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}) 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() else()
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
DEPENDS lokinet lokinet-gui COMMAND ${YARN} install --frozen-lockfile &&
# FIXME: we really shouldn't be building inside the source directory but this is npm... 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 COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe" "${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe"
"${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
) WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
add_custom_target(assemble_gui ALL
DEPENDS ${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe)
endif() endif()
add_custom_target(assemble_gui ALL COMMAND "true" DEPENDS "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
else() else()
message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform") message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform")
endif() endif()
else() else()
message(STATUS "not building gui") message(STATUS "not building gui")
endif() 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) if(WIN32)
include(cmake/win32_installer_deps.cmake) 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) elseif(APPLE)
set(CPACK_GENERATOR DragNDrop;ZIP) set(CPACK_GENERATOR DragNDrop;ZIP)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
endif() endif()
# This must always be last!
include(CPack) 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) if(NOT WIN32)
return() return()
endif() endif()
if (NOT STATIC_LINK)
message(FATAL_ERROR "windows requires static builds (thanks balmer)")
endif()
enable_language(RC) 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) # unlike unix where you get a *single* compiler ID string in .comment
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-bad-function-cast>) # GNU ld sees fit to merge *all* the .ident sections in object files
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-cast-function-type>) # to .r[o]data section one after the other!
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fpermissive>) add_compile_options(-fno-ident -Wa,-mbig-obj)
# unlike unix where you get a *single* compiler ID string in .comment # the minimum windows version, set to 6 rn because supporting older windows is hell
# GNU ld sees fit to merge *all* the .ident sections in object files set(_winver 0x0600)
# to .r[o]data section one after the other! add_definitions(-D_WIN32_WINNT=${_winver})
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()
if(EMBEDDED_CFG) if(EMBEDDED_CFG)
link_libatomic() link_libatomic()
endif() 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) set(WINDIVERT_VERSION 2.2.0-A CACHE STRING "windivert version")
message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll") set(WINDIVERT_MIRROR https://reqrypt.org/download
message("for release builds, turn on STATIC_LINK in cmake options") CACHE STRING "windivert mirror(s)")
endif() 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 BUILD_GUI)
if(NOT GUI_ZIP_URL) if(NOT GUI_ZIP_URL)
set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip") 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!") message(FATAL_ERROR "Downloaded gui archive from ${GUI_ZIP_URL} does not contain gui/lokinet-gui.exe!")
endif() endif()
endif() endif()
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui) 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) install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet") 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_DEFINES "RequestExecutionLevel admin")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
function(read_nsis_file filename outvar) function(read_nsis_file filename outvar)
file(STRINGS "${filename}" _outvar) file(STRINGS "${filename}" _outvar)
list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\") list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\")
@ -51,9 +52,9 @@ read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_
set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}") set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}") set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}")
set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}") set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}")
set(CPACK_NSIS_MODIFY_PATH ON)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")

@ -34,6 +34,7 @@ for abi in $build_abis; do
-DBUILD_TESTING=OFF \ -DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \ -DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \ -DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \ -DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \ -DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \ -DWITH_SYSTEMD=OFF \

@ -34,8 +34,11 @@ else
fi fi
mkdir -v "$base" mkdir -v "$base"
if [ -e build-windows ]; then if [ -e build/win32 ]; then
cp -av build-windows/lokinet-*.exe "$base" # 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 # zipit up yo
archive="$base.zip" archive="$base.zip"
zip -r "$archive" "$base" zip -r "$archive" "$base"
@ -77,6 +80,10 @@ $mkdirs
put $archive $upload_to put $archive $upload_to
SFTP 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 set +o xtrace
echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n" 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 -e
set +x set +x
root="$(readlink -f $(dirname $0)/../)" root="$(readlink -f $(dirname $0)/../)"
cd "$root" mkdir -p $root/build/win32
./contrib/windows-configure.sh . build-windows "$@" $root/contrib/windows-configure.sh $root $root/build/win32 "$@"
make package -j${JOBS:-$(nproc)} -C build-windows make package -j${JOBS:-$(nproc)} -C $root/build/win32

@ -46,15 +46,27 @@ if(WITH_BOOTSTRAP)
endif() endif()
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}) foreach(exe ${exetargets})
if(WIN32 AND NOT MSVC_VERSION) if(WIN32)
target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc) 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 -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") elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_link_directories(${exe} PRIVATE /usr/local/lib) target_link_directories(${exe} PRIVATE /usr/local/lib)
endif() 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}") target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}")
if(should_install) if(should_install)
if(APPLE) if(APPLE)
@ -71,3 +83,9 @@ endforeach()
if(SETCAP) if(SETCAP)
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)") install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
endif() 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 // Create the service
schService = CreateService( schService = CreateService(
schSCManager, // SCM database schSCManager, // SCM database
"lokinet", // name of service strdup("lokinet"), // name of service
"Lokinet for Windows", // service name to display "Lokinet for Windows", // service name to display
SERVICE_ALL_ACCESS, // desired access SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_WIN32_OWN_PROCESS, // service type
@ -134,10 +134,10 @@ insert_description()
SC_HANDLE schSCManager; SC_HANDLE schSCManager;
SC_HANDLE schService; SC_HANDLE schService;
SERVICE_DESCRIPTION sd; SERVICE_DESCRIPTION sd;
LPTSTR szDesc = LPTSTR szDesc = strdup(
"LokiNET is a free, open source, private, " "LokiNET is a free, open source, private, "
"decentralized, \"market based sybil resistant\" " "decentralized, \"market based sybil resistant\" "
"and IP based onion routing network"; "and IP based onion routing network");
// Get a handle to the SCM database. // Get a handle to the SCM database.
schSCManager = OpenSCManager( schSCManager = OpenSCManager(
NULL, // local computer NULL, // local computer
@ -270,7 +270,7 @@ run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions o
} }
catch (std::exception& ex) 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); exit_code.set_value(1);
return; return;
} }
@ -367,7 +367,7 @@ main(int argc, char* argv[])
return lokinet_main(argc, argv); return lokinet_main(argc, argv);
#else #else
SERVICE_TABLE_ENTRY DispatchTable[] = { 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) if (lstrcmpi(argv[1], "--win32-daemon") == 0)
{ {
start_as_daemon = true; start_as_daemon = true;
@ -680,7 +680,7 @@ win32_daemon_entry(DWORD argc, LPTSTR* argv)
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
// SCM clobbers startup args, regenerate them here // SCM clobbers startup args, regenerate them here
argc = 2; argc = 2;
argv[1] = "c:/programdata/lokinet/lokinet.ini"; argv[1] = strdup("c:/programdata/lokinet/lokinet.ini");
argv[2] = nullptr; argv[2] = nullptr;
lokinet_main(argc, argv); lokinet_main(argc, argv);
} }

@ -2,4 +2,4 @@ add_library(lokinet-android
SHARED SHARED
lokinet_config.cpp lokinet_config.cpp
lokinet_daemon.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/fs.cpp
util/json.cpp util/json.cpp
util/logging/buffer.cpp util/logging/buffer.cpp
util/lokinet_init.c util/easter_eggs.cpp
util/mem.cpp util/mem.cpp
util/str.cpp util/str.cpp
util/thread/queue_manager.cpp util/thread/queue_manager.cpp
@ -32,12 +32,11 @@ add_library(lokinet-platform
STATIC STATIC
# for networking # for networking
ev/ev.cpp ev/ev.cpp
ev/ev_libuv.cpp ev/libuv.cpp
net/ip.cpp net/ip.cpp
net/ip_address.cpp net/ip_address.cpp
net/ip_packet.cpp net/ip_packet.cpp
net/ip_range.cpp net/ip_range.cpp
net/net.cpp
net/net_int.cpp net/net_int.cpp
net/sock_addr.cpp net/sock_addr.cpp
vpn/packet_router.cpp vpn/packet_router.cpp
@ -58,34 +57,66 @@ endif()
if (WIN32) if (WIN32)
target_sources(lokinet-platform PRIVATE target_sources(lokinet-platform PRIVATE
win32/win32_inet.c net/win32.cpp
win32/win32_intrnl.c) win32/exec.cpp)
add_library(lokinet-win32 STATIC
target_link_libraries(lokinet-platform PUBLIC iphlpapi) win32/dll.cpp
win32/exception.cpp)
add_library(lokinet-wintun STATIC
win32/wintun.cpp)
add_library(lokinet-windivert STATIC
win32/windivert.cpp)
# wintun and windivert are privated linked by lokinet-platform
# this is so their details do not leak out to deps of lokinet-platform
# wintun and windivert still need things from lokinet-platform
target_compile_options(lokinet-wintun PUBLIC -I${CMAKE_BINARY_DIR}/wintun/include/)
target_compile_options(lokinet-windivert PUBLIC -I${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/include/)
target_include_directories(lokinet-windivert PUBLIC ${PROJECT_SOURCE_DIR})
target_link_libraries(lokinet-wintun PUBLIC lokinet-platform lokinet-util lokinet-config)
target_link_libraries(lokinet-win32 PUBLIC lokinet-util)
target_link_libraries(lokinet-windivert PUBLIC oxen-logging)
target_link_libraries(lokinet-windivert PRIVATE lokinet-win32)
target_link_libraries(lokinet-platform PRIVATE lokinet-win32 lokinet-wintun lokinet-windivert)
else()
target_sources(lokinet-platform PRIVATE
net/posix.cpp)
endif() endif()
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_include_directories(lokinet-platform SYSTEM PUBLIC /usr/local/include) target_include_directories(lokinet-platform SYSTEM PUBLIC /usr/local/include)
endif() endif()
add_library(liblokinet add_library(lokinet-dns
STATIC STATIC
config/config.cpp
config/definition.cpp
config/ini.cpp
config/key_manager.cpp
dns/message.cpp dns/message.cpp
dns/name.cpp dns/name.cpp
dns/multi_platform.cpp dns/platform.cpp
dns/nm_platform.cpp
dns/sd_platform.cpp
dns/question.cpp dns/question.cpp
dns/rr.cpp dns/rr.cpp
dns/serialize.cpp dns/serialize.cpp
dns/server.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/table.cpp
consensus/reachability_testing.cpp consensus/reachability_testing.cpp
@ -115,7 +146,7 @@ add_library(liblokinet
dht/taglookup.cpp dht/taglookup.cpp
endpoint_base.cpp endpoint_base.cpp
exit/context.cpp exit/context.cpp
exit/endpoint.cpp exit/endpoint.cpp
exit/exit_messages.cpp exit/exit_messages.cpp
@ -170,7 +201,7 @@ add_library(liblokinet
router/rc_gossiper.cpp router/rc_gossiper.cpp
router/router.cpp router/router.cpp
router/route_poker.cpp router/route_poker.cpp
routing/dht_message.cpp routing/dht_message.cpp
routing/message_parser.cpp routing/message_parser.cpp
routing/path_confirm_message.cpp routing/path_confirm_message.cpp
@ -204,42 +235,42 @@ add_library(liblokinet
service/tag.cpp 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(WITH_PEERSTATS_BACKEND)
target_compile_definitions(lokinet-amalgum PRIVATE -DLOKINET_PEERSTATS_BACKEND)
if(TRACY_ROOT) target_link_libraries(lokinet-amalgum PUBLIC sqlite_orm)
target_sources(liblokinet PRIVATE ${TRACY_ROOT}/TracyClient.cpp)
endif() endif()
if(WITH_HIVE) if(WITH_HIVE)
target_sources(liblokinet PRIVATE target_sources(lokinet-amalgum PRIVATE
tooling/router_hive.cpp tooling/router_hive.cpp
tooling/hive_router.cpp tooling/hive_router.cpp
tooling/hive_context.cpp tooling/hive_context.cpp
) )
endif() 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 cxxopts
oxenc::oxenc oxenc::oxenc
lokinet-platform lokinet-platform
lokinet-config
lokinet-dns
lokinet-util lokinet-util
lokinet-cryptography lokinet-cryptography
ngtcp2_static ngtcp2_static
oxenmq::oxenmq) 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) pkg_check_modules(CRYPT libcrypt IMPORTED_TARGET)
if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING) if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING)
add_definitions(-DHAVE_CRYPT) add_definitions(-DHAVE_CRYPT)
add_library(libcrypt INTERFACE) add_library(libcrypt INTERFACE)
target_link_libraries(libcrypt INTERFACE PkgConfig::CRYPT) 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}") message(STATUS "using libcrypt ${CRYPT_VERSION}")
endif() endif()
@ -247,7 +278,7 @@ endif()
if(BUILD_LIBLOKINET) if(BUILD_LIBLOKINET)
include(GNUInstallDirs) include(GNUInstallDirs)
add_library(lokinet-shared SHARED lokinet_shared.cpp) 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) if(WIN32)
set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "") set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
endif() endif()

@ -18,12 +18,9 @@ target_sources(lokinet-platform PRIVATE vpn_platform.cpp vpn_interface.cpp route
add_executable(lokinet-extension MACOSX_BUNDLE add_executable(lokinet-extension MACOSX_BUNDLE
PacketTunnelProvider.m PacketTunnelProvider.m
DNSTrampoline.m DNSTrampoline.m
) )
enable_lto(lokinet-extension) enable_lto(lokinet-extension)
target_link_libraries(lokinet-extension PRIVATE
liblokinet
${COREFOUNDATION}
${NETEXT})
# -fobjc-arc enables automatic reference counting for objective-C code # -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. # -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) set(product_type com.apple.product-type.app-extension)
endif() endif()
target_link_libraries(lokinet-extension PRIVATE
lokinet-amalgum
${COREFOUNDATION}
${NETEXT})
set_target_properties(lokinet-extension PROPERTIES set_target_properties(lokinet-extension PROPERTIES
BUNDLE TRUE BUNDLE TRUE
BUNDLE_EXTENSION ${bundle_ext} BUNDLE_EXTENSION ${bundle_ext}

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <llarp.hpp> #include <llarp.hpp>
#include <llarp/ev/vpn.hpp> #include <llarp/vpn/platform.hpp>
#include <llarp/util/thread/queue.hpp> #include <llarp/util/thread/queue.hpp>
#include <memory> #include <memory>

@ -140,14 +140,14 @@ namespace llarp
"this setting specifies the public IP at which this router is reachable. When", "this setting specifies the public IP at which this router is reachable. When",
"provided the public-port option must also be specified.", "provided the public-port option must also be specified.",
}, },
[this](std::string arg) { [this, net = params.Net_ptr()](std::string arg) {
if (arg.empty()) if (arg.empty())
return; return;
nuint32_t addr{}; nuint32_t addr{};
if (not addr.FromString(arg)) if (not addr.FromString(arg))
throw std::invalid_argument{fmt::format("{} is not a valid IPv4 address", 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{ throw std::invalid_argument{
fmt::format("{} is not a publicly routable ip address", addr)}; fmt::format("{} is not a publicly routable ip address", addr)};
@ -648,6 +648,7 @@ namespace llarp
throw std::invalid_argument{ throw std::invalid_argument{
fmt::format("[network]:ip6-range invalid value: '{}'", arg)}; fmt::format("[network]:ip6-range invalid value: '{}'", arg)};
}); });
// TODO: could be useful for snodes in the future, but currently only implemented for clients: // TODO: could be useful for snodes in the future, but currently only implemented for clients:
conf.defineOption<std::string>( conf.defineOption<std::string>(
"network", "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>( conf.defineOption<std::string>(
"dns", "dns",
"bind", "bind",

@ -155,9 +155,12 @@ namespace llarp
struct DnsConfig struct DnsConfig
{ {
bool m_raw_dns;
std::vector<SockAddr> m_bind; std::vector<SockAddr> m_bind;
std::vector<SockAddr> m_upstreamDNS; std::vector<SockAddr> m_upstreamDNS;
std::vector<fs::path> m_hostfiles; std::vector<fs::path> m_hostfiles;
std::optional<SockAddr> m_QueryBind;
std::unordered_multimap<std::string, std::string> m_ExtraOpts; std::unordered_multimap<std::string, std::string> m_ExtraOpts;
void 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 false
#endif #endif
; ;
/// we have systemd ?
inline constexpr bool has_systemd = /// building with systemd enabled ?
inline constexpr bool with_systemd =
#ifdef WITH_SYSTEMD #ifdef WITH_SYSTEMD
true true
#else #else
@ -47,6 +48,15 @@ namespace llarp::platform
#endif #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 ? /// are we an android platform ?
inline constexpr bool is_android = inline constexpr bool is_android =
#ifdef ANDROID #ifdef ANDROID
@ -65,9 +75,26 @@ namespace llarp::platform
#endif #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 ? /// are we a mobile phone ?
inline constexpr bool is_mobile = is_android or is_iphone; inline constexpr bool is_mobile = is_android or is_iphone;
/// does this platform support native ipv6 ? /// does this platform support native ipv6 ?
// TODO: make windows support ipv6
inline constexpr bool supports_ipv6 = not is_windows; inline constexpr bool supports_ipv6 = not is_windows;
} // namespace llarp::platform } // 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 namespace llarp::dns::nm
{ {
void void
Platform::set_resolver(std::string, llarp::SockAddr, bool) Platform::set_resolver(unsigned int, llarp::SockAddr, bool)
{ {
// todo: implement me eventually // todo: implement me eventually
} }

@ -17,7 +17,7 @@ namespace llarp::dns
virtual ~Platform() = default; virtual ~Platform() = default;
void 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 }; // namespace nm
using NM_Platform_t = std::conditional_t<false, nm::Platform, Null_Platform>; 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 namespace llarp::dns
{ {
@ -9,14 +9,14 @@ namespace llarp::dns
} }
void 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}; size_t fails{0};
for (const auto& ptr : m_Impls) for (const auto& ptr : m_Impls)
{ {
try try
{ {
ptr->set_resolver(ifname, dns, global); ptr->set_resolver(index, dns, global);
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {

@ -3,9 +3,12 @@
#include <memory> #include <memory>
#include <llarp/net/sock_addr.hpp> #include <llarp/net/sock_addr.hpp>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include <stdexcept> #include <stdexcept>
#ifndef _WIN32
#include <net/if.h>
#endif
namespace llarp::dns namespace llarp::dns
{ {
/// sets dns settings in a platform dependant way /// sets dns settings in a platform dependant way
@ -18,13 +21,39 @@ namespace llarp::dns
/// throws if unsupported or fails. /// throws if unsupported or fails.
/// ///
/// ///
/// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0. /// \param if_index -- the interface index to which we add the DNS servers, this can be gotten
/// Typically tun_endpoint.GetIfName(). /// 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 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 /// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
/// addresses (false). /// addresses (false).
virtual void 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 } // namespace llarp::dns

@ -13,14 +13,8 @@ using namespace std::literals;
namespace llarp::dns::sd namespace llarp::dns::sd
{ {
void 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{ linux::DBUS _dbus{
"org.freedesktop.resolve1", "org.freedesktop.resolve1",
"/org/freedesktop/resolve1", "/org/freedesktop/resolve1",

@ -16,7 +16,7 @@ namespace llarp::dns
virtual ~Platform() = default; virtual ~Platform() = default;
void 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 } // namespace sd
using SD_Platform_t = using SD_Platform_t =

@ -9,9 +9,9 @@
#include <unbound.h> #include <unbound.h>
#include <uvw.hpp> #include <uvw.hpp>
#include "multi_platform.hpp"
#include "sd_platform.hpp" #include "sd_platform.hpp"
#include "nm_platform.hpp" #include "nm_platform.hpp"
#include "win32_platform.hpp"
namespace llarp::dns namespace llarp::dns
{ {
@ -37,7 +37,7 @@ namespace llarp::dns
m_udp = loop->make_udp([&](auto&, SockAddr src, llarp::OwnedBuffer buf) { m_udp = loop->make_udp([&](auto&, SockAddr src, llarp::OwnedBuffer buf) {
if (src == m_LocalAddr) if (src == m_LocalAddr)
return; 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); LogWarn("did not handle dns packet from ", src, " to ", m_LocalAddr);
} }
@ -83,7 +83,7 @@ namespace llarp::dns
class Query : public QueryJob_Base class Query : public QueryJob_Base
{ {
std::weak_ptr<Resolver> parent; std::weak_ptr<Resolver> parent;
std::weak_ptr<PacketSource_Base> src; std::shared_ptr<PacketSource_Base> src;
SockAddr resolverAddr; SockAddr resolverAddr;
SockAddr askerAddr; SockAddr askerAddr;
@ -91,7 +91,7 @@ namespace llarp::dns
explicit Query( explicit Query(
std::weak_ptr<Resolver> parent_, std::weak_ptr<Resolver> parent_,
Message query, Message query,
std::weak_ptr<PacketSource_Base> pktsrc, std::shared_ptr<PacketSource_Base> pktsrc,
SockAddr toaddr, SockAddr toaddr,
SockAddr fromaddr) SockAddr fromaddr)
: QueryJob_Base{std::move(query)} : QueryJob_Base{std::move(query)}
@ -118,6 +118,8 @@ namespace llarp::dns
std::shared_ptr<uvw::PollHandle> m_Poller; std::shared_ptr<uvw::PollHandle> m_Poller;
#endif #endif
std::optional<SockAddr> m_LocalAddr;
struct ub_result_deleter struct ub_result_deleter
{ {
void void
@ -127,6 +129,12 @@ namespace llarp::dns
} }
}; };
const net::Platform*
Net_ptr() const
{
return m_Loop.lock()->Net_ptr();
}
static void static void
Callback(void* data, int err, ub_result* _result) Callback(void* data, int err, ub_result* _result)
{ {
@ -186,6 +194,12 @@ namespace llarp::dns
return "unbound"; return "unbound";
} }
virtual std::optional<SockAddr>
GetLocalAddr() const override
{
return m_LocalAddr;
}
void void
Up(const llarp::DnsConfig& conf) Up(const llarp::DnsConfig& conf)
{ {
@ -220,23 +234,39 @@ namespace llarp::dns
throw std::runtime_error{ throw std::runtime_error{
fmt::format("cannot use {} as upstream dns: {}", str, ub_strerror(err))}; 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 if (auto maybe_addr = conf.m_QueryBind)
// tunnel using normal sockets but instead we "get" to use Apple's interfaces, hurray). {
if (hoststr == "127.0.0.1") SockAddr addr{*maybe_addr};
std::string host{addr.hostString()};
if (addr.getPort() == 0)
{ {
// Not at all clear why this is needed but without it we get "send failed: Can't // unbound manages their own sockets because of COURSE it does. so we find an open port
// assign requested address" when unbound tries to connect to the localhost address // on our system and use it so we KNOW what it is before giving it to unbound to
// using a source address of 0.0.0.0. Yay apple. // explicitly bind to JUST that port.
SetOpt("outgoing-interface:", hoststr.c_str());
// The trampoline expects just a single source port (and sends everything back to it) addrinfo hints{};
SetOpt("outgoing-range:", "1"); addrinfo* result{nullptr};
SetOpt("outgoing-port-avoid:", "0-65535"); hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
SetOpt("outgoing-port-permit:", "1253"); 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);
} }
#endif m_LocalAddr = addr;
LogInfo(fmt::format("sening dns queries from {}:{}", host, addr.getPort()));
// set up query bind port if needed
SetOpt("outgoing-interface:", host);
SetOpt("outgoing-range:", "1");
SetOpt("outgoing-port-avoid:", "0-65535");
SetOpt("outgoing-port-permit:", std::to_string(addr.getPort()));
} }
// set async // set async
ub_ctx_async(m_ctx.get(), 1); ub_ctx_async(m_ctx.get(), 1);
// setup mainloop // setup mainloop
@ -339,7 +369,7 @@ namespace llarp::dns
bool bool
MaybeHookDNS( MaybeHookDNS(
std::weak_ptr<PacketSource_Base> source, std::shared_ptr<PacketSource_Base> source,
const Message& query, const Message& query,
const SockAddr& to, const SockAddr& to,
const SockAddr& from) override const SockAddr& from) override
@ -388,13 +418,10 @@ namespace llarp::dns
void void
Query::SendReply(llarp::OwnedBuffer replyBuf) const Query::SendReply(llarp::OwnedBuffer replyBuf) const
{ {
auto packet_src = src.lock(); if (auto ptr = parent.lock())
auto parent_ptr = parent.lock();
if (packet_src and parent_ptr)
{ {
parent_ptr->call([packet_src, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] { ptr->call([this, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] {
packet_src->SendTo(to, from, OwnedBuffer::copy_from(buf)); src->SendTo(to, from, OwnedBuffer::copy_from(buf));
}); });
} }
else else
@ -402,13 +429,22 @@ namespace llarp::dns
} }
} // namespace libunbound } // 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_Loop{std::move(loop)}
, m_Config{std::move(conf)} , m_Config{std::move(conf)}
, m_Platform{CreatePlatform()} , 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 void
Server::Start() Server::Start()
{ {
@ -433,6 +469,10 @@ namespace llarp::dns
plat->add_impl(std::make_unique<SD_Platform_t>()); plat->add_impl(std::make_unique<SD_Platform_t>());
plat->add_impl(std::make_unique<NM_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; return plat;
} }
@ -531,19 +571,16 @@ namespace llarp::dns
Server::SetDNSMode(bool all_queries) Server::SetDNSMode(bool all_queries)
{ {
if (auto maybe_addr = FirstBoundPacketSourceAddr()) 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 bool
Server::MaybeHandlePacket( Server::MaybeHandlePacket(
std::weak_ptr<PacketSource_Base> src, std::shared_ptr<PacketSource_Base> ptr,
const SockAddr& to, const SockAddr& to,
const SockAddr& from, const SockAddr& from,
llarp::OwnedBuffer buf) llarp::OwnedBuffer buf)
{ {
auto ptr = src.lock();
if (not ptr)
return false;
// dont process to prevent feedback loop // dont process to prevent feedback loop
if (ptr->WouldLoop(to, from)) if (ptr->WouldLoop(to, from))
{ {
@ -557,6 +594,7 @@ namespace llarp::dns
LogWarn("invalid dns message format from ", from, " to dns listener on ", to); LogWarn("invalid dns message format from ", from, " to dns listener on ", to);
return false; return false;
} }
auto& msg = *maybe; auto& msg = *maybe;
// we don't provide a DoH resolver because it requires verified TLS // 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 // 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()) if (auto res_ptr = resolver.lock())
{ {
LogDebug("check resolver ", res_ptr->ResolverName(), " for dns from ", from, " to ", to); 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; return true;
} }
} }

@ -67,17 +67,62 @@ namespace llarp::dns
BoundOn() const = 0; 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 /// non complex implementation of QueryJob_Base for use in things that
/// only ever called on the mainloop thread /// only ever called on the mainloop thread
class QueryJob : public QueryJob_Base, std::enable_shared_from_this<QueryJob> 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 resolver;
const SockAddr asker; const SockAddr asker;
public: public:
explicit QueryJob( explicit QueryJob(
std::weak_ptr<PacketSource_Base> source, std::shared_ptr<PacketSource_Base> source,
const Message& query, const Message& query,
const SockAddr& to_, const SockAddr& to_,
const SockAddr& from_) const SockAddr& from_)
@ -87,8 +132,7 @@ namespace llarp::dns
void void
SendReply(llarp::OwnedBuffer replyBuf) const override SendReply(llarp::OwnedBuffer replyBuf) const override
{ {
if (auto ptr = src.lock()) src->SendTo(asker, resolver, std::move(replyBuf));
ptr->SendTo(asker, resolver, std::move(replyBuf));
} }
}; };
@ -119,6 +163,13 @@ namespace llarp::dns
return Rank() > other.Rank(); 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 /// get printable name
virtual std::string_view virtual std::string_view
ResolverName() const = 0; ResolverName() const = 0;
@ -134,7 +185,7 @@ namespace llarp::dns
/// returns true if we consumed this query and it should not be processed again /// returns true if we consumed this query and it should not be processed again
virtual bool virtual bool
MaybeHookDNS( MaybeHookDNS(
std::weak_ptr<PacketSource_Base> source, std::shared_ptr<PacketSource_Base> source,
const Message& query, const Message& query,
const SockAddr& to, const SockAddr& to,
const SockAddr& from) = 0; const SockAddr& from) = 0;
@ -167,7 +218,8 @@ namespace llarp::dns
public: public:
virtual ~Server() = default; 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 /// returns all sockaddr we have from all of our PacketSources
std::vector<SockAddr> std::vector<SockAddr>
@ -205,16 +257,18 @@ namespace llarp::dns
virtual std::shared_ptr<Resolver_Base> virtual std::shared_ptr<Resolver_Base>
MakeDefaultResolver(); 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 true if we decided to process the packet and consumed it
/// returns false if we dont want to process the packet /// returns false if we dont want to process the packet
bool bool
MaybeHandlePacket( MaybeHandlePacket(
std::weak_ptr<PacketSource_Base> pktsource, std::shared_ptr<PacketSource_Base> pktsource,
const SockAddr& resolver, const SockAddr& resolver,
const SockAddr& from, const SockAddr& from,
llarp::OwnedBuffer buf); llarp::OwnedBuffer buf);
/// set which dns mode we are in. /// set which dns mode we are in.
/// true for intercepting all queries. false for just .loki and .snode /// true for intercepting all queries. false for just .loki and .snode
void void
@ -226,7 +280,7 @@ namespace llarp::dns
std::shared_ptr<I_Platform> m_Platform; std::shared_ptr<I_Platform> m_Platform;
private: private:
const std::string m_NetifName; const unsigned int m_NetIfIndex;
std::set<std::shared_ptr<Resolver_Base>, ComparePtr<std::shared_ptr<Resolver_Base>>> std::set<std::shared_ptr<Resolver_Base>, ComparePtr<std::shared_ptr<Resolver_Base>>>
m_OwnedResolvers; m_OwnedResolvers;
std::set<std::weak_ptr<Resolver_Base>, CompareWeakPtr<Resolver_Base>> m_Resolvers; 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 <cstring>
#include <string_view> #include <string_view>
// We libuv now #include "libuv.hpp"
#include "ev_libuv.hpp" #include <llarp/net/net.hpp>
namespace llarp namespace llarp
{ {
@ -16,4 +16,11 @@ namespace llarp
{ {
return std::make_shared<llarp::uv::Loop>(queueLength); return std::make_shared<llarp::uv::Loop>(queueLength);
} }
const net::Platform*
EventLoop::Net_ptr() const
{
return net::Platform::Default_ptr();
}
} // namespace llarp } // namespace llarp

@ -4,7 +4,7 @@
#include <llarp/util/time.hpp> #include <llarp/util/time.hpp>
#include <llarp/util/thread/threading.hpp> #include <llarp/util/thread/threading.hpp>
#include <llarp/constants/evloop.hpp> #include <llarp/constants/evloop.hpp>
#include <llarp/net/interface_info.hpp>
#include <algorithm> #include <algorithm>
#include <deque> #include <deque>
#include <list> #include <list>
@ -28,8 +28,10 @@ namespace llarp
namespace net namespace net
{ {
class Platform;
struct IPPacket; struct IPPacket;
} } // namespace net
/// distinct event loop waker upper; used to idempotently schedule a task on the next event loop /// 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 ~EventLoop() = default;
virtual const net::Platform*
Net_ptr() const;
using UDPReceiveFunc = std::function<void(UDPHandle&, SockAddr src, llarp::OwnedBuffer buf)>; using UDPReceiveFunc = std::function<void(UDPHandle&, SockAddr src, llarp::OwnedBuffer buf)>;
// Constructs a UDP socket that can be used for sending and/or receiving // Constructs a UDP socket that can be used for sending and/or receiving

@ -1,12 +1,12 @@
#include "ev_libuv.hpp" #include "libuv.hpp"
#include "vpn.hpp"
#include <memory> #include <memory>
#include <thread> #include <thread>
#include <type_traits> #include <type_traits>
#include <cstring>
#include <llarp/util/exceptions.hpp> #include <llarp/util/exceptions.hpp>
#include <llarp/util/thread/queue.hpp> #include <llarp/util/thread/queue.hpp>
#include <cstring> #include <llarp/vpn/platform.hpp>
#include "ev.hpp"
#include <uvw.hpp> #include <uvw.hpp>
@ -251,19 +251,21 @@ namespace llarp::uv
using event_t = uvw::PrepareEvent; using event_t = uvw::PrepareEvent;
auto handle = m_Impl->resource<uvw::PrepareHandle>(); auto handle = m_Impl->resource<uvw::PrepareHandle>();
#endif #endif
if (!handle) if (!handle)
return false; return false;
handle->on<event_t>([netif = std::move(netif), handler = std::move(handler)]( handle->on<event_t>([netif = std::move(netif), handler = std::move(handler)](
const event_t&, [[maybe_unused]] auto& handle) { 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) if (handler)
handler(std::move(pkt)); handler(std::move(pkt));
// on windows/apple, vpn packet io does not happen as an io action that wakes up the event // 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. // 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(); netif->MaybeWakeUpperLayers();
} }
}); });

@ -110,7 +110,7 @@ namespace llarp
bool bool
Endpoint::QueueOutboundTraffic( 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()}; const service::ConvoTag tag{path.as_array()};
if (t == service::ProtocolType::QUIC) if (t == service::ProtocolType::QUIC)
@ -118,18 +118,19 @@ namespace llarp
auto quic = m_Parent->GetQUICTunnel(); auto quic = m_Parent->GetQUICTunnel();
if (not quic) if (not quic)
return false; 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_LastActive = m_Parent->Now();
m_TxRate += buf.underlying.sz;
return true; return true;
} }
// queue overflow // queue overflow
if (m_UpstreamQueue.size() > MaxUpstreamQueueSize) if (m_UpstreamQueue.size() > MaxUpstreamQueueSize)
return false; return false;
llarp::net::IPPacket pkt; llarp::net::IPPacket pkt{std::move(buf)};
if (!pkt.Load(buf.underlying)) if (pkt.empty())
return false; return false;
if (pkt.IsV6() && m_Parent->SupportsV6()) if (pkt.IsV6() && m_Parent->SupportsV6())
{ {
huint128_t dst; huint128_t dst;
@ -152,24 +153,19 @@ namespace llarp
{ {
return false; return false;
} }
m_UpstreamQueue.emplace(pkt, counter); m_TxRate += pkt.size();
m_TxRate += buf.underlying.sz; m_UpstreamQueue.emplace(std::move(pkt), counter);
m_LastActive = m_Parent->Now(); m_LastActive = m_Parent->Now();
return true; return true;
} }
bool 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)); llarp::net::IPPacket pkt{std::move(buf)};
std::copy_n(buf.underlying.base, pkt.sz, pkt.buf); if (pkt.empty())
}
else
{
if (!pkt.Load(buf.underlying))
return false; return false;
huint128_t src; huint128_t src;
@ -181,11 +177,11 @@ namespace llarp
pkt.UpdateIPv6Address(src, m_IP); pkt.UpdateIPv6Address(src, m_IP);
else else
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP))); pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP)));
buf = pkt.steal();
} }
const auto _pktbuf = pkt.ConstBuffer();
auto& pktbuf = _pktbuf.underlying;
const uint8_t queue_idx = pktbuf.sz / llarp::routing::ExitPadSize; const uint8_t queue_idx = buf.size() / llarp::routing::ExitPadSize;
if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end()) if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end())
m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{}); m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{});
auto& queue = m_DownstreamQueues.at(queue_idx); auto& queue = m_DownstreamQueues.at(queue_idx);
@ -193,17 +189,17 @@ namespace llarp
{ {
queue.emplace_back(); queue.emplace_back();
queue.back().protocol = type; queue.back().protocol = type;
return queue.back().PutBuffer(pktbuf, m_Counter++); return queue.back().PutBuffer(std::move(buf), m_Counter++);
} }
auto& msg = queue.back(); auto& msg = queue.back();
if (msg.Size() + pktbuf.sz > llarp::routing::ExitPadSize) if (msg.Size() + buf.size() > llarp::routing::ExitPadSize)
{ {
queue.emplace_back(); queue.emplace_back();
queue.back().protocol = type; queue.back().protocol = type;
return queue.back().PutBuffer(pktbuf, m_Counter++); return queue.back().PutBuffer(std::move(buf), m_Counter++);
} }
msg.protocol = type; msg.protocol = type;
return msg.PutBuffer(pktbuf, m_Counter++); return msg.PutBuffer(std::move(buf), m_Counter++);
} }
bool bool
@ -212,7 +208,8 @@ namespace llarp
// flush upstream queue // flush upstream queue
while (m_UpstreamQueue.size()) 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(); m_UpstreamQueue.pop();
} }
// flush downstream queue // flush downstream queue

@ -58,7 +58,7 @@ namespace llarp
/// queue traffic from service node / internet to be transmitted /// queue traffic from service node / internet to be transmitted
bool bool
QueueInboundTraffic(ManagedBuffer buff, service::ProtocolType t); QueueInboundTraffic(std::vector<byte_t> data, service::ProtocolType t);
/// flush inbound and outbound traffic queues /// flush inbound and outbound traffic queues
bool bool
@ -68,7 +68,7 @@ namespace llarp
/// does ip rewrite here /// does ip rewrite here
bool bool
QueueOutboundTraffic( 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 /// update local path id and cascade information to parent
/// return true if success /// return true if success
@ -122,7 +122,7 @@ namespace llarp
struct UpstreamBuffer 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; llarp::net::IPPacket pkt;

@ -213,8 +213,8 @@ namespace llarp
if (m_WritePacket) if (m_WritePacket)
{ {
llarp::net::IPPacket pkt; llarp::net::IPPacket pkt{buf.view()};
if (!pkt.Load(buf)) if (pkt.empty())
return false; return false;
m_LastUse = m_router->Now(); m_LastUse = m_router->Now();
m_Downstream.emplace(counter, pkt); m_Downstream.emplace(counter, pkt);
@ -235,9 +235,7 @@ namespace llarp
BaseSession::QueueUpstreamTraffic( BaseSession::QueueUpstreamTraffic(
llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t) llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t)
{ {
const auto pktbuf = pkt.ConstBuffer(); auto& queue = m_Upstream[pkt.size() / N];
const llarp_buffer_t& buf = pktbuf;
auto& queue = m_Upstream[buf.sz / N];
// queue overflow // queue overflow
if (queue.size() >= MaxUpstreamQueueLength) if (queue.size() >= MaxUpstreamQueueLength)
return false; return false;
@ -245,18 +243,18 @@ namespace llarp
{ {
queue.emplace_back(); queue.emplace_back();
queue.back().protocol = t; queue.back().protocol = t;
return queue.back().PutBuffer(buf, m_Counter++); return queue.back().PutBuffer(std::move(pkt), m_Counter++);
} }
auto& back = queue.back(); auto& back = queue.back();
// pack to nearest N // pack to nearest N
if (back.Size() + buf.sz > N) if (back.Size() + pkt.size() > N)
{ {
queue.emplace_back(); queue.emplace_back();
queue.back().protocol = t; queue.back().protocol = t;
return queue.back().PutBuffer(buf, m_Counter++); return queue.back().PutBuffer(std::move(pkt), m_Counter++);
} }
back.protocol = t; back.protocol = t;
return back.PutBuffer(buf, m_Counter++); return back.PutBuffer(std::move(pkt), m_Counter++);
} }
bool bool
@ -333,7 +331,7 @@ namespace llarp
while (m_Downstream.size()) while (m_Downstream.size())
{ {
if (m_WritePacket) if (m_WritePacket)
m_WritePacket(m_Downstream.top().second.ConstBuffer()); m_WritePacket(const_cast<net::IPPacket&>(m_Downstream.top().second).steal());
m_Downstream.pop(); m_Downstream.pop();
} }
} }
@ -369,8 +367,8 @@ namespace llarp
void void
SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
{ {
net::IPPacket pkt; net::IPPacket pkt{buf.view()};
if (not pkt.Load(buf)) if (pkt.empty())
return; return;
pkt.ZeroAddresses(); pkt.ZeroAddresses();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t); QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t);
@ -379,9 +377,10 @@ namespace llarp
void void
ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t) ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
{ {
net::IPPacket pkt; net::IPPacket pkt{buf.view()};
if (not pkt.Load(buf)) if (pkt.empty())
return; return;
pkt.ZeroSourceAddress(); pkt.ZeroSourceAddress();
QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t); QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t);
} }

@ -109,7 +109,7 @@ namespace llarp
{ {
if (not itr->second->LooksDead(Now())) if (not itr->second->LooksDead(Now()))
{ {
if (itr->second->QueueInboundTraffic(ManagedBuffer{payload}, type)) if (itr->second->QueueInboundTraffic(payload.copy(), type))
return true; return true;
} }
} }
@ -117,10 +117,10 @@ namespace llarp
if (not m_Router->PathToRouterAllowed(*rid)) if (not m_Router->PathToRouterAllowed(*rid))
return false; return false;
ObtainSNodeSession(*rid, [data = payload.copy(), type](auto session) { ObtainSNodeSession(*rid, [pkt = payload.copy(), type](auto session) mutable {
if (session and session->IsReady()) if (session and session->IsReady())
{ {
session->SendPacketToRemote(data, type); session->SendPacketToRemote(std::move(pkt), type);
} }
}); });
} }
@ -209,7 +209,7 @@ namespace llarp
bool bool
ExitEndpoint::MaybeHookDNS( ExitEndpoint::MaybeHookDNS(
std::weak_ptr<dns::PacketSource_Base> source, std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query, const dns::Message& query,
const SockAddr& to, const SockAddr& to,
const SockAddr& from) const SockAddr& from)
@ -374,20 +374,24 @@ namespace llarp
{ {
while (not m_InetToNetwork.empty()) while (not m_InetToNetwork.empty())
{ {
net::IPPacket pkt{m_InetToNetwork.top()}; auto& top = m_InetToNetwork.top();
m_InetToNetwork.pop();
PubKey pk; // get a session by public key
std::optional<PubKey> maybe_pk;
{ {
auto itr = m_IPToKey.find(pkt.dstv6()); auto itr = m_IPToKey.find(top.dstv6());
if (itr == m_IPToKey.end()) if (itr != m_IPToKey.end())
{ maybe_pk = itr->second;
// drop
LogWarn(Name(), " dropping packet, has no session at ", pkt.dstv6());
continue;
}
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 // check if this key is a service node
if (m_SNodeKeys.count(pk)) if (m_SNodeKeys.count(pk))
{ {
@ -397,13 +401,14 @@ namespace llarp
auto itr = m_SNodeSessions.find(pk); auto itr = m_SNodeSessions.find(pk);
if (itr != m_SNodeSessions.end()) 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; continue;
} }
} }
auto tryFlushingTraffic = [&](exit::Endpoint* const ep) -> bool { auto tryFlushingTraffic =
if (!ep->QueueInboundTraffic( [this, buf = std::move(buf), pk](exit::Endpoint* const ep) -> bool {
ManagedBuffer{pkt.Buffer()}, service::ProtocolType::TrafficV4)) if (!ep->QueueInboundTraffic(buf, service::ProtocolType::TrafficV4))
{ {
LogWarn( LogWarn(
Name(), Name(),
@ -451,13 +456,14 @@ namespace llarp
m_IPToKey[ip] = us; m_IPToKey[ip] = us;
m_IPActivity[ip] = std::numeric_limits<llarp_time_t>::max(); m_IPActivity[ip] = std::numeric_limits<llarp_time_t>::max();
m_SNodeKeys.insert(us); m_SNodeKeys.insert(us);
if (m_ShouldInitTun) if (m_ShouldInitTun)
{ {
vpn::InterfaceInfo info; vpn::InterfaceInfo info;
info.ifname = m_ifname; 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) if (not m_NetIf)
{ {
llarp::LogError("Could not create interface"); llarp::LogError("Could not create interface");
@ -471,7 +477,12 @@ namespace llarp
} }
GetRouter()->loop()->add_ticker([this] { Flush(); }); 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(); m_Resolver->Start();
#endif
} }
return true; return true;
} }
@ -596,7 +607,7 @@ namespace llarp
rc.srvRecords.clear(); rc.srvRecords.clear();
for (auto& record : srvRecords) for (auto& record : srvRecords)
rc.srvRecords.emplace_back(record); 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; rc.version = 1;
return rc; return rc;
}); });
@ -651,8 +662,8 @@ namespace llarp
bool bool
ExitEndpoint::QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from) ExitEndpoint::QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from)
{ {
net::IPPacket pkt; net::IPPacket pkt{buf.view()};
if (!pkt.Load(buf)) if (pkt.empty())
return false; return false;
// rewrite ip // rewrite ip
if (m_UseV6) if (m_UseV6)
@ -708,6 +719,9 @@ namespace llarp
return true; return true;
} }
*/ */
m_DNSConf = dnsConfig;
if (networkConfig.m_endpointType == "null") if (networkConfig.m_endpointType == "null")
{ {
m_ShouldInitTun = false; m_ShouldInitTun = false;
@ -743,7 +757,6 @@ namespace llarp
return llarp::SockAddr{ifaddr, huint16_t{port}}; return llarp::SockAddr{ifaddr, huint16_t{port}};
}); });
} }
m_Resolver = std::make_shared<dns::Server>(m_Router->loop(), dnsConfig, m_ifname);
} }
huint128_t huint128_t

@ -32,7 +32,7 @@ namespace llarp
bool bool
MaybeHookDNS( MaybeHookDNS(
std::weak_ptr<dns::PacketSource_Base> source, std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query, const dns::Message& query,
const SockAddr& to, const SockAddr& to,
const SockAddr& from) override; const SockAddr& from) override;
@ -245,6 +245,7 @@ namespace llarp
/// internet to llarp packet queue /// internet to llarp packet queue
PacketQueue_t m_InetToNetwork; PacketQueue_t m_InetToNetwork;
bool m_UseV6; bool m_UseV6;
DnsConfig m_DNSConf;
}; };
} // namespace handlers } // namespace handlers
} // namespace llarp } // namespace llarp

@ -17,7 +17,7 @@ namespace llarp::handlers
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) { , m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) {
var::visit( var::visit(
[&pkt](auto&& from) { [&pkt](auto&& from) {
LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes"); LogError("unhandled traffic from: ", from, " of ", pkt.size(), " bytes");
}, },
from); from);
}}} }}}

@ -25,8 +25,7 @@
#include <llarp/rpc/endpoint_rpc.hpp> #include <llarp/rpc/endpoint_rpc.hpp>
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#include <llarp/dns/srv_data.hpp> #include <llarp/dns/srv_data.hpp>
#include <llarp/constants/platform.hpp> #include <llarp/constants/net.hpp>
#include <llarp/constants/platform.hpp> #include <llarp/constants/platform.hpp>
#include <oxenc/bt.h> #include <oxenc/bt.h>
@ -37,7 +36,7 @@ namespace llarp
{ {
bool bool
TunEndpoint::MaybeHookDNS( TunEndpoint::MaybeHookDNS(
std::weak_ptr<dns::PacketSource_Base> source, std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query, const dns::Message& query,
const SockAddr& to, const SockAddr& to,
const SockAddr& from) const SockAddr& from)
@ -46,21 +45,25 @@ namespace llarp
return false; return false;
auto job = std::make_shared<dns::QueryJob>(source, query, to, from); 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(); job->Cancel();
return true; 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 /// Intercepts DNS IP packets on platforms where binding to a low port isn't viable.
// because of OS restrictions, but a tun interface *is* available. /// (windows/macos/ios/android ... aka everything that is not linux... funny that)
class DnsInterceptor : public dns::PacketSource_Base class DnsInterceptor : public dns::PacketSource_Base
{ {
public: std::function<void(net::IPPacket)> m_Reply;
TunEndpoint* const m_Endpoint; net::ipaddr_t m_OurIP;
llarp::DnsConfig m_Config; llarp::DnsConfig m_Config;
explicit DnsInterceptor(TunEndpoint* ep, llarp::DnsConfig conf) public:
: m_Endpoint{ep}, m_Config{conf} 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; virtual ~DnsInterceptor() = default;
@ -68,17 +71,11 @@ namespace llarp
void void
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
{ {
const auto pkt = net::IPPacket::UDP( auto pkt = net::IPPacket::make_udp(from, to, std::move(buf));
from.getIPv4(),
ToNet(huint16_t{from.getPort()}), if (pkt.empty())
to.getIPv4(),
ToNet(huint16_t{to.getPort()}),
buf);
if (pkt.sz == 0)
return; return;
m_Endpoint->HandleWriteIPPacket( m_Reply(std::move(pkt));
pkt.ConstBuffer(), net::ExpandV4(from.asIPv4()), net::ExpandV4(to.asIPv4()), 0);
} }
void void
@ -90,92 +87,131 @@ namespace llarp
return std::nullopt; return std::nullopt;
} }
#ifdef ANDROID
bool bool
WouldLoop(const SockAddr&, const SockAddr&) const override WouldLoop(const SockAddr& to, const SockAddr& from) const override
{ {
#ifdef __APPLE__
(void)from;
// DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data
// through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual
// upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver
// won't work for us. However when active the mac also only queries the main tunnel IP for
// DNS, so we consider anything else to be upstream-bound DNS to let it through the tunnel.
return to.asIPv6() != m_OurIP();
#else
if (auto maybe_addr = m_Config.m_QueryBind)
{
const auto& addr = *maybe_addr;
// omit traffic to and from our dns socket
return addr == to or addr == from;
}
return false; return false;
}
#endif #endif
#ifdef __APPLE__
// DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data
// through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual
// upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver,
// above, won't work for us). However when active the mac also only queries the main tunnel
// IP for DNS, so we consider anything else to be upstream-bound DNS to let it through the
// tunnel.
bool
WouldLoop(const SockAddr& to, const SockAddr&) const override
{
return to.asIPv6() != m_Endpoint->GetIfAddr();
} }
#endif
}; };
#if defined(ANDROID) || defined(__APPLE__)
class TunDNS : public dns::Server class TunDNS : public dns::Server
{ {
std::optional<SockAddr> m_QueryBind;
net::ipaddr_t m_OurIP;
TunEndpoint* const m_Endpoint; TunEndpoint* const m_Endpoint;
public: public:
std::weak_ptr<dns::PacketSource_Base> PacketSource; std::shared_ptr<dns::PacketSource_Base> PacketSource;
virtual ~TunDNS() = default; virtual ~TunDNS() = default;
explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf) 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> std::shared_ptr<dns::PacketSource_Base>
MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override 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; PacketSource = ptr;
return 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) TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent)
: service::Endpoint{r, 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)); }); [this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
} }
void void
TunEndpoint::SetupDNS() TunEndpoint::SetupDNS()
{ {
#if defined(ANDROID) || defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION)) const auto& info = GetVPNInterface()->Info();
auto dns = std::make_shared<TunDNS>(this, m_DnsConfig); if (m_DnsConfig.m_raw_dns)
m_DNS = dns; {
m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) { auto dns = std::make_shared<TunDNS>(this, m_DnsConfig);
const size_t ip_header_size = (pkt.Header()->ihl * 4); if (auto vpn = Router()->GetVPNPlatform())
{
const uint8_t* ptr = pkt.buf + ip_header_size; // get the first local address we know of
const auto dst = ToNet(pkt.dstv4()); std::optional<SockAddr> localaddr;
const auto src = ToNet(pkt.srcv4()); for (auto res : dns->GetAllResolvers())
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)}}; if (localaddr)
continue;
OwnedBuffer buf{pkt.sz - (8 + ip_header_size)}; if (auto ptr = res.lock())
std::copy_n(ptr + 8, buf.sz, buf.buf.get()); localaddr = ptr->GetLocalAddr();
}
if (dns->MaybeHandlePacket(dns->PacketSource, raddr, laddr, std::move(buf))) if (platform::is_windows)
return; {
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;
HandleGotUserPacket(std::move(pkt)); m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) {
}); auto dns_pkt_src = dns->PacketSource;
#else if (const auto& reply = pkt.reply)
m_DNS = std::make_shared<dns::Server>(Loop(), m_DnsConfig, GetIfName()); dns_pkt_src = std::make_shared<dns::PacketSource_Wrapper>(dns_pkt_src, reply);
#endif 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, info.index);
if (m_RawDNS)
m_RawDNS->Start();
m_DNS->AddResolver(weak_from_this()); m_DNS->AddResolver(weak_from_this());
m_DNS->Start(); m_DNS->Start();
} }
@ -969,45 +1005,39 @@ namespace llarp
} }
vpn::InterfaceInfo info; vpn::InterfaceInfo info;
info.addrs.emplace(m_OurRange); info.addrs.emplace_back(m_OurRange);
if (m_BaseV6Address) if (m_BaseV6Address)
{ {
IPRange v6range = m_OurRange; IPRange v6range = m_OurRange;
v6range.addr = (*m_BaseV6Address) | m_OurRange.addr; v6range.addr = (*m_BaseV6Address) | m_OurRange.addr;
LogInfo(Name(), " using v6 range: ", v6range); LogInfo(Name(), " using v6 range: ", v6range);
info.addrs.emplace(v6range, AF_INET6); info.addrs.emplace_back(v6range, AF_INET6);
} }
info.ifname = m_IfName; 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..."); LogInfo(Name(), " setting up network...");
try try
{ {
m_NetIf = Router()->GetVPNPlatform()->ObtainInterface(std::move(info), Router()); m_NetIf = Router()->GetVPNPlatform()->CreateInterface(std::move(info), Router());
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
LogError(Name(), " failed to set up network interface: ", ex.what()); LogError(Name(), " failed to set up network interface: ", ex.what());
}
if (not m_NetIf)
{
LogError(Name(), " failed to obtain network interface");
return false; return false;
} }
m_IfName = m_NetIf->IfName();
m_IfName = m_NetIf->Info().ifname;
LogInfo(Name(), " got network interface ", m_IfName); LogInfo(Name(), " got network interface ", m_IfName);
if (not Router()->loop()->add_network_interface(m_NetIf, [this](net::IPPacket pkt) { auto handle_packet = [netif = m_NetIf, pkt_router = m_PacketRouter](auto pkt) {
m_PacketRouter->HandleIPPacket(std::move(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"); LogError(Name(), " failed to add network interface");
return false; 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); return HasAddress(ourAddr);
} }
@ -1060,9 +1091,13 @@ namespace llarp
bool bool
TunEndpoint::Stop() TunEndpoint::Stop()
{ {
// stop vpn tunnel
if (m_NetIf)
m_NetIf->Stop();
if (m_RawDNS)
m_RawDNS->Stop();
// save address map if applicable // save address map if applicable
#ifndef ANDROID if (m_PersistAddrMapFile and not platform::is_android)
if (m_PersistAddrMapFile)
{ {
const auto& file = *m_PersistAddrMapFile; const auto& file = *m_PersistAddrMapFile;
LogInfo(Name(), " saving address map to ", file); LogInfo(Name(), " saving address map to ", file);
@ -1082,7 +1117,6 @@ namespace llarp
maybe->write(data.data(), data.size()); maybe->write(data.data(), data.size());
} }
} }
#endif
if (m_DNS) if (m_DNS)
m_DNS->Stop(); m_DNS->Stop();
return llarp::service::Endpoint::Stop(); return llarp::service::Endpoint::Stop();
@ -1096,12 +1130,18 @@ namespace llarp
// is it already mapped? return the mapping // is it already mapped? return the mapping
if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end()) if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end())
return itr->second; return itr->second;
const auto& net = m_router->Net();
const bool is_bogon = net.IsBogonIP(ip);
// build up our candidates to choose // build up our candidates to choose
std::unordered_set<service::Address> candidates; std::unordered_set<service::Address> candidates;
for (const auto& entry : m_ExitMap.FindAllEntries(ip)) for (const auto& entry : m_ExitMap.FindAllEntries(ip))
{ {
// make sure it is allowed by the range if the ip is a bogon // in the event the exit's range is a bogon range, make sure the ip is located in that range
if (not IsBogon(ip) or entry.first.BogonContains(ip)) // 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); candidates.emplace(entry.second);
} }
// no candidates? bail. // no candidates? bail.
@ -1163,13 +1203,20 @@ namespace llarp
return; return;
} }
std::function<void(void)> extra_cb;
if (not HasFlowToService(addr))
{
extra_cb = [poker = Router()->routePoker()]() { poker->Up(); };
}
pkt.ZeroSourceAddress(); pkt.ZeroSourceAddress();
MarkAddressOutbound(addr); MarkAddressOutbound(addr);
EnsurePathToService( EnsurePathToService(
addr, addr,
[pkt, this](service::Address addr, service::OutboundContext* ctx) { [pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) {
if (ctx) if (ctx)
{ {
if (extra_cb)
extra_cb();
ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit); ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit);
Router()->TriggerPump(); Router()->TriggerPump();
return; return;

@ -2,22 +2,20 @@
#include <llarp/dns/server.hpp> #include <llarp/dns/server.hpp>
#include <llarp/ev/ev.hpp> #include <llarp/ev/ev.hpp>
#include <llarp/ev/vpn.hpp>
#include <llarp/net/ip.hpp> #include <llarp/net/ip.hpp>
#include <llarp/net/ip_packet.hpp> #include <llarp/net/ip_packet.hpp>
#include <llarp/net/net.hpp> #include <llarp/net/net.hpp>
#include <llarp/service/endpoint.hpp> #include <llarp/service/endpoint.hpp>
#include <llarp/service/protocol_type.hpp>
#include <llarp/util/priority_queue.hpp>
#include <llarp/util/thread/threading.hpp> #include <llarp/util/thread/threading.hpp>
#include <llarp/vpn/packet_router.hpp> #include <llarp/vpn/packet_router.hpp>
#include <llarp/vpn/platform.hpp>
#include <future> #include <future>
#include <type_traits> #include <type_traits>
#include <variant> #include <variant>
#include <llarp/service/protocol_type.hpp>
#include <llarp/util/priority_queue.hpp>
namespace llarp namespace llarp
{ {
namespace handlers namespace handlers
@ -29,6 +27,12 @@ namespace llarp
TunEndpoint(AbstractRouter* r, llarp::service::Context* parent); TunEndpoint(AbstractRouter* r, llarp::service::Context* parent);
~TunEndpoint() override; ~TunEndpoint() override;
vpn::NetworkInterface*
GetVPNInterface() override
{
return m_NetIf.get();
}
int int
Rank() const override Rank() const override
{ {
@ -43,7 +47,7 @@ namespace llarp
bool bool
MaybeHookDNS( MaybeHookDNS(
std::weak_ptr<dns::PacketSource_Base> source, std::shared_ptr<dns::PacketSource_Base> source,
const dns::Message& query, const dns::Message& query,
const SockAddr& to, const SockAddr& to,
const SockAddr& from) override; const SockAddr& from) override;
@ -306,7 +310,7 @@ namespace llarp
std::shared_ptr<vpn::NetworkInterface> m_NetIf; 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; std::optional<net::TrafficPolicy> m_TrafficPolicy;
/// ranges we advetise as reachable /// ranges we advetise as reachable
@ -316,6 +320,9 @@ namespace llarp
/// a file to load / store the ephemeral address map to /// a file to load / store the ephemeral address map to
std::optional<fs::path> m_PersistAddrMapFile; std::optional<fs::path> m_PersistAddrMapFile;
/// for raw packet dns
std::shared_ptr<vpn::I_Packet_IO> m_RawDNS;
}; };
} // namespace handlers } // namespace handlers

@ -954,7 +954,7 @@ extern "C"
dstport, dstport,
llarp_buffer_t{reinterpret_cast<const uint8_t*>(ptr), len}); llarp_buffer_t{reinterpret_cast<const uint8_t*>(ptr), len});
if (pkt.sz == 0) if (pkt.empty())
return EINVAL; return EINVAL;
std::promise<int> ret; std::promise<int> ret;
ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() { ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() {

@ -50,9 +50,19 @@ namespace llarp
fromSockAddr(const SockAddr& address); fromSockAddr(const SockAddr& address);
/// get this as an explicit v4 or explicit v6 /// get this as an explicit v4 or explicit v6
std::variant<nuint32_t, nuint128_t> net::ipaddr_t
IP() const; 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 std::string
ToString() const; 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); SockAddr addr(m_ipAddress);
const auto* addr6 = static_cast<const sockaddr_in6*>(addr); const auto* addr6 = static_cast<const sockaddr_in6*>(addr);
const uint8_t* raw = addr6->sin6_addr.s6_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 std::string

@ -19,14 +19,12 @@ namespace llarp
/// As a convenience, it can produce a SockAddr for dealing with network libraries which depend /// 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 /// sockaddr structs. However, it does not keep this as a member variable and isn't responsible
/// for its lifetime/memory/etc. /// for its lifetime/memory/etc.
/// struct [[deprecated("use llarp::SockAddr instead")]] IpAddress
/// TODO: IPv6 is not currently supported.
struct IpAddress
{ {
/// Empty constructor. /// Empty constructor.
IpAddress() = default; IpAddress() = default;
/// move construtor /// move construtor
IpAddress(IpAddress&&) = default; IpAddress(IpAddress &&) = default;
/// copy construct /// copy construct
IpAddress(const IpAddress&); IpAddress(const IpAddress&);
@ -56,80 +54,64 @@ namespace llarp
/// @param addr is an SockAddr to initialize from. /// @param addr is an SockAddr to initialize from.
IpAddress(const SockAddr& addr); IpAddress(const SockAddr& addr);
IpAddress& IpAddress& operator=(const sockaddr& other);
operator=(const sockaddr& other);
/// move assignment /// move assignment
IpAddress& IpAddress& operator=(IpAddress&& other);
operator=(IpAddress&& other);
/// copy assignment /// copy assignment
IpAddress& IpAddress& operator=(const IpAddress& other);
operator=(const IpAddress& other);
/// Return the port. Returns -1 if no port has been provided. /// Return the port. Returns -1 if no port has been provided.
/// ///
/// @return the port, if present /// @return the port, if present
std::optional<uint16_t> std::optional<uint16_t> getPort() const;
getPort() const;
/// Return true if we have a port set otherwise return false /// Return true if we have a port set otherwise return false
bool bool hasPort() const;
hasPort() const;
/// Set the port. /// Set the port.
/// ///
/// @param port /// @param port
void void setPort(std::optional<uint16_t> port);
setPort(std::optional<uint16_t> port);
/// Set the IP address. Follows the same logic as the constructor with the same signature, but /// 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. /// doesn't overwrite the port if the port isn't present in the string.
void void setAddress(std::string_view str);
setAddress(std::string_view str); void setAddress(std::string_view str, std::optional<uint16_t> port);
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) /// 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 /// TODO: could return an int (e.g. 4 or 6) or an enum
/// ///
/// @return true if this is an IPv4 address, false otherwise /// @return true if this is an IPv4 address, false otherwise
bool bool isIPv4();
isIPv4();
/// Returns true if this represents a valid IpAddress, false otherwise. /// Returns true if this represents a valid IpAddress, false otherwise.
/// ///
/// @return whether or not this IpAddress is empty /// @return whether or not this IpAddress is empty
bool bool isEmpty() const;
isEmpty() const;
/// Creates an instance of SockAddr representing this IpAddress. /// Creates an instance of SockAddr representing this IpAddress.
/// ///
/// @return an instance of a SockAddr created from this IpAddress /// @return an instance of a SockAddr created from this IpAddress
SockAddr SockAddr createSockAddr() const;
createSockAddr() const;
/// Returns true if this IpAddress is a bogon, false otherwise /// Returns true if this IpAddress is a bogon, false otherwise
/// ///
/// @return whether or not this IpAddress is a bogon /// @return whether or not this IpAddress is a bogon
bool bool isBogon() const;
isBogon() const;
/// Returns a string representing this IpAddress /// Returns a string representing this IpAddress
/// ///
/// @return string representation of this IpAddress /// @return string representation of this IpAddress
std::string std::string ToString() const;
ToString() const;
std::string std::string toHost() const;
toHost() const;
huint32_t huint32_t toIP() const;
toIP() const;
huint128_t huint128_t toIP6() const;
toIP6() const;
// TODO: other utility functions left over from Addr which may be useful // TODO: other utility functions left over from Addr which may be useful
// IsBogon() const; // IsBogon() const;
@ -137,11 +119,9 @@ namespace llarp
// std::hash // std::hash
// to string / stream / etc // to string / stream / etc
bool bool operator<(const IpAddress& other) const;
operator<(const IpAddress& other) const;
bool bool operator==(const IpAddress& other) const;
operator==(const IpAddress& other) const;
private: private:
bool m_empty = true; bool m_empty = true;

@ -1,6 +1,6 @@
#include "ip_packet.hpp" #include "ip_packet.hpp"
#include "ip.hpp" #include "ip.hpp"
#include <llarp/constants/net.hpp>
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <llarp/util/mem.hpp> #include <llarp/util/mem.hpp>
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
@ -75,7 +75,6 @@ namespace llarp::net
} }
throw std::invalid_argument{"no such ip protocol: '" + data + "'"}; throw std::invalid_argument{"no such ip protocol: '" + data + "'"};
} }
inline static uint32_t* inline static uint32_t*
in6_uint32_ptr(in6_addr& addr) in6_uint32_ptr(in6_addr& addr)
{ {
@ -106,30 +105,54 @@ namespace llarp::net
return ExpandV4(dstv4()); return ExpandV4(dstv4());
} }
bool IPPacket::IPPacket(byte_view_t view)
IPPacket::Load(const llarp_buffer_t& pkt) {
if (view.size() < MinSize)
{
_buf.resize(0);
return;
}
_buf.resize(view.size());
std::copy_n(view.data(), size(), data());
}
IPPacket::IPPacket(size_t sz)
{
if (sz and sz < MinSize)
throw std::invalid_argument{"buffer size is too small to hold an ip packet"};
_buf.resize(sz);
}
SockAddr
IPPacket::src() const
{
auto port = SrcPort();
if (IsV4())
return SockAddr{ToNet(srcv4()), *port};
else
return SockAddr{ToNet(srcv6()), *port};
}
SockAddr
IPPacket::dst() const
{ {
if (pkt.sz > sizeof(buf) or pkt.sz == 0) auto port = DstPort();
return false; if (IsV4())
sz = pkt.sz; return SockAddr{ToNet(dstv4()), *port};
std::copy_n(pkt.base, sz, buf); else
return true; return SockAddr{ToNet(dstv6()), *port};
} }
ManagedBuffer IPPacket::IPPacket(std::vector<byte_t>&& stolen) : _buf{stolen}
IPPacket::ConstBuffer() const
{ {
const byte_t* ptr = buf; if (size() < MinSize)
llarp_buffer_t b(ptr, sz); _buf.resize(0);
return ManagedBuffer(b);
} }
ManagedBuffer byte_view_t
IPPacket::Buffer() IPPacket::view() const
{ {
byte_t* ptr = buf; return byte_view_t{data(), size()};
llarp_buffer_t b(ptr, sz);
return ManagedBuffer(b);
} }
std::optional<nuint16_t> std::optional<nuint16_t>
@ -139,7 +162,7 @@ namespace llarp::net
{ {
case IPProtocol::TCP: case IPProtocol::TCP:
case IPProtocol::UDP: 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: default:
return std::nullopt; return std::nullopt;
} }
@ -152,7 +175,7 @@ namespace llarp::net
{ {
case IPProtocol::TCP: case IPProtocol::TCP:
case IPProtocol::UDP: 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: default:
return std::nullopt; return std::nullopt;
} }
@ -397,7 +420,8 @@ namespace llarp::net
auto oSrcIP = nuint32_t{hdr->saddr}; auto oSrcIP = nuint32_t{hdr->saddr};
auto oDstIP = nuint32_t{hdr->daddr}; auto oDstIP = nuint32_t{hdr->daddr};
auto* buf = data();
auto sz = size();
// L4 checksum // L4 checksum
auto ihs = size_t(hdr->ihl * 4); auto ihs = size_t(hdr->ihl * 4);
if (ihs <= sz) if (ihs <= sz)
@ -435,7 +459,7 @@ namespace llarp::net
IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional<nuint32_t> flowlabel) IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional<nuint32_t> flowlabel)
{ {
const size_t ihs = 4 + 4 + 16 + 16; const size_t ihs = 4 + 4 + 16 + 16;
const auto sz = size();
// XXX should've been checked at upper level? // XXX should've been checked at upper level?
if (sz <= ihs) if (sz <= ihs)
return; return;
@ -459,11 +483,11 @@ namespace llarp::net
const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr); const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr);
// TODO IPv6 header options // TODO IPv6 header options
auto pld = buf + ihs; auto* pld = data() + ihs;
auto psz = sz - ihs; auto psz = sz - ihs;
size_t fragoff = 0; size_t fragoff = 0;
auto nextproto = hdr->proto; auto nextproto = hdr->protocol;
for (;;) for (;;)
{ {
switch (nextproto) switch (nextproto)
@ -554,31 +578,27 @@ namespace llarp::net
if (IsV4()) if (IsV4())
{ {
constexpr auto icmp_Header_size = 8; constexpr auto icmp_Header_size = 8;
constexpr auto ip_Header_size = 20; auto ip_Header_size = Header()->ihl * 4;
net::IPPacket pkt{}; auto pkt_size = (icmp_Header_size + ip_Header_size) * 2;
auto* pkt_Header = pkt.Header(); net::IPPacket pkt{static_cast<size_t>(pkt_size)};
auto* pkt_Header = pkt.Header();
pkt_Header->version = 4; pkt_Header->version = 4;
pkt_Header->ihl = 0x05; pkt_Header->ihl = 0x05;
pkt_Header->tos = 0; pkt_Header->tos = 0;
pkt_Header->check = 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->saddr = Header()->daddr;
pkt_Header->daddr = Header()->saddr; pkt_Header->daddr = Header()->saddr;
pkt_Header->protocol = 1; // ICMP pkt_Header->protocol = 1; // ICMP
pkt_Header->ttl = 1; pkt_Header->ttl = Header()->ttl;
pkt_Header->frag_off = htons(0b0100000000000000); 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; 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' uint8_t* icmp_begin = itr; // type 'destination unreachable'
*itr++ = 3; *itr++ = 3;
// code 'Destination host unknown error' // code 'Destination host unknown error'
*itr++ = 7; *itr++ = 7;
// checksum + unused // checksum + unused
oxenc::write_host_as_big<uint32_t>(0, itr); oxenc::write_host_as_big<uint32_t>(0, itr);
@ -588,14 +608,13 @@ namespace llarp::net
oxenc::write_host_as_big<uint16_t>(1500, itr); oxenc::write_host_as_big<uint16_t>(1500, itr);
itr += 2; itr += 2;
// copy ip header and first 8 bytes of datagram for icmp rject // copy ip header and first 8 bytes of datagram for icmp rject
std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr); std::copy_n(data(), ip_Header_size + icmp_Header_size, itr);
itr += l4_PacketSize + l3_HeaderSize; itr += ip_Header_size + icmp_Header_size;
// calculate checksum of ip header // 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); const auto icmp_size = std::distance(icmp_begin, itr);
// calculate icmp checksum // calculate icmp checksum
*checksum = ipchksum(icmp_begin, icmp_size); *checksum = ipchksum(icmp_begin, icmp_size);
pkt.sz = ntohs(pkt_Header->tot_len);
return pkt; return pkt;
} }
return std::nullopt; return std::nullopt;
@ -614,56 +633,85 @@ namespace llarp::net
return std::nullopt; return std::nullopt;
// check for invalid size // check for invalid size
if (sz < (hdr->ihl * 4) + l4_HeaderSize) if (size() < (hdr->ihl * 4) + l4_HeaderSize)
return std::nullopt; return std::nullopt;
const uint8_t* ptr = buf + ((hdr->ihl * 4) + l4_HeaderSize); const uint8_t* ptr = data() + ((hdr->ihl * 4) + l4_HeaderSize);
return std::make_pair(reinterpret_cast<const char*>(ptr), std::distance(ptr, buf + sz)); return std::make_pair(reinterpret_cast<const char*>(ptr), std::distance(ptr, data() + size()));
} }
IPPacket namespace
IPPacket::UDP(
nuint32_t srcaddr,
nuint16_t srcport,
nuint32_t dstaddr,
nuint16_t dstport,
const llarp_buffer_t& buf)
{ {
net::IPPacket pkt; IPPacket
make_ip4_udp(
if (buf.sz + 28 > sizeof(pkt.buf)) net::ipv4addr_t srcaddr,
net::port_t srcport,
net::ipv4addr_t dstaddr,
net::port_t dstport,
std::vector<byte_t> udp_data)
{ {
pkt.sz = 0; constexpr auto pkt_overhead = constants::udp_header_bytes + constants::ip_header_min_bytes;
net::IPPacket pkt{udp_data.size() + pkt_overhead};
auto* hdr = pkt.Header();
pkt.data()[1] = 0;
hdr->version = 4;
hdr->ihl = 5;
hdr->tot_len = htons(pkt_overhead + udp_data.size());
hdr->protocol = 0x11; // udp
hdr->ttl = 64;
hdr->frag_off = htons(0b0100000000000000);
hdr->saddr = srcaddr.n;
hdr->daddr = dstaddr.n;
// make udp packet
uint8_t* ptr = pkt.data() + constants::ip_header_min_bytes;
std::memcpy(ptr, &srcport.n, 2);
ptr += 2;
std::memcpy(ptr, &dstport.n, 2);
ptr += 2;
oxenc::write_host_as_big(
static_cast<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(udp_data.data(), udp_data.size(), ptr);
hdr->check = 0;
hdr->check = net::ipchksum(pkt.data(), 20);
return pkt; return pkt;
} }
auto* hdr = pkt.Header(); } // namespace
pkt.buf[1] = 0; IPPacket
hdr->version = 4; IPPacket::make_udp(
hdr->ihl = 5; net::ipaddr_t srcaddr,
hdr->tot_len = htons(buf.sz + 28); net::port_t srcport,
hdr->protocol = 0x11; // udp net::ipaddr_t dstaddr,
hdr->ttl = 64; net::port_t dstport,
hdr->frag_off = htons(0b0100000000000000); std::vector<byte_t> udp_data)
{
hdr->saddr = srcaddr.n; auto getfam = [](auto&& v) {
hdr->daddr = dstaddr.n; if (std::holds_alternative<net::ipv4addr_t>(v))
return AF_INET;
// make udp packet else if (std::holds_alternative<net::ipv6addr_t>(v))
uint8_t* ptr = pkt.buf + 20; return AF_INET6;
std::memcpy(ptr, &srcport.n, 2); else
ptr += 2; return AF_UNSPEC;
std::memcpy(ptr, &dstport.n, 2); };
ptr += 2; auto fam = getfam(srcaddr);
oxenc::write_host_as_big(static_cast<uint16_t>(buf.sz + 8), ptr); if (fam != getfam(dstaddr))
ptr += 2; return net::IPPacket{size_t{}};
oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum if (fam == AF_INET)
ptr += 2; {
std::copy_n(buf.base, buf.sz, ptr); return make_ip4_udp(
*std::get_if<net::ipv4addr_t>(&srcaddr),
hdr->check = 0; srcport,
hdr->check = net::ipchksum(pkt.buf, 20); *std::get_if<net::ipv4addr_t>(&dstaddr),
pkt.sz = 28 + buf.sz; dstport,
return pkt; std::move(udp_data));
}
// TODO: ipv6
return net::IPPacket{size_t{}};
} }
} // namespace llarp::net } // namespace llarp::net

@ -69,7 +69,7 @@ namespace llarp::net
} preamble; } preamble;
uint16_t payload_len; uint16_t payload_len;
uint8_t proto; uint8_t protocol;
uint8_t hoplimit; uint8_t hoplimit;
in6_addr srcaddr; in6_addr srcaddr;
in6_addr dstaddr; in6_addr dstaddr;
@ -109,34 +109,119 @@ namespace llarp::net
/// an Packet /// an Packet
struct IPPacket struct IPPacket
{ {
static constexpr size_t MaxSize = 1500; static constexpr size_t _max_size = 1500;
llarp_time_t timestamp; 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, UDP(nuint32_t srcaddr,
nuint16_t srcport, nuint16_t srcport,
nuint32_t dstaddr, nuint32_t dstaddr,
nuint16_t dstport, nuint16_t dstport,
const llarp_buffer_t& data); const llarp_buffer_t& data)
{
return make_udp(srcaddr, srcport, dstaddr, dstport, data.copy());
}
static IPPacket
make_udp(
net::ipaddr_t srcaddr,
net::port_t srcport,
net::ipaddr_t dstaddr,
net::port_t dstport,
std::vector<byte_t> udp_body);
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{}};
}
[[deprecated("deprecated because of llarp_buffer_t")]] inline bool
Load(const llarp_buffer_t& buf)
{
_buf = buf.copy();
if (size() >= MinSize)
return true;
_buf.resize(0);
return false;
}
ManagedBuffer [[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t
Buffer(); ConstBuffer() const
{
return llarp_buffer_t{_buf};
}
ManagedBuffer /// steal the underlying vector
ConstBuffer() const; inline std::vector<byte_t>
steal()
{
std::vector<byte_t> buf;
buf.resize(0);
std::swap(_buf, buf);
return buf;
}
bool inline byte_t*
Load(const llarp_buffer_t& buf); 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 struct CompareSize
{ {
bool bool
operator()(const IPPacket& left, const IPPacket& right) 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* inline ip_header*
Header() Header()
{ {
return reinterpret_cast<ip_header*>(&buf[0]); return reinterpret_cast<ip_header*>(data());
} }
inline const ip_header* inline const ip_header*
Header() const Header() const
{ {
return reinterpret_cast<const ip_header*>(&buf[0]); return reinterpret_cast<const ip_header*>(data());
} }
inline ipv6_header* inline ipv6_header*
HeaderV6() HeaderV6()
{ {
return reinterpret_cast<ipv6_header*>(&buf[0]); return reinterpret_cast<ipv6_header*>(data());
} }
inline const ipv6_header* inline const ipv6_header*
HeaderV6() const HeaderV6() const
{ {
return reinterpret_cast<const ipv6_header*>(&buf[0]); return reinterpret_cast<const ipv6_header*>(data());
} }
inline int inline int
@ -179,6 +264,15 @@ namespace llarp::net
return Header()->version; return Header()->version;
} }
inline byte_t
protocol() const
{
if (IsV4())
return Header()->protocol;
else
return HeaderV6()->protocol;
}
inline bool inline bool
IsV4() const IsV4() const
{ {
@ -226,6 +320,12 @@ namespace llarp::net
huint128_t huint128_t
dst4to6Lan() const; dst4to6Lan() const;
SockAddr
src() const;
SockAddr
dst() const;
/// get destination port if applicable /// get destination port if applicable
std::optional<nuint16_t> std::optional<nuint16_t>
DstPort() const; DstPort() const;
@ -238,6 +338,14 @@ namespace llarp::net
std::optional<std::pair<const char*, size_t>> std::optional<std::pair<const char*, size_t>>
L4Data() const; 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 void
UpdateIPv4Address(nuint32_t src, nuint32_t dst); 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 /// make an icmp unreachable reply packet based of this ip packet
std::optional<IPPacket> std::optional<IPPacket>
MakeICMPUnreachable() const; MakeICMPUnreachable() const;
std::function<void(net::IPPacket)> reply;
}; };
/// generate ip checksum /// generate ip checksum

@ -90,4 +90,15 @@ namespace llarp
return addr.ToString(); 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 } // namespace llarp

@ -9,10 +9,6 @@
namespace llarp namespace llarp
{ {
/// forward declare
bool
IsBogon(huint128_t ip);
struct IPRange struct IPRange
{ {
using Addr_t = huint128_t; using Addr_t = huint128_t;
@ -25,6 +21,12 @@ namespace llarp
: addr{std::move(address)}, netmask_bits{std::move(netmask)} : 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 static constexpr IPRange
FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask) FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask)
{ {
@ -42,8 +44,7 @@ namespace llarp
constexpr bool constexpr bool
IsV4() const IsV4() const
{ {
constexpr auto ipv4_map = IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)}; return V4MappedRange().Contains(addr);
return ipv4_map.Contains(addr);
} }
/// get address family /// get address family
@ -55,27 +56,6 @@ namespace llarp
return AF_INET6; 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 /// return the number of bits set in the hostmask
constexpr int constexpr int
HostmaskBits() const HostmaskBits() const
@ -147,6 +127,9 @@ namespace llarp
std::string std::string
BaseAddressString() const; BaseAddressString() const;
std::string
NetmaskString() const;
bool bool
FromString(std::string str); FromString(std::string str);

@ -9,6 +9,8 @@
#include <llarp/util/mem.hpp> #include <llarp/util/mem.hpp>
#include <llarp/util/bits.hpp> #include <llarp/util/bits.hpp>
#include "interface_info.hpp"
#include <functional> #include <functional>
#include <cstdlib> // for itoa #include <cstdlib> // for itoa
#include <vector> #include <vector>
@ -22,50 +24,73 @@
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <wspiapi.h> #include <wspiapi.h>
#define inet_aton(x, y) inet_pton(AF_INET, x, y)
#endif #endif
#ifndef _WIN32 #ifndef _WIN32
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
bool #include "bogon_ranges.hpp"
operator==(const sockaddr& a, const sockaddr& b);
bool
operator==(const sockaddr_in& a, const sockaddr_in& b);
bool namespace llarp
operator==(const sockaddr_in6& a, const sockaddr_in6& b); {
inline bool
bool operator==(const in_addr& a, const in_addr& b)
operator<(const sockaddr_in6& a, const sockaddr_in6& b); {
return memcmp(&a, &b, sizeof(in_addr)) == 0;
}
bool inline bool
operator<(const in6_addr& a, const in6_addr& b); operator==(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) == 0;
}
bool inline bool
operator==(const in6_addr& a, const in6_addr& b); operator==(const sockaddr_in& a, const sockaddr_in& b)
{
return a.sin_port == b.sin_port and a.sin_addr.s_addr == b.sin_addr.s_addr;
}
namespace llarp inline bool
{ operator==(const sockaddr_in6& a, const sockaddr_in6& b)
bool {
IsIPv4Bogon(const huint32_t& addr); return a.sin6_port == b.sin6_port and a.sin6_addr == b.sin6_addr;
}
inline bool inline bool
IsIPv4Bogon(const nuint32_t& addr) operator==(const sockaddr& a, const sockaddr& b)
{ {
return IsIPv4Bogon(ToHost(addr)); if (a.sa_family != b.sa_family)
return false;
switch (a.sa_family)
{
case AF_INET:
return reinterpret_cast<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;
}
} }
bool inline bool
IsBogon(const in6_addr& addr); operator<(const in_addr& a, const in_addr& b)
{
return memcmp(&a, &b, sizeof(in_addr)) < 0;
}
bool inline bool
IsBogon(const huint128_t addr); operator<(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) < 0;
}
bool inline bool
IsBogonRange(const in6_addr& host, const in6_addr& mask); 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 namespace net
{ {
@ -86,8 +111,27 @@ namespace llarp
virtual std::optional<SockAddr> virtual std::optional<SockAddr>
AllInterfaces(SockAddr pubaddr) const = 0; AllInterfaces(SockAddr pubaddr) const = 0;
virtual SockAddr inline SockAddr
Wildcard(int af = AF_INET) const = 0; 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 inline SockAddr
WildcardWithPort(port_t port, int af = AF_INET) const WildcardWithPort(port_t port, int af = AF_INET) const
@ -104,12 +148,24 @@ namespace llarp
HasInterfaceAddress(ipaddr_t ip) const = 0; HasInterfaceAddress(ipaddr_t ip) const = 0;
/// return true if ip is considered a loopback address /// return true if ip is considered a loopback address
virtual bool inline bool
IsLoopbackAddress(ipaddr_t ip) const = 0; 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 /// return true if ip is considered a wildcard address
virtual bool inline bool
IsWildcardAddress(ipaddr_t ip) const = 0; IsWildcardAddress(ipaddr_t ip) const
{
return var::visit([](auto&& ip) { return not ip.n; }, ip);
}
virtual std::optional<std::string> virtual std::optional<std::string>
GetBestNetIF(int af = AF_INET) const = 0; GetBestNetIF(int af = AF_INET) const = 0;
@ -143,11 +199,63 @@ namespace llarp
return std::nullopt; return std::nullopt;
} }
virtual bool inline bool
IsBogon(const SockAddr& addr) const = 0; 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> virtual std::optional<int>
GetInterfaceIndex(ipaddr_t ip) const = 0; GetInterfaceIndex(ipaddr_t ip) const = 0;
/// returns a vector holding all of our network interfaces
virtual std::vector<InterfaceInfo>
AllNetworkInterfaces() const = 0;
}; };
} // namespace net } // namespace net

@ -40,14 +40,6 @@ namespace llarp
// IPv4 mapped address live at ::ffff:0:0/96 // 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 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 namespace net
{ {
inline auto inline auto

@ -1,19 +1,9 @@
#include "net.hpp" #include "net.hpp"
#include "net_if.hpp" #include "net_if.hpp"
#include <stdexcept> #include <stdexcept>
#include <llarp/constants/platform.hpp> #include <llarp/constants/platform.hpp>
#ifdef ANDROID
#include <llarp/android/ifaddrs.h>
#endif
#ifndef _WIN32
#include <arpa/inet.h> #include <arpa/inet.h>
#ifndef ANDROID
#include <ifaddrs.h>
#endif
#endif
#include "ip.hpp" #include "ip.hpp"
#include "ip_range.hpp" #include "ip_range.hpp"
@ -23,116 +13,15 @@
#ifdef ANDROID #ifdef ANDROID
#include <llarp/android/ifaddrs.h> #include <llarp/android/ifaddrs.h>
#else #else
#ifdef _WIN32
#include <iphlpapi.h>
#include <llarp/win32/exception.hpp>
#else
#include <ifaddrs.h> #include <ifaddrs.h>
#endif #endif
#endif
#include <cstdio> #include <cstdio>
#include <list> #include <list>
#include <type_traits> #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 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 #ifdef _WIN32
class Platform_Impl : public Platform_Base class Platform_Impl : public Platform_Base
{ {
@ -293,11 +182,30 @@ namespace llarp::net
{ {
return GetInterfaceIndex(ip) != std::nullopt; 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 #else
class Platform_Impl : public Platform_Base class Platform_Impl : public Platform
{ {
template <typename Visit_t> template <typename Visit_t>
void void
@ -469,6 +377,33 @@ namespace llarp::net
}); });
return found; 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 #endif
@ -480,100 +415,3 @@ namespace llarp::net
return &g_plat; return &g_plat;
} }
} // namespace llarp::net } // 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 "sock_addr.hpp"
#include "ip_range.hpp"
#include "address_info.hpp" #include "address_info.hpp"
#include "ip.hpp" #include "ip.hpp"
#include "net_bits.hpp" #include "net_bits.hpp"
#include "net.hpp"
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include <llarp/util/mem.hpp> #include <llarp/util/mem.hpp>
@ -11,17 +13,6 @@
namespace llarp 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 /// shared utility functions
/// ///
@ -156,7 +147,7 @@ namespace llarp
init(); init();
memcpy(&m_addr, &other, sizeof(sockaddr_in6)); memcpy(&m_addr, &other, sizeof(sockaddr_in6));
if (ipv6_is_mapped_ipv4(other.sin6_addr)) if (IPRange::V4MappedRange().Contains(asIPv6()))
{ {
setIPv4( setIPv4(
other.sin6_addr.s6_addr[12], other.sin6_addr.s6_addr[12],
@ -179,9 +170,8 @@ namespace llarp
SockAddr::operator=(const in6_addr& other) SockAddr::operator=(const in6_addr& other)
{ {
init(); init();
memcpy(&m_addr.sin6_addr.s6_addr, &other.s6_addr, sizeof(m_addr.sin6_addr.s6_addr)); 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]); 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; m_addr4.sin_port = m_addr.sin6_port;
@ -335,7 +325,7 @@ namespace llarp
bool bool
SockAddr::isIPv4() const SockAddr::isIPv4() const
{ {
return ipv6_is_mapped_ipv4(m_addr.sin6_addr); return IPRange::V4MappedRange().Contains(asIPv6());
} }
bool bool
SockAddr::isIPv6() const SockAddr::isIPv6() const
@ -438,10 +428,10 @@ namespace llarp
setPort(ToNet(port)); setPort(ToNet(port));
} }
uint16_t net::port_t
SockAddr::getPort() const SockAddr::port() const
{ {
return ntohs(m_addr.sin6_port); return net::port_t{m_addr.sin6_port};
} }
} // namespace llarp } // namespace llarp

@ -141,9 +141,16 @@ namespace llarp
setPort(huint16_t{port}); setPort(huint16_t{port});
} }
/// port is always returned in native (host) order /// get the port of this sockaddr in network order
uint16_t net::port_t
getPort() const; 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. /// True if this stores an IPv6 address, false if IPv4.
bool 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([&currentRanges](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 = [&currentRanges](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(); auto self = shared_from_this();
bool result = true; bool result = true;
for (const auto& hook : m_ObtainedExitHooks) for (const auto& hook : m_ObtainedExitHooks)
result &= hook(self, B); result = hook(self, B) and result;
m_ObtainedExitHooks.clear(); m_ObtainedExitHooks.clear();
return result; return result;
} }
@ -897,7 +897,7 @@ namespace llarp
for (const auto& pkt : msg.X) for (const auto& pkt : msg.X)
{ {
if (pkt.size() <= 8) if (pkt.size() <= 8)
return false; continue;
auto counter = oxenc::load_big_to_host<uint64_t>(pkt.data()); auto counter = oxenc::load_big_to_host<uint64_t>(pkt.data());
if (m_ExitTrafficHandler( if (m_ExitTrafficHandler(
self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol)) self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol))

@ -397,11 +397,9 @@ namespace llarp
if (pkt.size() <= 8) if (pkt.size() <= 8)
continue; continue;
auto counter = oxenc::load_big_to_host<uint64_t>(pkt.data()); auto counter = oxenc::load_big_to_host<uint64_t>(pkt.data());
sent &= endpoint->QueueOutboundTraffic( llarp_buffer_t buf{pkt.data() + 8, pkt.size() - 8};
info.rxID, sent =
ManagedBuffer(llarp_buffer_t(pkt.data() + 8, pkt.size() - 8)), endpoint->QueueOutboundTraffic(info.rxID, buf.copy(), counter, msg.protocol) and sent;
counter,
msg.protocol);
} }
return sent; return sent;
} }

@ -6,7 +6,7 @@
#include <oxenc/variant.h> #include <oxenc/variant.h>
#include <llarp/service/address.hpp> #include <llarp/service/address.hpp>
#include <llarp/service/endpoint.hpp> #include <llarp/service/endpoint.hpp>
#include <llarp/ev/ev_libuv.hpp> #include <llarp/ev/libuv.hpp>
#include <variant> #include <variant>

@ -333,13 +333,11 @@ namespace llarp::quic
extern "C" inline void extern "C" inline void
ngtcp_trace_logger([[maybe_unused]] void* user_data, const char* fmt, ...) ngtcp_trace_logger([[maybe_unused]] void* user_data, const char* fmt, ...)
{ {
std::array<char, 2048> buf{};
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
if (char* msg; vasprintf(&msg, fmt, ap) >= 0) if (vsnprintf(buf.data(), buf.size(), fmt, ap) >= 0)
{ LogTrace(fmt::format("{}", buf.data()));
LogTrace{msg};
std::free(msg);
}
va_end(ap); va_end(ap);
} }
#endif #endif

@ -5,7 +5,7 @@
#include <llarp/crypto/crypto.hpp> #include <llarp/crypto/crypto.hpp>
#include <llarp/util/logging/buffer.hpp> #include <llarp/util/logging/buffer.hpp>
#include <llarp/service/endpoint.hpp> #include <llarp/service/endpoint.hpp>
#include <llarp/ev/ev_libuv.hpp> #include <llarp/ev/libuv.hpp>
#include <iostream> #include <iostream>
#include <random> #include <random>

@ -7,7 +7,7 @@
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include <llarp/util/logging/buffer.hpp> #include <llarp/util/logging/buffer.hpp>
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#include <llarp/ev/ev_libuv.hpp> #include <llarp/ev/libuv.hpp>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <type_traits> #include <type_traits>

@ -181,8 +181,8 @@ namespace llarp
virtual ILinkManager& virtual ILinkManager&
linkManager() = 0; linkManager() = 0;
virtual RoutePoker& virtual const std::shared_ptr<RoutePoker>&
routePoker() = 0; routePoker() const = 0;
virtual I_RCLookupHandler& virtual I_RCLookupHandler&
rcLookupHandler() = 0; rcLookupHandler() = 0;
@ -215,6 +215,10 @@ namespace llarp
virtual void virtual void
Stop() = 0; Stop() = 0;
/// indicate we are about to sleep for a while
virtual void
Freeze() = 0;
/// thaw from long sleep or network changed event /// thaw from long sleep or network changed event
virtual void virtual void
Thaw() = 0; Thaw() = 0;

@ -7,83 +7,93 @@
namespace llarp namespace llarp
{ {
namespace
{
auto logcat = log::Cat("route-poker");
}
void void
RoutePoker::AddRoute(huint32_t ip) RoutePoker::AddRoute(net::ipv4addr_t ip)
{ {
m_PokedRoutes[ip] = m_CurrentGateway; bool has_existing = m_PokedRoutes.count(ip);
if (m_CurrentGateway.h == 0) // set up route and apply as needed
{ auto& gw = m_PokedRoutes[ip];
llarp::LogDebug("RoutePoker::AddRoute no current gateway, cannot enable route."); if (m_CurrentGateway)
}
else if (m_Enabled or m_Enabling)
{ {
llarp::LogInfo( // remove existing mapping as needed
"RoutePoker::AddRoute enabled, enabling route to ", ip, " via ", m_CurrentGateway); if (has_existing)
EnableRoute(ip, m_CurrentGateway); DisableRoute(ip, gw);
// update and add new mapping
gw = *m_CurrentGateway;
EnableRoute(ip, gw);
} }
else else
{ gw = net::ipv4addr_t{};
llarp::LogDebug("RoutePoker::AddRoute disabled, not enabling route.");
}
} }
void void
RoutePoker::DisableRoute(huint32_t ip, huint32_t gateway) RoutePoker::DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway)
{ {
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); if (ip.n and gateway.n and IsEnabled())
route.DelRoute(ip, gateway); {
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
route.DelRoute(ip, gateway);
}
} }
void void
RoutePoker::EnableRoute(huint32_t ip, huint32_t gateway) RoutePoker::EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway)
{ {
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); if (ip.n and gateway.n and IsEnabled())
route.AddRoute(ip, gateway); {
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
route.AddRoute(ip, gateway);
}
} }
void void
RoutePoker::DelRoute(huint32_t ip) RoutePoker::DelRoute(net::ipv4addr_t ip)
{ {
const auto itr = m_PokedRoutes.find(ip); const auto itr = m_PokedRoutes.find(ip);
if (itr == m_PokedRoutes.end()) if (itr == m_PokedRoutes.end())
return; return;
if (m_Enabled) DisableRoute(itr->first, itr->second);
DisableRoute(itr->first, itr->second);
m_PokedRoutes.erase(itr); m_PokedRoutes.erase(itr);
} }
void void
RoutePoker::Init(AbstractRouter* router, bool enable) RoutePoker::Start(AbstractRouter* router)
{ {
m_Router = router; m_Router = router;
m_Enabled = enable; if (m_Router->IsServiceNode())
m_CurrentGateway = {0}; return;
m_Router->loop()->call_every(100ms, weak_from_this(), [this]() { Update(); });
} }
void void
RoutePoker::DeleteAllRoutes() RoutePoker::DeleteAllRoutes()
{ {
// DelRoute will check enabled, so no need here // DelRoute will check enabled, so no need here
for (const auto& [ip, gateway] : m_PokedRoutes) for (const auto& item : m_PokedRoutes)
DelRoute(ip); DelRoute(item.first);
} }
void void
RoutePoker::DisableAllRoutes() RoutePoker::DisableAllRoutes()
{ {
for (const auto& [ip, gateway] : m_PokedRoutes) for (const auto& [ip, gateway] : m_PokedRoutes)
{
DisableRoute(ip, gateway); DisableRoute(ip, gateway);
}
} }
void void
RoutePoker::EnableAllRoutes() RoutePoker::RefreshAllRoutes()
{ {
for (auto& [ip, gateway] : m_PokedRoutes) for (const auto& item : m_PokedRoutes)
{ AddRoute(item.first);
gateway = m_CurrentGateway;
EnableRoute(ip, m_CurrentGateway);
}
} }
RoutePoker::~RoutePoker() RoutePoker::~RoutePoker()
@ -91,149 +101,145 @@ namespace llarp
if (not m_Router or not m_Router->GetVPNPlatform()) if (not m_Router or not m_Router->GetVPNPlatform())
return; return;
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager(); auto& route = m_Router->GetVPNPlatform()->RouteManager();
for (const auto& [ip, gateway] : m_PokedRoutes) for (const auto& [ip, gateway] : m_PokedRoutes)
{ {
if (gateway.h) if (gateway.n and ip.n)
route.DelRoute(ip, gateway); route.DelRoute(ip, gateway);
} }
route.DelBlackhole(); route.DelBlackhole();
} }
std::optional<huint32_t> bool
RoutePoker::GetDefaultGateway() const RoutePoker::IsEnabled() const
{ {
if (not m_Router) 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(); throw std::runtime_error{"Attempting to use RoutePoker with router with no config set"};
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;
} }
void void
RoutePoker::Update() RoutePoker::Update()
{ {
if (not m_Router) 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 // ensure we have an endpoint
const auto maybe = GetDefaultGateway(); auto ep = m_Router->hiddenServiceContext().GetDefault();
if (not maybe.has_value()) if (ep == nullptr)
{ return;
#ifndef ANDROID // ensure we have a vpn platform
LogError("Network is down"); auto* platform = m_Router->GetVPNPlatform();
#endif if (platform == nullptr)
// mark network lost return;
m_HasNetwork = false; // ensure we have a vpn interface
auto* vpn = ep->GetVPNInterface();
if (vpn == nullptr)
return; return;
auto& route = platform->RouteManager();
// find current gateways
auto gateways = route.GetGatewaysNotOnInterface(*vpn);
std::optional<net::ipv4addr_t> next_gw;
for (auto& gateway : gateways)
{
if (auto* gw_ptr = std::get_if<net::ipv4addr_t>(&gateway))
next_gw = *gw_ptr;
} }
const huint32_t gateway = *maybe;
const bool gatewayChanged = m_CurrentGateway.h != 0 and m_CurrentGateway != gateway; auto is_equal = [](auto lhs, auto rhs) {
if (lhs == std::nullopt and rhs == std::nullopt)
return true;
if (lhs and rhs)
return *lhs == *rhs;
return false;
};
if (m_CurrentGateway != gateway) // update current gateway and apply state chnages as needed
if (not is_equal(m_CurrentGateway, next_gw))
{ {
LogInfo("found default gateway: ", gateway); if (next_gw and m_CurrentGateway)
m_CurrentGateway = gateway;
if (m_Enabling)
{ {
EnableAllRoutes(); log::info(logcat, "default gateway changed from {} to {}", *m_CurrentGateway, *next_gw);
Up(); m_CurrentGateway = next_gw;
m_Router->Thaw();
if (m_Router->HasClientExit())
Up();
else
RefreshAllRoutes();
}
else if (m_CurrentGateway)
{
log::warning(logcat, "default gateway {} has gone away", *m_CurrentGateway);
m_CurrentGateway = next_gw;
m_Router->Freeze();
}
else if (next_gw)
{
log::info(logcat, "default gateway found at {}", *next_gw);
m_CurrentGateway = next_gw;
} }
}
// revive network connectitivity on gateway change or network wakeup
if (gatewayChanged or not m_HasNetwork)
{
LogInfo("our network changed, thawing router state");
m_Router->Thaw();
m_HasNetwork = true;
} }
} }
void void
RoutePoker::SetDNSMode(bool exit_mode_on) const 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); dns_server->SetDNSMode(exit_mode_on);
} }
void void
RoutePoker::Enable() RoutePoker::Up()
{ {
if (m_Enabled) if (IsEnabled())
return;
if (m_Router->GetConfig()->network.m_EnableRoutePoker)
{ {
m_Enabling = true; vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
Update();
m_Enabling = false; // black hole all routes if enabled
m_Enabled = true; if (m_Router->GetConfig()->network.m_BlackholeRoutes)
route.AddBlackhole();
// explicit route pokes for first hops
m_Router->ForEachPeer(
[this](auto session, auto) { AddRoute(session->GetRemoteEndpoint().getIPv4()); }, false);
// add default route
const auto ep = m_Router->hiddenServiceContext().GetDefault();
if (auto* vpn = ep->GetVPNInterface())
route.AddDefaultRouteViaInterface(*vpn);
} }
SetDNSMode(true); SetDNSMode(true);
} }
void
RoutePoker::Disable()
{
if (not m_Enabled)
return;
DisableAllRoutes();
m_Enabled = false;
SetDNSMode(false);
}
void
RoutePoker::Up()
{
if (not m_Router->GetConfig()->network.m_EnableRoutePoker)
return;
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
// black hole all routes if enabled
if (m_Router->GetConfig()->network.m_BlackholeRoutes)
route.AddBlackhole();
// explicit route pokes for first hops
m_Router->ForEachPeer(
[&](auto session, auto) mutable { AddRoute(session->GetRemoteEndpoint().asIPv4()); },
false);
// add default route
const auto ep = m_Router->hiddenServiceContext().GetDefault();
route.AddDefaultRouteViaInterface(ep->GetIfName());
}
void void
RoutePoker::Down() RoutePoker::Down()
{ {
if (not m_Router->GetConfig()->network.m_EnableRoutePoker)
return;
// unpoke routes for first hops // unpoke routes for first hops
m_Router->ForEachPeer( m_Router->ForEachPeer(
[&](auto session, auto) mutable { DelRoute(session->GetRemoteEndpoint().asIPv4()); }, [this](auto session, auto) { DelRoute(session->GetRemoteEndpoint().getIPv4()); }, false);
false);
// remove default route // remove default route
const auto ep = m_Router->hiddenServiceContext().GetDefault();
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
route.DelDefaultRouteViaInterface(ep->GetIfName()); if (IsEnabled())
// delete route blackhole {
route.DelBlackhole(); vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
const auto ep = m_Router->hiddenServiceContext().GetDefault();
if (auto* vpn = ep->GetVPNInterface())
route.DelDefaultRouteViaInterface(*vpn);
// delete route blackhole
route.DelBlackhole();
}
SetDNSMode(false);
} }
} // namespace llarp } // namespace llarp

@ -10,32 +10,19 @@ namespace llarp
{ {
struct AbstractRouter; struct AbstractRouter;
struct RoutePoker struct RoutePoker : public std::enable_shared_from_this<RoutePoker>
{ {
void void
AddRoute(huint32_t ip); AddRoute(net::ipv4addr_t ip);
void void
DelRoute(huint32_t ip); DelRoute(net::ipv4addr_t ip);
void void
Init(AbstractRouter* router, bool enable = false); Start(AbstractRouter* router);
~RoutePoker(); ~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 /// explicitly put routes up
void void
Up(); Up();
@ -50,6 +37,12 @@ namespace llarp
SetDNSMode(bool using_exit_mode) const; SetDNSMode(bool using_exit_mode) const;
private: private:
void
Update();
bool
IsEnabled() const;
void void
DeleteAllRoutes(); DeleteAllRoutes();
@ -57,25 +50,18 @@ namespace llarp
DisableAllRoutes(); DisableAllRoutes();
void void
EnableAllRoutes(); RefreshAllRoutes();
void void
EnableRoute(huint32_t ip, huint32_t gateway); EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway);
void 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<huint32_t, huint32_t> m_PokedRoutes; std::unordered_map<net::ipv4addr_t, net::ipv4addr_t> m_PokedRoutes;
huint32_t m_CurrentGateway;
bool m_Enabled = false; std::optional<net::ipv4addr_t> m_CurrentGateway;
bool m_Enabling = false;
AbstractRouter* m_Router = nullptr; AbstractRouter* m_Router = nullptr;
bool m_HasNetwork = true;
}; };
} // namespace llarp } // namespace llarp

@ -48,22 +48,21 @@ namespace llarp
static auto logcat = log::Cat("router"); static auto logcat = log::Cat("router");
Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform) Router::Router(EventLoop_ptr loop, std::shared_ptr<vpn::Platform> vpnPlatform)
: ready(false) : ready{false}
, m_lmq(std::make_shared<oxenmq::OxenMQ>()) , m_lmq{std::make_shared<oxenmq::OxenMQ>()}
, _loop(std::move(loop)) , _loop{std::move(loop)}
, _vpnPlatform(std::move(vpnPlatform)) , _vpnPlatform{std::move(vpnPlatform)}
, paths(this) , paths{this}
, _exitContext(this) , _exitContext{this}
, _dht(llarp_dht_context_new(this)) , _dht{llarp_dht_context_new(this)}
, m_DiskThread(m_lmq->add_tagged_thread("disk")) , m_DiskThread{m_lmq->add_tagged_thread("disk")}
, inbound_link_msg_parser(this) , inbound_link_msg_parser{this}
, _hiddenServiceContext(this) , _hiddenServiceContext{this}
, m_RPCServer(new rpc::RpcServer(m_lmq, this)) , m_RoutePoker{std::make_shared<RoutePoker>()}
#ifdef LOKINET_HIVE , m_RPCServer{new rpc::RpcServer{m_lmq, this}}
, _randomStartDelay(std::chrono::milliseconds((llarp::randint() % 1250) + 2000)) , _randomStartDelay{
#else platform::is_simulation ? std::chrono::milliseconds{(llarp::randint() % 1250) + 2000}
, _randomStartDelay(std::chrono::seconds((llarp::randint() % 30) + 10)) : 0s}
#endif
{ {
m_keyManager = std::make_shared<KeyManager>(); m_keyManager = std::make_shared<KeyManager>();
// for lokid, so we don't close the connection when syncing the whitelist // 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); return inbound_link_msg_parser.ProcessFrom(session, buf);
} }
void
Router::Freeze()
{
if (IsServiceNode())
return;
linkManager().ForEachPeer([](auto peer) {
if (peer)
peer->Close();
});
}
void void
Router::Thaw() Router::Thaw()
{ {
if (IsServiceNode())
return;
// get pubkeys we are connected to // get pubkeys we are connected to
std::unordered_set<RouterID> peerPubkeys; std::unordered_set<RouterID> peerPubkeys;
linkManager().ForEachPeer([&peerPubkeys](auto peer) { linkManager().ForEachPeer([&peerPubkeys](auto peer) {
@ -420,8 +431,6 @@ namespace llarp
if (not EnsureIdentity()) if (not EnsureIdentity())
throw std::runtime_error("EnsureIdentity() failed"); throw std::runtime_error("EnsureIdentity() failed");
m_RoutePoker.Init(this);
return true; return true;
} }
@ -1003,19 +1012,6 @@ namespace llarp
_linkManager.CheckPersistingSessions(now); _linkManager.CheckPersistingSessions(now);
if (not isSvcNode)
{
if (HasClientExit())
{
m_RoutePoker.Enable();
}
else
{
m_RoutePoker.Disable();
}
m_RoutePoker.Update();
}
size_t connected = NumberOfConnectedRouters(); size_t connected = NumberOfConnectedRouters();
if (not isSvcNode) if (not isSvcNode)
{ {
@ -1123,7 +1119,7 @@ namespace llarp
if (const auto maybe = nodedb()->Get(remote); maybe.has_value()) if (const auto maybe = nodedb()->Get(remote); maybe.has_value())
{ {
for (const auto& addr : maybe->addrs) 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"}; throw std::runtime_error{"cannot override public ip, it is already set"};
ai.fromSockAddr(*_ourAddress); ai.fromSockAddr(*_ourAddress);
} }
if (RouterContact::BlockBogons && IsBogon(ai.ip)) if (RouterContact::BlockBogons && Net().IsBogon(ai.ip))
throw std::runtime_error{var::visit( throw std::runtime_error{var::visit(
[](auto&& ip) { [](auto&& ip) {
return "cannot use " + ip.ToString() 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()) if (IsServiceNode() and not _rc.IsPublicRouter())
{ {
LogError("we are configured as relay but have no reachable addresses"); LogError("we are configured as relay but have no reachable addresses");
@ -1345,6 +1335,7 @@ namespace llarp
LogInfo("have ", _nodedb->NumLoaded(), " routers"); LogInfo("have ", _nodedb->NumLoaded(), " routers");
_loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); }); _loop->call_every(ROUTER_TICK_INTERVAL, weak_from_this(), [this] { Tick(); });
m_RoutePoker->Start(this);
_running.store(true); _running.store(true);
_startedAt = Now(); _startedAt = Now();
#if defined(WITH_SYSTEMD) #if defined(WITH_SYSTEMD)
@ -1677,11 +1668,8 @@ namespace llarp
[this](llarp::RouterContact rc) { [this](llarp::RouterContact rc) {
if (IsServiceNode()) if (IsServiceNode())
return; return;
llarp::LogTrace( for (const auto& addr : rc.addrs)
"Before connect, outbound link adding route to (", m_RoutePoker->AddRoute(addr.IPv4());
rc.addrs[0].toIpAddress().toIP(),
") via gateway.");
m_RoutePoker.AddRoute(rc.addrs[0].toIpAddress().toIP());
}, },
util::memFn(&Router::ConnectionEstablished, this), util::memFn(&Router::ConnectionEstablished, this),
util::memFn(&AbstractRouter::CheckRenegotiateValid, this), util::memFn(&AbstractRouter::CheckRenegotiateValid, this),

@ -274,27 +274,13 @@ namespace llarp
/// bootstrap RCs /// bootstrap RCs
BootstrapList bootstrapRCList; BootstrapList bootstrapRCList;
bool const std::shared_ptr<RoutePoker>&
ExitEnabled() const routePoker() const override
{
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
{ {
return m_RoutePoker; return m_RoutePoker;
} }
RoutePoker m_RoutePoker; std::shared_ptr<RoutePoker> m_RoutePoker;
void void
TriggerPump() override; TriggerPump() override;
@ -401,6 +387,9 @@ namespace llarp
bool bool
StartRpcServer() override; StartRpcServer() override;
void
Freeze() override;
void void
Thaw() override; Thaw() override;

@ -485,9 +485,11 @@ namespace llarp
if (IsExpired(now) and not allowExpired) if (IsExpired(now) and not allowExpired)
return false; return false;
// TODO: make net* overridable
const auto* net = net::Platform::Default_ptr();
for (const auto& a : addrs) for (const auto& a : addrs)
{ {
if (IsBogon(a.ip) && BlockBogons) if (net->IsBogon(a.ip) && BlockBogons)
{ {
llarp::LogError("invalid address info: ", a); llarp::LogError("invalid address info: ", a);
return false; return false;

@ -516,8 +516,8 @@ namespace llarp::rpc
{ {
auto mapExit = [=](service::Address addr) mutable { auto mapExit = [=](service::Address addr) mutable {
ep->MapExitRange(range, addr); ep->MapExitRange(range, addr);
r->routePoker().Enable();
r->routePoker().Up(); r->routePoker()->Up();
bool shouldSendAuth = false; bool shouldSendAuth = false;
if (token.has_value()) if (token.has_value())
{ {
@ -531,7 +531,7 @@ namespace llarp::rpc
reply(CreateJSONError("we dont have an exit?")); reply(CreateJSONError("we dont have an exit?"));
}; };
auto onBadResult = [r, reply, ep, range](std::string reason) { auto onBadResult = [r, reply, ep, range](std::string reason) {
r->routePoker().Down(); r->routePoker()->Down();
ep->UnmapExitRange(range); ep->UnmapExitRange(range);
reply(CreateJSONError(reason)); reply(CreateJSONError(reason));
}; };
@ -614,7 +614,7 @@ namespace llarp::rpc
} }
else if (not map) else if (not map)
{ {
r->routePoker().Down(); r->routePoker()->Down();
ep->UnmapExitRange(range); ep->UnmapExitRange(range);
reply(CreateJSONResponse("OK")); reply(CreateJSONResponse("OK"));
} }

@ -37,7 +37,6 @@
#include <llarp/quic/server.hpp> #include <llarp/quic/server.hpp>
#include <llarp/quic/tunnel.hpp> #include <llarp/quic/tunnel.hpp>
#include <llarp/ev/ev_libuv.hpp>
#include <uvw.hpp> #include <uvw.hpp>
#include <variant> #include <variant>
@ -124,8 +123,7 @@ namespace llarp
// add supported ethertypes // add supported ethertypes
if (HasIfAddr()) if (HasIfAddr())
{ {
const auto ourIP = net::HUIntToIn6(GetIfAddr()); if (IPRange::V4MappedRange().Contains(GetIfAddr()))
if (ipv6_is_mapped_ipv4(ourIP))
{ {
introSet().supportedProtocols.push_back(ProtocolType::TrafficV4); introSet().supportedProtocols.push_back(ProtocolType::TrafficV4);
} }
@ -2074,6 +2072,11 @@ namespace llarp
LogInfo(Name(), " map ", range, " to exit at ", exit); LogInfo(Name(), " map ", range, " to exit at ", exit);
m_ExitMap.Insert(range, exit); m_ExitMap.Insert(range, exit);
} }
bool
Endpoint::HasFlowToService(Address addr) const
{
return HasOutboundConvo(addr) or HasInboundConvo(addr);
}
void void
Endpoint::UnmapExitRange(IPRange range) Endpoint::UnmapExitRange(IPRange range)

@ -174,6 +174,12 @@ namespace llarp
return nullptr; return nullptr;
}; };
virtual vpn::NetworkInterface*
GetVPNInterface()
{
return nullptr;
}
bool bool
PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override; PublishIntroSet(const EncryptedIntroSet& i, AbstractRouter* r) override;
@ -355,6 +361,9 @@ namespace llarp
bool bool
HasPathToSNode(const RouterID remote) const; HasPathToSNode(const RouterID remote) const;
bool
HasFlowToService(const Address remote) const;
void void
PutSenderFor(const ConvoTag& tag, const ServiceInfo& info, bool inbound) override; PutSenderFor(const ConvoTag& tag, const ServiceInfo& info, bool inbound) override;

@ -2,31 +2,23 @@
#include "protocol.hpp" #include "protocol.hpp"
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <algorithm>
#include <iterator>
#include <vector> #include <vector>
namespace llarp namespace llarp::service
{ {
namespace service struct PendingBuffer
{ {
struct PendingBuffer std::vector<byte_t> payload;
{ ProtocolType protocol;
std::vector<byte_t> payload;
ProtocolType protocol;
PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload(buf.sz), protocol(t) inline llarp_buffer_t
{ Buffer()
std::copy(buf.base, buf.base + buf.sz, std::back_inserter(payload)); {
} return llarp_buffer_t{payload};
}
ManagedBuffer PendingBuffer(const llarp_buffer_t& buf, ProtocolType t) : payload{buf.copy()}, protocol{t}
Buffer() {}
{ };
return ManagedBuffer{llarp_buffer_t(payload)};
}
};
} // namespace service
} // namespace llarp } // namespace llarp::service

@ -5,6 +5,7 @@
#include "endpoint.hpp" #include "endpoint.hpp"
#include <utility> #include <utility>
#include <unordered_set> #include <unordered_set>
#include <llarp/crypto/crypto.hpp>
namespace llarp 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; 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 namespace llarp
{ {
std::vector<byte_t> std::vector<byte_t>

@ -15,59 +15,17 @@
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <string_view>
/** namespace llarp
* buffer.h {
* using byte_view_t = std::basic_string_view<byte_t>;
* generic memory buffer }
*
* TODO: replace usage of these with std::span (via a backport until we move to C++20). That's a
* fairly big job, though, as llarp_buffer_t is currently used a bit differently (i.e. maintains
* both start and current position, plus has some value reading/writing methods).
*/
/**
llarp_buffer_t represents a region of memory that is ONLY
valid in the current scope.
make sure to follow the rules:
ALWAYS copy the contents of the buffer if that data is to be used outside the
current scope.
ALWAYS pass a llarp_buffer_t * if you plan on modifying the data associated
with the buffer
ALWAYS pass a llarp_buffer_t * if you plan on advancing the stream position
ALWAYS pass a const llarp_buffer_t & if you are doing a read only operation
that does not modify the buffer
ALWAYS pass a const llarp_buffer_t & if you don't want to advance the stream
position
ALWAYS bail out of the current operation if you run out of space in a buffer
ALWAYS assume the pointers in the buffer are stack allocated memory
(yes even if you know they are not)
NEVER malloc() the pointers in the buffer when using it
NEVER realloc() the pointers in the buffer when using it
NEVER free() the pointers in the buffer when using it
NEVER use llarp_buffer_t ** (double pointers)
NEVER use llarp_buffer_t ** (double pointers)
ABSOLUTELY NEVER USE DOUBLE POINTERS.
*/
struct ManagedBuffer;
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 /// starting memory address
byte_t* base{nullptr}; byte_t* base{nullptr};
@ -76,26 +34,22 @@ struct llarp_buffer_t
/// max size of buffer /// max size of buffer
size_t sz{0}; size_t sz{0};
byte_t byte_t operator[](size_t x)
operator[](size_t x)
{ {
return *(this->base + x); return *(this->base + x);
} }
llarp_buffer_t() = default; 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) /// Construct referencing some 1-byte, trivially copyable (e.g. char, unsigned char, byte_t)
/// pointer type and a buffer size. /// pointer type and a buffer size.
template < template <
typename T, typename T,
typename = std::enable_if_t<sizeof(T) == 1 and std::is_trivially_copyable_v<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))) : base(reinterpret_cast<byte_t*>(const_cast<std::remove_const_t<T>*>(buf)))
, cur(base) , cur(base)
, sz(_sz) , sz(_sz)
@ -105,82 +59,73 @@ struct llarp_buffer_t
template < template <
typename T, typename T,
typename = std::void_t<decltype(std::declval<T>().data() + std::declval<T>().size())>> 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* byte_t* begin()
begin()
{ {
return base; return base;
} }
byte_t* byte_t* begin() const
begin() const
{ {
return base; return base;
} }
byte_t* byte_t* end()
end()
{ {
return base + sz; return base + sz;
} }
byte_t* byte_t* end() const
end() const
{ {
return base + sz; return base + sz;
} }
size_t size_t size_left() const;
size_left() const;
template <typename OutputIt> template <typename OutputIt>
bool bool read_into(OutputIt begin, OutputIt end);
read_into(OutputIt begin, OutputIt end);
template <typename InputIt> template <typename InputIt>
bool bool write(InputIt begin, InputIt end);
write(InputIt begin, InputIt end);
#ifndef _WIN32 #ifndef _WIN32
bool bool writef(const char* fmt, ...) __attribute__((format(printf, 2, 3)));
writef(const char* fmt, ...) __attribute__((format(printf, 2, 3)));
#elif defined(__MINGW64__) || defined(__MINGW32__) #elif defined(__MINGW64__) || defined(__MINGW32__)
bool bool writef(const char* fmt, ...) __attribute__((__format__(__MINGW_PRINTF_FORMAT, 2, 3)));
writef(const char* fmt, ...) __attribute__((__format__(__MINGW_PRINTF_FORMAT, 2, 3)));
#else #else
bool bool writef(const char* fmt, ...);
writef(const char* fmt, ...);
#endif #endif
bool bool put_uint16(uint16_t i);
put_uint16(uint16_t i); bool put_uint32(uint32_t i);
bool
put_uint32(uint32_t i);
bool bool put_uint64(uint64_t i);
put_uint64(uint64_t i);
bool bool read_uint16(uint16_t & i);
read_uint16(uint16_t& i); bool read_uint32(uint32_t & i);
bool
read_uint32(uint32_t& i);
bool bool read_uint64(uint64_t & i);
read_uint64(uint64_t& i);
size_t size_t read_until(char delim, byte_t* result, size_t resultlen);
read_until(char delim, byte_t* result, size_t resultlen);
/// make a copy of this buffer /// make a copy of this buffer
std::vector<byte_t> std::vector<byte_t> copy() const;
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: private:
friend struct ManagedBuffer; friend struct ManagedBuffer;
llarp_buffer_t(const llarp_buffer_t&) = default; llarp_buffer_t(const llarp_buffer_t&) = default;
llarp_buffer_t(llarp_buffer_t&&) = default; llarp_buffer_t(llarp_buffer_t &&) = default;
}; };
bool bool
operator==(const llarp_buffer_t& buff, std::string_view data); 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`. Provide a copyable/moveable wrapper around `llarp_buffer_t`.
*/ */
struct ManagedBuffer struct [[deprecated("deprecated along with llarp_buffer_t")]] ManagedBuffer
{ {
llarp_buffer_t underlying; llarp_buffer_t underlying;
@ -223,7 +168,7 @@ struct ManagedBuffer
explicit ManagedBuffer(const llarp_buffer_t& b) : underlying(b) explicit ManagedBuffer(const llarp_buffer_t& b) : underlying(b)
{} {}
ManagedBuffer(ManagedBuffer&&) = default; ManagedBuffer(ManagedBuffer &&) = default;
ManagedBuffer(const ManagedBuffer&) = default; ManagedBuffer(const ManagedBuffer&) = default;
operator const llarp_buffer_t&() const operator const llarp_buffer_t&() const
@ -234,6 +179,8 @@ struct ManagedBuffer
namespace llarp 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 // Wrapper around a std::unique_ptr<byte_t[]> that owns its own memory and is also implicitly
// convertible to a llarp_buffer_t. // convertible to a llarp_buffer_t.
struct OwnedBuffer 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 #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif
#ifndef Lokinet_INIT
#if defined(_WIN32)
#define Lokinet_INIT \
DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit
#else
#define Lokinet_INIT _lokinet_non_shit_platform_INIT
#endif
#endif #endif
int int

@ -7,6 +7,12 @@
#include <string> #include <string>
#include <set> #include <set>
#ifdef _WIN32
#include <windows.h>
#include <stringapiset.h>
#include <llarp/win32/exception.hpp>
#endif
namespace llarp namespace llarp
{ {
bool bool
@ -168,4 +174,22 @@ namespace llarp
dur.count()); 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 } // namespace llarp

@ -5,7 +5,6 @@
#include <chrono> #include <chrono>
#include <iterator> #include <iterator>
#include <charconv> #include <charconv>
#include <fmt/format.h> #include <fmt/format.h>
namespace llarp namespace llarp
@ -141,4 +140,8 @@ namespace llarp
std::string std::string
friendly_duration(std::chrono::nanoseconds dur); friendly_duration(std::chrono::nanoseconds dur);
/// convert a "normal" string into a wide string
std::wstring
to_wide(std::string data);
} // namespace llarp } // namespace llarp

@ -3,7 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <llarp/ev/vpn.hpp> #include "platform.hpp"
#include "common.hpp" #include "common.hpp"
#include <llarp.hpp> #include <llarp.hpp>
@ -12,10 +12,9 @@ namespace llarp::vpn
class AndroidInterface : public NetworkInterface class AndroidInterface : public NetworkInterface
{ {
const int m_fd; const int m_fd;
const InterfaceInfo m_Info; // likely 100% ignored on android, at least for now
public: 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) if (m_fd == -1)
throw std::runtime_error( throw std::runtime_error(
@ -37,38 +36,34 @@ namespace llarp::vpn
net::IPPacket net::IPPacket
ReadNextPacket() override ReadNextPacket() override
{ {
net::IPPacket pkt{}; std::vector<byte_t> pkt;
const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf)); pkt.reserve(net::IPPacket::MaxSize);
if (sz >= 0) const auto n = read(m_fd, pkt.data(), pkt.capacity());
pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)}); pkt.resize(std::min(std::max(ssize_t{}, n), static_cast<ssize_t>(pkt.capacity())));
return pkt; return net::IPPacket{std::move(pkt)};
} }
bool bool
WritePacket(net::IPPacket pkt) override 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) if (sz <= 0)
return false; return false;
return sz == static_cast<ssize_t>(pkt.sz); return sz == static_cast<ssize_t>(pkt.size());
}
std::string
IfName() const override
{
return m_Info.ifname;
} }
}; };
class AndroidRouteManager : public IRouteManager 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 void
AddRouteViaInterface(NetworkInterface&, IPRange) override{}; AddRouteViaInterface(NetworkInterface&, IPRange) override{};
@ -76,9 +71,10 @@ namespace llarp::vpn
void void
DelRouteViaInterface(NetworkInterface&, IPRange) override{}; 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{}; AndroidRouteManager _routeManager{};
public: public:
AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD) AndroidPlatform(llarp::Context* ctx) : fd{ctx->androidFD}
{} {}
std::shared_ptr<NetworkInterface> 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 #pragma once
#include <llarp/ev/vpn.hpp> #include "platform.hpp"
#include <unistd.h> #include <unistd.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
@ -36,11 +36,10 @@ namespace llarp::vpn
class LinuxInterface : public NetworkInterface class LinuxInterface : public NetworkInterface
{ {
const int m_fd; const int m_fd;
const InterfaceInfo m_Info;
public: public:
LinuxInterface(InterfaceInfo info) 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) if (m_fd == -1)
@ -60,7 +59,7 @@ namespace llarp::vpn
control.ioctl(SIOCGIFFLAGS, &ifr); control.ioctl(SIOCGIFFLAGS, &ifr);
const int flags = ifr.ifr_flags; const int flags = ifr.ifr_flags;
control.ioctl(SIOCGIFINDEX, &ifr); control.ioctl(SIOCGIFINDEX, &ifr);
const int ifindex = ifr.ifr_ifindex; m_Info.index = ifr.ifr_ifindex;
for (const auto& ifaddr : m_Info.addrs) for (const auto& ifaddr : m_Info.addrs)
{ {
@ -79,7 +78,7 @@ namespace llarp::vpn
{ {
ifr6.addr = net::HUIntToIn6(ifaddr.range.addr); ifr6.addr = net::HUIntToIn6(ifaddr.range.addr);
ifr6.prefixlen = llarp::bits::count_bits(ifaddr.range.netmask_bits); ifr6.prefixlen = llarp::bits::count_bits(ifaddr.range.netmask_bits);
ifr6.ifindex = ifindex; ifr6.ifindex = m_Info.index;
try try
{ {
IOCTL{AF_INET6}.ioctl(SIOCSIFADDR, &ifr6); IOCTL{AF_INET6}.ioctl(SIOCSIFADDR, &ifr6);
@ -108,30 +107,29 @@ namespace llarp::vpn
net::IPPacket net::IPPacket
ReadNextPacket() override ReadNextPacket() override
{ {
net::IPPacket pkt; std::vector<byte_t> pkt;
const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf)); pkt.resize(net::IPPacket::MaxSize);
if (sz >= 0) const auto sz = read(m_fd, pkt.data(), pkt.capacity());
pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)}); if (errno)
else if (errno == EAGAIN || errno == EWOULDBLOCK) {
pkt.sz = 0; if (errno == EAGAIN or errno == EWOULDBLOCK)
else {
errno = 0;
return net::IPPacket{};
}
throw std::error_code{errno, std::system_category()}; throw std::error_code{errno, std::system_category()};
}
pkt.resize(sz);
return pkt; return pkt;
} }
bool bool
WritePacket(net::IPPacket pkt) override 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) if (sz <= 0)
return false; return false;
return sz == static_cast<ssize_t>(pkt.sz); return sz == static_cast<ssize_t>(pkt.size());
}
std::string
IfName() const override
{
return m_Info.ifname;
} }
}; };
@ -182,19 +180,18 @@ namespace llarp::vpn
unsigned char bitlen; unsigned char bitlen;
unsigned char data[sizeof(struct in6_addr)]; 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; family = AF_INET;
bitlen = bits; 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; family = AF_INET6;
bitlen = bits; bitlen = bits;
const nuint128_t net = ToNet(addr); std::memcpy(data, &addr.n, 16);
std::memcpy(data, &net, 16);
} }
}; };
@ -290,76 +287,68 @@ namespace llarp::vpn
} }
void 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& info = vpn.Info();
const auto maybe = Net().GetInterfaceAddr(ifname);
const auto maybe = Net().GetInterfaceAddr(info.ifname);
if (not maybe) if (not maybe)
throw std::runtime_error{"we dont have our own network interface?"}; throw std::runtime_error{"we dont have our own network interface?"};
const _inet_addr gateway{maybe->asIPv4()}; const _inet_addr gateway{maybe->getIPv4()};
const _inet_addr lower{ipaddr_ipv4_bits(0, 0, 0, 0), 1}; const _inet_addr lower{ToNet(ipaddr_ipv4_bits(0, 0, 0, 0)), 1};
const _inet_addr upper{ipaddr_ipv4_bits(128, 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, lower, gateway, GatewayMode::eLowerDefault, info.index);
Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, if_idx); 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::"}) for (const std::string str : {"::", "4000::", "8000::", "c000::"})
{ {
huint128_t _hole{}; const _inet_addr hole6{net::ipv6addr_t::from_string(str), 2};
_hole.FromString(str); Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, info.index);
const _inet_addr hole6{_hole, 2};
Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, if_idx);
} }
} }
} }
void 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()) if (range.IsV4())
{ {
const auto maybe = Net().GetInterfaceAddr(ifname); const auto maybe = Net().GetInterfaceAddr(info.ifname);
if (not maybe) if (not maybe)
throw std::runtime_error{"we dont have our own network interface?"}; 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{ 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 else
{ {
const auto maybe = Net().GetInterfaceIPv6Address(ifname); const auto maybe = Net().GetInterfaceIPv6Address(info.ifname);
if (not maybe) if (not maybe)
throw std::runtime_error{"we dont have our own network interface?"}; throw std::runtime_error{"we dont have our own network interface?"};
const _inet_addr gateway{*maybe, 128}; const _inet_addr gateway{ToNet(*maybe), 128};
const _inet_addr addr{range.addr, bits::count_bits(range.netmask_bits)}; const _inet_addr addr{ToNet(range.addr), bits::count_bits(range.netmask_bits)};
Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx); Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index);
} }
} }
void 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 auto _ip = var::visit([](auto&& i) { return _inet_addr{i}; }, ip);
std::visit( auto _gw = var::visit([](auto&& i) { return _inet_addr{i}; }, gateway);
[gateway, cmd, flags, this](auto&& ip) {
const _inet_addr toAddr{ip}; Route(cmd, flags, _ip, _gw, GatewayMode::eFirstHop, 0);
std::visit(
[toAddr, cmd, flags, this](auto&& gateway) {
const _inet_addr gwAddr{gateway};
Route(cmd, flags, toAddr, gwAddr, GatewayMode::eFirstHop, 0);
},
gateway);
},
ip);
} }
public: public:
@ -375,45 +364,46 @@ namespace llarp::vpn
} }
void 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); Route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ip, gateway);
} }
void 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); Route(RTM_DELROUTE, 0, ip, gateway);
} }
void 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 void
DelDefaultRouteViaInterface(std::string ifname) override DelDefaultRouteViaInterface(NetworkInterface& vpn) override
{ {
DefaultRouteViaInterface(ifname, RTM_DELROUTE, 0); DefaultRouteViaInterface(vpn, RTM_DELROUTE, 0);
} }
void void
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override 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 void
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{ {
RouteViaInterface(RTM_DELROUTE, 0, vpn.IfName(), range); RouteViaInterface(RTM_DELROUTE, 0, vpn, range);
} }
std::vector<IPVariant_t> std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(std::string ifname) override 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"}; std::ifstream inf{"/proc/net/route"};
for (std::string line; std::getline(inf, line);) for (std::string line; std::getline(inf, line);)
{ {
@ -425,7 +415,7 @@ namespace llarp::vpn
{ {
huint32_t x{}; huint32_t x{};
oxenc::from_hex(ip.begin(), ip.end(), reinterpret_cast<char*>(&x.h)); 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 struct UDPPacketHandler : public Layer4Handler
{ {
PacketHandlerFunc m_BaseHandler; PacketHandlerFunc_t m_BaseHandler;
std::unordered_map<nuint16_t, PacketHandlerFunc> m_LocalPorts; 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 void
AddSubHandler(nuint16_t localport, PacketHandlerFunc handler) override AddSubHandler(nuint16_t localport, PacketHandlerFunc_t handler) override
{ {
m_LocalPorts.emplace(localport, std::move(handler)); m_LocalPorts.emplace(localport, std::move(handler));
} }
@ -19,12 +20,15 @@ namespace llarp::vpn
void void
HandleIPPacket(llarp::net::IPPacket pkt) override HandleIPPacket(llarp::net::IPPacket pkt) override
{ {
const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2; auto dstport = pkt.DstPort();
const nuint16_t dstPort{*reinterpret_cast<const uint16_t*>(ptr)}; if (not dstport)
if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end())
{ {
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 else
m_BaseHandler(std::move(pkt)); m_BaseHandler(std::move(pkt));
} }
@ -32,9 +36,9 @@ namespace llarp::vpn
struct GenericLayer4Handler : public Layer4Handler 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)} : 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 void
@ -53,15 +58,13 @@ namespace llarp::vpn
{ {
const auto proto = pkt.Header()->protocol; const auto proto = pkt.Header()->protocol;
if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end()) if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end())
{
itr->second->HandleIPPacket(std::move(pkt)); itr->second->HandleIPPacket(std::move(pkt));
}
else else
m_BaseHandler(std::move(pkt)); m_BaseHandler(std::move(pkt));
} }
void void
PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc func) PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func)
{ {
constexpr byte_t udp_proto = 0x11; constexpr byte_t udp_proto = 0x11;
@ -73,7 +76,7 @@ namespace llarp::vpn
} }
void 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)); m_IPProtoHandler[proto] = std::make_unique<GenericLayer4Handler>(std::move(func));
} }

@ -6,25 +6,18 @@
namespace llarp::vpn namespace llarp::vpn
{ {
using PacketHandlerFunc = std::function<void(llarp::net::IPPacket)>; using PacketHandlerFunc_t = std::function<void(llarp::net::IPPacket)>;
struct Layer4Handler struct Layer4Handler;
{
virtual ~Layer4Handler() = default;
virtual void
HandleIPPacket(llarp::net::IPPacket pkt) = 0;
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc){};
};
class PacketRouter class PacketRouter
{ {
PacketHandlerFunc m_BaseHandler; PacketHandlerFunc_t m_BaseHandler;
std::unordered_map<uint8_t, std::unique_ptr<Layer4Handler>> m_IPProtoHandler; std::unordered_map<uint8_t, std::unique_ptr<Layer4Handler>> m_IPProtoHandler;
public: public:
/// baseHandler will be called if no other handlers matches a packet /// baseHandler will be called if no other handlers matches a packet
explicit PacketRouter(PacketHandlerFunc baseHandler); explicit PacketRouter(PacketHandlerFunc_t baseHandler);
/// feed in an ip packet for handling /// feed in an ip packet for handling
void void
@ -32,14 +25,25 @@ namespace llarp::vpn
/// add a non udp packet handler using ip protocol proto /// add a non udp packet handler using ip protocol proto
void void
AddIProtoHandler(uint8_t proto, PacketHandlerFunc func); AddIProtoHandler(uint8_t proto, PacketHandlerFunc_t func);
/// helper that adds a udp packet handler for UDP destinted for localport /// helper that adds a udp packet handler for UDP destinted for localport
void void
AddUDPHandler(huint16_t localport, PacketHandlerFunc func); AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func);
/// remove a udp handler that is already set up by bound port /// remove a udp handler that is already set up by bound port
void void
RemoveUDPHandler(huint16_t localport); RemoveUDPHandler(huint16_t localport);
}; };
struct Layer4Handler
{
virtual ~Layer4Handler() = default;
virtual void
HandleIPPacket(llarp::net::IPPacket pkt) = 0;
virtual void AddSubHandler(nuint16_t, PacketHandlerFunc_t){};
};
} // namespace llarp::vpn } // namespace llarp::vpn

@ -1,5 +1,5 @@
#include <llarp/ev/vpn.hpp> #include "platform.hpp"
#ifdef _WIN32 #ifdef _WIN32
#include "win32.hpp" #include "win32.hpp"
@ -28,7 +28,7 @@ namespace llarp::vpn
(void)ctx; (void)ctx;
std::shared_ptr<Platform> plat; std::shared_ptr<Platform> plat;
#ifdef _WIN32 #ifdef _WIN32
plat = std::make_shared<vpn::Win32Platform>(); plat = std::make_shared<llarp::win32::VPNPlatform>(ctx);
#endif #endif
#ifdef __linux__ #ifdef __linux__
#ifdef ANDROID #ifdef ANDROID

@ -2,10 +2,12 @@
#include <llarp/net/ip_range.hpp> #include <llarp/net/ip_range.hpp>
#include <llarp/net/ip_packet.hpp> #include <llarp/net/ip_packet.hpp>
#include <set>
#include <oxenc/variant.h> #include <oxenc/variant.h>
#include "i_packet_io.hpp"
#include <set>
namespace llarp namespace llarp
{ {
struct Context; struct Context;
@ -30,36 +32,40 @@ namespace llarp::vpn
struct InterfaceInfo struct InterfaceInfo
{ {
std::string ifname; std::string ifname;
unsigned int index;
huint32_t dnsaddr; 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 /// a vpn network interface
class NetworkInterface class NetworkInterface : public I_Packet_IO
{ {
protected:
InterfaceInfo m_Info;
public: public:
NetworkInterface() = default; NetworkInterface(InterfaceInfo info) : m_Info{std::move(info)}
{}
NetworkInterface(const NetworkInterface&) = delete; NetworkInterface(const NetworkInterface&) = delete;
NetworkInterface(NetworkInterface&&) = delete; NetworkInterface(NetworkInterface&&) = delete;
virtual ~NetworkInterface() = default; virtual ~NetworkInterface() = default;
/// get pollable fd for reading const InterfaceInfo&
virtual int Info() const
PollFD() const = 0; {
return m_Info;
/// 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;
/// idempotently wake up the upper layers as needed (platform dependant) /// idempotently wake up the upper layers as needed (platform dependant)
virtual void virtual void
@ -69,8 +75,6 @@ namespace llarp::vpn
class IRouteManager class IRouteManager
{ {
public: public:
using IPVariant_t = std::variant<huint32_t, huint128_t>;
IRouteManager() = default; IRouteManager() = default;
IRouteManager(const IRouteManager&) = delete; IRouteManager(const IRouteManager&) = delete;
IRouteManager(IRouteManager&&) = delete; IRouteManager(IRouteManager&&) = delete;
@ -86,16 +90,16 @@ namespace llarp::vpn
} }
virtual void virtual void
AddRoute(IPVariant_t ip, IPVariant_t gateway) = 0; AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0;
virtual void virtual void
DelRoute(IPVariant_t ip, IPVariant_t gateway) = 0; DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0;
virtual void virtual void
AddDefaultRouteViaInterface(std::string ifname) = 0; AddDefaultRouteViaInterface(NetworkInterface& vpn) = 0;
virtual void virtual void
DelDefaultRouteViaInterface(std::string ifname) = 0; DelDefaultRouteViaInterface(NetworkInterface& vpn) = 0;
virtual void virtual void
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; AddRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0;
@ -103,8 +107,8 @@ namespace llarp::vpn
virtual void virtual void
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0; DelRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0;
virtual std::vector<IPVariant_t> virtual std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(std::string ifname) = 0; GetGatewaysNotOnInterface(NetworkInterface& vpn) = 0;
virtual void virtual void
AddBlackhole(){}; AddBlackhole(){};
@ -117,20 +121,43 @@ namespace llarp::vpn
/// responsible for obtaining vpn interfaces /// responsible for obtaining vpn interfaces
class Platform 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: public:
Platform() = default; Platform() = default;
Platform(const Platform&) = delete; Platform(const Platform&) = delete;
Platform(Platform&&) = delete; Platform(Platform&&) = delete;
virtual ~Platform() = default; virtual ~Platform() = default;
/// get a new network interface fully configured given the interface info /// create and start a network interface
/// blocks until ready, throws on error inline std::shared_ptr<NetworkInterface>
virtual std::shared_ptr<NetworkInterface> CreateInterface(InterfaceInfo info, AbstractRouter* router)
ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0; {
if (auto netif = ObtainInterface(std::move(info), router))
{
netif->Start();
return netif;
}
return nullptr;
}
/// get owned ip route manager for managing routing table /// get owned ip route manager for managing routing table
virtual IRouteManager& virtual IRouteManager&
RouteManager() = 0; 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 /// create native vpn platform

@ -5,662 +5,191 @@
#include <io.h> #include <io.h>
#include <fcntl.h> #include <fcntl.h>
#include <llarp/util/thread/queue.hpp> #include <llarp/util/thread/queue.hpp>
#include <llarp/ev/vpn.hpp>
#include <llarp/router/abstractrouter.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> #include <fmt/std.h>
// DDK macros #include "platform.hpp"
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define FILE_DEVICE_UNKNOWN 0x00000022
#define FILE_ANY_ACCESS 0x00000000
#define METHOD_BUFFERED 0
/* From OpenVPN tap driver, common.h */
#define TAP_CONTROL_CODE(request, method) \
CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE(2, METHOD_BUFFERED)
#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE(3, METHOD_BUFFERED)
#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE(4, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE(5, METHOD_BUFFERED)
#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE(7, METHOD_BUFFERED)
#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE(8, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE(9, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE(10, METHOD_BUFFERED)
/* Windows registry crap */
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
#define NETWORK_ADAPTERS \
"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-" \
"08002BE10318}"
typedef unsigned long IPADDR; namespace llarp::win32
namespace llarp::vpn
{ {
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 namespace
{ {
std::wstring template <typename T>
get_win_sys_path() std::string
ip_to_string(T ip)
{ {
wchar_t win_sys_path[MAX_PATH] = {0}; return var::visit([](auto&& ip) { return ip.ToString(); }, ip);
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;
} }
} // namespace } // namespace
class Win32Interface final : public NetworkInterface using namespace llarp::vpn;
class VPNPlatform : public Platform, public IRouteManager
{ {
std::atomic<bool> m_Run; llarp::Context* const _ctx;
HANDLE m_Device, m_IOCP; const int m_Metric{2};
std::vector<std::thread> m_Threads;
thread::Queue<net::IPPacket> m_ReadQueue;
InterfaceInfo m_Info;
AbstractRouter* const _router; const auto&
Net() const
static std::string
NetSHCommand()
{ {
std::wstring wcmd = get_win_sys_path() + L"\\netsh.exe"; return _ctx->router->Net();
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
return converter.to_bytes(wcmd);
} }
static void void
NetSH(std::string commands) Route(std::string ip, std::string gw, std::string cmd)
{ {
commands = NetSHCommand() + " interface IPv6 " + commands; llarp::win32::Exec(
LogInfo("exec: ", commands); "route.exe",
::system(commands.c_str()); fmt::format("{} {} MASK 255.255.255.255 {} METRIC {}", cmd, ip, gw, m_Metric));
} }
public: void
static std::string DefaultRouteViaInterface(NetworkInterface& vpn, std::string cmd)
RouteCommand()
{ {
std::wstring wcmd = get_win_sys_path() + L"\\route.exe"; // route hole for loopback bacause god is dead on windows
llarp::win32::Exec(
using convert_type = std::codecvt_utf8<wchar_t>; "route.exe", fmt::format("{} 127.0.0.0 MASK 255.0.0.0 0.0.0.0 METRIC {}", cmd, m_Metric));
std::wstring_convert<convert_type, wchar_t> converter; // set up ipv4 routes
return converter.to_bytes(wcmd); auto lower = RouteViaInterface(vpn, "0.0.0.0", "128.0.0.0", cmd);
auto upper = RouteViaInterface(vpn, "128.0.0.0", "128.0.0.0", cmd);
} }
Win32Interface(InterfaceInfo info, AbstractRouter* router) OneShotExec
: m_ReadQueue{1024}, m_Info{std::move(info)}, _router{router} RouteViaInterface(NetworkInterface& vpn, std::string addr, std::string mask, std::string cmd)
{ {
DWORD len; const auto& info = vpn.Info();
auto index = info.index;
const auto device_id = reg_query(NETWORK_ADAPTERS); if (index == 0)
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"); if (auto maybe_idx = net::Platform::Default_ptr()->GetInterfaceIndex(info[0]))
throw std::invalid_argument{"cannot open " + fname}; index = *maybe_idx;
} }
LogInfo("putting interface up..."); auto ifaddr = ip_to_string(info[0]);
ULONG flag = 1; // this changes the last 1 to a 0 so that it routes over the interface
// put the interface up // this is required because windows is idiotic af
if (not DeviceIoControl( ifaddr.back()--;
m_Device, if (index)
TAP_IOCTL_SET_MEDIA_STATUS,
&flag,
sizeof(flag),
&flag,
sizeof(flag),
&len,
nullptr))
{ {
LogError("cannot up interface up"); return OneShotExec{
throw std::invalid_argument{"cannot put interface up"}; "route.exe",
fmt::format(
"{} {} MASK {} {} IF {} METRIC {}", cmd, addr, mask, ifaddr, info.index, m_Metric)};
} }
else
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) return OneShotExec{
{ "route.exe",
const auto maybe = GetInterfaceIndex(ip); fmt::format("{} {} MASK {} {} METRIC {}", cmd, addr, mask, ifaddr, m_Metric)};
if (maybe.has_value())
{
NetSH(
"add address interface=" + std::to_string(*maybe) + " " + ifaddr.range.ToString());
}
}
} }
} }
~Win32Interface() std::shared_ptr<WintunContext> _wintun;
{
ULONG flag = 0;
DWORD len;
// put the interface down
DeviceIoControl(
m_Device,
TAP_IOCTL_SET_MEDIA_STATUS,
&flag,
sizeof(flag),
&flag,
sizeof(flag),
&len,
nullptr);
m_Run = false;
CloseHandle(m_IOCP);
// close the handle
CloseHandle(m_Device);
// close the reader threads
for (auto& thread : m_Threads)
thread.join();
}
virtual void
MaybeWakeUpperLayers() const override
{
_router->TriggerPump();
}
int
PollFD() const override
{
return -1;
}
std::string
IfName() const override
{
return m_Info.ifname;
}
void
Start()
{
m_Run = true;
const auto numThreads = std::thread::hardware_concurrency();
// allocate packets
for (size_t idx = 0; idx < numThreads; ++idx)
m_Packets.emplace_back(new asio_evt_pkt{true});
// create completion port
m_IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
// attach the handle or some shit
CreateIoCompletionPort(m_Device, m_IOCP, 0, 0);
// spawn reader threads
for (size_t idx = 0; idx < numThreads; ++idx)
m_Threads.emplace_back([this, idx]() { ReadLoop(idx); });
}
net::IPPacket
ReadNextPacket() override
{
if (m_ReadQueue.empty())
return net::IPPacket{};
return m_ReadQueue.popFront();
}
struct asio_evt_pkt
{
explicit asio_evt_pkt(bool _read) : read{_read}
{}
OVERLAPPED hdr = {0, 0, 0, 0, nullptr}; // must be first, since this is part of the IO call WinDivert_API m_WinDivert{};
bool read;
net::IPPacket pkt;
void public:
Read(HANDLE dev) VPNPlatform(const VPNPlatform&) = delete;
{ VPNPlatform(VPNPlatform&&) = delete;
ReadFile(dev, pkt.buf, sizeof(pkt.buf), nullptr, &hdr);
}
};
std::vector<std::unique_ptr<asio_evt_pkt>> m_Packets; VPNPlatform(llarp::Context* ctx) : Platform{}, _ctx{ctx}, _wintun{WintunContext_new()}
{}
bool virtual ~VPNPlatform() = default;
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;
}
void void
ReadLoop(size_t packetIndex) AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
{ {
auto& ev = m_Packets[packetIndex]; Route(ip_to_string(ip), ip_to_string(gateway), "ADD");
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 void
Execute(std::string cmd) const DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
{ {
llarp::LogInfo("exec: ", cmd); Route(ip_to_string(ip), ip_to_string(gateway), "DELETE");
::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 void
Route(IPVariant_t ip, IPVariant_t gateway, std::string cmd) AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{ {
Execute(fmt::format( RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "ADD");
"{} {} {} MASK 255.255.255.255 {} METRIC 2", RouteCommand(), cmd, ip, gateway));
} }
void void
DefaultRouteViaInterface(std::string ifname, std::string cmd) DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{ {
// poke hole for loopback bacause god is dead on windows RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "DELETE");
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 std::vector<net::ipaddr_t>
RouteViaInterface(std::string ifname, IPRange range, std::string cmd) GetGatewaysNotOnInterface(NetworkInterface& vpn) override
{ {
if (range.IsV4()) std::vector<net::ipaddr_t> gateways;
{
const huint32_t addr4 = net::TruncateV6(range.addr); auto idx = vpn.Info().index;
const huint32_t mask4 = net::TruncateV6(range.netmask_bits); using UInt_t = decltype(idx);
Execute( for (const auto& iface : Net().AllNetworkInterfaces())
RouteCommand() + " " + cmd + " " + addr4.ToString() + " MASK " + mask4.ToString() + " "
+ ifname);
}
else
{ {
Execute( if (static_cast<UInt_t>(iface.index) == idx)
RouteCommand() + " " + cmd + range.addr.ToString() + " MASK " continue;
+ range.netmask_bits.ToString() + " " + ifname); if (iface.gateway)
gateways.emplace_back(*iface.gateway);
} }
} return gateways;
public:
void
AddRoute(IPVariant_t ip, IPVariant_t gateway) override
{
Route(ip, gateway, "ADD");
} }
void void
DelRoute(IPVariant_t ip, IPVariant_t gateway) override AddDefaultRouteViaInterface(NetworkInterface& vpn) override
{ {
Route(ip, gateway, "DELETE"); // kill ipv6
} llarp::win32::Exec(
"WindowsPowerShell\\v1.0\\powershell.exe",
"-Command (Disable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
void DefaultRouteViaInterface(vpn, "ADD");
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override llarp::win32::Exec("ipconfig.exe", "/flushdns");
{
RouteViaInterface(vpn.IfName(), range, "ADD");
} }
void void
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override DelDefaultRouteViaInterface(NetworkInterface& vpn) override
{ {
RouteViaInterface(vpn.IfName(), range, "DELETE"); // restore ipv6
} llarp::win32::Exec(
"WindowsPowerShell\\v1.0\\powershell.exe",
"-Command (Enable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
std::vector<IPVariant_t> DefaultRouteViaInterface(vpn, "DELETE");
GetGatewaysNotOnInterface(std::string ifname) override llarp::win32::Exec("netsh.exe", "winsock reset");
{ llarp::win32::Exec("ipconfig.exe", "/flushdns");
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)
{
llarp::LogTrace(
"Win32 find gateway: Adding gateway (", interface_name, ") to list of gateways.");
huint32_t x{};
if (x.FromString(interface_name))
gateways.push_back(x);
}
});
return gateways;
} }
void std::shared_ptr<NetworkInterface>
AddDefaultRouteViaInterface(std::string ifname) override ObtainInterface(InterfaceInfo info, AbstractRouter* router) override
{ {
// kill ipv6 return WintunInterface_new(_wintun, std::move(info), router);
Execute(PowerShell() + R"(Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)");
DefaultRouteViaInterface(ifname, "ADD");
} }
void std::shared_ptr<I_Packet_IO>
DelDefaultRouteViaInterface(std::string ifname) override create_packet_io(unsigned int ifindex) override
{ {
// restore ipv6 // we only want do this on all interfaes with windivert
Execute(PowerShell() + R"(Enable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)"); if (ifindex)
DefaultRouteViaInterface(ifname, "DELETE"); throw std::invalid_argument{
"cannot create packet io on explicitly specified interface, not currently supported on "
"windows (yet)"};
return m_WinDivert.make_intercepter(
"outbound and ( udp.DstPort == 53 or tcp.DstPort == 53 )",
[router = _ctx->router] { router->TriggerPump(); });
} }
};
class Win32Platform : public Platform
{
Win32RouteManager _routeManager{};
public:
std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo info, AbstractRouter* router) override
{
auto netif = std::make_shared<Win32Interface>(std::move(info), router);
netif->Start();
return netif;
};
IRouteManager& IRouteManager&
RouteManager() override 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 #pragma once
#include <array>
#include <string>
#include <windows.h> #include <windows.h>
#include <string>
#include <stdexcept> #include <stdexcept>
#include <llarp/util/str.hpp>
namespace llarp::win32 namespace llarp::win32
{ {
namespace std::string
{ error_to_string(DWORD err);
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
class error : public std::runtime_error class error : public std::runtime_error
{ {
public: public:
error(DWORD err, std::string msg) error(std::string msg);
: std::runtime_error{fmt::format("{}: {}", msg, error_to_string(err))} virtual ~error() = default;
{} explicit error(DWORD err, std::string msg);
}; };
} // namespace llarp::win32 } // namespace llarp::win32

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save