/* 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 "interface/Interface.h"
#include "util/log/Log.h"
#include "switch/SwitchCore.h"
#include "switch/NumberCompress.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/Message.h"
#include
#include
struct SwitchInterface
{
struct Interface* iface;
struct SwitchCore* core;
struct Allocator_OnFreeJob* onFree;
/**
* How much traffic has flowed down an interface as the sum of all packet priority.
* If this number reaches bufferMax, further incoming traffic is dropped to prevent flooding.
* Users should periodically adjust the buffer toward zero to fairly meter out priority in
* congestion situations.
*/
int64_t buffer;
/**
* How high the buffer is allowed to get before beginning to drop packets.
* For nodes in the core, this number should be large because a buffer
* limit of a core link will cause route flapping.
* For edge nodes it is a measure of how much the ISP trusts the end user not to flood.
*/
int64_t bufferMax;
/**
* How congested an interface is.
* this number is subtraced from packet priority when the packet is sent down this interface.
*/
uint32_t congestion;
};
struct SwitchCore
{
struct SwitchInterface interfaces[NumberCompress_INTERFACES];
uint32_t interfaceCount;
bool routerAdded;
struct Log* logger;
struct Allocator* allocator;
};
struct SwitchCore* SwitchCore_new(struct Log* logger, struct Allocator* allocator)
{
struct SwitchCore* core = Allocator_calloc(allocator, sizeof(struct SwitchCore), 1);
core->allocator = allocator;
core->interfaceCount = 0;
core->logger = logger;
return core;
}
static inline uint16_t sendMessage(const struct SwitchInterface* switchIf,
struct Message* toSend,
struct Log* logger)
{
struct Headers_SwitchHeader* switchHeader = (struct Headers_SwitchHeader*) toSend->bytes;
uint32_t priority = Headers_getPriority(switchHeader);
if (switchIf->buffer + priority > switchIf->bufferMax) {
uint32_t messageType = Headers_getMessageType(switchHeader);
Headers_setPriorityAndMessageType(switchHeader, 0, messageType);
}
return Interface_sendMessage(switchIf->iface, toSend);
}
struct ErrorPacket {
struct Headers_SwitchHeader switchHeader;
struct Control ctrl;
};
Assert_compileTime(
sizeof(struct ErrorPacket) == Headers_SwitchHeader_SIZE + sizeof(struct Control));
static inline void sendError(struct SwitchInterface* iface,
struct Message* cause,
uint32_t code,
struct Log* logger)
{
struct Headers_SwitchHeader* header = (struct Headers_SwitchHeader*) cause->bytes;
if (Headers_getMessageType(header) == Headers_SwitchHeader_TYPE_CONTROL
&& ((struct ErrorPacket*) cause->bytes)->ctrl.type_be == Control_ERROR_be)
{
// Errors never cause other errors to be sent.
return;
}
// 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,
Headers_SwitchHeader_SIZE + Control_HEADER_SIZE + Control_Error_HEADER_SIZE,
NULL);
struct ErrorPacket* err = (struct ErrorPacket*) cause->bytes;
err->switchHeader.label_be = Bits_bitReverse64(header->label_be);
Headers_setPriorityAndMessageType(&err->switchHeader,
Headers_getPriority(header),
Headers_SwitchHeader_TYPE_CONTROL);
err->ctrl.type_be = Control_ERROR_be;
err->ctrl.content.error.errorType_be = Endian_hostToBigEndian32(code);
err->ctrl.checksum_be = 0;
err->ctrl.checksum_be =
Checksum_engine((uint8_t*) &err->ctrl, cause->length - Headers_SwitchHeader_SIZE);
sendMessage(iface, cause, logger);
}
#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 uint8_t receiveMessage(struct Message* message, struct Interface* iface)
{
struct SwitchInterface* sourceIf = (struct SwitchInterface*) iface->receiverContext;
if (sourceIf->buffer > sourceIf->bufferMax) {
Log_warn(sourceIf->core->logger, "DROP because node seems to be flooding.");
return Error_NONE;
}
if (message->length < Headers_SwitchHeader_SIZE) {
Log_debug(sourceIf->core->logger, "DROP runt packet.");
return Error_NONE;
}
struct SwitchCore* core = sourceIf->core;
struct Headers_SwitchHeader* header = (struct Headers_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) {
if (1 != (label & 0xf)) {
/* routing interface: must always be compressed as 0001 */
DEBUG_SRC_DST(sourceIf->core->logger,
"DROP packet for this router because the destination "
"discriminator was wrong");
sendError(sourceIf, message, Error_MALFORMED_ADDRESS, sourceIf->core->logger);
return Error_NONE;
}
//Assert_true(bits == 4);
}
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)))) {
// 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(sourceIf->core->logger,
"DROP packet for this router because there is no way to "
"represent the return path.");
sendError(sourceIf, message, Error_MALFORMED_ADDRESS, sourceIf->core->logger);
return Error_NONE;
}
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(sourceIf->core->logger, "DROP packet because source address is "
"larger than destination address.");
sendError(sourceIf, message, Error_MALFORMED_ADDRESS, sourceIf->core->logger);
return Error_NONE;
}
} else {
DEBUG_SRC_DST(sourceIf->core->logger, "DROP packet because source address is "
"larger than destination address.");
sendError(sourceIf, message, Error_MALFORMED_ADDRESS, sourceIf->core->logger);
return Error_NONE;
}
}
if (core->interfaces[destIndex].iface == NULL) {
DEBUG_SRC_DST(sourceIf->core->logger, "DROP packet because there is no interface "
"where the bits specify.");
sendError(sourceIf, message, Error_MALFORMED_ADDRESS, sourceIf->core->logger);
return Error_NONE;
}
if (sourceIndex == destIndex) {
DEBUG_SRC_DST(sourceIf->core->logger, "DROP Packet with redundant route.");
sendError(sourceIf, message, Error_MALFORMED_ADDRESS, sourceIf->core->logger);
return Error_NONE;
}
uint64_t sourceLabel = Bits_bitReverse64(NumberCompress_getCompressed(sourceIndex, bits));
uint64_t targetLabel = (label >> bits) | sourceLabel;
header->label_be = Endian_hostToBigEndian64(targetLabel);
/* Too much noise.
Log_debug(sourceIf->core->logger,
"Forwarding packet ([%u] to [%u]), labels [0x%016" PRIx64 "] -> [0x%016" PRIx64 "]",
sourceIndex, destIndex, label, targetLabel);
*/
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);
const uint16_t err = sendMessage(&core->interfaces[destIndex], message, sourceIf->core->logger);
if (err) {
Log_debug(sourceIf->core->logger, "Sending packet caused an error [%s]",
Error_strerror(err));
// be careful, the message could have decrypted content in it
// and we don't want to spill it out over the wire.
message->length = message->capacity;
Message_shift(message, -message->length, NULL);
Message_shift(message, Control_Error_MAX_SIZE, NULL);
Bits_memcpy(message->bytes, messageClone, cloneLength);
message->length = cloneLength;
header = (struct Headers_SwitchHeader*) message->bytes;
header->label_be = Endian_bigEndianToHost64(label);
sendError(sourceIf, message, err, sourceIf->core->logger);
return Error_NONE;
}
return Error_NONE;
}
static int removeInterface(struct Allocator_OnFreeJob* job)
{
struct SwitchInterface* si = (struct SwitchInterface*) job->userData;
Bits_memset(si, 0, sizeof(struct SwitchInterface));
return 0;
}
void SwitchCore_swapInterfaces(struct Interface* if1, struct Interface* if2)
{
struct SwitchInterface* si1 = (struct SwitchInterface*) if1->receiverContext;
struct SwitchInterface* si2 = (struct SwitchInterface*) if2->receiverContext;
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));
// Now the if#'s are in reverse order :)
si1->onFree = Allocator_onFree(if2->allocator, removeInterface, si1);
si2->onFree = Allocator_onFree(if1->allocator, removeInterface, si2);
if1->receiverContext = si2;
if2->receiverContext = si1;
}
/**
* @param trust a positive integer representing how much you trust the
* connected node not to send a flood.
* @param labelOut an integer pointer which will be set to the path to the newly added node
* in host endian order.
* @return 0 if all goes well, -1 if the list is full.
*/
int SwitchCore_addInterface(struct Interface* iface,
const uint64_t trust,
uint64_t* labelOut,
struct SwitchCore* core)
{
// This is some hackery to make sure the router interface is always index 1.
uint32_t ifIndex = core->interfaceCount;
if (ifIndex > 0 && !core->routerAdded) {
ifIndex++;
} else if (ifIndex == 1) {
ifIndex--;
}
// If there's a vacent spot where another iface was before it was removed, use that.
for (uint32_t i = 0; i < ifIndex; i++) {
if (core->interfaces[i].iface == NULL && i != 1) {
ifIndex = i;
break;
}
}
if (ifIndex == NumberCompress_INTERFACES) {
return SwitchCore_addInterface_OUT_OF_SPACE;
}
struct SwitchInterface* newIf = &core->interfaces[ifIndex];
Bits_memcpyConst(newIf, (&(struct SwitchInterface) {
.iface = iface,
.core = core,
.buffer = 0,
.bufferMax = trust,
.congestion = 0
}), sizeof(struct SwitchInterface));
newIf->onFree = Allocator_onFree(iface->allocator, removeInterface, newIf);
iface->receiverContext = &core->interfaces[ifIndex];
iface->receiveMessage = receiveMessage;
uint32_t bits = NumberCompress_bitsUsedForNumber(ifIndex);
*labelOut = NumberCompress_getCompressed(ifIndex, bits) | (1 << bits);
core->interfaceCount++;
return 0;
}
int SwitchCore_setRouterInterface(struct Interface* iface, struct SwitchCore* core)
{
Bits_memcpyConst(&core->interfaces[1], (&(struct SwitchInterface) {
.iface = iface,
.core = core,
.buffer = 0,
.bufferMax = INT64_MAX,
.congestion = 0
}), sizeof(struct SwitchInterface));
iface->receiverContext = &core->interfaces[1];
iface->receiveMessage = receiveMessage;
core->interfaceCount++;
core->routerAdded = true;
return 0;
}