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',
'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix',
'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix',
'VERBOSE=1 JOBS=' + jobs + ' ./contrib/windows.sh ' + ci_mirror_opts,
'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts,
] + extra_cmds,
},
],

@ -61,6 +61,8 @@ option(WITH_HIVE "build simulation stubs" OFF)
option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF)
option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
option(WITH_PEERSTATS "build with experimental peerstats db support" OFF)
option(STRIP_SYMBOLS "strip off all debug symbols into an external archive for all executables built" OFF)
include(cmake/enable_lto.cmake)
@ -81,6 +83,12 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
set(debug OFF)
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
set(debug ON)
add_definitions(-DLOKINET_DEBUG)
endif()
if(BUILD_STATIC_DEPS AND STATIC_LINK)
message(STATUS "we are building static deps so we won't build shared libs")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
@ -171,11 +179,18 @@ if(NOT TARGET sodium)
export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake)
endif()
if(NOT APPLE)
add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla)
if(APPLE)
add_compile_options(-Wno-deprecated-declarations)
else()
add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werror=vla)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-unknown-warning-option)
endif()
if(debug)
add_compile_options(-Wdeprecated-declarations)
else()
add_compile_options(-Wno-deprecated-declarations)
endif()
endif()
if(XSAN)
@ -186,11 +201,6 @@ if(XSAN)
message(STATUS "Doing a ${XSAN} sanitizer build")
endif()
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
add_definitions(-DLOKINET_DEBUG)
endif()
include(cmake/coverage.cmake)
# these vars are set by the cmake toolchain spec
@ -317,6 +327,10 @@ if(NOT TARGET uninstall)
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
if(BUILD_PACKAGE)
include(cmake/installer.cmake)
if(BUILD_PACKAGE AND NOT APPLE)
include(cmake/installer.cmake)
endif()
if(TARGET package)
add_dependencies(package assemble_gui)
endif()

@ -2,6 +2,9 @@
include(CheckIPOSupported)
option(WITH_LTO "enable lto on compile time" ON)
if(WITH_LTO)
if(WIN32)
message(FATAL_ERROR "LTO not supported on win32 targets, please set -DWITH_LTO=OFF")
endif()
check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error)
if(IPO_ENABLED)
message(STATUS "LTO enabled")

@ -8,25 +8,30 @@ elseif(WIN32)
set(default_gui_target win32)
endif()
if(WIN32)
option(GUI_EXE "path to an externally built lokinet gui.exe" OFF)
endif()
option(BUILD_GUI "build electron gui from 'gui' submodule source" ${default_build_gui})
set(GUI_YARN_TARGET "${default_gui_target}" CACHE STRING "yarn target for building the GUI")
set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn build command")
if (BUILD_GUI)
message(STATUS "Building lokinet-gui")
# allow manually specifying yarn with -DYARN=
if(NOT YARN)
find_program(YARN NAMES yarn yarnpkg REQUIRED)
find_program(YARN NAMES yarnpkg yarn REQUIRED)
endif()
message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}")
set(wine_env)
if(WIN32)
set(wine_env USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix")
endif()
add_custom_target(lokinet-gui
COMMAND ${YARN} install --frozen-lockfile &&
${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
if(NOT WIN32)
add_custom_target(lokinet-gui
COMMAND ${YARN} install --frozen-lockfile &&
${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
endif()
if(APPLE)
add_custom_target(assemble_gui ALL
@ -38,14 +43,13 @@ if (BUILD_GUI)
COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns"
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/"
COMMAND /usr/libexec/PlistBuddy
-c "Delete :CFBundleDisplayName"
-c "Add :LSHasLocalizedDisplayName bool true"
-c "Add :CFBundleDevelopmentRegion string en"
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
"${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
-c "Delete :CFBundleDisplayName"
-c "Add :LSHasLocalizedDisplayName bool true"
-c "Add :CFBundleDevelopmentRegion string en"
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
"${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
)
elseif(WIN32)
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui")
option(GUI_ZIP_FILE "custom lokinet gui for windows from zip file" OFF)
@ -53,20 +57,28 @@ if (BUILD_GUI)
message(STATUS "using custom lokinet gui from ${GUI_ZIP_FILE}")
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true")
elseif(GUI_EXE)
message(STATUS "using custom lokinet gui executable: ${GUI_EXE}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GUI_EXE}" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true")
else()
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
DEPENDS lokinet lokinet-gui
# FIXME: we really shouldn't be building inside the source directory but this is npm...
COMMAND ${YARN} install --frozen-lockfile &&
USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all WINEPREFIX="${PROJECT_BINARY_DIR}/wineprefix" ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe"
"${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
)
add_custom_target(assemble_gui ALL
DEPENDS ${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe)
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
endif()
add_custom_target(assemble_gui ALL COMMAND "true" DEPENDS "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
else()
message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform")
endif()
else()
message(STATUS "not building gui")
endif()
if(NOT TARGET assemble_gui)
add_custom_target(assemble_gui COMMAND "true")
endif()

@ -5,11 +5,42 @@ set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
if(WIN32)
include(cmake/win32_installer_deps.cmake)
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-exit.ini DESTINATION share/conf.d COMPONENT exit_configs)
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-keyfile.ini DESTINATION share/conf.d COMPONENT keyfile_configs)
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-debug-log.ini DESTINATION share/conf.d COMPONENT debug_configs)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified" "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs")
list(APPEND CPACK_COMPONENTS_ALL "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs")
elseif(APPLE)
set(CPACK_GENERATOR DragNDrop;ZIP)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
endif()
# This must always be last!
include(CPack)
if(WIN32)
cpack_add_component(lokinet
DISPLAY_NAME "lokinet"
DESCRIPTION "core required lokinet files"
REQUIRED)
cpack_add_component(gui
DISPLAY_NAME "lokinet gui"
DESCRIPTION "electron based control panel for lokinet")
cpack_add_component(exit_configs
DISPLAY_NAME "auto-enable exit"
DESCRIPTION "automatically enable usage of exit.loki as an exit node\n"
DISABLED)
cpack_add_component(keyfile_configs
DISPLAY_NAME "persist address"
DESCRIPTION "persist .loki address across restarts of lokinet\nnot recommended when enabling exit nodes"
DISABLED)
cpack_add_component(debug_configs
DISPLAY_NAME "debug logging"
DESCRIPTION "enable debug spew log level by default"
DISABLED)
endif()

@ -1,32 +1,52 @@
if(NOT WIN32)
return()
endif()
if (NOT STATIC_LINK)
message(FATAL_ERROR "windows requires static builds (thanks balmer)")
endif()
enable_language(RC)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
if(NOT MSVC_VERSION)
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-bad-function-cast>)
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-cast-function-type>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fpermissive>)
# unlike unix where you get a *single* compiler ID string in .comment
# GNU ld sees fit to merge *all* the .ident sections in object files
# to .r[o]data section one after the other!
add_compile_options(-fno-ident -Wa,-mbig-obj)
link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
# the minimum windows version, set to 6 rn because supporting older windows is hell
set(_winver 0x0600)
add_definitions(-DWINVER=${_winver} -D_WIN32_WINNT=${_winver})
endif()
option(WITH_WINDOWS_32 "build 32 bit windows" OFF)
# unlike unix where you get a *single* compiler ID string in .comment
# GNU ld sees fit to merge *all* the .ident sections in object files
# to .r[o]data section one after the other!
add_compile_options(-fno-ident -Wa,-mbig-obj)
# the minimum windows version, set to 6 rn because supporting older windows is hell
set(_winver 0x0600)
add_definitions(-D_WIN32_WINNT=${_winver})
if(EMBEDDED_CFG)
link_libatomic()
endif()
add_definitions(-DWIN32_LEAN_AND_MEAN -DWIN32)
set(WINTUN_VERSION 0.14.1 CACHE STRING "wintun version")
set(WINTUN_MIRROR https://www.wintun.net/builds
CACHE STRING "wintun mirror(s)")
set(WINTUN_SOURCE wintun-${WINTUN_VERSION}.zip)
set(WINTUN_HASH SHA256=07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51
CACHE STRING "wintun source hash")
if (NOT STATIC_LINK AND NOT MSVC)
message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll")
message("for release builds, turn on STATIC_LINK in cmake options")
endif()
set(WINDIVERT_VERSION 2.2.0-A CACHE STRING "windivert version")
set(WINDIVERT_MIRROR https://reqrypt.org/download
CACHE STRING "windivert mirror(s)")
set(WINDIVERT_SOURCE WinDivert-${WINDIVERT_VERSION}.zip)
set(WINDIVERT_HASH SHA256=2a7630aac0914746fbc565ac862fa096e3e54233883ac52d17c83107496b7a7f
CACHE STRING "windivert source hash")
set(WINTUN_URL ${WINTUN_MIRROR}/${WINTUN_SOURCE}
CACHE STRING "wintun download url")
set(WINDIVERT_URL ${WINDIVERT_MIRROR}/${WINDIVERT_SOURCE}
CACHE STRING "windivert download url")
message(STATUS "Downloading wintun from ${WINTUN_URL}")
file(DOWNLOAD ${WINTUN_URL} ${CMAKE_BINARY_DIR}/wintun.zip EXPECTED_HASH ${WINTUN_HASH})
message(STATUS "Downloading windivert from ${WINDIVERT_URL}")
file(DOWNLOAD ${WINDIVERT_URL} ${CMAKE_BINARY_DIR}/windivert.zip EXPECTED_HASH ${WINDIVERT_HASH})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/wintun.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/windivert.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

@ -1,11 +1,3 @@
set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe")
set(TUNTAP_EXE "${CMAKE_BINARY_DIR}/tuntap-install.exe")
set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed")
file(DOWNLOAD
${TUNTAP_URL}
${TUNTAP_EXE})
if(NOT BUILD_GUI)
if(NOT GUI_ZIP_URL)
set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip")
@ -25,9 +17,19 @@ if(NOT BUILD_GUI)
message(FATAL_ERROR "Downloaded gui archive from ${GUI_ZIP_URL} does not contain gui/lokinet-gui.exe!")
endif()
endif()
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap)
if(WITH_WINDOWS_32)
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/x86/wintun.dll DESTINATION bin COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.sys DESTINATION lib COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.dll DESTINATION bin COMPONENT lokinet)
else()
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/amd64/wintun.dll DESTINATION bin COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert64.sys DESTINATION lib COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert.dll DESTINATION bin COMPONENT lokinet)
endif()
set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed")
install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed)
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet")
@ -35,7 +37,6 @@ set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico")
set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
function(read_nsis_file filename outvar)
file(STRINGS "${filename}" _outvar)
list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\")
@ -51,9 +52,9 @@ read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_
set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}")
set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}")
set(CPACK_NSIS_MODIFY_PATH ON)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")

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

@ -34,8 +34,11 @@ else
fi
mkdir -v "$base"
if [ -e build-windows ]; then
cp -av build-windows/lokinet-*.exe "$base"
if [ -e build/win32 ]; then
# save debug symbols
cp -av build/win32/daemon/debug-symbols.tar.xz "$base-debug-symbols.tar.xz"
# save installer
cp -av build/win32/*.exe "$base"
# zipit up yo
archive="$base.zip"
zip -r "$archive" "$base"
@ -77,6 +80,10 @@ $mkdirs
put $archive $upload_to
SFTP
if [ -e "$base-debug-symbols.tar.xz" ] ; then
sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<<"put $base-debug-symbols.tar.xz $upload_to"
fi
set +o xtrace
echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n"

@ -0,0 +1,2 @@
[logging]
level=debug

@ -0,0 +1,5 @@
#
# "suggested" default exit node config
#
[network]
exit-node=exit.loki

@ -0,0 +1,5 @@
#
# persist .loki address in a private key file in the data dir
#
[network]
keyfile=lokinet-addr.privkey

@ -6,9 +6,8 @@
set -e
set +x
root="$(readlink -f $(dirname $0)/../)"
cd "$root"
./contrib/windows-configure.sh . build-windows "$@"
make package -j${JOBS:-$(nproc)} -C build-windows
mkdir -p $root/build/win32
$root/contrib/windows-configure.sh $root $root/build/win32 "$@"
make package -j${JOBS:-$(nproc)} -C $root/build/win32

@ -46,15 +46,27 @@ if(WITH_BOOTSTRAP)
endif()
endif()
# cmake interface library for bunch of cmake hacks to fix final link order
add_library(hax_and_shims_for_cmake INTERFACE)
if(WIN32)
target_link_libraries(hax_and_shims_for_cmake INTERFACE uvw oxenmq::oxenmq -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
endif()
foreach(exe ${exetargets})
if(WIN32 AND NOT MSVC_VERSION)
if(WIN32)
target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc)
target_compile_options(${exe} PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fpermissive>)
target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00)
target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_link_directories(${exe} PRIVATE /usr/local/lib)
endif()
target_link_libraries(${exe} PUBLIC liblokinet)
target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake)
if(STRIP_SYMBOLS)
add_custom_command(TARGET ${exe}
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ARGS --only-keep-debug $<TARGET_FILE:${exe}> $<TARGET_FILE:${exe}>.debug
COMMAND ${CMAKE_STRIP} ARGS --strip-all $<TARGET_FILE:${exe}>)
endif()
target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}")
if(should_install)
if(APPLE)
@ -71,3 +83,9 @@ endforeach()
if(SETCAP)
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
endif()
if(STRIP_SYMBOLS)
add_custom_target(symbols ALL
COMMAND ${CMAKE_COMMAND} -E tar cJf ${CMAKE_CURRENT_BINARY_DIR}/debug-symbols.tar.xz $<TARGET_FILE:lokinet>.debug
DEPENDS lokinet)
endif()

@ -101,7 +101,7 @@ install_win32_daemon()
// Create the service
schService = CreateService(
schSCManager, // SCM database
"lokinet", // name of service
strdup("lokinet"), // name of service
"Lokinet for Windows", // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
@ -134,10 +134,10 @@ insert_description()
SC_HANDLE schSCManager;
SC_HANDLE schService;
SERVICE_DESCRIPTION sd;
LPTSTR szDesc =
LPTSTR szDesc = strdup(
"LokiNET is a free, open source, private, "
"decentralized, \"market based sybil resistant\" "
"and IP based onion routing network";
"and IP based onion routing network");
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
@ -270,7 +270,7 @@ run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions o
}
catch (std::exception& ex)
{
llarp::LogError("failed to start up lokinet: {}", ex.what());
llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what()));
exit_code.set_value(1);
return;
}
@ -367,7 +367,7 @@ main(int argc, char* argv[])
return lokinet_main(argc, argv);
#else
SERVICE_TABLE_ENTRY DispatchTable[] = {
{"lokinet", (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
{strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
if (lstrcmpi(argv[1], "--win32-daemon") == 0)
{
start_as_daemon = true;
@ -680,7 +680,7 @@ win32_daemon_entry(DWORD argc, LPTSTR* argv)
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
// SCM clobbers startup args, regenerate them here
argc = 2;
argv[1] = "c:/programdata/lokinet/lokinet.ini";
argv[1] = strdup("c:/programdata/lokinet/lokinet.ini");
argv[2] = nullptr;
lokinet_main(argc, argv);
}

@ -2,4 +2,4 @@ add_library(lokinet-android
SHARED
lokinet_config.cpp
lokinet_daemon.cpp)
target_link_libraries(lokinet-android liblokinet)
target_link_libraries(lokinet-android lokinet-amalgum)

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

@ -18,12 +18,9 @@ target_sources(lokinet-platform PRIVATE vpn_platform.cpp vpn_interface.cpp route
add_executable(lokinet-extension MACOSX_BUNDLE
PacketTunnelProvider.m
DNSTrampoline.m
)
)
enable_lto(lokinet-extension)
target_link_libraries(lokinet-extension PRIVATE
liblokinet
${COREFOUNDATION}
${NETEXT})
# -fobjc-arc enables automatic reference counting for objective-C code
# -e _NSExtensionMain because the appex has that instead of a `main` function entry point, of course.
@ -43,6 +40,11 @@ else()
set(product_type com.apple.product-type.app-extension)
endif()
target_link_libraries(lokinet-extension PRIVATE
lokinet-amalgum
${COREFOUNDATION}
${NETEXT})
set_target_properties(lokinet-extension PROPERTIES
BUNDLE TRUE
BUNDLE_EXTENSION ${bundle_ext}

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

@ -140,14 +140,14 @@ namespace llarp
"this setting specifies the public IP at which this router is reachable. When",
"provided the public-port option must also be specified.",
},
[this](std::string arg) {
[this, net = params.Net_ptr()](std::string arg) {
if (arg.empty())
return;
nuint32_t addr{};
if (not addr.FromString(arg))
throw std::invalid_argument{fmt::format("{} is not a valid IPv4 address", arg)};
if (IsIPv4Bogon(addr))
if (net->IsBogonIP(addr))
throw std::invalid_argument{
fmt::format("{} is not a publicly routable ip address", addr)};
@ -648,6 +648,7 @@ namespace llarp
throw std::invalid_argument{
fmt::format("[network]:ip6-range invalid value: '{}'", arg)};
});
// TODO: could be useful for snodes in the future, but currently only implemented for clients:
conf.defineOption<std::string>(
"network",
@ -813,6 +814,30 @@ namespace llarp
}
});
conf.defineOption<bool>(
"dns",
"l3-intercept",
Default{
platform::is_windows or platform::is_android
or (platform::is_macos and not platform::is_apple_sysex)},
Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface "
"instead of binding a local udp socket"},
AssignmentAcceptor(m_raw_dns));
conf.defineOption<std::string>(
"dns",
"query-bind",
#ifdef __APPLE__
Default{"127.0.0.1:1253"},
#endif
#ifdef _WIN32
Default{"0.0.0.0:0"},
#endif
Comment{
"Address to bind to for sending upstream DNS requests.",
},
[this](std::string arg) { m_QueryBind = SockAddr{arg}; });
conf.defineOption<std::string>(
"dns",
"bind",

@ -155,9 +155,12 @@ namespace llarp
struct DnsConfig
{
bool m_raw_dns;
std::vector<SockAddr> m_bind;
std::vector<SockAddr> m_upstreamDNS;
std::vector<fs::path> m_hostfiles;
std::optional<SockAddr> m_QueryBind;
std::unordered_multimap<std::string, std::string> m_ExtraOpts;
void

@ -0,0 +1,7 @@
#pragma once
namespace llarp::constants
{
constexpr auto udp_header_bytes = 8;
constexpr auto ip_header_min_bytes = 20;
} // namespace llarp::constants

@ -11,8 +11,9 @@ namespace llarp::platform
false
#endif
;
/// we have systemd ?
inline constexpr bool has_systemd =
/// building with systemd enabled ?
inline constexpr bool with_systemd =
#ifdef WITH_SYSTEMD
true
#else
@ -47,6 +48,15 @@ namespace llarp::platform
#endif
;
/// are we building as an apple system extension
inline constexpr bool is_apple_sysex =
#ifdef MACOS_SYSTEM_EXTENSION
true
#else
false
#endif
;
/// are we an android platform ?
inline constexpr bool is_android =
#ifdef ANDROID
@ -65,9 +75,26 @@ namespace llarp::platform
#endif
;
/// are we running with pybind simulation mode enabled?
inline constexpr bool is_simulation =
#ifdef LOKINET_HIVE
true
#else
false
#endif
;
/// do we have systemd support ?
// on cross compiles sometimes weird permutations of target and host make this value not correct,
// this ensures it always is
inline constexpr bool has_systemd = is_linux and with_systemd and not(is_android or is_windows);
/// are we using macos ?
inline constexpr bool is_macos = is_apple and not is_iphone;
/// are we a mobile phone ?
inline constexpr bool is_mobile = is_android or is_iphone;
/// does this platform support native ipv6 ?
// TODO: make windows support ipv6
inline constexpr bool supports_ipv6 = not is_windows;
} // namespace llarp::platform

@ -1,22 +1 @@
#pragma once
#include "platform.hpp"
#include <vector>
namespace llarp::dns
{
/// a collection of dns platforms that are tried in order when setting dns
class Multi_Platform : public I_Platform
{
std::vector<std::unique_ptr<I_Platform>> m_Impls;
public:
/// add a platform to be owned
void
add_impl(std::unique_ptr<I_Platform> impl);
/// try all owned platforms to set the resolver, throws if none of them work
void
set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override;
};
} // namespace llarp::dns

@ -13,7 +13,7 @@ using namespace std::literals;
namespace llarp::dns::nm
{
void
Platform::set_resolver(std::string, llarp::SockAddr, bool)
Platform::set_resolver(unsigned int, llarp::SockAddr, bool)
{
// todo: implement me eventually
}

@ -17,7 +17,7 @@ namespace llarp::dns
virtual ~Platform() = default;
void
set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override;
set_resolver(unsigned int index, llarp::SockAddr dns, bool global) override;
};
}; // namespace nm
using NM_Platform_t = std::conditional_t<false, nm::Platform, Null_Platform>;

@ -1,14 +1 @@
#pragma once
#include "platform.hpp"
namespace llarp::dns
{
/// a dns platform does silently does nothing, successfully
class Null_Platform : public I_Platform
{
public:
void
set_resolver(std::string, llarp::SockAddr, bool) override
{}
};
} // namespace llarp::dns

@ -1,4 +1,4 @@
#include "multi_platform.hpp"
#include "platform.hpp"
namespace llarp::dns
{
@ -9,14 +9,14 @@ namespace llarp::dns
}
void
Multi_Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global)
Multi_Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool global)
{
size_t fails{0};
for (const auto& ptr : m_Impls)
{
try
{
ptr->set_resolver(ifname, dns, global);
ptr->set_resolver(index, dns, global);
}
catch (std::exception& ex)
{

@ -3,9 +3,12 @@
#include <memory>
#include <llarp/net/sock_addr.hpp>
#include <llarp/util/logging.hpp>
#include <stdexcept>
#ifndef _WIN32
#include <net/if.h>
#endif
namespace llarp::dns
{
/// sets dns settings in a platform dependant way
@ -18,13 +21,39 @@ namespace llarp::dns
/// throws if unsupported or fails.
///
///
/// \param if_name -- the interface name to which we add the DNS servers, e.g. lokitun0.
/// Typically tun_endpoint.GetIfName().
/// \param if_index -- the interface index to which we add the DNS servers, this can be gotten
/// from the interface name e.g. lokitun0 (Typically tun_endpoint.GetIfName().) and then put
/// through if_nametoindex().
/// \param dns -- the listening address of the lokinet DNS server
/// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
/// addresses (false).
virtual void
set_resolver(std::string if_name, llarp::SockAddr dns, bool global) = 0;
set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) = 0;
};
/// a dns platform does silently does nothing, successfully
class Null_Platform : public I_Platform
{
public:
~Null_Platform() override = default;
void
set_resolver(unsigned int, llarp::SockAddr, bool) override
{}
};
/// a collection of dns platforms that are tried in order when setting dns
class Multi_Platform : public I_Platform
{
std::vector<std::unique_ptr<I_Platform>> m_Impls;
public:
~Multi_Platform() override = default;
/// add a platform to be owned
void
add_impl(std::unique_ptr<I_Platform> impl);
/// try all owned platforms to set the resolver, throws if none of them work
void
set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override;
};
} // namespace llarp::dns

@ -13,14 +13,8 @@ using namespace std::literals;
namespace llarp::dns::sd
{
void
Platform::set_resolver(std::string ifname, llarp::SockAddr dns, bool global)
Platform::set_resolver(unsigned int if_ndx, llarp::SockAddr dns, bool global)
{
unsigned int if_ndx = if_nametoindex(ifname.c_str());
if (if_ndx == 0)
{
throw std::runtime_error{"No such interface '" + ifname + "'"};
}
linux::DBUS _dbus{
"org.freedesktop.resolve1",
"/org/freedesktop/resolve1",

@ -16,7 +16,7 @@ namespace llarp::dns
virtual ~Platform() = default;
void
set_resolver(std::string ifname, llarp::SockAddr dns, bool global) override;
set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override;
};
} // namespace sd
using SD_Platform_t =

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

@ -67,17 +67,62 @@ namespace llarp::dns
BoundOn() const = 0;
};
/// a packet source which will override the sendto function of an wrapped packet source to
/// construct a raw ip packet as a reply
class PacketSource_Wrapper : public PacketSource_Base
{
std::weak_ptr<PacketSource_Base> m_Wrapped;
std::function<void(net::IPPacket)> m_WritePacket;
public:
explicit PacketSource_Wrapper(
std::weak_ptr<PacketSource_Base> wrapped, std::function<void(net::IPPacket)> write_packet)
: m_Wrapped{wrapped}, m_WritePacket{write_packet}
{}
bool
WouldLoop(const SockAddr& to, const SockAddr& from) const override
{
if (auto ptr = m_Wrapped.lock())
return ptr->WouldLoop(to, from);
return true;
}
void
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
{
m_WritePacket(net::IPPacket::make_udp(to, from, std::move(buf)));
}
/// stop reading packets and end operation
void
Stop() override
{
if (auto ptr = m_Wrapped.lock())
ptr->Stop();
}
/// returns the sockaddr we are bound on if applicable
std::optional<SockAddr>
BoundOn() const override
{
if (auto ptr = m_Wrapped.lock())
return ptr->BoundOn();
return std::nullopt;
}
};
/// non complex implementation of QueryJob_Base for use in things that
/// only ever called on the mainloop thread
class QueryJob : public QueryJob_Base, std::enable_shared_from_this<QueryJob>
{
std::weak_ptr<PacketSource_Base> src;
std::shared_ptr<PacketSource_Base> src;
const SockAddr resolver;
const SockAddr asker;
public:
explicit QueryJob(
std::weak_ptr<PacketSource_Base> source,
std::shared_ptr<PacketSource_Base> source,
const Message& query,
const SockAddr& to_,
const SockAddr& from_)
@ -87,8 +132,7 @@ namespace llarp::dns
void
SendReply(llarp::OwnedBuffer replyBuf) const override
{
if (auto ptr = src.lock())
ptr->SendTo(asker, resolver, std::move(replyBuf));
src->SendTo(asker, resolver, std::move(replyBuf));
}
};
@ -119,6 +163,13 @@ namespace llarp::dns
return Rank() > other.Rank();
}
/// get local socket address that queries are sent from
virtual std::optional<SockAddr>
GetLocalAddr() const
{
return std::nullopt;
}
/// get printable name
virtual std::string_view
ResolverName() const = 0;
@ -134,7 +185,7 @@ namespace llarp::dns
/// returns true if we consumed this query and it should not be processed again
virtual bool
MaybeHookDNS(
std::weak_ptr<PacketSource_Base> source,
std::shared_ptr<PacketSource_Base> source,
const Message& query,
const SockAddr& to,
const SockAddr& from) = 0;
@ -167,7 +218,8 @@ namespace llarp::dns
public:
virtual ~Server() = default;
explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, std::string netif_name);
explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif_index);
/// returns all sockaddr we have from all of our PacketSources
std::vector<SockAddr>
@ -205,16 +257,18 @@ namespace llarp::dns
virtual std::shared_ptr<Resolver_Base>
MakeDefaultResolver();
/// feed a packet buffer from a packet source
std::vector<std::weak_ptr<Resolver_Base>>
GetAllResolvers() const;
/// feed a packet buffer from a packet source.
/// returns true if we decided to process the packet and consumed it
/// returns false if we dont want to process the packet
bool
MaybeHandlePacket(
std::weak_ptr<PacketSource_Base> pktsource,
std::shared_ptr<PacketSource_Base> pktsource,
const SockAddr& resolver,
const SockAddr& from,
llarp::OwnedBuffer buf);
/// set which dns mode we are in.
/// true for intercepting all queries. false for just .loki and .snode
void
@ -226,7 +280,7 @@ namespace llarp::dns
std::shared_ptr<I_Platform> m_Platform;
private:
const std::string m_NetifName;
const unsigned int m_NetIfIndex;
std::set<std::shared_ptr<Resolver_Base>, ComparePtr<std::shared_ptr<Resolver_Base>>>
m_OwnedResolvers;
std::set<std::weak_ptr<Resolver_Base>, CompareWeakPtr<Resolver_Base>> m_Resolvers;

@ -0,0 +1,51 @@
#include "win32_platform.hpp"
#include <llarp/net/net.hpp>
namespace llarp::dns::win32
{
void
Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool)
{
#ifdef _WIN32
// clear any previous dns settings
m_UndoDNS.clear();
auto interfaces = m_Loop->Net_ptr()->AllNetworkInterfaces();
// remove dns
{
std::vector<llarp::win32::OneShotExec> jobs;
for (const auto& ent : interfaces)
{
if (ent.index == index)
continue;
jobs.emplace_back(
"netsh.exe", fmt::format("interface ipv4 delete dns \"{}\" all", ent.name));
jobs.emplace_back(
"netsh.exe", fmt::format("interface ipv6 delete dns \"{}\" all", ent.name));
}
}
// add new dns
{
std::vector<llarp::win32::OneShotExec> jobs;
for (const auto& ent : interfaces)
{
if (ent.index == index)
continue;
jobs.emplace_back(
"netsh.exe",
fmt::format("interface ipv4 add dns \"{}\" {} validate=no", ent.name, dns.asIPv4()));
jobs.emplace_back(
"netsh.exe",
fmt::format("interface ipv6 add dns \"{}\" {} validate=no", ent.name, dns.asIPv6()));
m_UndoDNS.emplace_back("netsh.exe", fmt::format("", index));
}
m_UndoDNS.emplace_back("netsh.exe", "winsock reset");
}
// flush dns
llarp::win32::Exec("ipconfig.exe", "/flushdns");
#endif
}
} // namespace llarp::dns::win32

@ -0,0 +1,8 @@
#pragma once
#include "platform.hpp"
namespace llarp::dns
{
// TODO: implement me
using Win32_Platform_t = Null_Platform;
} // namespace llarp::dns

@ -6,8 +6,8 @@
#include <cstring>
#include <string_view>
// We libuv now
#include "ev_libuv.hpp"
#include "libuv.hpp"
#include <llarp/net/net.hpp>
namespace llarp
{
@ -16,4 +16,11 @@ namespace llarp
{
return std::make_shared<llarp::uv::Loop>(queueLength);
}
const net::Platform*
EventLoop::Net_ptr() const
{
return net::Platform::Default_ptr();
}
} // namespace llarp

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

@ -1,12 +1,12 @@
#include "ev_libuv.hpp"
#include "vpn.hpp"
#include "libuv.hpp"
#include <memory>
#include <thread>
#include <type_traits>
#include <cstring>
#include <llarp/util/exceptions.hpp>
#include <llarp/util/thread/queue.hpp>
#include <cstring>
#include "ev.hpp"
#include <llarp/vpn/platform.hpp>
#include <uvw.hpp>
@ -251,19 +251,21 @@ namespace llarp::uv
using event_t = uvw::PrepareEvent;
auto handle = m_Impl->resource<uvw::PrepareHandle>();
#endif
if (!handle)
return false;
handle->on<event_t>([netif = std::move(netif), handler = std::move(handler)](
const event_t&, [[maybe_unused]] auto& handle) {
for (auto pkt = netif->ReadNextPacket(); pkt.sz > 0; pkt = netif->ReadNextPacket())
for (auto pkt = netif->ReadNextPacket(); true; pkt = netif->ReadNextPacket())
{
LogDebug("got packet ", pkt.sz);
if (pkt.empty())
return;
if (handler)
handler(std::move(pkt));
// on windows/apple, vpn packet io does not happen as an io action that wakes up the event
// loop thus, we must manually wake up the event loop when we get a packet on our interface.
// on linux this is a nop
// on linux/android this is a nop
netif->MaybeWakeUpperLayers();
}
});

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

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

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

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

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

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

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

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

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

@ -50,9 +50,19 @@ namespace llarp
fromSockAddr(const SockAddr& address);
/// get this as an explicit v4 or explicit v6
std::variant<nuint32_t, nuint128_t>
net::ipaddr_t
IP() const;
/// get this as an v4 or throw if it is not one
inline net::ipv4addr_t
IPv4() const
{
auto ip = IP();
if (auto* ptr = std::get_if<net::ipv4addr_t>(&ip))
return *ptr;
throw std::runtime_error{"no ipv4 address found in address info"};
}
std::string
ToString() const;
};

@ -0,0 +1,36 @@
#pragma once
#include "ip_range.hpp"
namespace llarp
{
static constexpr std::array bogonRanges_v6 = {
// zero
IPRange{huint128_t{0}, netmask_ipv6_bits(128)},
// loopback
IPRange{huint128_t{1}, netmask_ipv6_bits(128)},
// yggdrasil
IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)},
// multicast
IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
// local
IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
// local
IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}};
static constexpr std::array bogonRanges_v4 = {
IPRange::FromIPv4(0, 0, 0, 0, 8),
IPRange::FromIPv4(10, 0, 0, 0, 8),
IPRange::FromIPv4(100, 64, 0, 0, 10),
IPRange::FromIPv4(127, 0, 0, 0, 8),
IPRange::FromIPv4(169, 254, 0, 0, 16),
IPRange::FromIPv4(172, 16, 0, 0, 12),
IPRange::FromIPv4(192, 0, 0, 0, 24),
IPRange::FromIPv4(192, 0, 2, 0, 24),
IPRange::FromIPv4(192, 88, 99, 0, 24),
IPRange::FromIPv4(192, 168, 0, 0, 16),
IPRange::FromIPv4(198, 18, 0, 0, 15),
IPRange::FromIPv4(198, 51, 100, 0, 24),
IPRange::FromIPv4(203, 0, 113, 0, 24),
IPRange::FromIPv4(224, 0, 0, 0, 4),
IPRange::FromIPv4(240, 0, 0, 0, 4)};
} // namespace llarp

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <vector>
#include "ip_range.hpp"
namespace llarp::net
{
/// info about a network interface lokinet does not own
struct InterfaceInfo
{
/// human readable name of interface
std::string name;
/// interface's index
int index;
/// the addresses owned by this interface
std::vector<IPRange> addrs;
/// a gateway we can use if it exists
std::optional<ipaddr_t> gateway;
};
} // namespace llarp::net

@ -127,7 +127,7 @@ namespace llarp
SockAddr addr(m_ipAddress);
const auto* addr6 = static_cast<const sockaddr_in6*>(addr);
const uint8_t* raw = addr6->sin6_addr.s6_addr;
return IsIPv4Bogon(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15]));
return IPRange::V4MappedRange().Contains(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15]));
}
std::string

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

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

@ -69,7 +69,7 @@ namespace llarp::net
} preamble;
uint16_t payload_len;
uint8_t proto;
uint8_t protocol;
uint8_t hoplimit;
in6_addr srcaddr;
in6_addr dstaddr;
@ -109,34 +109,119 @@ namespace llarp::net
/// an Packet
struct IPPacket
{
static constexpr size_t MaxSize = 1500;
static constexpr size_t _max_size = 1500;
llarp_time_t timestamp;
size_t sz;
std::vector<byte_t> _buf;
alignas(ip_header) byte_t buf[MaxSize];
public:
IPPacket() : IPPacket{size_t{}}
{}
/// create an ip packet buffer of all zeros of size sz
explicit IPPacket(size_t sz);
/// create an ip packet from a view
explicit IPPacket(byte_view_t);
/// create an ip packet from a vector we then own
IPPacket(std::vector<byte_t>&&);
static IPPacket
~IPPacket() = default;
static constexpr size_t MaxSize = _max_size;
static constexpr size_t MinSize = 20;
[[deprecated("deprecated because of llarp_buffer_t")]] static IPPacket
UDP(nuint32_t srcaddr,
nuint16_t srcport,
nuint32_t dstaddr,
nuint16_t dstport,
const llarp_buffer_t& data);
const llarp_buffer_t& data)
{
return make_udp(srcaddr, srcport, dstaddr, dstport, data.copy());
}
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
Buffer();
[[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t
ConstBuffer() const
{
return llarp_buffer_t{_buf};
}
ManagedBuffer
ConstBuffer() const;
/// steal the underlying vector
inline std::vector<byte_t>
steal()
{
std::vector<byte_t> buf;
buf.resize(0);
std::swap(_buf, buf);
return buf;
}
bool
Load(const llarp_buffer_t& buf);
inline byte_t*
data()
{
return _buf.data();
}
inline const byte_t*
data() const
{
return _buf.data();
}
constexpr size_t
capacity() const
{
return _max_size;
}
inline size_t
size() const
{
return _buf.size();
}
inline bool
empty() const
{
return _buf.empty();
}
byte_view_t
view() const;
struct CompareSize
{
bool
operator()(const IPPacket& left, const IPPacket& right)
{
return left.sz < right.sz;
return left.size() < right.size();
}
};
@ -152,25 +237,25 @@ namespace llarp::net
inline ip_header*
Header()
{
return reinterpret_cast<ip_header*>(&buf[0]);
return reinterpret_cast<ip_header*>(data());
}
inline const ip_header*
Header() const
{
return reinterpret_cast<const ip_header*>(&buf[0]);
return reinterpret_cast<const ip_header*>(data());
}
inline ipv6_header*
HeaderV6()
{
return reinterpret_cast<ipv6_header*>(&buf[0]);
return reinterpret_cast<ipv6_header*>(data());
}
inline const ipv6_header*
HeaderV6() const
{
return reinterpret_cast<const ipv6_header*>(&buf[0]);
return reinterpret_cast<const ipv6_header*>(data());
}
inline int
@ -179,6 +264,15 @@ namespace llarp::net
return Header()->version;
}
inline byte_t
protocol() const
{
if (IsV4())
return Header()->protocol;
else
return HeaderV6()->protocol;
}
inline bool
IsV4() const
{
@ -226,6 +320,12 @@ namespace llarp::net
huint128_t
dst4to6Lan() const;
SockAddr
src() const;
SockAddr
dst() const;
/// get destination port if applicable
std::optional<nuint16_t>
DstPort() const;
@ -238,6 +338,14 @@ namespace llarp::net
std::optional<std::pair<const char*, size_t>>
L4Data() const;
inline std::optional<OwnedBuffer>
L4OwnedBuffer() const
{
if (auto data = L4Data())
return OwnedBuffer{reinterpret_cast<const byte_t*>(data->first), data->second};
return std::nullopt;
}
void
UpdateIPv4Address(nuint32_t src, nuint32_t dst);
@ -256,6 +364,8 @@ namespace llarp::net
/// make an icmp unreachable reply packet based of this ip packet
std::optional<IPPacket>
MakeICMPUnreachable() const;
std::function<void(net::IPPacket)> reply;
};
/// generate ip checksum

@ -90,4 +90,15 @@ namespace llarp
return addr.ToString();
}
std::string
IPRange::NetmaskString() const
{
if (IsV4())
{
const huint32_t mask = net::TruncateV6(netmask_bits);
return mask.ToString();
}
return netmask_bits.ToString();
}
} // namespace llarp

@ -9,10 +9,6 @@
namespace llarp
{
/// forward declare
bool
IsBogon(huint128_t ip);
struct IPRange
{
using Addr_t = huint128_t;
@ -25,6 +21,12 @@ namespace llarp
: addr{std::move(address)}, netmask_bits{std::move(netmask)}
{}
static constexpr IPRange
V4MappedRange()
{
return IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)};
}
static constexpr IPRange
FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask)
{
@ -42,8 +44,7 @@ namespace llarp
constexpr bool
IsV4() const
{
constexpr auto ipv4_map = IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)};
return ipv4_map.Contains(addr);
return V4MappedRange().Contains(addr);
}
/// get address family
@ -55,27 +56,6 @@ namespace llarp
return AF_INET6;
}
/// return true if we intersect with a bogon range
bool
BogonRange() const
{
// special case for 0.0.0.0/0
if (IsV4() and netmask_bits == netmask_ipv6_bits(96))
return false;
// special case for ::/0
if (netmask_bits == huint128_t{0})
return false;
return IsBogon(addr) or IsBogon(HighestAddr());
}
/// return true if we intersect with a bogon range *and* we contain the given address
template <typename Addr>
bool
BogonContains(Addr&& addr) const
{
return BogonRange() and Contains(std::forward<Addr>(addr));
}
/// return the number of bits set in the hostmask
constexpr int
HostmaskBits() const
@ -147,6 +127,9 @@ namespace llarp
std::string
BaseAddressString() const;
std::string
NetmaskString() const;
bool
FromString(std::string str);

@ -9,6 +9,8 @@
#include <llarp/util/mem.hpp>
#include <llarp/util/bits.hpp>
#include "interface_info.hpp"
#include <functional>
#include <cstdlib> // for itoa
#include <vector>
@ -22,50 +24,73 @@
#include <winsock2.h>
#include <ws2tcpip.h>
#include <wspiapi.h>
#define inet_aton(x, y) inet_pton(AF_INET, x, y)
#endif
#ifndef _WIN32
#include <arpa/inet.h>
#endif
bool
operator==(const sockaddr& a, const sockaddr& b);
bool
operator==(const sockaddr_in& a, const sockaddr_in& b);
#include "bogon_ranges.hpp"
bool
operator==(const sockaddr_in6& a, const sockaddr_in6& b);
bool
operator<(const sockaddr_in6& a, const sockaddr_in6& b);
namespace llarp
{
inline bool
operator==(const in_addr& a, const in_addr& b)
{
return memcmp(&a, &b, sizeof(in_addr)) == 0;
}
bool
operator<(const in6_addr& a, const in6_addr& b);
inline bool
operator==(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) == 0;
}
bool
operator==(const in6_addr& a, const in6_addr& b);
inline bool
operator==(const sockaddr_in& a, const sockaddr_in& b)
{
return a.sin_port == b.sin_port and a.sin_addr.s_addr == b.sin_addr.s_addr;
}
namespace llarp
{
bool
IsIPv4Bogon(const huint32_t& addr);
inline bool
operator==(const sockaddr_in6& a, const sockaddr_in6& b)
{
return a.sin6_port == b.sin6_port and a.sin6_addr == b.sin6_addr;
}
inline bool
IsIPv4Bogon(const nuint32_t& addr)
operator==(const sockaddr& a, const sockaddr& b)
{
return IsIPv4Bogon(ToHost(addr));
if (a.sa_family != b.sa_family)
return false;
switch (a.sa_family)
{
case AF_INET:
return reinterpret_cast<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
IsBogon(const in6_addr& addr);
inline bool
operator<(const in_addr& a, const in_addr& b)
{
return memcmp(&a, &b, sizeof(in_addr)) < 0;
}
bool
IsBogon(const huint128_t addr);
inline bool
operator<(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) < 0;
}
bool
IsBogonRange(const in6_addr& host, const in6_addr& mask);
inline bool
operator<(const sockaddr_in6& a, const sockaddr_in6& b)
{
return a.sin6_addr < b.sin6_addr or a.sin6_port < b.sin6_port;
}
namespace net
{
@ -86,8 +111,27 @@ namespace llarp
virtual std::optional<SockAddr>
AllInterfaces(SockAddr pubaddr) const = 0;
virtual SockAddr
Wildcard(int af = AF_INET) const = 0;
inline SockAddr
Wildcard(int af = AF_INET) const
{
if (af == AF_INET)
{
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0);
return SockAddr{addr};
}
if (af == AF_INET6)
{
sockaddr_in6 addr6{};
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(0);
addr6.sin6_addr = IN6ADDR_ANY_INIT;
return SockAddr{addr6};
}
throw std::invalid_argument{fmt::format("{} is not a valid address family")};
}
inline SockAddr
WildcardWithPort(port_t port, int af = AF_INET) const
@ -104,12 +148,24 @@ namespace llarp
HasInterfaceAddress(ipaddr_t ip) const = 0;
/// return true if ip is considered a loopback address
virtual bool
IsLoopbackAddress(ipaddr_t ip) const = 0;
inline bool
IsLoopbackAddress(ipaddr_t ip) const
{
return var::visit(
[loopback6 = IPRange{huint128_t{uint128_t{0UL, 1UL}}, netmask_ipv6_bits(128)},
loopback4 = IPRange::FromIPv4(127, 0, 0, 0, 8)](auto&& ip) {
const auto h_ip = ToHost(ip);
return loopback4.Contains(h_ip) or loopback6.Contains(h_ip);
},
ip);
}
/// return true if ip is considered a wildcard address
virtual bool
IsWildcardAddress(ipaddr_t ip) const = 0;
inline bool
IsWildcardAddress(ipaddr_t ip) const
{
return var::visit([](auto&& ip) { return not ip.n; }, ip);
}
virtual std::optional<std::string>
GetBestNetIF(int af = AF_INET) const = 0;
@ -143,11 +199,63 @@ namespace llarp
return std::nullopt;
}
virtual bool
IsBogon(const SockAddr& addr) const = 0;
inline bool
IsBogon(const SockAddr& addr) const
{
return IsBogonIP(addr.asIPv6());
}
inline bool
IsBogonRange(const IPRange& range) const
{
// special case for 0.0.0.0/0
if (range.IsV4() and range.netmask_bits == netmask_ipv6_bits(96))
return false;
// special case for ::/0
if (IsWildcardAddress(ToNet(range.netmask_bits)))
return false;
return IsBogonIP(range.addr) or IsBogonIP(range.HighestAddr());
}
inline bool
IsBogonIP(const net::ipaddr_t& addr) const
{
return IsBogonIP(var::visit(
[](auto&& ip) {
if constexpr (std::is_same_v<net::ipv4addr_t, std::decay_t<decltype(ip)>>)
return ExpandV4(ToHost(ip));
else
return ToHost(ip);
},
addr));
}
inline bool
IsBogonIP(const huint128_t& addr) const
{
if (not IPRange::V4MappedRange().Contains(addr))
{
for (const auto& v6_range : bogonRanges_v6)
{
if (v6_range.Contains(addr))
return true;
}
return false;
}
const auto v4_addr = net::TruncateV6(addr);
for (const auto& v4_range : bogonRanges_v4)
{
if (v4_range.Contains(v4_addr))
return true;
}
return false;
}
virtual std::optional<int>
GetInterfaceIndex(ipaddr_t ip) const = 0;
/// returns a vector holding all of our network interfaces
virtual std::vector<InterfaceInfo>
AllNetworkInterfaces() const = 0;
};
} // namespace net

@ -40,14 +40,6 @@ namespace llarp
// IPv4 mapped address live at ::ffff:0:0/96
constexpr std::array<uint8_t, 12> ipv4_map_prefix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
constexpr bool
ipv6_is_mapped_ipv4(const in6_addr& addr)
{
for (size_t i = 0; i < ipv4_map_prefix.size(); i++)
if (addr.s6_addr[i] != ipv4_map_prefix[i])
return false;
return true;
}
namespace net
{
inline auto

@ -1,19 +1,9 @@
#include "net.hpp"
#include "net_if.hpp"
#include <stdexcept>
#include <llarp/constants/platform.hpp>
#ifdef ANDROID
#include <llarp/android/ifaddrs.h>
#endif
#ifndef _WIN32
#include <arpa/inet.h>
#ifndef ANDROID
#include <ifaddrs.h>
#endif
#endif
#include "ip.hpp"
#include "ip_range.hpp"
@ -23,116 +13,15 @@
#ifdef ANDROID
#include <llarp/android/ifaddrs.h>
#else
#ifdef _WIN32
#include <iphlpapi.h>
#include <llarp/win32/exception.hpp>
#else
#include <ifaddrs.h>
#endif
#endif
#include <cstdio>
#include <list>
#include <type_traits>
bool
operator==(const sockaddr& a, const sockaddr& b)
{
if (a.sa_family != b.sa_family)
return false;
switch (a.sa_family)
{
case AF_INET:
return *((const sockaddr_in*)&a) == *((const sockaddr_in*)&b);
case AF_INET6:
return *((const sockaddr_in6*)&a) == *((const sockaddr_in6*)&b);
default:
return false;
}
}
bool
operator<(const sockaddr_in6& a, const sockaddr_in6& b)
{
return a.sin6_addr < b.sin6_addr || a.sin6_port < b.sin6_port;
}
bool
operator<(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) < 0;
}
bool
operator==(const in6_addr& a, const in6_addr& b)
{
return memcmp(&a, &b, sizeof(in6_addr)) == 0;
}
bool
operator==(const sockaddr_in& a, const sockaddr_in& b)
{
return a.sin_port == b.sin_port && a.sin_addr.s_addr == b.sin_addr.s_addr;
}
bool
operator==(const sockaddr_in6& a, const sockaddr_in6& b)
{
return a.sin6_port == b.sin6_port && a.sin6_addr == b.sin6_addr;
}
namespace llarp::net
{
class Platform_Base : public llarp::net::Platform
{
public:
bool
IsLoopbackAddress(ipaddr_t ip) const override
{
return var::visit(
[loopback6 = IPRange{huint128_t{uint128_t{0UL, 1UL}}, netmask_ipv6_bits(128)},
loopback4 = IPRange::FromIPv4(127, 0, 0, 0, 8)](auto&& ip) {
const auto h_ip = ToHost(ip);
return loopback4.Contains(h_ip) or loopback6.Contains(h_ip);
},
ip);
}
SockAddr
Wildcard(int af) const override
{
if (af == AF_INET)
{
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0);
return SockAddr{addr};
}
if (af == AF_INET6)
{
sockaddr_in6 addr6{};
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(0);
addr6.sin6_addr = IN6ADDR_ANY_INIT;
return SockAddr{addr6};
}
throw std::invalid_argument{fmt::format("{} is not a valid address family")};
}
bool
IsBogon(const llarp::SockAddr& addr) const override
{
return llarp::IsBogon(addr.asIPv6());
}
bool
IsWildcardAddress(ipaddr_t ip) const override
{
return var::visit([](auto&& ip) { return not ip.n; }, ip);
}
};
#ifdef _WIN32
class Platform_Impl : public Platform_Base
{
@ -293,11 +182,30 @@ namespace llarp::net
{
return GetInterfaceIndex(ip) != std::nullopt;
}
std::vector<InterfaceInfo>
AllNetworkInterfaces() const override
{
std::vector<InterfaceInfo> all;
iter_adapters([&all](auto* a) {
auto& cur = all.emplace_back();
cur.index = a->IfIndex;
cur.name = a->AdapterName;
for (auto* addr = a->FirstUnicastAddress; addr and addr->Next; addr = addr->Next)
{
SockAddr saddr{*addr->Address.lpSockaddr};
cur.addrs.emplace_back(
saddr.asIPv6(),
ipaddr_netmask_bits(addr->OnLinkPrefixLength, addr->Address.lpSockaddr->sa_family));
}
});
return all;
}
};
#else
class Platform_Impl : public Platform_Base
class Platform_Impl : public Platform
{
template <typename Visit_t>
void
@ -469,6 +377,33 @@ namespace llarp::net
});
return found;
}
std::vector<InterfaceInfo>
AllNetworkInterfaces() const override
{
std::unordered_map<std::string, InterfaceInfo> ifmap;
iter_all([&ifmap](auto* i) {
if (i == nullptr or i->ifa_addr == nullptr)
return;
const auto fam = i->ifa_addr->sa_family;
if (fam != AF_INET and fam != AF_INET6)
return;
auto& ent = ifmap[i->ifa_name];
if (ent.name.empty())
{
ent.name = i->ifa_name;
ent.index = if_nametoindex(i->ifa_name);
}
SockAddr addr{*i->ifa_addr};
SockAddr mask{*i->ifa_netmask};
ent.addrs.emplace_back(addr.asIPv6(), mask.asIPv6());
});
std::vector<InterfaceInfo> all;
for (auto& [name, ent] : ifmap)
all.emplace_back(std::move(ent));
return all;
}
};
#endif
@ -480,100 +415,3 @@ namespace llarp::net
return &g_plat;
}
} // namespace llarp::net
namespace llarp
{
#if !defined(TESTNET)
static constexpr std::array bogonRanges_v6 = {
// zero
IPRange{huint128_t{0}, netmask_ipv6_bits(128)},
// loopback
IPRange{huint128_t{1}, netmask_ipv6_bits(128)},
// yggdrasil
IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)},
// multicast
IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
// local
IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
// local
IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}};
static constexpr std::array bogonRanges_v4 = {
IPRange::FromIPv4(0, 0, 0, 0, 8),
IPRange::FromIPv4(10, 0, 0, 0, 8),
IPRange::FromIPv4(100, 64, 0, 0, 10),
IPRange::FromIPv4(127, 0, 0, 0, 8),
IPRange::FromIPv4(169, 254, 0, 0, 16),
IPRange::FromIPv4(172, 16, 0, 0, 12),
IPRange::FromIPv4(192, 0, 0, 0, 24),
IPRange::FromIPv4(192, 0, 2, 0, 24),
IPRange::FromIPv4(192, 88, 99, 0, 24),
IPRange::FromIPv4(192, 168, 0, 0, 16),
IPRange::FromIPv4(198, 18, 0, 0, 15),
IPRange::FromIPv4(198, 51, 100, 0, 24),
IPRange::FromIPv4(203, 0, 113, 0, 24),
IPRange::FromIPv4(224, 0, 0, 0, 4),
IPRange::FromIPv4(240, 0, 0, 0, 4)};
#endif
bool
IsBogon(const in6_addr& addr)
{
#if defined(TESTNET)
(void)addr;
return false;
#else
if (not ipv6_is_mapped_ipv4(addr))
{
const auto ip = net::In6ToHUInt(addr);
for (const auto& range : bogonRanges_v6)
{
if (range.Contains(ip))
return true;
}
return false;
}
return IsIPv4Bogon(
ipaddr_ipv4_bits(addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15]));
#endif
}
bool
IsBogon(const huint128_t ip)
{
const nuint128_t netIP{ntoh128(ip.h)};
in6_addr addr{};
std::copy_n((const uint8_t*)&netIP.n, 16, &addr.s6_addr[0]);
return IsBogon(addr);
}
bool
IsBogonRange(const in6_addr& host, const in6_addr&)
{
// TODO: implement me
return IsBogon(host);
}
#if !defined(TESTNET)
bool
IsIPv4Bogon(const huint32_t& addr)
{
for (const auto& bogon : bogonRanges_v4)
{
if (bogon.Contains(addr))
{
return true;
}
}
return false;
}
#else
bool
IsIPv4Bogon(const huint32_t&)
{
return false;
}
#endif
} // namespace llarp

@ -1,7 +1,9 @@
#include "sock_addr.hpp"
#include "ip_range.hpp"
#include "address_info.hpp"
#include "ip.hpp"
#include "net_bits.hpp"
#include "net.hpp"
#include <llarp/util/str.hpp>
#include <llarp/util/logging.hpp>
#include <llarp/util/mem.hpp>
@ -11,17 +13,6 @@
namespace llarp
{
bool
operator==(const in6_addr& lh, const in6_addr& rh)
{
return memcmp(&lh, &rh, sizeof(in6_addr)) == 0;
}
bool
operator<(const in6_addr& lh, const in6_addr& rh)
{
return memcmp(&lh, &rh, sizeof(in6_addr)) < 0;
}
/// shared utility functions
///
@ -156,7 +147,7 @@ namespace llarp
init();
memcpy(&m_addr, &other, sizeof(sockaddr_in6));
if (ipv6_is_mapped_ipv4(other.sin6_addr))
if (IPRange::V4MappedRange().Contains(asIPv6()))
{
setIPv4(
other.sin6_addr.s6_addr[12],
@ -179,9 +170,8 @@ namespace llarp
SockAddr::operator=(const in6_addr& other)
{
init();
memcpy(&m_addr.sin6_addr.s6_addr, &other.s6_addr, sizeof(m_addr.sin6_addr.s6_addr));
if (ipv6_is_mapped_ipv4(other))
if (IPRange::V4MappedRange().Contains(asIPv6()))
{
setIPv4(other.s6_addr[12], other.s6_addr[13], other.s6_addr[14], other.s6_addr[15]);
m_addr4.sin_port = m_addr.sin6_port;
@ -335,7 +325,7 @@ namespace llarp
bool
SockAddr::isIPv4() const
{
return ipv6_is_mapped_ipv4(m_addr.sin6_addr);
return IPRange::V4MappedRange().Contains(asIPv6());
}
bool
SockAddr::isIPv6() const
@ -438,10 +428,10 @@ namespace llarp
setPort(ToNet(port));
}
uint16_t
SockAddr::getPort() const
net::port_t
SockAddr::port() const
{
return ntohs(m_addr.sin6_port);
return net::port_t{m_addr.sin6_port};
}
} // namespace llarp

@ -141,9 +141,16 @@ namespace llarp
setPort(huint16_t{port});
}
/// port is always returned in native (host) order
uint16_t
getPort() const;
/// get the port of this sockaddr in network order
net::port_t
port() const;
/// port is always returned in host order
inline uint16_t
getPort() const
{
return ToHost(port()).h;
}
/// True if this stores an IPv6 address, false if IPv4.
bool

@ -0,0 +1,216 @@
#include "net.hpp"
#include "net_if.hpp"
#include <stdexcept>
#include <llarp/constants/platform.hpp>
#include "ip.hpp"
#include "ip_range.hpp"
#include <llarp/util/logging.hpp>
#include <llarp/util/str.hpp>
#include <iphlpapi.h>
#include <llarp/win32/exception.hpp>
#include <cstdio>
#include <list>
#include <type_traits>
namespace llarp::net
{
class Platform_Impl : public Platform
{
/// visit all adapters (not addresses). windows serves net info per adapter unlink posix which
/// gives a list of all distinct addresses.
template <typename Visit_t>
void
iter_adapters(Visit_t&& visit, int af = AF_UNSPEC) const
{
ULONG sz{};
GetAdaptersAddresses(af, 0, nullptr, nullptr, &sz);
auto ptr = std::make_unique<byte_t[]>(sz);
auto* addrs = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(ptr.get());
if (auto err = GetAdaptersAddresses(af, 0, nullptr, addrs, &sz); err != ERROR_SUCCESS)
throw llarp::win32::error{err, "GetAdaptersAddresses()"};
for (auto* addr = addrs; addr->Next; addr = addr->Next)
visit(addr);
}
template <typename adapter_t>
bool
adapter_has_ip(adapter_t* a, ipaddr_t ip) const
{
for (auto* addr = a->FirstUnicastAddress; addr->Next; addr = addr->Next)
{
SockAddr saddr{*addr->Address.lpSockaddr};
LogDebug(fmt::format("'{}' has address '{}'", a->AdapterName, saddr));
if (saddr.getIP() == ip)
return true;
}
return false;
}
template <typename adapter_t>
bool
adapter_has_fam(adapter_t* a, int af) const
{
for (auto* addr = a->FirstUnicastAddress; addr->Next; addr = addr->Next)
{
SockAddr saddr{*addr->Address.lpSockaddr};
if (saddr.Family() == af)
return true;
}
return false;
}
public:
std::optional<int>
GetInterfaceIndex(ipaddr_t ip) const override
{
std::optional<int> found;
int af{AF_INET};
if (std::holds_alternative<ipv6addr_t>(ip))
af = AF_INET6;
iter_adapters(
[&found, ip, this](auto* adapter) {
if (found)
return;
LogDebug(fmt::format(
"visit adapter looking for '{}': '{}' idx={}",
ip,
adapter->AdapterName,
adapter->IfIndex));
if (adapter_has_ip(adapter, ip))
{
found = adapter->IfIndex;
}
},
af);
return found;
}
std::optional<llarp::SockAddr>
GetInterfaceAddr(std::string_view name, int af) const override
{
std::optional<SockAddr> found;
iter_adapters([name = std::string{name}, af, &found, this](auto* a) {
if (found)
return;
if (std::string{a->AdapterName} != name)
return;
if (adapter_has_fam(a, af))
found = SockAddr{*a->FirstUnicastAddress->Address.lpSockaddr};
});
return found;
}
std::optional<SockAddr>
AllInterfaces(SockAddr fallback) const override
{
// windows seems to not give a shit about source address
return fallback.isIPv6() ? SockAddr{"[::]"} : SockAddr{"0.0.0.0"};
}
std::optional<std::string>
FindFreeTun() const override
{
return "lokitun0";
}
std::optional<std::string>
GetBestNetIF(int) const override
{
// TODO: implement me ?
return std::nullopt;
}
std::optional<IPRange>
FindFreeRange() const override
{
std::list<IPRange> currentRanges;
iter_adapters([&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();
bool result = true;
for (const auto& hook : m_ObtainedExitHooks)
result &= hook(self, B);
result = hook(self, B) and result;
m_ObtainedExitHooks.clear();
return result;
}
@ -897,7 +897,7 @@ namespace llarp
for (const auto& pkt : msg.X)
{
if (pkt.size() <= 8)
return false;
continue;
auto counter = oxenc::load_big_to_host<uint64_t>(pkt.data());
if (m_ExitTrafficHandler(
self, llarp_buffer_t(pkt.data() + 8, pkt.size() - 8), counter, msg.protocol))

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

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

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

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

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

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

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

@ -10,32 +10,19 @@ namespace llarp
{
struct AbstractRouter;
struct RoutePoker
struct RoutePoker : public std::enable_shared_from_this<RoutePoker>
{
void
AddRoute(huint32_t ip);
AddRoute(net::ipv4addr_t ip);
void
DelRoute(huint32_t ip);
DelRoute(net::ipv4addr_t ip);
void
Init(AbstractRouter* router, bool enable = false);
Start(AbstractRouter* router);
~RoutePoker();
void
Update();
// sets stored routes and causes AddRoute to actually
// set routes rather than just storing them
void
Enable();
// unsets stored routes, and causes AddRoute to simply
// remember the desired routes rather than setting them.
void
Disable();
/// explicitly put routes up
void
Up();
@ -50,6 +37,12 @@ namespace llarp
SetDNSMode(bool using_exit_mode) const;
private:
void
Update();
bool
IsEnabled() const;
void
DeleteAllRoutes();
@ -57,25 +50,18 @@ namespace llarp
DisableAllRoutes();
void
EnableAllRoutes();
RefreshAllRoutes();
void
EnableRoute(huint32_t ip, huint32_t gateway);
EnableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway);
void
DisableRoute(huint32_t ip, huint32_t gateway);
std::optional<huint32_t>
GetDefaultGateway() const;
DisableRoute(net::ipv4addr_t ip, net::ipv4addr_t gateway);
std::unordered_map<huint32_t, huint32_t> m_PokedRoutes;
huint32_t m_CurrentGateway;
std::unordered_map<net::ipv4addr_t, net::ipv4addr_t> m_PokedRoutes;
bool m_Enabled = false;
bool m_Enabling = false;
std::optional<net::ipv4addr_t> m_CurrentGateway;
AbstractRouter* m_Router = nullptr;
bool m_HasNetwork = true;
};
} // namespace llarp

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

@ -274,27 +274,13 @@ namespace llarp
/// bootstrap RCs
BootstrapList bootstrapRCList;
bool
ExitEnabled() const
{
return false; // FIXME - have to fix the FIXME because FIXME
throw std::runtime_error("FIXME: this needs to be derived from config");
/*
// TODO: use equal_range ?
auto itr = netConfig.find("exit");
if (itr == netConfig.end())
return false;
return IsTrueValue(itr->second.c_str());
*/
}
RoutePoker&
routePoker() override
const std::shared_ptr<RoutePoker>&
routePoker() const override
{
return m_RoutePoker;
}
RoutePoker m_RoutePoker;
std::shared_ptr<RoutePoker> m_RoutePoker;
void
TriggerPump() override;
@ -401,6 +387,9 @@ namespace llarp
bool
StartRpcServer() override;
void
Freeze() override;
void
Thaw() override;

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

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

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

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

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

@ -5,6 +5,7 @@
#include "endpoint.hpp"
#include <utility>
#include <unordered_set>
#include <llarp/crypto/crypto.hpp>
namespace llarp
{

@ -132,6 +132,14 @@ operator==(const llarp_buffer_t& buff, std::string_view data)
{
return std::string_view{reinterpret_cast<const char*>(buff.cur), buff.size_left()} == data;
}
llarp::byte_view_t
llarp_buffer_t::view() const
{
return {base, sz};
}
namespace llarp
{
std::vector<byte_t>

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

@ -0,0 +1,39 @@
#include "lokinet_init.h"
#if defined(_WIN32)
#include <windows.h>
#include <winuser.h>
#include <stdio.h>
#define WineKiller \
DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit
struct WineKiller
{
WineKiller()
{
if (auto hntdll = GetModuleHandle("ntdll.dll"))
{
if (GetProcAddress(hntdll, "wine_get_version"))
{
static const char* text =
"dont run lokinet in wine like wtf man we support linux and pretty "
"much every flavour of BSD, and even some flavours of unix system "
"5.x.\nThis Program Will now crash lmao.";
static const char* title = "srsly fam wtf";
MessageBoxA(NULL, text, title, MB_ICONHAND);
abort();
}
}
}
};
// i heckin love static initalization
WineKiller lmao{};
#endif
extern "C" int
Lokinet_INIT(void)
{
return 0;
}

@ -1,35 +0,0 @@
#include "lokinet_init.h"
#if defined(_WIN32)
#include <windows.h>
#include <winuser.h>
#include <stdio.h>
int
Lokinet_INIT(void)
{
static const char*(CDECL * pwine_get_version)(void);
HMODULE hntdll = GetModuleHandle("ntdll.dll");
if (hntdll)
{
pwine_get_version = (void*)GetProcAddress(hntdll, "wine_get_version");
if (pwine_get_version)
{
static const char* text =
"dont run lokinet in wine like wtf man we support linux and pretty "
"much every flavour of BSD, and even some flavours of unix system "
"5.x.\nThis Program Will now crash lmao.";
static const char* title = "srsly fam wtf";
MessageBoxA(NULL, text, title, MB_ICONHAND);
abort();
}
}
return 0;
}
#else
int
Lokinet_INIT(void)
{
return 0;
}
#endif

@ -3,15 +3,6 @@
#ifdef __cplusplus
extern "C"
{
#endif
#ifndef Lokinet_INIT
#if defined(_WIN32)
#define Lokinet_INIT \
DieInCaseSomehowThisGetsRunInWineButLikeWTFThatShouldNotHappenButJustInCaseHandleItWithAPopupOrSomeShit
#else
#define Lokinet_INIT _lokinet_non_shit_platform_INIT
#endif
#endif
int

@ -7,6 +7,12 @@
#include <string>
#include <set>
#ifdef _WIN32
#include <windows.h>
#include <stringapiset.h>
#include <llarp/win32/exception.hpp>
#endif
namespace llarp
{
bool
@ -168,4 +174,22 @@ namespace llarp
dur.count());
}
std::wstring
to_wide(std::string data)
{
std::wstring buf;
buf.resize(data.size());
#ifdef _WIN32
// win32 specific codepath because balmer made windows so that man may suffer
if (MultiByteToWideChar(CP_UTF8, 0, data.c_str(), data.size(), buf.data(), buf.size()) == 0)
throw win32::error{GetLastError(), "failed to convert string to wide string"};
#else
// this dumb but probably works (i guess?)
std::transform(
data.begin(), data.end(), buf.begin(), [](const auto& ch) -> wchar_t { return ch; });
#endif
return buf;
}
} // namespace llarp

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

@ -3,7 +3,7 @@
#include <stdio.h>
#include <unistd.h>
#include <llarp/ev/vpn.hpp>
#include "platform.hpp"
#include "common.hpp"
#include <llarp.hpp>
@ -12,10 +12,9 @@ namespace llarp::vpn
class AndroidInterface : public NetworkInterface
{
const int m_fd;
const InterfaceInfo m_Info; // likely 100% ignored on android, at least for now
public:
AndroidInterface(InterfaceInfo info, int fd) : m_fd(fd), m_Info(info)
AndroidInterface(InterfaceInfo info, int fd) : NetworkInterface{std::move(info)}, m_fd{fd}
{
if (m_fd == -1)
throw std::runtime_error(
@ -37,38 +36,34 @@ namespace llarp::vpn
net::IPPacket
ReadNextPacket() override
{
net::IPPacket pkt{};
const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf));
if (sz >= 0)
pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)});
return pkt;
std::vector<byte_t> pkt;
pkt.reserve(net::IPPacket::MaxSize);
const auto n = read(m_fd, pkt.data(), pkt.capacity());
pkt.resize(std::min(std::max(ssize_t{}, n), static_cast<ssize_t>(pkt.capacity())));
return net::IPPacket{std::move(pkt)};
}
bool
WritePacket(net::IPPacket pkt) override
{
const auto sz = write(m_fd, pkt.buf, pkt.sz);
const auto sz = write(m_fd, pkt.data(), pkt.size());
if (sz <= 0)
return false;
return sz == static_cast<ssize_t>(pkt.sz);
}
std::string
IfName() const override
{
return m_Info.ifname;
return sz == static_cast<ssize_t>(pkt.size());
}
};
class AndroidRouteManager : public IRouteManager
{
void AddRoute(IPVariant_t, IPVariant_t) override{};
void AddRoute(net::ipaddr_t, net::ipaddr_t) override{};
void DelRoute(IPVariant_t, IPVariant_t) override{};
void DelRoute(net::ipaddr_t, net::ipaddr_t) override{};
void AddDefaultRouteViaInterface(std::string) override{};
void
AddDefaultRouteViaInterface(NetworkInterface&) override{};
void DelDefaultRouteViaInterface(std::string) override{};
void
DelDefaultRouteViaInterface(NetworkInterface&) override{};
void
AddRouteViaInterface(NetworkInterface&, IPRange) override{};
@ -76,9 +71,10 @@ namespace llarp::vpn
void
DelRouteViaInterface(NetworkInterface&, IPRange) override{};
std::vector<IPVariant_t> GetGatewaysNotOnInterface(std::string) override
std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(NetworkInterface&) override
{
return std::vector<IPVariant_t>{};
return std::vector<net::ipaddr_t>{};
};
};
@ -88,7 +84,7 @@ namespace llarp::vpn
AndroidRouteManager _routeManager{};
public:
AndroidPlatform(llarp::Context* ctx) : fd(ctx->androidFD)
AndroidPlatform(llarp::Context* ctx) : fd{ctx->androidFD}
{}
std::shared_ptr<NetworkInterface>

@ -0,0 +1,35 @@
#pragma once
#include <functional>
#include <llarp/net/ip_packet.hpp>
#include <llarp/util/types.hpp>
namespace llarp::vpn
{
class I_Packet_IO
{
public:
virtual ~I_Packet_IO() = default;
/// start any platform specific operations before running
virtual void
Start(){};
/// stop operation and tear down anything that Start() set up.
virtual void
Stop(){};
/// read next ip packet, return an empty packet if there are none ready.
virtual net::IPPacket
ReadNextPacket() = 0;
/// write a packet to the interface
/// returns false if we dropped it
virtual bool
WritePacket(net::IPPacket pkt) = 0;
/// get pollable fd for reading
virtual int
PollFD() const = 0;
};
} // namespace llarp::vpn

@ -1,6 +1,6 @@
#pragma once
#include <llarp/ev/vpn.hpp>
#include "platform.hpp"
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
@ -36,11 +36,10 @@ namespace llarp::vpn
class LinuxInterface : public NetworkInterface
{
const int m_fd;
const InterfaceInfo m_Info;
public:
LinuxInterface(InterfaceInfo info)
: NetworkInterface{}, m_fd{::open("/dev/net/tun", O_RDWR)}, m_Info{std::move(info)}
: NetworkInterface{std::move(info)}, m_fd{::open("/dev/net/tun", O_RDWR)}
{
if (m_fd == -1)
@ -60,7 +59,7 @@ namespace llarp::vpn
control.ioctl(SIOCGIFFLAGS, &ifr);
const int flags = ifr.ifr_flags;
control.ioctl(SIOCGIFINDEX, &ifr);
const int ifindex = ifr.ifr_ifindex;
m_Info.index = ifr.ifr_ifindex;
for (const auto& ifaddr : m_Info.addrs)
{
@ -79,7 +78,7 @@ namespace llarp::vpn
{
ifr6.addr = net::HUIntToIn6(ifaddr.range.addr);
ifr6.prefixlen = llarp::bits::count_bits(ifaddr.range.netmask_bits);
ifr6.ifindex = ifindex;
ifr6.ifindex = m_Info.index;
try
{
IOCTL{AF_INET6}.ioctl(SIOCSIFADDR, &ifr6);
@ -108,30 +107,29 @@ namespace llarp::vpn
net::IPPacket
ReadNextPacket() override
{
net::IPPacket pkt;
const auto sz = read(m_fd, pkt.buf, sizeof(pkt.buf));
if (sz >= 0)
pkt.sz = std::min(sz, ssize_t{sizeof(pkt.buf)});
else if (errno == EAGAIN || errno == EWOULDBLOCK)
pkt.sz = 0;
else
std::vector<byte_t> pkt;
pkt.resize(net::IPPacket::MaxSize);
const auto sz = read(m_fd, pkt.data(), pkt.capacity());
if (errno)
{
if (errno == EAGAIN or errno == EWOULDBLOCK)
{
errno = 0;
return net::IPPacket{};
}
throw std::error_code{errno, std::system_category()};
}
pkt.resize(sz);
return pkt;
}
bool
WritePacket(net::IPPacket pkt) override
{
const auto sz = write(m_fd, pkt.buf, pkt.sz);
const auto sz = write(m_fd, pkt.data(), pkt.size());
if (sz <= 0)
return false;
return sz == static_cast<ssize_t>(pkt.sz);
}
std::string
IfName() const override
{
return m_Info.ifname;
return sz == static_cast<ssize_t>(pkt.size());
}
};
@ -182,19 +180,18 @@ namespace llarp::vpn
unsigned char bitlen;
unsigned char data[sizeof(struct in6_addr)];
_inet_addr(huint32_t addr, size_t bits = 32)
_inet_addr(net::ipv4addr_t addr, size_t bits = 32)
{
family = AF_INET;
bitlen = bits;
oxenc::write_host_as_big(addr.h, data);
std::memcpy(data, &addr.n, 4);
}
_inet_addr(huint128_t addr, size_t bits = 128)
_inet_addr(net::ipv6addr_t addr, size_t bits = 128)
{
family = AF_INET6;
bitlen = bits;
const nuint128_t net = ToNet(addr);
std::memcpy(data, &net, 16);
std::memcpy(data, &addr.n, 16);
}
};
@ -290,76 +287,68 @@ namespace llarp::vpn
}
void
DefaultRouteViaInterface(std::string ifname, int cmd, int flags)
DefaultRouteViaInterface(NetworkInterface& vpn, int cmd, int flags)
{
int if_idx = if_nametoindex(ifname.c_str());
const auto maybe = Net().GetInterfaceAddr(ifname);
const auto& info = vpn.Info();
const auto maybe = Net().GetInterfaceAddr(info.ifname);
if (not maybe)
throw std::runtime_error{"we dont have our own network interface?"};
const _inet_addr gateway{maybe->asIPv4()};
const _inet_addr lower{ipaddr_ipv4_bits(0, 0, 0, 0), 1};
const _inet_addr upper{ipaddr_ipv4_bits(128, 0, 0, 0), 1};
const _inet_addr gateway{maybe->getIPv4()};
const _inet_addr lower{ToNet(ipaddr_ipv4_bits(0, 0, 0, 0)), 1};
const _inet_addr upper{ToNet(ipaddr_ipv4_bits(128, 0, 0, 0)), 1};
Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, if_idx);
Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, if_idx);
Route(cmd, flags, lower, gateway, GatewayMode::eLowerDefault, info.index);
Route(cmd, flags, upper, gateway, GatewayMode::eUpperDefault, info.index);
if (const auto maybe6 = Net().GetInterfaceIPv6Address(ifname))
if (const auto maybe6 = Net().GetInterfaceIPv6Address(info.ifname))
{
const _inet_addr gateway6{*maybe6, 128};
const _inet_addr gateway6{ToNet(*maybe6), 128};
for (const std::string str : {"::", "4000::", "8000::", "c000::"})
{
huint128_t _hole{};
_hole.FromString(str);
const _inet_addr hole6{_hole, 2};
Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, if_idx);
const _inet_addr hole6{net::ipv6addr_t::from_string(str), 2};
Route(cmd, flags, hole6, gateway6, GatewayMode::eUpperDefault, info.index);
}
}
}
void
RouteViaInterface(int cmd, int flags, std::string ifname, IPRange range)
RouteViaInterface(int cmd, int flags, NetworkInterface& vpn, IPRange range)
{
int if_idx = if_nametoindex(ifname.c_str());
const auto& info = vpn.Info();
if (range.IsV4())
{
const auto maybe = Net().GetInterfaceAddr(ifname);
const auto maybe = Net().GetInterfaceAddr(info.ifname);
if (not maybe)
throw std::runtime_error{"we dont have our own network interface?"};
const _inet_addr gateway{maybe->asIPv4()};
const auto gateway = var::visit([](auto&& ip) { return _inet_addr{ip}; }, maybe->getIP());
const _inet_addr addr{
net::TruncateV6(range.addr), bits::count_bits(net::TruncateV6(range.netmask_bits))};
ToNet(net::TruncateV6(range.addr)),
bits::count_bits(net::TruncateV6(range.netmask_bits))};
Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx);
Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index);
}
else
{
const auto maybe = Net().GetInterfaceIPv6Address(ifname);
const auto maybe = Net().GetInterfaceIPv6Address(info.ifname);
if (not maybe)
throw std::runtime_error{"we dont have our own network interface?"};
const _inet_addr gateway{*maybe, 128};
const _inet_addr addr{range.addr, bits::count_bits(range.netmask_bits)};
Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, if_idx);
const _inet_addr gateway{ToNet(*maybe), 128};
const _inet_addr addr{ToNet(range.addr), bits::count_bits(range.netmask_bits)};
Route(cmd, flags, addr, gateway, GatewayMode::eUpperDefault, info.index);
}
}
void
Route(int cmd, int flags, IPVariant_t ip, IPVariant_t gateway)
Route(int cmd, int flags, net::ipaddr_t ip, net::ipaddr_t gateway)
{
// do bullshit double std::visit because lol variants
std::visit(
[gateway, cmd, flags, this](auto&& ip) {
const _inet_addr toAddr{ip};
std::visit(
[toAddr, cmd, flags, this](auto&& gateway) {
const _inet_addr gwAddr{gateway};
Route(cmd, flags, toAddr, gwAddr, GatewayMode::eFirstHop, 0);
},
gateway);
},
ip);
auto _ip = var::visit([](auto&& i) { return _inet_addr{i}; }, ip);
auto _gw = var::visit([](auto&& i) { return _inet_addr{i}; }, gateway);
Route(cmd, flags, _ip, _gw, GatewayMode::eFirstHop, 0);
}
public:
@ -375,45 +364,46 @@ namespace llarp::vpn
}
void
AddRoute(IPVariant_t ip, IPVariant_t gateway) override
AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
{
Route(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, ip, gateway);
}
void
DelRoute(IPVariant_t ip, IPVariant_t gateway) override
DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
{
Route(RTM_DELROUTE, 0, ip, gateway);
}
void
AddDefaultRouteViaInterface(std::string ifname) override
AddDefaultRouteViaInterface(NetworkInterface& vpn) override
{
DefaultRouteViaInterface(ifname, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL);
DefaultRouteViaInterface(vpn, RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL);
}
void
DelDefaultRouteViaInterface(std::string ifname) override
DelDefaultRouteViaInterface(NetworkInterface& vpn) override
{
DefaultRouteViaInterface(ifname, RTM_DELROUTE, 0);
DefaultRouteViaInterface(vpn, RTM_DELROUTE, 0);
}
void
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{
RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn.IfName(), range);
RouteViaInterface(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, vpn, range);
}
void
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{
RouteViaInterface(RTM_DELROUTE, 0, vpn.IfName(), range);
RouteViaInterface(RTM_DELROUTE, 0, vpn, range);
}
std::vector<IPVariant_t>
GetGatewaysNotOnInterface(std::string ifname) override
std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(NetworkInterface& vpn) override
{
std::vector<IPVariant_t> gateways{};
const auto& ifname = vpn.Info().ifname;
std::vector<net::ipaddr_t> gateways{};
std::ifstream inf{"/proc/net/route"};
for (std::string line; std::getline(inf, line);)
{
@ -425,7 +415,7 @@ namespace llarp::vpn
{
huint32_t x{};
oxenc::from_hex(ip.begin(), ip.end(), reinterpret_cast<char*>(&x.h));
gateways.emplace_back(x);
gateways.emplace_back(net::ipv4addr_t::from_host(x.h));
}
}
}

@ -0,0 +1,27 @@
#pragma once
#include <functional>
#include <vector>
#include <llarp/util/types.hpp>
namespace llarp::vpn
{
using PacketSendFunc_t = std::function<void(std::vector<byte_t>)>;
using PacketInterceptFunc_t = std::function<void(std::vector<byte_t>, PacketSendFunc_t)>;
class I_PacketIntercepter
{
public:
virtual ~I_PacketIntercepter() = default;
/// start intercepting packets and call a callback for each one we get
/// the callback passes in an ip packet and a function that we can use to send an ip packet to
/// its origin
virtual void
start(PacketInterceptFunc_t f) = 0;
/// stop intercepting packets
virtual void
stop() = 0;
};
} // namespace llarp::vpn

@ -4,14 +4,15 @@ namespace llarp::vpn
{
struct UDPPacketHandler : public Layer4Handler
{
PacketHandlerFunc m_BaseHandler;
std::unordered_map<nuint16_t, PacketHandlerFunc> m_LocalPorts;
PacketHandlerFunc_t m_BaseHandler;
std::unordered_map<nuint16_t, PacketHandlerFunc_t> m_LocalPorts;
explicit UDPPacketHandler(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)}
explicit UDPPacketHandler(PacketHandlerFunc_t baseHandler)
: m_BaseHandler{std::move(baseHandler)}
{}
void
AddSubHandler(nuint16_t localport, PacketHandlerFunc handler) override
AddSubHandler(nuint16_t localport, PacketHandlerFunc_t handler) override
{
m_LocalPorts.emplace(localport, std::move(handler));
}
@ -19,12 +20,15 @@ namespace llarp::vpn
void
HandleIPPacket(llarp::net::IPPacket pkt) override
{
const uint8_t* ptr = pkt.buf + (pkt.Header()->ihl * 4) + 2;
const nuint16_t dstPort{*reinterpret_cast<const uint16_t*>(ptr)};
if (auto itr = m_LocalPorts.find(dstPort); itr != m_LocalPorts.end())
auto dstport = pkt.DstPort();
if (not dstport)
{
itr->second(std::move(pkt));
m_BaseHandler(std::move(pkt));
return;
}
if (auto itr = m_LocalPorts.find(*dstport); itr != m_LocalPorts.end())
itr->second(std::move(pkt));
else
m_BaseHandler(std::move(pkt));
}
@ -32,9 +36,9 @@ namespace llarp::vpn
struct GenericLayer4Handler : public Layer4Handler
{
PacketHandlerFunc m_BaseHandler;
PacketHandlerFunc_t m_BaseHandler;
explicit GenericLayer4Handler(PacketHandlerFunc baseHandler)
explicit GenericLayer4Handler(PacketHandlerFunc_t baseHandler)
: m_BaseHandler{std::move(baseHandler)}
{}
@ -45,7 +49,8 @@ namespace llarp::vpn
}
};
PacketRouter::PacketRouter(PacketHandlerFunc baseHandler) : m_BaseHandler{std::move(baseHandler)}
PacketRouter::PacketRouter(PacketHandlerFunc_t baseHandler)
: m_BaseHandler{std::move(baseHandler)}
{}
void
@ -53,15 +58,13 @@ namespace llarp::vpn
{
const auto proto = pkt.Header()->protocol;
if (const auto itr = m_IPProtoHandler.find(proto); itr != m_IPProtoHandler.end())
{
itr->second->HandleIPPacket(std::move(pkt));
}
else
m_BaseHandler(std::move(pkt));
}
void
PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc func)
PacketRouter::AddUDPHandler(huint16_t localport, PacketHandlerFunc_t func)
{
constexpr byte_t udp_proto = 0x11;
@ -73,7 +76,7 @@ namespace llarp::vpn
}
void
PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc func)
PacketRouter::AddIProtoHandler(uint8_t proto, PacketHandlerFunc_t func)
{
m_IPProtoHandler[proto] = std::make_unique<GenericLayer4Handler>(std::move(func));
}

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

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

@ -2,10 +2,12 @@
#include <llarp/net/ip_range.hpp>
#include <llarp/net/ip_packet.hpp>
#include <set>
#include <oxenc/variant.h>
#include "i_packet_io.hpp"
#include <set>
namespace llarp
{
struct Context;
@ -30,36 +32,40 @@ namespace llarp::vpn
struct InterfaceInfo
{
std::string ifname;
unsigned int index;
huint32_t dnsaddr;
std::set<InterfaceAddress> addrs;
std::vector<InterfaceAddress> addrs;
/// get address number N
inline net::ipaddr_t
operator[](size_t idx) const
{
const auto& range = addrs[idx].range;
if (range.IsV4())
return ToNet(net::TruncateV6(range.addr));
return ToNet(range.addr);
}
};
/// a vpn network interface
class NetworkInterface
class NetworkInterface : public I_Packet_IO
{
protected:
InterfaceInfo m_Info;
public:
NetworkInterface() = default;
NetworkInterface(InterfaceInfo info) : m_Info{std::move(info)}
{}
NetworkInterface(const NetworkInterface&) = delete;
NetworkInterface(NetworkInterface&&) = delete;
virtual ~NetworkInterface() = default;
/// get pollable fd for reading
virtual int
PollFD() const = 0;
/// the interface's name
virtual std::string
IfName() const = 0;
/// read next ip packet, return an empty packet if there are none ready.
virtual net::IPPacket
ReadNextPacket() = 0;
/// write a packet to the interface
/// returns false if we dropped it
virtual bool
WritePacket(net::IPPacket pkt) = 0;
const InterfaceInfo&
Info() const
{
return m_Info;
}
/// idempotently wake up the upper layers as needed (platform dependant)
virtual void
@ -69,8 +75,6 @@ namespace llarp::vpn
class IRouteManager
{
public:
using IPVariant_t = std::variant<huint32_t, huint128_t>;
IRouteManager() = default;
IRouteManager(const IRouteManager&) = delete;
IRouteManager(IRouteManager&&) = delete;
@ -86,16 +90,16 @@ namespace llarp::vpn
}
virtual void
AddRoute(IPVariant_t ip, IPVariant_t gateway) = 0;
AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0;
virtual void
DelRoute(IPVariant_t ip, IPVariant_t gateway) = 0;
DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) = 0;
virtual void
AddDefaultRouteViaInterface(std::string ifname) = 0;
AddDefaultRouteViaInterface(NetworkInterface& vpn) = 0;
virtual void
DelDefaultRouteViaInterface(std::string ifname) = 0;
DelDefaultRouteViaInterface(NetworkInterface& vpn) = 0;
virtual void
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0;
@ -103,8 +107,8 @@ namespace llarp::vpn
virtual void
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) = 0;
virtual std::vector<IPVariant_t>
GetGatewaysNotOnInterface(std::string ifname) = 0;
virtual std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(NetworkInterface& vpn) = 0;
virtual void
AddBlackhole(){};
@ -117,20 +121,43 @@ namespace llarp::vpn
/// responsible for obtaining vpn interfaces
class Platform
{
protected:
/// get a new network interface fully configured given the interface info
/// blocks until ready, throws on error
virtual std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0;
public:
Platform() = default;
Platform(const Platform&) = delete;
Platform(Platform&&) = delete;
virtual ~Platform() = default;
/// get a new network interface fully configured given the interface info
/// blocks until ready, throws on error
virtual std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo info, AbstractRouter* router) = 0;
/// create and start a network interface
inline std::shared_ptr<NetworkInterface>
CreateInterface(InterfaceInfo info, AbstractRouter* router)
{
if (auto netif = ObtainInterface(std::move(info), router))
{
netif->Start();
return netif;
}
return nullptr;
}
/// get owned ip route manager for managing routing table
virtual IRouteManager&
RouteManager() = 0;
/// create a packet io that will read (and optionally write) packets on a network interface the
/// lokinet process does not own
/// @param index the interface index of the network interface to use or 0 for all
/// interfaces on the system
virtual std::shared_ptr<I_Packet_IO>
create_packet_io(unsigned int)
{
throw std::runtime_error{"raw packet io is unimplemented"};
}
};
/// create native vpn platform

@ -5,662 +5,191 @@
#include <io.h>
#include <fcntl.h>
#include <llarp/util/thread/queue.hpp>
#include <llarp/ev/vpn.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <llarp/win32/exec.hpp>
#include <llarp/win32/windivert.hpp>
#include <llarp/win32/wintun.hpp>
#include <llarp.hpp>
#include <fmt/std.h>
// DDK macros
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define FILE_DEVICE_UNKNOWN 0x00000022
#define FILE_ANY_ACCESS 0x00000000
#define METHOD_BUFFERED 0
/* From OpenVPN tap driver, common.h */
#define TAP_CONTROL_CODE(request, method) \
CTL_CODE(FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
#define TAP_IOCTL_GET_MAC TAP_CONTROL_CODE(1, METHOD_BUFFERED)
#define TAP_IOCTL_GET_VERSION TAP_CONTROL_CODE(2, METHOD_BUFFERED)
#define TAP_IOCTL_GET_MTU TAP_CONTROL_CODE(3, METHOD_BUFFERED)
#define TAP_IOCTL_GET_INFO TAP_CONTROL_CODE(4, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE(5, METHOD_BUFFERED)
#define TAP_IOCTL_SET_MEDIA_STATUS TAP_CONTROL_CODE(6, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_DHCP_MASQ TAP_CONTROL_CODE(7, METHOD_BUFFERED)
#define TAP_IOCTL_GET_LOG_LINE TAP_CONTROL_CODE(8, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_DHCP_SET_OPT TAP_CONTROL_CODE(9, METHOD_BUFFERED)
#define TAP_IOCTL_CONFIG_TUN TAP_CONTROL_CODE(10, METHOD_BUFFERED)
/* Windows registry crap */
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
#define NETWORK_ADAPTERS \
"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-" \
"08002BE10318}"
#include "platform.hpp"
typedef unsigned long IPADDR;
namespace llarp::vpn
namespace llarp::win32
{
static char*
reg_query(char* key_name)
{
HKEY adapters, adapter;
DWORD i, ret, len;
char* deviceid = nullptr;
DWORD sub_keys = 0;
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(key_name), 0, KEY_READ, &adapters);
if (ret != ERROR_SUCCESS)
{
return nullptr;
}
ret = RegQueryInfoKey(
adapters, NULL, NULL, NULL, &sub_keys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS)
{
return nullptr;
}
if (sub_keys <= 0)
{
return nullptr;
}
/* Walk througt all adapters */
for (i = 0; i < sub_keys; i++)
{
char new_key[MAX_KEY_LENGTH];
char data[256];
TCHAR key[MAX_KEY_LENGTH];
DWORD keylen = MAX_KEY_LENGTH;
/* Get the adapter key name */
ret = RegEnumKeyEx(adapters, i, key, &keylen, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS)
{
continue;
}
/* Append it to NETWORK_ADAPTERS and open it */
snprintf(new_key, sizeof new_key, "%s\\%s", key_name, key);
ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT(new_key), 0, KEY_READ, &adapter);
if (ret != ERROR_SUCCESS)
{
continue;
}
/* Check its values */
len = sizeof data;
ret = RegQueryValueEx(adapter, "ComponentId", NULL, NULL, (LPBYTE)data, &len);
if (ret != ERROR_SUCCESS)
{
/* This value doesn't exist in this adaptater tree */
goto clean;
}
/* If its a tap adapter, its all good */
if (strncmp(data, "tap0901", 7) == 0)
{
DWORD type;
len = sizeof data;
ret = RegQueryValueEx(adapter, "NetCfgInstanceId", NULL, &type, (LPBYTE)data, &len);
if (ret != ERROR_SUCCESS)
{
goto clean;
}
deviceid = strdup(data);
break;
}
clean:
RegCloseKey(adapter);
}
RegCloseKey(adapters);
return deviceid;
}
template <typename Visit>
void
ForEachWIN32Interface(Visit visit)
{
#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))
MIB_IPFORWARDTABLE* pIpForwardTable;
DWORD dwSize = 0;
DWORD dwRetVal = 0;
pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(sizeof(MIB_IPFORWARDTABLE));
if (pIpForwardTable == nullptr)
return;
if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
{
FREE(pIpForwardTable);
pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize);
if (pIpForwardTable == nullptr)
{
return;
}
}
if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR)
{
for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++)
{
visit(&pIpForwardTable->table[i]);
}
}
FREE(pIpForwardTable);
#undef MALLOC
#undef FREE
}
std::optional<int>
GetInterfaceIndex(huint32_t ip)
{
std::optional<int> ret = std::nullopt;
ForEachWIN32Interface([&ret, n = ToNet(ip)](auto* iface) {
if (ret.has_value())
return;
if (iface->dwForwardNextHop == n.n)
{
ret = iface->dwForwardIfIndex;
}
});
return ret;
}
namespace
{
std::wstring
get_win_sys_path()
template <typename T>
std::string
ip_to_string(T ip)
{
wchar_t win_sys_path[MAX_PATH] = {0};
const wchar_t* default_sys_path = L"C:\\Windows\\system32";
if (!GetSystemDirectoryW(win_sys_path, _countof(win_sys_path)))
{
wcsncpy(win_sys_path, default_sys_path, _countof(win_sys_path));
win_sys_path[_countof(win_sys_path) - 1] = L'\0';
}
return win_sys_path;
return var::visit([](auto&& ip) { return ip.ToString(); }, ip);
}
} // namespace
class Win32Interface final : public NetworkInterface
using namespace llarp::vpn;
class VPNPlatform : public Platform, public IRouteManager
{
std::atomic<bool> m_Run;
HANDLE m_Device, m_IOCP;
std::vector<std::thread> m_Threads;
thread::Queue<net::IPPacket> m_ReadQueue;
InterfaceInfo m_Info;
llarp::Context* const _ctx;
const int m_Metric{2};
AbstractRouter* const _router;
static std::string
NetSHCommand()
const auto&
Net() const
{
std::wstring wcmd = get_win_sys_path() + L"\\netsh.exe";
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
return converter.to_bytes(wcmd);
return _ctx->router->Net();
}
static void
NetSH(std::string commands)
void
Route(std::string ip, std::string gw, std::string cmd)
{
commands = NetSHCommand() + " interface IPv6 " + commands;
LogInfo("exec: ", commands);
::system(commands.c_str());
llarp::win32::Exec(
"route.exe",
fmt::format("{} {} MASK 255.255.255.255 {} METRIC {}", cmd, ip, gw, m_Metric));
}
public:
static std::string
RouteCommand()
void
DefaultRouteViaInterface(NetworkInterface& vpn, std::string cmd)
{
std::wstring wcmd = get_win_sys_path() + L"\\route.exe";
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
return converter.to_bytes(wcmd);
// route hole for loopback bacause god is dead on windows
llarp::win32::Exec(
"route.exe", fmt::format("{} 127.0.0.0 MASK 255.0.0.0 0.0.0.0 METRIC {}", cmd, m_Metric));
// set up ipv4 routes
auto lower = RouteViaInterface(vpn, "0.0.0.0", "128.0.0.0", cmd);
auto upper = RouteViaInterface(vpn, "128.0.0.0", "128.0.0.0", cmd);
}
Win32Interface(InterfaceInfo info, AbstractRouter* router)
: m_ReadQueue{1024}, m_Info{std::move(info)}, _router{router}
OneShotExec
RouteViaInterface(NetworkInterface& vpn, std::string addr, std::string mask, std::string cmd)
{
DWORD len;
const auto device_id = reg_query(NETWORK_ADAPTERS);
if (device_id == nullptr)
{
LogError("cannot query registry");
throw std::invalid_argument{"cannot query registery"};
}
const auto fname = fmt::format(R"(\\.\Global\{}.tap)", device_id);
m_Device = CreateFile(
fname.c_str(),
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
0);
if (m_Device == INVALID_HANDLE_VALUE)
const auto& info = vpn.Info();
auto index = info.index;
if (index == 0)
{
LogError("failed to open device");
throw std::invalid_argument{"cannot open " + fname};
if (auto maybe_idx = net::Platform::Default_ptr()->GetInterfaceIndex(info[0]))
index = *maybe_idx;
}
LogInfo("putting interface up...");
ULONG flag = 1;
// put the interface up
if (not DeviceIoControl(
m_Device,
TAP_IOCTL_SET_MEDIA_STATUS,
&flag,
sizeof(flag),
&flag,
sizeof(flag),
&len,
nullptr))
auto ifaddr = ip_to_string(info[0]);
// this changes the last 1 to a 0 so that it routes over the interface
// this is required because windows is idiotic af
ifaddr.back()--;
if (index)
{
LogError("cannot up interface up");
throw std::invalid_argument{"cannot put interface up"};
return OneShotExec{
"route.exe",
fmt::format(
"{} {} MASK {} {} IF {} METRIC {}", cmd, addr, mask, ifaddr, info.index, m_Metric)};
}
LogInfo("setting addresses");
huint32_t ip{};
// set ipv4 addresses
for (const auto& ifaddr : m_Info.addrs)
{
if (ifaddr.fam == AF_INET)
{
IPADDR sock[3]{};
const nuint32_t addr = xhtonl(net::TruncateV6(ifaddr.range.addr));
ip = net::TruncateV6(ifaddr.range.addr);
m_Info.ifname = ip.ToString();
const nuint32_t mask = xhtonl(net::TruncateV6(ifaddr.range.netmask_bits));
LogInfo("address ", addr, " netmask ", mask);
sock[0] = addr.n;
sock[1] = addr.n & mask.n;
sock[2] = mask.n;
if (not DeviceIoControl(
m_Device,
TAP_IOCTL_CONFIG_TUN,
&sock,
sizeof(sock),
&sock,
sizeof(sock),
&len,
nullptr))
{
LogError("cannot set address");
throw std::invalid_argument{"cannot configure tun interface address"};
}
IPADDR ep[4]{};
ep[0] = addr.n;
ep[1] = mask.n;
ep[2] = (addr.n | ~mask.n) - htonl(1);
ep[3] = 31536000;
if (not DeviceIoControl(
m_Device,
TAP_IOCTL_CONFIG_DHCP_MASQ,
ep,
sizeof(ep),
ep,
sizeof(ep),
&len,
nullptr))
{
LogError("cannot set dhcp masq");
throw std::invalid_argument{"Cannot configure tun interface dhcp"};
}
#pragma pack(push)
#pragma pack(1)
struct opt
{
uint8_t dhcp_opt;
uint8_t length;
uint32_t value;
} dns, gateway;
#pragma pack(pop)
const nuint32_t dnsaddr{xhtonl(m_Info.dnsaddr)};
dns.dhcp_opt = 6;
dns.length = 4;
dns.value = dnsaddr.n;
gateway.dhcp_opt = 3;
gateway.length = 4;
gateway.value = addr.n + htonl(1);
if (not DeviceIoControl(
m_Device,
TAP_IOCTL_CONFIG_DHCP_SET_OPT,
&gateway,
sizeof(gateway),
&gateway,
sizeof(gateway),
&len,
nullptr))
{
LogError("cannot set gateway");
throw std::invalid_argument{"cannot set tun gateway"};
}
if (not DeviceIoControl(
m_Device,
TAP_IOCTL_CONFIG_DHCP_SET_OPT,
&dns,
sizeof(dns),
&dns,
sizeof(dns),
&len,
nullptr))
{
LogError("cannot set dns");
throw std::invalid_argument{"cannot set tun dns"};
}
}
}
// set ipv6 addresses
for (const auto& ifaddr : m_Info.addrs)
else
{
if (ifaddr.fam == AF_INET6)
{
const auto maybe = GetInterfaceIndex(ip);
if (maybe.has_value())
{
NetSH(
"add address interface=" + std::to_string(*maybe) + " " + ifaddr.range.ToString());
}
}
return OneShotExec{
"route.exe",
fmt::format("{} {} MASK {} {} METRIC {}", cmd, addr, mask, ifaddr, m_Metric)};
}
}
~Win32Interface()
{
ULONG flag = 0;
DWORD len;
// put the interface down
DeviceIoControl(
m_Device,
TAP_IOCTL_SET_MEDIA_STATUS,
&flag,
sizeof(flag),
&flag,
sizeof(flag),
&len,
nullptr);
m_Run = false;
CloseHandle(m_IOCP);
// close the handle
CloseHandle(m_Device);
// close the reader threads
for (auto& thread : m_Threads)
thread.join();
}
virtual void
MaybeWakeUpperLayers() const override
{
_router->TriggerPump();
}
int
PollFD() const override
{
return -1;
}
std::string
IfName() const override
{
return m_Info.ifname;
}
void
Start()
{
m_Run = true;
const auto numThreads = std::thread::hardware_concurrency();
// allocate packets
for (size_t idx = 0; idx < numThreads; ++idx)
m_Packets.emplace_back(new asio_evt_pkt{true});
// create completion port
m_IOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
// attach the handle or some shit
CreateIoCompletionPort(m_Device, m_IOCP, 0, 0);
// spawn reader threads
for (size_t idx = 0; idx < numThreads; ++idx)
m_Threads.emplace_back([this, idx]() { ReadLoop(idx); });
}
net::IPPacket
ReadNextPacket() override
{
if (m_ReadQueue.empty())
return net::IPPacket{};
return m_ReadQueue.popFront();
}
struct asio_evt_pkt
{
explicit asio_evt_pkt(bool _read) : read{_read}
{}
std::shared_ptr<WintunContext> _wintun;
OVERLAPPED hdr = {0, 0, 0, 0, nullptr}; // must be first, since this is part of the IO call
bool read;
net::IPPacket pkt;
WinDivert_API m_WinDivert{};
void
Read(HANDLE dev)
{
ReadFile(dev, pkt.buf, sizeof(pkt.buf), nullptr, &hdr);
}
};
public:
VPNPlatform(const VPNPlatform&) = delete;
VPNPlatform(VPNPlatform&&) = delete;
std::vector<std::unique_ptr<asio_evt_pkt>> m_Packets;
VPNPlatform(llarp::Context* ctx) : Platform{}, _ctx{ctx}, _wintun{WintunContext_new()}
{}
bool
WritePacket(net::IPPacket pkt)
{
LogDebug("write packet ", pkt.sz);
asio_evt_pkt* ev = new asio_evt_pkt{false};
std::copy_n(pkt.buf, pkt.sz, ev->pkt.buf);
ev->pkt.sz = pkt.sz;
WriteFile(m_Device, ev->pkt.buf, ev->pkt.sz, nullptr, &ev->hdr);
return true;
}
virtual ~VPNPlatform() = default;
void
ReadLoop(size_t packetIndex)
AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
{
auto& ev = m_Packets[packetIndex];
ev->Read(m_Device);
while (m_Run)
{
DWORD size;
ULONG_PTR user;
OVERLAPPED* ovl = nullptr;
if (not GetQueuedCompletionStatus(m_IOCP, &size, &user, &ovl, 1000))
continue;
asio_evt_pkt* pkt = (asio_evt_pkt*)ovl;
LogDebug("got iocp event size=", size, " read=", pkt->read);
if (pkt->read)
{
pkt->pkt.sz = size;
m_ReadQueue.pushBack(pkt->pkt);
pkt->Read(m_Device);
}
else
delete pkt;
}
Route(ip_to_string(ip), ip_to_string(gateway), "ADD");
}
};
class Win32RouteManager : public IRouteManager
{
void
Execute(std::string cmd) const
DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway) override
{
llarp::LogInfo("exec: ", cmd);
::system(cmd.c_str());
}
static std::string
PowerShell()
{
std::wstring wcmd =
get_win_sys_path() + L"\\WindowsPowerShell\\v1.0\\powershell.exe -Command ";
using convert_type = std::codecvt_utf8<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
return converter.to_bytes(wcmd);
}
static std::string
RouteCommand()
{
return Win32Interface::RouteCommand();
Route(ip_to_string(ip), ip_to_string(gateway), "DELETE");
}
void
Route(IPVariant_t ip, IPVariant_t gateway, std::string cmd)
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{
Execute(fmt::format(
"{} {} {} MASK 255.255.255.255 {} METRIC 2", RouteCommand(), cmd, ip, gateway));
RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "ADD");
}
void
DefaultRouteViaInterface(std::string ifname, std::string cmd)
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{
// poke hole for loopback bacause god is dead on windows
Execute(RouteCommand() + " " + cmd + " 127.0.0.0 MASK 255.0.0.0 0.0.0.0");
huint32_t ip{};
ip.FromString(ifname);
const auto ipv6 = net::ExpandV4Lan(ip);
Execute(RouteCommand() + " " + cmd + " ::/2 " + ipv6.ToString());
Execute(RouteCommand() + " " + cmd + " 4000::/2 " + ipv6.ToString());
Execute(RouteCommand() + " " + cmd + " 8000::/2 " + ipv6.ToString());
Execute(RouteCommand() + " " + cmd + " c000::/2 " + ipv6.ToString());
ifname.back()++;
Execute(RouteCommand() + " " + cmd + " 0.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2");
Execute(RouteCommand() + " " + cmd + " 128.0.0.0 MASK 128.0.0.0 " + ifname + " METRIC 2");
RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "DELETE");
}
void
RouteViaInterface(std::string ifname, IPRange range, std::string cmd)
std::vector<net::ipaddr_t>
GetGatewaysNotOnInterface(NetworkInterface& vpn) override
{
if (range.IsV4())
{
const huint32_t addr4 = net::TruncateV6(range.addr);
const huint32_t mask4 = net::TruncateV6(range.netmask_bits);
Execute(
RouteCommand() + " " + cmd + " " + addr4.ToString() + " MASK " + mask4.ToString() + " "
+ ifname);
}
else
std::vector<net::ipaddr_t> gateways;
auto idx = vpn.Info().index;
using UInt_t = decltype(idx);
for (const auto& iface : Net().AllNetworkInterfaces())
{
Execute(
RouteCommand() + " " + cmd + range.addr.ToString() + " MASK "
+ range.netmask_bits.ToString() + " " + ifname);
if (static_cast<UInt_t>(iface.index) == idx)
continue;
if (iface.gateway)
gateways.emplace_back(*iface.gateway);
}
}
public:
void
AddRoute(IPVariant_t ip, IPVariant_t gateway) override
{
Route(ip, gateway, "ADD");
return gateways;
}
void
DelRoute(IPVariant_t ip, IPVariant_t gateway) override
AddDefaultRouteViaInterface(NetworkInterface& vpn) override
{
Route(ip, gateway, "DELETE");
}
// kill ipv6
llarp::win32::Exec(
"WindowsPowerShell\\v1.0\\powershell.exe",
"-Command (Disable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
void
AddRouteViaInterface(NetworkInterface& vpn, IPRange range) override
{
RouteViaInterface(vpn.IfName(), range, "ADD");
DefaultRouteViaInterface(vpn, "ADD");
llarp::win32::Exec("ipconfig.exe", "/flushdns");
}
void
DelRouteViaInterface(NetworkInterface& vpn, IPRange range) override
DelDefaultRouteViaInterface(NetworkInterface& vpn) override
{
RouteViaInterface(vpn.IfName(), range, "DELETE");
}
// restore ipv6
llarp::win32::Exec(
"WindowsPowerShell\\v1.0\\powershell.exe",
"-Command (Enable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
std::vector<IPVariant_t>
GetGatewaysNotOnInterface(std::string ifname) override
{
std::vector<IPVariant_t> gateways;
ForEachWIN32Interface([&](auto w32interface) {
struct in_addr gateway, interface_addr;
gateway.S_un.S_addr = (u_long)w32interface->dwForwardDest;
interface_addr.S_un.S_addr = (u_long)w32interface->dwForwardNextHop;
std::string interface_name{inet_ntoa(interface_addr)};
if ((!gateway.S_un.S_addr) and interface_name != ifname)
{
llarp::LogTrace(
"Win32 find gateway: Adding gateway (", interface_name, ") to list of gateways.");
huint32_t x{};
if (x.FromString(interface_name))
gateways.push_back(x);
}
});
return gateways;
DefaultRouteViaInterface(vpn, "DELETE");
llarp::win32::Exec("netsh.exe", "winsock reset");
llarp::win32::Exec("ipconfig.exe", "/flushdns");
}
void
AddDefaultRouteViaInterface(std::string ifname) override
std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo info, AbstractRouter* router) override
{
// kill ipv6
Execute(PowerShell() + R"(Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)");
DefaultRouteViaInterface(ifname, "ADD");
return WintunInterface_new(_wintun, std::move(info), router);
}
void
DelDefaultRouteViaInterface(std::string ifname) override
std::shared_ptr<I_Packet_IO>
create_packet_io(unsigned int ifindex) override
{
// restore ipv6
Execute(PowerShell() + R"(Enable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6)");
DefaultRouteViaInterface(ifname, "DELETE");
// we only want do this on all interfaes with windivert
if (ifindex)
throw std::invalid_argument{
"cannot create packet io on explicitly specified interface, not currently supported on "
"windows (yet)"};
return m_WinDivert.make_intercepter(
"outbound and ( udp.DstPort == 53 or tcp.DstPort == 53 )",
[router = _ctx->router] { router->TriggerPump(); });
}
};
class Win32Platform : public Platform
{
Win32RouteManager _routeManager{};
public:
std::shared_ptr<NetworkInterface>
ObtainInterface(InterfaceInfo info, AbstractRouter* router) override
{
auto netif = std::make_shared<Win32Interface>(std::move(info), router);
netif->Start();
return netif;
};
IRouteManager&
RouteManager() override
{
return _routeManager;
return *this;
}
};
} // namespace llarp::vpn
} // namespace llarp::win32

@ -0,0 +1,22 @@
#include "dll.hpp"
#include <llarp/util/logging.hpp>
#include <llarp/util/str.hpp>
namespace llarp::win32
{
namespace
{
auto cat = log::Cat("win32-dll");
}
DLL::DLL(std::string dll) : m_Handle{LoadLibraryA(dll.c_str())}
{
if (not m_Handle)
throw win32::error{fmt::format("failed to load '{}'", dll)};
log::info(cat, "loaded '{}'", dll);
}
DLL::~DLL()
{
FreeLibrary(m_Handle);
}
} // namespace llarp::win32

@ -0,0 +1,30 @@
#pragma once
#include <windows.h>
#include "exception.hpp"
#include <llarp/util/str.hpp>
namespace llarp::win32
{
class DLL
{
const HMODULE m_Handle;
protected:
/// given a name of a function pointer find it and put it into `func`
/// throws if the function does not exist in the DLL we openned.
template <typename Func_t>
void
init(std::string name, Func_t& func)
{
auto ptr = GetProcAddress(m_Handle, name.c_str());
if (not ptr)
throw win32::error{fmt::format("function '{}' not found", name)};
func = reinterpret_cast<Func_t&>(ptr);
}
public:
DLL(std::string dll);
virtual ~DLL();
};
} // namespace llarp::win32

@ -0,0 +1,39 @@
#include "windows.h"
#include "exception.hpp"
#include <llarp/util/str.hpp>
#include <array>
namespace llarp::win32
{
error::error(std::string msg) : error{GetLastError(), std::move(msg)}
{}
error::error(DWORD err, std::string msg)
: std::runtime_error{fmt::format("{}: {} (code={})", msg, error_to_string(err), err)}
{}
std::string
error_to_string(DWORD err)
{
// mostly yoinked from https://stackoverflow.com/a/45565001
LPTSTR psz{nullptr};
const DWORD cchMsg = FormatMessage(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
nullptr,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPTSTR>(&psz),
0,
nullptr);
if (cchMsg <= 0)
{
// cannot get message for error, reset the last error here so it does not propagate
::SetLastError(0);
return "unknown error";
}
auto deleter = [](void* p) { ::LocalFree(p); };
std::unique_ptr<TCHAR, decltype(deleter)> ptrBuffer{psz, deleter};
return std::string{ptrBuffer.get(), cchMsg};
}
} // namespace llarp::win32

@ -1,31 +1,18 @@
#pragma once
#include <array>
#include <string>
#include <windows.h>
#include <string>
#include <stdexcept>
#include <llarp/util/str.hpp>
namespace llarp::win32
{
namespace
{
inline std::string
error_to_string(DWORD err)
{
std::array<CHAR, 512> buffer{};
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, &err, 0, 0, buffer.data(), buffer.size(), nullptr);
return std::string{buffer.data()};
}
} // namespace
std::string
error_to_string(DWORD err);
class error : public std::runtime_error
{
public:
error(DWORD err, std::string msg)
: std::runtime_error{fmt::format("{}: {}", msg, error_to_string(err))}
{}
error(std::string msg);
virtual ~error() = default;
explicit error(DWORD err, std::string msg);
};
} // namespace llarp::win32

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

Loading…
Cancel
Save