mirror of https://github.com/oxen-io/lokinet
parent
63ed5c16ed
commit
0708a0d897
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 189.4 189.4" style="enable-background:new 0 0 189.4 189.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<polygon class="st0" points="113.6,132.6 94.7,151.5 75.8,132.6 56.8,151.5 94.7,189.4 132.6,151.5 "/>
|
||||||
|
<polygon class="st0" points="132.6,113.6 151.5,94.7 132.6,75.8 151.5,56.8 189.4,94.7 151.5,132.6 "/>
|
||||||
|
<polygon class="st0" points="56.8,75.8 37.9,94.7 56.8,113.6 37.9,132.6 0,94.7 37.9,56.8 "/>
|
||||||
|
<polygon class="st0" points="75.8,56.8 94.7,37.9 113.6,56.8 132.6,37.9 94.7,0 56.8,37.9 "/>
|
||||||
|
|
||||||
|
<rect x="100.2" y="100.2" transform="matrix(0.7071 0.7071 -0.7071 0.7071 113.6329 -47.0683)" class="st0" width="26.8" height="26.8"/>
|
||||||
|
|
||||||
|
<rect x="62.4" y="62.4" transform="matrix(0.7071 0.7071 -0.7071 0.7071 75.7552 -31.3789)" class="st0" width="26.8" height="26.8"/>
|
||||||
|
|
||||||
|
<rect x="100.2" y="62.4" transform="matrix(0.7071 0.7071 -0.7071 0.7071 86.8493 -58.1624)" class="st0" width="26.8" height="26.8"/>
|
||||||
|
|
||||||
|
<rect x="62.4" y="100.2" transform="matrix(0.7071 0.7071 -0.7071 0.7071 102.5388 -20.2848)" class="st0" width="26.8" height="26.8"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# build the shit on mac
|
||||||
|
# t. jeff
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set +x
|
||||||
|
mkdir -p build-mac
|
||||||
|
cd build-mac
|
||||||
|
cmake \
|
||||||
|
-G Ninja \
|
||||||
|
-DBUILD_STATIC_DEPS=ON \
|
||||||
|
-DBUILD_PACKAGE=ON \
|
||||||
|
-DBUILD_SHARED_LIBS=OFF \
|
||||||
|
-DBUILD_TESTING=OFF \
|
||||||
|
-DBUILD_LIBLOKINET=OFF \
|
||||||
|
-DWITH_TESTS=OFF \
|
||||||
|
-DNATIVE_BUILD=OFF \
|
||||||
|
-DSTATIC_LINK=ON \
|
||||||
|
-DWITH_SYSTEMD=OFF \
|
||||||
|
-DFORCE_OXENMQ_SUBMODULE=ON \
|
||||||
|
-DSUBMODULE_CHECK=OFF \
|
||||||
|
-DWITH_LTO=OFF \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=$(pwd) \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
$@ ..
|
||||||
|
ninja install && ninja sign
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Lokinet</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>MacOS/lokinet</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.lokinet.Daemon</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>lokinet</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XPC!</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.1</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0.1</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>Lokinet</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>lokinet-extension</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>org.lokinet.NetworkExtension</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>Lokinet</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>XPC!</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>0.1</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>0.1</string>
|
||||||
|
<key>NSExtension</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSExtensionPointIdentifier</key>
|
||||||
|
<string>com.apple.networkextension.packet-tunnel</string>
|
||||||
|
<key>NSExtensionPrincipalClass</key>
|
||||||
|
<string>Lokinet.LLARPPacketTunnel</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.developer.networking.networkextension</key>
|
||||||
|
<array>
|
||||||
|
<string>packet-tunnel-provider</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -x
|
||||||
|
set -e
|
||||||
|
for file in "${SIGN_TARGET}/Contents/Frameworks/lokinet-extension.framework" "${SIGN_TARGET}/Contents/MacOS/Lokinet" "${SIGN_TARGET}" ; do
|
||||||
|
codesign -vvvv --force -s "${CODESIGN_KEY}" --entitlements "${SIGN_ENTITLEMENTS}" --deep --timestamp --options=runtime "$file"
|
||||||
|
done
|
||||||
|
|
||||||
|
codesign --verify "${SIGN_TARGET}"
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
module LokinetExtension {
|
||||||
|
header "@CMAKE_SOURCE_DIR@/include/lokinet-extension.hpp"
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#include <NetworkExtension/NetworkExtension.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main (int argc, const char * argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
// AppDelegateExtension.swift
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import LokinetExtension
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
class LokinetMain: NSObject {
|
||||||
|
var vpnManager = NETunnelProviderManager()
|
||||||
|
|
||||||
|
let lokinetComponent = "org.lokinet.NetworkExtension"
|
||||||
|
var lokinetAdminTimer: DispatchSourceTimer?
|
||||||
|
|
||||||
|
func runMain() {
|
||||||
|
print("Starting up lokinet")
|
||||||
|
NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in
|
||||||
|
if let error = error {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let savedManagers = savedManagers {
|
||||||
|
for manager in savedManagers {
|
||||||
|
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.lokinetComponent {
|
||||||
|
print("Found saved VPN Manager")
|
||||||
|
self.vpnManager = manager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let providerProtocol = NETunnelProviderProtocol()
|
||||||
|
providerProtocol.serverAddress = "lokinet"
|
||||||
|
providerProtocol.providerBundleIdentifier = self.lokinetComponent
|
||||||
|
self.vpnManager.protocolConfiguration = providerProtocol
|
||||||
|
self.vpnManager.isEnabled = true
|
||||||
|
self.vpnManager.saveToPreferences(completionHandler: { error -> Void in
|
||||||
|
if error != nil {
|
||||||
|
print("Error saving to preferences")
|
||||||
|
} else {
|
||||||
|
print("saved...")
|
||||||
|
self.vpnManager.loadFromPreferences(completionHandler: { error in
|
||||||
|
if error != nil {
|
||||||
|
print("Error loading from preferences")
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
print("Trying to start")
|
||||||
|
self.initializeConnectionObserver()
|
||||||
|
try self.vpnManager.connection.startVPNTunnel()
|
||||||
|
} catch let error as NSError {
|
||||||
|
print(error)
|
||||||
|
} catch {
|
||||||
|
print("There was a fatal error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initializeConnectionObserver() {
|
||||||
|
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: vpnManager.connection, queue: OperationQueue.main) { _ -> Void in
|
||||||
|
|
||||||
|
if self.vpnManager.connection.status == .invalid {
|
||||||
|
print("VPN configuration is invalid")
|
||||||
|
} else if self.vpnManager.connection.status == .disconnected {
|
||||||
|
print("VPN is disconnected.")
|
||||||
|
} else if self.vpnManager.connection.status == .connecting {
|
||||||
|
print("VPN is connecting...")
|
||||||
|
} else if self.vpnManager.connection.status == .reasserting {
|
||||||
|
print("VPN is reasserting...")
|
||||||
|
} else if self.vpnManager.connection.status == .disconnecting {
|
||||||
|
print("VPN is disconnecting...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lokinet = LokinetMain()
|
||||||
|
lokinet.runMain()
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
module Lokinet {
|
||||||
|
header "lokinet-extension.hpp"
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// AppDelegateExtension.swift
|
||||||
|
// lifed from yggdrasil network ios port
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Lokinet
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
|
class LokinetMain: PlatformAppDelegate {
|
||||||
|
var vpnManager = NETunnelProviderManager()
|
||||||
|
var app = NSApplication.shared()
|
||||||
|
let lokinetComponent = "org.lokinet.NetworkExtension"
|
||||||
|
var lokinetAdminTimer: DispatchSourceTimer?
|
||||||
|
|
||||||
|
func runMain() {
|
||||||
|
print("Starting up lokinet")
|
||||||
|
NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in
|
||||||
|
if let error = error {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let savedManagers = savedManagers {
|
||||||
|
for manager in savedManagers {
|
||||||
|
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.lokinetComponent {
|
||||||
|
print("Found saved VPN Manager")
|
||||||
|
self.vpnManager = manager
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.vpnManager.loadFromPreferences(completionHandler: { (error: Error?) in
|
||||||
|
if let error = error {
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
self.vpnManager.localizedDescription = "Lokinet"
|
||||||
|
self.vpnManager.isEnabled = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
app.finishLaunching()
|
||||||
|
app.run()
|
||||||
|
print("end")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
#include <NetworkExtension/NetworkExtension.h>
|
||||||
|
|
||||||
|
struct ContextWrapper;
|
||||||
|
|
||||||
|
@interface LLARPPacketTunnel : NEPacketTunnelProvider
|
||||||
|
{
|
||||||
|
@private
|
||||||
|
struct ContextWrapper* m_Context;
|
||||||
|
}
|
||||||
|
- (void)startTunnelWithOptions:(NSDictionary<NSString*, NSObject*>*)options
|
||||||
|
completionHandler:(void (^)(NSError* error))completionHandler;
|
||||||
|
|
||||||
|
- (void)stopTunnelWithReason:(NEProviderStopReason)reason
|
||||||
|
completionHandler:(void (^)(void))completionHandler;
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,202 @@
|
|||||||
|
#include <lokinet-extension.hpp>
|
||||||
|
#include <llarp.hpp>
|
||||||
|
|
||||||
|
#include <llarp/config/config.hpp>
|
||||||
|
#include <llarp/ev/vpn.hpp>
|
||||||
|
#include <llarp/util/thread/queue.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace llarp::apple
|
||||||
|
{
|
||||||
|
struct FrameworkContext : public llarp::Context
|
||||||
|
{
|
||||||
|
|
||||||
|
explicit FrameworkContext(NEPacketTunnelProvider * tunnel);
|
||||||
|
|
||||||
|
~FrameworkContext() {}
|
||||||
|
|
||||||
|
std::shared_ptr<vpn::Platform>
|
||||||
|
makeVPNPlatform() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
Start();
|
||||||
|
|
||||||
|
private:
|
||||||
|
NEPacketTunnelProvider * m_Tunnel;
|
||||||
|
std::unique_ptr<std::thread> m_Runner;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VPNInterface final : public vpn::NetworkInterface
|
||||||
|
{
|
||||||
|
NEPacketTunnelProvider * m_Tunnel;
|
||||||
|
|
||||||
|
static inline constexpr auto PacketQueueSize = 1024;
|
||||||
|
|
||||||
|
thread::Queue<net::IPPacket> m_ReadQueue;
|
||||||
|
|
||||||
|
void
|
||||||
|
OfferReadPacket(NSData * data)
|
||||||
|
{
|
||||||
|
llarp::net::IPPacket pkt;
|
||||||
|
const llarp_buffer_t buf{static_cast<const uint8_t *>(data.bytes), data.length};
|
||||||
|
if(pkt.Load(buf))
|
||||||
|
m_ReadQueue.tryPushBack(std::move(pkt));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit VPNInterface(NEPacketTunnelProvider * tunnel)
|
||||||
|
: m_Tunnel{tunnel},
|
||||||
|
m_ReadQueue{PacketQueueSize}
|
||||||
|
{
|
||||||
|
auto handler =
|
||||||
|
[this](NSArray<NSData*> * packets, NSArray<NSNumber*> *)
|
||||||
|
{
|
||||||
|
NSUInteger num = [packets count];
|
||||||
|
for(NSUInteger idx = 0; idx < num ; ++idx)
|
||||||
|
{
|
||||||
|
NSData * pkt = [packets objectAtIndex:idx];
|
||||||
|
OfferReadPacket(pkt);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
[m_Tunnel.packetFlow readPacketsWithCompletionHandler:handler];
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
PollFD() const override
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
IfName() const override
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
net::IPPacket
|
||||||
|
ReadNextPacket() override
|
||||||
|
{
|
||||||
|
net::IPPacket pkt{};
|
||||||
|
if(not m_ReadQueue.empty())
|
||||||
|
pkt = m_ReadQueue.popFront();
|
||||||
|
return pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WritePacket(net::IPPacket pkt) override
|
||||||
|
{
|
||||||
|
const sa_family_t fam = pkt.IsV6() ? AF_INET6 : AF_INET;
|
||||||
|
const uint8_t * pktbuf = pkt.buf;
|
||||||
|
const size_t pktsz = pkt.sz;
|
||||||
|
NSData * datapkt = [NSData dataWithBytes:pktbuf length:pktsz];
|
||||||
|
NEPacket * npkt = [[NEPacket alloc] initWithData:datapkt protocolFamily:fam];
|
||||||
|
NSArray * pkts = @[npkt];
|
||||||
|
return [m_Tunnel.packetFlow writePacketObjects:pkts];
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class VPNPlatform final : public vpn::Platform
|
||||||
|
{
|
||||||
|
NEPacketTunnelProvider * m_Tunnel;
|
||||||
|
public:
|
||||||
|
explicit VPNPlatform(NEPacketTunnelProvider * tunnel)
|
||||||
|
: m_Tunnel{tunnel}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<vpn::NetworkInterface>
|
||||||
|
ObtainInterface(vpn::InterfaceInfo) override
|
||||||
|
{
|
||||||
|
return std::make_shared<VPNInterface>(m_Tunnel);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FrameworkContext::FrameworkContext(NEPacketTunnelProvider * tunnel) :
|
||||||
|
llarp::Context{},
|
||||||
|
m_Tunnel{tunnel}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FrameworkContext::Start()
|
||||||
|
{
|
||||||
|
std::promise<void> result;
|
||||||
|
|
||||||
|
m_Runner = std::make_unique<std::thread>(
|
||||||
|
[&result, this]()
|
||||||
|
{
|
||||||
|
const RuntimeOptions opts{};
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Setup(opts);
|
||||||
|
Configure(llarp::Config::NetworkExtensionConfig());
|
||||||
|
}
|
||||||
|
catch(std::exception & )
|
||||||
|
{
|
||||||
|
result.set_exception(std::current_exception());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result.set_value();
|
||||||
|
Run(opts);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto ftr = result.get_future();
|
||||||
|
ftr.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<vpn::Platform>
|
||||||
|
FrameworkContext::makeVPNPlatform()
|
||||||
|
{
|
||||||
|
return std::make_shared<VPNPlatform>(m_Tunnel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct ContextWrapper
|
||||||
|
{
|
||||||
|
std::shared_ptr<llarp::apple::FrameworkContext> m_Context;
|
||||||
|
public:
|
||||||
|
explicit ContextWrapper(NEPacketTunnelProvider * tunnel) :
|
||||||
|
m_Context{std::make_shared<llarp::apple::FrameworkContext>(tunnel)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void
|
||||||
|
Start()
|
||||||
|
{
|
||||||
|
m_Context->Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Stop()
|
||||||
|
{
|
||||||
|
m_Context->CloseAsync();
|
||||||
|
m_Context->Wait();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@implementation LLARPPacketTunnel
|
||||||
|
|
||||||
|
- (void)startTunnelWithOptions:(NSDictionary<NSString *,NSObject *> *)options completionHandler:(void (^)(NSError *error))completionHandler {
|
||||||
|
m_Context = new ContextWrapper{self};
|
||||||
|
m_Context->Start();
|
||||||
|
completionHandler(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stopTunnelWithReason:(NEProviderStopReason)reason
|
||||||
|
completionHandler:(void (^)(void))completionHandler {
|
||||||
|
if(m_Context)
|
||||||
|
{
|
||||||
|
m_Context->Stop();
|
||||||
|
delete m_Context;
|
||||||
|
m_Context = nullptr;
|
||||||
|
}
|
||||||
|
completionHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
@ -1,173 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <llarp/ev/vpn.hpp>
|
|
||||||
#include "common.hpp"
|
|
||||||
|
|
||||||
#include <sys/kern_control.h>
|
|
||||||
#include <sys/sys_domain.h>
|
|
||||||
#include <sys/kern_event.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/uio.h>
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <net/if_var.h>
|
|
||||||
#include <net/if_types.h>
|
|
||||||
#include <net/route.h>
|
|
||||||
#include <netinet/if_ether.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
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<size_t>(n) == pkt.sz;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ApplePlatform : public Platform
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::shared_ptr<NetworkInterface>
|
|
||||||
ObtainInterface(InterfaceInfo info) override
|
|
||||||
{
|
|
||||||
return std::make_shared<AppleInterface>(std::move(info));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace llarp::vpn
|
|
Loading…
Reference in New Issue