mirror of https://github.com/oxen-io/lokinet
Merge pull request #1319 from majestrate/clean-up-dev-2020-07-27
exit tool rewrite in C++ (without macos support yet)pull/1332/head
commit
521765b5a8
@ -0,0 +1,229 @@
|
||||
#include <lokimq/lokimq.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <cxxopts.hpp>
|
||||
#include <future>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <net/net.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
// add the unholy windows headers for iphlpapi
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <strsafe.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
/// do a lokimq request on an lmq instance blocking style
|
||||
/// returns a json object parsed from the result
|
||||
std::optional<nlohmann::json>
|
||||
LMQ_Request(
|
||||
lokimq::LokiMQ& lmq,
|
||||
const lokimq::ConnectionID& id,
|
||||
std::string_view method,
|
||||
std::optional<nlohmann::json> args = std::nullopt)
|
||||
{
|
||||
std::promise<std::optional<std::string>> result_promise;
|
||||
|
||||
auto handleRequest = [&result_promise](bool success, std::vector<std::string> result) {
|
||||
if ((not success) or result.empty())
|
||||
{
|
||||
result_promise.set_value(std::nullopt);
|
||||
return;
|
||||
}
|
||||
result_promise.set_value(result[0]);
|
||||
};
|
||||
if (args.has_value())
|
||||
{
|
||||
lmq.request(id, method, handleRequest, args->dump());
|
||||
}
|
||||
else
|
||||
{
|
||||
lmq.request(id, method, handleRequest);
|
||||
}
|
||||
auto ftr = result_promise.get_future();
|
||||
const auto str = ftr.get();
|
||||
if (str.has_value())
|
||||
return nlohmann::json::parse(*str);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
cxxopts::Options opts("lokinet-vpn", "LokiNET vpn control utility");
|
||||
|
||||
opts.add_options()("v,verbose", "Verbose", cxxopts::value<bool>())(
|
||||
"h,help", "help", cxxopts::value<bool>())("up", "put vpn up", cxxopts::value<bool>())(
|
||||
"down", "put vpn down", cxxopts::value<bool>())(
|
||||
"exit", "specify exit node address", cxxopts::value<std::string>())(
|
||||
"rpc", "rpc url for lokinet", cxxopts::value<std::string>())(
|
||||
"endpoint", "endpoint to use", cxxopts::value<std::string>())(
|
||||
"token", "exit auth token to use", cxxopts::value<std::string>());
|
||||
|
||||
lokimq::address rpcURL("tcp://127.0.0.1:1190");
|
||||
std::string exitAddress;
|
||||
std::string endpoint = "default";
|
||||
std::optional<std::string> token;
|
||||
lokimq::LogLevel logLevel = lokimq::LogLevel::warn;
|
||||
bool goUp = false;
|
||||
bool goDown = false;
|
||||
try
|
||||
{
|
||||
const auto result = opts.parse(argc, argv);
|
||||
|
||||
if (result.count("help") > 0)
|
||||
{
|
||||
std::cout << opts.help() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (result.count("verbose") > 0)
|
||||
{
|
||||
logLevel = lokimq::LogLevel::debug;
|
||||
}
|
||||
if (result.count("rpc") > 0)
|
||||
{
|
||||
rpcURL = lokimq::address(result["rpc"].as<std::string>());
|
||||
}
|
||||
if (result.count("exit") > 0)
|
||||
{
|
||||
exitAddress = result["exit"].as<std::string>();
|
||||
}
|
||||
goUp = result.count("up") > 0;
|
||||
goDown = result.count("down") > 0;
|
||||
|
||||
if (result.count("endpoint") > 0)
|
||||
{
|
||||
endpoint = result["endpoint"].as<std::string>();
|
||||
}
|
||||
if (result.count("token") > 0)
|
||||
{
|
||||
token = result["token"].as<std::string>();
|
||||
}
|
||||
}
|
||||
catch (const cxxopts::option_not_exists_exception& ex)
|
||||
{
|
||||
std::cerr << ex.what();
|
||||
std::cout << opts.help() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if ((not goUp) and (not goDown))
|
||||
{
|
||||
std::cout << opts.help() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (goUp and exitAddress.empty())
|
||||
{
|
||||
std::cout << "no exit address provided" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
lokimq::LokiMQ lmq{[](lokimq::LogLevel lvl, const char* file, int line, std::string msg) {
|
||||
std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl;
|
||||
},
|
||||
logLevel};
|
||||
|
||||
lmq.start();
|
||||
|
||||
std::promise<bool> connectPromise;
|
||||
|
||||
const auto connID = lmq.connect_remote(
|
||||
rpcURL,
|
||||
[&connectPromise](auto) { connectPromise.set_value(true); },
|
||||
[&connectPromise](auto, std::string_view msg) {
|
||||
std::cout << "failed to connect to lokinet RPC: " << msg << std::endl;
|
||||
connectPromise.set_value(false);
|
||||
});
|
||||
|
||||
auto ftr = connectPromise.get_future();
|
||||
if (not ftr.get())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<std::string> firstHops;
|
||||
std::string ifname;
|
||||
|
||||
const auto maybe_status = LMQ_Request(lmq, connID, "llarp.status");
|
||||
if (not maybe_status.has_value())
|
||||
{
|
||||
std::cout << "call to llarp.status failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// extract first hops
|
||||
const auto& links = maybe_status->at("result")["links"]["outbound"];
|
||||
for (const auto& link : links)
|
||||
{
|
||||
const auto& sessions = link["sessions"]["established"];
|
||||
for (const auto& session : sessions)
|
||||
{
|
||||
std::string addr = session["remoteAddr"];
|
||||
const auto pos = addr.find(":");
|
||||
firstHops.push_back(addr.substr(0, pos));
|
||||
}
|
||||
}
|
||||
// get interface name
|
||||
#ifdef _WIN32
|
||||
// strip off the "::ffff."
|
||||
ifname = maybe_status->at("result")["services"][endpoint]["ifaddr"];
|
||||
const auto pos = ifname.find("/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
ifname = ifname.substr(0, pos);
|
||||
}
|
||||
#else
|
||||
ifname = maybe_status->at("result")["services"][endpoint]["ifname"];
|
||||
#endif
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << "failed to parse result: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
if (goUp)
|
||||
{
|
||||
std::optional<nlohmann::json> maybe_result;
|
||||
if (token.has_value())
|
||||
{
|
||||
maybe_result = LMQ_Request(
|
||||
lmq,
|
||||
connID,
|
||||
"llarp.exit",
|
||||
nlohmann::json{{"exit", exitAddress}, {"range", "0.0.0.0/0"}, {"token", *token}});
|
||||
}
|
||||
else
|
||||
{
|
||||
maybe_result = LMQ_Request(
|
||||
lmq, connID, "llarp.exit", nlohmann::json{{"exit", exitAddress}, {"range", "0.0.0.0/0"}});
|
||||
}
|
||||
|
||||
if (not maybe_result.has_value())
|
||||
{
|
||||
std::cout << "could not add exit" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (maybe_result->contains("error") and maybe_result->at("error").is_string())
|
||||
{
|
||||
std::cout << maybe_result->at("error").get<std::string>() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (goDown)
|
||||
{
|
||||
LMQ_Request(lmq, connID, "llarp.exit", nlohmann::json{{"range", "0.0.0.0/0"}, {"unmap", true}});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
|
||||
to configure lokinet to be an exit add into `lokinet.ini` `[network]` section the following:
|
||||
|
||||
exit=true
|
||||
keyfile=/var/lib/lokinet/exit.private
|
||||
reachable=1
|
||||
ifaddr=10.0.0.1/16
|
||||
|
||||
post setup for exit (as root) given `eth0` is used to get to the internet:
|
||||
|
||||
# echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
# iptables -t nat -A POSTROUTING -s 10.0.0.0/16 -o eth0 -j MASQUERADE
|
@ -1 +1 @@
|
||||
Subproject commit e7b3b40b5a95bc74b9a7f662830a27c49ffc01b4
|
||||
Subproject commit db78ac1d7716f56fc9f1b030b715f872f93964e4
|
@ -0,0 +1,443 @@
|
||||
#include "route.hpp"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/net.hpp>
|
||||
#include <exception>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <net/net.hpp>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <stdio.h>
|
||||
#include <strsafe.h>
|
||||
#endif
|
||||
|
||||
#include <sstream>
|
||||
#include <util/logging/logger.hpp>
|
||||
|
||||
namespace llarp::net
|
||||
{
|
||||
#ifndef __linux__
|
||||
void
|
||||
Execute(std::string cmd)
|
||||
{
|
||||
std::cout << cmd << std::endl;
|
||||
#ifdef _WIN32
|
||||
system(cmd.c_str());
|
||||
#else
|
||||
std::vector<std::string> parts_str;
|
||||
std::vector<const char*> parts_raw;
|
||||
std::stringstream in(cmd);
|
||||
for (std::string part; std::getline(in, part, ' ');)
|
||||
{
|
||||
if (part.empty())
|
||||
continue;
|
||||
parts_str.push_back(part);
|
||||
}
|
||||
for (const auto& part : parts_str)
|
||||
{
|
||||
parts_raw.push_back(part.c_str());
|
||||
}
|
||||
parts_raw.push_back(nullptr);
|
||||
const auto pid = fork();
|
||||
if (pid == -1)
|
||||
{
|
||||
throw std::runtime_error("failed to fork");
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
char* const* args = (char* const*)parts_raw.data();
|
||||
const auto result = execv(parts_raw[0], args);
|
||||
if (result)
|
||||
{
|
||||
std::cout << "failed: " << result << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "ok" << std::endl;
|
||||
}
|
||||
exit(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
waitpid(pid, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
struct NLSocket
|
||||
{
|
||||
NLSocket() : fd(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE))
|
||||
{
|
||||
if (fd == -1)
|
||||
throw std::runtime_error("failed to make netlink socket");
|
||||
}
|
||||
|
||||
~NLSocket()
|
||||
{
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
const int fd;
|
||||
};
|
||||
|
||||
/* Helper structure for ip address data and attributes */
|
||||
typedef struct
|
||||
{
|
||||
unsigned char family;
|
||||
unsigned char bitlen;
|
||||
unsigned char data[sizeof(struct in6_addr)];
|
||||
} _inet_addr;
|
||||
|
||||
/* */
|
||||
|
||||
#define NLMSG_TAIL(nmsg) ((struct rtattr*)(((intptr_t)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
|
||||
|
||||
/* Add new data to rtattr */
|
||||
int
|
||||
rtattr_add(struct nlmsghdr* n, unsigned int maxlen, int type, const void* data, int alen)
|
||||
{
|
||||
int len = RTA_LENGTH(alen);
|
||||
struct rtattr* rta;
|
||||
|
||||
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen)
|
||||
{
|
||||
fprintf(stderr, "rtattr_add error: message exceeded bound of %d\n", maxlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rta = NLMSG_TAIL(n);
|
||||
rta->rta_type = type;
|
||||
rta->rta_len = len;
|
||||
|
||||
if (alen)
|
||||
{
|
||||
memcpy(RTA_DATA(rta), data, alen);
|
||||
}
|
||||
|
||||
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
do_route(int sock, int cmd, int flags, _inet_addr* dst, _inet_addr* gw, int def_gw, int if_idx)
|
||||
{
|
||||
struct
|
||||
{
|
||||
struct nlmsghdr n;
|
||||
struct rtmsg r;
|
||||
char buf[4096];
|
||||
} nl_request{};
|
||||
|
||||
/* Initialize request structure */
|
||||
nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
|
||||
nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags;
|
||||
nl_request.n.nlmsg_type = cmd;
|
||||
nl_request.n.nlmsg_pid = getpid();
|
||||
nl_request.r.rtm_family = dst->family;
|
||||
nl_request.r.rtm_table = RT_TABLE_MAIN;
|
||||
if (if_idx)
|
||||
{
|
||||
nl_request.r.rtm_scope = RT_SCOPE_LINK;
|
||||
}
|
||||
else
|
||||
{
|
||||
nl_request.r.rtm_scope = RT_SCOPE_NOWHERE;
|
||||
}
|
||||
/* Set additional flags if NOT deleting route */
|
||||
if (cmd != RTM_DELROUTE)
|
||||
{
|
||||
nl_request.r.rtm_protocol = RTPROT_BOOT;
|
||||
nl_request.r.rtm_type = RTN_UNICAST;
|
||||
}
|
||||
|
||||
nl_request.r.rtm_family = dst->family;
|
||||
nl_request.r.rtm_dst_len = dst->bitlen;
|
||||
|
||||
/* Select scope, for simplicity we supports here only IPv6 and IPv4 */
|
||||
if (nl_request.r.rtm_family == AF_INET6)
|
||||
{
|
||||
nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
nl_request.r.rtm_scope = RT_SCOPE_LINK;
|
||||
}
|
||||
|
||||
/* Set gateway */
|
||||
if (gw->bitlen != 0)
|
||||
{
|
||||
rtattr_add(&nl_request.n, sizeof(nl_request), RTA_GATEWAY, &gw->data, gw->bitlen / 8);
|
||||
nl_request.r.rtm_scope = 0;
|
||||
nl_request.r.rtm_family = gw->family;
|
||||
}
|
||||
|
||||
/* Don't set destination and interface in case of default gateways */
|
||||
if (!def_gw)
|
||||
{
|
||||
/* Set destination network */
|
||||
rtattr_add(
|
||||
&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &dst->data, dst->bitlen / 8);
|
||||
|
||||
/* Set interface */
|
||||
rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof(int));
|
||||
}
|
||||
|
||||
/* Send message to the netlink */
|
||||
return send(sock, &nl_request, sizeof(nl_request), 0);
|
||||
}
|
||||
|
||||
int
|
||||
read_addr(const char* addr, _inet_addr* res)
|
||||
{
|
||||
if (strchr(addr, ':'))
|
||||
{
|
||||
res->family = AF_INET6;
|
||||
res->bitlen = 128;
|
||||
}
|
||||
else
|
||||
{
|
||||
res->family = AF_INET;
|
||||
res->bitlen = 32;
|
||||
}
|
||||
return inet_pton(res->family, addr, res->data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void
|
||||
AddRoute(std::string ip, std::string gateway)
|
||||
{
|
||||
#ifdef __linux__
|
||||
NLSocket sock;
|
||||
int default_gw = 0;
|
||||
int if_idx = 0;
|
||||
_inet_addr to_addr{};
|
||||
_inet_addr gw_addr{};
|
||||
int nl_cmd = RTM_NEWROUTE;
|
||||
int nl_flags = NLM_F_CREATE | NLM_F_EXCL;
|
||||
read_addr(gateway.c_str(), &gw_addr);
|
||||
read_addr(ip.c_str(), &to_addr);
|
||||
LogInfo("add route: ", ip, " via ", gateway);
|
||||
do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, default_gw, if_idx);
|
||||
#else
|
||||
std::stringstream ss;
|
||||
#if _WIN32
|
||||
ss << "route ADD " << ip << " MASK 255.255.255.255 " << gateway << " METRIC 2";
|
||||
#elif __APPLE__
|
||||
ss << "route -n add -host " << ip << " " << gateway;
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
Execute(ss.str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DelRoute(std::string ip, std::string gateway)
|
||||
{
|
||||
#ifdef __linux__
|
||||
NLSocket sock;
|
||||
int default_gw = 0;
|
||||
int if_idx = 0;
|
||||
_inet_addr to_addr{};
|
||||
_inet_addr gw_addr{};
|
||||
int nl_cmd = RTM_DELROUTE;
|
||||
int nl_flags = 0;
|
||||
read_addr(gateway.c_str(), &gw_addr);
|
||||
read_addr(ip.c_str(), &to_addr);
|
||||
do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, default_gw, if_idx);
|
||||
#else
|
||||
std::stringstream ss;
|
||||
#if _WIN32
|
||||
ss << "route DELETE " << ip << " MASK 255.255.255.255 " << gateway << " METRIC 2";
|
||||
#elif __APPLE__
|
||||
ss << "route -n delete -host " << ip << " " << gateway;
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
Execute(ss.str());
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
AddDefaultRouteViaInterface(std::string ifname)
|
||||
{
|
||||
#ifdef __linux__
|
||||
NLSocket sock;
|
||||
int default_gw = 1;
|
||||
int if_idx = if_nametoindex(ifname.c_str());
|
||||
_inet_addr to_addr{};
|
||||
_inet_addr gw_addr{};
|
||||
|
||||
const auto maybe = GetIFAddr(ifname);
|
||||
if (not maybe.has_value())
|
||||
throw std::runtime_error("we dont have our own net interface?");
|
||||
int nl_cmd = RTM_NEWROUTE;
|
||||
int nl_flags = NLM_F_CREATE | NLM_F_EXCL;
|
||||
read_addr(maybe->toHost().c_str(), &gw_addr);
|
||||
LogInfo("default route via ", ifname, " (", if_idx, ")");
|
||||
do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, default_gw, if_idx);
|
||||
#elif _WIN32
|
||||
ifname.back()++;
|
||||
Execute("route ADD 0.0.0.0 MASK 128.0.0.0 " + ifname);
|
||||
Execute("route ADD 128.0.0.0 MASK 128.0.0.0 " + ifname);
|
||||
#elif __APPLE__
|
||||
Execute("route -cloning add -net 0.0.0.0 -netmask 0.0.0.0 -interface " + ifname);
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
DelDefaultRouteViaInterface(std::string ifname)
|
||||
{
|
||||
#ifdef __linux__
|
||||
|
||||
NLSocket sock;
|
||||
int default_gw = 1;
|
||||
int if_idx = if_nametoindex(ifname.c_str());
|
||||
_inet_addr to_addr{};
|
||||
_inet_addr gw_addr{};
|
||||
const auto maybe = GetIFAddr(ifname);
|
||||
|
||||
if (not maybe.has_value())
|
||||
throw std::runtime_error("we dont have our own net interface?");
|
||||
int nl_cmd = RTM_DELROUTE;
|
||||
int nl_flags = 0;
|
||||
read_addr(maybe->toHost().c_str(), &gw_addr);
|
||||
do_route(sock.fd, nl_cmd, nl_flags, &to_addr, &gw_addr, default_gw, if_idx);
|
||||
#elif _WIN32
|
||||
ifname.back()++;
|
||||
Execute("route DELETE 0.0.0.0 MASK 128.0.0.0 " + ifname);
|
||||
Execute("route DELETE 128.0.0.0 MASK 128.0.0.0 " + ifname);
|
||||
#elif __APPLE__
|
||||
Execute("route -cloning delete -net 0.0.0.0 -netmask 0.0.0.0 -interface " + ifname);
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
GetGatewaysNotOnInterface(std::string ifname)
|
||||
{
|
||||
std::vector<std::string> gateways;
|
||||
#ifdef __linux__
|
||||
|
||||
FILE* p = popen("ip route", "r");
|
||||
if (p == nullptr)
|
||||
return gateways;
|
||||
char* line = nullptr;
|
||||
size_t len = 0;
|
||||
ssize_t read = 0;
|
||||
while ((read = getline(&line, &len, p)) != -1)
|
||||
{
|
||||
std::string line_str(line, len);
|
||||
std::vector<std::string> words;
|
||||
std::istringstream instr(line_str);
|
||||
for (std::string word; std::getline(instr, word, ' ');)
|
||||
{
|
||||
words.emplace_back(std::move(word));
|
||||
}
|
||||
if (words[0] == "default" and words[1] == "via" and words[3] == "dev" and words[4] != ifname)
|
||||
{
|
||||
gateways.emplace_back(std::move(words[2]));
|
||||
}
|
||||
}
|
||||
pclose(p);
|
||||
return gateways;
|
||||
#elif _WIN32
|
||||
#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 gateways;
|
||||
|
||||
if (GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
FREE(pIpForwardTable);
|
||||
pIpForwardTable = (MIB_IPFORWARDTABLE*)MALLOC(dwSize);
|
||||
if (pIpForwardTable == nullptr)
|
||||
{
|
||||
return gateways;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, 0)) == NO_ERROR)
|
||||
{
|
||||
for (int i = 0; i < (int)pIpForwardTable->dwNumEntries; i++)
|
||||
{
|
||||
struct in_addr gateway, interface_addr;
|
||||
gateway.S_un.S_addr = (u_long)pIpForwardTable->table[i].dwForwardDest;
|
||||
interface_addr.S_un.S_addr = (u_long)pIpForwardTable->table[i].dwForwardNextHop;
|
||||
std::array<char, 128> interface_str{};
|
||||
StringCchCopy(interface_str.data(), interface_str.size(), inet_ntoa(interface_addr));
|
||||
std::string interface_name{interface_str.data()};
|
||||
if ((!gateway.S_un.S_addr) and interface_name != ifname)
|
||||
{
|
||||
gateways.push_back(std::move(interface_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
FREE(pIpForwardTable);
|
||||
#undef MALLOC
|
||||
#undef FREE
|
||||
return gateways;
|
||||
#elif __APPLE__
|
||||
const auto maybe = GetIFAddr(ifname);
|
||||
if (not maybe.has_value())
|
||||
return gateways;
|
||||
const auto interface = maybe->toString();
|
||||
// mac os is so godawful man
|
||||
FILE* p = popen("netstat -rn -f inet", "r");
|
||||
if (p == nullptr)
|
||||
return gateways;
|
||||
char* line = nullptr;
|
||||
size_t len = 0;
|
||||
ssize_t read = 0;
|
||||
while ((read = getline(&line, &len, p)) != -1)
|
||||
{
|
||||
std::string line_str(line, len);
|
||||
if (line_str.find("default") == 0)
|
||||
{
|
||||
line_str = line_str.substr(7);
|
||||
while (line_str[0] == ' ')
|
||||
{
|
||||
line_str = line_str.substr(1);
|
||||
}
|
||||
const auto pos = line_str.find(" ");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto gateway = line_str.substr(0, pos);
|
||||
if (gateway != interface)
|
||||
gateways.emplace_back(std::move(gateway));
|
||||
}
|
||||
}
|
||||
}
|
||||
pclose(p);
|
||||
return gateways;
|
||||
#else
|
||||
#error unsupported platform
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace llarp::net
|
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::net
|
||||
{
|
||||
/// get every ip address that is a gateway that isn't owned by interface with name ifname
|
||||
std::vector<std::string>
|
||||
GetGatewaysNotOnInterface(std::string ifname);
|
||||
|
||||
/// add route to ipaddr via gateway ip
|
||||
void
|
||||
AddRoute(std::string ipaddr, std::string gateway);
|
||||
|
||||
/// delete route to ipaddr via gateway ip
|
||||
void
|
||||
DelRoute(std::string ipaddr, std::string gateway);
|
||||
|
||||
/// add default route via interface with name ifname
|
||||
void
|
||||
AddDefaultRouteViaInterface(std::string ifname);
|
||||
|
||||
/// delete default route via interface with name ifname
|
||||
void
|
||||
DelDefaultRouteViaInterface(std::string ifname);
|
||||
|
||||
} // namespace llarp::net
|
Loading…
Reference in New Issue