static void lookup(Dict* args, void* vcontext, String* txid) { struct Context* ctx = vcontext; String* addrStr = Dict_getString(args, String_CONST("address")); char* err = NULL; uint8_t addr[16]; uint8_t resultBuff[60]; char* result = (char*) resultBuff; if (addrStr->len != 39) { err = "address wrong length"; } else if (AddrTools_parseIp(addr, (uint8_t*) addrStr->bytes)) { err = "failed to parse address"; } else { struct Node* n = RouterModule_lookup(addr, ctx->routerModule); if (!n) { result = "not found"; } else if (memcmp(addr, n->address.ip6.bytes, 16)) { Address_print(resultBuff, &n->address); } else { AddrTools_printPath(resultBuff, n->address.path); } } Dict response = Dict_CONST( String_CONST("error"), String_OBJ(String_CONST((err) ? err : "none")), Dict_CONST( String_CONST("result"), String_OBJ(String_CONST(result)), NULL )); Admin_sendMessage(&response, txid, ctx->admin); }
// Called by the TUN device. static inline uint8_t ip6FromTun(struct Message* message, struct Interface* interface) { struct Ducttape* context = (struct Ducttape*) interface->receiverContext; struct Headers_IP6Header* header = (struct Headers_IP6Header*) message->bytes; if (memcmp(header->sourceAddr, context->myAddr.ip6.bytes, 16)) { Log_warn(context->logger, "dropped message because only one address is allowed to be used " "and the source address was different.\n"); return Error_INVALID; } if (!memcmp(header->destinationAddr, context->myAddr.ip6.bytes, 16)) { // I'm Gonna Sit Right Down and Write Myself a Letter interface->sendMessage(message, interface); return Error_NONE; } context->switchHeader = NULL; struct Node* bestNext = RouterModule_lookup(header->destinationAddr, context->routerModule); if (bestNext) { context->forwardTo = &bestNext->address; if (!memcmp(header->destinationAddr, bestNext->address.ip6.bytes, 16)) { // Direct send, skip the innermost layer of encryption. #ifdef Log_DEBUG uint8_t nhAddr[60]; Address_print(nhAddr, &bestNext->address); Log_debug1(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); #endif return sendToRouter(&bestNext->address, message, context); } } // Grab out the header so it doesn't get clobbered. struct Headers_IP6Header headerStore; Bits_memcpyConst(&headerStore, header, Headers_IP6Header_SIZE); context->ip6Header = &headerStore; // Shift over the content. Message_shift(message, -Headers_IP6Header_SIZE); struct Interface* session = SessionManager_getSession(headerStore.destinationAddr, NULL, context->sm); // This comes out at outgoingFromMe() context->layer = INNER_LAYER; return session->sendMessage(message, session); }
/** * Send an arbitrary message to a node. * * @param message to be sent, must be prefixed with IpTunnel_PacketInfoHeader. * @param iface an interface for which receiverContext is the ducttape. */ static uint8_t sendToNode(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*)iface->receiverContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true); struct IpTunnel_PacketInfoHeader* header = (struct IpTunnel_PacketInfoHeader*) message->bytes; Message_shift(message, -IpTunnel_PacketInfoHeader_SIZE); struct Node* n = RouterModule_lookup(header->nodeIp6Addr, context->routerModule); if (n) { if (!Bits_memcmp(header->nodeKey, n->address.key, 32)) { // Found the node. #ifdef Log_DEBUG uint8_t nhAddr[60]; Address_print(nhAddr, &n->address); Log_debug(context->logger, "Sending arbitrary data to [%s]", nhAddr); #endif struct SessionManager_Session* session = SessionManager_getSession(n->address.ip6.bytes, n->address.key, context->sm); n->version = session->version = (n->version > session->version) ? n->version : session->version; dtHeader->switchLabel = n->address.path; return sendToRouter(message, dtHeader, session, context); } } #ifdef Log_DEBUG uint8_t printedIp6[40]; AddrTools_printIp(printedIp6, header->nodeIp6Addr); Log_debug(context->logger, "Couldn't find node [%s] for sending to.", printedIp6); #endif // Now lets trigger a search for this node. uint64_t now = Time_currentTimeMilliseconds(context->eventBase); if (context->timeOfLastSearch + context->timeBetweenSearches < now) { context->timeOfLastSearch = now; RouterModule_search(header->nodeIp6Addr, context->routerModule, context->alloc); } return 0; }
/** * Messages with content encrypted and header decrypted are sent here to be forwarded. * they may come from us, or from another node and may be to us or to any other node. * Message is aligned on the beginning of the ipv6 header. */ static inline int core(struct Message* message, struct Ducttape* context) { context->ip6Header = (struct Headers_IP6Header*) message->bytes; if (isForMe(message, context)) { Message_shift(message, -Headers_IP6Header_SIZE); if (memcmp(context->routerAddress, context->ip6Header->sourceAddr, 16)) { // triple encrypted // This call goes to incomingForMe() context->layer = INNER_LAYER; context->session = SessionManager_getSession(context->ip6Header->sourceAddr, NULL, context->sm); return context->session->receiveMessage(message, context->session); } else { // double encrypted, inner layer plaintext. // The session is still set from the router-to-router traffic and that is the one we use // to determine the node's id. return incomingForMe(message, context, CryptoAuth_getHerPublicKey(context->session)); } } if (context->ip6Header->hopLimit == 0) { Log_debug(context->logger, "dropped message because hop limit has been exceeded.\n"); // TODO: send back an error message in response. return Error_UNDELIVERABLE; } context->ip6Header->hopLimit--; struct Address* ft = context->forwardTo; context->forwardTo = NULL; if (!ft) { struct Node* bestNext = RouterModule_lookup(context->ip6Header->destinationAddr, context->routerModule); if (bestNext) { ft = &bestNext->address; } } if (ft) { #ifdef Log_DEBUG uint8_t nhAddr[60]; Address_print(nhAddr, ft); if (memcmp(context->ip6Header->destinationAddr, ft->ip6.bytes, 16)) { // Potentially forwarding for ourselves. struct Address destination; Bits_memcpyConst(destination.ip6.bytes, context->ip6Header->destinationAddr, 16); uint8_t ipAddr[40]; Address_printIp(ipAddr, &destination); Log_debug2(context->logger, "Forwarding data to %s via %s\n", ipAddr, nhAddr); } else { // Definitely forwarding on behalf of someone else. Log_debug1(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); } #endif return sendToRouter(ft, message, context); } Log_debug(context->logger, "Dropped message because this node is the closest known " "node to the destination.\n"); return Error_UNDELIVERABLE; }
static void pingNode(Dict* args, void* vctx, String* txid, struct Allocator* requestAlloc) { struct Context* ctx = Identity_cast((struct Context*) vctx); String* pathStr = Dict_getString(args, String_CONST("path")); int64_t* timeoutPtr = Dict_getInt(args, String_CONST("timeout")); uint32_t timeout = (timeoutPtr && *timeoutPtr > 0) ? *timeoutPtr : 0; char* err = NULL; struct Address addr = {.path=0}; struct Node* n = NULL; if (pathStr->len == 19 && !AddrTools_parsePath(&addr.path, (uint8_t*) pathStr->bytes)) { n = RouterModule_getNode(addr.path, ctx->router); } else if (!AddrTools_parseIp(addr.ip6.bytes, (uint8_t*) pathStr->bytes)) { n = RouterModule_lookup(addr.ip6.bytes, ctx->router); if (n && Bits_memcmp(addr.ip6.bytes, n->address.ip6.bytes, 16)) { n = NULL; } } else { err = "Unexpected address, must be either an ipv6 address " "eg: 'fc4f:d:e499:8f5b:c49f:6e6b:1ae:3120', 19 char path eg: '0123.4567.89ab.cdef'"; } if (!err) { if (!n) { err = "could not find node to ping"; } else { struct RouterModule_Promise* rp = RouterModule_pingNode(n, timeout, ctx->router, ctx->allocator); struct Ping* ping = Allocator_calloc(rp->alloc, sizeof(struct Ping), 1); Identity_set(ping); ping->txid = String_clone(txid, rp->alloc); ping->rp = rp; ping->ctx = ctx; rp->userData = ping; rp->callback = pingResponse; } } if (err) { Dict errDict = Dict_CONST(String_CONST("error"), String_OBJ(String_CONST(err)), NULL); Admin_sendMessage(&errDict, txid, ctx->admin); } } void RouterModule_admin_register(struct RouterModule* module, struct Admin* admin, struct Allocator* alloc) { struct Context* ctx = Allocator_clone(alloc, (&(struct Context) { .admin = admin, .allocator = alloc, .router = module })); Identity_set(ctx); Admin_registerFunction("RouterModule_lookup", lookup, ctx, true, ((struct Admin_FunctionArg[]) { { .name = "address", .required = 1, .type = "String" } }), admin);
/** * Messages with content encrypted and header decrypted are sent here to be forwarded. * they may come from us, or from another node and may be to us or to any other node. * Message is aligned on the beginning of the ipv6 header. */ static inline int core(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { struct Headers_IP6Header* ip6Header = (struct Headers_IP6Header*) message->bytes; dtHeader->ip6Header = ip6Header; if (isForMe(message, context)) { Message_shift(message, -Headers_IP6Header_SIZE); if (Bits_memcmp(session->ip6, ip6Header->sourceAddr, 16)) { // triple encrypted // This call goes to incomingForMe() struct SessionManager_Session* session = SessionManager_getSession(ip6Header->sourceAddr, NULL, context->sm); #ifdef Log_DEBUG uint8_t addr[40]; AddrTools_printIp(addr, ip6Header->sourceAddr); Log_debug(context->logger, "Incoming layer3 message, ostensibly from [%s]", addr); #endif dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_INNER; return session->iface.receiveMessage(message, &session->iface); } else { // double encrypted, inner layer plaintext. // The session is still set from the router-to-router traffic and that is the one we use // to determine the node's id. return incomingForMe(message, dtHeader, session, context, CryptoAuth_getHerPublicKey(&session->iface)); } } if (ip6Header->hopLimit == 0) { Log_debug(context->logger, "dropped message because hop limit has been exceeded.\n"); // TODO: send back an error message in response. return Error_UNDELIVERABLE; } ip6Header->hopLimit--; struct SessionManager_Session* nextHopSession = NULL; if (!dtHeader->nextHopReceiveHandle || !dtHeader->switchLabel) { struct Node* n = RouterModule_lookup(ip6Header->destinationAddr, context->routerModule); if (n) { nextHopSession = SessionManager_getSession(n->address.ip6.bytes, n->address.key, context->sm); dtHeader->switchLabel = n->address.path; } } else { nextHopSession = SessionManager_sessionForHandle(dtHeader->nextHopReceiveHandle, context->sm); } if (nextHopSession) { #ifdef Log_DEBUG struct Address addr; Bits_memcpyConst(addr.ip6.bytes, nextHopSession->ip6, 16); addr.path = dtHeader->switchLabel; uint8_t nhAddr[60]; Address_print(nhAddr, &addr); if (Bits_memcmp(ip6Header->destinationAddr, addr.ip6.bytes, 16)) { // Potentially forwarding for ourselves. struct Address destination; Bits_memcpyConst(destination.ip6.bytes, ip6Header->destinationAddr, 16); uint8_t ipAddr[40]; Address_printIp(ipAddr, &destination); Log_debug(context->logger, "Forwarding data to %s via %s\n", ipAddr, nhAddr); } else { // Definitely forwarding on behalf of someone else. Log_debug(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); } #endif return sendToRouter(message, dtHeader, nextHopSession, context); } #ifdef Log_INFO struct Address destination; Bits_memcpyConst(destination.ip6.bytes, ip6Header->destinationAddr, 16); uint8_t ipAddr[40]; Address_printIp(ipAddr, &destination); Log_info(context->logger, "Dropped message because this node is the closest known " "node to the destination %s.", ipAddr); #endif return Error_UNDELIVERABLE; }
// Called by the TUN device. static inline uint8_t incomingFromTun(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) iface->receiverContext); uint16_t ethertype = TUNMessageType_pop(message); struct Headers_IP6Header* header = (struct Headers_IP6Header*) message->bytes; int version = Headers_getIpVersion(message->bytes); if ((ethertype == Ethernet_TYPE_IP4 && version != 4) || (ethertype == Ethernet_TYPE_IP6 && version != 6)) { Log_warn(context->logger, "dropped packet because ip version [%d] " "doesn't match ethertype [%u].", version, Endian_bigEndianToHost16(ethertype)); return Error_INVALID; } if (ethertype != Ethernet_TYPE_IP6 || !AddressCalc_validAddress(header->sourceAddr)) { return context->ipTunnel->tunInterface.sendMessage(message, &context->ipTunnel->tunInterface); } if (Bits_memcmp(header->sourceAddr, context->myAddr.ip6.bytes, 16)) { uint8_t expectedSource[40]; AddrTools_printIp(expectedSource, context->myAddr.ip6.bytes); uint8_t packetSource[40]; AddrTools_printIp(packetSource, header->sourceAddr); Log_warn(context->logger, "dropped packet from [%s] because all messages must have source address [%s]", (char*) packetSource, (char*) expectedSource); return Error_INVALID; } if (!Bits_memcmp(header->destinationAddr, context->myAddr.ip6.bytes, 16)) { // I'm Gonna Sit Right Down and Write Myself a Letter TUNMessageType_push(message, ethertype); iface->sendMessage(message, iface); return Error_NONE; } struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true); struct Node* bestNext = RouterModule_lookup(header->destinationAddr, context->routerModule); struct SessionManager_Session* nextHopSession; if (bestNext) { nextHopSession = SessionManager_getSession(bestNext->address.ip6.bytes, bestNext->address.key, context->sm); bestNext->version = nextHopSession->version = (bestNext->version > nextHopSession->version) ? bestNext->version : nextHopSession->version; dtHeader->switchLabel = bestNext->address.path; dtHeader->nextHopReceiveHandle = Endian_bigEndianToHost32(nextHopSession->receiveHandle_be); if (!Bits_memcmp(header->destinationAddr, bestNext->address.ip6.bytes, 16)) { // Direct send, skip the innermost layer of encryption. #ifdef Log_DEBUG uint8_t nhAddr[60]; Address_print(nhAddr, &bestNext->address); Log_debug(context->logger, "Forwarding data to %s (last hop)\n", nhAddr); #endif return sendToRouter(message, dtHeader, nextHopSession, context); } // else { the message will need to be 3 layer encrypted but since we already did a lookup // of the best node to forward to, we can skip doing another lookup by storing a pointer // to that node in the context (bestNext). } else { #ifdef Log_WARN uint8_t thisAddr[40]; uint8_t destAddr[40]; AddrTools_printIp(thisAddr, context->myAddr.ip6.bytes); AddrTools_printIp(destAddr, header->destinationAddr); Log_warn(context->logger, "Dropped message from TUN because this node [%s] is closest to dest [%s]", thisAddr, destAddr); #endif return Error_UNDELIVERABLE; } #ifdef Log_DEBUG uint8_t destAddr[40]; AddrTools_printIp(destAddr, header->destinationAddr); uint8_t nhAddr[60]; Address_print(nhAddr, &bestNext->address); Log_debug(context->logger, "Sending to [%s] via [%s]", destAddr, nhAddr); #endif struct SessionManager_Session* session = SessionManager_getSession(header->destinationAddr, NULL, context->sm); // Copy the IP6 header back from where the CA header will be placed. // this is a mess. // We can't just copy the header to a safe place because the CryptoAuth // might buffer the message and send a connect-to-me packet and when the // hello packet comes in return, the CA will send the message and the header // needs to be in the message buffer. // // The CryptoAuth may send a 120 byte CA header and it might only send a 4 byte // nonce and 16 byte authenticator depending on its state. if (CryptoAuth_getState(&session->iface) < CryptoAuth_HANDSHAKE3) { // shift, copy, shift because shifting asserts that there is enough buffer space. Message_shift(message, Headers_CryptoAuth_SIZE + 4); Bits_memcpyConst(message->bytes, header, Headers_IP6Header_SIZE); Message_shift(message, -(Headers_IP6Header_SIZE + Headers_CryptoAuth_SIZE + 4)); // now push the receive handle *under* the CA header. Message_push(message, &session->receiveHandle_be, 4); debugHandles0(context->logger, session, "layer3 sending start message"); } else { // shift, copy, shift because shifting asserts that there is enough buffer space. Message_shift(message, 20); Bits_memmoveConst(message->bytes, header, Headers_IP6Header_SIZE); Message_shift(message, -(20 + Headers_IP6Header_SIZE)); debugHandles0(context->logger, session, "layer3 sending run message"); } // This comes out at outgoingFromCryptoAuth() then outgoingFromMe() dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_INNER; return session->iface.sendMessage(message, &session->iface); }