static inline uint8_t incomingDHT(struct Message* message, struct Address* addr, struct Ducttape_pvt* context) { struct DHTMessage dht = { .address = addr, .binMessage = message, .allocator = message->alloc }; DHTModuleRegistry_handleIncoming(&dht, context->registry); // TODO(cjd): return something meaningful. return Error_NONE; } /** Header must not be encrypted and must be aligned on the beginning of the ipv6 header. */ static inline uint8_t sendToRouter(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { int safeDistance = SwitchHeader_SIZE; CryptoAuth_resetIfTimeout(session->internal); if (CryptoAuth_getState(session->internal) < CryptoAuth_HANDSHAKE3) { // Put the handle into the message so that it's authenticated. // see: sendToSwitch() //Log_debug(context->logger, "Sending receive handle under CryptoAuth"); Message_push(message, &session->receiveHandle_be, 4, NULL); safeDistance += CryptoHeader_SIZE; } else { // 16 for the authenticator, 4 for the nonce and 4 for the handle safeDistance += 24; } Message_shift(message, safeDistance, NULL); if (dtHeader->switchHeader) { if (message->bytes != (uint8_t*)dtHeader->switchHeader) { Bits_memmoveConst(message->bytes, dtHeader->switchHeader, SwitchHeader_SIZE); dtHeader->switchHeader = (struct SwitchHeader*) message->bytes; } } else { dtHeader->switchHeader = (struct SwitchHeader*) message->bytes; Bits_memset(dtHeader->switchHeader, 0, SwitchHeader_SIZE); } Message_shift(message, -safeDistance, NULL); SwitchHeader_setVersion(dtHeader->switchHeader, SwitchHeader_CURRENT_VERSION); SwitchHeader_setLabelShift(dtHeader->switchHeader, 0); dtHeader->switchHeader->label_be = Endian_hostToBigEndian64(dtHeader->switchLabel); // This comes out in outgoingFromCryptoAuth() then sendToSwitch() dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_OUTER; return Interface_sendMessage(session->internal, message); }
// Incoming message which has passed through the cryptoauth and needs to be forwarded to the switch. static uint8_t receivedAfterCryptoAuth(struct Message* msg, struct Interface* cryptoAuthIf) { struct IFCPeer* ep = Identity_cast((struct IFCPeer*) cryptoAuthIf->receiverContext); struct Context* ic = ifcontrollerForPeer(ep); ep->bytesIn += msg->length; if (ep->state < InterfaceController_PeerState_ESTABLISHED) { if (CryptoAuth_getState(cryptoAuthIf) >= CryptoAuth_HANDSHAKE3) { moveEndpointIfNeeded(ep, ic); ep->state = InterfaceController_PeerState_ESTABLISHED; } else { ep->state = InterfaceController_PeerState_HANDSHAKE; // prevent some kinds of nasty things which could be done with packet replay. // This is checking the message switch header and will drop it unless the label // directs it to *this* router. if (msg->length < 8 || msg->bytes[7] != 1) { Log_info(ic->logger, "Dropping message because CA is not established."); return Error_NONE; } else { // When a "server" gets a new connection from a "client" the router doesn't // know about that client so if the client sends a packet to the server, the // server will be unable to handle it until the client has sent inter-router // communication to the server. Here we will ping the client so when the // server gets the ping response, it will insert the client into its table // and know its version. // prevent DoS by limiting the number of times this can be called per second // limit it to 7, this will affect innocent packets but it doesn't matter much // since this is mostly just an optimization and for keeping the tests happy. if ((ic->pingCount + 1) % 7) { pingCallback(ic); } } } } else if (ep->state == InterfaceController_PeerState_UNRESPONSIVE && CryptoAuth_getState(cryptoAuthIf) >= CryptoAuth_HANDSHAKE3) { ep->state = InterfaceController_PeerState_ESTABLISHED; } else { ep->timeOfLastMessage = Time_currentTimeMilliseconds(ic->eventBase); } return ep->switchIf.receiveMessage(msg, &ep->switchIf); }
// Incoming message which has passed through the cryptoauth and needs to be forwarded to the switch. static Iface_DEFUN receivedPostCryptoAuth(struct Message* msg, struct Peer* ep, struct InterfaceController_pvt* ic) { ep->bytesIn += msg->length; int caState = CryptoAuth_getState(ep->caSession); if (ep->state < InterfaceController_PeerState_ESTABLISHED) { // EP states track CryptoAuth states... ep->state = caState; SwitchCore_setInterfaceState(&ep->switchIf, SwitchCore_setInterfaceState_ifaceState_UP); Bits_memcpy(ep->addr.key, ep->caSession->herPublicKey, 32); Address_getPrefix(&ep->addr); if (caState == CryptoAuth_ESTABLISHED) { moveEndpointIfNeeded(ep); //sendPeer(0xffffffff, PFChan_Core_PEER, ep);// version is not known at this point. } else { // prevent some kinds of nasty things which could be done with packet replay. // This is checking the message switch header and will drop it unless the label // directs it to *this* router. if (msg->length < 8 || msg->bytes[7] != 1) { Log_info(ic->logger, "DROP message because CA is not established."); return 0; } else { // When a "server" gets a new connection from a "client" the router doesn't // know about that client so if the client sends a packet to the server, the // server will be unable to handle it until the client has sent inter-router // communication to the server. Here we will ping the client so when the // server gets the ping response, it will insert the client into its table // and know its version. // prevent DoS by limiting the number of times this can be called per second // limit it to 7, this will affect innocent packets but it doesn't matter much // since this is mostly just an optimization and for keeping the tests happy. if ((ep->pingCount + 1) % 7) { sendPing(ep); } } } } else if (ep->state == InterfaceController_PeerState_UNRESPONSIVE && caState == CryptoAuth_ESTABLISHED) { ep->state = InterfaceController_PeerState_ESTABLISHED; SwitchCore_setInterfaceState(&ep->switchIf, SwitchCore_setInterfaceState_ifaceState_UP); } else { ep->timeOfLastMessage = Time_currentTimeMilliseconds(ic->eventBase); } Identity_check(ep); Assert_true(!(msg->capacity % 4)); return Iface_next(&ep->switchIf, msg); }
static void sessionStats(Dict* args, void* vcontext, String* txid, struct Allocator* alloc) { struct Context* context = Identity_check((struct Context*) vcontext); int64_t* handleP = Dict_getInt(args, String_CONST("handle")); uint32_t handle = *handleP; struct SessionManager_Session* session = SessionManager_sessionForHandle(handle, context->sm); Dict* r = Dict_new(alloc); if (!session) { Dict_putString(r, String_CONST("error"), String_CONST("no such session"), alloc); Admin_sendMessage(r, txid, context->admin); return; } uint8_t printedAddr[40]; AddrTools_printIp(printedAddr, session->caSession->herIp6); Dict_putString(r, String_CONST("ip6"), String_new(printedAddr, alloc), alloc); String* state = String_new(CryptoAuth_stateString(CryptoAuth_getState(session->caSession)), alloc); Dict_putString(r, String_CONST("state"), state, alloc); struct ReplayProtector* rp = &session->caSession->replayProtector; Dict_putInt(r, String_CONST("duplicates"), rp->duplicates, alloc); Dict_putInt(r, String_CONST("lostPackets"), rp->lostPackets, alloc); Dict_putInt(r, String_CONST("receivedOutOfRange"), rp->receivedOutOfRange, alloc); struct Address addr; Bits_memcpyConst(addr.key, session->caSession->herPublicKey, 32); addr.path = session->sendSwitchLabel; addr.protocolVersion = session->version; Dict_putString(r, String_CONST("addr"), Address_toString(&addr, alloc), alloc); Dict_putString(r, String_CONST("publicKey"), Key_stringify(session->caSession->herPublicKey, alloc), alloc); Dict_putInt(r, String_CONST("version"), session->version, alloc); Dict_putInt(r, String_CONST("handle"), session->receiveHandle, alloc); Dict_putInt(r, String_CONST("sendHandle"), session->sendHandle, alloc); Dict_putInt(r, String_CONST("timeOfLastIn"), session->timeOfLastIn, alloc); Dict_putInt(r, String_CONST("timeOfLastOut"), session->timeOfLastOut, alloc); Dict_putString(r, String_CONST("deprecation"), String_CONST("publicKey,version will soon be removed"), alloc); Admin_sendMessage(r, txid, context->admin); return; }
/** Header must not be encrypted and must be aligned on the beginning of the ipv6 header. */ static inline uint8_t sendToRouter(struct Message* message, struct Ducttape_MessageHeader* dtHeader, struct SessionManager_Session* session, struct Ducttape_pvt* context) { int safeDistance = Headers_SwitchHeader_SIZE; if (CryptoAuth_getState(&session->iface) < CryptoAuth_HANDSHAKE3) { // Bug 104, see Version.h #ifdef Version_2_COMPAT if (session->version >= 3) { #endif // Put the handle into the message so that it's authenticated. // see: sendToSwitch() Log_debug(context->logger, "Sending receive handle under CryptoAuth"); Message_push(message, &session->receiveHandle_be, 4); #ifdef Version_2_COMPAT } else { // Otherwise it will be added on the other side. safeDistance += 4; } #endif safeDistance += Headers_CryptoAuth_SIZE; } else { // 16 for the authenticator, 4 for the nonce and 4 for the handle safeDistance += 24; } Message_shift(message, safeDistance); if (dtHeader->switchHeader) { if (message->bytes != (uint8_t*)dtHeader->switchHeader) { Bits_memmoveConst(message->bytes, dtHeader->switchHeader, Headers_SwitchHeader_SIZE); dtHeader->switchHeader = (struct Headers_SwitchHeader*) message->bytes; } } else { dtHeader->switchHeader = (struct Headers_SwitchHeader*) message->bytes; Bits_memset(dtHeader->switchHeader, 0, Headers_SwitchHeader_SIZE); } Message_shift(message, -safeDistance); dtHeader->switchHeader->label_be = Endian_hostToBigEndian64(dtHeader->switchLabel); // This comes out in outgoingFromCryptoAuth() then sendToSwitch() dtHeader->receiveHandle = Endian_bigEndianToHost32(session->receiveHandle_be); dtHeader->layer = Ducttape_SessionLayer_OUTER; return session->iface.sendMessage(message, &session->iface); }
static uint8_t sendMessage(struct Message* message, struct Interface* interface) { struct CryptoAuth_Wrapper* wrapper = Identity_cast((struct CryptoAuth_Wrapper*) interface->senderContext); // If there has been no incoming traffic for a while, reset the connection to state 0. // This will prevent "connection in bad state" situations from lasting forever. // this will reset the session if it has timed out. CryptoAuth_getState(interface); // If the nonce wraps, start over. if (wrapper->nextNonce >= 0xfffffff0) { reset(wrapper); } #ifdef Log_DEBUG Assert_true(!((uintptr_t)message->bytes % 4) || !"alignment fault"); #endif // nextNonce 0: sending hello, we are initiating connection. // nextNonce 1: sending another hello, nothing received yet. // nextNonce 2: sending key, hello received. // nextNonce 3: sending key again, no data packet recieved yet. // nextNonce >3: handshake complete // // if it's a blind handshake, every message will be empty and nextNonce will remain // zero until the first message is received back. if (wrapper->nextNonce < 5) { if (wrapper->nextNonce < 4) { return encryptHandshake(message, wrapper, 0); } else { cryptoAuthDebug0(wrapper, "Doing final step to send message. nonce=4"); Assert_true(!Bits_isZero(wrapper->ourTempPrivKey, 32)); Assert_true(!Bits_isZero(wrapper->herTempPubKey, 32)); getSharedSecret(wrapper->sharedSecret, wrapper->ourTempPrivKey, wrapper->herTempPubKey, NULL, wrapper->context->logger); } } Assert_true(message->length > 0 && "Empty packet during handshake"); return encryptMessage(message, wrapper); }
static uint8_t sendMessage(struct Message* msg, struct Interface* iface) { struct SessionManager_Session_pvt* ss = Identity_check((struct SessionManager_Session_pvt*)iface); uint64_t timeOfLastOut = ss->pub.timeOfLastOut; ss->pub.timeOfLastOut = Time_currentTimeMilliseconds(ss->sm->eventBase); int prevState = ss->pub.cryptoAuthState; ss->pub.cryptoAuthState = CryptoAuth_getState(ss->pub.internal); if ((ss->pub.timeOfLastOut - timeOfLastOut) > STATE_UPDATE_TIME || prevState != ss->pub.cryptoAuthState) { stateChange(ss, ss->pub.timeOfLastIn, timeOfLastOut, prevState); } return Interface_sendMessage(&ss->sm->iface, msg); }
static uint8_t receiveMessage(struct Message* msg, struct Interface* iface) { struct SessionManager_Session_pvt* ss = Identity_check((struct SessionManager_Session_pvt*)iface->receiverContext); // nonce added by CryptoAuth Message_pop(msg, NULL, 4, NULL); uint64_t timeOfLastIn = ss->pub.timeOfLastIn; ss->pub.timeOfLastIn = Time_currentTimeMilliseconds(ss->sm->eventBase); int prevState = ss->pub.cryptoAuthState; ss->pub.cryptoAuthState = CryptoAuth_getState(ss->pub.internal); if ((ss->pub.timeOfLastIn - timeOfLastIn) > STATE_UPDATE_TIME || prevState != ss->pub.cryptoAuthState) { stateChange(ss, timeOfLastIn, ss->pub.timeOfLastOut, prevState); } return Interface_receiveMessage(&ss->sm->iface, msg); }
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; }
// 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); }
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); }
/** * 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; }