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', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", false, nullptr },
{ 'M', "no-portmap", "Disable portmapping", "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>" }, { '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', "uplimit", "Set max upload speed in " SPEED_K_STR, "u", true, "<speed>" },
{ 'U', "no-uplimit", "Don't limit the upload speed", "U", false, nullptr }, { 'U', "no-uplimit", "Don't limit the upload speed", "U", false, nullptr },
{ 'v', "verify", "Verify the specified torrent", "v", 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; break;
case 't': case 't':
tr_variantDictAddInt(d, TR_KEY_peer_socket_tos, atoi(my_optarg)); tr_variantDictAddStr(d, TR_KEY_peer_socket_tos, my_optarg);
break; break;
case 'u': case 'u':

View File

@ -32,7 +32,7 @@
.Op Fl h .Op Fl h
.Op Fl m | M .Op Fl m | M
.Op Fl p Ar port .Op Fl p Ar port
.Op Fl t Ar tos .Op Fl -tos Ar tos
.Op Fl u Ar number | Fl U .Op Fl u Ar number | Fl U
.Op Fl v .Op Fl v
.Op Fl w Ar directory .Op Fl w Ar directory
@ -74,17 +74,43 @@ Enable portmapping via NAT-PMP or UPnP
Disable portmapping Disable portmapping
.It Fl p -port Ar port .It Fl p -port Ar port
Set the port to listen for incoming peers. (Default: 51413) Set the port to listen for incoming peers. (Default: 51413)
.It Fl t -tos Ar tos .It Fl -tos Ar tos
Set the peer socket TOS for local router-based traffic shaping. .nf
Valid values are 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 .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 default ,
.Dq lowcost , .Dq lowcost ,
.Dq throughput , .Dq lowdelay,
.Dq reliability , .Dq reliability ,
.Dq lowdelay , .Dq throughput
.Pp .Pp
a decimal value 0-255 or a hexidecimal value 0x00-0xff)
.It Fl u -uplimit Ar number .It Fl u -uplimit Ar number
Set the maximum upload speed in KB/s Set the maximum upload speed in KB/s
.It Fl U -no-uplimit .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. // It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder. // License text can be found in the licenses/ folder.
@ -9,6 +9,7 @@
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <string_view> #include <string_view>
#include <utility> // std::pair
#include <sys/types.h> #include <sys/types.h>
@ -149,8 +150,70 @@ int tr_address_compare(tr_address const* a, tr_address const* b)
* TCP sockets * 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) 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 (type == TR_AF_INET)
{ {
#if defined(IP_TOS) && !defined(_WIN32) #if defined(IP_TOS) && !defined(_WIN32)

View File

@ -9,6 +9,8 @@
#endif #endif
#include <cstddef> // size_t #include <cstddef> // size_t
#include <optional>
#include <string>
#include <string_view> #include <string_view>
#ifdef _WIN32 #ifdef _WIN32
@ -104,23 +106,12 @@ constexpr bool tr_address_is_valid(tr_address const* a)
* Sockets * 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; struct tr_session;
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppressMsgs); 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); 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_netSetCongestionControl(tr_socket_t s, char const* algorithm);
void tr_netClose(tr_session* session, tr_socket_t s); 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); 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. * @brief get a human-representable string representing the network error.
* @param err an errno on Unix/Linux and an WSAError on win32) * @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) 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()); 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); io->event_write = event_new(session->event_base, io->socket.handle.tcp, EV_WRITE, event_write_cb, io);
event_enable(io, pendingEvents); 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()); maybeSetCongestionAlgorithm(io->socket.handle.tcp, session->peerCongestionAlgorithm());
return 0; 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 #ifdef TR_LIGHTWEIGHT
#define TR_DEFAULT_ENCRYPTION TR_CLEAR_PREFERRED #define TR_DEFAULT_ENCRYPTION TR_CLEAR_PREFERRED
#else #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_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_low, s->randomPortLow);
tr_variantDictAddInt(d, TR_KEY_peer_port_random_high, s->randomPortHigh); 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_variantDictAddStr(d, TR_KEY_peer_congestion_algorithm, s->peerCongestionAlgorithm());
tr_variantDictAddBool(d, TR_KEY_pex_enabled, s->isPexEnabled); tr_variantDictAddBool(d, TR_KEY_pex_enabled, s->isPexEnabled);
tr_variantDictAddBool(d, TR_KEY_port_forwarding_enabled, tr_sessionIsPortForwardingEnabled(s)); 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)); 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; sv = ""sv;

View File

@ -231,14 +231,9 @@ public:
peer_congestion_algorithm_ = algorithm; peer_congestion_algorithm_ = algorithm;
} }
int peerSocketTos() const void setSocketTOS(tr_socket_t sock, tr_address_type type)
{ {
return peer_socket_tos_; tr_netSetTOS(sock, peer_socket_tos_, type);
}
void setPeerSocketTos(int tos)
{
peer_socket_tos_ = tos;
} }
public: public:
@ -358,6 +353,11 @@ public:
std::unique_ptr<tr_rpc_server> rpc_server_; 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: private:
static std::recursive_mutex session_mutex_; static std::recursive_mutex session_mutex_;
@ -367,8 +367,6 @@ private:
std::string incomplete_dir_; std::string incomplete_dir_;
std::string peer_congestion_algorithm_; std::string peer_congestion_algorithm_;
int peer_socket_tos_ = 0;
std::array<bool, TR_SCRIPT_N_TYPES> scripts_enabled_; std::array<bool, TR_SCRIPT_N_TYPES> scripts_enabled_;
bool blocklist_enabled_ = false; bool blocklist_enabled_ = false;
bool incomplete_dir_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) void tr_udpSetSocketTOS(tr_session* session)
{ {
auto const tos = session->peerSocketTos(); session->setSocketTOS(session->udp_socket, TR_AF_INET);
session->setSocketTOS(session->udp6_socket, TR_AF_INET6);
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);
}
} }
/* BEP-32 has a rather nice explanation of why we need to bind to one /* 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_PORT 9091
#define TR_DEFAULT_RPC_URL_STR "/transmission/" #define TR_DEFAULT_RPC_URL_STR "/transmission/"
#define TR_DEFAULT_PEER_PORT_STR "51413" #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_GLOBAL_STR "200"
#define TR_DEFAULT_PEER_LIMIT_TORRENT_STR "50" #define TR_DEFAULT_PEER_LIMIT_TORRENT_STR "50"
#define TR_DEFAULT_PEER_LIMIT_TORRENT 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 }, { 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_ENABLED, TR_KEY_script_torrent_done_enabled, QVariant::Bool },
{ SCRIPT_TORRENT_DONE_FILENAME, TR_KEY_script_torrent_done_filename, QVariant::String }, { 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 }, { START, TR_KEY_start_added_torrents, QVariant::Bool },
{ TRASH_ORIGINAL, TR_KEY_trash_original_torrent_files, QVariant::Bool }, { TRASH_ORIGINAL, TR_KEY_trash_original_torrent_files, QVariant::Bool },
{ PEX_ENABLED, TR_KEY_pex_enabled, QVariant::Bool }, { PEX_ENABLED, TR_KEY_pex_enabled, QVariant::Bool },