/* 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 "subnode/SubnodePathfinder.h" #include "subnode/AddrSet.h" #include "subnode/MsgCore.h" #include "subnode/SupernodeHunter.h" #include "subnode/PingResponder.h" #include "subnode/BoilerplateResponder.h" #include "subnode/ReachabilityCollector.h" #include "crypto/AddressCalc.h" #include "dht/Address.h" #include "wire/DataHeader.h" #include "wire/RouteHeader.h" #include "dht/dhtcore/ReplySerializer.h" #include "util/AddrTools.h" #include "util/events/Timeout.h" #include "net/SwitchPinger.h" #include "switch/LabelSplicer.h" #include "wire/Error.h" #include "wire/PFChan.h" #include "wire/DataHeader.h" #include "util/CString.h" #include "wire/Metric.h" #include "subnode/ReachabilityAnnouncer.h" struct Query { struct Address target; uint8_t routeFrom[16]; uint8_t routeTo[16]; }; #define Map_NAME OfPromiseByQuery #define Map_KEY_TYPE struct Query #define Map_VALUE_TYPE struct MsgCore_Promise* #define Map_ENABLE_HANDLES #include "util/Map.h" struct SubnodePathfinder_pvt { struct SubnodePathfinder pub; struct Iface msgCoreIf; struct Allocator* alloc; struct Log* log; struct EventBase* base; struct Random* rand; #define SubnodePathfinder_pvt_state_INITIALIZING 0 #define SubnodePathfinder_pvt_state_RUNNING 1 int state; struct Address* myAddress; struct AddrSet* myPeerAddrs; struct MsgCore* msgCore; struct Admin* admin; struct BoilerplateResponder* br; struct ReachabilityAnnouncer* ra; struct Map_OfPromiseByQuery queryMap; struct SwitchPinger* sp; struct Iface switchPingerIf; struct EncodingScheme* myScheme; uint8_t* privateKey; String* encodingSchemeStr; Identity }; static void nodeForAddress(struct PFChan_Node* nodeOut, struct Address* addr, uint32_t metric) { Bits_memset(nodeOut, 0, PFChan_Node_SIZE); nodeOut->version_be = Endian_hostToBigEndian32(addr->protocolVersion); nodeOut->metric_be = Endian_hostToBigEndian32(metric); nodeOut->path_be = Endian_hostToBigEndian64(addr->path); Bits_memcpy(nodeOut->publicKey, addr->key, 32); Bits_memcpy(nodeOut->ip6, addr->ip6.bytes, 16); } static Iface_DEFUN sendNode(struct Message* msg, struct Address* addr, uint32_t metric, enum PFChan_Pathfinder msgType, struct SubnodePathfinder_pvt* pf) { Message_reset(msg); Er_assert(Message_eshift(msg, PFChan_Node_SIZE)); nodeForAddress((struct PFChan_Node*) msg->msgbytes, addr, metric); if (addr->path == UINT64_MAX) { ((struct PFChan_Node*) msg->msgbytes)->path_be = 0; } Er_assert(Message_epush32be(msg, msgType)); return Iface_next(&pf->pub.eventIf, msg); } static Iface_DEFUN connected(struct SubnodePathfinder_pvt* pf, struct Message* msg) { Log_debug(pf->log, "INIT"); pf->state = SubnodePathfinder_pvt_state_RUNNING; return NULL; } static uint32_t addressForNode(struct Address* addrOut, struct Message* msg) { Bits_memset(addrOut, 0, sizeof(struct Address)); struct PFChan_Node node; Er_assert(Message_epop(msg, &node, PFChan_Node_SIZE)); Assert_true(!Message_getLength(msg)); addrOut->protocolVersion = Endian_bigEndianToHost32(node.version_be); addrOut->path = Endian_bigEndianToHost64(node.path_be); Bits_memcpy(addrOut->key, node.publicKey, 32); Bits_memcpy(addrOut->ip6.bytes, node.ip6, 16); return Endian_bigEndianToHost32(node.metric_be); } static Iface_DEFUN switchErr(struct Message* msg, struct SubnodePathfinder_pvt* pf) { struct PFChan_Core_SwitchErr switchErr; Er_assert(Message_epop(msg, &switchErr, PFChan_Core_SwitchErr_MIN_SIZE)); uint64_t path = Endian_bigEndianToHost64(switchErr.sh.label_be); if (pf->pub.snh->snodeAddr.path && // pf->pub.snh->snodeAddr.path != path && // ctrl errors from the snode should be same LabelSplicer_routesThrough(pf->pub.snh->snodeAddr.path, path)) { uint8_t pathStr[20]; AddrTools_printPath(pathStr, path); int err = Endian_bigEndianToHost32(switchErr.ctrlErr.errorType_be); Log_debug(pf->log, "switch err from active snode [%s] type [%d]", pathStr, err); pf->pub.snh->snodeIsReachable = false; if (pf->pub.snh->onSnodeUnreachable) { pf->pub.snh->onSnodeUnreachable(pf->pub.snh, 0, 0); } } // TODO(cjd): We should be reporting a bad link to the session manager but // we only really have the ability to report a node with known IPv6 address // so we will need to add a new event type to PFChan. return NULL; } struct SnodeQuery { struct SubnodePathfinder_pvt* pf; uint32_t mapHandle; Identity }; static void getRouteReply(Dict* msg, struct Address* src, struct MsgCore_Promise* prom) { struct SnodeQuery* snq = Identity_check((struct SnodeQuery*) prom->userData); struct SubnodePathfinder_pvt* pf = Identity_check(snq->pf); int index = Map_OfPromiseByQuery_indexForHandle(snq->mapHandle, &pf->queryMap); Assert_true(index > -1); Map_OfPromiseByQuery_remove(index, &pf->queryMap); if (!src) { Log_debug(pf->log, "GetRoute timeout"); return; } Log_debug(pf->log, "Search reply!"); struct Address_List* al = ReplySerializer_parse(src, msg, pf->log, false, prom->alloc); if (!al || al->length == 0) { return; } Log_debug(pf->log, "reply with[%s]", Address_toString(&al->elems[0], prom->alloc)->bytes); if (al->elems[0].protocolVersion < 20) { Log_debug(pf->log, "not sending [%s] because version is old", Address_toString(&al->elems[0], prom->alloc)->bytes); return; } //NodeCache_discoverNode(pf->nc, &al->elems[0]); struct Message* msgToCore = Message_new(0, 512, prom->alloc); Iface_CALL(sendNode, msgToCore, &al->elems[0], Metric_SNODE_SAYS, PFChan_Pathfinder_NODE, pf); } static void queryRs(struct SubnodePathfinder_pvt* pf, uint8_t addr[16], uint8_t printedAddr[40]) { if (!pf->pub.snh || !pf->pub.snh->snodeAddr.path) { Log_debug(pf->log, "Search for [%s] impossible because we have no snode", printedAddr); return; } struct Query q = { .routeFrom = { 0 } }; Bits_memcpy(&q.target, &pf->pub.snh->snodeAddr, sizeof(struct Address)); Bits_memcpy(q.routeFrom, pf->myAddress->ip6.bytes, 16); Bits_memcpy(q.routeFrom, addr, 16); if (Map_OfPromiseByQuery_indexForKey(&q, &pf->queryMap) > -1) { Log_debug(pf->log, "Search for [%s] skipped because one is outstanding", printedAddr); return; } struct MsgCore_Promise* qp = MsgCore_createQuery(pf->msgCore, 0, pf->alloc); struct SnodeQuery* snq = Allocator_calloc(qp->alloc, sizeof(struct SnodeQuery), 1); Identity_set(snq); snq->pf = pf; Dict* dict = qp->msg = Dict_new(qp->alloc); qp->cb = getRouteReply; qp->userData = snq; Assert_true(AddressCalc_validAddress(pf->pub.snh->snodeAddr.ip6.bytes)); qp->target = &pf->pub.snh->snodeAddr; Log_debug(pf->log, "Sending getRoute to snode [%s] for [%s]", Address_toString(qp->target, qp->alloc)->bytes, printedAddr); Dict_putStringCC(dict, "sq", "gr", qp->alloc); String* src = String_newBinary(pf->myAddress->ip6.bytes, 16, qp->alloc); Dict_putStringC(dict, "src", src, qp->alloc); String* target = String_newBinary(addr, 16, qp->alloc); Dict_putStringC(dict, "tar", target, qp->alloc); int index = Map_OfPromiseByQuery_put(&q, &qp, &pf->queryMap); snq->mapHandle = pf->queryMap.handles[index]; } static Iface_DEFUN searchReq(struct Message* msg, struct SubnodePathfinder_pvt* pf) { uint8_t addr[16]; Er_assert(Message_epop(msg, addr, 16)); Er_assert(Message_epop32be(msg)); uint32_t version = Er_assert(Message_epop32be(msg)); if (version && version < 20) { return Error(msg, "UNHANDLED"); } Assert_true(!Message_getLength(msg)); uint8_t printedAddr[40]; AddrTools_printIp(printedAddr, addr); // We're not going to query for a direct peer because it slows down the process significantly // and right now, Metric.h always prefers a reported peer to the advice of the snode, which for (int i = 0; i < pf->myPeerAddrs->length; ++i) { struct Address* myPeer = AddrSet_get(pf->myPeerAddrs, i); if (Bits_memcmp(myPeer->ip6.bytes, addr, 16)) { continue; } Log_debug(pf->log, "Skip for [%s] is our peer, provide that immediately", printedAddr); // warning: msg is nolonger usable, it has disappeared into sendNode queryRs(pf, addr, printedAddr); return sendNode(msg, myPeer, Metric_PF_PEER, PFChan_Pathfinder_NODE, pf); } if (pf->pub.snh && !Bits_memcmp(pf->pub.snh->snodeAddr.ip6.bytes, addr, 16)) { // Querying for a path TO our snode, we can return the path we know right now but also // make the query... Log_debug(pf->log, "Skip for [%s] is our snode, provide that immediately", printedAddr); queryRs(pf, addr, printedAddr); return sendNode(msg, &pf->pub.snh->snodeAddr, Metric_SNODE, PFChan_Pathfinder_NODE, pf); } queryRs(pf, addr, printedAddr); return NULL; } static void rcChange(struct ReachabilityCollector* rc, struct Address* nodeAddr, struct ReachabilityCollector_PeerInfo* pi) { struct SubnodePathfinder_pvt* pf = Identity_check((struct SubnodePathfinder_pvt*) rc->userData); ReachabilityAnnouncer_updatePeer(pf->ra, nodeAddr, pi); } struct Ping { struct SubnodePathfinder_pvt* pf; uint32_t mapHandle; Identity }; static void pingReply(Dict* msg, struct Address* src, struct MsgCore_Promise* prom) { struct Ping* usp = Identity_check((struct Ping*) prom->userData); struct SubnodePathfinder_pvt* pf = Identity_check(usp->pf); int index = Map_OfPromiseByQuery_indexForHandle(usp->mapHandle, &pf->queryMap); Assert_true(index > -1); Map_OfPromiseByQuery_remove(index, &pf->queryMap); if (!src) { Log_debug(pf->log, "Ping timeout pinging [%s]", Address_toString(prom->target, prom->alloc)->bytes); return; } Log_debug(pf->log, "Ping reply from [%s]", Address_toString(src, prom->alloc)->bytes); struct Message* msgToCore = Message_new(0, 512, prom->alloc); Iface_CALL(sendNode, msgToCore, src, Metric_PING_REPLY, PFChan_Pathfinder_NODE, pf); } static void pingNode(struct SubnodePathfinder_pvt* pf, struct Address* addr) { struct Query q = { .routeFrom = { 0 } }; Bits_memcpy(&q.target, addr, sizeof(struct Address)); if (Map_OfPromiseByQuery_indexForKey(&q, &pf->queryMap) > -1) { Log_debug(pf->log, "Skipping ping because one is already outstanding"); return; } // We have a path to the node but the session is not setup, lets ping them... struct MsgCore_Promise* qp = MsgCore_createQuery(pf->msgCore, 0, pf->alloc); struct Ping* usp = Allocator_calloc(qp->alloc, sizeof(struct Ping), 1); Identity_set(usp); usp->pf = pf; Dict* dict = qp->msg = Dict_new(qp->alloc); qp->cb = pingReply; qp->userData = usp; Assert_true(AddressCalc_validAddress(addr->ip6.bytes)); Assert_true(addr->path); qp->target = Address_clone(addr, qp->alloc); Log_debug(pf->log, "Sending ping to [%s]", Address_toString(qp->target, qp->alloc)->bytes); Dict_putStringCC(dict, "q", "pn", qp->alloc); BoilerplateResponder_addBoilerplate(pf->br, dict, addr, qp->alloc); int index = Map_OfPromiseByQuery_put(&q, &qp, &pf->queryMap); usp->mapHandle = pf->queryMap.handles[index]; } static Iface_DEFUN peer(struct Message* msg, struct SubnodePathfinder_pvt* pf) { struct Address addr = {0}; uint32_t metric = addressForNode(&addr, msg); String* str = Address_toString(&addr, Message_getAlloc(msg)); int index = AddrSet_indexOf(pf->myPeerAddrs, &addr, AddrSet_Match_BOTH); if (index > -1) { struct Address* myPeer = AddrSet_get(pf->myPeerAddrs, index); // Just in case there is a difference myPeer->protocolVersion = addr.protocolVersion; } else { Log_debug(pf->log, "Peer [%s]", str->bytes); AddrSet_add(pf->myPeerAddrs, &addr, AddrSet_Match_BOTH); // immediately ping them pingNode(pf, &addr); } ReachabilityCollector_change(pf->pub.rc, &addr); if ((metric & ~Metric_IC_PEER_MASK) != Metric_IC_PEER) { } else if ((metric & Metric_IC_PEER_MASK) == Metric_IC_PEER_MASK) { } else { ReachabilityCollector_lagSample(pf->pub.rc, addr.path, (metric & Metric_IC_PEER_MASK)); } // We send this as Metric_PF_PEER because if it happens that this is a really crappy // peering link, we would prefer to take the route server's advice and bounce the connection // over a link which makes more sense to use. return sendNode(msg, &addr, Metric_PF_PEER, PFChan_Pathfinder_NODE, pf); } static Iface_DEFUN peerGone(struct Message* msg, struct SubnodePathfinder_pvt* pf) { struct Address addr = {0}; addressForNode(&addr, msg); AddrSet_remove(pf->myPeerAddrs, &addr, AddrSet_Match_BOTH); ReachabilityCollector_unreachable(pf->pub.rc, &addr); return sendNode(msg, &addr, Metric_DEAD_LINK, PFChan_Pathfinder_NODE, pf); } static Iface_DEFUN session(struct Message* msg, struct SubnodePathfinder_pvt* pf) { struct Address addr = {0}; addressForNode(&addr, msg); String* str = Address_toString(&addr, Message_getAlloc(msg)); Log_debug(pf->log, "Session [%s]", str->bytes); //if (addr.protocolVersion) { NodeCache_discoverNode(pf->nc, &addr); } return NULL; } static Iface_DEFUN sessionEnded(struct Message* msg, struct SubnodePathfinder_pvt* pf) { struct Address addr = {0}; addressForNode(&addr, msg); String* str = Address_toString(&addr, Message_getAlloc(msg)); Log_debug(pf->log, "Session ended [%s]", str->bytes); //NodeCache_forgetNode(pf->nc, &addr); return NULL; } static Iface_DEFUN discoveredPath(struct Message* msg, struct SubnodePathfinder_pvt* pf) { //struct Address addr = {0}; //addressForNode(&addr, msg); //Log_debug(pf->log, "discoveredPath(%s)", Address_toString(&addr, Message_getAlloc(msg))->bytes); //if (addr.protocolVersion) { NodeCache_discoverNode(pf->nc, &addr); } return NULL; } static Iface_DEFUN handlePing(struct Message* msg, struct SubnodePathfinder_pvt* pf) { //Log_debug(pf->log, "Received ping"); Er_assert(Message_epush32be(msg, PFChan_Pathfinder_PONG)); return Iface_next(&pf->pub.eventIf, msg); } static Iface_DEFUN handlePong(struct Message* msg, struct SubnodePathfinder_pvt* pf) { //Log_debug(pf->log, "Received pong"); return NULL; } static Iface_DEFUN ctrlMsgFromSwitchPinger(struct Message* msg, struct Iface* iface) { struct SubnodePathfinder_pvt* pf = Identity_containerOf(iface, struct SubnodePathfinder_pvt, switchPingerIf); Er_assert(Message_epush32be(msg, PFChan_Pathfinder_CTRL_SENDMSG)); return Iface_next(&pf->pub.eventIf, msg); } static Iface_DEFUN ctrlMsg(struct Message* msg, struct SubnodePathfinder_pvt* pf) { return Iface_next(&pf->switchPingerIf, msg); } static Iface_DEFUN unsetupSession(struct Message* msg, struct SubnodePathfinder_pvt* pf) { struct PFChan_Node node; Er_assert(Message_epop(msg, &node, PFChan_Node_SIZE)); Assert_true(!Message_getLength(msg)); struct Address addr = { .protocolVersion = Endian_bigEndianToHost32(node.version_be), .path = Endian_bigEndianToHost64(node.path_be), }; Bits_memcpy(addr.ip6.bytes, node.ip6, 16); Bits_memcpy(addr.key, node.publicKey, 32); pingNode(pf, &addr); return NULL; } static Iface_DEFUN incomingMsg(struct Message* msg, struct SubnodePathfinder_pvt* pf) { return Iface_next(&pf->msgCoreIf, msg); } static Iface_DEFUN linkState(struct Message* msg, struct SubnodePathfinder_pvt* pf) { while (Message_getLength(msg)) { struct PFChan_LinkState_Entry lse; Er_assert(Message_epop(msg, &lse, PFChan_LinkState_Entry_SIZE)); ReachabilityCollector_updateBandwidthAndDrops( pf->pub.rc, lse.peerLabel, lse.sumOfPackets, lse.sumOfDrops, lse.sumOfKb); } return NULL; } static Iface_DEFUN incomingFromMsgCore(struct Message* msg, struct Iface* iface) { struct SubnodePathfinder_pvt* pf = Identity_containerOf(iface, struct SubnodePathfinder_pvt, msgCoreIf); Assert_true(Message_getLength(msg) >= (RouteHeader_SIZE + DataHeader_SIZE)); struct RouteHeader* rh = (struct RouteHeader*) msg->msgbytes; struct DataHeader* dh = (struct DataHeader*) &rh[1]; Assert_true(DataHeader_getContentType(dh) == ContentType_CJDHT); Assert_true(!Bits_isZero(rh->publicKey, 32)); Assert_true(rh->version_be); Assert_true(rh->sh.label_be); Er_assert(Message_epush32be(msg, PFChan_Pathfinder_SENDMSG)); return Iface_next(&pf->pub.eventIf, msg); } static Iface_DEFUN incomingFromEventIf(struct Message* msg, struct Iface* eventIf) { struct SubnodePathfinder_pvt* pf = Identity_containerOf(eventIf, struct SubnodePathfinder_pvt, pub.eventIf); enum PFChan_Core ev = Er_assert(Message_epop32be(msg)); if (SubnodePathfinder_pvt_state_INITIALIZING == pf->state) { Assert_true(ev == PFChan_Core_CONNECT); return connected(pf, msg); } switch (ev) { case PFChan_Core_SWITCH_ERR: return switchErr(msg, pf); case PFChan_Core_SEARCH_REQ: return searchReq(msg, pf); case PFChan_Core_PEER: return peer(msg, pf); case PFChan_Core_PEER_GONE: return peerGone(msg, pf); case PFChan_Core_SESSION: return session(msg, pf); case PFChan_Core_SESSION_ENDED: return sessionEnded(msg, pf); case PFChan_Core_DISCOVERED_PATH: return discoveredPath(msg, pf); case PFChan_Core_MSG: return incomingMsg(msg, pf); case PFChan_Core_PING: return handlePing(msg, pf); case PFChan_Core_PONG: return handlePong(msg, pf); case PFChan_Core_CTRL_MSG: return ctrlMsg(msg, pf); case PFChan_Core_UNSETUP_SESSION: return unsetupSession(msg, pf); case PFChan_Core_LINK_STATE: return linkState(msg, pf); default:; } Assert_failure("unexpected event [%d]", ev); } static void sendEvent(struct SubnodePathfinder_pvt* pf, enum PFChan_Pathfinder ev, void* data, int size) { struct Allocator* alloc = Allocator_child(pf->alloc); struct Message* msg = Message_new(0, 512+size, alloc); Er_assert(Message_epush(msg, data, size)); Er_assert(Message_epush32be(msg, ev)); Iface_send(&pf->pub.eventIf, msg); Allocator_free(alloc); } void SubnodePathfinder_start(struct SubnodePathfinder* sp) { struct SubnodePathfinder_pvt* pf = Identity_check((struct SubnodePathfinder_pvt*) sp); struct MsgCore* msgCore = pf->msgCore = MsgCore_new(pf->base, pf->rand, pf->alloc, pf->log, pf->myScheme); Iface_plumb(&pf->msgCoreIf, &msgCore->interRouterIf); PingResponder_new(pf->alloc, pf->log, msgCore, pf->br, pf->myScheme); struct ReachabilityCollector* rc = pf->pub.rc = ReachabilityCollector_new( pf->alloc, msgCore, pf->log, pf->base, pf->br, pf->myAddress, pf->myScheme, pf->sp); rc->userData = pf; rc->onChange = rcChange; struct SupernodeHunter* snh = pf->pub.snh = SupernodeHunter_new( pf->alloc, pf->log, pf->base, pf->sp, pf->myPeerAddrs, msgCore, pf->myAddress, rc); pf->ra = ReachabilityAnnouncer_new( pf->alloc, pf->log, pf->base, pf->rand, msgCore, snh, pf->privateKey, pf->myScheme, rc); struct PFChan_Pathfinder_Connect conn = { .superiority_be = Endian_hostToBigEndian32(1), .version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL) }; CString_safeStrncpy(conn.userAgent, "Cjdns subnode pathfinder", 64); sendEvent(pf, PFChan_Pathfinder_CONNECT, &conn, PFChan_Pathfinder_Connect_SIZE); } static void sendCurrentSupernode(void* vsp) { struct SubnodePathfinder_pvt* pf = Identity_check((struct SubnodePathfinder_pvt*) vsp); struct Allocator* alloc = Allocator_child(pf->alloc); struct Message* msgToCore = Message_new(0, 512, alloc); Iface_CALL(sendNode, msgToCore, &pf->pub.snh->snodeAddr, 0, PFChan_Pathfinder_SNODE, pf); Allocator_free(alloc); } struct SubnodePathfinder* SubnodePathfinder_new(struct Allocator* allocator, struct Log* log, struct EventBase* base, struct Random* rand, struct Address* myAddress, uint8_t* privateKey, struct EncodingScheme* myScheme) { struct Allocator* alloc = Allocator_child(allocator); struct SubnodePathfinder_pvt* pf = Allocator_calloc(alloc, sizeof(struct SubnodePathfinder_pvt), 1); Identity_set(pf); pf->alloc = alloc; pf->log = log; pf->base = base; pf->rand = rand; pf->myAddress = myAddress; pf->myPeerAddrs = AddrSet_new(alloc); pf->pub.eventIf.send = incomingFromEventIf; pf->msgCoreIf.send = incomingFromMsgCore; pf->privateKey = privateKey; pf->queryMap.allocator = Allocator_child(alloc); pf->myScheme = myScheme; pf->br = BoilerplateResponder_new(myScheme, alloc); pf->sp = SwitchPinger_new(base, rand, log, myAddress, alloc); pf->switchPingerIf.send = ctrlMsgFromSwitchPinger; Iface_plumb(&pf->switchPingerIf, &pf->sp->controlHandlerIf); Timeout_setInterval(sendCurrentSupernode, pf, 3000, base, alloc); return &pf->pub; }