Files
ovgate/cmake/Dependencies.cmake
T
iceBear67andClaude Opus 5 b2ba45c9f8 OpenVPN client with an authenticated SOCKS5 front door
A userspace VPN gateway: builds an OpenVPN tunnel to a VPNGate node with
the OpenVPN 3 core, terminates it in-process with lwIP, and serves SOCKS5
(RFC 1928/1929, CONNECT and UDP ASSOCIATE) over it. No root, no tun
device, no routing table changes.

Layout follows the module boundaries in docs/ARCHITECTURE.md:

  vpngate/   directory fetch + CSV parse (lines run to ~13.5 KB, so the
             parser streams rather than splitting on newlines)
  selector/  two-phase pick: cheap prior over the whole list, then real
             TCP handshake timing of the top K
  ovpn/      openvpn3 driven through TunBuilder, packets over a socketpair
  netstack/  lwIP: the TCP/IP stack that makes "no root" possible
  egress/    the swappable way out, and make-before-break switching
  socks5/    the front door
  health/    per-window scoring, and the decision to move
  app/       wiring, admin HTTP, signals

docs/FEASIBILITY.md is the analysis this was built from, including the
one requirement that is not physically possible -- carrying established
TCP connections across a node switch -- and what is done instead
(zero-progress redial, UDP re-homing, grace-period drain).

Tests: 155 without the tunnel egress, 172 with it. The seam is the egress
factory; selection, scoring, history and probing all run for real.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 04:38:39 +00:00

104 lines
4.5 KiB
CMake

# Third-party dependency resolution.
#
# System packages (asio, OpenSSL, lz4, fmt) are expected to be installed.
# openvpn3 and lwIP are source dependencies: point OVG_OPENVPN3_DIR / OVG_LWIP_DIR
# at an existing checkout, or let FetchContent pull them.
include(FetchContent)
find_package(PkgConfig REQUIRED)
find_package(Threads REQUIRED)
pkg_search_module(OPENSSL REQUIRED IMPORTED_TARGET openssl)
pkg_search_module(LZ4 REQUIRED IMPORTED_TARGET liblz4)
find_package(fmt REQUIRED)
find_path(ASIO_INCLUDE_DIR asio.hpp REQUIRED)
message(STATUS "asio headers: ${ASIO_INCLUDE_DIR}")
set(OVG_OPENVPN3_DIR "" CACHE PATH "Existing openvpn3 checkout (skips download)")
set(OVG_LWIP_DIR "" CACHE PATH "Existing lwIP checkout (skips download)")
if(OVG_WITH_TUNNEL)
# ---- openvpn3 core -------------------------------------------------------
if(OVG_OPENVPN3_DIR)
set(OPENVPN3_SOURCE_DIR ${OVG_OPENVPN3_DIR})
else()
FetchContent_Declare(openvpn3
GIT_REPOSITORY https://github.com/OpenVPN/openvpn3.git
GIT_TAG master
GIT_SHALLOW TRUE)
FetchContent_Populate(openvpn3) # populate only: we compile it into our own target
set(OPENVPN3_SOURCE_DIR ${openvpn3_SOURCE_DIR})
endif()
if(NOT EXISTS ${OPENVPN3_SOURCE_DIR}/client/ovpncli.cpp)
message(FATAL_ERROR "openvpn3 not found at ${OPENVPN3_SOURCE_DIR}")
endif()
message(STATUS "openvpn3 source: ${OPENVPN3_SOURCE_DIR}")
# The openvpn3 core is header-only apart from these two units. It cannot be
# built as a standalone library because callers must supply OPENVPN_LOG and
# friends at compile time, so we compile the sources into our own target.
add_library(ovg_openvpn3 STATIC
${OPENVPN3_SOURCE_DIR}/client/ovpncli.cpp
${OPENVPN3_SOURCE_DIR}/openvpn/crypto/data_epoch.cpp)
target_include_directories(ovg_openvpn3 PUBLIC ${OPENVPN3_SOURCE_DIR} ${ASIO_INCLUDE_DIR})
# -isystem for consumers. The core's headers are not clean under our warning
# set and never will be; suppressing per-warning here would mean chasing a
# new flag on every upstream bump, and blanket-disabling warnings on the file
# that includes them would blind us to problems in our own code in that same
# file. This draws the line exactly where it belongs.
set_target_properties(ovg_openvpn3 PROPERTIES
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${OPENVPN3_SOURCE_DIR}")
target_compile_definitions(ovg_openvpn3 PUBLIC
ASIO_STANDALONE
USE_ASIO
HAVE_LZ4
USE_OPENSSL
USE_TUN_BUILDER) # <-- the whole design hinges on this: see docs/FEASIBILITY.md §2.2
target_link_libraries(ovg_openvpn3 PUBLIC
PkgConfig::OPENSSL PkgConfig::LZ4 fmt::fmt Threads::Threads)
target_compile_options(ovg_openvpn3 PRIVATE -Wno-deprecated-declarations)
# ---- lwIP ----------------------------------------------------------------
if(OVG_LWIP_DIR)
set(LWIP_SOURCE_DIR ${OVG_LWIP_DIR})
else()
FetchContent_Declare(lwip
GIT_REPOSITORY https://github.com/lwip-tcpip/lwip.git
GIT_TAG STABLE-2_2_1_RELEASE
GIT_SHALLOW TRUE)
FetchContent_Populate(lwip)
set(LWIP_SOURCE_DIR ${lwip_SOURCE_DIR})
endif()
if(NOT EXISTS ${LWIP_SOURCE_DIR}/src/Filelists.cmake)
message(FATAL_ERROR "lwIP not found at ${LWIP_SOURCE_DIR}")
endif()
message(STATUS "lwIP source: ${LWIP_SOURCE_DIR}")
set(LWIP_DIR ${LWIP_SOURCE_DIR})
# Our lwipopts.h and the arch/ shim live in src/netstack/lwip_port.
set(LWIP_INCLUDE_DIRS
${LWIP_SOURCE_DIR}/src/include
${CMAKE_CURRENT_SOURCE_DIR}/src/netstack/lwip_port)
include(${LWIP_SOURCE_DIR}/src/Filelists.cmake)
# Core + IPv4 only. Deliberately NOT lwipapi_SRCS: api_lib/api_msg/netbuf/
# sockets are the sequential and BSD-socket APIs, which require NO_SYS=0 and
# a real threading layer. We drive the raw callback API from a strand instead
# (see src/netstack/lwip_stack.h), so those files cannot compile and would not
# be used if they did.
add_library(ovg_lwip STATIC ${lwipcore_SRCS} ${lwipcore4_SRCS})
target_include_directories(ovg_lwip PUBLIC ${LWIP_INCLUDE_DIRS})
# -isystem for consumers: lwIP's headers are not clean under our warning set.
set_target_properties(ovg_lwip PROPERTIES
INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "${LWIP_SOURCE_DIR}/src/include")
# No LWIP_DEBUG define here, on purpose. lwIP tests it with #ifdef, not #if,
# so defining it to 0 *enables* the debug machinery -- the opposite of what it
# reads like. lwip_port/lwipopts.h says the same thing where it #undefs it.
target_compile_options(ovg_lwip PRIVATE -Wno-unused-parameter -Wno-address)
endif()