Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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

…ment beyond 1280 bytes
  • Loading branch information...
commit 6fb67a2af5a7ec9e91e65c42e2da5b05dddf4de8 1 parent 9fe6551
Caleb James DeLisle authored
View
83 interface/ICMP6Generator.c
@@ -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,6 +93,65 @@ 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)
{
@@ -108,22 +159,27 @@ static uint8_t incoming(struct Message* msg, struct Interface* iface)
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;
View
30 interface/ICMP6Generator_pvt.h
@@ -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
View
80 interface/test/ICMP6Generator_test.c
@@ -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
36 wire/Headers.h
@@ -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;
Please sign in to comment.
Something went wrong with that request. Please try again.