static Iface_DEFUN switchErr(struct Message* msg, struct Pathfinder_pvt* pf) { struct PFChan_Core_SwitchErr switchErr; Message_pop(msg, &switchErr, PFChan_Core_SwitchErr_MIN_SIZE, NULL); uint64_t path = Endian_bigEndianToHost64(switchErr.sh.label_be); uint64_t pathAtErrorHop = Endian_bigEndianToHost64(switchErr.ctrlErr.cause.label_be); uint8_t pathStr[20]; AddrTools_printPath(pathStr, path); int err = Endian_bigEndianToHost32(switchErr.ctrlErr.errorType_be); Log_debug(pf->log, "switch err from [%s] type [%s][%d]", pathStr, Error_strerror(err), err); struct Node_Link* link = NodeStore_linkForPath(pf->nodeStore, path); uint8_t nodeAddr[16]; if (link) { Bits_memcpy(nodeAddr, link->child->address.ip6.bytes, 16); } NodeStore_brokenLink(pf->nodeStore, path, pathAtErrorHop); if (link) { // Don't touch the node again, it might be a dangling pointer SearchRunner_search(nodeAddr, 20, 3, pf->searchRunner, pf->alloc); } return NULL; }
/** * Splice a label and a label fragment together. * */ uint64_t LabelSplicer_splice(uint64_t goHere_be, uint64_t viaHere_be) { uint64_t goHere = Endian_bigEndianToHost64(goHere_be); uint64_t viaHere = Endian_bigEndianToHost64(viaHere_be); uint64_t log2ViaHere = Bits_log2x64(viaHere); if (Bits_log2x64(goHere) + log2ViaHere > 61) { // Too big, can't splice. return UINT64_MAX; } return Endian_hostToBigEndian64(((goHere ^ 1) << log2ViaHere) ^ viaHere); }
int main() { printInfo(); volatile uint64_t b = 0x0123456789abcdef; volatile uint64_t sb = 0xefcdab8967452301; volatile uint32_t a = 0x01234567; volatile uint32_t sa = 0x67452301; volatile uint16_t c = 0xcabe; volatile uint16_t sc = 0xbeca; if (!Endian_isBigEndian()) { Assert_true(c == Endian_bigEndianToHost16(sc)); Assert_true(c == Endian_hostToBigEndian16(sc)); Assert_true(c == Endian_hostToLittleEndian16(c)); Assert_true(c == Endian_littleEndianToHost16(c)); Assert_true(a == Endian_bigEndianToHost32(sa)); Assert_true(a == Endian_hostToBigEndian32(sa)); Assert_true(a == Endian_hostToLittleEndian32(a)); Assert_true(a == Endian_littleEndianToHost32(a)); Assert_true(b == Endian_bigEndianToHost64(sb)); Assert_true(b == Endian_hostToBigEndian64(sb)); Assert_true(b == Endian_hostToLittleEndian64(b)); Assert_true(b == Endian_littleEndianToHost64(b)); } else { Assert_true(c == Endian_bigEndianToHost16(c)); Assert_true(c == Endian_hostToBigEndian16(c)); Assert_true(c == Endian_hostToLittleEndian16(sc)); Assert_true(c == Endian_littleEndianToHost16(sc)); Assert_true(a == Endian_bigEndianToHost32(a)); Assert_true(a == Endian_hostToBigEndian32(a)); Assert_true(a == Endian_hostToLittleEndian32(sa)); Assert_true(a == Endian_littleEndianToHost32(sa)); Assert_true(b == Endian_bigEndianToHost64(b)); Assert_true(b == Endian_hostToBigEndian64(b)); Assert_true(b == Endian_hostToLittleEndian64(sb)); Assert_true(b == Endian_littleEndianToHost64(sb)); } Assert_true(b == Endian_byteSwap64(sb)); Assert_true(a == Endian_byteSwap32(sa)); Assert_true(c == Endian_byteSwap16(sc)); return 0; }
static void onGetPeers(Dict* msg, struct Address* src, struct Allocator* tmpAlloc, struct MsgCore_Handler* handler) { struct GetPeersResponder_pvt* gprp = Identity_check((struct GetPeersResponder_pvt*) handler->userData); Log_debug(gprp->log, "Received getPeers req from [%s]", Address_toString(src, tmpAlloc)->bytes); String* txid = Dict_getStringC(msg, "txid"); if (!txid) { Log_debug(gprp->log, "getPeers missing txid"); return; } String* nearLabelStr = Dict_getStringC(msg, "tar"); uint64_t label; if (!nearLabelStr || nearLabelStr->len != 8) { Log_debug(gprp->log, "getPeers does not contain proper target"); return; } else { uint64_t label_be; Bits_memcpy(&label_be, nearLabelStr->bytes, 8); label = Endian_bigEndianToHost64(label_be); } struct Address outAddrs[GETPEERS_RESPONSE_NODES] = { { .padding = 0 } };
static void addressForNode(struct Address* addrOut, struct Message* msg) { struct PFChan_Node node; Message_pop(msg, &node, PFChan_Node_SIZE, NULL); Assert_true(!msg->length); 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); }
/** * Get the label for a particular destination from a given source. * This needs to be called before handing out a label because if a source interface is * represented using more bits than the destination interface, the destination interface * must be padded out so that the switch will find the source and destination labels compatable. * * @param target_be the label for the location to send to in big endian. * @param whoIsAsking_be the label for the node which we are sending the target to in big endian. * @return the modified target for that node in big endian. */ uint64_t LabelSplicer_getLabelFor(uint64_t target_be, uint64_t whoIsAsking_be) { uint64_t target = Endian_bigEndianToHost64(target_be); uint64_t whoIsAsking = Endian_bigEndianToHost64(whoIsAsking_be); uint32_t targetBits = NumberCompress_bitsUsedForLabel(target); uint32_t whoIsAskingBits = NumberCompress_bitsUsedForLabel(whoIsAsking); if (targetBits >= whoIsAskingBits) { return target_be; } uint32_t targetIfaceNum = NumberCompress_getDecompressed(target, targetBits); uint64_t out = ((target & (UINT64_MAX << targetBits)) << (whoIsAskingBits - targetBits)) | NumberCompress_getCompressed(targetIfaceNum, whoIsAskingBits); return Endian_hostToBigEndian64(out); }
/** * Handle an incoming control message from a switch. * * @param context the ducttape context. * @param message the control message, this should be alligned on the beginning of the content, * that is to say, after the end of the switch header. * @param switchHeader the header. * @param switchIf the interface which leads to the switch. * @param isFormV8 true if the control message is in the form specified by protocol version 8+ */ static Iface_DEFUN incomingFromCore(struct Message* msg, struct Iface* coreIf) { struct ControlHandler_pvt* ch = Identity_check((struct ControlHandler_pvt*) coreIf); struct RouteHeader routeHdr; Message_pop(msg, &routeHdr, RouteHeader_SIZE, NULL); uint8_t labelStr[20]; uint64_t label = Endian_bigEndianToHost64(routeHdr.sh.label_be); AddrTools_printPath(labelStr, label); // happens in benchmark // Log_debug(ch->log, "ctrl packet from [%s]", labelStr); if (msg->length < 4 + Control_Header_SIZE) { Log_info(ch->log, "DROP runt ctrl packet from [%s]", labelStr); return NULL; } Assert_true(routeHdr.flags & RouteHeader_flags_CTRLMSG); if (Checksum_engine(msg->bytes, msg->length)) { Log_info(ch->log, "DROP ctrl packet from [%s] with invalid checksum", labelStr); return NULL; } struct Control* ctrl = (struct Control*) msg->bytes; if (ctrl->header.type_be == Control_ERROR_be) { return handleError(msg, ch, label, labelStr); } else if (ctrl->header.type_be == Control_KEYPING_be || ctrl->header.type_be == Control_PING_be) { return handlePing(msg, ch, label, labelStr, ctrl->header.type_be); } else if (ctrl->header.type_be == Control_KEYPONG_be || ctrl->header.type_be == Control_PONG_be) { Log_debug(ch->log, "got switch pong from [%s]", labelStr); Message_push(msg, &routeHdr, RouteHeader_SIZE, NULL); return Iface_next(&ch->pub.switchPingerIf, msg); } Log_info(ch->log, "DROP control packet of unknown type from [%s], type [%d]", labelStr, Endian_bigEndianToHost16(ctrl->header.type_be)); return NULL; }
/** * Handle an incoming control message from a switch. * * @param context the ducttape context. * @param message the control message, this should be alligned on the beginning of the content, * that is to say, after the end of the switch header. * @param switchHeader the header. * @param switchIf the interface which leads to the switch. * @param isFormV8 true if the control message is in the form specified by protocol version 8+ */ static Iface_DEFUN incomingFromCore(struct Message* msg, struct Iface* coreIf) { struct ControlHandler_pvt* ch = Identity_check((struct ControlHandler_pvt*) coreIf); struct SwitchHeader switchHeader; Message_pop(msg, &switchHeader, SwitchHeader_SIZE, NULL); uint8_t labelStr[20]; uint64_t label = Endian_bigEndianToHost64(switchHeader.label_be); AddrTools_printPath(labelStr, label); Log_debug(ch->log, "ctrl packet from [%s]", labelStr); if (msg->length < 4 + Control_Header_SIZE) { Log_info(ch->log, "DROP runt ctrl packet from [%s]", labelStr); return NULL; } Assert_true(Message_pop32(msg, NULL) == 0xffffffff); if (Checksum_engine(msg->bytes, msg->length)) { Log_info(ch->log, "DROP ctrl packet from [%s] with invalid checksum", labelStr); return NULL; } struct Control* ctrl = (struct Control*) msg->bytes; if (ctrl->header.type_be == Control_ERROR_be) { return handleError(msg, ch, label, labelStr); } else if (ctrl->header.type_be == Control_KEYPING_be || ctrl->header.type_be == Control_PING_be) { return handlePing(msg, ch, label, labelStr, ctrl->header.type_be); } else if (ctrl->header.type_be == Control_KEYPONG_be || ctrl->header.type_be == Control_PONG_be) { Log_debug(ch->log, "got switch pong from [%s]", labelStr); // Shift back over the header Message_shift(msg, 4 + SwitchHeader_SIZE, NULL); return Iface_next(&ch->pub.switchPingerIf, msg); } Log_info(ch->log, "DROP control packet of unknown type from [%s], type [%d]", labelStr, Endian_bigEndianToHost16(ctrl->header.type_be)); return NULL; }
/** * Parse out a path. * * @param out a pointer to a number which will be set to the path in HOST BYTE ORDER. * @param netAddr a string representation of the path such as "0000.1111.2222.3333" in Big Endian. * @return 0 if successful, -1 if the netAddr is malformed. */ int AddrTools_parsePath(uint64_t* out, const uint8_t netAddr[20]) { if (netAddr[4] != '.' || netAddr[9] != '.' || netAddr[14] != '.' || netAddr[19] != '\0') { return -1; } uint8_t hex[16] = { netAddr[ 0], netAddr[ 1], netAddr[ 2], netAddr[ 3], netAddr[ 5], netAddr[ 6], netAddr[ 7], netAddr[ 8], netAddr[10], netAddr[11], netAddr[12], netAddr[13], netAddr[15], netAddr[16], netAddr[17], netAddr[18] }; uint8_t numberBytes[8]; if (Hex_decode(numberBytes, 8, hex, 16) != 8) { return -1; } uint64_t out_be; Bits_memcpy(&out_be, numberBytes, 8); *out = Endian_bigEndianToHost64(out_be); return 0; }
static int handleOutgoing(struct DHTMessage* dmessage, void* vcontext) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) vcontext); struct Message message = { .length = dmessage->length, .bytes = (uint8_t*) dmessage->bytes, .padding = 512, .capacity = DHTMessage_MAX_SIZE }; Message_shift(&message, Headers_UDPHeader_SIZE); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message.bytes; uh->sourceAndDestPorts = 0; uh->length_be = Endian_hostToBigEndian16(dmessage->length); uh->checksum_be = 0; uint16_t payloadLength = message.length; Message_shift(&message, Headers_IP6Header_SIZE); struct Headers_IP6Header* header = (struct Headers_IP6Header*) message.bytes; header->versionClassAndFlowLabel = 0; header->flowLabelLow_be = 0; header->nextHeader = 17; header->hopLimit = 0; header->payloadLength_be = Endian_hostToBigEndian16(payloadLength); Bits_memcpyConst(header->sourceAddr, context->myAddr.ip6.bytes, Address_SEARCH_TARGET_SIZE); Bits_memcpyConst(header->destinationAddr, dmessage->address->ip6.bytes, Address_SEARCH_TARGET_SIZE); #ifdef Log_DEBUG Assert_true(!((uintptr_t)dmessage->bytes % 4) || !"alignment fault"); #endif uh->checksum_be = Checksum_udpIp6(header->sourceAddr, (uint8_t*) uh, message.length - Headers_IP6Header_SIZE); struct Ducttape_MessageHeader* dtHeader = getDtHeader(&message, true); dtHeader->ip6Header = header; dtHeader->switchLabel = dmessage->address->path; struct SessionManager_Session* session = SessionManager_getSession(dmessage->address->ip6.bytes, dmessage->address->key, context->sm); if (session->version == Version_DEFAULT_ASSUMPTION && dmessage->replyTo) { int64_t* verPtr = Dict_getInt(dmessage->replyTo->asDict, String_CONST("p")); session->version = (verPtr) ? *verPtr : Version_DEFAULT_ASSUMPTION; } if (session->version == Version_DEFAULT_ASSUMPTION) { struct Node* n = RouterModule_getNode(dmessage->address->path, context->routerModule); if (n) { n->version = session->version = (n->version > session->version) ? n->version : session->version; } } sendToRouter(&message, dtHeader, session, context); return 0; } // Aligned on the beginning of the content. static inline bool isRouterTraffic(struct Message* message, struct Headers_IP6Header* ip6) { if (ip6->nextHeader != 17 || ip6->hopLimit != 0) { return false; } struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; return message->length >= Headers_UDPHeader_SIZE && uh->sourceAndDestPorts == 0 && (int) Endian_bigEndianToHost16(uh->length_be) == (message->length - Headers_UDPHeader_SIZE); } #define debugHandles(logger, session, message, ...) \ do { \ uint8_t ip[40]; \ AddrTools_printIp(ip, session->ip6); \ Log_debug(logger, "ver[%u] send[%d] recv[%u] ip[%s] " message, \ session->version, \ Endian_hostToBigEndian32(session->sendHandle_be), \ Endian_hostToBigEndian32(session->receiveHandle_be), \ ip, \ __VA_ARGS__); \ } while (0) //CHECKFILES_IGNORE expecting a ; #define debugHandles0(logger, session, message) \ debugHandles(logger, session, message "%s", "") #define debugHandlesAndLabel(logger, session, label, message, ...) \ do { \ uint8_t path[20]; \ AddrTools_printPath(path, label); \ debugHandles(logger, session, "path[%s] " message, path, __VA_ARGS__); \ } while (0) //CHECKFILES_IGNORE expecting a ; #define debugHandlesAndLabel0(logger, session, label, message) \ debugHandlesAndLabel(logger, session, label, "%s", message) /** * Message which is for us, message is aligned on the beginning of the content. * this is called from core() which calls through an interfaceMap. */ static inline uint8_t incomingForMe(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context, uint8_t herPublicKey[32]) { struct Address addr; Bits_memcpyConst(addr.ip6.bytes, session->ip6, 16); //AddressCalc_addressForPublicKey(addr.ip6.bytes, herPubKey); if (Bits_memcmp(addr.ip6.bytes, dtHeader->ip6Header->sourceAddr, 16)) { #ifdef Log_DEBUG uint8_t keyAddr[40]; Address_printIp(keyAddr, &addr); Bits_memcpyConst(addr.ip6.bytes, dtHeader->ip6Header->sourceAddr, 16); uint8_t srcAddr[40]; Address_printIp(srcAddr, &addr); Log_debug(context->logger, "Dropped packet because source address is not same as key.\n" " %s source addr\n" " %s hash of key\n", srcAddr, keyAddr); #endif return Error_INVALID; } if (isRouterTraffic(message, dtHeader->ip6Header)) { // Check the checksum. struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; if (Checksum_udpIp6(dtHeader->ip6Header->sourceAddr, (uint8_t*)uh, message->length)) { #ifdef Log_DEBUG uint8_t keyAddr[40]; Address_printIp(keyAddr, &addr); Log_debug(context->logger, "Router packet with incorrect checksum, from [%s]", keyAddr); #endif return Error_INVALID; } // Shift off the UDP header. Message_shift(message, -Headers_UDPHeader_SIZE); addr.path = Endian_bigEndianToHost64(dtHeader->switchHeader->label_be); Bits_memcpyConst(addr.key, herPublicKey, 32); return incomingDHT(message, &addr, context); } if (!context->userIf) { Log_warn(context->logger, "Dropping message because there is no router interface configured.\n"); return Error_UNDELIVERABLE; } // prevent router advertizement schenanigans if (dtHeader->ip6Header->hopLimit == 255) { dtHeader->ip6Header->hopLimit--; } // Now write a message to the TUN device. // Need to move the ipv6 header forward up to the content because there's a crypto header // between the ipv6 header and the content which just got eaten. Message_shift(message, Headers_IP6Header_SIZE); uint16_t sizeDiff = message->bytes - (uint8_t*)dtHeader->ip6Header; if (sizeDiff) { dtHeader->ip6Header->payloadLength_be = Endian_hostToBigEndian16( Endian_bigEndianToHost16(dtHeader->ip6Header->payloadLength_be) - sizeDiff); Bits_memmoveConst(message->bytes, dtHeader->ip6Header, Headers_IP6Header_SIZE); } TUNMessageType_push(message, Ethernet_TYPE_IP6); context->userIf->sendMessage(message, context->userIf); return Error_NONE; } uint8_t Ducttape_injectIncomingForMe(struct Message* message, struct Ducttape* dt, uint8_t herPublicKey[32]) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*)dt); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true); struct Headers_SwitchHeader sh; Bits_memcpyConst(&sh, message->bytes, Headers_SwitchHeader_SIZE); dtHeader->switchHeader = &sh; Message_shift(message, -Headers_SwitchHeader_SIZE); struct Headers_IP6Header ip6; Bits_memcpyConst(&ip6, message->bytes, Headers_IP6Header_SIZE); dtHeader->ip6Header = &ip6; Message_shift(message, -Headers_IP6Header_SIZE); struct SessionManager_Session s; AddressCalc_addressForPublicKey(s.ip6, herPublicKey); s.version = Version_CURRENT_PROTOCOL; return incomingForMe(message, dtHeader, &s, context, herPublicKey); } /** * Send a message to another switch. * Switchheader will precede the message. */ static inline uint8_t sendToSwitch(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { uint64_t label = dtHeader->switchLabel; if (CryptoAuth_getState(&session->iface) >= CryptoAuth_HANDSHAKE3) { debugHandlesAndLabel0(context->logger, session, label, "layer2 sending run message"); uint32_t sendHandle_be = session->sendHandle_be; #ifdef Version_2_COMPAT if (session->version < 3) { sendHandle_be |= HANDLE_FLAG_BIT_be; } #endif Message_push(message, &sendHandle_be, 4); } else { debugHandlesAndLabel0(context->logger, session, label, "layer2 sending start message"); #ifdef Version_2_COMPAT if (session->version < 3) { Message_push(message, &session->receiveHandle_be, 4); } #endif } Message_shift(message, Headers_SwitchHeader_SIZE); Assert_true(message->bytes == (uint8_t*)dtHeader->switchHeader); return context->switchInterface.receiveMessage(message, &context->switchInterface); }
/** * Determine if the node at the end of the given label is one hop away. * * @param label_be the label to test. * @return true if the node is 1 hop away, false otherwise. */ bool LabelSplicer_isOneHop(uint64_t label_be) { uint64_t label = Endian_bigEndianToHost64(label_be); return (int)NumberCompress_bitsUsedForLabel(label) == Bits_log2x64(label); }
static int reconnectionNewEndpointTest(struct InterfaceController* ifController, uint8_t* pk, struct Message** fromSwitchPtr, struct Allocator* alloc, struct EventBase* eventBase, struct Log* logger, struct Interface* routerIf, struct Random* rand) { struct Message* message; struct Interface iface = { .sendMessage = messageFromInterface, .senderContext = &message, .allocator = alloc }; uint8_t* buffer = Allocator_malloc(alloc, 512); struct Message* outgoing = &(struct Message) { .length = 0, .padding = 512, .bytes = buffer + 512 }; struct CryptoAuth* externalCa = CryptoAuth_new(alloc, NULL, eventBase, logger, rand); struct Interface* wrapped = CryptoAuth_wrapInterface(&iface, pk, NULL, false, "", externalCa); CryptoAuth_setAuth(String_CONST("passwd"), 1, wrapped); struct Interface icIface = { .allocator = alloc, .sendMessage = messageFromInterface, .senderContext = &message }; InterfaceController_registerPeer(ifController, NULL, NULL, true, false, &icIface); uint8_t hexBuffer[1025]; for (int i = 0; i < 4; i++) { outgoing->length = 0; outgoing->padding = 512; outgoing->bytes = buffer + 512; Message_shift(outgoing, 12, NULL); Bits_memcpyConst(outgoing->bytes, "hello world", 12); Message_shift(outgoing, SwitchHeader_SIZE, NULL); Bits_memcpyConst(outgoing->bytes, (&(struct SwitchHeader) { .label_be = Endian_hostToBigEndian64(1), .lowBits_be = 0 }), SwitchHeader_SIZE); wrapped->sendMessage(outgoing, wrapped); *fromSwitchPtr = NULL; icIface.receiveMessage(outgoing, &icIface); message = *fromSwitchPtr; Assert_true(message); Assert_true(message->length == 24); Hex_encode(hexBuffer, 1025, message->bytes, message->length); printf("%s\n", hexBuffer); // Need to bounce the packets back when connecting after the first try. // This is needed to establish the CryptoAuth session and make the InterfaceController // merge the endpoints. if (i > 0) { // Reverse the bits to reverse the path: uint64_t path; Bits_memcpyConst(&path, message->bytes, 8); path = Bits_bitReverse64(path); Bits_memcpyConst(message->bytes, &path, 8); printf("sending back response.\n"); routerIf->receiveMessage(message, routerIf); printf("forwarding response to external cryptoAuth.\n"); iface.receiveMessage(message, &iface); printf("forwarded.\n"); } else { printf("not responding because we don't want to establish a connection yet.\n"); } } // check everything except the label Assert_true(!CString_strcmp((char*)hexBuffer+16, "0000000068656c6c6f20776f726c6400")); // check label: make sure the interface has been switched back into position 0. uint64_t label_be; Hex_decode((uint8_t*) &label_be, 8, hexBuffer, 16); uint64_t rev_label = Bits_bitReverse64(Endian_bigEndianToHost64(label_be)); // check label is decoded to 0 Assert_true(0 == NumberCompress_getDecompressed(rev_label, NumberCompress_bitsUsedForLabel(rev_label))); // check no other bits are set uint64_t out = NumberCompress_getCompressed(0, NumberCompress_bitsUsedForLabel(rev_label)); Assert_true(rev_label == out); return 0; }
static int getBadness(struct Address* badAddr, struct Address* selfAddr) { uint64_t xor = Endian_bigEndianToHost64(badAddr->ip6.longs.one_be ^ selfAddr->ip6.longs.one_be); return Bits_log2x64(xor) + Bits_log2x64(badAddr->path); }
/** * This is called as sendMessage() by the switch. * There is only one switch interface which sends all traffic. * message is aligned on the beginning of the switch header. */ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf) { struct Ducttape* context = switchIf->senderContext; struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) message->bytes; Message_shift(message, -Headers_SwitchHeader_SIZE); // The label comes in reversed from the switch because the switch doesn't know that we aren't // another switch ready to parse more bits, bit reversing the label yields the source address. switchHeader->label_be = Bits_bitReverse64(switchHeader->label_be); if (Headers_getMessageType(switchHeader) == Headers_SwitchHeader_TYPE_CONTROL) { uint8_t labelStr[20]; uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be); AddrTools_printPath(labelStr, label); if (message->length < Control_HEADER_SIZE) { Log_info1(context->logger, "dropped runt ctrl packet from [%s]", labelStr); return Error_NONE; } else { Log_debug1(context->logger, "ctrl packet from [%s]", labelStr); } struct Control* ctrl = (struct Control*) message->bytes; bool pong = false; if (ctrl->type_be == Control_ERROR_be) { if (message->length < Control_Error_MIN_SIZE) { Log_info1(context->logger, "dropped runt error packet from [%s]", labelStr); return Error_NONE; } Log_info2(context->logger, "error packet from [%s], error type [%d]", labelStr, Endian_bigEndianToHost32(ctrl->content.error.errorType_be)); RouterModule_brokenPath(Endian_bigEndianToHost64(switchHeader->label_be), context->routerModule); uint8_t causeType = Headers_getMessageType(&ctrl->content.error.cause); if (causeType == Headers_SwitchHeader_TYPE_CONTROL) { if (message->length < Control_Error_MIN_SIZE + Control_HEADER_SIZE) { Log_info1(context->logger, "error packet from [%s] containing runt cause packet", labelStr); return Error_NONE; } struct Control* causeCtrl = (struct Control*) &(&ctrl->content.error.cause)[1]; if (causeCtrl->type_be != Control_PING_be) { Log_info3(context->logger, "error packet from [%s] caused by [%s] packet ([%d])", labelStr, Control_typeString(causeCtrl->type_be), Endian_bigEndianToHost16(causeCtrl->type_be)); } else { Log_debug2(context->logger, "error packet from [%s] in response to ping, length: [%d].", labelStr, message->length); // errors resulting from pings are forwarded back to the pinger. pong = true; } } else if (causeType != Headers_SwitchHeader_TYPE_DATA) { Log_info1(context->logger, "error packet from [%s] containing cause of unknown type [%d]", labelStr); } } else if (ctrl->type_be == Control_PONG_be) { pong = true; } else if (ctrl->type_be == Control_PING_be) { ctrl->type_be = Control_PONG_be; Message_shift(message, Headers_SwitchHeader_SIZE); switchIf->receiveMessage(message, switchIf); } else { Log_info2(context->logger, "control packet of unknown type from [%s], type [%d]", labelStr, Endian_bigEndianToHost16(ctrl->type_be)); } if (pong) { // Shift back over the header Message_shift(message, Headers_SwitchHeader_SIZE); context->switchPingerIf->receiveMessage(message, context->switchPingerIf); } return Error_NONE; } uint8_t* herKey = extractPublicKey(message, switchHeader->label_be, context->logger); int herAddrIndex; if (herKey) { uint8_t herAddrStore[16]; AddressCalc_addressForPublicKey(herAddrStore, herKey); if (herAddrStore[0] != 0xFC) { Log_debug(context->logger, "Got message from peer whose address is not in fc00::/8 range.\n"); return 0; } herAddrIndex = AddressMapper_put(switchHeader->label_be, herAddrStore, &context->addrMap); } else { herAddrIndex = AddressMapper_indexOf(switchHeader->label_be, &context->addrMap); if (herAddrIndex == -1) { uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be); struct Node* n = RouterModule_getNode(label, context->routerModule); if (n) { herAddrIndex = AddressMapper_put(switchHeader->label_be, n->address.ip6.bytes, &context->addrMap); } else { #ifdef Log_DEBUG uint8_t switchAddr[20]; AddrTools_printPath(switchAddr, Endian_bigEndianToHost64(switchHeader->label_be)); Log_debug1(context->logger, "Dropped traffic packet from unknown node. (%s)\n", &switchAddr); #endif return 0; } } } // If the source address is the same as the router address, no third layer of crypto. context->routerAddress = context->addrMap.entries[herAddrIndex].address; // This is needed so that the priority and other information // from the switch header can be passed on properly. context->switchHeader = switchHeader; context->session = SessionManager_getSession(context->routerAddress, herKey, context->sm); // This goes to incomingFromCryptoAuth() // then incomingFromRouter() then core() context->layer = OUTER_LAYER; context->session->receiveMessage(message, context->session); return 0; }
static int handleOutgoing(struct DHTMessage* dmessage, void* vcontext) { struct Ducttape* context = (struct Ducttape*) vcontext; struct Message message = { .length = dmessage->length, .bytes = (uint8_t*) dmessage->bytes, .padding = 512 }; Message_shift(&message, Headers_UDPHeader_SIZE); struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message.bytes; uh->sourceAndDestPorts = 0; uh->length_be = Endian_hostToBigEndian16(dmessage->length); uh->checksum_be = 0; uint16_t payloadLength = message.length; Message_shift(&message, Headers_IP6Header_SIZE); struct Headers_IP6Header* header = (struct Headers_IP6Header*) message.bytes; header->versionClassAndFlowLabel = 0; header->flowLabelLow_be = 0; header->nextHeader = 17; header->hopLimit = 0; header->payloadLength_be = Endian_hostToBigEndian16(payloadLength); Bits_memcpyConst(header->sourceAddr, context->myAddr.ip6.bytes, Address_SEARCH_TARGET_SIZE); Bits_memcpyConst(header->destinationAddr, dmessage->address->ip6.bytes, Address_SEARCH_TARGET_SIZE); context->ip6Header = header; context->switchHeader = NULL; sendToRouter(dmessage->address, &message, context); return 0; } // Aligned on the beginning of the content. static inline bool isRouterTraffic(struct Message* message, struct Headers_IP6Header* ip6) { if (ip6->nextHeader != 17 || ip6->hopLimit != 0) { return false; } struct Headers_UDPHeader* uh = (struct Headers_UDPHeader*) message->bytes; return message->length >= Headers_UDPHeader_SIZE && uh->sourceAndDestPorts == 0 && (int) Endian_bigEndianToHost16(uh->length_be) == message->length - Headers_UDPHeader_SIZE; } /** * Message which is for us, message is aligned on the beginning of the content. * this is called from core() which calls through an interfaceMap. */ static inline uint8_t incomingForMe(struct Message* message, struct Ducttape* context, uint8_t herPubKey[32]) { struct Address addr; AddressCalc_addressForPublicKey(addr.ip6.bytes, herPubKey); if (memcmp(addr.ip6.bytes, context->ip6Header->sourceAddr, 16)) { #ifdef Log_DEBUG uint8_t keyAddr[40]; Address_printIp(keyAddr, &addr); Bits_memcpyConst(addr.ip6.bytes, context->ip6Header->sourceAddr, 16); uint8_t srcAddr[40]; Address_printIp(srcAddr, &addr); Log_debug2(context->logger, "Dropped packet because source address is not same as key.\n" " %s source addr\n" " %s hash of key\n", srcAddr, keyAddr); #endif return Error_INVALID; } if (message->length == 0) { #ifdef Log_WARN uint8_t keyAddr[40]; uint8_t ipv6Hex[80]; Address_printIp(keyAddr, &addr); Hex_encode(ipv6Hex, 80, (uint8_t*) context->ip6Header, 40); Log_warn2(context->logger, "Got ipv6 packet from %s which has no content!\nIPv6 Header: [%s]", keyAddr, ipv6Hex); #endif return Error_INVALID; } if (isRouterTraffic(message, context->ip6Header)) { // Shift off the UDP header. Message_shift(message, -Headers_UDPHeader_SIZE); addr.path = Endian_bigEndianToHost64(context->switchHeader->label_be); Bits_memcpyConst(addr.key, herPubKey, 32); return incomingDHT(message, &addr, context); } // RouterModule_addNode(&addr, context->routerModule); if (!context->routerIf) { Log_warn(context->logger, "Dropping message because there is no router interface configured.\n"); return Error_UNDELIVERABLE; } // Now write a message to the TUN device. // Need to move the ipv6 header forward up to the content because there's a crypto header // between the ipv6 header and the content which just got eaten. Message_shift(message, Headers_IP6Header_SIZE); uint16_t sizeDiff = message->bytes - (uint8_t*)context->ip6Header; if (sizeDiff) { context->ip6Header->payloadLength_be = Endian_hostToBigEndian16( Endian_bigEndianToHost16(context->ip6Header->payloadLength_be) - sizeDiff); memmove(message->bytes, context->ip6Header, Headers_IP6Header_SIZE); } context->routerIf->sendMessage(message, context->routerIf); return Error_NONE; } uint8_t Ducttape_injectIncomingForMe(struct Message* message, struct Ducttape* context, uint8_t herPublicKey[32]) { struct Headers_SwitchHeader sh; Bits_memcpyConst(&sh, message->bytes, Headers_SwitchHeader_SIZE); context->switchHeader = &sh; Message_shift(message, -Headers_SwitchHeader_SIZE); struct Headers_IP6Header ip6; Bits_memcpyConst(&ip6, message->bytes, Headers_IP6Header_SIZE); context->ip6Header = &ip6; Message_shift(message, -Headers_IP6Header_SIZE); return incomingForMe(message, context, herPublicKey); } /** * Send a message to another switch. * Switchheader will precede the message. */ static inline uint8_t sendToSwitch(struct Message* message, struct Headers_SwitchHeader* destinationSwitchHeader, struct Ducttape* context) { Message_shift(message, Headers_SwitchHeader_SIZE); struct Headers_SwitchHeader* switchHeaderLocation = (struct Headers_SwitchHeader*) message->bytes; if (destinationSwitchHeader != switchHeaderLocation) { memmove(message->bytes, destinationSwitchHeader, Headers_SwitchHeader_SIZE); } return context->switchInterface.receiveMessage(message, &context->switchInterface); }
static Iface_DEFUN incoming(struct Message* msg, struct Iface* interRouterIf) { struct MsgCore_pvt* mcp = Identity_containerOf(interRouterIf, struct MsgCore_pvt, pub.interRouterIf); struct Address addr = { .padding = 0 }; struct RouteHeader* hdr = (struct RouteHeader*) msg->bytes; Message_shift(msg, -(RouteHeader_SIZE + DataHeader_SIZE), NULL); Bits_memcpy(addr.ip6.bytes, hdr->ip6, 16); Bits_memcpy(addr.key, hdr->publicKey, 32); addr.protocolVersion = Endian_bigEndianToHost32(hdr->version_be); addr.path = Endian_bigEndianToHost64(hdr->sh.label_be); Dict* content = NULL; uint8_t* msgBytes = msg->bytes; int length = msg->length; //Log_debug(mcp->log, "Receive msg [%s] from [%s]", // Escape_getEscaped(msg->bytes, msg->length, msg->alloc), // Address_toString(&addr, msg->alloc)->bytes); // BencMessageReader_readNoExcept(msg, msg->alloc, &content); if (!content) { char* esc = Escape_getEscaped(msgBytes, length, msg->alloc); Log_debug(mcp->log, "DROP Malformed message [%s]", esc); return NULL; } int64_t* verP = Dict_getIntC(content, "p"); if (!verP) { Log_debug(mcp->log, "DROP Message without version"); return NULL; } addr.protocolVersion = *verP; String* q = Dict_getStringC(content, "q"); if (!Defined(SUBNODE)) { String* txid = Dict_getStringC(content, "txid"); Assert_true(txid); if (q) { if (txid->bytes[0] == '0') { Log_debug(mcp->log, "DROP query which begins with 0 and is for old pathfinder"); return NULL; } } else { if (txid->bytes[0] != '1') { Log_debug(mcp->log, "DROP reply which does not begin with 1"); return NULL; } String* newTxid = String_newBinary(NULL, txid->len - 1, msg->alloc); Bits_memcpy(newTxid->bytes, &txid->bytes[1], txid->len - 1); Dict_putStringC(content, "txid", newTxid, msg->alloc); txid = newTxid; } } if (q) { return queryMsg(mcp, content, &addr, msg); } else { return replyMsg(mcp, content, &addr, msg); } } struct MsgCore* MsgCore_new(struct EventBase* base, struct Random* rand, struct Allocator* allocator, struct Log* log, struct EncodingScheme* scheme) { struct Allocator* alloc = Allocator_child(allocator); struct MsgCore_pvt* mcp = Allocator_calloc(alloc, sizeof(struct MsgCore_pvt), 1); Identity_set(mcp); mcp->pub.interRouterIf.send = incoming; mcp->qh = ArrayList_OfQueryHandlers_new(alloc); mcp->pinger = Pinger_new(base, rand, log, alloc); mcp->log = log; mcp->scheme = scheme; mcp->schemeDefinition = EncodingScheme_serialize(scheme, alloc); return &mcp->pub; }
static inline uint32_t getSwitchIndex(struct Address* addr) { uint64_t label = Endian_bigEndianToHost64(addr->networkAddress_be); uint32_t bits = NumberCompress_bitsUsedForLabel(label); return NumberCompress_getDecompressed(label, bits); }
static Iface_DEFUN incomingMsg(struct Message* msg, struct Pathfinder_pvt* pf) { struct Address addr; struct RouteHeader* hdr = (struct RouteHeader*) msg->bytes; Message_shift(msg, -(RouteHeader_SIZE + DataHeader_SIZE), NULL); Bits_memcpy(addr.ip6.bytes, hdr->ip6, 16); Bits_memcpy(addr.key, hdr->publicKey, 32); addr.protocolVersion = Endian_bigEndianToHost32(hdr->version_be); addr.padding = 0; addr.path = Endian_bigEndianToHost64(hdr->sh.label_be); //Log_debug(pf->log, "Incoming DHT"); struct DHTMessage dht = { .address = &addr, .binMessage = msg, .allocator = msg->alloc }; DHTModuleRegistry_handleIncoming(&dht, pf->registry); struct Message* nodeMsg = Message_new(0, 256, msg->alloc); Iface_CALL(sendNode, nodeMsg, &addr, 0xfffffff0u, pf); if (dht.pleaseRespond) { // what a beautiful hack, see incomingFromDHT return Iface_next(&pf->pub.eventIf, msg); } return NULL; } static Iface_DEFUN incomingFromEventIf(struct Message* msg, struct Iface* eventIf) { struct Pathfinder_pvt* pf = Identity_containerOf(eventIf, struct Pathfinder_pvt, pub.eventIf); enum PFChan_Core ev = Message_pop32(msg, NULL); if (Pathfinder_pvt_state_INITIALIZING == pf->state) { Assert_true(ev == PFChan_Core_CONNECT); return connected(pf, msg); } // Let the PF send another 128 path changes again because it's basically a new tick. pf->bestPathChanges = 0; 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_UNSETUP_SESSION: case PFChan_Core_LINK_STATE: case PFChan_Core_CTRL_MSG: return NULL; default:; } Assert_failure("unexpected event [%d]", ev); } static void sendEvent(struct Pathfinder_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); Message_push(msg, data, size, NULL); Message_push32(msg, ev, NULL); Iface_send(&pf->pub.eventIf, msg); Allocator_free(alloc); } static void init(void* vpf) { struct Pathfinder_pvt* pf = Identity_check((struct Pathfinder_pvt*) vpf); struct PFChan_Pathfinder_Connect conn = { .superiority_be = Endian_hostToBigEndian32(1), .version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL) }; CString_strncpy(conn.userAgent, "Cjdns internal pathfinder", 64); sendEvent(pf, PFChan_Pathfinder_CONNECT, &conn, PFChan_Pathfinder_Connect_SIZE); } struct Pathfinder* Pathfinder_register(struct Allocator* allocator, struct Log* log, struct EventBase* base, struct Random* rand, struct Admin* admin) { struct Allocator* alloc = Allocator_child(allocator); struct Pathfinder_pvt* pf = Allocator_calloc(alloc, sizeof(struct Pathfinder_pvt), 1); Identity_set(pf); pf->alloc = alloc; pf->log = log; pf->base = base; pf->rand = rand; pf->admin = admin; pf->pub.eventIf.send = incomingFromEventIf; pf->dhtModule.context = pf; pf->dhtModule.handleOutgoing = incomingFromDHT; // This needs to be done asynchronously so the pf can be plumbed to the core Timeout_setTimeout(init, pf, 0, base, alloc); return &pf->pub; }
/** * This is called as sendMessage() by the switch. * There is only one switch interface which sends all traffic. * message is aligned on the beginning of the switch header. */ static uint8_t incomingFromSwitch(struct Message* message, struct Interface* switchIf) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*)switchIf->senderContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, true); struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) message->bytes; Message_shift(message, -Headers_SwitchHeader_SIZE); // The label comes in reversed from the switch because the switch doesn't know that we aren't // another switch ready to parse more bits, bit reversing the label yields the source address. switchHeader->label_be = Bits_bitReverse64(switchHeader->label_be); if (Headers_getMessageType(switchHeader) == Headers_SwitchHeader_TYPE_CONTROL) { return handleControlMessage(context, message, switchHeader, switchIf); } if (message->length < 8) { Log_info(context->logger, "runt"); return Error_INVALID; } #ifdef Version_2_COMPAT translateVersion2(message, dtHeader); #endif // #1 try to get the session using the handle. uint32_t nonceOrHandle = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); struct SessionManager_Session* session = NULL; if (nonceOrHandle > 3) { // Run message, it's a handle. session = SessionManager_sessionForHandle(nonceOrHandle, context->sm); Message_shift(message, -4); if (session) { uint32_t nonce = Endian_bigEndianToHost32(((uint32_t*)message->bytes)[0]); if (nonce == ~0u) { Log_debug(context->logger, "Got connectToMe packet at switch layer"); return 0; } debugHandlesAndLabel(context->logger, session, Endian_bigEndianToHost64(switchHeader->label_be), "running session nonce[%u]", nonce); dtHeader->receiveHandle = nonceOrHandle; } else { Log_debug(context->logger, "Got message with unrecognized handle"); } } else if (message->length >= Headers_CryptoAuth_SIZE) { union Headers_CryptoAuth* caHeader = (union Headers_CryptoAuth*) message->bytes; uint8_t ip6[16]; uint8_t* herKey = caHeader->handshake.publicKey; AddressCalc_addressForPublicKey(ip6, herKey); // a packet which claims to be "from us" causes problems if (AddressCalc_validAddress(ip6) && Bits_memcmp(ip6, &context->myAddr, 16)) { session = SessionManager_getSession(ip6, herKey, context->sm); debugHandlesAndLabel(context->logger, session, Endian_bigEndianToHost64(switchHeader->label_be), "new session nonce[%d]", nonceOrHandle); dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); } else { Log_debug(context->logger, "Got message with invalid ip addr"); } } if (!session) { #ifdef Log_INFO uint8_t path[20]; AddrTools_printPath(path, Endian_bigEndianToHost64(switchHeader->label_be)); Log_info(context->logger, "Dropped traffic packet from unknown node. [%s]", path); #endif return 0; } // This is needed so that the priority and other information // from the switch header can be passed on properly. dtHeader->switchHeader = switchHeader; // This goes to incomingFromCryptoAuth() // then incomingFromRouter() then core() dtHeader->layer = Ducttape_SessionLayer_OUTER; if (session->iface.receiveMessage(message, &session->iface) == Error_AUTHENTICATION) { debugHandlesAndLabel(context->logger, session, Endian_bigEndianToHost64(switchHeader->label_be), "Failed decrypting message NoH[%d] state[%d]", nonceOrHandle, CryptoAuth_getState(&session->iface)); return Error_AUTHENTICATION; } return 0; }
// incoming message from network, pointing to the beginning of the switch header. static uint8_t receiveMessage(struct Message* msg, struct Interface* iface) { struct SwitchPinger* ctx = Identity_check((struct SwitchPinger*) iface->receiverContext); struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes; ctx->incomingLabel = Endian_bigEndianToHost64(switchHeader->label_be); ctx->incomingVersion = 0; Message_shift(msg, -SwitchHeader_SIZE, NULL); uint32_t handle = Message_pop32(msg, NULL); #ifdef Version_7_COMPAT if (handle != 0xffffffff) { Message_push32(msg, handle, NULL); handle = 0xffffffff; Assert_true(SwitchHeader_isV7Ctrl(switchHeader)); } #endif Assert_true(handle == 0xffffffff); struct Control* ctrl = (struct Control*) msg->bytes; if (ctrl->type_be == Control_PONG_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); ctx->error = Error_NONE; if (msg->length >= Control_Pong_MIN_SIZE) { struct Control_Ping* pongHeader = (struct Control_Ping*) msg->bytes; ctx->incomingVersion = Endian_bigEndianToHost32(pongHeader->version_be); if (pongHeader->magic != Control_Pong_MAGIC) { Log_debug(ctx->logger, "dropped invalid switch pong"); return Error_INVALID; } Message_shift(msg, -Control_Pong_HEADER_SIZE, NULL); } else { Log_debug(ctx->logger, "got runt pong message, length: [%d]", msg->length); return Error_INVALID; } } else if (ctrl->type_be == Control_KEYPONG_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); ctx->error = Error_NONE; if (msg->length >= Control_KeyPong_HEADER_SIZE && msg->length <= Control_KeyPong_MAX_SIZE) { struct Control_KeyPing* pongHeader = (struct Control_KeyPing*) msg->bytes; ctx->incomingVersion = Endian_bigEndianToHost32(pongHeader->version_be); if (pongHeader->magic != Control_KeyPong_MAGIC) { Log_debug(ctx->logger, "dropped invalid switch key-pong"); return Error_INVALID; } Bits_memcpyConst(ctx->incomingKey, pongHeader->key, 32); Message_shift(msg, -Control_KeyPong_HEADER_SIZE, NULL); } else if (msg->length > Control_KeyPong_MAX_SIZE) { Log_debug(ctx->logger, "got overlong key-pong message, length: [%d]", msg->length); return Error_INVALID; } else { Log_debug(ctx->logger, "got runt key-pong message, length: [%d]", msg->length); return Error_INVALID; } } else if (ctrl->type_be == Control_ERROR_be) { Message_shift(msg, -Control_HEADER_SIZE, NULL); Assert_true((uint8_t*)&ctrl->content.error.errorType_be == msg->bytes); if (msg->length < (Control_Error_HEADER_SIZE + SwitchHeader_SIZE + Control_HEADER_SIZE)) { Log_debug(ctx->logger, "runt error packet"); return Error_NONE; } ctx->error = Message_pop32(msg, NULL); Message_push32(msg, 0, NULL); Message_shift(msg, -(Control_Error_HEADER_SIZE + SwitchHeader_SIZE), NULL); struct Control* origCtrl = (struct Control*) msg->bytes; Log_debug(ctx->logger, "error [%s] was caused by our [%s]", Error_strerror(ctx->error), Control_typeString(origCtrl->type_be)); int shift; if (origCtrl->type_be == Control_PING_be) { shift = -(Control_HEADER_SIZE + Control_Ping_HEADER_SIZE); } else if (origCtrl->type_be == Control_KEYPING_be) { shift = -(Control_HEADER_SIZE + Control_KeyPing_HEADER_SIZE); } else { Assert_failure("problem in Ducttape.c"); } if (msg->length < -shift) { Log_debug(ctx->logger, "runt error packet"); } Message_shift(msg, shift, NULL); } else { // If it gets here then Ducttape.c is failing. Assert_true(false); } String* msgStr = &(String) { .bytes = (char*) msg->bytes, .len = msg->length }; Pinger_pongReceived(msgStr, ctx->pinger); Bits_memset(ctx->incomingKey, 0, 32); return Error_NONE; } static void onPingResponse(String* data, uint32_t milliseconds, void* vping) { struct Ping* p = Identity_check((struct Ping*) vping); enum SwitchPinger_Result err = SwitchPinger_Result_OK; uint64_t label = p->context->incomingLabel; if (data) { if (label != p->label) { err = SwitchPinger_Result_LABEL_MISMATCH; } else if ((p->data || data->len > 0) && !String_equals(data, p->data)) { err = SwitchPinger_Result_WRONG_DATA; } else if (p->context->error == Error_LOOP_ROUTE) { err = SwitchPinger_Result_LOOP_ROUTE; } else if (p->context->error) { err = SwitchPinger_Result_ERROR_RESPONSE; } } else { err = SwitchPinger_Result_TIMEOUT; } uint32_t version = p->context->incomingVersion; struct SwitchPinger_Response* resp = Allocator_calloc(p->pub.pingAlloc, sizeof(struct SwitchPinger_Response), 1); resp->version = p->context->incomingVersion; resp->res = err; resp->label = label; resp->data = data; resp->milliseconds = milliseconds; resp->version = version; Bits_memcpyConst(resp->key, p->context->incomingKey, 32); resp->ping = &p->pub; p->onResponse(resp, p->pub.onResponseContext); } static void sendPing(String* data, void* sendPingContext) { struct Ping* p = Identity_check((struct Ping*) sendPingContext); struct Message* msg = Message_new(0, data->len + 512, p->pub.pingAlloc); while (((uintptr_t)msg->bytes - data->len) % 4) { Message_push8(msg, 0, NULL); } msg->length = 0; Message_push(msg, data->bytes, data->len, NULL); Assert_true(!((uintptr_t)msg->bytes % 4) && "alignment fault"); if (p->pub.keyPing) { Message_shift(msg, Control_KeyPing_HEADER_SIZE, NULL); struct Control_KeyPing* keyPingHeader = (struct Control_KeyPing*) msg->bytes; keyPingHeader->magic = Control_KeyPing_MAGIC; keyPingHeader->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); Bits_memcpyConst(keyPingHeader->key, p->context->myAddr->key, 32); } else { Message_shift(msg, Control_Ping_HEADER_SIZE, NULL); struct Control_Ping* pingHeader = (struct Control_Ping*) msg->bytes; pingHeader->magic = Control_Ping_MAGIC; pingHeader->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); } Message_shift(msg, Control_HEADER_SIZE, NULL); struct Control* ctrl = (struct Control*) msg->bytes; ctrl->checksum_be = 0; ctrl->type_be = (p->pub.keyPing) ? Control_KEYPING_be : Control_PING_be; ctrl->checksum_be = Checksum_engine(msg->bytes, msg->length); #ifdef Version_7_COMPAT if (0) { #endif Message_push32(msg, 0xffffffff, NULL); #ifdef Version_7_COMPAT } #endif Message_shift(msg, SwitchHeader_SIZE, NULL); struct SwitchHeader* switchHeader = (struct SwitchHeader*) msg->bytes; switchHeader->label_be = Endian_hostToBigEndian64(p->label); SwitchHeader_setVersion(switchHeader, SwitchHeader_CURRENT_VERSION); SwitchHeader_setPenalty(switchHeader, 0); SwitchHeader_setCongestion(switchHeader, 0); #ifdef Version_7_COMPAT // v7 detects ctrl packets by the bit which has been // re-appropriated for suppression of errors. switchHeader->congestAndSuppressErrors = 1; SwitchHeader_setVersion(switchHeader, 0); #endif p->context->iface->sendMessage(msg, p->context->iface); } static String* RESULT_STRING_OK = String_CONST_SO("pong"); static String* RESULT_STRING_LABEL_MISMATCH = String_CONST_SO("diff_label"); static String* RESULT_STRING_WRONG_DATA = String_CONST_SO("diff_data"); static String* RESULT_STRING_ERROR_RESPONSE = String_CONST_SO("err_switch"); static String* RESULT_STRING_TIMEOUT = String_CONST_SO("timeout"); static String* RESULT_STRING_UNKNOWN = String_CONST_SO("err_unknown"); static String* RESULT_STRING_LOOP = String_CONST_SO("err_loop"); String* SwitchPinger_resultString(enum SwitchPinger_Result result) { switch (result) { case SwitchPinger_Result_OK: return RESULT_STRING_OK; case SwitchPinger_Result_LABEL_MISMATCH: return RESULT_STRING_LABEL_MISMATCH; case SwitchPinger_Result_WRONG_DATA: return RESULT_STRING_WRONG_DATA; case SwitchPinger_Result_ERROR_RESPONSE: return RESULT_STRING_ERROR_RESPONSE; case SwitchPinger_Result_TIMEOUT: return RESULT_STRING_TIMEOUT; case SwitchPinger_Result_LOOP_ROUTE: return RESULT_STRING_LOOP; default: return RESULT_STRING_UNKNOWN; }; } static int onPingFree(struct Allocator_OnFreeJob* job) { struct Ping* ping = Identity_check((struct Ping*)job->userData); struct SwitchPinger* ctx = Identity_check(ping->context); ctx->outstandingPings--; Assert_true(ctx->outstandingPings >= 0); return 0; } struct SwitchPinger_Ping* SwitchPinger_newPing(uint64_t label, String* data, uint32_t timeoutMilliseconds, SwitchPinger_ResponseCallback onResponse, struct Allocator* alloc, struct SwitchPinger* ctx) { if (data && data->len > Control_Ping_MAX_SIZE) { return NULL; } if (ctx->outstandingPings > ctx->maxConcurrentPings) { Log_debug(ctx->logger, "Skipping switch ping because there are already [%d] outstanding", ctx->outstandingPings); return NULL; } struct Pinger_Ping* pp = Pinger_newPing(data, onPingResponse, sendPing, timeoutMilliseconds, alloc, ctx->pinger); struct Ping* ping = Allocator_clone(pp->pingAlloc, (&(struct Ping) { .pub = { .pingAlloc = pp->pingAlloc }, .label = label, .data = String_clone(data, pp->pingAlloc), .context = ctx, .onResponse = onResponse, .pingerPing = pp }));
static inline int incomingFromRouter(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { uint8_t* pubKey = CryptoAuth_getHerPublicKey(&session->iface); if (!validEncryptedIP6(message)) { // Not valid cjdns IPv6, we'll try it as an IPv4 or ICANN-IPv6 packet // and check if we have an agreement with the node who sent it. Message_shift(message, IpTunnel_PacketInfoHeader_SIZE); struct IpTunnel_PacketInfoHeader* header = (struct IpTunnel_PacketInfoHeader*) message->bytes; uint8_t* addr = session->ip6; Bits_memcpyConst(header->nodeIp6Addr, addr, 16); Bits_memcpyConst(header->nodeKey, pubKey, 32); struct Interface* ipTun = &context->ipTunnel->nodeInterface; return ipTun->sendMessage(message, ipTun); } struct Address srcAddr = { .path = Endian_bigEndianToHost64(dtHeader->switchHeader->label_be) }; Bits_memcpyConst(srcAddr.key, pubKey, 32); //Log_debug(context->logger, "Got message from router.\n"); int ret = core(message, dtHeader, session, context); struct Node* n = RouterModule_getNode(srcAddr.path, context->routerModule); if (!n) { Address_getPrefix(&srcAddr); RouterModule_addNode(context->routerModule, &srcAddr, session->version); } else { n->reach += 1; RouterModule_updateReach(n, context->routerModule); } return ret; } static uint8_t incomingFromCryptoAuth(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) iface->receiverContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, false); enum Ducttape_SessionLayer layer = dtHeader->layer; dtHeader->layer = Ducttape_SessionLayer_INVALID; struct SessionManager_Session* session = SessionManager_sessionForHandle(dtHeader->receiveHandle, context->sm); if (!session) { // This should never happen but there's no strong preventitive. Log_info(context->logger, "SESSION DISAPPEARED!"); return 0; } // If the packet came from a new session, put the send handle in the session. if (CryptoAuth_getState(iface) < CryptoAuth_ESTABLISHED) { // If this is true then the incoming message is definitely a handshake. if (message->length < 4) { debugHandles0(context->logger, session, "runt"); return Error_INVALID; } if (layer == Ducttape_SessionLayer_OUTER) { #ifdef Version_2_COMPAT if (dtHeader->currentSessionVersion >= 3) { session->version = dtHeader->currentSessionVersion; #endif Message_pop(message, &session->sendHandle_be, 4); #ifdef Version_2_COMPAT } else { session->sendHandle_be = dtHeader->currentSessionSendHandle_be; } #endif } else { // inner layer, always grab the handle Message_pop(message, &session->sendHandle_be, 4); debugHandles0(context->logger, session, "New session, incoming layer3"); } } switch (layer) { case Ducttape_SessionLayer_OUTER: return incomingFromRouter(message, dtHeader, session, context); case Ducttape_SessionLayer_INNER: return incomingForMe(message, dtHeader, session, context, CryptoAuth_getHerPublicKey(iface)); default: Assert_always(false); } // never reached. return 0; } static uint8_t outgoingFromCryptoAuth(struct Message* message, struct Interface* iface) { struct Ducttape_pvt* context = Identity_cast((struct Ducttape_pvt*) iface->senderContext); struct Ducttape_MessageHeader* dtHeader = getDtHeader(message, false); struct SessionManager_Session* session = SessionManager_sessionForHandle(dtHeader->receiveHandle, context->sm); enum Ducttape_SessionLayer layer = dtHeader->layer; dtHeader->layer = Ducttape_SessionLayer_INVALID; if (!session) { // This should never happen but there's no strong preventitive. Log_info(context->logger, "SESSION DISAPPEARED!"); return 0; } if (layer == Ducttape_SessionLayer_OUTER) { return sendToSwitch(message, dtHeader, session, context); } else if (layer == Ducttape_SessionLayer_INNER) { Log_debug(context->logger, "Sending layer3 message"); return outgoingFromMe(message, dtHeader, session, context); } else { Assert_true(0); } } /** * Handle an incoming control message from a switch. * * @param context the ducttape context. * @param message the control message, this should be alligned on the beginning of the content, * that is to say, after the end of the switch header. * @param switchHeader the header. * @param switchIf the interface which leads to the switch. */ static uint8_t handleControlMessage(struct Ducttape_pvt* context, struct Message* message, struct Headers_SwitchHeader* switchHeader, struct Interface* switchIf) { uint8_t labelStr[20]; uint64_t label = Endian_bigEndianToHost64(switchHeader->label_be); AddrTools_printPath(labelStr, label); if (message->length < Control_HEADER_SIZE) { Log_info(context->logger, "dropped runt ctrl packet from [%s]", labelStr); return Error_NONE; } struct Control* ctrl = (struct Control*) message->bytes; if (Checksum_engine(message->bytes, message->length)) { Log_info(context->logger, "ctrl packet from [%s] with invalid checksum.", labelStr); return Error_NONE; } bool pong = false; if (ctrl->type_be == Control_ERROR_be) { if (message->length < Control_Error_MIN_SIZE) { Log_info(context->logger, "dropped runt error packet from [%s]", labelStr); return Error_NONE; } uint64_t path = Endian_bigEndianToHost64(switchHeader->label_be); RouterModule_brokenPath(path, context->routerModule); uint8_t causeType = Headers_getMessageType(&ctrl->content.error.cause); if (causeType == Headers_SwitchHeader_TYPE_CONTROL) { if (message->length < Control_Error_MIN_SIZE + Control_HEADER_SIZE) { Log_info(context->logger, "error packet from [%s] containing runt cause packet", labelStr); return Error_NONE; } struct Control* causeCtrl = (struct Control*) &(&ctrl->content.error.cause)[1]; if (causeCtrl->type_be != Control_PING_be) { Log_info(context->logger, "error packet from [%s] caused by [%s] packet ([%u])", labelStr, Control_typeString(causeCtrl->type_be), Endian_bigEndianToHost16(causeCtrl->type_be)); } else { if (LabelSplicer_isOneHop(label) && ctrl->content.error.errorType_be == Endian_hostToBigEndian32(Error_UNDELIVERABLE)) { // this is our own InterfaceController complaining // because the node isn't responding to pings. return Error_NONE; } Log_debug(context->logger, "error packet from [%s] in response to ping, err [%u], length: [%u].", labelStr, Endian_bigEndianToHost32(ctrl->content.error.errorType_be), message->length); // errors resulting from pings are forwarded back to the pinger. pong = true; } } else if (causeType != Headers_SwitchHeader_TYPE_DATA) { Log_info(context->logger, "error packet from [%s] containing cause of unknown type [%u]", labelStr, causeType); } else { Log_info(context->logger, "error packet from [%s], error type [%u]", labelStr, Endian_bigEndianToHost32(ctrl->content.error.errorType_be)); } } else if (ctrl->type_be == Control_PONG_be) { pong = true; } else if (ctrl->type_be == Control_PING_be) { Message_shift(message, -Control_HEADER_SIZE); if (message->length < Control_Ping_MIN_SIZE) { Log_info(context->logger, "dropped runt ping"); return Error_INVALID; } struct Control_Ping* ping = (struct Control_Ping*) message->bytes; ping->magic = Control_Pong_MAGIC; ping->version_be = Endian_hostToBigEndian32(Version_CURRENT_PROTOCOL); Message_shift(message, Control_HEADER_SIZE); ctrl->type_be = Control_PONG_be; ctrl->checksum_be = 0; ctrl->checksum_be = Checksum_engine(message->bytes, message->length); Message_shift(message, Headers_SwitchHeader_SIZE); Log_info(context->logger, "got switch ping from [%s]", labelStr); switchIf->receiveMessage(message, switchIf); } else { Log_info(context->logger, "control packet of unknown type from [%s], type [%d]", labelStr, Endian_bigEndianToHost16(ctrl->type_be)); } if (pong && context->pub.switchPingerIf.receiveMessage) { // Shift back over the header Message_shift(message, Headers_SwitchHeader_SIZE); context->pub.switchPingerIf.receiveMessage( message, &context->pub.switchPingerIf); } return Error_NONE; }