mirror of https://github.com/oxen-io/lokinet
initial commit for #1595
This commit is contained in:
parent
545021aa3d
commit
fedc56e3f1
|
@ -154,6 +154,7 @@ add_library(liblokinet
|
|||
messages/relay_status.cpp
|
||||
net/address_info.cpp
|
||||
net/exit_info.cpp
|
||||
net/traffic_policy.cpp
|
||||
nodedb.cpp
|
||||
path/ihophandler.cpp
|
||||
path/path_context.cpp
|
||||
|
@ -212,6 +213,7 @@ add_library(liblokinet
|
|||
service/name.cpp
|
||||
service/outbound_context.cpp
|
||||
service/protocol.cpp
|
||||
service/protocol_type.cpp
|
||||
service/router_lookup_job.cpp
|
||||
service/sendcontext.cpp
|
||||
service/session.cpp
|
||||
|
|
|
@ -415,19 +415,25 @@ namespace llarp
|
|||
"on the server and may pose liability concerns. Enable at your own risk.",
|
||||
});
|
||||
|
||||
// TODO: not implemented yet!
|
||||
// TODO: define the order of precedence (e.g. is whitelist applied before blacklist?)
|
||||
// additionally, what's default? What if I don't whitelist anything?
|
||||
/*
|
||||
conf.defineOption<std::string>("network", "exit-whitelist", MultiValue, Comment{
|
||||
"List of destination protocol:port pairs to whitelist, example: udp:*",
|
||||
"or tcp:80. Multiple values supported.",
|
||||
}, FIXME-acceptor);
|
||||
conf.defineOption<std::string>(
|
||||
"network",
|
||||
"traffic-whitelist",
|
||||
MultiValue,
|
||||
Comment{
|
||||
"List of ip traffic whitelist, anything not specified will be dropped by us."
|
||||
"examples:",
|
||||
"tcp for all tcp traffic regardless of port",
|
||||
"0x69 for all packets using ip protocol 0x69"
|
||||
"udp/53 for udp port 53",
|
||||
"tcp/smtp for smtp port",
|
||||
},
|
||||
[this](std::string arg) {
|
||||
if (not m_TrafficPolicy)
|
||||
m_TrafficPolicy = net::TrafficPolicy{};
|
||||
|
||||
conf.defineOption<std::string>("network", "exit-blacklist", MultiValue, Comment{
|
||||
"Blacklist of destinations (same format as whitelist).",
|
||||
}, FIXME-acceptor);
|
||||
*/
|
||||
// this will throw on error
|
||||
m_TrafficPolicy->protocols.emplace(arg);
|
||||
});
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"network",
|
||||
|
|
|
@ -122,6 +122,9 @@ namespace llarp
|
|||
|
||||
std::optional<huint128_t> m_baseV6Address;
|
||||
|
||||
std::set<IPRange> m_AdvertisedRanges;
|
||||
std::optional<net::TrafficPolicy> m_TrafficPolicy;
|
||||
|
||||
// TODO:
|
||||
// on-up
|
||||
// on-down
|
||||
|
|
|
@ -158,56 +158,6 @@ namespace llarp
|
|||
m_AuthPolicy = std::move(auth);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: reinstate this option (it's not even clear what section this came from...)
|
||||
*
|
||||
if (k == "isolate-network" && IsTrueValue(v.c_str()))
|
||||
{
|
||||
#if defined(__linux__)
|
||||
LogInfo(Name(), " isolating network...");
|
||||
if (!SpawnIsolatedNetwork())
|
||||
{
|
||||
LogError(Name(), " failed to spawn isolated network");
|
||||
return false;
|
||||
}
|
||||
LogInfo(Name(), " booyeah network isolation succeeded");
|
||||
return true;
|
||||
#else
|
||||
LogError(Name(), " network isolation is not supported on your platform");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: this is currently defined for [router] / RouterConfig, but is clearly an [endpoint]
|
||||
* option. either move it to [endpoint] or plumb RouterConfig through
|
||||
*
|
||||
if (k == "strict-connect")
|
||||
{
|
||||
RouterID connect;
|
||||
if (!connect.FromString(v))
|
||||
{
|
||||
LogError(Name(), " invalid snode for strict-connect: ", v);
|
||||
return false;
|
||||
}
|
||||
|
||||
RouterContact rc;
|
||||
if (!m_router->nodedb()->Get(connect, rc))
|
||||
{
|
||||
LogError(
|
||||
Name(), " we don't have the RC for ", v, " so we can't use it in strict-connect");
|
||||
return false;
|
||||
}
|
||||
for (const auto& ai : rc.addrs)
|
||||
{
|
||||
m_StrictConnectAddrs.emplace_back(ai);
|
||||
LogInfo(Name(), " added ", m_StrictConnectAddrs.back(), " to strict connect");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
m_LocalResolverAddr = dnsConf.m_bind;
|
||||
m_UpstreamResolvers = dnsConf.m_upstreamDNS;
|
||||
|
||||
|
@ -916,34 +866,46 @@ namespace llarp
|
|||
auto itr = m_IPToAddr.find(dst);
|
||||
if (itr == m_IPToAddr.end())
|
||||
{
|
||||
const auto exits = m_ExitMap.FindAll(dst);
|
||||
if (IsBogon(dst) or exits.empty())
|
||||
// find all ranges that match the destination ip
|
||||
const auto exitEntries = m_ExitMap.FindAllEntries(dst);
|
||||
if (exitEntries.empty())
|
||||
{
|
||||
// send icmp unreachable
|
||||
const auto icmp = pkt.MakeICMPUnreachable();
|
||||
if (icmp.has_value())
|
||||
// send icmp unreachable as we dont have any exits for this ip
|
||||
if (const auto icmp = pkt.MakeICMPUnreachable())
|
||||
{
|
||||
HandleWriteIPPacket(icmp->ConstBuffer(), dst, src, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
service::Address addr{};
|
||||
for (const auto& [range, exitAddr] : exitEntries)
|
||||
{
|
||||
const auto addr = *exits.begin();
|
||||
if (addr.IsZero()) // drop
|
||||
return;
|
||||
pkt.ZeroSourceAddress();
|
||||
MarkAddressOutbound(addr);
|
||||
EnsurePathToService(
|
||||
addr,
|
||||
[addr, pkt, self = this](service::Address, service::OutboundContext* ctx) {
|
||||
if (ctx)
|
||||
{
|
||||
ctx->sendTimeout = 5s;
|
||||
}
|
||||
self->SendToOrQueue(addr, pkt.ConstBuffer(), service::ProtocolType::Exit);
|
||||
},
|
||||
1s);
|
||||
if (range.BogonRange() and range.Contains(dst))
|
||||
{
|
||||
// we permit this because it matches our rules and we allow bogons
|
||||
addr = exitAddr;
|
||||
}
|
||||
else if (not IsBogon(dst))
|
||||
{
|
||||
// allow because the destination is not a bogon and the mapped range is not a bogon
|
||||
addr = exitAddr;
|
||||
}
|
||||
// we do not permit bogons when they don't explicitly match a permitted bogon range
|
||||
}
|
||||
if (addr.IsZero()) // drop becase no exit was found that matches our rules
|
||||
return;
|
||||
pkt.ZeroSourceAddress();
|
||||
MarkAddressOutbound(addr);
|
||||
EnsurePathToService(
|
||||
addr,
|
||||
[addr, pkt, self = this](service::Address, service::OutboundContext* ctx) {
|
||||
if (ctx)
|
||||
{
|
||||
ctx->sendTimeout = 5s;
|
||||
}
|
||||
self->SendToOrQueue(addr, pkt.ConstBuffer(), service::ProtocolType::Exit);
|
||||
},
|
||||
1s);
|
||||
return;
|
||||
}
|
||||
bool rewriteAddrs = true;
|
||||
|
@ -979,6 +941,18 @@ namespace llarp
|
|||
});
|
||||
}
|
||||
|
||||
bool
|
||||
TunEndpoint::ShouldAllowTraffic(const net::IPPacket& pkt) const
|
||||
{
|
||||
if (const auto exitPolicy = GetExitPolicy())
|
||||
{
|
||||
if (not exitPolicy->AllowsTraffic(pkt))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TunEndpoint::HandleInboundPacket(
|
||||
const service::ConvoTag tag,
|
||||
|
@ -1024,6 +998,11 @@ namespace llarp
|
|||
if (m_state->m_ExitEnabled)
|
||||
{
|
||||
// exit side from exit
|
||||
|
||||
// check packet against exit policy and if as needed
|
||||
if (not ShouldAllowTraffic(pkt))
|
||||
return false;
|
||||
|
||||
src = ObtainIPForAddr(addr);
|
||||
if (t == service::ProtocolType::Exit)
|
||||
{
|
||||
|
@ -1055,18 +1034,22 @@ namespace llarp
|
|||
src = pkt.srcv6();
|
||||
}
|
||||
// find what exit we think this should be for
|
||||
const auto mapped = m_ExitMap.FindAll(src);
|
||||
if (IsBogon(src))
|
||||
return false;
|
||||
|
||||
if (const auto ptr = std::get_if<service::Address>(&addr))
|
||||
const auto mapped = m_ExitMap.FindAllEntries(src);
|
||||
bool allow = false;
|
||||
for (const auto& [range, exitAddr] : mapped)
|
||||
{
|
||||
if (mapped.count(*ptr) == 0)
|
||||
if ((range.BogonRange() and range.Contains(src)) or not IsBogon(src))
|
||||
{
|
||||
// we got exit traffic from someone who we should not have gotten it from
|
||||
return false;
|
||||
// this range is either not a bogon or is a bogon we are explicitly allowing
|
||||
if (const auto* ptr = std::get_if<service::Address>(&addr))
|
||||
{
|
||||
// allow if this address matches the endpoint we think it should be
|
||||
allow = exitAddr == *ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (not allow)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -123,6 +123,18 @@ namespace llarp
|
|||
bool
|
||||
HasLocalIP(const huint128_t& ip) const;
|
||||
|
||||
std::optional<net::TrafficPolicy>
|
||||
GetExitPolicy() const override
|
||||
{
|
||||
return m_ExitPolicy;
|
||||
}
|
||||
|
||||
/// ip packet against any exit policies we have
|
||||
/// returns false if this traffic is disallowed by any of those policies
|
||||
/// returns true otherwise
|
||||
bool
|
||||
ShouldAllowTraffic(const net::IPPacket& pkt) const;
|
||||
|
||||
/// get a key for ip address
|
||||
std::optional<std::variant<service::Address, RouterID>>
|
||||
ObtainAddrForIP(huint128_t ip) const override;
|
||||
|
@ -245,6 +257,8 @@ namespace llarp
|
|||
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
|
||||
|
||||
std::unique_ptr<vpn::PacketRouter> m_PacketRouter;
|
||||
|
||||
std::optional<net::TrafficPolicy> m_ExitPolicy;
|
||||
};
|
||||
|
||||
} // namespace handlers
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
/// Exit info model
|
||||
namespace llarp
|
||||
{
|
||||
/// deprecated don't use me , this is only for backwards compat
|
||||
struct ExitInfo
|
||||
{
|
||||
IpAddress ipAddress;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/endian.hpp>
|
||||
#include <llarp/util/mem.hpp>
|
||||
|
||||
#include <llarp/util/str.hpp>
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
@ -32,6 +32,33 @@ namespace llarp
|
|||
{
|
||||
namespace net
|
||||
{
|
||||
std::string
|
||||
IPProtocolName(IPProtocol proto)
|
||||
{
|
||||
if (const auto* ent = ::getprotobynumber(static_cast<uint8_t>(proto)))
|
||||
{
|
||||
return ent->p_name;
|
||||
}
|
||||
throw std::invalid_argument{
|
||||
"cannot determine protocol name for ip proto '" + std::to_string(static_cast<int>(proto))
|
||||
+ "'"};
|
||||
}
|
||||
|
||||
IPProtocol
|
||||
ParseIPProtocol(std::string data)
|
||||
{
|
||||
if (const auto* ent = ::getprotobyname(data.c_str()))
|
||||
{
|
||||
return static_cast<IPProtocol>(ent->p_proto);
|
||||
}
|
||||
if (starts_with(data, "0x"))
|
||||
{
|
||||
if (const int intVal = std::stoi(data.substr(2), nullptr, 16); intVal > 0)
|
||||
return static_cast<IPProtocol>(intVal);
|
||||
}
|
||||
throw std::invalid_argument{"no such ip protocol: '" + data + "'"};
|
||||
}
|
||||
|
||||
inline static uint32_t*
|
||||
in6_uint32_ptr(in6_addr& addr)
|
||||
{
|
||||
|
@ -88,6 +115,19 @@ namespace llarp
|
|||
return ManagedBuffer(b);
|
||||
}
|
||||
|
||||
std::optional<nuint16_t>
|
||||
IPPacket::DstPort() const
|
||||
{
|
||||
switch (IPProtocol{Header()->protocol})
|
||||
{
|
||||
case IPProtocol::TCP:
|
||||
case IPProtocol::UDP:
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(buf + (Header()->ihl * 4) + 2)};
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
huint32_t
|
||||
IPPacket::srcv4() const
|
||||
{
|
||||
|
|
|
@ -112,6 +112,31 @@ namespace llarp
|
|||
{
|
||||
namespace net
|
||||
{
|
||||
/// "well known" ip protocols
|
||||
/// TODO: extend this to non "well known values"
|
||||
enum class IPProtocol : uint8_t
|
||||
{
|
||||
ICMP = 0x01,
|
||||
IGMP = 0x02,
|
||||
IPIP = 0x04,
|
||||
TCP = 0x06,
|
||||
UDP = 0x11,
|
||||
GRE = 0x2F,
|
||||
ICMP6 = 0x3A,
|
||||
OSFP = 0x59,
|
||||
PGM = 0x71,
|
||||
};
|
||||
|
||||
/// get string representation of this protocol
|
||||
/// throws std::invalid_argument if we don't know the name of this ip protocol
|
||||
std::string
|
||||
IPProtocolName(IPProtocol proto);
|
||||
|
||||
/// parse a string to an ip protocol
|
||||
/// throws std::invalid_argument if cannot be parsed
|
||||
IPProtocol
|
||||
ParseIPProtocol(std::string data);
|
||||
|
||||
/// an Packet
|
||||
struct IPPacket
|
||||
{
|
||||
|
@ -264,6 +289,10 @@ namespace llarp
|
|||
huint128_t
|
||||
dst4to6Lan() const;
|
||||
|
||||
/// get destination port if applicable
|
||||
std::optional<nuint16_t>
|
||||
DstPort() const;
|
||||
|
||||
void
|
||||
UpdateIPv4Address(nuint32_t src, nuint32_t dst);
|
||||
|
||||
|
|
|
@ -1,7 +1,38 @@
|
|||
#include "ip_range.hpp"
|
||||
|
||||
#include "oxenmq/bt_serialize.h"
|
||||
|
||||
#include "llarp/util/bencode.h"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
bool
|
||||
IPRange::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
const auto str = oxenmq::bt_serialize(ToString());
|
||||
return buf->write(str.begin(), str.end());
|
||||
}
|
||||
|
||||
bool
|
||||
IPRange::BDecode(llarp_buffer_t* buf)
|
||||
{
|
||||
const auto* start = buf->cur;
|
||||
if (not bencode_discard(buf))
|
||||
return false;
|
||||
std::string_view data{
|
||||
reinterpret_cast<const char*>(start), static_cast<size_t>(buf->cur - start)};
|
||||
std::string str;
|
||||
try
|
||||
{
|
||||
oxenmq::bt_deserialize(data, str);
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return FromString(str);
|
||||
}
|
||||
|
||||
bool
|
||||
IPRange::FromString(std::string str)
|
||||
{
|
||||
|
|
|
@ -3,11 +3,16 @@
|
|||
#include "ip.hpp"
|
||||
#include "net_bits.hpp"
|
||||
#include <llarp/util/bits.hpp>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/types.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/// forward declare
|
||||
bool
|
||||
IsBogon(huint128_t ip);
|
||||
|
||||
struct IPRange
|
||||
{
|
||||
using Addr_t = huint128_t;
|
||||
|
@ -34,6 +39,19 @@ namespace llarp
|
|||
return ipv4_map.Contains(addr);
|
||||
}
|
||||
|
||||
/// return true if we intersect with a bogon range
|
||||
constexpr bool
|
||||
BogonRange() const
|
||||
{
|
||||
// special case for 0.0.0.0/0
|
||||
if (IsV4() and netmask_bits == netmask_ipv6_bits(96))
|
||||
return false;
|
||||
// special case for ::/0
|
||||
if (netmask_bits == huint128_t{0})
|
||||
return false;
|
||||
return IsBogon(addr) or IsBogon(HighestAddr());
|
||||
}
|
||||
|
||||
/// return the number of bits set in the hostmask
|
||||
constexpr int
|
||||
HostmaskBits() const
|
||||
|
@ -106,6 +124,12 @@ namespace llarp
|
|||
|
||||
bool
|
||||
FromString(std::string str);
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t* buf) const;
|
||||
|
||||
bool
|
||||
BDecode(llarp_buffer_t* buf);
|
||||
};
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "ip_range.hpp"
|
||||
#include <llarp/util/status.hpp>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -19,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::list<Entry_t>;
|
||||
using Container_t = std::vector<Entry_t>;
|
||||
|
||||
/// get a set of all values
|
||||
std::set<Value_t>
|
||||
|
@ -89,15 +89,15 @@ namespace llarp
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// return a set of all values who's range contains this IP
|
||||
std::set<Value_t>
|
||||
FindAll(const IP_t& addr) const
|
||||
/// return a set of all entries who's range contains this IP
|
||||
std::set<Entry_t>
|
||||
FindAllEntries(const IP_t& addr) const
|
||||
{
|
||||
std::set<Value_t> found;
|
||||
std::set<Entry_t> found;
|
||||
for (const auto& entry : m_Entries)
|
||||
{
|
||||
if (entry.first.Contains(addr))
|
||||
found.insert(entry.second);
|
||||
found.insert(entry);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
@ -114,8 +114,7 @@ namespace llarp
|
|||
void
|
||||
Insert(const Range_t& addr, const Value_t& val)
|
||||
{
|
||||
m_Entries.emplace_front(addr, val);
|
||||
m_Entries.sort(CompareEntry{});
|
||||
m_Entries.emplace_back(addr, val);
|
||||
}
|
||||
|
||||
template <typename Visit_t>
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
#include "traffic_policy.hpp"
|
||||
#include "llarp/util/str.hpp"
|
||||
|
||||
namespace llarp::net
|
||||
{
|
||||
ProtocolInfo::ProtocolInfo(std::string_view data)
|
||||
{
|
||||
const auto parts = split(data, "/");
|
||||
protocol = ParseIPProtocol(std::string{parts[0]});
|
||||
if (parts.size() == 2)
|
||||
{
|
||||
huint16_t portHost{};
|
||||
std::string portStr{parts[1]};
|
||||
std::string protoName = IPProtocolName(protocol);
|
||||
if (const auto* serv = ::getservbyname(portStr.c_str(), protoName.c_str()))
|
||||
{
|
||||
portHost.h = serv->s_port;
|
||||
}
|
||||
else if (const auto portInt = std::stoi(portStr); portInt > 0)
|
||||
{
|
||||
portHost.h = portInt;
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument{"invalid port in protocol info: " + portStr};
|
||||
port = ToNet(portHost);
|
||||
}
|
||||
else
|
||||
port = std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtocolInfo::MatchesPacket(const IPPacket& pkt) const
|
||||
{
|
||||
if (pkt.Header()->protocol != static_cast<std::underlying_type_t<IPProtocol>>(protocol))
|
||||
return false;
|
||||
|
||||
if (not port)
|
||||
return true;
|
||||
if (const auto maybe = pkt.DstPort())
|
||||
{
|
||||
return *port == *maybe;
|
||||
}
|
||||
// we can't tell what the port is but the protocol matches and that's good enough
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TrafficPolicy::AllowsTraffic(const IPPacket& pkt) const
|
||||
{
|
||||
if (protocols.empty() and ranges.empty())
|
||||
return true;
|
||||
|
||||
for (const auto& proto : protocols)
|
||||
{
|
||||
if (proto.MatchesPacket(pkt))
|
||||
return true;
|
||||
}
|
||||
for (const auto& range : ranges)
|
||||
{
|
||||
huint128_t dst;
|
||||
if (pkt.IsV6())
|
||||
dst = pkt.dstv6();
|
||||
else if (pkt.IsV4())
|
||||
dst = pkt.dst4to6();
|
||||
else
|
||||
return false;
|
||||
if (range.Contains(dst))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtocolInfo::BDecode(llarp_buffer_t* buf)
|
||||
{
|
||||
port = std::nullopt;
|
||||
std::vector<uint64_t> vals;
|
||||
if (not bencode_read_list(
|
||||
[&vals](llarp_buffer_t* buf, bool more) {
|
||||
if (more)
|
||||
{
|
||||
uint64_t intval;
|
||||
if (not bencode_read_integer(buf, &intval))
|
||||
return false;
|
||||
vals.push_back(intval);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
buf))
|
||||
return false;
|
||||
if (vals.empty())
|
||||
return false;
|
||||
if (vals.size() >= 1)
|
||||
{
|
||||
if (vals[0] > 255)
|
||||
return false;
|
||||
protocol = static_cast<IPProtocol>(vals[0]);
|
||||
}
|
||||
if (vals.size() >= 2)
|
||||
{
|
||||
if (vals[1] > 65536)
|
||||
return false;
|
||||
port = ToNet(huint16_t{static_cast<uint16_t>(vals[1])});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProtocolInfo::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
if (not bencode_start_list(buf))
|
||||
return false;
|
||||
if (not bencode_write_uint64(buf, static_cast<std::underlying_type_t<IPProtocol>>(protocol)))
|
||||
return false;
|
||||
if (port)
|
||||
{
|
||||
const auto hostint = ToHost(*port);
|
||||
if (not bencode_write_uint64(buf, hostint.h))
|
||||
return false;
|
||||
}
|
||||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
bool
|
||||
TrafficPolicy::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
if (not bencode_start_dict(buf))
|
||||
return false;
|
||||
|
||||
if (not bencode_write_bytestring(buf, "p", 1))
|
||||
return false;
|
||||
|
||||
if (not bencode_start_list(buf))
|
||||
return false;
|
||||
|
||||
for (const auto& item : protocols)
|
||||
{
|
||||
if (not item.BEncode(buf))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not bencode_end(buf))
|
||||
return false;
|
||||
|
||||
if (not bencode_write_bytestring(buf, "r", 1))
|
||||
return false;
|
||||
|
||||
if (not bencode_start_list(buf))
|
||||
return false;
|
||||
|
||||
for (const auto& item : ranges)
|
||||
{
|
||||
if (not item.BEncode(buf))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not bencode_end(buf))
|
||||
return false;
|
||||
|
||||
return bencode_end(buf);
|
||||
}
|
||||
|
||||
bool
|
||||
TrafficPolicy::BDecode(llarp_buffer_t* buf)
|
||||
{
|
||||
return bencode_read_dict(
|
||||
[&](llarp_buffer_t* buffer, llarp_buffer_t* key) -> bool {
|
||||
if (key == nullptr)
|
||||
return true;
|
||||
if (*key == "p")
|
||||
{
|
||||
return BEncodeReadSet(protocols, buffer);
|
||||
}
|
||||
if (*key == "r")
|
||||
{
|
||||
return BEncodeReadSet(ranges, buffer);
|
||||
}
|
||||
return bencode_discard(buffer);
|
||||
},
|
||||
buf);
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
ProtocolInfo::ExtractStatus() const
|
||||
{
|
||||
util::StatusObject status{
|
||||
{"protocol", static_cast<uint>(protocol)},
|
||||
};
|
||||
if (port)
|
||||
status["port"] = ToHost(*port).h;
|
||||
return status;
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
TrafficPolicy::ExtractStatus() const
|
||||
{
|
||||
std::vector<util::StatusObject> rangesStatus;
|
||||
std::transform(
|
||||
ranges.begin(), ranges.end(), std::back_inserter(rangesStatus), [](const auto& range) {
|
||||
return range.ToString();
|
||||
});
|
||||
|
||||
std::vector<util::StatusObject> protosStatus;
|
||||
std::transform(
|
||||
protocols.begin(),
|
||||
protocols.end(),
|
||||
std::back_inserter(protosStatus),
|
||||
[](const auto& proto) { return proto.ExtractStatus(); });
|
||||
|
||||
return util::StatusObject{{"ranges", rangesStatus}, {"protocols", protosStatus}};
|
||||
}
|
||||
|
||||
} // namespace llarp::net
|
|
@ -0,0 +1,70 @@
|
|||
#pragma once
|
||||
|
||||
#include "ip_range.hpp"
|
||||
#include "ip_packet.hpp"
|
||||
#include "llarp/util/status.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace llarp::net
|
||||
{
|
||||
/// information about an IP protocol
|
||||
struct ProtocolInfo
|
||||
{
|
||||
/// ip protocol byte of this protocol
|
||||
IPProtocol protocol;
|
||||
/// the layer 3 port if applicable
|
||||
std::optional<nuint16_t> port;
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t* buf) const;
|
||||
|
||||
bool
|
||||
BDecode(llarp_buffer_t* buf);
|
||||
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
|
||||
/// returns true if an ip packet looks like it matches this protocol info
|
||||
/// returns false otherwise
|
||||
bool
|
||||
MatchesPacket(const IPPacket& pkt) const;
|
||||
|
||||
bool
|
||||
operator<(const ProtocolInfo& other) const
|
||||
{
|
||||
if (port and other.port)
|
||||
{
|
||||
return protocol < other.protocol or *port < *other.port;
|
||||
}
|
||||
return protocol < other.protocol;
|
||||
}
|
||||
|
||||
ProtocolInfo() = default;
|
||||
|
||||
explicit ProtocolInfo(std::string_view spec);
|
||||
};
|
||||
|
||||
/// information about what traffic an endpoint will carry
|
||||
struct TrafficPolicy
|
||||
{
|
||||
/// ranges that are explicitly allowed
|
||||
std::set<IPRange> ranges;
|
||||
|
||||
/// protocols that are explicity allowed
|
||||
std::set<ProtocolInfo> protocols;
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t* buf) const;
|
||||
|
||||
bool
|
||||
BDecode(llarp_buffer_t* buf);
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
|
||||
/// returns true if we allow the traffic in this ip packet
|
||||
/// returns false otherwise
|
||||
bool
|
||||
AllowsTraffic(const IPPacket& pkt) const;
|
||||
};
|
||||
} // namespace llarp::net
|
|
@ -131,6 +131,13 @@ namespace llarp::quic
|
|||
void
|
||||
receive_packet(const service::ConvoTag& tag, const llarp_buffer_t& buf);
|
||||
|
||||
/// return true if we have any listeners added
|
||||
inline bool
|
||||
hasListeners() const
|
||||
{
|
||||
return not incoming_handlers_.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
EndpointBase& service_endpoint_;
|
||||
|
||||
|
|
|
@ -113,12 +113,41 @@ namespace llarp
|
|||
BuildOne();
|
||||
return;
|
||||
}
|
||||
introSet().I.clear();
|
||||
|
||||
// add supported ethertypes
|
||||
if (HasIfAddr())
|
||||
{
|
||||
const auto ourIP = net::HUIntToIn6(GetIfAddr());
|
||||
if (ipv6_is_mapped_ipv4(ourIP))
|
||||
{
|
||||
introSet().supportedProtocols.push_back(ProtocolType::TrafficV4);
|
||||
}
|
||||
else
|
||||
{
|
||||
introSet().supportedProtocols.push_back(ProtocolType::TrafficV6);
|
||||
}
|
||||
|
||||
// exit related stuffo
|
||||
if (m_state->m_ExitEnabled)
|
||||
{
|
||||
introSet().supportedProtocols.push_back(ProtocolType::Exit);
|
||||
introSet().exitTrafficPolicy = GetExitPolicy();
|
||||
introSet().ownedRanges = GetOwnedRanges();
|
||||
}
|
||||
}
|
||||
// add quic ethertype if we have listeners set up
|
||||
if (auto* quic = GetQUICTunnel())
|
||||
{
|
||||
if (quic->hasListeners())
|
||||
introSet().supportedProtocols.push_back(ProtocolType::QUIC);
|
||||
}
|
||||
|
||||
introSet().intros.clear();
|
||||
for (auto& intro : introset)
|
||||
{
|
||||
introSet().I.emplace_back(std::move(intro));
|
||||
introSet().intros.emplace_back(std::move(intro));
|
||||
}
|
||||
if (introSet().I.size() == 0)
|
||||
if (introSet().intros.empty())
|
||||
{
|
||||
LogWarn("not enough intros to publish introset for ", Name());
|
||||
if (ShouldBuildMore(now))
|
||||
|
@ -145,7 +174,7 @@ namespace llarp
|
|||
Endpoint::IsReady() const
|
||||
{
|
||||
const auto now = Now();
|
||||
if (introSet().I.size() == 0)
|
||||
if (introSet().intros.empty())
|
||||
return false;
|
||||
if (introSet().IsExpired(now))
|
||||
return false;
|
||||
|
@ -716,7 +745,7 @@ namespace llarp
|
|||
void
|
||||
Endpoint::PutNewOutboundContext(const service::IntroSet& introset, llarp_time_t left)
|
||||
{
|
||||
Address addr{introset.A.Addr()};
|
||||
Address addr{introset.addressKeys.Addr()};
|
||||
|
||||
auto& remoteSessions = m_state->m_RemoteSessions;
|
||||
auto& serviceLookups = m_state->m_PendingServiceLookups;
|
||||
|
|
|
@ -113,6 +113,22 @@ namespace llarp
|
|||
return {0};
|
||||
}
|
||||
|
||||
/// get the exit policy for our exit if we have one
|
||||
/// override me
|
||||
virtual std::optional<net::TrafficPolicy>
|
||||
GetExitPolicy() const
|
||||
{
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
/// get the ip ranges we claim to own
|
||||
/// override me
|
||||
virtual std::vector<IPRange>
|
||||
GetOwnedRanges() const
|
||||
{
|
||||
return {};
|
||||
};
|
||||
|
||||
virtual void
|
||||
Thaw(){};
|
||||
|
||||
|
|
|
@ -155,31 +155,31 @@ namespace llarp
|
|||
{
|
||||
EncryptedIntroSet encrypted;
|
||||
|
||||
if (other_i.I.size() == 0)
|
||||
return {};
|
||||
IntroSet i(other_i);
|
||||
if (other_i.intros.empty())
|
||||
return std::nullopt;
|
||||
IntroSet i{other_i};
|
||||
encrypted.nounce.Randomize();
|
||||
// set timestamp
|
||||
// TODO: round to nearest 1000 ms
|
||||
i.T = now;
|
||||
i.timestampSignedAt = now;
|
||||
encrypted.signedAt = now;
|
||||
// set service info
|
||||
i.A = pub;
|
||||
i.addressKeys = pub;
|
||||
// set public encryption key
|
||||
i.K = pq_keypair_to_public(pq);
|
||||
i.sntrupKey = pq_keypair_to_public(pq);
|
||||
std::array<byte_t, MAX_INTROSET_SIZE> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
llarp_buffer_t buf{tmp};
|
||||
if (not i.BEncode(&buf))
|
||||
return {};
|
||||
return std::nullopt;
|
||||
// rewind and resize buffer
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
const SharedSecret k(i.A.Addr());
|
||||
const SharedSecret k{i.addressKeys.Addr()};
|
||||
CryptoManager::instance()->xchacha20(buf, k, encrypted.nounce);
|
||||
encrypted.introsetPayload.resize(buf.sz);
|
||||
std::copy_n(buf.base, buf.sz, encrypted.introsetPayload.data());
|
||||
encrypted.introsetPayload = buf.copy();
|
||||
|
||||
if (not encrypted.Sign(derivedSignKey))
|
||||
return {};
|
||||
return std::nullopt;
|
||||
return encrypted;
|
||||
}
|
||||
} // namespace service
|
||||
|
|
|
@ -90,7 +90,7 @@ namespace llarp::service
|
|||
llarp_buffer_t buf(payload);
|
||||
CryptoManager::instance()->xchacha20(buf, k, nounce);
|
||||
if (not i.BDecode(&buf))
|
||||
return std::nullopt;
|
||||
return {};
|
||||
return i;
|
||||
}
|
||||
|
||||
|
@ -139,16 +139,39 @@ namespace llarp::service
|
|||
util::StatusObject
|
||||
IntroSet::ExtractStatus() const
|
||||
{
|
||||
util::StatusObject obj{{"published", to_json(T)}};
|
||||
util::StatusObject obj{{"published", to_json(timestampSignedAt)}};
|
||||
std::vector<util::StatusObject> introsObjs;
|
||||
std::transform(
|
||||
I.begin(),
|
||||
I.end(),
|
||||
intros.begin(),
|
||||
intros.end(),
|
||||
std::back_inserter(introsObjs),
|
||||
[](const auto& intro) -> util::StatusObject { return intro.ExtractStatus(); });
|
||||
obj["intros"] = introsObjs;
|
||||
if (!topic.IsZero())
|
||||
obj["topic"] = topic.ToString();
|
||||
|
||||
std::vector<util::StatusObject> protocols;
|
||||
std::transform(
|
||||
supportedProtocols.begin(),
|
||||
supportedProtocols.end(),
|
||||
std::back_inserter(protocols),
|
||||
[](const auto& proto) -> util::StatusObject {
|
||||
std::stringstream ss;
|
||||
ss << proto;
|
||||
return ss.str();
|
||||
});
|
||||
obj["protos"] = protocols;
|
||||
std::vector<util::StatusObject> ranges;
|
||||
std::transform(
|
||||
ownedRanges.begin(),
|
||||
ownedRanges.end(),
|
||||
std::back_inserter(ranges),
|
||||
[](const auto& range) -> util::StatusObject { return range.ToString(); });
|
||||
|
||||
obj["advertisedRanges"] = ranges;
|
||||
if (exitTrafficPolicy)
|
||||
obj["exitPolicy"] = exitTrafficPolicy->ExtractStatus();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
@ -156,19 +179,49 @@ namespace llarp::service
|
|||
IntroSet::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
bool read = false;
|
||||
if (!BEncodeMaybeReadDictEntry("a", A, read, key, buf))
|
||||
if (!BEncodeMaybeReadDictEntry("a", addressKeys, read, key, buf))
|
||||
return false;
|
||||
|
||||
if (key == "e")
|
||||
{
|
||||
net::TrafficPolicy policy;
|
||||
if (not policy.BDecode(buf))
|
||||
return false;
|
||||
exitTrafficPolicy = policy;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == "i")
|
||||
{
|
||||
return BEncodeReadList(I, buf);
|
||||
return BEncodeReadList(intros, buf);
|
||||
}
|
||||
if (!BEncodeMaybeReadDictEntry("k", K, read, key, buf))
|
||||
if (!BEncodeMaybeReadDictEntry("k", sntrupKey, read, key, buf))
|
||||
return false;
|
||||
|
||||
if (!BEncodeMaybeReadDictEntry("n", topic, read, key, buf))
|
||||
return false;
|
||||
|
||||
if (key == "p")
|
||||
{
|
||||
return bencode_read_list(
|
||||
[&](llarp_buffer_t* buf, bool more) {
|
||||
if (more)
|
||||
{
|
||||
uint64_t protoval;
|
||||
if (not bencode_read_integer(buf, &protoval))
|
||||
return false;
|
||||
supportedProtocols.emplace_back(static_cast<ProtocolType>(protoval));
|
||||
}
|
||||
return true;
|
||||
},
|
||||
buf);
|
||||
}
|
||||
|
||||
if (key == "r")
|
||||
{
|
||||
return BEncodeReadList(ownedRanges, buf);
|
||||
}
|
||||
|
||||
if (key == "s")
|
||||
{
|
||||
byte_t* begin = buf->cur;
|
||||
|
@ -177,8 +230,8 @@ namespace llarp::service
|
|||
|
||||
byte_t* end = buf->cur;
|
||||
|
||||
std::string_view srvString{
|
||||
reinterpret_cast<char*>(begin), static_cast<std::size_t>(end - begin)};
|
||||
std::string_view srvString(
|
||||
reinterpret_cast<const char*>(begin), static_cast<size_t>(end - begin));
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -192,53 +245,71 @@ namespace llarp::service
|
|||
read = true;
|
||||
}
|
||||
|
||||
if (!BEncodeMaybeReadDictInt("t", T, read, key, buf))
|
||||
if (!BEncodeMaybeReadDictInt("t", timestampSignedAt, read, key, buf))
|
||||
return false;
|
||||
|
||||
if (key == "w")
|
||||
{
|
||||
W.emplace();
|
||||
return bencode_decode_dict(*W, buf);
|
||||
}
|
||||
|
||||
if (!BEncodeMaybeReadDictInt("v", version, read, key, buf))
|
||||
return false;
|
||||
|
||||
if (!BEncodeMaybeReadDictEntry("z", Z, read, key, buf))
|
||||
if (!BEncodeMaybeReadDictEntry("z", signature, read, key, buf))
|
||||
return false;
|
||||
|
||||
if (read)
|
||||
return true;
|
||||
|
||||
return bencode_discard(buf);
|
||||
return read or bencode_discard(buf);
|
||||
}
|
||||
|
||||
bool
|
||||
IntroSet::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
if (!bencode_start_dict(buf))
|
||||
if (not bencode_start_dict(buf))
|
||||
return false;
|
||||
if (!BEncodeWriteDictEntry("a", A, buf))
|
||||
if (not BEncodeWriteDictEntry("a", addressKeys, buf))
|
||||
return false;
|
||||
|
||||
// exit policy if applicable
|
||||
if (exitTrafficPolicy)
|
||||
{
|
||||
if (not BEncodeWriteDictEntry("e", *exitTrafficPolicy, buf))
|
||||
return false;
|
||||
}
|
||||
// start introduction list
|
||||
if (!bencode_write_bytestring(buf, "i", 1))
|
||||
if (not bencode_write_bytestring(buf, "i", 1))
|
||||
return false;
|
||||
if (!BEncodeWriteList(I.begin(), I.end(), buf))
|
||||
if (not BEncodeWriteList(intros.begin(), intros.end(), buf))
|
||||
return false;
|
||||
// end introduction list
|
||||
|
||||
// pq pubkey
|
||||
if (!BEncodeWriteDictEntry("k", K, buf))
|
||||
if (not BEncodeWriteDictEntry("k", sntrupKey, buf))
|
||||
return false;
|
||||
|
||||
// topic tag
|
||||
if (topic.ToString().size())
|
||||
if (not topic.ToString().empty())
|
||||
{
|
||||
if (!BEncodeWriteDictEntry("n", topic, buf))
|
||||
if (not BEncodeWriteDictEntry("n", topic, buf))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SRVs.size())
|
||||
// supported ethertypes
|
||||
if (not supportedProtocols.empty())
|
||||
{
|
||||
if (not bencode_write_bytestring(buf, "p", 1))
|
||||
return false;
|
||||
|
||||
if (not bencode_start_list(buf))
|
||||
return false;
|
||||
|
||||
for (const auto& proto : supportedProtocols)
|
||||
{
|
||||
if (not bencode_write_uint64(buf, static_cast<uint64_t>(proto)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not bencode_end(buf))
|
||||
return false;
|
||||
}
|
||||
|
||||
// srv records
|
||||
if (not SRVs.empty())
|
||||
{
|
||||
std::string serial = oxenmq::bt_serialize(SRVs);
|
||||
if (!bencode_write_bytestring(buf, "s", 1))
|
||||
|
@ -247,19 +318,22 @@ namespace llarp::service
|
|||
return false;
|
||||
}
|
||||
|
||||
// Timestamp published
|
||||
if (!BEncodeWriteDictInt("t", T.count(), buf))
|
||||
// owned ranges
|
||||
if (not ownedRanges.empty())
|
||||
{
|
||||
if (not BEncodeWriteDictArray("r", ownedRanges, buf))
|
||||
return false;
|
||||
}
|
||||
|
||||
// timestamp
|
||||
if (!BEncodeWriteDictInt("t", timestampSignedAt.count(), buf))
|
||||
return false;
|
||||
|
||||
// write version
|
||||
if (!BEncodeWriteDictInt("v", version, buf))
|
||||
return false;
|
||||
if (W)
|
||||
{
|
||||
if (!BEncodeWriteDictEntry("w", *W, buf))
|
||||
return false;
|
||||
}
|
||||
if (!BEncodeWriteDictEntry("z", Z, buf))
|
||||
|
||||
if (!BEncodeWriteDictEntry("z", signature, buf))
|
||||
return false;
|
||||
|
||||
return bencode_end(buf);
|
||||
|
@ -268,8 +342,8 @@ namespace llarp::service
|
|||
bool
|
||||
IntroSet::HasExpiredIntros(llarp_time_t now) const
|
||||
{
|
||||
for (const auto& i : I)
|
||||
if (now >= i.expiresAt)
|
||||
for (const auto& intro : intros)
|
||||
if (now >= intro.expiresAt)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -300,10 +374,10 @@ namespace llarp::service
|
|||
IntroSet::Verify(llarp_time_t now) const
|
||||
{
|
||||
std::array<byte_t, MAX_INTROSET_SIZE> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
llarp_buffer_t buf{tmp};
|
||||
IntroSet copy;
|
||||
copy = *this;
|
||||
copy.Z.Zero();
|
||||
copy.signature.Zero();
|
||||
if (!copy.BEncode(&buf))
|
||||
{
|
||||
return false;
|
||||
|
@ -311,57 +385,39 @@ namespace llarp::service
|
|||
// rewind and resize buffer
|
||||
buf.sz = buf.cur - buf.base;
|
||||
buf.cur = buf.base;
|
||||
if (!A.Verify(buf, Z))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// validate PoW
|
||||
if (W && !W->IsValid(now))
|
||||
if (!addressKeys.Verify(buf, signature))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// valid timestamps
|
||||
// add max clock skew
|
||||
now += MAX_INTROSET_TIME_DELTA;
|
||||
for (const auto& intro : I)
|
||||
for (const auto& intro : intros)
|
||||
{
|
||||
if (intro.expiresAt > now && intro.expiresAt - now > path::default_lifetime)
|
||||
{
|
||||
if (!W)
|
||||
{
|
||||
LogWarn("intro has too high expire time");
|
||||
return false;
|
||||
}
|
||||
if (intro.expiresAt - W->extendedLifetime > path::default_lifetime)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (IsExpired(now))
|
||||
{
|
||||
LogWarn("introset expired: ", *this);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return not IsExpired(now);
|
||||
}
|
||||
|
||||
llarp_time_t
|
||||
IntroSet::GetNewestIntroExpiration() const
|
||||
{
|
||||
llarp_time_t t = 0s;
|
||||
for (const auto& intro : I)
|
||||
t = std::max(intro.expiresAt, t);
|
||||
return t;
|
||||
llarp_time_t maxTime = 0s;
|
||||
for (const auto& intro : intros)
|
||||
maxTime = std::max(intro.expiresAt, maxTime);
|
||||
return maxTime;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
IntroSet::print(std::ostream& stream, int level, int spaces) const
|
||||
{
|
||||
Printer printer(stream, level, spaces);
|
||||
printer.printAttribute("A", A);
|
||||
printer.printAttribute("I", I);
|
||||
printer.printAttribute("K", K);
|
||||
printer.printAttribute("addressKeys", addressKeys);
|
||||
printer.printAttribute("intros", intros);
|
||||
printer.printAttribute("sntrupKey", sntrupKey);
|
||||
|
||||
std::string _topic = topic.ToString();
|
||||
|
||||
|
@ -374,17 +430,10 @@ namespace llarp::service
|
|||
printer.printAttribute("topic", topic);
|
||||
}
|
||||
|
||||
printer.printAttribute("T", T.count());
|
||||
if (W)
|
||||
{
|
||||
printer.printAttribute("W", *W);
|
||||
}
|
||||
else
|
||||
{
|
||||
printer.printAttribute("W", "NULL");
|
||||
}
|
||||
printer.printAttribute("V", version);
|
||||
printer.printAttribute("Z", Z);
|
||||
printer.printAttribute("signedAt", timestampSignedAt.count());
|
||||
|
||||
printer.printAttribute("version", version);
|
||||
printer.printAttribute("sig", signature);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
#include "info.hpp"
|
||||
#include "intro.hpp"
|
||||
#include "tag.hpp"
|
||||
#include "protocol_type.hpp"
|
||||
#include <llarp/util/bencode.hpp>
|
||||
#include <llarp/util/time.hpp>
|
||||
#include <llarp/util/status.hpp>
|
||||
#include <llarp/dns/srv_data.hpp>
|
||||
|
||||
#include <llarp/net/ip_range.hpp>
|
||||
#include <llarp/net/traffic_policy.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
@ -26,20 +30,31 @@ namespace llarp
|
|||
|
||||
struct IntroSet
|
||||
{
|
||||
ServiceInfo A;
|
||||
std::vector<Introduction> I;
|
||||
PQPubKey K;
|
||||
ServiceInfo addressKeys;
|
||||
std::vector<Introduction> intros;
|
||||
PQPubKey sntrupKey;
|
||||
Tag topic;
|
||||
std::vector<llarp::dns::SRVTuple> SRVs;
|
||||
llarp_time_t T = 0s;
|
||||
std::optional<PoW> W;
|
||||
Signature Z;
|
||||
llarp_time_t timestampSignedAt = 0s;
|
||||
|
||||
/// ethertypes we advertise that we speak
|
||||
std::vector<ProtocolType> supportedProtocols;
|
||||
/// aonnuce that these ranges are reachable via our endpoint
|
||||
/// only set when we support exit traffic ethertype is supported
|
||||
std::vector<IPRange> ownedRanges;
|
||||
|
||||
/// policies about traffic that we are willing to carry
|
||||
/// a protocol/range whitelist or blacklist
|
||||
/// only set when we support exit traffic ethertype
|
||||
std::optional<net::TrafficPolicy> exitTrafficPolicy;
|
||||
|
||||
Signature signature;
|
||||
uint64_t version = LLARP_PROTO_VERSION;
|
||||
|
||||
bool
|
||||
OtherIsNewer(const IntroSet& other) const
|
||||
{
|
||||
return T < other.T;
|
||||
return timestampSignedAt < other.timestampSignedAt;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
|
@ -82,14 +97,28 @@ namespace llarp
|
|||
inline bool
|
||||
operator<(const IntroSet& lhs, const IntroSet& rhs)
|
||||
{
|
||||
return lhs.A < rhs.A;
|
||||
return lhs.addressKeys < rhs.addressKeys;
|
||||
}
|
||||
|
||||
inline bool
|
||||
operator==(const IntroSet& lhs, const IntroSet& rhs)
|
||||
{
|
||||
return std::tie(lhs.A, lhs.I, lhs.K, lhs.T, lhs.version, lhs.topic, lhs.W, lhs.Z)
|
||||
== std::tie(rhs.A, rhs.I, rhs.K, rhs.T, rhs.version, rhs.topic, rhs.W, rhs.Z);
|
||||
return std::tie(
|
||||
lhs.addressKeys,
|
||||
lhs.intros,
|
||||
lhs.sntrupKey,
|
||||
lhs.timestampSignedAt,
|
||||
lhs.version,
|
||||
lhs.topic,
|
||||
lhs.signature)
|
||||
== std::tie(
|
||||
rhs.addressKeys,
|
||||
rhs.intros,
|
||||
rhs.sntrupKey,
|
||||
rhs.timestampSignedAt,
|
||||
rhs.version,
|
||||
rhs.topic,
|
||||
rhs.signature);
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -56,13 +56,13 @@ namespace llarp
|
|||
|
||||
OutboundContext::OutboundContext(const IntroSet& introset, Endpoint* parent)
|
||||
: path::Builder(parent->Router(), 4, parent->numHops)
|
||||
, SendContext(introset.A, {}, this, parent)
|
||||
, location(introset.A.Addr().ToKey())
|
||||
, SendContext(introset.addressKeys, {}, this, parent)
|
||||
, location(introset.addressKeys.Addr().ToKey())
|
||||
, currentIntroSet(introset)
|
||||
|
||||
{
|
||||
updatingIntroSet = false;
|
||||
for (const auto& intro : introset.I)
|
||||
for (const auto& intro : introset.intros)
|
||||
{
|
||||
if (intro.expiresAt > m_NextIntro.expiresAt)
|
||||
m_NextIntro = intro;
|
||||
|
@ -80,7 +80,7 @@ namespace llarp
|
|||
if (remoteIntro != m_NextIntro)
|
||||
{
|
||||
remoteIntro = m_NextIntro;
|
||||
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.A, false);
|
||||
m_DataHandler->PutSenderFor(currentConvoTag, currentIntroSet.addressKeys, false);
|
||||
m_DataHandler->PutIntroFor(currentConvoTag, remoteIntro);
|
||||
}
|
||||
}
|
||||
|
@ -94,12 +94,12 @@ namespace llarp
|
|||
updatingIntroSet = false;
|
||||
if (foundIntro)
|
||||
{
|
||||
if (foundIntro->T == 0s)
|
||||
if (foundIntro->timestampSignedAt == 0s)
|
||||
{
|
||||
LogWarn(Name(), " got introset with zero timestamp: ", *foundIntro);
|
||||
return true;
|
||||
}
|
||||
if (currentIntroSet.T > foundIntro->T)
|
||||
if (currentIntroSet.timestampSignedAt > foundIntro->timestampSignedAt)
|
||||
{
|
||||
LogInfo("introset is old, dropping");
|
||||
return true;
|
||||
|
@ -136,7 +136,7 @@ namespace llarp
|
|||
{
|
||||
const auto now = Now();
|
||||
Introduction selectedIntro;
|
||||
for (const auto& intro : currentIntroSet.I)
|
||||
for (const auto& intro : currentIntroSet.intros)
|
||||
{
|
||||
if (intro.expiresAt > selectedIntro.expiresAt && intro.router != r)
|
||||
{
|
||||
|
@ -215,7 +215,7 @@ namespace llarp
|
|||
m_Endpoint->Loop(),
|
||||
remoteIdent,
|
||||
m_Endpoint->GetIdentity(),
|
||||
currentIntroSet.K,
|
||||
currentIntroSet.sntrupKey,
|
||||
remoteIntro,
|
||||
m_DataHandler,
|
||||
currentConvoTag,
|
||||
|
@ -236,7 +236,8 @@ namespace llarp
|
|||
std::string
|
||||
OutboundContext::Name() const
|
||||
{
|
||||
return "OBContext:" + m_Endpoint->Name() + "-" + currentIntroSet.A.Addr().ToString();
|
||||
return "OBContext:" + m_Endpoint->Name() + "-"
|
||||
+ currentIntroSet.addressKeys.Addr().ToString();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -244,7 +245,7 @@ namespace llarp
|
|||
{
|
||||
if (updatingIntroSet || markedBad)
|
||||
return;
|
||||
const auto addr = currentIntroSet.A.Addr();
|
||||
const auto addr = currentIntroSet.addressKeys.Addr();
|
||||
// we want to use the parent endpoint's paths because outbound context
|
||||
// does not implement path::PathSet::HandleGotIntroMessage
|
||||
const auto paths = GetManyPathsWithUniqueEndpoints(m_Endpoint, 2);
|
||||
|
@ -453,7 +454,7 @@ namespace llarp
|
|||
if (now - lastShift < MIN_SHIFT_INTERVAL)
|
||||
return false;
|
||||
bool shifted = false;
|
||||
std::vector<Introduction> intros = currentIntroSet.I;
|
||||
std::vector<Introduction> intros = currentIntroSet.intros;
|
||||
if (intros.size() > 1)
|
||||
{
|
||||
std::shuffle(intros.begin(), intros.end(), CSRNG{});
|
||||
|
@ -543,7 +544,7 @@ namespace llarp
|
|||
// hop off it
|
||||
Introduction picked;
|
||||
// get the latest intro that isn't on that endpoint
|
||||
for (const auto& intro : currentIntroSet.I)
|
||||
for (const auto& intro : currentIntroSet.intros)
|
||||
{
|
||||
if (intro.router == endpoint)
|
||||
continue;
|
||||
|
|
|
@ -12,19 +12,6 @@ namespace llarp
|
|||
{
|
||||
namespace service
|
||||
{
|
||||
std::ostream&
|
||||
operator<<(std::ostream& o, ProtocolType t)
|
||||
{
|
||||
return o
|
||||
<< (t == ProtocolType::Control ? "Control"
|
||||
: t == ProtocolType::TrafficV4 ? "TrafficV4"
|
||||
: t == ProtocolType::TrafficV6 ? "TrafficV6"
|
||||
: t == ProtocolType::Exit ? "Exit"
|
||||
: t == ProtocolType::Auth ? "Auth"
|
||||
: t == ProtocolType::QUIC ? "QUIC"
|
||||
: "(unknown-protocol-type)");
|
||||
}
|
||||
|
||||
ProtocolMessage::ProtocolMessage()
|
||||
{
|
||||
tag.Zero();
|
||||
|
|
|
@ -31,9 +31,6 @@ namespace llarp
|
|||
|
||||
constexpr std::size_t MAX_PROTOCOL_MESSAGE_SIZE = 2048 * 2;
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& o, ProtocolType t);
|
||||
|
||||
/// inner message
|
||||
struct ProtocolMessage
|
||||
{
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#include "protocol_type.hpp"
|
||||
|
||||
namespace llarp::service
|
||||
{
|
||||
std::ostream&
|
||||
operator<<(std::ostream& o, ProtocolType t)
|
||||
{
|
||||
return o
|
||||
<< (t == ProtocolType::Control ? "Control"
|
||||
: t == ProtocolType::TrafficV4 ? "TrafficV4"
|
||||
: t == ProtocolType::TrafficV6 ? "TrafficV6"
|
||||
: t == ProtocolType::Exit ? "Exit"
|
||||
: t == ProtocolType::Auth ? "Auth"
|
||||
: t == ProtocolType::QUIC ? "QUIC"
|
||||
: "(unknown-protocol-type)");
|
||||
}
|
||||
|
||||
} // namespace llarp::service
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include <ostream>
|
||||
|
||||
namespace llarp::service
|
||||
{
|
||||
// Supported protocol types; the values are given explicitly because they are specifically used
|
||||
|
@ -14,5 +16,10 @@ namespace llarp::service
|
|||
Exit = 3UL,
|
||||
Auth = 4UL,
|
||||
QUIC = 5UL,
|
||||
|
||||
};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& o, ProtocolType t);
|
||||
|
||||
} // namespace llarp::service
|
||||
|
|
|
@ -260,7 +260,6 @@ namespace llarp
|
|||
},
|
||||
buf);
|
||||
}
|
||||
|
||||
template <typename List_t>
|
||||
bool
|
||||
BEncodeReadList(List_t& result, llarp_buffer_t* buf)
|
||||
|
@ -279,6 +278,46 @@ namespace llarp
|
|||
buf);
|
||||
}
|
||||
|
||||
/// read a std::set of decodable entities and deny duplicates
|
||||
template <typename Set_t>
|
||||
bool
|
||||
BEncodeReadSet(Set_t& set, llarp_buffer_t* buffer)
|
||||
{
|
||||
return bencode_read_list(
|
||||
[&set](llarp_buffer_t* buf, bool more) {
|
||||
if (more)
|
||||
{
|
||||
typename Set_t::value_type item;
|
||||
if (not item.BDecode(buf))
|
||||
return false;
|
||||
// deny duplicates
|
||||
return set.emplace(std::move(item)).second;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
buffer);
|
||||
}
|
||||
|
||||
/// read a std::set of decodable entities and deny duplicates
|
||||
template <typename Set_t>
|
||||
bool
|
||||
BEncodeWriteSet(Set_t& set, llarp_buffer_t* buffer)
|
||||
{
|
||||
return bencode_read_list(
|
||||
[&set](llarp_buffer_t* buf, bool more) {
|
||||
if (more)
|
||||
{
|
||||
typename Set_t::value_type item;
|
||||
if (not item.BDecode(buf))
|
||||
return false;
|
||||
// deny duplicates
|
||||
return set.emplace(std::move(item)).second;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
buffer);
|
||||
}
|
||||
|
||||
template <typename List_t>
|
||||
bool
|
||||
BEncodeWriteDictList(const char* k, List_t& list, llarp_buffer_t* buf)
|
||||
|
|
|
@ -210,19 +210,19 @@ TEST_CASE("Test sign and encrypt introset", "[crypto]")
|
|||
ident.RegenerateKeys();
|
||||
service::Address addr;
|
||||
CHECK(ident.pub.CalculateAddress(addr.as_array()));
|
||||
service::IntroSet I;
|
||||
service::IntroSet introset;
|
||||
auto now = time_now_ms();
|
||||
I.T = now;
|
||||
while(I.I.size() < 10)
|
||||
introset.timestampSignedAt = now;
|
||||
while(introset.intros.size() < 10)
|
||||
{
|
||||
service::Introduction intro;
|
||||
intro.expiresAt = now + (path::default_lifetime / 2);
|
||||
intro.router.Randomize();
|
||||
intro.pathID.Randomize();
|
||||
I.I.emplace_back(std::move(intro));
|
||||
introset.intros.emplace_back(std::move(intro));
|
||||
}
|
||||
|
||||
const auto maybe = ident.EncryptAndSignIntroSet(I, now);
|
||||
const auto maybe = ident.EncryptAndSignIntroSet(introset, now);
|
||||
CHECK(maybe.has_value());
|
||||
CHECK(maybe->Verify(now));
|
||||
PubKey blind_key;
|
||||
|
|
Loading…
Reference in New Issue