/** * 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; }
// 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; }