mirror of
https://github.com/oxen-io/lokinet.git
synced 2024-11-11 07:10:36 +00:00
d00257b9f0
- straightened out setting of public addr and public port in config vs listen ("bind") addr - fixed small bug in router contact writing and saving
1658 lines
52 KiB
C++
1658 lines
52 KiB
C++
#include "config.hpp"
|
|
|
|
#include "definition.hpp"
|
|
#include "ini.hpp"
|
|
|
|
#include <llarp/constants/files.hpp>
|
|
#include <llarp/constants/platform.hpp>
|
|
#include <llarp/constants/version.hpp>
|
|
#include <llarp/net/ip.hpp>
|
|
#include <llarp/net/net.hpp>
|
|
#include <llarp/net/sock_addr.hpp>
|
|
#include <llarp/router_contact.hpp>
|
|
#include <llarp/service/name.hpp>
|
|
#include <llarp/util/file.hpp>
|
|
#include <llarp/util/logging.hpp>
|
|
#include <llarp/util/str.hpp>
|
|
|
|
#include <stdexcept>
|
|
|
|
namespace llarp
|
|
{
|
|
using namespace config;
|
|
namespace
|
|
{
|
|
struct ConfigGenParameters_impl : public ConfigGenParameters
|
|
{
|
|
const llarp::net::Platform*
|
|
Net_ptr() const override
|
|
{
|
|
return llarp::net::Platform::Default_ptr();
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
void
|
|
RouterConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
constexpr Default DefaultJobQueueSize{1024 * 8};
|
|
constexpr Default DefaultWorkerThreads{0};
|
|
constexpr Default DefaultBlockBogons{true};
|
|
|
|
conf.define_option<int>(
|
|
"router", "job-queue-size", DefaultJobQueueSize, Hidden, [this](int arg) {
|
|
if (arg < 1024)
|
|
throw std::invalid_argument("job-queue-size must be 1024 or greater");
|
|
|
|
job_que_size = arg;
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"router",
|
|
"netid",
|
|
Default{llarp::LOKINET_DEFAULT_NETID},
|
|
Comment{
|
|
"Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID + "' for mainnet, '"s
|
|
+ llarp::LOKINET_TESTNET_NETID + "' for testnet."s,
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.size() > NETID_SIZE)
|
|
throw std::invalid_argument{
|
|
fmt::format("netid is too long, max length is {}", NETID_SIZE)};
|
|
|
|
net_id = std::move(arg);
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"router",
|
|
"relay-connections",
|
|
Default{CLIENT_ROUTER_CONNECTIONS},
|
|
ClientOnly,
|
|
Comment{
|
|
"Minimum number of routers lokinet client will attempt to maintain connections to.",
|
|
},
|
|
[=](int arg) {
|
|
if (arg < CLIENT_ROUTER_CONNECTIONS)
|
|
throw std::invalid_argument{
|
|
fmt::format("Client relay connections must be >= {}", CLIENT_ROUTER_CONNECTIONS)};
|
|
|
|
client_router_connections = arg;
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"router",
|
|
"min-connections",
|
|
Deprecated,
|
|
Comment{
|
|
"Minimum number of routers lokinet will attempt to maintain connections to.",
|
|
},
|
|
[=](int) {
|
|
log::warning(
|
|
logcat, "Router min-connections is deprecated; use relay-connections for clients");
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"router",
|
|
"max-connections",
|
|
Deprecated,
|
|
Comment{
|
|
"Maximum number (hard limit) of routers lokinet will be connected to at any time.",
|
|
},
|
|
[=](int) {
|
|
log::warning(
|
|
logcat, "Router max-connections is deprecated; use relay-connections for clients");
|
|
});
|
|
|
|
conf.define_option<std::string>("router", "nickname", Deprecated);
|
|
|
|
conf.define_option<fs::path>(
|
|
"router",
|
|
"data-dir",
|
|
Default{params.default_data_dir},
|
|
Comment{
|
|
"Optional directory for containing lokinet runtime data. This includes generated",
|
|
"private keys.",
|
|
},
|
|
[this](fs::path arg) {
|
|
if (arg.empty())
|
|
throw std::invalid_argument("[router]:data-dir is empty");
|
|
if (not fs::exists(arg))
|
|
throw std::runtime_error{
|
|
fmt::format("Specified [router]:data-dir {} does not exist", arg)};
|
|
|
|
data_dir = std::move(arg);
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"router",
|
|
"public-ip",
|
|
RelayOnly,
|
|
Comment{
|
|
"For complex network configurations where the detected IP is incorrect or non-public",
|
|
"this setting specifies the public IP at which this router is reachable. When",
|
|
"provided the public-port option must also be specified.",
|
|
},
|
|
[this](std::string arg) { public_ip = std::move(arg); });
|
|
|
|
conf.define_option<std::string>("router", "public-address", Hidden, [](std::string) {
|
|
throw std::invalid_argument{
|
|
"[router]:public-address option no longer supported, use [router]:public-ip and "
|
|
"[router]:public-port instead"};
|
|
});
|
|
|
|
conf.define_option<uint16_t>(
|
|
"router",
|
|
"public-port",
|
|
RelayOnly,
|
|
Comment{
|
|
"When specifying public-ip=, this specifies the public UDP port at which this lokinet",
|
|
"router is reachable. Required when public-ip is used.",
|
|
},
|
|
[this](uint16_t arg) {
|
|
if (arg <= 0 || arg > std::numeric_limits<uint16_t>::max())
|
|
throw std::invalid_argument("public-port must be >= 0 and <= 65536");
|
|
public_port = arg;
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"router",
|
|
"worker-threads",
|
|
DefaultWorkerThreads,
|
|
Comment{
|
|
"The number of threads available for performing cryptographic functions.",
|
|
"The minimum is one thread, but network performance may increase with more.",
|
|
"threads. Should not exceed the number of logical CPU cores.",
|
|
"0 means use the number of logical CPU cores detected at startup.",
|
|
},
|
|
[this](int arg) {
|
|
if (arg < 0)
|
|
throw std::invalid_argument("worker-threads must be >= 0");
|
|
|
|
worker_threads = arg;
|
|
});
|
|
|
|
// Hidden option because this isn't something that should ever be turned off occasionally when
|
|
// doing dev/testing work.
|
|
conf.define_option<bool>(
|
|
"router", "block-bogons", DefaultBlockBogons, Hidden, assignment_acceptor(block_bogons));
|
|
|
|
constexpr auto relative_to_datadir =
|
|
"An absolute path is used as-is, otherwise relative to 'data-dir'.";
|
|
|
|
conf.define_option<std::string>(
|
|
"router",
|
|
"contact-file",
|
|
RelayOnly,
|
|
Default{llarp::our_rc_filename},
|
|
assignment_acceptor(rc_file),
|
|
Comment{
|
|
"Filename in which to store the router contact file",
|
|
relative_to_datadir,
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"router",
|
|
"encryption-privkey",
|
|
RelayOnly,
|
|
Default{llarp::our_enc_key_filename},
|
|
assignment_acceptor(enckey_file),
|
|
Comment{
|
|
"Filename in which to store the encryption private key",
|
|
relative_to_datadir,
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"router",
|
|
"ident-privkey",
|
|
RelayOnly,
|
|
Default{llarp::our_identity_filename},
|
|
assignment_acceptor(idkey_file),
|
|
Comment{
|
|
"Filename in which to store the identity private key",
|
|
relative_to_datadir,
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"router",
|
|
"transport-privkey",
|
|
RelayOnly,
|
|
Default{llarp::our_transport_key_filename},
|
|
assignment_acceptor(transkey_file),
|
|
Comment{
|
|
"Filename in which to store the transport private key.",
|
|
relative_to_datadir,
|
|
});
|
|
|
|
// Deprecated options:
|
|
|
|
// these weren't even ever used!
|
|
conf.define_option<std::string>("router", "max-routers", Deprecated);
|
|
conf.define_option<std::string>("router", "min-routers", Deprecated);
|
|
|
|
// TODO: this may have been a synonym for [router]worker-threads
|
|
conf.define_option<std::string>("router", "threads", Deprecated);
|
|
conf.define_option<std::string>("router", "net-threads", Deprecated);
|
|
|
|
is_relay = params.is_relay;
|
|
}
|
|
|
|
void
|
|
NetworkConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
|
|
static constexpr Default ProfilingValueDefault{true};
|
|
static constexpr Default SaveProfilesDefault{true};
|
|
static constexpr Default ReachableDefault{true};
|
|
static constexpr Default HopsDefault{4};
|
|
static constexpr Default PathsDefault{6};
|
|
static constexpr Default IP6RangeDefault{"fd00::"};
|
|
|
|
conf.define_option<std::string>(
|
|
"network", "type", Default{"tun"}, Hidden, assignment_acceptor(endpoint_type));
|
|
|
|
conf.define_option<bool>(
|
|
"network",
|
|
"save-profiles",
|
|
SaveProfilesDefault,
|
|
Hidden,
|
|
assignment_acceptor(save_profiles));
|
|
|
|
conf.define_option<bool>(
|
|
"network",
|
|
"profiling",
|
|
ProfilingValueDefault,
|
|
Hidden,
|
|
assignment_acceptor(enable_profiling));
|
|
|
|
conf.define_option<std::string>("network", "profiles", Deprecated);
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"strict-connect",
|
|
ClientOnly,
|
|
MultiValue,
|
|
[this](std::string value) {
|
|
RouterID router;
|
|
if (not router.from_string(value))
|
|
throw std::invalid_argument{"bad snode value: " + value};
|
|
if (not strict_connect.insert(router).second)
|
|
throw std::invalid_argument{"duplicate strict connect snode: " + value};
|
|
},
|
|
Comment{
|
|
"Public keys of routers which will act as pinned first-hops. This may be used to",
|
|
"provide a trusted router (consider that you are not fully anonymous with your",
|
|
"first hop). This REQUIRES two or more nodes to be specified.",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"keyfile",
|
|
ClientOnly,
|
|
assignment_acceptor(keyfile),
|
|
Comment{
|
|
"The private key to persist address with. If not specified the address will be",
|
|
"ephemeral.",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"auth",
|
|
ClientOnly,
|
|
Comment{
|
|
"Set the endpoint authentication mechanism.",
|
|
"none/whitelist/lmq/file",
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
return;
|
|
auth_type = service::parse_auth_type(arg);
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"auth-lmq",
|
|
ClientOnly,
|
|
assignment_acceptor(auth_url),
|
|
Comment{
|
|
"lmq endpoint to talk to for authenticating new sessions",
|
|
"ipc:///var/lib/lokinet/auth.socket",
|
|
"tcp://127.0.0.1:5555",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"auth-lmq-method",
|
|
ClientOnly,
|
|
Default{"llarp.auth"},
|
|
Comment{
|
|
"lmq function to call for authenticating new sessions",
|
|
"llarp.auth",
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
return;
|
|
auth_method = std::move(arg);
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"auth-whitelist",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"manually add a remote endpoint by .loki address to the access whitelist",
|
|
},
|
|
[this](std::string arg) {
|
|
service::Address addr;
|
|
if (not addr.FromString(arg))
|
|
throw std::invalid_argument{fmt::format("bad loki address: {}", arg)};
|
|
auth_whitelist.emplace(std::move(addr));
|
|
});
|
|
|
|
conf.define_option<fs::path>(
|
|
"network",
|
|
"auth-file",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Read auth tokens from file to accept endpoint auth",
|
|
"Can be provided multiple times",
|
|
},
|
|
[this](fs::path arg) {
|
|
if (not fs::exists(arg))
|
|
throw std::invalid_argument{
|
|
fmt::format("cannot load auth file {}: file does not exist", arg)};
|
|
auth_files.emplace(std::move(arg));
|
|
});
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"auth-file-type",
|
|
ClientOnly,
|
|
Comment{
|
|
"How to interpret the contents of an auth file.",
|
|
"Possible values: hashes, plaintext",
|
|
},
|
|
[this](std::string arg) {
|
|
auth_file_type = service::parse_auth_file_type(std::move(arg));
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"auth-static",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Manually add a static auth code to accept for endpoint auth",
|
|
"Can be provided multiple times",
|
|
},
|
|
[this](std::string arg) { auth_static_tokens.emplace(std::move(arg)); });
|
|
|
|
conf.define_option<bool>(
|
|
"network",
|
|
"reachable",
|
|
ClientOnly,
|
|
ReachableDefault,
|
|
assignment_acceptor(is_reachable),
|
|
Comment{
|
|
"Determines whether we will pubish our snapp's introset to the DHT.",
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"network",
|
|
"hops",
|
|
HopsDefault,
|
|
Comment{
|
|
"Number of hops in a path. Min 1, max 8.",
|
|
},
|
|
[this](int arg) {
|
|
if (arg < 1 or arg > 8)
|
|
throw std::invalid_argument("[endpoint]:hops must be >= 1 and <= 8");
|
|
hops = arg;
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"network",
|
|
"paths",
|
|
ClientOnly,
|
|
PathsDefault,
|
|
Comment{
|
|
"Number of paths to maintain at any given time.",
|
|
},
|
|
[this](int arg) {
|
|
if (arg < 3 or arg > 8)
|
|
throw std::invalid_argument("[endpoint]:paths must be >= 3 and <= 8");
|
|
paths = arg;
|
|
});
|
|
|
|
conf.define_option<bool>(
|
|
"network",
|
|
"exit",
|
|
ClientOnly,
|
|
Default{false},
|
|
assignment_acceptor(allow_exit),
|
|
Comment{
|
|
"Whether or not we should act as an exit node. Beware that this increases demand",
|
|
"on the server and may pose liability concerns. Enable at your own risk.",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"owned-range",
|
|
MultiValue,
|
|
Comment{
|
|
"When in exit mode announce we allow a private range in our introset. For example:",
|
|
" owned-range=10.0.0.0/24",
|
|
},
|
|
[this](std::string arg) {
|
|
IPRange range;
|
|
if (not range.FromString(arg))
|
|
throw std::invalid_argument{"bad ip range: '" + arg + "'"};
|
|
owned_ranges.insert(range);
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"traffic-whitelist",
|
|
MultiValue,
|
|
Comment{
|
|
"Adds an IP traffic type whitelist; can be specified multiple times. If any are",
|
|
"specified then only matched traffic will be allowed and all other traffic will be",
|
|
"dropped. Examples:",
|
|
" traffic-whitelist=tcp",
|
|
"would allow all TCP/IP packets (regardless of port);",
|
|
" traffic-whitelist=0x69",
|
|
"would allow IP traffic with IP protocol 0x69;",
|
|
" traffic-whitelist=udp/53",
|
|
"would allow UDP port 53; and",
|
|
" traffic-whitelist=tcp/smtp",
|
|
"would allow TCP traffic on the standard smtp port (21).",
|
|
},
|
|
[this](std::string arg) {
|
|
if (not traffic_policy)
|
|
traffic_policy = net::TrafficPolicy{};
|
|
|
|
// this will throw on error
|
|
traffic_policy->protocols.emplace(arg);
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"exit-node",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Specify a `.loki` address and an optional ip range to use as an exit broker.",
|
|
"Examples:",
|
|
" exit-node=whatever.loki",
|
|
"would map all exit traffic through whatever.loki; and",
|
|
" exit-node=stuff.loki:100.0.0.0/24",
|
|
"would map the IP range 100.0.0.0/24 through stuff.loki.",
|
|
"This option can be specified multiple times (to map different IP ranges).",
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
return;
|
|
service::Address exit;
|
|
IPRange range;
|
|
const auto pos = arg.find(":");
|
|
if (pos == std::string::npos)
|
|
{
|
|
range.FromString("::/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 (service::is_valid_name(arg))
|
|
{
|
|
ons_exit_map.Insert(range, arg);
|
|
return;
|
|
}
|
|
|
|
if (arg != "null" and not exit.FromString(arg))
|
|
{
|
|
throw std::invalid_argument{fmt::format("[network]:exit-node bad address: {}", arg)};
|
|
}
|
|
exit_map.Insert(range, exit);
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"exit-auth",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Specify an optional authentication code required to use a non-public exit node.",
|
|
"For example:",
|
|
" exit-auth=myfavouriteexit.loki:abc",
|
|
"uses the authentication code `abc` whenever myfavouriteexit.loki is accessed.",
|
|
"Can be specified multiple times to store codes for different exit nodes.",
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
return;
|
|
service::Address exit;
|
|
service::AuthInfo auth;
|
|
const auto pos = arg.find(":");
|
|
if (pos == std::string::npos)
|
|
{
|
|
throw std::invalid_argument(
|
|
"[network]:exit-auth invalid format, expects "
|
|
"exit-address.loki:auth-code-goes-here");
|
|
}
|
|
const auto exit_str = arg.substr(0, pos);
|
|
auth.token = arg.substr(pos + 1);
|
|
|
|
if (service::is_valid_name(exit_str))
|
|
{
|
|
ons_exit_auths.emplace(exit_str, auth);
|
|
return;
|
|
}
|
|
|
|
if (not exit.FromString(exit_str))
|
|
{
|
|
throw std::invalid_argument("[network]:exit-auth invalid exit address");
|
|
}
|
|
exit_auths.emplace(exit, auth);
|
|
});
|
|
|
|
conf.define_option<bool>(
|
|
"network",
|
|
"auto-routing",
|
|
ClientOnly,
|
|
Default{true},
|
|
Comment{
|
|
"Enable / disable automatic route configuration.",
|
|
"When this is enabled and an exit is used Lokinet will automatically configure the",
|
|
"operating system routes to route public internet traffic through the exit node.",
|
|
"This is enabled by default, but can be disabled if advanced/manual exit routing",
|
|
"configuration is desired."},
|
|
assignment_acceptor(enable_route_poker));
|
|
|
|
conf.define_option<bool>(
|
|
"network",
|
|
"blackhole-routes",
|
|
ClientOnly,
|
|
Default{true},
|
|
Comment{
|
|
"Enable / disable route configuration blackholes.",
|
|
"When enabled lokinet will drop IPv4 and IPv6 traffic (when in exit mode) that is not",
|
|
"handled in the exit configuration. Enabled by default."},
|
|
assignment_acceptor(blackhole_routes));
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"ifname",
|
|
Comment{
|
|
"Interface name for lokinet traffic. If unset lokinet will look for a free name",
|
|
"matching 'lokitunN', starting at N=0 (e.g. lokitun0, lokitun1, ...).",
|
|
},
|
|
assignment_acceptor(if_name));
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"ifaddr",
|
|
Comment{
|
|
"Local IP and range for lokinet traffic. For example, 172.16.0.1/16 to use",
|
|
"172.16.0.1 for this machine and 172.16.x.y for remote peers. If omitted then",
|
|
"lokinet will attempt to find an unused private range.",
|
|
},
|
|
[this](std::string arg) {
|
|
if (not if_addr.FromString(arg))
|
|
{
|
|
throw std::invalid_argument{fmt::format("[network]:ifaddr invalid value: '{}'", arg)};
|
|
}
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"ip6-range",
|
|
ClientOnly,
|
|
Comment{
|
|
"For all IPv6 exit traffic you will use this as the base address bitwised or'd with ",
|
|
"the v4 address in use.",
|
|
"To disable ipv6 set this to an empty value.",
|
|
"!!! WARNING !!! Disabling ipv6 tunneling when you have ipv6 routes WILL lead to ",
|
|
"de-anonymization as lokinet will no longer carry your ipv6 traffic.",
|
|
},
|
|
IP6RangeDefault,
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
{
|
|
LogError(
|
|
"!!! Disabling ipv6 tunneling when you have ipv6 routes WILL lead to "
|
|
"de-anonymization as lokinet will no longer carry your ipv6 traffic !!!");
|
|
base_ipv6_addr = std::nullopt;
|
|
return;
|
|
}
|
|
base_ipv6_addr = huint128_t{};
|
|
if (not base_ipv6_addr->FromString(arg))
|
|
throw std::invalid_argument{
|
|
fmt::format("[network]:ip6-range invalid value: '{}'", arg)};
|
|
});
|
|
|
|
// TODO: could be useful for snodes in the future, but currently only implemented for clients:
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"mapaddr",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Map a remote `.loki` address to always use a fixed local IP. For example:",
|
|
" mapaddr=whatever.loki:172.16.0.10",
|
|
"maps `whatever.loki` to `172.16.0.10` instead of using the next available IP.",
|
|
"The given IP address must be inside the range configured by ifaddr=",
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
return;
|
|
huint128_t ip;
|
|
service::Address addr;
|
|
const auto pos = arg.find(":");
|
|
if (pos == std::string::npos)
|
|
{
|
|
throw std::invalid_argument{fmt::format("[endpoint]:mapaddr invalid entry: {}", arg)};
|
|
}
|
|
std::string addrstr = arg.substr(0, pos);
|
|
std::string ipstr = arg.substr(pos + 1);
|
|
if (not ip.FromString(ipstr))
|
|
{
|
|
huint32_t ipv4;
|
|
if (not ipv4.FromString(ipstr))
|
|
{
|
|
throw std::invalid_argument{fmt::format("[endpoint]:mapaddr invalid ip: {}", ipstr)};
|
|
}
|
|
ip = net::ExpandV4(ipv4);
|
|
}
|
|
if (not addr.FromString(addrstr))
|
|
{
|
|
throw std::invalid_argument{
|
|
fmt::format("[endpoint]:mapaddr invalid addresss: {}", addrstr)};
|
|
}
|
|
if (map_addrs.find(ip) != map_addrs.end())
|
|
{
|
|
throw std::invalid_argument{
|
|
fmt::format("[endpoint]:mapaddr ip already mapped: {}", ipstr)};
|
|
}
|
|
map_addrs[ip] = addr;
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"blacklist-snode",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Adds a lokinet relay `.snode` address to the list of relays to avoid when",
|
|
"building paths. Can be specified multiple times.",
|
|
},
|
|
[this](std::string arg) {
|
|
RouterID id;
|
|
if (not id.from_string(arg))
|
|
throw std::invalid_argument{fmt::format("Invalid RouterID: {}", arg)};
|
|
|
|
auto itr = snode_blacklist.emplace(std::move(id));
|
|
if (not itr.second)
|
|
throw std::invalid_argument{fmt::format("Duplicate blacklist-snode: {}", arg)};
|
|
});
|
|
|
|
// TODO: support SRV records for routers, but for now client only
|
|
conf.define_option<std::string>(
|
|
"network",
|
|
"srv",
|
|
ClientOnly,
|
|
MultiValue,
|
|
Comment{
|
|
"Specify SRV Records for services hosted on the SNApp for protocols that use SRV",
|
|
"records for service discovery. Each line specifies a single SRV record as:",
|
|
" srv=_service._protocol priority weight port target.loki",
|
|
"and can be specified multiple times as needed.",
|
|
"For more info see",
|
|
"https://docs.oxen.io/products-built-on-oxen/lokinet/snapps/hosting-snapps",
|
|
"and general description of DNS SRV record configuration.",
|
|
},
|
|
[this](std::string arg) {
|
|
llarp::dns::SRVData newSRV;
|
|
if (not newSRV.fromString(arg))
|
|
throw std::invalid_argument{fmt::format("Invalid SRV Record string: {}", arg)};
|
|
|
|
srv_records.push_back(std::move(newSRV));
|
|
});
|
|
|
|
conf.define_option<int>(
|
|
"network",
|
|
"path-alignment-timeout",
|
|
ClientOnly,
|
|
Comment{
|
|
"How long to wait (in seconds) for a path to align to a pivot router when establishing",
|
|
"a path through the network to a remote .loki address.",
|
|
},
|
|
[this](int val) {
|
|
if (val <= 0)
|
|
throw std::invalid_argument{
|
|
"invalid path alignment timeout: " + std::to_string(val) + " <= 0"};
|
|
path_alignment_timeout = std::chrono::seconds{val};
|
|
});
|
|
|
|
conf.define_option<fs::path>(
|
|
"network",
|
|
"persist-addrmap-file",
|
|
ClientOnly,
|
|
Default{fs::path{params.default_data_dir / "addrmap.dat"}},
|
|
Comment{
|
|
"If given this specifies a file in which to record mapped local tunnel addresses so",
|
|
"the same local address will be used for the same lokinet address on reboot. If this",
|
|
"is not specified then the local IP of remote lokinet targets will not persist across",
|
|
"restarts of lokinet.",
|
|
},
|
|
[this](fs::path arg) {
|
|
if (arg.empty())
|
|
throw std::invalid_argument("persist-addrmap-file cannot be empty");
|
|
addr_map_persist_file = arg;
|
|
});
|
|
|
|
// Deprecated options:
|
|
conf.define_option<std::string>("network", "enabled", Deprecated);
|
|
}
|
|
|
|
void
|
|
DnsConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
|
|
// Most non-linux platforms have loopback as 127.0.0.1/32, but linux uses 127.0.0.1/8 so that we
|
|
// can bind to other 127.* IPs to avoid conflicting with something else that may be listening on
|
|
// 127.0.0.1:53.
|
|
constexpr std::array DefaultDNSBind{
|
|
#ifdef __linux__
|
|
#ifdef WITH_SYSTEMD
|
|
// when we have systemd support add a random high port on loopback as well
|
|
// see https://github.com/oxen-io/lokinet/issues/1887#issuecomment-1091897282
|
|
Default{"127.0.0.1:0"},
|
|
#endif
|
|
Default{"127.3.2.1:53"},
|
|
#else
|
|
Default{"127.0.0.1:53"},
|
|
#endif
|
|
};
|
|
|
|
// Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it
|
|
constexpr Default DefaultUpstreamDNS{"9.9.9.10:53"};
|
|
upstream_dns.emplace_back(DefaultUpstreamDNS.val);
|
|
|
|
conf.define_option<std::string>(
|
|
"dns",
|
|
"upstream",
|
|
MultiValue,
|
|
Comment{
|
|
"Upstream resolver(s) to use as fallback for non-loki addresses.",
|
|
"Multiple values accepted.",
|
|
},
|
|
[=, first = true](std::string arg) mutable {
|
|
if (first)
|
|
{
|
|
upstream_dns.clear();
|
|
first = false;
|
|
}
|
|
if (not arg.empty())
|
|
{
|
|
auto& entry = upstream_dns.emplace_back(std::move(arg));
|
|
if (not entry.getPort())
|
|
entry.setPort(53);
|
|
}
|
|
});
|
|
|
|
conf.define_option<bool>(
|
|
"dns",
|
|
"l3-intercept",
|
|
Default{
|
|
platform::is_windows or platform::is_android
|
|
or (platform::is_macos and not platform::is_apple_sysex)},
|
|
Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface "
|
|
"instead of binding a local udp socket"},
|
|
assignment_acceptor(raw));
|
|
|
|
conf.define_option<std::string>(
|
|
"dns",
|
|
"query-bind",
|
|
#if defined(_WIN32)
|
|
Default{"0.0.0.0:0"},
|
|
#else
|
|
Hidden,
|
|
#endif
|
|
Comment{
|
|
"Address to bind to for sending upstream DNS requests.",
|
|
},
|
|
[this](std::string arg) { query_bind = SockAddr{arg}; });
|
|
|
|
conf.define_option<std::string>(
|
|
"dns",
|
|
"bind",
|
|
DefaultDNSBind,
|
|
MultiValue,
|
|
Comment{
|
|
"Address to bind to for handling DNS requests.",
|
|
},
|
|
[=](std::string arg) {
|
|
SockAddr addr{arg};
|
|
// set dns port if no explicit port specified
|
|
// explicit :0 allowed
|
|
if (not addr.getPort() and not ends_with(arg, ":0"))
|
|
addr.setPort(53);
|
|
bind_addr.emplace_back(addr);
|
|
});
|
|
|
|
conf.define_option<fs::path>(
|
|
"dns",
|
|
"add-hosts",
|
|
ClientOnly,
|
|
Comment{"Add a hosts file to the dns resolver", "For use with client side dns filtering"},
|
|
[=](fs::path path) {
|
|
if (path.empty())
|
|
return;
|
|
if (not fs::exists(path))
|
|
throw std::invalid_argument{
|
|
fmt::format("cannot add hosts file {} as it does not exist", path)};
|
|
hostfiles.emplace_back(std::move(path));
|
|
});
|
|
|
|
// Ignored option (used by the systemd service file to disable resolvconf configuration).
|
|
conf.define_option<bool>(
|
|
"dns",
|
|
"no-resolvconf",
|
|
ClientOnly,
|
|
Comment{
|
|
"Can be uncommented and set to 1 to disable resolvconf configuration of lokinet DNS.",
|
|
"(This is not used directly by lokinet itself, but by the lokinet init scripts",
|
|
"on systems which use resolveconf)",
|
|
});
|
|
|
|
// forward the rest to libunbound
|
|
conf.add_undeclared_handler("dns", [this](auto, std::string_view key, std::string_view val) {
|
|
extra_opts.emplace(key, val);
|
|
});
|
|
}
|
|
|
|
void
|
|
LinksConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
conf.add_section_comments(
|
|
"bind",
|
|
{
|
|
"This section allows specifying the IPs that lokinet uses for incoming and outgoing",
|
|
"connections. For simple setups it can usually be left blank, but may be required",
|
|
"for routers with multiple IPs, or routers that must listen on a private IP with",
|
|
"forwarded public traffic. It can also be useful for clients that want to use a",
|
|
"consistent outgoing port for which firewall rules can be configured.",
|
|
});
|
|
|
|
const auto* net_ptr = params.Net_ptr();
|
|
|
|
conf.define_option<std::string>(
|
|
"bind",
|
|
"public-ip",
|
|
Hidden,
|
|
RelayOnly,
|
|
Comment{
|
|
"The IP address to advertise to the network instead of the incoming= or auto-detected",
|
|
"IP. This is typically required only when incoming= is used to listen on an internal",
|
|
"private range IP address that received traffic forwarded from the public IP.",
|
|
},
|
|
[this](std::string arg) {
|
|
public_addr = std::move(arg);
|
|
log::warning(
|
|
logcat,
|
|
"Using deprecated option; pass this value to [Router]:public-ip instead PLEASE");
|
|
});
|
|
|
|
conf.define_option<uint16_t>(
|
|
"bind",
|
|
"public-port",
|
|
Hidden,
|
|
RelayOnly,
|
|
Comment{
|
|
"The port to advertise to the network instead of the incoming= (or default) port.",
|
|
"This is typically required only when incoming= is used to listen on an internal",
|
|
"private range IP address/port that received traffic forwarded from the public IP.",
|
|
},
|
|
[this](uint16_t arg) {
|
|
if (arg <= 0 || arg > std::numeric_limits<uint16_t>::max())
|
|
throw std::invalid_argument("public-port must be >= 0 and <= 65536");
|
|
public_port = arg;
|
|
log::warning(
|
|
logcat,
|
|
"Using deprecated option; pass this value to [Router]:public-port instead PLEASE");
|
|
});
|
|
|
|
auto parse_addr_for_link = [net_ptr](const std::string& arg) {
|
|
std::optional<oxen::quic::Address> maybe = std::nullopt;
|
|
std::string_view arg_v;
|
|
|
|
// explicitly provided value
|
|
if (not arg.empty())
|
|
{
|
|
arg_v = std::string_view{arg};
|
|
}
|
|
|
|
if (arg_v[0] == ':')
|
|
{
|
|
uint16_t res;
|
|
if (auto rv = llarp::parse_int<uint16_t>(arg_v.substr(1), res); not rv)
|
|
res = DEFAULT_LISTEN_PORT;
|
|
|
|
maybe = oxen::quic::Address{""s, res};
|
|
}
|
|
else if (auto pos = arg_v.find(':'); pos != arg_v.npos)
|
|
{
|
|
auto h = arg_v.substr(0, pos);
|
|
uint16_t p;
|
|
if (auto rv = llarp::parse_int<uint16_t>(arg_v.substr(pos + 1), p); not rv)
|
|
p = DEFAULT_LISTEN_PORT;
|
|
|
|
maybe = oxen::quic::Address{std::string{h}, p};
|
|
|
|
if (maybe->is_loopback())
|
|
throw std::invalid_argument{fmt::format("{} is a loopback address", arg)};
|
|
}
|
|
if (not maybe)
|
|
{
|
|
// infer public address
|
|
if (auto maybe_ifname = net_ptr->GetBestNetIF())
|
|
maybe = oxen::quic::Address{*maybe_ifname};
|
|
}
|
|
|
|
if (maybe && maybe->port() == 0)
|
|
maybe = oxen::quic::Address{maybe->host(), DEFAULT_LISTEN_PORT};
|
|
|
|
return maybe;
|
|
};
|
|
|
|
conf.define_option<std::string>(
|
|
"bind",
|
|
"listen",
|
|
Comment{
|
|
"IP and/or port for lokinet to bind to for inbound/outbound connections.",
|
|
"",
|
|
"If IP is omitted then lokinet will search for a local network interface with a",
|
|
"public IP address and use that IP (and will exit with an error if no such IP is found",
|
|
"on the system). If port is omitted then lokinet defaults to 1090.",
|
|
"",
|
|
"Note: only one address will be accepted. If this option is not specified, it will ",
|
|
"default",
|
|
"to the inbound or outbound value. Conversely, specifying this option will supercede ",
|
|
"the",
|
|
"deprecated inbound/outbound opts.",
|
|
"",
|
|
"Examples:",
|
|
" listen=15.5.29.5:443",
|
|
" listen=10.0.2.2",
|
|
" listen=:1234",
|
|
"",
|
|
"Using a private range IP address (like the second example entry) will require using",
|
|
"the public-ip= and public-port= to specify the public IP address at which this",
|
|
"router can be reached.",
|
|
},
|
|
[this, parse_addr_for_link](const std::string& arg) {
|
|
if (auto a = parse_addr_for_link(arg); a and a->is_addressable())
|
|
{
|
|
listen_addr = *a;
|
|
using_user_value = true;
|
|
using_new_api = true;
|
|
}
|
|
else
|
|
throw std::invalid_argument{"Could not parse listen address!"};
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"bind",
|
|
"inbound",
|
|
RelayOnly,
|
|
MultiValue,
|
|
Hidden,
|
|
[this, parse_addr_for_link](const std::string& arg) {
|
|
if (using_new_api)
|
|
throw std::runtime_error{"USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"};
|
|
|
|
if (auto a = parse_addr_for_link(arg); a and a->is_addressable())
|
|
{
|
|
log::warning(
|
|
logcat,
|
|
"Loaded address from deprecated [inbound] options; update your config to use "
|
|
"[bind]:listen instead PLEASE");
|
|
listen_addr = *a;
|
|
using_user_value = true;
|
|
}
|
|
});
|
|
|
|
conf.define_option<std::string>("bind", "outbound", MultiValue, Deprecated, Hidden);
|
|
|
|
conf.add_undeclared_handler(
|
|
"bind", [this](std::string_view, std::string_view key, std::string_view val) {
|
|
if (using_new_api)
|
|
throw std::runtime_error{"USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"};
|
|
|
|
log::warning(logcat, "Please update your config to use [bind]:listen instead");
|
|
|
|
uint16_t port{0};
|
|
|
|
if (auto rv = llarp::parse_int<uint16_t>(val, port); not rv)
|
|
throw std::runtime_error{"Could not parse port; stop using this deprecated handler"};
|
|
|
|
port = port == 0 ? DEFAULT_LISTEN_PORT : port;
|
|
|
|
// special case: wildcard for outbound
|
|
if (key == "*")
|
|
{
|
|
log::warning(
|
|
logcat,
|
|
"Wildcat address referencing port {} is referencing deprecated outbound config "
|
|
"options; use [bind]:listen instead",
|
|
port);
|
|
return;
|
|
}
|
|
|
|
oxen::quic::Address temp;
|
|
|
|
try
|
|
{
|
|
temp = oxen::quic::Address{std::string{key}, port};
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw std::runtime_error{fmt::format(
|
|
"Could not parse address {}; please update your config to use [bind]:listen "
|
|
"instead: {}",
|
|
key,
|
|
e.what())};
|
|
}
|
|
|
|
if (not temp.is_addressable())
|
|
{
|
|
throw std::runtime_error{fmt::format(
|
|
"Invalid address: {}; stop using this deprecated handler, update your config to "
|
|
"use "
|
|
"[bind]:listen instead PLEASE",
|
|
temp)};
|
|
}
|
|
|
|
listen_addr = std::move(temp);
|
|
using_user_value = true;
|
|
});
|
|
}
|
|
|
|
void
|
|
ConnectConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
|
|
conf.add_undeclared_handler(
|
|
"connect", [this](std::string_view section, std::string_view name, std::string_view value) {
|
|
fs::path file{value.begin(), value.end()};
|
|
if (not fs::exists(file))
|
|
throw std::runtime_error{fmt::format(
|
|
"Specified bootstrap file {} specified in [{}]:{} does not exist",
|
|
value,
|
|
section,
|
|
name)};
|
|
|
|
routers.emplace_back(std::move(file));
|
|
return true;
|
|
});
|
|
}
|
|
|
|
void
|
|
ApiConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
constexpr std::array DefaultRPCBind{
|
|
Default{"tcp://127.0.0.1:1190"},
|
|
#ifndef _WIN32
|
|
Default{"ipc://rpc.sock"},
|
|
#endif
|
|
};
|
|
|
|
conf.define_option<bool>(
|
|
"api",
|
|
"enabled",
|
|
Default{not params.is_relay},
|
|
assignment_acceptor(enable_rpc_server),
|
|
Comment{
|
|
"Determines whether or not the LMQ JSON API is enabled. Defaults ",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"api",
|
|
"bind",
|
|
DefaultRPCBind,
|
|
MultiValue,
|
|
[this, first = true](std::string arg) mutable {
|
|
if (first)
|
|
{
|
|
rpc_bind_addrs.clear();
|
|
first = false;
|
|
}
|
|
if (arg.find("://") == std::string::npos)
|
|
{
|
|
arg = "tcp://" + arg;
|
|
}
|
|
rpc_bind_addrs.emplace_back(arg);
|
|
},
|
|
Comment{
|
|
"IP addresses and ports to bind to.",
|
|
"Recommend localhost-only for security purposes.",
|
|
});
|
|
|
|
conf.define_option<std::string>("api", "authkey", Deprecated);
|
|
|
|
// TODO: this was from pre-refactor:
|
|
// TODO: add pubkey to whitelist
|
|
}
|
|
|
|
void
|
|
LokidConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
conf.define_option<bool>(
|
|
"lokid",
|
|
"disable-testing",
|
|
Default{false},
|
|
Hidden,
|
|
RelayOnly,
|
|
Comment{
|
|
"Development option: set to true to disable reachability testing when using ",
|
|
"testnet"},
|
|
assignment_acceptor(disable_testing));
|
|
|
|
conf.define_option<std::string>(
|
|
"lokid",
|
|
"rpc",
|
|
RelayOnly,
|
|
Required,
|
|
Comment{
|
|
"oxenmq control address for for communicating with oxend. Depends on oxend's",
|
|
"lmq-local-control configuration option. By default this value should be",
|
|
"ipc://OXEND-DATA-DIRECTORY/oxend.sock, such as:",
|
|
" rpc=ipc:///var/lib/oxen/oxend.sock",
|
|
" rpc=ipc:///home/USER/.oxen/oxend.sock",
|
|
"but can use (non-default) TCP if oxend is configured that way:",
|
|
" rpc=tcp://127.0.0.1:5678",
|
|
},
|
|
[this](std::string arg) { rpc_addr = oxenmq::address(arg); });
|
|
|
|
// Deprecated options:
|
|
conf.define_option<std::string>("lokid", "jsonrpc", RelayOnly, Hidden, [](std::string arg) {
|
|
if (arg.empty())
|
|
return;
|
|
throw std::invalid_argument(
|
|
"the [lokid]:jsonrpc option is no longer supported; please use the [lokid]:rpc config "
|
|
"option instead with oxend's lmq-local-control address -- typically a value such as "
|
|
"rpc=ipc:///var/lib/oxen/oxend.sock or rpc=ipc:///home/snode/.oxen/oxend.sock");
|
|
});
|
|
conf.define_option<bool>("lokid", "enabled", RelayOnly, Deprecated);
|
|
conf.define_option<std::string>("lokid", "username", Deprecated);
|
|
conf.define_option<std::string>("lokid", "password", Deprecated);
|
|
conf.define_option<std::string>("lokid", "service-node-seed", Deprecated);
|
|
}
|
|
|
|
void
|
|
BootstrapConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
|
|
conf.define_option<bool>(
|
|
"bootstrap",
|
|
"seed-node",
|
|
Default{false},
|
|
Comment{"Whether or not to run as a seed node. We will not have any bootstrap routers "
|
|
"configured."},
|
|
assignment_acceptor(seednode));
|
|
|
|
conf.define_option<std::string>(
|
|
"bootstrap",
|
|
"add-node",
|
|
MultiValue,
|
|
Comment{
|
|
"Specify a bootstrap file containing a list of signed RouterContacts of service nodes",
|
|
"which can act as a bootstrap. Can be specified multiple times.",
|
|
},
|
|
[this](std::string arg) {
|
|
if (arg.empty())
|
|
{
|
|
throw std::invalid_argument("cannot use empty filename as bootstrap");
|
|
}
|
|
files.emplace_back(std::move(arg));
|
|
if (not fs::exists(files.back()))
|
|
{
|
|
throw std::invalid_argument("file does not exist: " + arg);
|
|
}
|
|
});
|
|
}
|
|
|
|
void
|
|
LoggingConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
|
|
constexpr Default DefaultLogType{
|
|
platform::is_android or platform::is_apple ? "system" : "print"};
|
|
constexpr Default DefaultLogFile{""};
|
|
|
|
const Default DefaultLogLevel{params.is_relay ? "warn" : "info"};
|
|
|
|
conf.define_option<std::string>(
|
|
"logging",
|
|
"type",
|
|
DefaultLogType,
|
|
[this](std::string arg) { type = log::type_from_string(arg); },
|
|
Comment{
|
|
"Log type (format). Valid options are:",
|
|
" print - print logs to standard output",
|
|
" system - logs directed to the system logger (syslog/eventlog/etc.)",
|
|
" file - plaintext formatting to a file",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"logging",
|
|
"level",
|
|
DefaultLogLevel,
|
|
[this](std::string arg) { level = log::level_from_string(arg); },
|
|
Comment{
|
|
"Minimum log level to print. Logging below this level will be ignored.",
|
|
"Valid log levels, in ascending order, are:",
|
|
" trace",
|
|
" debug",
|
|
" info",
|
|
" warn",
|
|
" error",
|
|
" critical",
|
|
" none",
|
|
});
|
|
|
|
conf.define_option<std::string>(
|
|
"logging",
|
|
"file",
|
|
DefaultLogFile,
|
|
assignment_acceptor(file),
|
|
Comment{
|
|
"When using type=file this is the output filename.",
|
|
});
|
|
}
|
|
|
|
void
|
|
PeerSelectionConfig::define_config_options(
|
|
ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
(void)params;
|
|
|
|
constexpr Default DefaultUniqueCIDR{32};
|
|
conf.define_option<int>(
|
|
"paths",
|
|
"unique-range-size",
|
|
DefaultUniqueCIDR,
|
|
ClientOnly,
|
|
[=](int arg) {
|
|
if (arg == 0)
|
|
{
|
|
unique_hop_netmask = arg;
|
|
}
|
|
else if (arg > 32 or arg < 4)
|
|
{
|
|
throw std::invalid_argument{"[paths]:unique-range-size must be between 4 and 32"};
|
|
}
|
|
unique_hop_netmask = arg;
|
|
},
|
|
Comment{
|
|
"Netmask for router path selection; each router must be from a distinct IPv4 subnet",
|
|
"of the given size.",
|
|
"E.g. 16 ensures that all routers are using IPs from distinct /16 IP ranges."});
|
|
|
|
#ifdef WITH_GEOIP
|
|
conf.defineOption<std::string>(
|
|
"paths",
|
|
"exclude-country",
|
|
ClientOnly,
|
|
MultiValue,
|
|
[=](std::string arg) {
|
|
m_ExcludeCountries.emplace(lowercase_ascii_string(std::move(arg)));
|
|
},
|
|
Comment{
|
|
"Exclude a country given its 2 letter country code from being used in path builds.",
|
|
"For example:",
|
|
" exclude-country=DE",
|
|
"would avoid building paths through routers with IPs in Germany.",
|
|
"This option can be specified multiple times to exclude multiple countries"});
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
PeerSelectionConfig::check_rcs(const std::set<RemoteRC>& rcs) const
|
|
{
|
|
if (unique_hop_netmask == 0)
|
|
return true;
|
|
const auto netmask = netmask_ipv6_bits(96 + unique_hop_netmask);
|
|
std::set<IPRange> seenRanges;
|
|
for (const auto& hop : rcs)
|
|
{
|
|
const auto network_addr = net::In6ToHUInt(hop.addr6()->in6().sin6_addr) & netmask;
|
|
if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::unique_ptr<ConfigGenParameters>
|
|
Config::make_gen_params() const
|
|
{
|
|
return std::make_unique<ConfigGenParameters_impl>();
|
|
}
|
|
|
|
Config::Config(std::optional<fs::path> datadir)
|
|
: data_dir{datadir ? std::move(*datadir) : fs::current_path()}
|
|
{}
|
|
|
|
constexpr auto GetOverridesDir = [](auto datadir) -> fs::path { return datadir / "conf.d"; };
|
|
|
|
void
|
|
Config::save()
|
|
{
|
|
const auto overridesDir = GetOverridesDir(data_dir);
|
|
if (not fs::exists(overridesDir))
|
|
fs::create_directory(overridesDir);
|
|
parser.save();
|
|
}
|
|
|
|
void
|
|
Config::override(std::string section, std::string key, std::string value)
|
|
{
|
|
parser.add_override(GetOverridesDir(data_dir) / "overrides.ini", section, key, value);
|
|
}
|
|
|
|
void
|
|
Config::load_overrides(ConfigDefinition& conf) const
|
|
{
|
|
ConfigParser parser;
|
|
const auto overridesDir = GetOverridesDir(data_dir);
|
|
if (fs::exists(overridesDir))
|
|
{
|
|
util::IterDir(overridesDir, [&](const fs::path& overrideFile) {
|
|
if (overrideFile.extension() == ".ini")
|
|
{
|
|
ConfigParser parser;
|
|
if (not parser.load_file(overrideFile))
|
|
throw std::runtime_error{"cannot load '" + overrideFile.u8string() + "'"};
|
|
|
|
parser.iter_all_sections([&](std::string_view section, const SectionValues& values) {
|
|
for (const auto& pair : values)
|
|
{
|
|
conf.add_config_value(section, pair.first, pair.second);
|
|
}
|
|
});
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
}
|
|
|
|
void
|
|
Config::add_default(std::string section, std::string key, std::string val)
|
|
{
|
|
additional.emplace_back(std::array<std::string, 3>{section, key, val});
|
|
}
|
|
|
|
bool
|
|
Config::load_config_data(std::string_view ini, std::optional<fs::path> filename, bool isRelay)
|
|
{
|
|
auto params = make_gen_params();
|
|
params->is_relay = isRelay;
|
|
params->default_data_dir = data_dir;
|
|
ConfigDefinition conf{isRelay};
|
|
add_backcompat_opts(conf);
|
|
init_config(conf, *params);
|
|
|
|
for (const auto& item : additional)
|
|
{
|
|
conf.add_config_value(item[0], item[1], item[2]);
|
|
}
|
|
|
|
parser.clear();
|
|
|
|
if (filename)
|
|
parser.set_filename(*filename);
|
|
else
|
|
parser.set_filename(fs::path{});
|
|
|
|
if (not parser.load_from_str(ini))
|
|
return false;
|
|
|
|
parser.iter_all_sections([&](std::string_view section, const SectionValues& values) {
|
|
for (const auto& pair : values)
|
|
{
|
|
conf.add_config_value(section, pair.first, pair.second);
|
|
}
|
|
});
|
|
|
|
load_overrides(conf);
|
|
|
|
conf.process();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
Config::load(std::optional<fs::path> fname, bool isRelay)
|
|
{
|
|
std::string ini;
|
|
if (fname)
|
|
{
|
|
try
|
|
{
|
|
ini = util::file_to_string(*fname);
|
|
}
|
|
catch (const std::exception&)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return load_config_data(ini, fname, isRelay);
|
|
}
|
|
|
|
bool
|
|
Config::load_string(std::string_view ini, bool isRelay)
|
|
{
|
|
return load_config_data(ini, std::nullopt, isRelay);
|
|
}
|
|
|
|
bool
|
|
Config::load_default_config(bool isRelay)
|
|
{
|
|
return load_string("", isRelay);
|
|
}
|
|
|
|
void
|
|
Config::init_config(ConfigDefinition& conf, const ConfigGenParameters& params)
|
|
{
|
|
router.define_config_options(conf, params);
|
|
network.define_config_options(conf, params);
|
|
paths.define_config_options(conf, params);
|
|
connect.define_config_options(conf, params);
|
|
dns.define_config_options(conf, params);
|
|
links.define_config_options(conf, params);
|
|
api.define_config_options(conf, params);
|
|
lokid.define_config_options(conf, params);
|
|
bootstrap.define_config_options(conf, params);
|
|
logging.define_config_options(conf, params);
|
|
}
|
|
|
|
void
|
|
Config::add_backcompat_opts(ConfigDefinition& conf)
|
|
{
|
|
// These config sections don't exist anymore:
|
|
|
|
conf.define_option<std::string>("system", "user", Deprecated);
|
|
conf.define_option<std::string>("system", "group", Deprecated);
|
|
conf.define_option<std::string>("system", "pidfile", Deprecated);
|
|
|
|
conf.define_option<std::string>("netdb", "dir", Deprecated);
|
|
|
|
conf.define_option<std::string>("metrics", "json-metrics-path", Deprecated);
|
|
}
|
|
|
|
void
|
|
ensure_config(fs::path dataDir, fs::path confFile, bool overwrite, bool asRouter)
|
|
{
|
|
// fail to overwrite if not instructed to do so
|
|
if (fs::exists(confFile) && !overwrite)
|
|
{
|
|
LogDebug("Not creating config file; it already exists.");
|
|
return;
|
|
}
|
|
|
|
const auto parent = confFile.parent_path();
|
|
|
|
// create parent dir if it doesn't exist
|
|
if ((not parent.empty()) and (not fs::exists(parent)))
|
|
{
|
|
fs::create_directory(parent);
|
|
}
|
|
|
|
llarp::LogInfo(
|
|
"Attempting to create config file for ",
|
|
(asRouter ? "router" : "client"),
|
|
" at ",
|
|
confFile);
|
|
|
|
llarp::Config config{dataDir};
|
|
std::string confStr;
|
|
if (asRouter)
|
|
confStr = config.generate_router_config_base();
|
|
else
|
|
confStr = config.generate_client_config_base();
|
|
|
|
// open a filestream
|
|
try
|
|
{
|
|
util::buffer_to_file(confFile, confStr);
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
throw std::runtime_error{
|
|
fmt::format("Failed to write config data to {}: {}", confFile, e.what())};
|
|
}
|
|
|
|
llarp::LogInfo("Generated new config ", confFile);
|
|
}
|
|
|
|
void
|
|
generate_common_config_comments(ConfigDefinition& def)
|
|
{
|
|
// router
|
|
def.add_section_comments(
|
|
"router",
|
|
{
|
|
"Configuration for routing activity.",
|
|
});
|
|
|
|
// logging
|
|
def.add_section_comments(
|
|
"logging",
|
|
{
|
|
"logging settings",
|
|
});
|
|
|
|
// api
|
|
def.add_section_comments(
|
|
"api",
|
|
{
|
|
"JSON API settings",
|
|
});
|
|
|
|
// dns
|
|
def.add_section_comments(
|
|
"dns",
|
|
{
|
|
"DNS configuration",
|
|
});
|
|
|
|
// bootstrap
|
|
def.add_section_comments(
|
|
"bootstrap",
|
|
{
|
|
"Configure nodes that will bootstrap us onto the network",
|
|
});
|
|
|
|
// network
|
|
def.add_section_comments(
|
|
"network",
|
|
{
|
|
"Network settings",
|
|
});
|
|
}
|
|
|
|
std::string
|
|
Config::generate_client_config_base()
|
|
{
|
|
auto params = make_gen_params();
|
|
params->is_relay = false;
|
|
params->default_data_dir = data_dir;
|
|
|
|
llarp::ConfigDefinition def{false};
|
|
init_config(def, *params);
|
|
generate_common_config_comments(def);
|
|
def.add_section_comments(
|
|
"paths",
|
|
{
|
|
"path selection algorithm options",
|
|
});
|
|
|
|
def.add_section_comments(
|
|
"network",
|
|
{
|
|
"Snapp settings",
|
|
});
|
|
|
|
return def.generate_ini_config(true);
|
|
}
|
|
|
|
std::string
|
|
Config::generate_router_config_base()
|
|
{
|
|
auto params = make_gen_params();
|
|
params->is_relay = true;
|
|
params->default_data_dir = data_dir;
|
|
|
|
llarp::ConfigDefinition def{true};
|
|
init_config(def, *params);
|
|
generate_common_config_comments(def);
|
|
|
|
// oxend
|
|
def.add_section_comments(
|
|
"lokid",
|
|
{
|
|
"Settings for communicating with oxend",
|
|
});
|
|
|
|
return def.generate_ini_config(true);
|
|
}
|
|
|
|
std::shared_ptr<Config>
|
|
Config::make_embedded_config()
|
|
{
|
|
auto config = std::make_shared<Config>();
|
|
config->load();
|
|
config->logging.level = log::Level::off;
|
|
config->api.enable_rpc_server = false;
|
|
config->network.endpoint_type = "null";
|
|
config->network.save_profiles = false;
|
|
config->bootstrap.files.clear();
|
|
return config;
|
|
}
|
|
|
|
} // namespace llarp
|