From ce37461705206606732dec0f6f72ec2f6a0ea733 Mon Sep 17 00:00:00 2001 From: Kleissner Date: Tue, 9 Nov 2021 21:04:06 +0100 Subject: [PATCH] Improve MTU settings and documentation. #41 --- README.md | 8 ++++++++ protocol/Message Encoding Transfer.go | 4 +++- protocol/Message Encoding.go | 11 ++++++++--- protocol/Packet Encoding.go | 8 ++++---- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index fcf4bd0..727b12d 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,14 @@ If a bucket is full when a new peer connects `ShouldEvict` is called. It compare * The default reply timeout (round-trip time) is 20 seconds set in `ReplyTimeout`. This applies to Response and Pong messages. The RTT timeout implies an average minimum connection speed between peers of about 6.4 KB/s for files of 64 KB size. * Separate timeouts for file transfers will be established. +### MTU + +The default MTU is set to 1280 bytes (see `internetSafeMTU` constant). This value (and by extension the lower value `TransferMaxEmbedSize` for file transfer) is chosen for safe transfer, not for highest performance. Different environments (IPv4, IPv6, Ethernet, Internet) have different smallest and common supported MTUs. MTU negotiation or detection is currently not implemented. + +### File Transfer Performance + +The packet encryption/signing overhead appears to require significant CPU overhead during file transfer. This can be improved in the future by defining special file transfer packets that start with a UUID and not the regular protocol header. It would reduce processing time, increase payload data per packet, and therefore the overall transfer speed. A symmetric encryption algorithm (and key negotiation during file transfer initiation) would be required to not lose the security benefit. + ### Network Listen Unless specified in the config via `Listen`, it will listen on all network adapters. The default port is 112, but that may be randomized in the future. diff --git a/protocol/Message Encoding Transfer.go b/protocol/Message Encoding Transfer.go index 9330583..c0f5bb1 100644 --- a/protocol/Message Encoding Transfer.go +++ b/protocol/Message Encoding Transfer.go @@ -75,7 +75,9 @@ func DecodeTransfer(msg *MessageRaw) (result *MessageTransfer, err error) { return result, nil } -// TransferMaxEmbedSize is the maximum size of embedded data inside the Transfer message. +// TransferMaxEmbedSize is a recommended default upper size of embedded data inside the Transfer message, to be used as MaxPacketSize limit in the embedded protocol. +// This value is chosen as the lowest denominator of different environments (IPv4, IPv6, Ethernet, Internet) for safe transfer, not for highest performance. +// The caller may send bigger payloads but may risk that data packets are simply dropped and never arrive. A MTU negotiation or detection could pimp that. const TransferMaxEmbedSize = internetSafeMTU - PacketLengthMin - transferPayloadHeaderSize // EncodeTransfer encodes a transfer message. The embedded packet size must be smaller than TransferMaxEmbedSize. diff --git a/protocol/Message Encoding.go b/protocol/Message Encoding.go index 20011c1..4b8ab88 100644 --- a/protocol/Message Encoding.go +++ b/protocol/Message Encoding.go @@ -22,12 +22,17 @@ type MessageRaw struct { SequenceInfo *SequenceExpiry // Sequence } -// The maximum packet size is 65507 bytes = 65535 - 8 UDP byte header - 20 byte IP header. +// The maximum packet size is = 65535 - 8 UDP byte header - 40 byte IPv6 header (IPv4 header is only 20 bytes). // However, due to the MTU soft limit and fragmentation, packets should be as small as possible. -const udpMaxPacketSize = 65507 +const udpMaxPacketSize = 65535 - 8 - 40 // internetSafeMTU is a value relatively safe to use for transmitting over the internet -const internetSafeMTU = 1500 - 20 - 8 +// Theory: The value is different for IPv4 (min 576 bytes, Ethernet 1500 bytes) and IPv6 (min 1280 bytes). 8 byte UDP header must be subtracted, as well as the IP header (20 bytes for IPv4, 40 for IPv6). +// One simple test during development showed that 1500 - 8 - 40 - 8 worked for file transfer over IPv6 in Prague. +// For IPv6 the internet recommends the minimal possible value: 1280 bytes. +// This will be good enough for now. MTU negotiation that deviates from this value can be implemented separately (for example as part of file transfer). +// Since packets may be sent at anytime via IPv4/IPv6 connections (even concurrently on multiple), there is a single MTU value here. +const internetSafeMTU = 1280 - 8 - 40 // isPacketSizeExceed checks if the max packet size would be exceeded with the payload func isPacketSizeExceed(currentSize int, testSize int) bool { diff --git a/protocol/Packet Encoding.go b/protocol/Packet Encoding.go index 3044cfc..4000c43 100644 --- a/protocol/Packet Encoding.go +++ b/protocol/Packet Encoding.go @@ -120,15 +120,15 @@ func PacketEncrypt(senderPrivateKey *btcec.PrivateKey, receiverPublicKey *btcec. } func packetGarbage(packetLength int) (random []byte) { - // Align maximum length at 508 bytes (UDP minimum no fragmentation) and 1472 bytes (MTU). + // Align maximum length at 508 bytes (UDP minimum no fragmentation) and at a relatively safe MTU. maxLength := maxRandomGarbage switch { - case packetLength == 508, packetLength == 1472: + case packetLength == 508, packetLength == internetSafeMTU: return nil case packetLength < 508 && (508-packetLength) < maxRandomGarbage: maxLength = packetLength - 508 - case packetLength < 1472 && (1472-packetLength) < maxRandomGarbage: - maxLength = packetLength - 1472 + case packetLength < internetSafeMTU && (internetSafeMTU-packetLength) < maxRandomGarbage: + maxLength = packetLength - internetSafeMTU } b := make([]byte, rand.Intn(maxLength))