/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "admin/Admin.h"
#include "admin/AdminLog.h"
#include "admin/angel/Angel.h"
#include "admin/angel/Core.h"
#include "admin/angel/Core_admin.h"
#include "admin/angel/InterfaceWaiter.h"
#include "admin/angel/Hermes.h"
#include "admin/AuthorizedPasswords.h"
#include "benc/Int.h"
#include "benc/serialization/BencSerializer.h"
#include "benc/serialization/standard/StandardBencSerializer.h"
#include "crypto/AddressCalc.h"
#include "crypto/random/Random.h"
#include "crypto/random/libuv/LibuvEntropyProvider.h"
#include "dht/ReplyModule.h"
#include "dht/SerializationModule.h"
#include "dht/dhtcore/RouterModule.h"
#include "dht/dhtcore/RouterModule_admin.h"
#include "dht/dhtcore/RouteTracer.h"
#include "dht/dhtcore/SearchRunner.h"
#include "dht/dhtcore/SearchRunner_admin.h"
#include "dht/dhtcore/NodeStore_admin.h"
#include "dht/dhtcore/Janitor.h"
#include "interface/addressable/AddrInterface.h"
#include "interface/addressable/UDPAddrInterface.h"
#include "interface/UDPInterface_admin.h"
#ifdef HAS_ETH_INTERFACE
#include "interface/ETHInterface_admin.h"
#endif
#include "interface/tuntap/TUNInterface.h"
#include "interface/InterfaceConnector.h"
#include "interface/InterfaceController_admin.h"
#include "interface/FramingInterface.h"
#include "interface/ICMP6Generator.h"
#include "io/ArrayReader.h"
#include "io/ArrayWriter.h"
#include "io/FileWriter.h"
#include "io/Reader.h"
#include "io/Writer.h"
#include "memory/Allocator.h"
#include "memory/MallocAllocator.h"
#include "net/Ducttape.h"
#include "net/DefaultInterfaceController.h"
#include "net/SwitchPinger.h"
#include "net/SwitchPinger_admin.h"
#include "switch/SwitchCore.h"
#include "tunnel/IpTunnel.h"
#include "tunnel/IpTunnel_admin.h"
#include "util/events/Timeout.h"
#include "util/events/EventBase.h"
#include "util/events/Pipe.h"
#include "util/log/FileWriterLog.h"
#include "util/log/IndirectLog.h"
#include "util/Security_admin.h"
#include "util/platform/netdev/NetDev.h"
#include "interface/SessionManager_admin.h"
#include
#include
#include
// Failsafe: abort if more than 2^22 bytes are allocated (4MB)
#define ALLOCATOR_FAILSAFE (1<<22)
/** The number of nodes which we will keep track of. */
#define NODE_STORE_SIZE 8192
/** The number of milliseconds between attempting local maintenance searches. */
#define LOCAL_MAINTENANCE_SEARCH_MILLISECONDS 1000
/**
* The number of milliseconds to pass between global maintainence searches.
* These are searches for random targets which are used to discover new nodes.
*/
#define GLOBAL_MAINTENANCE_SEARCH_MILLISECONDS 30000
/**
* The worst possible packet overhead.
* assuming the packet needs to be handed off to another node
* because we have no route to the destination.
* and the CryptoAuths to both the destination and the handoff node are both timed out.
*/
#define WORST_CASE_OVERHEAD ( \
/* TODO: Headers_IPv4_SIZE */ 20 \
+ Headers_UDPHeader_SIZE \
+ 4 /* Nonce */ \
+ 16 /* Poly1305 authenticator */ \
+ Headers_SwitchHeader_SIZE \
+ Headers_CryptoAuth_SIZE \
+ Headers_IP6Header_SIZE \
+ Headers_CryptoAuth_SIZE \
)
/** The default MTU, assuming the external MTU is 1492 (common for PPPoE DSL) */
#define DEFAULT_MTU ( \
1492 \
- WORST_CASE_OVERHEAD \
+ Headers_IP6Header_SIZE /* The OS subtracts the IP6 header. */ \
+ Headers_CryptoAuth_SIZE /* Linux won't let set the MTU below 1280.
TODO: make sure we never hand off to a node for which the CA session is expired. */ \
)
static void parsePrivateKey(uint8_t privateKey[32],
struct Address* addr,
struct Except* eh)
{
crypto_scalarmult_curve25519_base(addr->key, privateKey);
AddressCalc_addressForPublicKey(addr->ip6.bytes, addr->key);
if (!AddressCalc_validAddress(addr->ip6.bytes)) {
Except_raise(eh, -1, "Ip address outside of the FC00/8 range, invalid private key.");
}
}
static void adminPing(Dict* input, void* vadmin, String* txid)
{
Dict d = Dict_CONST(String_CONST("q"), String_OBJ(String_CONST("pong")), NULL);
Admin_sendMessage(&d, txid, (struct Admin*) vadmin);
}
struct Context
{
struct Allocator* allocator;
struct Admin* admin;
struct Log* logger;
struct Hermes* hermes;
struct EventBase* base;
String* exitTxid;
};
static void adminMemory(Dict* input, void* vcontext, String* txid)
{
struct Context* context = vcontext;
Dict d = Dict_CONST(
String_CONST("bytes"), Int_OBJ(MallocAllocator_bytesAllocated(context->allocator)), NULL
);
Admin_sendMessage(&d, txid, context->admin);
}
static void shutdown(void* vcontext)
{
struct Context* context = vcontext;
Allocator_free(context->allocator);
}
static void onAngelExitResponse(Dict* message, void* vcontext)
{
struct Context* context = vcontext;
Log_info(context->logger, "Angel stopped");
Log_info(context->logger, "Exiting");
Dict d = Dict_CONST(String_CONST("error"), String_OBJ(String_CONST("none")), NULL);
Admin_sendMessage(&d, context->exitTxid, context->admin);
Timeout_setTimeout(shutdown, context, 1, context->base, context->allocator);
}
static void adminExit(Dict* input, void* vcontext, String* txid)
{
struct Context* context = vcontext;
Log_info(context->logger, "Got request to exit");
Log_info(context->logger, "Stopping angel");
context->exitTxid = String_clone(txid, context->allocator);
Dict angelExit = Dict_CONST(String_CONST("q"), String_OBJ(String_CONST("Angel_exit")), NULL);
Hermes_callAngel(&angelExit,
onAngelExitResponse,
context,
context->allocator,
NULL,
context->hermes);
}
static void angelDied(struct Pipe* p, int status)
{
exit(1);
}
static Dict* getInitialConfig(struct Interface* iface,
struct EventBase* eventBase,
struct Allocator* alloc,
struct Except* eh)
{
struct Message* m = InterfaceWaiter_waitForData(iface, eventBase, alloc, eh);
struct Reader* reader = ArrayReader_new(m->bytes, m->length, alloc);
Dict* config = Dict_new(alloc);
if (StandardBencSerializer_get()->parseDictionary(reader, alloc, config)) {
Except_raise(eh, -1, "Failed to parse initial configuration.");
}
return config;
}
void Core_initTunnel(String* desiredDeviceName,
struct Sockaddr* addr,
uint8_t addressPrefix,
struct Ducttape* dt,
struct Log* logger,
struct IpTunnel* ipTunnel,
struct EventBase* eventBase,
struct Allocator* alloc,
struct Except* eh)
{
Log_debug(logger, "Initializing TUN device [%s]",
(desiredDeviceName) ? desiredDeviceName->bytes : "");
char assignedTunName[TUNInterface_IFNAMSIZ];
char* desiredName = (desiredDeviceName) ? desiredDeviceName->bytes : NULL;
struct Interface* tun =
TUNInterface_new(desiredName, assignedTunName, eventBase, logger, eh, alloc);
IpTunnel_setTunName(assignedTunName, ipTunnel);
Ducttape_setUserInterface(dt, tun);
NetDev_addAddress(assignedTunName, addr, addressPrefix, logger, eh);
NetDev_setMTU(assignedTunName, DEFAULT_MTU, logger, eh);
}
/** This is a response from a call which is intended only to send information to the angel. */
static void angelResponse(Dict* resp, void* vNULL)
{
// do nothing
}
/*
* This process is started with 2 parameters, they must all be numeric in base 10.
* toAngel the pipe which is used to send data back to the angel process.
* fromAngel the pipe which is used to read incoming data from the angel.
*
* Upon initialization, this process will wait for an initial configuration to be sent to
* it and then it will send an initial response.
*/
int Core_main(int argc, char** argv)
{
struct Except* eh = NULL;
if (argc != 3) {
Except_raise(eh, -1, "This is internal to cjdns and shouldn't started manually.");
}
struct Allocator* alloc = MallocAllocator_new(ALLOCATOR_FAILSAFE);
struct Log* preLogger = FileWriterLog_new(stderr, alloc);
struct EventBase* eventBase = EventBase_new(alloc);
// -------------------- Setup the Pre-Logger ---------------------- //
struct Log* logger = IndirectLog_new(alloc);
IndirectLog_set(logger, preLogger);
// -------------------- Setup the PRNG ---------------------- //
struct Random* rand = LibuvEntropyProvider_newDefaultRandom(eventBase, logger, eh, alloc);
// -------------------- Change Canary Value ---------------------- //
MallocAllocator_setCanary(alloc, (long)Random_int64(rand));
struct Allocator* tempAlloc = Allocator_child(alloc);
// The first read inside of getInitialConfig() will begin it waiting.
struct Pipe* angelPipe = Pipe_named(argv[2], eventBase, eh, alloc);
angelPipe->logger = logger;
angelPipe->onClose = angelDied;
struct Interface* angelIface = FramingInterface_new(65535, &angelPipe->iface, alloc);
Dict* config = getInitialConfig(angelIface, eventBase, tempAlloc, eh);
struct Hermes* hermes = Hermes_new(angelIface, eventBase, logger, alloc);
String* privateKeyHex = Dict_getString(config, String_CONST("privateKey"));
Dict* adminConf = Dict_getDict(config, String_CONST("admin"));
String* pass = Dict_getString(adminConf, String_CONST("pass"));
String* bind = Dict_getString(adminConf, String_CONST("bind"));
if (!(pass && privateKeyHex && bind)) {
if (!pass) {
Except_raise(eh, -1, "Expected 'pass'");
}
if (!bind) {
Except_raise(eh, -1, "Expected 'bind'");
}
if (!privateKeyHex) {
Except_raise(eh, -1, "Expected 'privateKey'");
}
Except_raise(eh, -1, "Expected 'pass', 'privateKey' and 'bind' in configuration.");
}
Log_keys(logger, "Starting core with admin password [%s]", pass->bytes);
uint8_t privateKey[32];
if (privateKeyHex->len != 64
|| Hex_decode(privateKey, 32, (uint8_t*) privateKeyHex->bytes, 64) != 32)
{
Except_raise(eh, -1, "privateKey must be 64 bytes of hex.");
}
struct Sockaddr_storage bindAddr;
if (Sockaddr_parse(bind->bytes, &bindAddr)) {
Except_raise(eh, -1, "bind address [%s] unparsable", bind->bytes);
}
struct AddrInterface* udpAdmin =
UDPAddrInterface_new(eventBase, &bindAddr.addr, alloc, eh, logger);
struct Admin* admin = Admin_new(udpAdmin, alloc, logger, eventBase, pass);
char* boundAddr = Sockaddr_print(udpAdmin->addr, tempAlloc);
Dict adminResponse = Dict_CONST(
String_CONST("bind"), String_OBJ(String_CONST(boundAddr)), NULL
);
Dict response = Dict_CONST(
String_CONST("error"), String_OBJ(String_CONST("none")), Dict_CONST(
String_CONST("admin"), Dict_OBJ(&adminResponse), NULL
));
// This always times out because the angel doesn't respond.
Hermes_callAngel(&response, angelResponse, NULL, alloc, eh, hermes);
// --------------------- Setup the Logger --------------------- //
Dict* logging = Dict_getDict(config, String_CONST("logging"));
String* logTo = Dict_getString(logging, String_CONST("logTo"));
if (logTo && String_equals(logTo, String_CONST("stdout"))) {
// do nothing, continue logging to stdout.
} else {
struct Log* adminLogger = AdminLog_registerNew(admin, alloc, rand);
IndirectLog_set(logger, adminLogger);
logger = adminLogger;
}
// CryptoAuth
struct Address addr;
parsePrivateKey(privateKey, &addr, eh);
struct CryptoAuth* cryptoAuth = CryptoAuth_new(alloc, privateKey, eventBase, logger, rand);
struct Sockaddr* myAddr = Sockaddr_fromBytes(addr.ip6.bytes, Sockaddr_AF_INET6, alloc);
struct SwitchCore* switchCore = SwitchCore_new(logger, alloc);
struct DHTModuleRegistry* registry = DHTModuleRegistry_new(alloc);
ReplyModule_register(registry, alloc);
struct NodeStore* nodeStore = NodeStore_new(&addr, NODE_STORE_SIZE, alloc, logger, rand);
struct RouterModule* routerModule = RouterModule_register(registry,
alloc,
addr.key,
eventBase,
logger,
rand,
nodeStore);
struct RouteTracer* routeTracer =
RouteTracer_new(nodeStore, routerModule, addr.ip6.bytes, eventBase, logger, alloc);
struct SearchRunner* searchRunner =
SearchRunner_new(nodeStore, logger, eventBase, routerModule, addr.ip6.bytes, alloc);
Janitor_new(LOCAL_MAINTENANCE_SEARCH_MILLISECONDS,
GLOBAL_MAINTENANCE_SEARCH_MILLISECONDS,
routerModule,
nodeStore,
searchRunner,
routeTracer,
logger,
alloc,
eventBase,
rand);
SerializationModule_register(registry, logger, alloc);
struct IpTunnel* ipTun = IpTunnel_new(logger, eventBase, alloc, rand, hermes);
struct Ducttape* dt = Ducttape_register(privateKey,
registry,
routerModule,
searchRunner,
switchCore,
eventBase,
alloc,
logger,
ipTun,
rand);
struct SwitchPinger* sp =
SwitchPinger_new(&dt->switchPingerIf, eventBase, rand, logger, alloc);
// Interfaces.
struct InterfaceController* ifController =
DefaultInterfaceController_new(cryptoAuth,
switchCore,
routerModule,
logger,
eventBase,
sp,
rand,
alloc);
// ------------------- Register RPC functions ----------------------- //
InterfaceController_admin_register(ifController, admin, alloc);
SwitchPinger_admin_register(sp, admin, alloc);
UDPInterface_admin_register(eventBase, alloc, logger, admin, ifController);
#ifdef HAS_ETH_INTERFACE
ETHInterface_admin_register(eventBase, alloc, logger, admin, ifController);
#endif
NodeStore_admin_register(nodeStore, admin, alloc);
RouterModule_admin_register(routerModule, admin, alloc);
SearchRunner_admin_register(searchRunner, admin, alloc);
AuthorizedPasswords_init(admin, cryptoAuth, alloc);
Admin_registerFunction("ping", adminPing, admin, false, NULL, admin);
Core_admin_register(myAddr, dt, logger, ipTun, alloc, admin, eventBase);
Security_admin_register(alloc, logger, admin);
IpTunnel_admin_register(ipTun, admin, alloc);
SessionManager_admin_register(dt->sessionManager, admin, alloc);
struct Context* ctx = Allocator_clone(alloc, (&(struct Context) {
.allocator = alloc,
.admin = admin,
.logger = logger,
.hermes = hermes,
.base = eventBase,
}));
Admin_registerFunction("memory", adminMemory, ctx, false, NULL, admin);
Admin_registerFunction("Core_exit", adminExit, ctx, true, NULL, admin);
EventBase_beginLoop(eventBase);
return 0;
}