mirror of https://github.com/oxen-io/lokinet
* rework exit codepath to allow multiple exits
* rework net code for ip ranges to be cleaner * clean up endpoint auth code * refactor config to validate network configs before setting up endpoints * remove buildone from path/pathbuilder.cpp so we don't spam connection attempts
This commit is contained in:
parent
2ef2e6171a
commit
0f21eeccb0
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
|
||||
from socket import AF_INET
|
||||
|
||||
|
@ -15,21 +17,75 @@ class LokinetRPC:
|
|||
self._rpc_socket.setsockopt(zmq.CONNECT_TIMEOUT, 5000)
|
||||
self._rpc_socket.setsockopt(zmq.HANDSHAKE_IVL, 5000)
|
||||
self._rpc_socket.connect(url)
|
||||
self._url = url
|
||||
|
||||
def rpc(self, method):
|
||||
self._rpc_socket.send_multipart([method.encode(), b'lokinet_vpn'+method.encode()])
|
||||
def rpc(self, method, data=None):
|
||||
tag = os.urandom(16)
|
||||
req = [method.encode(), tag]
|
||||
if data is not None:
|
||||
req.append(json.dumps(data).encode('utf-8'))
|
||||
self._rpc_socket.send_multipart(req)
|
||||
if not self._rpc_socket.poll(timeout=50):
|
||||
return
|
||||
reply = self._rpc_socket.recv_multipart()
|
||||
if len(reply) >= 3 and reply[0:2] == [b'REPLY', b'lokinet_vpn'+method.encode()]:
|
||||
return reply[2].decode()
|
||||
|
||||
if len(reply) >= 3 and reply[0:2] == [b'REPLY', tag]:
|
||||
try:
|
||||
return json.loads(reply[2].decode())
|
||||
except:
|
||||
return None
|
||||
|
||||
def map_exit(self, addr, maprange, token=None):
|
||||
"""
|
||||
map an exit range to a loki address and use token for auth if desires
|
||||
"""
|
||||
if token is None:
|
||||
data = self.rpc("llarp.exit", {'exit': addr, 'range': maprange})
|
||||
else:
|
||||
data = self.rpc("llarp.exit", {'exit': addr, 'token': token, 'range': maprange})
|
||||
if data is None:
|
||||
print("no response from rpc")
|
||||
return False
|
||||
if data['error'] is not None:
|
||||
print('failed to map exit range: {}'.format(data['error']))
|
||||
return False
|
||||
print('mapped {} to {}: {}'.format(maprange, addr, data['result']))
|
||||
return True
|
||||
|
||||
def unmap_exit(self, maprange):
|
||||
"""
|
||||
unmap an ip range from exit
|
||||
"""
|
||||
data = self.rpc("llarp.exit", {'range': maprange, 'unmap': True})
|
||||
if data is None:
|
||||
print("no response from rpc")
|
||||
return False
|
||||
if data['error'] is not None:
|
||||
print('failed to unmap exit range: {}'.format(data['error']))
|
||||
return False
|
||||
print('unmapped {}: {}'.format(maprange, data['result']))
|
||||
return True
|
||||
|
||||
def get_endpoint_ifname(self, endpoint):
|
||||
"""
|
||||
get the network interface name belongint to the endpoint called endpoint
|
||||
"""
|
||||
data = self.rpc("llarp.status")
|
||||
if data is None or data['error'] is not None:
|
||||
return
|
||||
services = data['result']['services']
|
||||
if endpoint not in services:
|
||||
return
|
||||
return services[endpoint]['ifname']
|
||||
|
||||
def get_first_hops(self):
|
||||
data = self.rpc("llarp.status")
|
||||
for link in data['result']['links']['outbound']:
|
||||
for session in link["sessions"]['established']:
|
||||
yield session['remoteAddr']
|
||||
if data is None:
|
||||
print("no response from rpc")
|
||||
elif data['error'] is not None:
|
||||
print('error detecting first hops: {}'.format(data['error']))
|
||||
else:
|
||||
for link in data['result']['links']['outbound']:
|
||||
for session in link["sessions"]['established']:
|
||||
yield session['remoteAddr']
|
||||
|
||||
def close(self):
|
||||
self._rpc_socket.close(linger=0)
|
||||
|
@ -37,27 +93,49 @@ class LokinetRPC:
|
|||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--rpc", type=str, default='tcp://127.0.0.1:1190')
|
||||
ap.add_argument("--ifname", type=str, default="lokitun0")
|
||||
ap.add_argument("--rpc", type=str, default='tcp://127.0.0.1:1190', help='rpc endpoint url for local lokinet')
|
||||
ap.add_argument("--exit", type=str, required=True, help='loki address of exit to use')
|
||||
ap.add_argument('--token', type=str, default=None, help='optional auth token to use when talking to exit')
|
||||
ap.add_argument('--range', type=str, default='0.0.0.0/0', help='optional ip range to map to exit')
|
||||
ap.add_argument("--endpoint", type=str, default='default', help='lokinet endpoint name to use')
|
||||
ap.add_argument("--up", action='store_const', dest='action', const='up')
|
||||
ap.add_argument("--down", action='store_const', dest='action', const='down')
|
||||
args = ap.parse_args()
|
||||
rpc = LokinetRPC(args.rpc)
|
||||
|
||||
hops = dict()
|
||||
for hop in rpc.get_first_hops():
|
||||
ip = hop.split(':')[0]
|
||||
hops[ip] = 0
|
||||
rpc.close()
|
||||
if len(hops) == 0:
|
||||
print("lokinet is not connected yet")
|
||||
rpc.close()
|
||||
return 1
|
||||
ifname = rpc.get_endpoint_ifname(args.endpoint)
|
||||
if ifname is None:
|
||||
print("cannot determine lokinet network interface name")
|
||||
rpc.close()
|
||||
return 1
|
||||
with IPRoute() as ip:
|
||||
ip.bind()
|
||||
try:
|
||||
idx = ip.link_lookup(ifname=args.ifname)[0]
|
||||
except:
|
||||
print("cannot find {}".format(args.ifname))
|
||||
idx = ip.link_lookup(ifname=ifname)[0]
|
||||
except Exception as ex:
|
||||
print("cannot find {}: {}".format(ifname, ex))
|
||||
rpc.close()
|
||||
return 1
|
||||
# set up exit after binding routing socket
|
||||
if args.action == 'up':
|
||||
if not rpc.map_exit(args.exit, args.range, args.token):
|
||||
rpc.close()
|
||||
print('cannot map exit')
|
||||
return
|
||||
elif args.action == 'down':
|
||||
if not rpc.unmap_exit(args.range):
|
||||
rpc.close()
|
||||
print('cannot unmap exit')
|
||||
return
|
||||
rpc.close()
|
||||
gateways = ip.get_default_routes(family=AF_INET)
|
||||
gateway = None
|
||||
for g in gateways:
|
||||
|
|
|
@ -203,11 +203,25 @@ namespace llarp
|
|||
if (arg.empty())
|
||||
return;
|
||||
service::Address exit;
|
||||
IPRange range;
|
||||
const auto pos = arg.find(":");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
range.FromString("0.0.0.0/0");
|
||||
}
|
||||
else if (not range.FromString(arg.substr(pos + 1)))
|
||||
{
|
||||
throw std::invalid_argument("[network]:exit-node invalid ip range for exit provided");
|
||||
}
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
arg = arg.substr(0, pos);
|
||||
}
|
||||
if (not exit.FromString(arg))
|
||||
{
|
||||
throw std::invalid_argument(stringify("[endpoint]:exit-node bad address: ", arg));
|
||||
throw std::invalid_argument(stringify("[network]:exit-node bad address: ", arg));
|
||||
}
|
||||
m_exitNode = exit;
|
||||
m_ExitMap.Insert(range, exit);
|
||||
});
|
||||
|
||||
conf.defineOption<std::string>("network", "mapaddr", false, true, "", [this](std::string arg) {
|
||||
|
@ -248,9 +262,13 @@ namespace llarp
|
|||
const auto maybe = llarp::FindFreeRange();
|
||||
if (not maybe)
|
||||
throw std::invalid_argument("cannot determine free ip range");
|
||||
arg = *maybe;
|
||||
m_ifaddr = *maybe;
|
||||
return;
|
||||
}
|
||||
if (not m_ifaddr.FromString(arg))
|
||||
{
|
||||
throw std::invalid_argument(stringify("[network]:ifaddr invalid value: ", arg));
|
||||
}
|
||||
m_ifaddr = arg;
|
||||
});
|
||||
|
||||
conf.defineOption<std::string>("network", "ifname", false, "", [this](std::string arg) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <constants/files.hpp>
|
||||
#include <net/ip_address.hpp>
|
||||
#include <net/net_int.hpp>
|
||||
#include <net/ip_range_map.hpp>
|
||||
#include <service/address.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
|
@ -70,7 +71,7 @@ namespace llarp
|
|||
std::string m_routerProfilesFile;
|
||||
std::string m_strictConnect;
|
||||
std::string m_ifname;
|
||||
std::string m_ifaddr;
|
||||
IPRange m_ifaddr;
|
||||
|
||||
std::optional<fs::path> m_keyfile;
|
||||
std::string m_endpointType;
|
||||
|
@ -79,7 +80,7 @@ namespace llarp
|
|||
std::optional<int> m_Paths;
|
||||
bool m_AllowExit = false;
|
||||
std::set<RouterID> m_snodeBlacklist;
|
||||
std::optional<service::Address> m_exitNode;
|
||||
net::IPRangeMap<service::Address> m_ExitMap;
|
||||
std::unordered_map<huint128_t, service::Address> m_mapAddrs;
|
||||
|
||||
bool m_AuthEnabled = false;
|
||||
|
|
|
@ -550,23 +550,11 @@ namespace llarp
|
|||
m_LocalResolverAddr = dnsConfig.m_bind;
|
||||
m_UpstreamResolvers = dnsConfig.m_upstreamDNS;
|
||||
|
||||
if (!m_OurRange.FromString(networkConfig.m_ifaddr))
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
stringify(Name(), " has invalid address range: ", networkConfig.m_ifaddr));
|
||||
}
|
||||
// TODO: clean this up (make a util function for handling CIDR, etc.)
|
||||
auto pos = networkConfig.m_ifaddr.find("/");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
throw std::invalid_argument(
|
||||
stringify(Name(), " ifaddr is not a cidr: ", networkConfig.m_ifaddr));
|
||||
}
|
||||
std::string nmask_str = networkConfig.m_ifaddr.substr(1 + pos);
|
||||
std::string host_str = networkConfig.m_ifaddr.substr(0, pos);
|
||||
m_OurRange = networkConfig.m_ifaddr;
|
||||
const auto host_str = m_OurRange.BaseAddressString();
|
||||
// string, or just a plain char array?
|
||||
strncpy(m_Tun.ifaddr, host_str.c_str(), sizeof(m_Tun.ifaddr) - 1);
|
||||
m_Tun.netmask = std::atoi(nmask_str.c_str());
|
||||
m_Tun.netmask = m_OurRange.HostmaskBits();
|
||||
m_IfAddr = m_OurRange.addr;
|
||||
m_NextAddr = m_IfAddr;
|
||||
m_HigestAddr = m_OurRange.HighestAddr();
|
||||
|
@ -580,7 +568,7 @@ namespace llarp
|
|||
m_IfAddr,
|
||||
" hi=",
|
||||
m_HigestAddr);
|
||||
m_UseV6 = false;
|
||||
m_UseV6 = not m_OurRange.IsV4();
|
||||
|
||||
if (networkConfig.m_ifname.length() >= sizeof(m_Tun.ifname))
|
||||
{
|
||||
|
|
|
@ -28,7 +28,6 @@ namespace llarp
|
|||
void
|
||||
TunEndpoint::FlushToUser(std::function<bool(net::IPPacket&)> send)
|
||||
{
|
||||
m_ExitMap.ForEachValue([r = Router()](const auto& exit) { exit->DownstreamFlush(r); });
|
||||
// flush network to user
|
||||
m_NetworkToUserPktQueue.Process(send);
|
||||
}
|
||||
|
@ -74,7 +73,10 @@ namespace llarp
|
|||
{
|
||||
auto obj = service::Endpoint::ExtractStatus();
|
||||
obj["ifaddr"] = m_OurRange.ToString();
|
||||
|
||||
if (tunif)
|
||||
{
|
||||
obj["ifname"] = tunif->ifname;
|
||||
}
|
||||
std::vector<std::string> resolvers;
|
||||
for (const auto& addr : m_UpstreamResolvers)
|
||||
resolvers.emplace_back(addr.toString());
|
||||
|
@ -196,37 +198,11 @@ namespace llarp
|
|||
}
|
||||
strncpy(tunif->ifname, ifname.c_str(), sizeof(tunif->ifname) - 1);
|
||||
llarp::LogInfo(Name() + " setting ifname to ", tunif->ifname);
|
||||
}
|
||||
|
||||
std::string ifaddr = conf.m_ifaddr;
|
||||
if (tunif)
|
||||
{
|
||||
std::string addr;
|
||||
m_UseV6 = addr.find(":") != std::string::npos;
|
||||
auto pos = ifaddr.find("/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string part = ifaddr.substr(pos + 1);
|
||||
int num = std::stoi(part);
|
||||
if (num > 0)
|
||||
{
|
||||
tunif->netmask = num;
|
||||
addr = ifaddr.substr(0, pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
llarp::LogError("bad ifaddr value: ", ifaddr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_UseV6)
|
||||
tunif->netmask = 128;
|
||||
else
|
||||
tunif->netmask = 32;
|
||||
addr = ifaddr;
|
||||
}
|
||||
m_OurRange = conf.m_ifaddr;
|
||||
m_UseV6 = not m_OurRange.IsV4();
|
||||
tunif->netmask = m_OurRange.HostmaskBits();
|
||||
const auto addr = m_OurRange.BaseAddressString();
|
||||
llarp::LogInfo(Name() + " set ifaddr to ", addr, " with netmask ", tunif->netmask);
|
||||
strncpy(tunif->ifaddr, addr.c_str(), sizeof(tunif->ifaddr) - 1);
|
||||
}
|
||||
|
@ -251,7 +227,6 @@ namespace llarp
|
|||
TunEndpoint::Flush()
|
||||
{
|
||||
FlushSend();
|
||||
m_ExitMap.ForEachValue([r = Router()](const auto& exit) { exit->UpstreamFlush(r); });
|
||||
Pump(Now());
|
||||
}
|
||||
|
||||
|
@ -513,7 +488,6 @@ namespace llarp
|
|||
TunEndpoint::ResetInternalState()
|
||||
{
|
||||
service::Endpoint::ResetInternalState();
|
||||
m_ExitMap.ForEachValue([](const auto& exit) { exit->ResetInternalState(); });
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -581,11 +555,6 @@ namespace llarp
|
|||
llarp::LogWarn("Couldn't start endpoint");
|
||||
return false;
|
||||
}
|
||||
const auto blacklist = SnodeBlacklist();
|
||||
m_ExitMap.ForEachValue([blacklist](const auto& exit) {
|
||||
for (const auto& snode : blacklist)
|
||||
exit->BlacklistSNode(snode);
|
||||
});
|
||||
return SetupNetworking();
|
||||
}
|
||||
|
||||
|
@ -769,14 +738,12 @@ namespace llarp
|
|||
void
|
||||
TunEndpoint::Tick(llarp_time_t now)
|
||||
{
|
||||
m_ExitMap.ForEachValue([&](const auto& exit) { exit->Tick(now); });
|
||||
Endpoint::Tick(now);
|
||||
}
|
||||
|
||||
bool
|
||||
TunEndpoint::Stop()
|
||||
{
|
||||
m_ExitMap.ForEachValue([](const auto& exit) { exit->Stop(); });
|
||||
return llarp::service::Endpoint::Stop();
|
||||
}
|
||||
|
||||
|
@ -800,7 +767,8 @@ namespace llarp
|
|||
auto itr = m_IPToAddr.find(dst);
|
||||
if (itr == m_IPToAddr.end())
|
||||
{
|
||||
if (IsBogon(dst) or not m_state->m_ExitNode.has_value())
|
||||
const auto exits = m_ExitMap.FindAll(dst);
|
||||
if (IsBogon(dst) or exits.empty())
|
||||
{
|
||||
// send icmp unreachable
|
||||
const auto icmp = pkt.MakeICMPUnreachable();
|
||||
|
@ -811,13 +779,13 @@ namespace llarp
|
|||
}
|
||||
else
|
||||
{
|
||||
const auto addr = *exits.begin();
|
||||
pkt.ZeroSourceAddress();
|
||||
MarkAddressOutbound(*m_state->m_ExitNode);
|
||||
MarkAddressOutbound(addr);
|
||||
EnsurePathToService(
|
||||
*m_state->m_ExitNode,
|
||||
[pkt, self = this](service::Address, service::OutboundContext*) {
|
||||
self->SendToServiceOrQueue(
|
||||
*self->m_state->m_ExitNode, pkt.ConstBuffer(), service::eProtocolExit);
|
||||
addr,
|
||||
[addr, pkt, self = this](service::Address, service::OutboundContext*) {
|
||||
self->SendToServiceOrQueue(addr, pkt.ConstBuffer(), service::eProtocolExit);
|
||||
},
|
||||
1s);
|
||||
}
|
||||
|
@ -883,17 +851,7 @@ namespace llarp
|
|||
net::IPPacket pkt;
|
||||
if (not pkt.Load(buf))
|
||||
return false;
|
||||
|
||||
if (m_state->m_ExitNode == service::Address{addr.as_array()} and t == service::eProtocolExit)
|
||||
{
|
||||
// client side from exit
|
||||
if (pkt.IsV4())
|
||||
src = pkt.src4to6();
|
||||
else if (pkt.IsV6())
|
||||
src = pkt.srcv6();
|
||||
dst = m_OurIP;
|
||||
}
|
||||
else if (m_state->m_ExitEnabled)
|
||||
if (m_state->m_ExitEnabled)
|
||||
{
|
||||
// exit side from exit
|
||||
src = ObtainIPForAddr(addr, snode);
|
||||
|
@ -902,6 +860,22 @@ namespace llarp
|
|||
else if (pkt.IsV6())
|
||||
dst = pkt.dstv6();
|
||||
}
|
||||
else if (t == service::eProtocolExit)
|
||||
{
|
||||
// client side exit traffic from exit
|
||||
if (pkt.IsV4())
|
||||
src = pkt.src4to6();
|
||||
else if (pkt.IsV6())
|
||||
src = pkt.srcv6();
|
||||
dst = m_OurIP;
|
||||
// find what exit we think this should be for
|
||||
const auto mapped = m_ExitMap.FindAll(src);
|
||||
if (mapped.count(service::Address{addr}) == 0 or IsBogon(src))
|
||||
{
|
||||
// we got exit traffic from someone who we should not have gotten it from
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// snapp traffic
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace llarp
|
|||
Tick(llarp_time_t now) override;
|
||||
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
ExtractStatus() const override;
|
||||
|
||||
std::unordered_map<std::string, std::string>
|
||||
NotifyParams() const override;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
#include <net/net_int.hpp>
|
||||
#include <net/net.hpp>
|
||||
#include <cstdint>
|
||||
|
||||
namespace llarp::net
|
||||
|
@ -14,7 +13,7 @@ namespace llarp::net
|
|||
constexpr huint128_t
|
||||
ExpandV4(huint32_t x)
|
||||
{
|
||||
return huint128_t{0xffff'0000'0000UL} | huint128_t{x.h};
|
||||
return huint128_t{0x0000'ffff'0000'0000UL} | huint128_t{x.h};
|
||||
}
|
||||
|
||||
constexpr huint32_t
|
||||
|
|
|
@ -2,12 +2,6 @@
|
|||
|
||||
namespace llarp
|
||||
{
|
||||
bool
|
||||
IPRange::ContainsV4(const huint32_t& ip) const
|
||||
{
|
||||
return Contains(net::ExpandV4(ip));
|
||||
}
|
||||
|
||||
bool
|
||||
IPRange::FromString(std::string str)
|
||||
{
|
||||
|
@ -27,7 +21,7 @@ namespace llarp
|
|||
addr = net::ExpandV4(ip);
|
||||
if (!bitsstr.empty())
|
||||
{
|
||||
auto bits = atoi(bitsstr.c_str());
|
||||
const auto bits = stoi(bitsstr);
|
||||
if (bits < 0 || bits > 32)
|
||||
return false;
|
||||
netmask_bits = netmask_ipv6_bits(96 + bits);
|
||||
|
@ -55,21 +49,14 @@ namespace llarp
|
|||
}
|
||||
|
||||
std::string
|
||||
IPRange::ToString() const
|
||||
IPRange::BaseAddressString() const
|
||||
{
|
||||
char buf[INET6_ADDRSTRLEN + 1] = {0};
|
||||
std::string str;
|
||||
in6_addr inaddr = {};
|
||||
size_t numset = 0;
|
||||
uint128_t bits = netmask_bits.h;
|
||||
while (bits)
|
||||
if (IsV4())
|
||||
{
|
||||
if (bits & 1)
|
||||
numset++;
|
||||
bits >>= 1;
|
||||
const huint32_t addr4 = net::TruncateV6(addr);
|
||||
return addr4.ToString();
|
||||
}
|
||||
str += inet_ntop(AF_INET6, &inaddr, buf, sizeof(buf));
|
||||
return str + "/" + std::to_string(numset);
|
||||
return addr.ToString();
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <ostream>
|
||||
#include <net/ip.hpp>
|
||||
#include <net/net_bits.hpp>
|
||||
#include <util/bits.hpp>
|
||||
#include <util/types.hpp>
|
||||
#include <string>
|
||||
|
@ -19,6 +20,32 @@ namespace llarp
|
|||
return IPRange{net::ExpandV4(ipaddr_ipv4_bits(a, b, c, d)), netmask_ipv6_bits(mask + 96)};
|
||||
}
|
||||
|
||||
/// return true if this iprange is in the SIIT range for containing ipv4 addresses
|
||||
constexpr bool
|
||||
IsV4() const
|
||||
{
|
||||
constexpr auto siit = IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)};
|
||||
return siit.Contains(addr);
|
||||
}
|
||||
|
||||
/// return the number of bits set in the hostmask
|
||||
constexpr int
|
||||
HostmaskBits() const
|
||||
{
|
||||
if (IsV4())
|
||||
{
|
||||
return bits::count_bits(net::TruncateV6(netmask_bits));
|
||||
}
|
||||
return bits::count_bits(netmask_bits);
|
||||
}
|
||||
|
||||
/// return true if the other range is inside our range
|
||||
constexpr bool
|
||||
Contains(const IPRange& other) const
|
||||
{
|
||||
return Contains(other.addr) and Contains(other.HighestAddr());
|
||||
}
|
||||
|
||||
/// return true if ip is contained in this ip range
|
||||
constexpr bool
|
||||
Contains(const Addr_t& ip) const
|
||||
|
@ -26,8 +53,14 @@ namespace llarp
|
|||
return (addr & netmask_bits) == (ip & netmask_bits);
|
||||
}
|
||||
|
||||
bool
|
||||
ContainsV4(const huint32_t& ip) const;
|
||||
/// return true if we are a ipv4 range and contains this ip
|
||||
constexpr bool
|
||||
Contains(const huint32_t& ip) const
|
||||
{
|
||||
if (not IsV4())
|
||||
return false;
|
||||
return Contains(net::ExpandV4(ip));
|
||||
}
|
||||
|
||||
friend std::ostream&
|
||||
operator<<(std::ostream& out, const IPRange& a)
|
||||
|
@ -36,7 +69,7 @@ namespace llarp
|
|||
}
|
||||
|
||||
/// get the highest address on this range
|
||||
huint128_t
|
||||
constexpr huint128_t
|
||||
HighestAddr() const
|
||||
{
|
||||
return (addr & netmask_bits) + (huint128_t{1} << (128 - bits::count_bits_128(netmask_bits.h)))
|
||||
|
@ -51,7 +84,13 @@ namespace llarp
|
|||
}
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
ToString() const
|
||||
{
|
||||
return BaseAddressString() + "/" + std::to_string(HostmaskBits());
|
||||
}
|
||||
|
||||
std::string
|
||||
BaseAddressString() const;
|
||||
|
||||
bool
|
||||
FromString(std::string str);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define LLARP_NET_IP_RANGE_MAP_HPP
|
||||
|
||||
#include <net/ip_range.hpp>
|
||||
#include <list>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -18,7 +19,7 @@ namespace llarp
|
|||
using IP_t = Range_t::Addr_t;
|
||||
|
||||
using Entry_t = std::pair<Range_t, Value_t>;
|
||||
using Container_t = std::forward_list<Entry_t>;
|
||||
using Container_t = std::list<Entry_t>;
|
||||
|
||||
/// get a set of all values
|
||||
std::set<Value_t>
|
||||
|
@ -30,6 +31,17 @@ namespace llarp
|
|||
return all;
|
||||
}
|
||||
|
||||
bool
|
||||
ContainsValue(const Value_t& val) const
|
||||
{
|
||||
for (const auto& entry : m_Entries)
|
||||
{
|
||||
if (entry.second == val)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ForEachValue(std::function<void(const Value_t&)> functor) const
|
||||
{
|
||||
|
@ -37,6 +49,14 @@ namespace llarp
|
|||
functor(entry.second);
|
||||
}
|
||||
|
||||
template <typename Visit_t>
|
||||
void
|
||||
ForEachEntry(Visit_t visit) const
|
||||
{
|
||||
for (const auto& entry : m_Entries)
|
||||
visit(entry.first, entry.second);
|
||||
}
|
||||
|
||||
/// convert all values into type T using a transformer
|
||||
template <typename T, typename Transformer>
|
||||
std::set<T>
|
||||
|
@ -80,6 +100,20 @@ namespace llarp
|
|||
m_Entries.sort(CompareEntry{});
|
||||
}
|
||||
|
||||
template <typename Visit_t>
|
||||
void
|
||||
RemoveIf(Visit_t visit)
|
||||
{
|
||||
auto itr = m_Entries.begin();
|
||||
while (itr != m_Entries.end())
|
||||
{
|
||||
if (visit(*itr))
|
||||
itr = m_Entries.erase(itr);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Container_t m_Entries;
|
||||
};
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <util/str.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <list>
|
||||
|
||||
bool
|
||||
operator==(const sockaddr& a, const sockaddr& b)
|
||||
|
@ -428,7 +429,7 @@ namespace llarp
|
|||
if (i->ifa_addr and i->ifa_addr->sa_family == AF_INET)
|
||||
{
|
||||
llarp::nuint32_t addr{((sockaddr_in*)i->ifa_addr)->sin_addr.s_addr};
|
||||
if (loopback.ContainsV4(xntohl(addr)))
|
||||
if (loopback.Contains(xntohl(addr)))
|
||||
{
|
||||
ifname = i->ifa_name;
|
||||
}
|
||||
|
@ -469,10 +470,10 @@ namespace llarp
|
|||
}
|
||||
|
||||
// TODO: ipv6?
|
||||
std::optional<std::string>
|
||||
std::optional<IPRange>
|
||||
FindFreeRange()
|
||||
{
|
||||
std::vector<IPRange> currentRanges;
|
||||
std::list<IPRange> currentRanges;
|
||||
IterAllNetworkInterfaces([&](ifaddrs* i) {
|
||||
if (i && i->ifa_addr)
|
||||
{
|
||||
|
@ -494,49 +495,33 @@ namespace llarp
|
|||
IPRange{net::ExpandV4(xntohl(ifaddr)), net::ExpandV4(xntohl(ifmask))});
|
||||
}
|
||||
});
|
||||
// try 10.x.0.0/16
|
||||
byte_t oct = 0;
|
||||
while (oct < 255)
|
||||
{
|
||||
const huint32_t loaddr = ipaddr_ipv4_bits(10, oct, 0, 1);
|
||||
const huint32_t hiaddr = ipaddr_ipv4_bits(10, oct, 255, 255);
|
||||
bool hit = false;
|
||||
for (const auto& range : currentRanges)
|
||||
auto ownsRange = [¤tRanges](IPRange range) -> bool {
|
||||
for (const auto& ownRange : currentRanges)
|
||||
{
|
||||
hit = hit || range.ContainsV4(loaddr) || range.ContainsV4(hiaddr);
|
||||
if (ownRange.Contains(range))
|
||||
return true;
|
||||
}
|
||||
if (!hit)
|
||||
return loaddr.ToString() + "/16";
|
||||
++oct;
|
||||
return false;
|
||||
};
|
||||
// generate possible ranges to in order of attempts
|
||||
std::list<IPRange> possibleRanges;
|
||||
for (byte_t oct = 0; oct < 255; ++oct)
|
||||
{
|
||||
possibleRanges.emplace_back(IPRange::FromIPv4(10, oct, 0, 1, 16));
|
||||
}
|
||||
// try 192.168.x.0/24
|
||||
oct = 0;
|
||||
while (oct < 255)
|
||||
for (byte_t oct = 0; oct < 255; ++oct)
|
||||
{
|
||||
const huint32_t loaddr = ipaddr_ipv4_bits(192, 168, oct, 1);
|
||||
const huint32_t hiaddr = ipaddr_ipv4_bits(192, 168, oct, 255);
|
||||
bool hit = false;
|
||||
for (const auto& range : currentRanges)
|
||||
{
|
||||
hit = hit || range.ContainsV4(loaddr) || range.ContainsV4(hiaddr);
|
||||
}
|
||||
if (!hit)
|
||||
return loaddr.ToString() + "/24";
|
||||
possibleRanges.emplace_back(IPRange::FromIPv4(192, 168, oct, 1, 24));
|
||||
}
|
||||
// try 172.16.x.0/24
|
||||
oct = 0;
|
||||
while (oct < 255)
|
||||
for (byte_t oct = 0; oct < 255; ++oct)
|
||||
{
|
||||
const huint32_t loaddr = ipaddr_ipv4_bits(172, 16, oct, 1);
|
||||
const huint32_t hiaddr = ipaddr_ipv4_bits(172, 16, oct, 255);
|
||||
bool hit = false;
|
||||
for (const auto& range : currentRanges)
|
||||
{
|
||||
hit = hit || range.ContainsV4(loaddr) || range.ContainsV4(hiaddr);
|
||||
}
|
||||
if (!hit)
|
||||
return loaddr.ToString() + "/24";
|
||||
++oct;
|
||||
possibleRanges.emplace_back(IPRange::FromIPv4(172, 16, 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;
|
||||
}
|
||||
|
@ -660,7 +645,7 @@ namespace llarp
|
|||
{
|
||||
for (const auto& bogon : bogonRanges)
|
||||
{
|
||||
if (bogon.ContainsV4(addr))
|
||||
if (bogon.Contains(addr))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <net/ip_address.hpp>
|
||||
#include <net/net_int.hpp>
|
||||
#include <net/net.h>
|
||||
#include <net/ip_range.hpp>
|
||||
#include <util/mem.hpp>
|
||||
#include <util/bits.hpp>
|
||||
|
||||
|
@ -45,52 +46,9 @@ operator==(const in6_addr& a, const in6_addr& b);
|
|||
|
||||
namespace llarp
|
||||
{
|
||||
/// get a netmask with the higest numset bits set
|
||||
constexpr huint128_t
|
||||
_netmask_ipv6_bits(uint32_t numset)
|
||||
{
|
||||
return (128 - numset) ? (huint128_t{1} << numset) | _netmask_ipv6_bits(numset + 1)
|
||||
: huint128_t{0};
|
||||
}
|
||||
|
||||
constexpr huint128_t
|
||||
netmask_ipv6_bits(uint32_t numset)
|
||||
{
|
||||
return _netmask_ipv6_bits(128 - numset);
|
||||
}
|
||||
|
||||
/// get a netmask with the higest numset bits set
|
||||
constexpr uint32_t
|
||||
_netmask_ipv4_bits(uint32_t numset)
|
||||
{
|
||||
return (32 - numset) ? (1 << numset) | _netmask_ipv4_bits(numset + 1) : 0;
|
||||
}
|
||||
|
||||
/// get a netmask given some /N range
|
||||
constexpr huint32_t
|
||||
netmask_ipv4_bits(uint32_t num)
|
||||
{
|
||||
return huint32_t{_netmask_ipv4_bits(32 - num)};
|
||||
}
|
||||
|
||||
constexpr huint32_t
|
||||
ipaddr_ipv4_bits(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
{
|
||||
return huint32_t{(d) | (c << 8) | (b << 16) | (a << 24)};
|
||||
}
|
||||
|
||||
bool
|
||||
IsIPv4Bogon(const huint32_t& addr);
|
||||
|
||||
constexpr bool
|
||||
ipv6_is_siit(const in6_addr& addr)
|
||||
{
|
||||
return addr.s6_addr[11] == 0xff && addr.s6_addr[10] == 0xff && addr.s6_addr[9] == 0
|
||||
&& addr.s6_addr[8] == 0 && addr.s6_addr[7] == 0 && addr.s6_addr[6] == 0
|
||||
&& addr.s6_addr[5] == 0 && addr.s6_addr[4] == 0 && addr.s6_addr[3] == 0
|
||||
&& addr.s6_addr[2] == 0 && addr.s6_addr[1] == 0 && addr.s6_addr[0] == 0;
|
||||
}
|
||||
|
||||
bool
|
||||
IsBogon(const in6_addr& addr);
|
||||
|
||||
|
@ -108,7 +66,7 @@ namespace llarp
|
|||
GetBestNetIF(std::string& ifname, int af = AF_INET);
|
||||
|
||||
/// look at adapter ranges and find a free one
|
||||
std::optional<std::string>
|
||||
std::optional<IPRange>
|
||||
FindFreeRange();
|
||||
|
||||
/// look at adapter names and find a free one
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
#include <net/net_int.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/// get a netmask with the higest numset bits set
|
||||
constexpr huint128_t
|
||||
_netmask_ipv6_bits(uint32_t numset)
|
||||
{
|
||||
return (128 - numset) ? (huint128_t{1} << numset) | _netmask_ipv6_bits(numset + 1)
|
||||
: huint128_t{0};
|
||||
}
|
||||
|
||||
constexpr huint128_t
|
||||
netmask_ipv6_bits(uint32_t numset)
|
||||
{
|
||||
return _netmask_ipv6_bits(128 - numset);
|
||||
}
|
||||
|
||||
/// get a netmask with the higest numset bits set
|
||||
constexpr uint32_t
|
||||
_netmask_ipv4_bits(uint32_t numset)
|
||||
{
|
||||
return (32 - numset) ? (1 << numset) | _netmask_ipv4_bits(numset + 1) : 0;
|
||||
}
|
||||
|
||||
/// get a netmask given some /N range
|
||||
constexpr huint32_t
|
||||
netmask_ipv4_bits(uint32_t num)
|
||||
{
|
||||
return huint32_t{_netmask_ipv4_bits(32 - num)};
|
||||
}
|
||||
|
||||
constexpr huint32_t
|
||||
ipaddr_ipv4_bits(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
|
||||
{
|
||||
return huint32_t{(d) | (c << 8) | (b << 16) | (a << 24)};
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
ipv6_is_siit(const in6_addr& addr)
|
||||
{
|
||||
return addr.s6_addr[11] == 0xff && addr.s6_addr[10] == 0xff && addr.s6_addr[9] == 0
|
||||
&& addr.s6_addr[8] == 0 && addr.s6_addr[7] == 0 && addr.s6_addr[6] == 0
|
||||
&& addr.s6_addr[5] == 0 && addr.s6_addr[4] == 0 && addr.s6_addr[3] == 0
|
||||
&& addr.s6_addr[2] == 0 && addr.s6_addr[1] == 0 && addr.s6_addr[0] == 0;
|
||||
}
|
||||
} // namespace llarp
|
|
@ -214,8 +214,6 @@ namespace llarp
|
|||
{
|
||||
if (m_router->NumberOfConnectedRouters() == 0)
|
||||
{
|
||||
// persist connection
|
||||
m_router->ConnectToRandomRouters(1);
|
||||
return false;
|
||||
}
|
||||
bool got = false;
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace llarp::rpc
|
|||
void
|
||||
EndpointAuthRPC::Start()
|
||||
{
|
||||
if (m_AuthURL.empty())
|
||||
if (m_AuthURL.empty() or m_AuthMethod.empty())
|
||||
return;
|
||||
m_LMQ->connect_remote(
|
||||
m_AuthURL,
|
||||
|
@ -40,41 +40,37 @@ namespace llarp::rpc
|
|||
std::function<void(service::AuthResult)> hook)
|
||||
{
|
||||
const auto from = msg->sender.Addr();
|
||||
auto reply = [logic = m_Endpoint->RouterLogic(), hook](service::AuthResult result) {
|
||||
logic->Call([hook, result]() { hook(result); });
|
||||
};
|
||||
if (m_AuthWhitelist.count(from))
|
||||
{
|
||||
// explicitly whitelisted source
|
||||
m_Endpoint->RouterLogic()->Call([hook]() { hook(service::AuthResult::eAuthAccepted); });
|
||||
reply(service::AuthResult::eAuthAccepted);
|
||||
return;
|
||||
}
|
||||
if (not m_Conn.has_value())
|
||||
{
|
||||
// we don't have a connection to the backend so it's failed
|
||||
m_Endpoint->RouterLogic()->Call([hook]() { hook(service::AuthResult::eAuthFailed); });
|
||||
reply(service::AuthResult::eAuthFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->proto != llarp::service::eProtocolAuth)
|
||||
{
|
||||
// not an auth message, reject
|
||||
m_Endpoint->RouterLogic()->Call([hook]() { hook(service::AuthResult::eAuthRejected); });
|
||||
reply(service::AuthResult::eAuthRejected);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto maybe = msg->MaybeEncodeAuthInfo();
|
||||
if (not maybe.has_value())
|
||||
{
|
||||
// cannot generate meta info, failed
|
||||
m_Endpoint->RouterLogic()->Call([hook]() { hook(service::AuthResult::eAuthFailed); });
|
||||
return;
|
||||
}
|
||||
std::string_view metaInfo{(char*)maybe->data(), maybe->size()};
|
||||
const auto authinfo = msg->EncodeAuthInfo();
|
||||
std::string_view metainfo{authinfo.data(), authinfo.size()};
|
||||
std::string_view payload{(char*)msg->payload.data(), msg->payload.size()};
|
||||
// call method with 2 parameters: metainfo and userdata
|
||||
m_LMQ->request(
|
||||
*m_Conn,
|
||||
m_AuthMethod,
|
||||
[self = shared_from_this(), hook, from = from.ToString()](
|
||||
bool success, std::vector<std::string> data) {
|
||||
[self = shared_from_this(), reply](bool success, std::vector<std::string> data) {
|
||||
service::AuthResult result = service::AuthResult::eAuthFailed;
|
||||
if (success and not data.empty())
|
||||
{
|
||||
|
@ -84,10 +80,10 @@ namespace llarp::rpc
|
|||
result = *maybe;
|
||||
}
|
||||
}
|
||||
self->m_Endpoint->RouterLogic()->Call([hook, result]() { hook(result); });
|
||||
reply(result);
|
||||
},
|
||||
metaInfo,
|
||||
metainfo,
|
||||
payload);
|
||||
} // namespace llarp::rpc
|
||||
}
|
||||
|
||||
} // namespace llarp::rpc
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
#include <router/abstractrouter.hpp>
|
||||
#include <util/thread/logic.hpp>
|
||||
#include <constants/version.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <net/ip_range.hpp>
|
||||
#include <service/context.hpp>
|
||||
#include <service/auth.hpp>
|
||||
|
||||
namespace llarp::rpc
|
||||
{
|
||||
|
@ -9,6 +13,72 @@ namespace llarp::rpc
|
|||
{
|
||||
}
|
||||
|
||||
/// maybe parse json from message paramter at index
|
||||
std::optional<nlohmann::json>
|
||||
MaybeParseJSON(const lokimq::Message& msg, size_t index = 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto& str = msg.data.at(index);
|
||||
return nlohmann::json::parse(str);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Result_t>
|
||||
std::string
|
||||
CreateJSONResponse(Result_t result)
|
||||
{
|
||||
const auto obj = nlohmann::json{
|
||||
{"error", nullptr},
|
||||
{"result", result},
|
||||
};
|
||||
return obj.dump();
|
||||
}
|
||||
|
||||
std::string
|
||||
CreateJSONError(std::string_view msg)
|
||||
{
|
||||
const auto obj = nlohmann::json{
|
||||
{"error", msg},
|
||||
};
|
||||
return obj.dump();
|
||||
}
|
||||
|
||||
/// a function that replies to an rpc request
|
||||
using ReplyFunction_t = std::function<void(std::string)>;
|
||||
|
||||
void
|
||||
HandleJSONRequest(
|
||||
lokimq::Message& msg, std::function<void(nlohmann::json, ReplyFunction_t)> handleRequest)
|
||||
{
|
||||
const auto maybe = MaybeParseJSON(msg);
|
||||
if (not maybe.has_value())
|
||||
{
|
||||
msg.send_reply(CreateJSONError("failed to parse json"));
|
||||
return;
|
||||
}
|
||||
if (not maybe->is_object())
|
||||
{
|
||||
msg.send_reply(CreateJSONError("request data not a json object"));
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
std::promise<std::string> reply;
|
||||
handleRequest(*maybe, [&reply](std::string result) { reply.set_value(result); });
|
||||
auto ftr = reply.get_future();
|
||||
msg.send_reply(ftr.get());
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
msg.send_reply(CreateJSONError(ex.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RpcServer::AsyncServeRPC(lokimq::address url)
|
||||
{
|
||||
|
@ -19,23 +89,102 @@ namespace llarp::rpc
|
|||
[&](lokimq::Message& msg) {
|
||||
if (not m_Router->IsRunning())
|
||||
{
|
||||
msg.send_reply("router is not running");
|
||||
msg.send_reply(CreateJSONError("router is not running"));
|
||||
return;
|
||||
}
|
||||
m_Router->Stop();
|
||||
msg.send_reply("OK");
|
||||
msg.send_reply(CreateJSONResponse("OK"));
|
||||
})
|
||||
.add_request_command(
|
||||
"version", [](lokimq::Message& msg) { msg.send_reply(llarp::VERSION_FULL); })
|
||||
.add_request_command("status", [&](lokimq::Message& msg) {
|
||||
std::promise<std::string> result;
|
||||
LogicCall(m_Router->logic(), [&result, r = m_Router]() {
|
||||
const auto state = r->ExtractStatus();
|
||||
result.set_value(state.dump());
|
||||
"version",
|
||||
[](lokimq::Message& msg) { msg.send_reply(CreateJSONResponse(llarp::VERSION_FULL)); })
|
||||
.add_request_command(
|
||||
"status",
|
||||
[&](lokimq::Message& msg) {
|
||||
std::promise<util::StatusObject> result;
|
||||
LogicCall(m_Router->logic(), [&result, r = m_Router]() {
|
||||
const auto state = r->ExtractStatus();
|
||||
result.set_value(state);
|
||||
});
|
||||
auto ftr = result.get_future();
|
||||
msg.send_reply(CreateJSONResponse(ftr.get()));
|
||||
})
|
||||
.add_request_command("exit", [&](lokimq::Message& msg) {
|
||||
HandleJSONRequest(msg, [r = m_Router](nlohmann::json obj, ReplyFunction_t reply) {
|
||||
std::optional<service::Address> exit;
|
||||
IPRange range;
|
||||
bool map = true;
|
||||
const auto exit_itr = obj.find("exit");
|
||||
if (exit_itr != obj.end())
|
||||
{
|
||||
service::Address addr;
|
||||
if (not addr.FromString(exit_itr->get<std::string>()))
|
||||
{
|
||||
reply(CreateJSONError("invalid exit address"));
|
||||
return;
|
||||
}
|
||||
exit = addr;
|
||||
}
|
||||
|
||||
const auto unmap_itr = obj.find("unmap");
|
||||
if (unmap_itr != obj.end() and unmap_itr->get<bool>())
|
||||
{
|
||||
map = false;
|
||||
}
|
||||
|
||||
const auto range_itr = obj.find("range");
|
||||
if (range_itr == obj.end())
|
||||
{
|
||||
range.FromString("0.0.0.0/0");
|
||||
}
|
||||
else if (not range.FromString(range_itr->get<std::string>()))
|
||||
{
|
||||
reply(CreateJSONError("invalid ip range"));
|
||||
return;
|
||||
}
|
||||
std::optional<std::string> token;
|
||||
const auto token_itr = obj.find("token");
|
||||
if (token_itr != obj.end())
|
||||
{
|
||||
token = token_itr->get<std::string>();
|
||||
}
|
||||
|
||||
std::string endpoint = "default";
|
||||
const auto endpoint_itr = obj.find("endpoint");
|
||||
if (endpoint_itr != obj.end())
|
||||
{
|
||||
endpoint = endpoint_itr->get<std::string>();
|
||||
}
|
||||
std::promise<std::string> result;
|
||||
LogicCall(r->logic(), [map, exit, range, token, endpoint, r, &result]() {
|
||||
auto ep = r->hiddenServiceContext().GetEndpointByName(endpoint);
|
||||
if (ep == nullptr)
|
||||
{
|
||||
result.set_value(CreateJSONError("no endpoint with name " + endpoint));
|
||||
return;
|
||||
}
|
||||
if (map and exit.has_value())
|
||||
{
|
||||
ep->MapExitRange(range, *exit);
|
||||
if (token.has_value())
|
||||
{
|
||||
ep->SetAuthInfoForEndpoint(*exit, service::AuthInfo{*token});
|
||||
}
|
||||
}
|
||||
else if (map and not exit.has_value())
|
||||
{
|
||||
result.set_value(CreateJSONError("no exit address provided"));
|
||||
return;
|
||||
}
|
||||
else if (not map)
|
||||
{
|
||||
ep->UnmapExitRange(range);
|
||||
}
|
||||
result.set_value(CreateJSONResponse("OK"));
|
||||
});
|
||||
auto ftr = result.get_future();
|
||||
reply(ftr.get());
|
||||
});
|
||||
auto ftr = result.get_future();
|
||||
msg.send_reply(ftr.get());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace llarp::rpc
|
||||
|
|
|
@ -38,4 +38,11 @@ namespace llarp::service
|
|||
std::shared_ptr<llarp::service::ProtocolMessage> msg,
|
||||
std::function<void(AuthResult)> hook) = 0;
|
||||
};
|
||||
|
||||
/// info needed by clients in order to authenticate to a remote endpoint
|
||||
struct AuthInfo
|
||||
{
|
||||
std::string token;
|
||||
};
|
||||
|
||||
} // namespace llarp::service
|
||||
|
|
|
@ -51,6 +51,9 @@ namespace llarp
|
|||
if (conf.m_Hops.has_value())
|
||||
numHops = *conf.m_Hops;
|
||||
|
||||
conf.m_ExitMap.ForEachEntry(
|
||||
[&](const IPRange& range, const service::Address& addr) { MapExitRange(range, addr); });
|
||||
|
||||
return m_state->Configure(conf);
|
||||
}
|
||||
|
||||
|
@ -837,7 +840,7 @@ namespace llarp
|
|||
Endpoint::ProcessDataMessage(std::shared_ptr<ProtocolMessage> msg)
|
||||
{
|
||||
if ((msg->proto == eProtocolExit
|
||||
&& (m_state->m_ExitEnabled || msg->sender.Addr() == m_state->m_ExitNode))
|
||||
&& (m_state->m_ExitEnabled || m_ExitMap.ContainsValue(msg->sender.Addr())))
|
||||
|| msg->proto == eProtocolTrafficV4 || msg->proto == eProtocolTrafficV6)
|
||||
{
|
||||
util::Lock l(m_state->m_InboundTrafficQueueMutex);
|
||||
|
@ -1376,5 +1379,40 @@ namespace llarp
|
|||
{
|
||||
return m_state->m_Sessions;
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::SetAuthInfoForEndpoint(Address addr, AuthInfo info)
|
||||
{
|
||||
m_RemoteAuthInfos[addr] = std::move(info);
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::MapExitRange(IPRange range, Address exit)
|
||||
{
|
||||
LogInfo(Name(), " map ", range, " to exit at ", exit);
|
||||
m_ExitMap.Insert(range, exit);
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::UnmapExitRange(IPRange range)
|
||||
{
|
||||
// unmap all ranges that fit in the range we gave
|
||||
m_ExitMap.RemoveIf([&](const auto& item) -> bool {
|
||||
if (not range.Contains(item.first))
|
||||
return false;
|
||||
LogInfo(Name(), " unmap ", item.first, " from exit at ", item.second);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
std::optional<AuthInfo>
|
||||
Endpoint::MaybeGetAuthInfoForEndpoint(Address remote)
|
||||
{
|
||||
const auto itr = m_RemoteAuthInfos.find(remote);
|
||||
if (itr == m_RemoteAuthInfos.end())
|
||||
return std::nullopt;
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
} // namespace service
|
||||
} // namespace llarp
|
||||
|
|
|
@ -89,7 +89,7 @@ namespace llarp
|
|||
virtual std::unordered_map<std::string, std::string>
|
||||
NotifyParams() const;
|
||||
|
||||
util::StatusObject
|
||||
virtual util::StatusObject
|
||||
ExtractStatus() const;
|
||||
|
||||
void
|
||||
|
@ -178,6 +178,10 @@ namespace llarp
|
|||
void
|
||||
SetEndpointAuth(std::shared_ptr<IAuthPolicy> policy);
|
||||
|
||||
/// sets how we authenticate with remote address
|
||||
void
|
||||
SetAuthInfoForEndpoint(Address remote, AuthInfo info);
|
||||
|
||||
// virtual huint128_t
|
||||
// ObtainIPForAddr(const AlignedBuffer< 32 >& addr, bool serviceNode) = 0;
|
||||
|
||||
|
@ -226,6 +230,12 @@ namespace llarp
|
|||
return m_Identity;
|
||||
}
|
||||
|
||||
void
|
||||
MapExitRange(IPRange range, service::Address exit);
|
||||
|
||||
void
|
||||
UnmapExitRange(IPRange range);
|
||||
|
||||
void
|
||||
PutLookup(IServiceLookup* lookup, uint64_t txid) override;
|
||||
|
||||
|
@ -373,6 +383,9 @@ namespace llarp
|
|||
bool
|
||||
SendToSNodeOrQueue(const RouterID& addr, const llarp_buffer_t& payload);
|
||||
|
||||
std::optional<AuthInfo>
|
||||
MaybeGetAuthInfoForEndpoint(service::Address addr);
|
||||
|
||||
protected:
|
||||
/// parent context that owns this endpoint
|
||||
Context* const context;
|
||||
|
@ -432,13 +445,14 @@ namespace llarp
|
|||
protected:
|
||||
IDataHandler* m_DataHandler = nullptr;
|
||||
Identity m_Identity;
|
||||
net::IPRangeMap<path::PathSet_ptr> m_ExitMap;
|
||||
net::IPRangeMap<service::Address> m_ExitMap;
|
||||
hooks::Backend_ptr m_OnUp;
|
||||
hooks::Backend_ptr m_OnDown;
|
||||
hooks::Backend_ptr m_OnReady;
|
||||
bool m_PublishIntroSet = true;
|
||||
std::unique_ptr<EndpointState> m_state;
|
||||
std::shared_ptr<IAuthPolicy> m_AuthPolicy;
|
||||
std::unordered_map<Address, AuthInfo, Address::Hash> m_RemoteAuthInfos;
|
||||
|
||||
private:
|
||||
void
|
||||
|
|
|
@ -17,7 +17,6 @@ namespace llarp
|
|||
m_Keyfile = conf.m_keyfile->string();
|
||||
m_SnodeBlacklist = conf.m_snodeBlacklist;
|
||||
m_ExitEnabled = conf.m_AllowExit;
|
||||
m_ExitNode = conf.m_exitNode;
|
||||
// TODO:
|
||||
/*
|
||||
if (k == "on-up")
|
||||
|
|
|
@ -53,7 +53,6 @@ namespace llarp
|
|||
std::string m_Name;
|
||||
std::string m_NetNS;
|
||||
bool m_ExitEnabled = false;
|
||||
std::optional<service::Address> m_ExitNode;
|
||||
|
||||
util::Mutex m_SendQueueMutex; // protects m_SendQueue
|
||||
std::deque<SendEvent_t> m_SendQueue GUARDED_BY(m_SendQueueMutex);
|
||||
|
|
|
@ -96,27 +96,27 @@ namespace llarp
|
|||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
std::optional<std::vector<byte_t>>
|
||||
ProtocolMessage::MaybeEncodeAuthInfo() const
|
||||
std::vector<char>
|
||||
ProtocolMessage::EncodeAuthInfo() const
|
||||
{
|
||||
std::array<byte_t, 1024> info;
|
||||
llarp_buffer_t buf{info};
|
||||
if (not bencode_start_dict(&buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
if (not BEncodeWriteDictInt("a", proto, &buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
if (not BEncodeWriteDictEntry("i", introReply, &buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
if (not BEncodeWriteDictEntry("s", sender, &buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
if (not BEncodeWriteDictEntry("t", tag, &buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
if (not BEncodeWriteDictInt("v", version, &buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
if (not bencode_end(&buf))
|
||||
return std::nullopt;
|
||||
throw std::runtime_error("impossibly small buffer");
|
||||
const std::size_t encodedSize = buf.cur - buf.base;
|
||||
std::vector<byte_t> data;
|
||||
std::vector<char> data;
|
||||
data.resize(encodedSize);
|
||||
std::copy_n(buf.base, encodedSize, data.data());
|
||||
return data;
|
||||
|
|
|
@ -50,9 +50,9 @@ namespace llarp
|
|||
uint64_t seqno = 0;
|
||||
uint64_t version = LLARP_PROTO_VERSION;
|
||||
|
||||
/// maybe encode metainfo for lmq endpoint auth
|
||||
std::optional<std::vector<byte_t>>
|
||||
MaybeEncodeAuthInfo() const;
|
||||
/// encode metainfo for lmq endpoint auth
|
||||
std::vector<char>
|
||||
EncodeAuthInfo() const;
|
||||
|
||||
bool
|
||||
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val);
|
||||
|
|
|
@ -114,6 +114,14 @@ namespace llarp
|
|||
if (lastGoodSend != 0s)
|
||||
{
|
||||
EncryptAndSendTo(data, protocol);
|
||||
return;
|
||||
}
|
||||
const auto maybe = m_Endpoint->MaybeGetAuthInfoForEndpoint(remoteIdent.Addr());
|
||||
if (maybe.has_value())
|
||||
{
|
||||
// send auth message
|
||||
const llarp_buffer_t authdata(maybe->token);
|
||||
AsyncGenIntro(authdata, eProtocolAuth);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <type_traits>
|
||||
#include <limits>
|
||||
#include <net/uint128.hpp>
|
||||
#include <net/net_int.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -27,6 +28,34 @@ namespace llarp
|
|||
return count_bits(i.upper) + count_bits(i.lower);
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr std::size_t
|
||||
count_bits(huint32_t x)
|
||||
{
|
||||
return count_bits(x.h);
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr std::size_t
|
||||
count_bits(nuint32_t x)
|
||||
{
|
||||
return count_bits(x.n);
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr std::size_t
|
||||
count_bits(huint128_t x)
|
||||
{
|
||||
return count_bits_128(x.h);
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr std::size_t
|
||||
count_bits(nuint128_t x)
|
||||
{
|
||||
return count_bits_128(x.n);
|
||||
}
|
||||
|
||||
template <typename InputIt>
|
||||
constexpr std::size_t
|
||||
count_array_bits_impl(InputIt begin, InputIt end)
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
#include <net/net_int.hpp>
|
||||
#include <net/ip.hpp>
|
||||
#include <net/ip_range.hpp>
|
||||
#include <net/net.hpp>
|
||||
|
||||
struct TestNet : public ::testing::Test
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
TEST_F(TestNet, TestIn6AddrFromString)
|
||||
{
|
||||
llarp::huint128_t ip;
|
||||
|
@ -34,30 +34,29 @@ TEST_F(TestNet, TestIn6AddrToHUInt)
|
|||
{
|
||||
llarp::huint128_t huint_parsed = {0};
|
||||
ASSERT_TRUE(huint_parsed.FromString("fd00::1"));
|
||||
in6_addr addr = { { { 0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
|
||||
in6_addr addr = {{{0xfd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01}}};
|
||||
auto huint = llarp::net::In6ToHUInt(addr);
|
||||
ASSERT_EQ(huint, huint_parsed);
|
||||
huint_parsed.h ++;
|
||||
huint_parsed.h++;
|
||||
ASSERT_NE(huint, huint_parsed);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestNet, TestRangeContains8)
|
||||
{
|
||||
ASSERT_TRUE(llarp::IPRange::FromIPv4(10, 0, 0, 1, 8)
|
||||
.ContainsV4(llarp::ipaddr_ipv4_bits(10, 40, 11, 6)));
|
||||
ASSERT_TRUE(
|
||||
llarp::IPRange::FromIPv4(10, 0, 0, 1, 8).Contains(llarp::ipaddr_ipv4_bits(10, 40, 11, 6)));
|
||||
}
|
||||
|
||||
TEST_F(TestNet, TestRangeContains24)
|
||||
{
|
||||
ASSERT_TRUE(llarp::IPRange::FromIPv4(10, 200, 0, 1, 24)
|
||||
.ContainsV4(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
|
||||
.Contains(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
|
||||
}
|
||||
|
||||
TEST_F(TestNet, TestRangeContainsFail)
|
||||
{
|
||||
ASSERT_TRUE(!llarp::IPRange::FromIPv4(192, 168, 0, 1, 24)
|
||||
.ContainsV4(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
|
||||
.Contains(llarp::ipaddr_ipv4_bits(10, 200, 0, 253)));
|
||||
}
|
||||
|
||||
TEST_F(TestNet, TestIPv4Netmask)
|
||||
|
|
Loading…
Reference in New Issue