/* vim: set expandtab ts=4 sw=4: */ /* * You may redistribute this program and/or modify it under the terms of * the GNU General Public License as published by the Free Software Foundation, * either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "memory/Allocator.h" #include "util/log/Log.h" #include "switch/SwitchCore.h" #include "switch/NumberCompress.h" #include "switch/Penalty.h" #include "util/Bits.h" #include "util/Checksum.h" #include "util/Endian.h" #include "wire/Control.h" #include "wire/Error.h" #include "wire/Headers.h" #include "wire/SwitchHeader.h" #include "wire/Message.h" #include #include struct SwitchInterface { struct Iface iface; struct Allocator* alloc; struct SwitchCore_pvt* core; struct Penalty* penalty; struct Allocator_OnFreeJob* onFree; int state; Identity }; struct SwitchCore_pvt { struct SwitchCore pub; struct SwitchInterface interfaces[NumberCompress_INTERFACES]; bool routerAdded; struct Log* logger; struct EventBase* eventBase; struct Allocator* allocator; Identity }; struct ErrorPacket8 { struct SwitchHeader switchHeader; uint32_t handle; struct Control ctrl; }; Assert_compileTime(sizeof(struct ErrorPacket8) == SwitchHeader_SIZE + 4 + sizeof(struct Control)); static inline Iface_DEFUN sendError(struct SwitchInterface* iface, struct Message* cause, uint32_t code, struct Log* logger) { if (cause->length < SwitchHeader_SIZE + 4) { Log_debug(logger, "runt"); return NULL; } struct SwitchHeader* header = (struct SwitchHeader*) cause->bytes; if (SwitchHeader_getSuppressErrors(header)) { // don't send errors if they're asking us to suppress them! return NULL; } // limit of 256 bytes cause->length = (cause->length < Control_Error_MAX_SIZE) ? cause->length : Control_Error_MAX_SIZE; // Shift back so we can add another header. Message_shift(cause, SwitchHeader_SIZE + 4 + Control_Header_SIZE + Control_Error_HEADER_SIZE, NULL); struct ErrorPacket8* err = (struct ErrorPacket8*) cause->bytes; err->switchHeader.label_be = Bits_bitReverse64(header->label_be); SwitchHeader_setSuppressErrors(header, true); SwitchHeader_setVersion(header, SwitchHeader_CURRENT_VERSION); SwitchHeader_setPenalty(header, 0); SwitchHeader_setCongestion(header, 0); err->handle = 0xffffffff; err->ctrl.header.type_be = Control_ERROR_be; err->ctrl.content.error.errorType_be = Endian_hostToBigEndian32(code); err->ctrl.header.checksum_be = 0; err->ctrl.header.checksum_be = Checksum_engine((uint8_t*) &err->ctrl, cause->length - SwitchHeader_SIZE - 4); return Iface_next(&iface->iface, cause); } #define DEBUG_SRC_DST(logger, message) \ Log_debug(logger, message " ([%u] to [%u])", sourceIndex, destIndex) /** This never returns an error, it sends an error packet instead. */ static Iface_DEFUN receiveMessage(struct Message* message, struct Iface* iface) { struct SwitchInterface* sourceIf = Identity_check((struct SwitchInterface*) iface); struct SwitchCore_pvt* core = Identity_check(sourceIf->core); if (message->length < SwitchHeader_SIZE) { Log_debug(core->logger, "DROP runt"); return NULL; } struct SwitchHeader* header = (struct SwitchHeader*) message->bytes; const uint64_t label = Endian_bigEndianToHost64(header->label_be); uint32_t bits = NumberCompress_bitsUsedForLabel(label); const uint32_t sourceIndex = sourceIf - core->interfaces; const uint32_t destIndex = NumberCompress_getDecompressed(label, bits); const uint32_t sourceBits = NumberCompress_bitsUsedForNumber(sourceIndex); Assert_true(destIndex < NumberCompress_INTERFACES); Assert_true(sourceIndex < NumberCompress_INTERFACES); if (1 == destIndex && 1 != (label & 0xf)) { // routing interface: must always be compressed as 0001 DEBUG_SRC_DST(core->logger, "DROP packet for this router because the destination " "discriminator was wrong"); return sendError(sourceIf, message, Error_MALFORMED_ADDRESS, core->logger); } if (sourceBits > bits) { if (destIndex == 1) { // If the destination index is this router, don't drop the packet since there no // way for a node to know the size of the representation of its source label. // - label ends in 0001; if there are enough zeroes at the end after removing the 1, // we can still fit in the source discriminator // - the return path probably doesn't start with 3 zeroes, but it will still be working, // as the source discriminator is large enough to make space for 3 zeroes between // reverse return path and forward path (see below) if (0 != ((label ^ 1) & (UINT64_MAX >> (64 - sourceBits - 4)))) { // This is a bug. // https://github.com/cjdelisle/cjdns/issues/93 // The problem is that there is no way to splice a route and know for certain // that you've not spliced one which will end up in this if statement. // Unfortunately there seems no clean way around this issue at the moment. // If this router and switch communicated using labels with "64 + four less // than the number of bits in largest discriminator" bits wide, it could handle // this situation, this solution is obviously non-trivial. DEBUG_SRC_DST(core->logger, "DROP packet for this router because there is no way to " "represent the return path."); return sendError(sourceIf, message, Error_RETURN_PATH_INVALID, core->logger); } bits = sourceBits; } else if (1 == sourceIndex) { // - we need at least 3 zeroes between reverse return path and forward path: // right now the label only contains the forward path // - sourceBits == 4, bits < 4 -> bits + 64 - sourceBits < 64 // - the reverse source discriminator "1000" and the target discriminator "0001" // can overlap as "10001" (or "100001" or ...) if (0 != label >> (bits + 64 - sourceBits)) { // not enough zeroes DEBUG_SRC_DST(core->logger, "DROP packet because source address is " "larger than destination address."); return sendError(sourceIf, message, Error_MALFORMED_ADDRESS, core->logger); } } else { Log_info(core->logger, "source exceeds dest"); DEBUG_SRC_DST(core->logger, "DROP packet because source address is " "larger than destination address."); return sendError(sourceIf, message, Error_MALFORMED_ADDRESS, core->logger); } } if (core->interfaces[destIndex].alloc == NULL) { Log_info(core->logger, "no such iface"); DEBUG_SRC_DST(core->logger, "DROP packet because there is no interface " "where the bits specify."); return sendError(sourceIf, message, Error_MALFORMED_ADDRESS, core->logger); } if (core->interfaces[destIndex].state == SwitchCore_setInterfaceState_ifaceState_DOWN && 1 != sourceIndex) { DEBUG_SRC_DST(core->logger, "DROP packet because interface is down"); return sendError(sourceIf, message, Error_UNDELIVERABLE, core->logger); } /*if (sourceIndex == destIndex && sourceIndex != 1) { DEBUG_SRC_DST(core->logger, "DROP Packet with redundant route."); return sendError(sourceIf, message, Error_LOOP_ROUTE, core->logger); }*/ uint64_t sourceLabel = Bits_bitReverse64(NumberCompress_getCompressed(sourceIndex, bits)); uint64_t targetLabel = (label >> bits) | sourceLabel; int cloneLength = (message->length < Control_Error_MAX_SIZE) ? message->length : Control_Error_MAX_SIZE; uint8_t messageClone[Control_Error_MAX_SIZE]; Bits_memcpy(messageClone, message->bytes, cloneLength); // Update the header header->label_be = Endian_hostToBigEndian64(targetLabel); uint32_t labelShift = SwitchHeader_getLabelShift(header) + bits; if (labelShift > 63) { // TODO(cjd): hmm should we return an error packet? Log_debug(core->logger, "Label rolled over"); return NULL; } SwitchHeader_setLabelShift(header, labelShift); if (sourceIndex != 1 && destIndex != 1) { // no penalty for our own packets Penalty_apply(sourceIf->penalty, header, message->length); } return Iface_next(&core->interfaces[destIndex].iface, message); } static int removeInterface(struct Allocator_OnFreeJob* job) { struct SwitchInterface* si = Identity_check((struct SwitchInterface*) job->userData); Bits_memset(si, 0, sizeof(struct SwitchInterface)); return 0; } void SwitchCore_setInterfaceState(struct Iface* userIf, int ifaceState) { struct SwitchInterface* sif = Identity_check((struct SwitchInterface*) userIf->connectedIf); Assert_true(ifaceState == (ifaceState & 1)); sif->state = ifaceState; } void SwitchCore_swapInterfaces(struct Iface* userIf1, struct Iface* userIf2) { struct SwitchInterface* si1 = Identity_check((struct SwitchInterface*) userIf1->connectedIf); struct SwitchInterface* si2 = Identity_check((struct SwitchInterface*) userIf2->connectedIf); Iface_unplumb(userIf1, &si1->iface); Iface_unplumb(userIf2, &si2->iface); Assert_true(Allocator_cancelOnFree(si1->onFree) > -1); Assert_true(Allocator_cancelOnFree(si2->onFree) > -1); struct SwitchInterface si3; Bits_memcpyConst(&si3, si1, sizeof(struct SwitchInterface)); Bits_memcpyConst(si1, si2, sizeof(struct SwitchInterface)); Bits_memcpyConst(si2, &si3, sizeof(struct SwitchInterface)); si1->onFree = Allocator_onFree(si1->alloc, removeInterface, si1); si2->onFree = Allocator_onFree(si2->alloc, removeInterface, si2); Iface_plumb(userIf2, &si1->iface); Iface_plumb(userIf1, &si2->iface); } int SwitchCore_addInterface(struct SwitchCore* switchCore, struct Iface* iface, struct Allocator* alloc, uint64_t* labelOut) { struct SwitchCore_pvt* core = Identity_check((struct SwitchCore_pvt*)switchCore); int ifIndex = 0; // If there's a vacent spot where another iface was before it was removed, use that. for (;;ifIndex++) { if (!core->interfaces[ifIndex].iface.send) { break; } if (ifIndex == NumberCompress_INTERFACES) { return SwitchCore_addInterface_OUT_OF_SPACE; } } struct SwitchInterface* newIf = &core->interfaces[ifIndex]; Identity_set(newIf); newIf->iface.send = receiveMessage; newIf->core = core; newIf->alloc = alloc; newIf->penalty = Penalty_new(alloc, core->eventBase, core->logger); newIf->onFree = Allocator_onFree(alloc, removeInterface, newIf); newIf->state = SwitchCore_setInterfaceState_ifaceState_UP; Iface_plumb(iface, &newIf->iface); uint32_t bits = NumberCompress_bitsUsedForNumber(ifIndex); *labelOut = NumberCompress_getCompressed(ifIndex, bits) | (1 << bits); return 0; } struct SwitchCore* SwitchCore_new(struct Log* logger, struct Allocator* allocator, struct EventBase* base) { struct SwitchCore_pvt* core = Allocator_calloc(allocator, sizeof(struct SwitchCore_pvt), 1); Identity_set(core); core->allocator = allocator; core->logger = logger; core->eventBase = base; struct SwitchInterface* routerIf = &core->interfaces[1]; Identity_set(routerIf); routerIf->iface.send = receiveMessage; routerIf->core = core; routerIf->alloc = allocator; routerIf->state = SwitchCore_setInterfaceState_ifaceState_UP; core->pub.routerIf = &routerIf->iface; return &core->pub; }