From 1f1334c6d3c15129bc14ae58dd18b8fcdc6a7454 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Wed, 29 Jan 2025 14:18:13 +0800 Subject: [PATCH] Restore support for VPN on Apple --- llarp/vpn/apple.hpp | 173 +++++++++++++++++++++++++++++++++++++++++ llarp/vpn/platform.cpp | 5 +- 2 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 llarp/vpn/apple.hpp diff --git llarp/vpn/apple.hpp llarp/vpn/apple.hpp new file mode 100644 index 000000000..f20c50ce3 --- /dev/null +++ llarp/vpn/apple.hpp @@ -0,0 +1,173 @@ +#pragma once + +#include +#include "common.hpp" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace llarp::vpn +{ + class AppleInterface : public NetworkInterface + { + const int m_FD; + const InterfaceInfo m_Info; + std::string m_IfName; + + static void + Exec(std::string cmd) + { + LogDebug(cmd); + system(cmd.c_str()); + } + + public: + AppleInterface(InterfaceInfo info) + : m_FD{::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)}, m_Info{std::move(info)} + { + if (m_FD == -1) + throw std::invalid_argument{"cannot open control socket: " + std::string{strerror(errno)}}; + + ctl_info cinfo{}; + const std::string apple_utun = "com.apple.net.utun_control"; + std::copy_n(apple_utun.c_str(), apple_utun.size(), cinfo.ctl_name); + if (::ioctl(m_FD, CTLIOCGINFO, &cinfo) < 0) + { + ::close(m_FD); + throw std::runtime_error{"ioctl CTLIOCGINFO call failed: " + std::string{strerror(errno)}}; + } + sockaddr_ctl addr{}; + addr.sc_id = cinfo.ctl_id; + + addr.sc_len = sizeof(addr); + addr.sc_family = AF_SYSTEM; + addr.ss_sysaddr = AF_SYS_CONTROL; + addr.sc_unit = 0; + + if (connect(m_FD, (sockaddr*)&addr, sizeof(addr)) < 0) + { + ::close(m_FD); + throw std::runtime_error{ + "cannot connect to control socket address: " + std::string{strerror(errno)}}; + } + uint32_t namesz = IFNAMSIZ; + char name[IFNAMSIZ + 1]{}; + if (getsockopt(m_FD, SYSPROTO_CONTROL, 2, name, &namesz) < 0) + { + ::close(m_FD); + throw std::runtime_error{ + "cannot query for interface name: " + std::string{strerror(errno)}}; + } + m_IfName = name; + for (const auto& ifaddr : m_Info.addrs) + { + if (ifaddr.fam == AF_INET) + { + const huint32_t addr = net::TruncateV6(ifaddr.range.addr); + const huint32_t netmask = net::TruncateV6(ifaddr.range.netmask_bits); + const huint32_t daddr = addr & netmask; + Exec( + "/sbin/ifconfig " + m_IfName + " " + addr.ToString() + " " + daddr.ToString() + + " mtu 1500 netmask 255.255.255.255 up"); + Exec( + "/sbin/route add " + daddr.ToString() + " -netmask " + netmask.ToString() + + " -interface " + m_IfName); + Exec("/sbin/route add " + addr.ToString() + " -interface lo0"); + } + else if (ifaddr.fam == AF_INET6) + { + Exec("/sbin/ifconfig " + m_IfName + " inet6 " + ifaddr.range.ToString()); + } + } + } + + ~AppleInterface() + { + ::close(m_FD); + } + + std::string + IfName() const override + { + return m_IfName; + } + + int + PollFD() const override + { + return m_FD; + } + + net::IPPacket + ReadNextPacket() override + { + constexpr int uintsize = sizeof(unsigned int); + net::IPPacket pkt{}; + unsigned int pktinfo = 0; + const struct iovec vecs[2] = { + {.iov_base = &pktinfo, .iov_len = uintsize}, + {.iov_base = pkt.buf, .iov_len = sizeof(pkt.buf)}}; + int sz = readv(m_FD, vecs, 2); + if (sz >= uintsize) + pkt.sz = sz - uintsize; + else if (sz >= 0 || errno == EAGAIN || errno == EWOULDBLOCK) + pkt.sz = 0; + else + throw std::error_code{errno, std::system_category()}; + return pkt; + } + + bool + WritePacket(net::IPPacket pkt) override + { + static unsigned int af4 = htonl(AF_INET); + static unsigned int af6 = htonl(AF_INET6); + + const struct iovec vecs[2] = { + {.iov_base = pkt.IsV6() ? &af6 : &af4, .iov_len = sizeof(unsigned int)}, + {.iov_base = pkt.buf, .iov_len = pkt.sz}}; + + ssize_t n = writev(m_FD, vecs, 2); + if (n >= (int)sizeof(unsigned int)) + { + n -= sizeof(unsigned int); + return static_cast(n) == pkt.sz; + } + return false; + } + }; + + class ApplePlatform : public Platform + { + public: + std::shared_ptr + ObtainInterface(InterfaceInfo info) override + { + return std::make_shared(std::move(info)); + } + }; +} // namespace llarp::vpn diff --git llarp/vpn/platform.cpp llarp/vpn/platform.cpp index 757894b61..43e0a347b 100644 --- llarp/vpn/platform.cpp +++ llarp/vpn/platform.cpp @@ -11,6 +11,9 @@ #include "linux.hpp" #endif #endif +#ifdef __APPLE__ +#include "apple.hpp" +#endif #include @@ -38,7 +41,7 @@ namespace llarp::vpn #endif #endif #ifdef __APPLE__ - throw std::runtime_error{"not supported"}; + plat = std::make_shared(); #endif return plat; }