From 397ef82b12263edee88f15a8b137f5bd0e292996 Mon Sep 17 00:00:00 2001 From: Olivier SOLDANO <olivier.soldano@savoirfairelinux.com> Date: Wed, 24 Jan 2018 12:16:33 -0500 Subject: [PATCH] fix asymetric IPV4/IPV6 mtu discovery when the remote (server) has a IPV6 interface selected by ICE, and local (client) has a IPV4 selected, the path MTU discovery triggers errors for packets too big on server side because of different IP headers overhead. Hence we have to signal to the TLS session to reduce the MTU discovery packets payload size on client side accordingly. [GR: use GenericSocket class to export local/remote address] Change-Id: I724b97c9ccd5e7240a87fb60ef000b4485710131 Signed-off-by: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> Reviewed-by: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com> --- src/generic_io.h | 12 ++++++++++++ src/ice_socket.h | 4 ++++ src/ice_transport.cpp | 12 ++++++++++++ src/security/tls_session.cpp | 16 +++++++++++++++- 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/generic_io.h b/src/generic_io.h index 6f67b24eab..4147959c92 100644 --- a/src/generic_io.h +++ b/src/generic_io.h @@ -20,6 +20,8 @@ #pragma once +#include "ip_utils.h" + #include <functional> #include <vector> #include <system_error> @@ -100,6 +102,16 @@ public: return res; } + /// Return the local IP address if known. + /// \note The address is not valid (addr.isUnspecified() returns true) if it's not known + /// or not available. + virtual IpAddr localAddr() const { return {}; } + + /// Return the remote IP address if known. + /// \note The address is not valid (addr.isUnspecified() returns true) if it's not known + /// or not available. + virtual IpAddr remoteAddr() const { return {}; } + protected: GenericSocket() = default; }; diff --git a/src/ice_socket.h b/src/ice_socket.h index a61413aca0..b05ae3f6a0 100644 --- a/src/ice_socket.h +++ b/src/ice_socket.h @@ -87,6 +87,10 @@ public: void setOnRecv(RecvCb&& cb) override; + IpAddr localAddr() const override; + + IpAddr remoteAddr() const override; + private: const int compId_; std::shared_ptr<IceTransport> ice_; diff --git a/src/ice_transport.cpp b/src/ice_transport.cpp index a7fdf5e0de..4e02c26378 100644 --- a/src/ice_transport.cpp +++ b/src/ice_transport.cpp @@ -1226,6 +1226,18 @@ IceSocketTransport::read(ValueType* buf, std::size_t len, std::error_code& ec) return res; } +IpAddr +IceSocketTransport::localAddr() const +{ + return ice_->getLocalAddress(compId_); +} + +IpAddr +IceSocketTransport::remoteAddr() const +{ + return ice_->getRemoteAddress(compId_); +} + //============================================================================== void diff --git a/src/security/tls_session.cpp b/src/security/tls_session.cpp index b336a4366f..eaee14128b 100644 --- a/src/security/tls_session.cpp +++ b/src/security/tls_session.cpp @@ -68,6 +68,7 @@ static constexpr auto HEARTBEAT_RETRANS_TIMEOUT = std::chrono::milliseconds(700) static constexpr auto HEARTBEAT_TOTAL_TIMEOUT = HEARTBEAT_RETRANS_TIMEOUT * HEARTBEAT_TRIES; // gnutls heartbeat time limit for heartbeat procedure (in milliseconds) static constexpr int MISS_ORDERING_LIMIT = 32; // maximal accepted distance of out-of-order packet (note: must be a signed type) static constexpr auto RX_OOO_TIMEOUT = std::chrono::milliseconds(1500); +static constexpr int ASYMETRIC_TRANSPORT_MTU_OFFSET = 20; // when client, if your local IP is IPV4 and server is IPV6; you must reduce your MTU to avoid packet too big error on server side. the offset is the difference in size of IP headers // Helper to cast any duration into an integer number of milliseconds template <class Rep, class Period> @@ -923,13 +924,26 @@ TlsSession::TlsSessionImpl::pathMtuHeartbeat() HEARTBEAT_TOTAL_TIMEOUT.count()); int errno_send = GNUTLS_E_SUCCESS; + int mtuOffset = 0; + + // when the remote (server) has a IPV6 interface selected by ICE, and local (client) has a IPV4 selected, + // the path MTU discovery triggers errors for packets too big on server side because of different IP headers overhead. + // Hence we have to signal to the TLS session to reduce the MTU on client size accordingly. + if (transport_.localAddr().isIpv4() and transport_.remoteAddr().isIpv6()) { + mtuOffset = ASYMETRIC_TRANSPORT_MTU_OFFSET; + RING_WARN() << "[TLS] local/remote IP protocol version not alike, use an MTU offset of " + << ASYMETRIC_TRANSPORT_MTU_OFFSET << " bytes to compensate"; + } + mtuProbe_ = MTUS_[0]; + for (auto mtu: MTUS_) { gnutls_dtls_set_mtu(session_, mtu); auto data_mtu = gnutls_dtls_get_data_mtu(session_); RING_DBG() << "[TLS] PMTUD: mtu " << mtu << ", payload " << data_mtu; - auto bytesToSend = data_mtu - 3; // want to know why -3? ask gnutls! + auto bytesToSend = data_mtu - mtuOffset - 3; // want to know why -3? ask gnutls! + do { errno_send = gnutls_heartbeat_ping(session_, bytesToSend, HEARTBEAT_TRIES, GNUTLS_HEARTBEAT_WAIT); } while (errno_send == GNUTLS_E_AGAIN || errno_send == GNUTLS_E_INTERRUPTED); -- GitLab