static Iface_DEFUN incomingFromEventIf(struct Message* msg, struct Iface* eventIf) { struct UpperDistributor_pvt* ud = Identity_containerOf(eventIf, struct UpperDistributor_pvt, eventIf); Assert_true(Message_pop32(msg, NULL) == PFChan_Pathfinder_SENDMSG); Message_pop32(msg, NULL); return Iface_next(&ud->pub.sessionManagerIf, msg); }
static Iface_DEFUN incomingFromEventEmitterIf(struct Message* msg, struct Iface* eventEmitterIf) { struct InterfaceController_pvt* ic = Identity_containerOf(eventEmitterIf, struct InterfaceController_pvt, eventEmitterIf); Assert_true(Message_pop32(msg, NULL) == PFChan_Pathfinder_PEERS); uint32_t pathfinderId = Message_pop32(msg, NULL); Assert_true(!msg->length); for (int j = 0; j < ic->icis->length; j++) { struct InterfaceController_Iface_pvt* ici = ArrayList_OfIfaces_get(ic->icis, j); for (int i = 0; i < (int)ici->peerMap.count; i++) { struct Peer* peer = Identity_check((struct Peer*) ici->peerMap.values[i]); if (peer->state != InterfaceController_PeerState_ESTABLISHED) { continue; } sendPeer(pathfinderId, PFChan_Core_PEER, peer); } } return NULL; }
static Iface_DEFUN searchReq(struct Message* msg, struct Pathfinder_pvt* pf) { uint8_t addr[16]; Message_pop(msg, addr, 16, NULL); Message_pop32(msg, NULL); uint32_t version = Message_pop32(msg, NULL); if (version && version >= 20) { return NULL; } Assert_true(!msg->length); uint8_t printedAddr[40]; AddrTools_printIp(printedAddr, addr); Log_debug(pf->log, "Search req [%s]", printedAddr); struct Node_Two* node = NodeStore_nodeForAddr(pf->nodeStore, addr); if (node) { onBestPathChange(pf, node); } else { SearchRunner_search(addr, 20, 3, pf->searchRunner, pf->alloc); } return NULL; }
static Iface_DEFUN incomingFromCore(struct Message* msg, struct Iface* trickIf) { struct EventEmitter_pvt* ee = Identity_containerOf(trickIf, struct EventEmitter_pvt, trickIf); Assert_true(!((uintptr_t)msg->bytes % 4) && "alignment"); enum PFChan_Core ev = Message_pop32(msg, NULL); Assert_true(PFChan_Core_sizeOk(ev, msg->length+4)); uint32_t pathfinderNum = Message_pop32(msg, NULL); Message_push32(msg, ev, NULL); if (pathfinderNum != 0xffffffff) { struct Pathfinder* pf = ArrayList_Pathfinders_get(ee->pathfinders, pathfinderNum); Assert_true(pf && pf->state == Pathfinder_state_CONNECTED); return sendToPathfinder(msg, pf); } else { for (int i = 0; i < ee->pathfinders->length; i++) { struct Pathfinder* pf = ArrayList_Pathfinders_get(ee->pathfinders, i); if (!pf || pf->state != Pathfinder_state_CONNECTED) { continue; } struct Message* messageClone = Message_clone(msg, msg->alloc); Iface_CALL(sendToPathfinder, messageClone, pf); } } 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; }
static Iface_DEFUN incomingFromPathfinder(struct Message* msg, struct Iface* iface) { struct Pathfinder* pf = Identity_containerOf(iface, struct Pathfinder, iface); struct EventEmitter_pvt* ee = Identity_check((struct EventEmitter_pvt*) pf->ee); if (msg->length < 4) { Log_debug(ee->log, "DROPPF runt"); return NULL; } enum PFChan_Pathfinder ev = Message_pop32(msg, NULL); Message_push32(msg, pf->pathfinderId, NULL); Message_push32(msg, ev, NULL); if (ev <= PFChan_Pathfinder__TOO_LOW || ev >= PFChan_Pathfinder__TOO_HIGH) { Log_debug(ee->log, "DROPPF invalid type [%d]", ev); return NULL; } if (!PFChan_Pathfinder_sizeOk(ev, msg->length)) { Log_debug(ee->log, "DROPPF incorrect length[%d] for type [%d]", msg->length, ev); return NULL; } if (pf->state == Pathfinder_state_DISCONNECTED) { if (ev != PFChan_Pathfinder_CONNECT) { Log_debug(ee->log, "DROPPF disconnected and event != CONNECT event:[%d]", ev); return NULL; } } else if (pf->state != Pathfinder_state_CONNECTED) { Log_debug(ee->log, "DROPPF error state"); return NULL; } if (handleFromPathfinder(ev, msg, ee, pf)) { return NULL; } struct ArrayList_Ifaces* handlers = getHandlers(ee, ev, false); if (!handlers) { return NULL; } for (int i = 0; i < handlers->length; i++) { struct Message* messageClone = Message_clone(msg, msg->alloc); struct Iface* iface = ArrayList_Ifaces_get(handlers, i); // We have to call this manually because we don't have an interface handy which is // actually plumbed with this one. Assert_true(iface); Assert_true(iface->send); Iface_CALL(iface->send, messageClone, iface); } return NULL; }
// 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 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; }
static int handleFromPathfinder(enum PFChan_Pathfinder ev, struct Message* msg, struct EventEmitter_pvt* ee, struct Pathfinder* pf) { switch (ev) { default: return false; case PFChan_Pathfinder_CONNECT: { struct PFChan_Pathfinder_Connect connect; Message_shift(msg, -8, NULL); Message_pop(msg, &connect, PFChan_Pathfinder_Connect_SIZE, NULL); pf->superiority = Endian_bigEndianToHost32(connect.superiority_be); pf->version = Endian_bigEndianToHost32(connect.version_be); Bits_memcpy(pf->userAgent, connect.userAgent, 64); pf->state = Pathfinder_state_CONNECTED; struct PFChan_Core_Connect resp; resp.version_be = Endian_bigEndianToHost32(Version_CURRENT_PROTOCOL); resp.pathfinderId_be = Endian_hostToBigEndian32(pf->pathfinderId); Bits_memcpy(resp.publicKey, ee->publicKey, 32); Message_push(msg, &resp, PFChan_Core_Connect_SIZE, NULL); Message_push32(msg, PFChan_Core_CONNECT, NULL); struct Message* sendMsg = Message_clone(msg, msg->alloc); Iface_CALL(sendToPathfinder, sendMsg, pf); break; } case PFChan_Pathfinder_SUPERIORITY: { Message_shift(msg, -8, NULL); pf->superiority = Message_pop32(msg, NULL); struct Message* resp = pathfinderMsg(PFChan_Core_PATHFINDER, pf, msg->alloc); Iface_CALL(incomingFromCore, resp, &ee->trickIf); break; } case PFChan_Pathfinder_PING: { struct Message* sendMsg = Message_clone(msg, msg->alloc); Iface_send(&pf->iface, sendMsg); break; } case PFChan_Pathfinder_PONG: { Message_shift(msg, -8, NULL); uint32_t cookie = Message_pop32(msg, NULL); uint32_t count = Message_pop32(msg, NULL); if (cookie != PING_MAGIC || count > pf->bytesSinceLastPing) { pf->state = Pathfinder_state_ERROR; struct Message* resp = pathfinderMsg(PFChan_Core_PATHFINDER_GONE, pf, msg->alloc); Iface_CALL(incomingFromCore, resp, &ee->trickIf); } else { pf->bytesSinceLastPing -= count; } break; } case PFChan_Pathfinder_PATHFINDERS: { for (int i = 0; i < ee->pathfinders->length; i++) { struct Pathfinder* xpf = ArrayList_Pathfinders_get(ee->pathfinders, i); if (!xpf || xpf->state != Pathfinder_state_CONNECTED) { continue; } struct Allocator* alloc = Allocator_child(msg->alloc); struct Message* resp = pathfinderMsg(PFChan_Core_PATHFINDER, pf, alloc); Iface_CALL(sendToPathfinder, resp, pf); Allocator_free(alloc); } break; } } return true; }