From 5adde5ab9c369e9c0389949c82804e9f23e39541 Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Fri, 15 Dec 2017 19:02:45 -0800 Subject: mpmd: Add get_mtu() API and MTU discovery for UDP transports The mpmd_xport_mgr classes can now return their own MTU. The UDP xport manager is a special case, it doesn't actually know its MTU, and thus runs an MTU discovery, using the MPM-ECHO command to discover MTU by sending variable-size packets as a probing mechanism. Reviewed-by: Trung Tran --- host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp | 4 + host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp | 6 ++ host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp | 4 + host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp | 122 +++++++++++++++++++++++++ host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp | 7 ++ 5 files changed, 143 insertions(+) (limited to 'host/lib/usrp/mpmd') diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp index e5a3b7e31..c3e71d495 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_base.hpp @@ -38,6 +38,10 @@ public: virtual bool is_valid( const mpmd_xport_mgr::xport_info_t& xport_info ) const = 0; + + virtual size_t get_mtu( + const uhd::direction_t dir + ) const = 0; }; }}} /* namespace uhd::mpmd::xport */ diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp index 2e4f024b4..30fb27e04 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.cpp @@ -129,6 +129,12 @@ bool mpmd_xport_ctrl_liberio::is_valid( return xport_info.at("type") == "liberio"; } +size_t mpmd_xport_ctrl_liberio::get_mtu( + const uhd::direction_t /* dir */ +) const { + return 8000; +} + uhd::transport::muxed_zero_copy_if::sptr mpmd_xport_ctrl_liberio::make_muxed_liberio_xport( const std::string &tx_dev, diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp index 76bb88f03..52fc6ae48 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_liberio.hpp @@ -35,6 +35,10 @@ public: const mpmd_xport_mgr::xport_info_t& xport_info ) const; + size_t get_mtu( + const uhd::direction_t dir + ) const ; + private: /*! Create a muxed liberio transport for control packets */ uhd::transport::muxed_zero_copy_if::sptr make_muxed_liberio_xport( diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp index 0da080724..d900467d3 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.cpp @@ -4,9 +4,13 @@ // SPDX-License-Identifier: GPL-3.0 // +#include "mpmd_impl.hpp" #include "mpmd_xport_mgr.hpp" #include "mpmd_xport_ctrl_udp.hpp" #include +#include +#include + using namespace uhd; using namespace uhd::mpmd::xport; @@ -22,6 +26,8 @@ namespace { const size_t MPMD_10GE_DATA_FRAME_MAX_SIZE = 8000; // CHDR packet size in bytes + const double MPMD_MTU_DISCOVERY_TIMEOUT = 0.02; // seconds + std::vector get_addrs_from_mb_args( const uhd::device_addr_t& mb_args ) { @@ -36,6 +42,104 @@ namespace { } return addrs; } + + /*! Do a binary search to discover MTU + * + * Uses the MPM echo service to figure out MTU. We simply send a bunch of + * packets and see if they come back until we converged on the path MTU. + * The end result must lie between \p min_frame_size and \p max_frame_size. + * + * \param address IP address + * \param port UDP port (yeah it's a string!) + * \param min_frame_size Minimum frame size, initialize algorithm to start + * with this value + * \param max_frame_size Maximum frame size, initialize algorithm to start + * with this value + * \param echo_timeout Timeout value in seconds. For frame sizes that + * exceed the MTU, we don't expect a response, and this + * is the amount of time we'll wait before we assume + * the frame size exceeds the MTU. + */ + size_t discover_mtu( + const std::string &address, + const std::string &port, + size_t min_frame_size, + size_t max_frame_size, + const double echo_timeout = 0.020 + ) { + const size_t echo_prefix_offset = + uhd::mpmd::mpmd_impl::MPM_ECHO_CMD.size(); + const size_t mtu_hdr_len = echo_prefix_offset + 10; + UHD_ASSERT_THROW(min_frame_size < max_frame_size); + UHD_ASSERT_THROW(min_frame_size % 4 == 0); + UHD_ASSERT_THROW(max_frame_size % 4 == 0); + UHD_ASSERT_THROW(min_frame_size >= echo_prefix_offset + mtu_hdr_len); + using namespace uhd::transport; + // The return port will probably differ from the discovery port, so we + // need a "broadcast" UDP connection; using make_connected() would + // drop packets + udp_simple::sptr udp = udp_simple::make_broadcast(address, port); + std::string send_buf(uhd::mpmd::mpmd_impl::MPM_ECHO_CMD); + send_buf.resize(max_frame_size, '#'); + UHD_ASSERT_THROW(send_buf.size() == max_frame_size); + std::vector recv_buf; + recv_buf.resize(max_frame_size, ' '); + + // Little helper to check returned packets match the sent ones + auto require_bufs_match = [&recv_buf, &send_buf, mtu_hdr_len]( + const size_t len + ){ + if (len < mtu_hdr_len or std::memcmp( + (void *) &recv_buf[0], + (void *) &send_buf[0], + mtu_hdr_len + ) != 0) { + throw uhd::runtime_error("Unexpected content of MTU " + "discovery return packet!"); + } + }; + UHD_LOG_TRACE("MPMD", "Determining UDP MTU... "); + size_t seq_no = 0; + while (min_frame_size < max_frame_size) { + // Only test multiples of 4 bytes! + const size_t test_frame_size = + (max_frame_size/2 + min_frame_size/2 + 3) & ~size_t(3); + // Encode sequence number and current size in the string, makes it + // easy to debug in code or Wireshark. Is also used for identifying + // response packets. + std::sprintf( + &send_buf[echo_prefix_offset], + ";%04lu,%04lu", + seq_no++, + test_frame_size + ); + UHD_LOG_TRACE("MPMD", "Testing frame size " << test_frame_size); + udp->send(boost::asio::buffer(&send_buf[0], test_frame_size)); + + const size_t len = + udp->recv(boost::asio::buffer(recv_buf), echo_timeout); + if (len == 0) { + // Nothing received, so this is probably too big + max_frame_size = test_frame_size - 4; + } else if (len >= test_frame_size) { + // Size went through, so bump the minimum + require_bufs_match(len); + min_frame_size = test_frame_size; + } else if (len < test_frame_size) { + // This is an odd case. Something must have snipped the packet + // on the way back. Still, we'll just back off and try + // something smaller. + UHD_LOG_DEBUG("MPMD", + "Unexpected packet truncation during MTU discovery."); + require_bufs_match(len); + max_frame_size = len; + } + } + UHD_LOG_DEBUG("MPMD", + "Path MTU for address " << address << ": " << min_frame_size); + return min_frame_size; + } + } @@ -45,7 +149,21 @@ mpmd_xport_ctrl_udp::mpmd_xport_ctrl_udp( , _recv_args(filter_args(mb_args, "recv")) , _send_args(filter_args(mb_args, "send")) , _available_addrs(get_addrs_from_mb_args(mb_args)) + , _mtu(MPMD_10GE_DATA_FRAME_MAX_SIZE) { + auto discover_mtu_for_ip = [](const std::string &ip_addr){ + return discover_mtu( + ip_addr, + std::to_string(mpmd_impl::MPM_DISCOVERY_PORT), + IP_PROTOCOL_MIN_MTU_SIZE-IP_PROTOCOL_UDP_PLUS_IP_HEADER, + MPMD_10GE_DATA_FRAME_MAX_SIZE, + MPMD_MTU_DISCOVERY_TIMEOUT + ); + }; + + for (const auto &ip_addr : _available_addrs) { + _mtu = std::min(_mtu, discover_mtu_for_ip(ip_addr)); + } } uhd::both_xports_t @@ -105,3 +223,7 @@ bool mpmd_xport_ctrl_udp::is_valid( ) != _available_addrs.cend(); } +size_t mpmd_xport_ctrl_udp::get_mtu(const uhd::direction_t /*dir*/) const +{ + return _mtu; +} diff --git a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp index 0c8c19479..fad775c39 100644 --- a/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp +++ b/host/lib/usrp/mpmd/mpmd_xport_ctrl_udp.hpp @@ -34,11 +34,18 @@ public: const mpmd_xport_mgr::xport_info_t& xport_info ) const; + size_t get_mtu( + const uhd::direction_t dir + ) const; + private: const uhd::device_addr_t _mb_args; const uhd::dict _recv_args; const uhd::dict _send_args; + //! A list of IP addresses we can connect our CHDR connections to const std::vector _available_addrs; + //! MTU + size_t _mtu; }; }}} /* namespace uhd::mpmd::xport */ -- cgit v1.2.3