Skip to content

Commit

Permalink
Added packet fragmentation because the system is not required to frag…
Browse files Browse the repository at this point in the history
…ment beyond 1280 bytes
  • Loading branch information
Caleb James DeLisle committed Nov 24, 2012
1 parent 9fe6551 commit 6fb67a2
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 19 deletions.
83 changes: 70 additions & 13 deletions interface/ICMP6Generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "interface/ICMP6Generator.h"
#include "interface/ICMP6Generator_pvt.h"
#include "util/Checksum.h"
#include "util/Bits.h"
#include "util/log/Log.h"
#include "util/Identity.h"
#include "wire/Headers.h"
#include "wire/Error.h"

Expand All @@ -39,13 +38,6 @@
+ Headers_CryptoAuth_SIZE)


struct ICMP6Generator_pvt
{
struct ICMP6Generator pub;

Identity
};

static inline uint8_t numForType(enum ICMP6Generator_Type type)
{
switch (type) {
Expand Down Expand Up @@ -101,29 +93,93 @@ void ICMP6Generator_generate(struct Message* msg,
icmp6->checksum = Checksum_icmp6(ip6->sourceAddr, (uint8_t*)icmp6, contentLen);
}

static uint8_t sendFragmented(struct ICMP6Generator_pvt* ctx,
struct Message* msg,
uint32_t mtu,
int offsetBytes)
{
uint64_t msgHeader[(Headers_IP6Header_SIZE + Headers_IP6Fragment_SIZE) / 8];
Assert_true(msg->length > (int)sizeof(msgHeader));
Bits_memcpyConst(msgHeader, msg->bytes, sizeof(msgHeader));

const int headersSize = (Headers_IP6Header_SIZE + Headers_IP6Fragment_SIZE);
struct Headers_IP6Header* ip6 = (struct Headers_IP6Header*) msg->bytes;
struct Headers_IP6Fragment* frag = (struct Headers_IP6Fragment*) (&ip6[1]);
Message_shift(msg, -headersSize);

// prepare next message.
struct Message* nextMessage = &(struct Message) {
.bytes = msg->bytes,
.length = msg->length,
.padding = msg->padding
};
int nextMessageOffsetBytes = offsetBytes + (((mtu - headersSize) / 8) * 8);
Message_shift(nextMessage, -(nextMessageOffsetBytes - offsetBytes));

msg->length -= nextMessage->length;
ip6->payloadLength_be = Endian_hostToBigEndian16(msg->length + Headers_IP6Fragment_SIZE);
Message_shift(msg, headersSize);

// sanity check
Assert_true(!Bits_memcmp(&msg->bytes[msg->length], nextMessage->bytes, 8));
uint64_t msgNextPartFirstLong = ((uint64_t*)nextMessage->bytes)[0];

// Set the required fields.
// RFC-2460 includes the fragment header in the offset so we have to add another 8 bytes.
Headers_IP6Fragment_setOffset(frag, offsetBytes / 8);
Headers_IP6Fragment_setMoreFragments(frag, true);
Interface_sendMessage(msg, &ctx->pub.internal);

// sanity check
Assert_true(!Bits_memcmp(&msgNextPartFirstLong, nextMessage->bytes, 8));

Message_shift(nextMessage, sizeof(msgHeader));
Bits_memcpyConst(nextMessage->bytes, msgHeader, sizeof(msgHeader));

if (nextMessage->length > (int)mtu) {
return sendFragmented(ctx, nextMessage, mtu, nextMessageOffsetBytes);
}

// Set the required fields for the last fragment.
ip6 = (struct Headers_IP6Header*) nextMessage->bytes;
frag = (struct Headers_IP6Fragment*) (nextMessage->bytes + Headers_IP6Header_SIZE);
Headers_IP6Fragment_setOffset(frag, nextMessageOffsetBytes / 8);
// If the kernel did some fragmentation of it's own, we don't want to set the "last fragment"
// flag so we'll leave it as it is.
//Headers_IP6Fragment_setMoreFragments(frag, false);
ip6->payloadLength_be = Endian_hostToBigEndian16(nextMessage->length - Headers_IP6Header_SIZE);

return Interface_sendMessage(nextMessage, &ctx->pub.internal);
}

/** Message from the external (TUN facing) interface. */
static uint8_t incoming(struct Message* msg, struct Interface* iface)
{
struct ICMP6Generator_pvt* ctx =
Identity_cast((struct ICMP6Generator_pvt*)
(((uint8_t*)iface) - offsetof(struct ICMP6Generator, external)));

// TODO calculate this on a per-node basis.
int mtu = ctx->mtu;

// source address for packets coming from the router.
const uint8_t magicAddr[] = { 0xfc,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 };

struct Headers_IP6Header* header = (struct Headers_IP6Header*) msg->bytes;
// TODO calculate this on a per-node basis.
if (msg->length >= Headers_IP6Header_SIZE
&& Headers_getIpVersion(header) == 6
&& msg->length > (EXPECTED_MTU - CJDNS_OVERHEAD))
&& msg->length > mtu)
{
if (header->nextHeader == Headers_IP6Fragment_TYPE) {
return sendFragmented(ctx, msg, mtu, 0);
}
uint8_t destAddr[16];
Bits_memcpyConst(destAddr, header->sourceAddr, 16);
ICMP6Generator_generate(msg,
magicAddr,
destAddr,
ICMP6Generator_Type_PACKET_TOO_BIG,
(EXPECTED_MTU - CJDNS_OVERHEAD));
mtu);

Interface_sendMessage(msg, &ctx->pub.external);
return Error_NONE;
Expand Down Expand Up @@ -151,7 +207,8 @@ struct ICMP6Generator* ICMP6Generator_new(struct Allocator* alloc)
.internal = {
.sendMessage = outgoing
}
}
},
.mtu = EXPECTED_MTU - CJDNS_OVERHEAD
}));
Identity_set(out);
return &out->pub;
Expand Down
30 changes: 30 additions & 0 deletions interface/ICMP6Generator_pvt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef ICMP6Generator_pvt_H
#define ICMP6Generator_pvt_H

#include "interface/ICMP6Generator.h"
#include "util/Identity.h"

struct ICMP6Generator_pvt
{
struct ICMP6Generator pub;

int mtu;

Identity
};

#endif
80 changes: 74 additions & 6 deletions interface/test/ICMP6Generator_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,27 @@
#include "memory/MallocAllocator.h"
#include "memory/CanaryAllocator.h"
#include "crypto/Random.h"
#include "interface/ICMP6Generator.h"
#include "interface/ICMP6Generator_pvt.h"
#include "wire/Headers.h"
#include "util/Assert.h"
#include "util/Bits.h"

#define MIN(a, b) (((a) < (b)) ? (a) : (b))

static void mtuTest(struct Allocator* mainAlloc, struct Random* rand, int messageSize, uint32_t mtu)
static struct Message* newMessage(struct Allocator* alloc, int messageSize)
{
struct Allocator* alloc = CanaryAllocator_new(Allocator_child(mainAlloc), rand);
uint8_t* buff = Allocator_calloc(alloc, messageSize + 64, 1);
CanaryAllocator_check(alloc);
struct Message* msg = &(struct Message) {
return Allocator_clone(alloc, (&(struct Message) {
.bytes = buff + 64,
.length = messageSize,
.padding = 64
};
}));
}

static void mtuTest(struct Allocator* mainAlloc, struct Random* rand, int messageSize, uint32_t mtu)
{
struct Allocator* alloc = CanaryAllocator_new(Allocator_child(mainAlloc), rand);
struct Message* msg = newMessage(alloc, messageSize);

uint8_t* sourceAddr = (uint8_t*) "sourceAddress123";
uint8_t* destAddr = (uint8_t*) "destinationAddr1";
Expand Down Expand Up @@ -81,6 +85,64 @@ static void mtuTest(struct Allocator* mainAlloc, struct Random* rand, int messag
Allocator_free(alloc);
}

static uint8_t messageFromGenerator(struct Message* msg, struct Interface* iface)
{
uint64_t* reassemblyBuff = iface->receiverContext;
struct Headers_IP6Header* ip6 = (struct Headers_IP6Header*) msg->bytes;
struct Headers_IP6Fragment* frag = (struct Headers_IP6Fragment*) (&ip6[1]);

int index = Headers_IP6Fragment_getOffset(frag);

Message_shift(msg, -Headers_IP6Header_SIZE);
Assert_always(Endian_bigEndianToHost16(ip6->payloadLength_be) == msg->length);
Message_shift(msg, -Headers_IP6Fragment_SIZE);
Assert_always(msg->length > 0);

Bits_memcpy(&reassemblyBuff[index], msg->bytes, msg->length);
Message_shift(msg, (Headers_IP6Header_SIZE + Headers_IP6Fragment_SIZE));

printf("Got message fragment with index [%d] length [%d] hasMoreFragments [%d]\n",
index, msg->length, Headers_IP6Fragment_hasMoreFragments(frag));
return 0;
}

static void fragTest(struct Allocator* mainAlloc,
struct Random* rand,
int messageSize,
uint32_t mtu)
{
struct Allocator* alloc = CanaryAllocator_new(Allocator_child(mainAlloc), rand);
struct Message* msg = newMessage(alloc, messageSize);
for (int i = 0; i < msg->length; i++) {
msg->bytes[i] = i & 0xff;
}
struct Headers_IP6Header* ip6 = (struct Headers_IP6Header*) msg->bytes;
Headers_setIpVersion(ip6);
ip6->payloadLength_be = Endian_hostToBigEndian16(messageSize - Headers_IP6Header_SIZE);
struct Headers_IP6Fragment* frag = (struct Headers_IP6Fragment*) (&ip6[1]);
ip6->nextHeader = 44;
Bits_memset(frag, 0, sizeof(frag));

const uint32_t headersSize = Headers_IP6Header_SIZE + Headers_IP6Fragment_SIZE;
const uint32_t reassemblySize = ((messageSize + 7 - headersSize) / 8) * 8;
uint8_t* reassemblyBuff = Allocator_calloc(alloc, reassemblySize, 1);
printf("Allocating [%d] bytes for reassembly.\n", reassemblySize);
struct ICMP6Generator* ig = ICMP6Generator_new(alloc);
((struct ICMP6Generator_pvt*)ig)->mtu = mtu;
ig->internal.receiveMessage = messageFromGenerator;
ig->internal.receiverContext = reassemblyBuff;

CanaryAllocator_check(alloc);
ig->external.sendMessage(msg, &ig->external);
CanaryAllocator_check(alloc);

for (int i = 0; i < (int)(messageSize - headersSize); i++) {
Assert_always(reassemblyBuff[i] == ((i + headersSize) & 0xff));
}

Allocator_free(alloc);
}

int main()
{
struct Allocator* alloc = MallocAllocator_new(20000);
Expand All @@ -91,4 +153,10 @@ int main()
mtuTest(alloc, rand, 1280, 1024);
mtuTest(alloc, rand, 1492, 1024);
mtuTest(alloc, rand, 1024, 512);

fragTest(alloc, rand, 2048, 1000);
fragTest(alloc, rand, 1400, 1000);
fragTest(alloc, rand, 1300, 500);
fragTest(alloc, rand, 1500, 200);
fragTest(alloc, rand, 1500, 100);
}
36 changes: 36 additions & 0 deletions wire/Headers.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,42 @@ struct Headers_IP6Header
#define Headers_IP6Header_SIZE 40
Assert_compileTime(sizeof(struct Headers_IP6Header) == Headers_IP6Header_SIZE);

struct Headers_IP6Fragment
{
uint8_t nextHeader;
uint8_t zero;
uint16_t fragmentOffsetAndMoreFragments_be;
uint32_t identifier;
};
#define Headers_IP6Fragment_SIZE 8
Assert_compileTime(sizeof(struct Headers_IP6Fragment) == Headers_IP6Fragment_SIZE);
#define Headers_IP6Fragment_TYPE 44

static inline uint32_t Headers_IP6Fragment_getOffset(struct Headers_IP6Fragment* frag)
{
return Endian_bigEndianToHost16(frag->fragmentOffsetAndMoreFragments_be) >> 3;
}

static inline void Headers_IP6Fragment_setOffset(struct Headers_IP6Fragment* frag, uint16_t offset)
{
frag->fragmentOffsetAndMoreFragments_be &= Endian_hostToBigEndian16(7);
frag->fragmentOffsetAndMoreFragments_be |= Endian_hostToBigEndian16(offset << 3);
}

static inline bool Headers_IP6Fragment_hasMoreFragments(struct Headers_IP6Fragment* frag)
{
return frag->fragmentOffsetAndMoreFragments_be & Endian_hostToBigEndian16(1);
}

static inline void Headers_IP6Fragment_setMoreFragments(struct Headers_IP6Fragment* frag, bool more)
{
if (more) {
frag->fragmentOffsetAndMoreFragments_be |= Endian_hostToBigEndian16(1);
} else {
frag->fragmentOffsetAndMoreFragments_be &= Endian_hostToBigEndian16(0xFFFF << 1);
}
}

struct Headers_IP4Header
{
uint8_t versionAndHeaderLength;
Expand Down

0 comments on commit 6fb67a2

Please sign in to comment.