Permalink
Browse files

Added packet fragmentation because the system is not required to frag…

…ment beyond 1280 bytes
  • Loading branch information...
1 parent 9fe6551 commit 6fb67a2af5a7ec9e91e65c42e2da5b05dddf4de8 Caleb James DeLisle committed Nov 24, 2012
Showing with 210 additions and 19 deletions.
  1. +70 −13 interface/ICMP6Generator.c
  2. +30 −0 interface/ICMP6Generator_pvt.h
  3. +74 −6 interface/test/ICMP6Generator_test.c
  4. +36 −0 wire/Headers.h
View
@@ -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"
@@ -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) {
@@ -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;
@@ -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;
@@ -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
@@ -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";
@@ -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);
@@ -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);
}
View
@@ -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;

0 comments on commit 6fb67a2

Please sign in to comment.