From 3fa42cb8bb6fcb54b539b89a8a4693cf167963d8 Mon Sep 17 00:00:00 2001 From: John Hood Date: Mon, 16 Nov 2015 02:35:18 -0500 Subject: [PATCH] Support different IPv4 and IPv6 MTUs. Closes #688. --- src/crypto/crypto.h | 2 ++ src/network/network.cc | 19 ++++++++++++++++- src/network/network.h | 35 ++++++++++++++++++++++++++++++-- src/network/transportfragment.cc | 9 ++++---- src/network/transportfragment.h | 9 +++----- src/network/transportsender.cc | 5 +++-- 6 files changed, 64 insertions(+), 15 deletions(-) diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 174070d..da3b49b 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -132,6 +132,8 @@ namespace Crypto { public: static const int RECEIVE_MTU = 2048; + /* Overhead (not counting the nonce, which is handled by network transport) */ + static const int ADDED_BYTES = 16 /* final OCB block */; Session( Base64Key s_key ); ~Session(); diff --git a/src/network/network.cc b/src/network/network.cc index 4658ea8..bdb196e 100644 --- a/src/network/network.cc +++ b/src/network/network.cc @@ -203,6 +203,20 @@ const std::vector< int > Connection::fds( void ) const return ret; } +void Connection::set_MTU( int family ) +{ + switch ( family ) { + case AF_INET: + MTU = DEFAULT_IPV4_MTU - IPV4_HEADER_LEN; + break; + case AF_INET6: + MTU = DEFAULT_IPV6_MTU - IPV6_HEADER_LEN; + break; + default: + throw NetworkException( "Unknown address family", 0 ); + } +} + class AddrInfo { public: struct addrinfo *res; @@ -319,6 +333,7 @@ bool Connection::try_bind( const char *addr, int port_low, int port_high ) } if ( bind( sock(), &local_addr.sa, local_addr_len ) == 0 ) { + set_MTU( local_addr.sa.sa_family ); return true; } else if ( i == search_high ) { /* last port to search */ int saved_errno = errno; @@ -379,6 +394,8 @@ Connection::Connection( const char *key_str, const char *ip, const char *port ) has_remote_addr = true; socks.push_back( Socket( remote_addr.sa.sa_family ) ); + + set_MTU( remote_addr.sa.sa_family ); } void Connection::send( string s ) @@ -404,7 +421,7 @@ void Connection::send( string s ) send_exception = NetworkException( "sendto", errno ); if ( errno == EMSGSIZE ) { - MTU = 500; /* payload MTU of last resort */ + MTU = DEFAULT_SEND_MTU; /* payload MTU of last resort */ } } diff --git a/src/network/network.h b/src/network/network.h index 6672572..23022ac 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -101,7 +101,33 @@ namespace Network { class Connection { private: - static const int DEFAULT_SEND_MTU = 1300; + /* + * For IPv4, guess the typical (minimum) header length; + * fragmentation is not dangerous, just inefficient. + */ + static const int IPV4_HEADER_LEN = 20 /* base IP header */ + + 8 /* UDP */; + /* + * For IPv6, we don't want to ever have MTU issues, so make a + * conservative guess about header size. + */ + static const int IPV6_HEADER_LEN = 40 /* base IPv6 header */ + + 16 /* 2 minimum-sized extension headers */ + + 8 /* UDP */; + /* Application datagram MTU. For constructors and fallback. */ + static const int DEFAULT_SEND_MTU = 500; + /* + * IPv4 MTU. Don't use full Ethernet-derived MTU, + * mobile networks have high tunneling overhead. + * + * About 95% of IPv4 TCP MSS I see are >= 1360. + * An IP MTU is 20 bytes larger. + * We let smaller MTUs fragment. + */ + static const int DEFAULT_IPV4_MTU = 1380; + /* IPv6 MTU. Use the guaranteed minimum to avoid fragmentation. */ + static const int DEFAULT_IPV6_MTU = 1280; + static const uint64_t MIN_RTO = 50; /* ms */ static const uint64_t MAX_RTO = 1000; /* ms */ @@ -139,7 +165,7 @@ namespace Network { bool server; - int MTU; + int MTU; /* application datagram MTU */ Base64Key key; Session session; @@ -175,7 +201,12 @@ namespace Network { string recv_one( int sock_to_recv, bool nonblocking ); + void set_MTU( int family ); + public: + /* Network transport overhead. */ + static const int ADDED_BYTES = 8 /* seqno/nonce */ + 4 /* timestamps */; + Connection( const char *desired_ip, const char *desired_port ); /* server */ Connection( const char *key_str, const char *ip, const char *port ); /* client */ diff --git a/src/network/transportfragment.cc b/src/network/transportfragment.cc index d3cb81f..ad52187 100644 --- a/src/network/transportfragment.cc +++ b/src/network/transportfragment.cc @@ -154,8 +154,9 @@ bool Fragment::operator==( const Fragment &x ) const && ( initialized == x.initialized ) && ( contents == x.contents ); } -vector Fragmenter::make_fragments( const Instruction &inst, int MTU ) +vector Fragmenter::make_fragments( const Instruction &inst, size_t MTU ) { + MTU -= Fragment::frag_header_len; if ( (inst.old_num() != last_instruction.old_num()) || (inst.new_num() != last_instruction.new_num()) || (inst.ack_num() != last_instruction.ack_num()) @@ -182,9 +183,9 @@ vector Fragmenter::make_fragments( const Instruction &inst, int MTU ) string this_fragment; bool final = false; - if ( int( payload.size() + HEADER_LEN ) > MTU ) { - this_fragment = string( payload.begin(), payload.begin() + MTU - HEADER_LEN ); - payload = string( payload.begin() + MTU - HEADER_LEN, payload.end() ); + if ( payload.size() > MTU ) { + this_fragment = string( payload.begin(), payload.begin() + MTU ); + payload = string( payload.begin() + MTU, payload.end() ); } else { this_fragment = payload; payload.clear(); diff --git a/src/network/transportfragment.h b/src/network/transportfragment.h index 5e99ed1..9fda5a8 100644 --- a/src/network/transportfragment.h +++ b/src/network/transportfragment.h @@ -44,14 +44,11 @@ using std::string; using namespace TransportBuffers; namespace Network { - static const int HEADER_LEN = 66; - class Fragment { - private: + public: static const size_t frag_header_len = sizeof( uint64_t ) + sizeof( uint16_t ); - public: uint64_t id; uint16_t fragment_num; bool final; @@ -94,7 +91,7 @@ namespace Network { private: uint64_t next_instruction_id; Instruction last_instruction; - int last_MTU; + size_t last_MTU; public: Fragmenter() : next_instruction_id( 0 ), last_instruction(), last_MTU( -1 ) @@ -102,7 +99,7 @@ namespace Network { last_instruction.set_old_num( -1 ); last_instruction.set_new_num( -1 ); } - vector make_fragments( const Instruction &inst, int MTU ); + vector make_fragments( const Instruction &inst, size_t MTU ); uint64_t last_ack_sent( void ) const { return last_instruction.ack_num(); } }; diff --git a/src/network/transportsender.cc b/src/network/transportsender.cc index e641655..5f2ea4d 100644 --- a/src/network/transportsender.cc +++ b/src/network/transportsender.cc @@ -320,8 +320,9 @@ void TransportSender::send_in_fragments( string diff, uint64_t new_num shutdown_tries++; } - vector fragments = fragmenter.make_fragments( inst, connection->get_MTU() ); - + vector fragments = fragmenter.make_fragments( inst, connection->get_MTU() + - Network::Connection::ADDED_BYTES + - Crypto::Session::ADDED_BYTES ); for ( vector::iterator i = fragments.begin(); i != fragments.end(); i++ ) {