From 3c20ae08b959188d67b353bc10a18fc6151c9ed5 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Sun, 12 May 2024 08:31:15 +0300 Subject: [PATCH] curl: support IP Type of Service / Traffic Class: --ip-tos Add --ip-tos option to the command line tool for setting TOS for IPv4 or Traffic Class for IPv6. Closes #13606 --- .github/scripts/spellcheck.words | 6 +++ docs/cmdline-opts/Makefile.inc | 1 + docs/cmdline-opts/ip-tos.md | 54 +++++++++++++++++++++++++ docs/options-in-versions | 1 + src/tool_cfgable.h | 1 + src/tool_getparam.c | 58 +++++++++++++++++++++++++++ src/tool_listhelp.c | 3 ++ src/tool_operate.c | 68 ++++++++++++++++++++++++++++++++ 8 files changed, 192 insertions(+) create mode 100644 docs/cmdline-opts/ip-tos.md diff --git a/.github/scripts/spellcheck.words b/.github/scripts/spellcheck.words index 87b010ad7b..333b128fcb 100644 --- a/.github/scripts/spellcheck.words +++ b/.github/scripts/spellcheck.words @@ -214,6 +214,8 @@ ECHConfigList ecl ECONNREFUSED eCOS +ECT +EF EFnet EGD EHLO @@ -457,6 +459,8 @@ LOGDIR logfile lookups loopback +LOWCOST +LOWDELAY LPRT LSB lseek @@ -488,6 +492,7 @@ Micrium MicroBlaze MicroOS middlebox +MINCOST mingw MinGW MINIX @@ -837,6 +842,7 @@ toolchain toolchains toolset toplevel +TOS TPF TrackMemory transcode diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index deb4c7c326..2a8ce0c9fd 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -136,6 +136,7 @@ DPAGES = \ include.md \ insecure.md \ interface.md \ + ip-tos.md \ ipfs-gateway.md \ ipv4.md \ ipv6.md \ diff --git a/docs/cmdline-opts/ip-tos.md b/docs/cmdline-opts/ip-tos.md new file mode 100644 index 0000000000..3fd3c4ad1f --- /dev/null +++ b/docs/cmdline-opts/ip-tos.md @@ -0,0 +1,54 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: ip-tos +Arg: +Help: Set IP Type of Service or Traffic Class +Added: 8.9.0 +Category: connection +Protocols: All +Multi: single +See-also: + - tcp-nodelay +Example: + - --ip-tos CS5 $URL +--- + +# `--ip-tos` + +Set Type of Service (TOS) for IPv4 or Traffic Class for IPv6. (Added in 8.9.0). + +The values allowed for \ can be a numeric value between 1 and 255 +or one of the following: + +* CS0 +* CS1 +* CS2 +* CS3 +* CS4 +* CS5 +* CS6 +* CS7 +* AF11 +* AF12 +* AF13 +* AF21 +* AF22 +* AF23 +* AF31 +* AF32 +* AF33 +* AF41 +* AF42 +* AF43 +* EF +* VOICE-ADMIT +* ECT1 +* ECT0 +* CE +* LE +* LOWCOST +* LOWDELAY +* THROUGHPUT +* RELIABILITY +* MINCOST diff --git a/docs/options-in-versions b/docs/options-in-versions index d3513ff119..e1375da5c3 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -98,6 +98,7 @@ --http3 7.66.0 --http3-only 7.88.0 --ignore-content-length 7.14.1 +--ip-tos 8.9.0 --ipfs-gateway 8.4.0 --include (-i) 4.8 --insecure (-k) 7.10 diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 5d99d605f3..27f15055b0 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -85,6 +85,7 @@ struct OperationConfig { char *range; long low_speed_limit; long low_speed_time; + long ip_tos; /* IP Type of Service */ char *dns_servers; /* dot notation: 1.1.1.1;2.2.2.2 */ char *dns_interface; /* interface name */ char *dns_ipv4_addr; /* dot notation */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index e3eb12fa0b..d524f092eb 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -328,6 +328,7 @@ typedef enum { C_TRACE_CONFIG, C_TRACE_IDS, C_TRACE_TIME, + C_IP_TOS, C_UNIX_SOCKET, C_UPLOAD_FILE, C_URL, @@ -455,6 +456,7 @@ static const struct LongShort aliases[]= { {"include", ARG_BOOL, 'i', C_INCLUDE}, {"insecure", ARG_BOOL, 'k', C_INSECURE}, {"interface", ARG_STRG, ' ', C_INTERFACE}, + {"ip-tos", ARG_STRG, ' ', C_IP_TOS}, {"ipfs-gateway", ARG_STRG, ' ', C_IPFS_GATEWAY}, {"ipv4", ARG_NONE, '4', C_IPV4}, {"ipv6", ARG_NONE, '6', C_IPV6}, @@ -1027,6 +1029,52 @@ static const struct LongShort *single(char letter) return singles[letter - ' ']; } +struct TOSEntry { + const char *name; + unsigned char value; +}; + +static const struct TOSEntry tos_entries[] = { + {"AF11", 0x28}, + {"AF12", 0x30}, + {"AF13", 0x38}, + {"AF21", 0x48}, + {"AF22", 0x50}, + {"AF23", 0x58}, + {"AF31", 0x68}, + {"AF32", 0x70}, + {"AF33", 0x78}, + {"AF41", 0x88}, + {"AF42", 0x90}, + {"AF43", 0x98}, + {"CE", 0x03}, + {"CS0", 0x00}, + {"CS1", 0x20}, + {"CS2", 0x40}, + {"CS3", 0x60}, + {"CS4", 0x80}, + {"CS5", 0xa0}, + {"CS6", 0xc0}, + {"CS7", 0xe0}, + {"ECT0", 0x02}, + {"ECT1", 0x01}, + {"EF", 0xb8}, + {"LE", 0x04}, + {"LOWCOST", 0x02}, + {"LOWDELAY", 0x10}, + {"MINCOST", 0x02}, + {"RELIABILITY", 0x04}, + {"THROUGHPUT", 0x08}, + {"VOICE-ADMIT", 0xb0} +}; + +static int find_tos(const void *a, const void *b) +{ + const struct TOSEntry *aa = a; + const struct TOSEntry *bb = b; + return strcmp(aa->name, bb->name); +} + #define MAX_QUERY_LEN 100000 /* larger is not likely to ever work */ static ParameterError url_query(char *nextarg, struct GlobalConfig *global, @@ -1630,6 +1678,16 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case C_TCP_NODELAY: /* --tcp-nodelay */ config->tcp_nodelay = toggle; break; + case C_IP_TOS: { /* --ip-tos */ + const struct TOSEntry *entry = bsearch( + &nextarg, tos_entries, sizeof(tos_entries)/sizeof(*tos_entries), + sizeof(*tos_entries), find_tos); + if(entry) + config->ip_tos = entry->value; + else /* numeric tos value */ + err = str2unummax(&config->ip_tos, nextarg, 0xFF); + break; + } case C_PROXY_DIGEST: /* --proxy-digest */ config->proxydigest = toggle; break; diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index df05de31c2..250a0c28b3 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -306,6 +306,9 @@ const struct helptxt helptext[] = { {" --interface ", "Use network interface", CURLHELP_CONNECTION}, + {" --ip-tos ", + "Set IP Type of Service or Traffic Class", + CURLHELP_CONNECTION}, {" --ipfs-gateway ", "Gateway for IPFS", CURLHELP_IPFS}, diff --git a/src/tool_operate.c b/src/tool_operate.c index b5e7d4ccff..471f783351 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -45,6 +45,10 @@ # include #endif +#ifdef HAVE_NETINET_IN_H +# include +#endif + #define ENABLE_CURLX_PRINTF /* use our own printf() functions */ #include "curlx.h" @@ -95,6 +99,10 @@ # define O_BINARY 0 #endif +#ifndef SOL_IP +# define SOL_IP IPPROTO_IP +#endif + #define CURL_CA_CERT_ERRORMSG \ "More details here: https://curl.se/docs/sslcerts.html\n\n" \ "curl failed to verify the legitimacy of the server and therefore " \ @@ -142,6 +150,55 @@ static bool is_pkcs11_uri(const char *string) } } +#ifdef IP_TOS +static int get_address_family(curl_socket_t sockfd) +{ + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + if(getsockname(sockfd, (struct sockaddr *)&addr, &addrlen) == 0) + return addr.ss_family; + return AF_UNSPEC; +} +#endif + +#if defined(IP_TOS) || defined(IPV6_TCLASS) +static int sockopt_callback(void *clientp, curl_socket_t curlfd, + curlsocktype purpose) +{ + struct OperationConfig *config = (struct OperationConfig *)clientp; + if(purpose != CURLSOCKTYPE_IPCXN) + return CURL_SOCKOPT_OK; + (void)config; + (void)curlfd; + if(config->ip_tos > 0) { + int tos = (int)config->ip_tos; + int result = 0; + switch(get_address_family(curlfd)) { + case AF_INET: +#ifdef IP_TOS + result = setsockopt(curlfd, SOL_IP, IP_TOS, + (const char *)&tos, sizeof(tos)); +#endif + break; + case AF_INET6: +#ifdef IPV6_TCLASS + result = setsockopt(curlfd, IPPROTO_IPV6, IPV6_TCLASS, + (const char *)&tos, sizeof(tos)); +#endif + break; + } + if(result < 0) { + int error = errno; + warnf(config->global, + "Setting type of service to %d failed with errno %d: %s;\n", + tos, error, strerror(error)); + } + } + return CURL_SOCKOPT_OK; +} +#endif + + #ifdef __VMS /* * get_vms_file_size does what it takes to get the real size of the file @@ -2189,6 +2246,17 @@ static CURLcode single_transfer(struct GlobalConfig *global, my_setopt_str(curl, CURLOPT_ECH, config->ech_config); #endif + /* new in 8.9.0 */ + if(config->ip_tos > 0) { +#if defined(IP_TOS) || defined(IPV6_TCLASS) + my_setopt(curl, CURLOPT_SOCKOPTFUNCTION, sockopt_callback); + my_setopt(curl, CURLOPT_SOCKOPTDATA, config); +#else + warnf(config->global, + "Type of service is not supported in this build."); +#endif + } + /* initialize retry vars for loop below */ per->retry_sleep_default = (config->retry_delay) ? config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */ -- 2.47.3