feat: support dscp classes in socket iptos (#2594)

* refactor: Replace ToS with DSCP

IP TOS was superseded by DSCP in RFC 2474.

Co-authored-by: dgcampea <dgcampea@outlook.com>
This commit is contained in:
Charles Kerr 2022-02-10 15:35:28 -06:00 committed by GitHub
parent ec6a92d761
commit 6383e2ff5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 141 additions and 109 deletions

View File

@ -76,7 +76,13 @@ static auto constexpr Options = std::array<tr_option, 19>{
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "M", false, nullptr },
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", true, "<port>" },
{ 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR ")", "t", true, "<tos>" },
{ 't',
"tos",
"Peer socket DSCP / ToS setting (number, or a DSCP string, e.g. 'af11' or 'cs0', default=" TR_DEFAULT_PEER_SOCKET_TOS_STR
")",
"t",
true,
"<dscp-or-tos>" },
{ 'u', "uplimit", "Set max upload speed in " SPEED_K_STR, "u", true, "<speed>" },
{ 'U', "no-uplimit", "Don't limit the upload speed", "U", false, nullptr },
{ 'v', "verify", "Verify the specified torrent", "v", false, nullptr },
@ -434,7 +440,7 @@ static int parseCommandLine(tr_variant* d, int argc, char const** argv)
break;
case 't':
tr_variantDictAddInt(d, TR_KEY_peer_socket_tos, atoi(my_optarg));
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, my_optarg);
break;
case 'u':

View File

@ -32,7 +32,7 @@
.Op Fl h
.Op Fl m | M
.Op Fl p Ar port
.Op Fl t Ar tos
.Op Fl -tos Ar tos
.Op Fl u Ar number | Fl U
.Op Fl v
.Op Fl w Ar directory
@ -74,17 +74,43 @@ Enable portmapping via NAT-PMP or UPnP
Disable portmapping
.It Fl p -port Ar port
Set the port to listen for incoming peers. (Default: 51413)
.It Fl t -tos Ar tos
Set the peer socket TOS for local router-based traffic shaping.
Valid values are
.It Fl -tos Ar tos
.nf
Use a ToS or DSCP name to set the peer socket service type for
local router-based traffic shaping. Valid values are a decimal
value 0-255, or any of these DSCP strings:
.fi
.Pp
.Dq af11 ,
.Dq af12 ,
.Dq af13 ,
.Dq af21 ,
.Dq af22 ,
.Dq af23 ,
.Dq af31 ,
.Dq af32 ,
.Dq af33 ,
.Dq af41 ,
.Dq af42 ,
.Dq af43 ,
.Dq cs0 ,
.Dq cs1 ,
.Dq cs2 ,
.Dq cs3 ,
.Dq cs4 ,
.Dq cs5 ,
.Dq cs6 ,
.Dq cs7 ,
.Dq ef ,
.Dq le
These ToS keys are deprecated and will be removed in the future:
.Dq default ,
.Dq lowcost ,
.Dq throughput ,
.Dq lowdelay,
.Dq reliability ,
.Dq lowdelay ,
.Dq throughput
.Pp
a decimal value 0-255 or a hexidecimal value 0x00-0xff)
.It Fl u -uplimit Ar number
Set the maximum upload speed in KB/s
.It Fl U -no-uplimit

View File

@ -1,4 +1,4 @@
// This file Copyright © 2010 Transmission authors and contributors.
// This file Copyright © 2010-2022 Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
@ -9,6 +9,7 @@
#include <cstring>
#include <ctime>
#include <string_view>
#include <utility> // std::pair
#include <sys/types.h>
@ -149,8 +150,70 @@ int tr_address_compare(tr_address const* a, tr_address const* b)
* TCP sockets
**********************************************************************/
// RFCs 2474, 3246, 4594 & 8622
// Service class names are defined in RFC 4594, RFC 5865, and RFC 8622.
// Not all platforms have these IPTOS_ definitions, so hardcode them here
static auto constexpr IpTosNames = std::array<std::pair<int, std::string_view>, 28>{ {
{ 0x00, "cs0" }, // IPTOS_CLASS_CS0
{ 0x04, "le" },
{ 0x20, "cs1" }, // IPTOS_CLASS_CS1
{ 0x28, "af11" }, // IPTOS_DSCP_AF11
{ 0x30, "af12" }, // IPTOS_DSCP_AF12
{ 0x38, "af13" }, // IPTOS_DSCP_AF13
{ 0x40, "cs2" }, // IPTOS_CLASS_CS2
{ 0x48, "af21" }, // IPTOS_DSCP_AF21
{ 0x50, "af22" }, // IPTOS_DSCP_AF22
{ 0x58, "af23" }, // IPTOS_DSCP_AF23
{ 0x60, "cs3" }, // IPTOS_CLASS_CS3
{ 0x68, "af31" }, // IPTOS_DSCP_AF31
{ 0x70, "af32" }, // IPTOS_DSCP_AF32
{ 0x78, "af33" }, // IPTOS_DSCP_AF33
{ 0x80, "cs4" }, // IPTOS_CLASS_CS4
{ 0x88, "af41" }, // IPTOS_DSCP_AF41
{ 0x90, "af42" }, // IPTOS_DSCP_AF42
{ 0x98, "af43" }, // IPTOS_DSCP_AF43
{ 0xa0, "cs5" }, // IPTOS_CLASS_CS5
{ 0xb8, "ef" }, // IPTOS_DSCP_EF
{ 0xc0, "cs6" }, // IPTOS_CLASS_CS6
{ 0xe0, "cs7" }, // IPTOS_CLASS_CS7
// <netinet/ip.h> lists these TOS names as deprecated,
// but keep them defined here for backward compatibility
{ 0x00, "routine" }, // IPTOS_PREC_ROUTINE
{ 0x02, "lowcost" }, // IPTOS_LOWCOST
{ 0x02, "mincost" }, // IPTOS_MINCOST
{ 0x04, "reliable" }, // IPTOS_RELIABILITY
{ 0x08, "throughput" }, // IPTOS_THROUGHPUT
{ 0x10, "lowdelay" }, // IPTOS_LOWDELAY
} };
std::string tr_netTosToName(int tos)
{
auto const test = [tos](auto const& pair)
{
return pair.first == tos;
};
auto const it = std::find_if(std::begin(IpTosNames), std::end(IpTosNames), test);
return it == std::end(IpTosNames) ? std::to_string(tos) : std::string{ it->second };
}
std::optional<int> tr_netTosFromName(std::string_view name)
{
auto const test = [&name](auto const& pair)
{
return pair.second == name;
};
auto const it = std::find_if(std::begin(IpTosNames), std::end(IpTosNames), test);
return it != std::end(IpTosNames) ? it->first : tr_parseNum<int>(name);
}
void tr_netSetTOS([[maybe_unused]] tr_socket_t s, [[maybe_unused]] int tos, tr_address_type type)
{
if (s == TR_BAD_SOCKET)
{
return;
}
if (type == TR_AF_INET)
{
#if defined(IP_TOS) && !defined(_WIN32)

View File

@ -9,6 +9,8 @@
#endif
#include <cstddef> // size_t
#include <optional>
#include <string>
#include <string_view>
#ifdef _WIN32
@ -104,23 +106,12 @@ constexpr bool tr_address_is_valid(tr_address const* a)
* Sockets
**********************************************************************/
/* https://en.wikipedia.org/wiki/Differentiated_services#Class_Selector */
enum
{
TR_IPTOS_LOWCOST = 0x38, /* AF13: low prio, high drop */
TR_IPTOS_LOWDELAY = 0x70, /* AF32: high prio, mid drop */
TR_IPTOS_THRUPUT = 0x20, /* CS1: low prio, undef drop */
TR_IPTOS_RELIABLE = 0x28 /* AF11: low prio, low drop */
};
struct tr_session;
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppressMsgs);
tr_socket_t tr_netAccept(tr_session* session, tr_socket_t bound, tr_address* setme_addr, tr_port* setme_port);
void tr_netSetTOS(tr_socket_t s, int tos, tr_address_type type);
void tr_netSetCongestionControl(tr_socket_t s, char const* algorithm);
void tr_netClose(tr_session* session, tr_socket_t s);
@ -129,6 +120,17 @@ void tr_netCloseSocket(tr_socket_t fd);
bool tr_net_hasIPv6(tr_port);
/// TOS / DSCP
// get a string of one of <netinet/ip.h>'s IPTOS_ values, e.g. "cs0"
std::string tr_netTosToName(int tos);
// get the number that corresponds to the specified IPTOS_ name, e.g. "cs0" returns 0x00
std::optional<int> tr_netTosFromName(std::string_view name);
// set the IPTOS_ value for the specified socket
void tr_netSetTOS(tr_socket_t sock, int tos, tr_address_type type);
/**
* @brief get a human-representable string representing the network error.
* @param err an errno on Unix/Linux and an WSAError on win32)

View File

@ -624,7 +624,7 @@ static tr_peerIo* tr_peerIoNew(
if (socket.type == TR_PEER_SOCKET_TYPE_TCP)
{
tr_netSetTOS(socket.handle.tcp, session->peerSocketTos(), addr->type);
session->setSocketTOS(socket.handle.tcp, addr->type);
maybeSetCongestionAlgorithm(socket.handle.tcp, session->peerCongestionAlgorithm());
}
@ -979,7 +979,7 @@ int tr_peerIoReconnect(tr_peerIo* io)
io->event_write = event_new(session->event_base, io->socket.handle.tcp, EV_WRITE, event_write_cb, io);
event_enable(io, pendingEvents);
tr_netSetTOS(io->socket.handle.tcp, session->peerSocketTos(), io->addr.type);
io->session->setSocketTOS(io->socket.handle.tcp, io->addr.type);
maybeSetCongestionAlgorithm(io->socket.handle.tcp, session->peerCongestionAlgorithm());
return 0;

View File

@ -250,62 +250,6 @@ tr_address const* tr_sessionGetPublicAddress(tr_session const* session, int tr_a
****
***/
static int parseTos(std::string_view tos_in)
{
auto tos = tr_strlower(tr_strvStrip(tos_in));
if (tos == ""sv || tos == "default"sv)
{
return 0;
}
if (tos == "lowcost"sv || tos == "mincost"sv)
{
return TR_IPTOS_LOWCOST;
}
if (tos == "throughput"sv)
{
return TR_IPTOS_THRUPUT;
}
if (tos == "reliability"sv)
{
return TR_IPTOS_RELIABLE;
}
if (tos == "lowdelay"sv)
{
return TR_IPTOS_LOWDELAY;
}
return std::stoi(tos);
}
static std::string format_tos(int value)
{
switch (value)
{
case 0:
return "default";
case TR_IPTOS_LOWCOST:
return "lowcost";
case TR_IPTOS_THRUPUT:
return "throughput";
case TR_IPTOS_RELIABLE:
return "reliability";
case TR_IPTOS_LOWDELAY:
return "lowdelay";
default:
return std::to_string(value);
}
}
#ifdef TR_LIGHTWEIGHT
#define TR_DEFAULT_ENCRYPTION TR_CLEAR_PREFERRED
#else
@ -416,7 +360,7 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
tr_variantDictAddBool(d, TR_KEY_peer_port_random_on_start, s->isPortRandom);
tr_variantDictAddInt(d, TR_KEY_peer_port_random_low, s->randomPortLow);
tr_variantDictAddInt(d, TR_KEY_peer_port_random_high, s->randomPortHigh);
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, format_tos(s->peerSocketTos()));
tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, tr_netTosToName(s->peer_socket_tos_));
tr_variantDictAddStr(d, TR_KEY_peer_congestion_algorithm, s->peerCongestionAlgorithm());
tr_variantDictAddBool(d, TR_KEY_pex_enabled, s->isPexEnabled);
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, tr_sessionIsPortForwardingEnabled(s));
@ -827,9 +771,16 @@ static void sessionSetImpl(void* vdata)
tr_sessionSetEncryption(session, tr_encryption_mode(i));
}
if (tr_variantDictFindStrView(settings, TR_KEY_peer_socket_tos, &sv))
if (tr_variantDictFindInt(settings, TR_KEY_peer_socket_tos, &i))
{
session->setPeerSocketTos(parseTos(sv));
session->peer_socket_tos_ = i;
}
else if (tr_variantDictFindStrView(settings, TR_KEY_peer_socket_tos, &sv))
{
if (auto ip_tos = tr_netTosFromName(sv); ip_tos)
{
session->peer_socket_tos_ = *ip_tos;
}
}
sv = ""sv;

View File

@ -231,14 +231,9 @@ public:
peer_congestion_algorithm_ = algorithm;
}
int peerSocketTos() const
void setSocketTOS(tr_socket_t sock, tr_address_type type)
{
return peer_socket_tos_;
}
void setPeerSocketTos(int tos)
{
peer_socket_tos_ = tos;
tr_netSetTOS(sock, peer_socket_tos_, type);
}
public:
@ -358,6 +353,11 @@ public:
std::unique_ptr<tr_rpc_server> rpc_server_;
// One of <netinet/ip.h>'s IPTOS_ values.
// See tr_netTos*() in libtransmission/net.h for more info
// Only session.cc should use this.
int peer_socket_tos_ = *tr_netTosFromName(TR_DEFAULT_PEER_SOCKET_TOS_STR);
private:
static std::recursive_mutex session_mutex_;
@ -367,8 +367,6 @@ private:
std::string incomplete_dir_;
std::string peer_congestion_algorithm_;
int peer_socket_tos_ = 0;
std::array<bool, TR_SCRIPT_N_TYPES> scripts_enabled_;
bool blocklist_enabled_ = false;
bool incomplete_dir_enabled_ = false;

View File

@ -107,22 +107,8 @@ void tr_udpSetSocketBuffers(tr_session* session)
void tr_udpSetSocketTOS(tr_session* session)
{
auto const tos = session->peerSocketTos();
if (tos != 0)
{
return;
}
if (session->udp_socket != TR_BAD_SOCKET)
{
tr_netSetTOS(session->udp_socket, tos, TR_AF_INET);
}
if (session->udp6_socket != TR_BAD_SOCKET)
{
tr_netSetTOS(session->udp6_socket, tos, TR_AF_INET6);
}
session->setSocketTOS(session->udp_socket, TR_AF_INET);
session->setSocketTOS(session->udp6_socket, TR_AF_INET6);
}
/* BEP-32 has a rather nice explanation of why we need to bind to one

View File

@ -126,7 +126,7 @@ char const* tr_getDefaultDownloadDir(void);
#define TR_DEFAULT_RPC_PORT 9091
#define TR_DEFAULT_RPC_URL_STR "/transmission/"
#define TR_DEFAULT_PEER_PORT_STR "51413"
#define TR_DEFAULT_PEER_SOCKET_TOS_STR "default"
#define TR_DEFAULT_PEER_SOCKET_TOS_STR "le"
#define TR_DEFAULT_PEER_LIMIT_GLOBAL_STR "200"
#define TR_DEFAULT_PEER_LIMIT_TORRENT_STR "50"
#define TR_DEFAULT_PEER_LIMIT_TORRENT 50

View File

@ -129,7 +129,7 @@ std::array<Prefs::PrefItem, Prefs::PREFS_COUNT> const Prefs::Items{
{ QUEUE_STALLED_MINUTES, TR_KEY_queue_stalled_minutes, QVariant::Int },
{ SCRIPT_TORRENT_DONE_ENABLED, TR_KEY_script_torrent_done_enabled, QVariant::Bool },
{ SCRIPT_TORRENT_DONE_FILENAME, TR_KEY_script_torrent_done_filename, QVariant::String },
{ SOCKET_TOS, TR_KEY_peer_socket_tos, QVariant::Int },
{ SOCKET_TOS, TR_KEY_peer_socket_tos, QVariant::String },
{ START, TR_KEY_start_added_torrents, QVariant::Bool },
{ TRASH_ORIGINAL, TR_KEY_trash_original_torrent_files, QVariant::Bool },
{ PEX_ENABLED, TR_KEY_pex_enabled, QVariant::Bool },