]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
curl: support IP Type of Service / Traffic Class: --ip-tos
authorOrgad Shaneh <orgad.shaneh@audiocodes.com>
Sun, 12 May 2024 05:31:15 +0000 (08:31 +0300)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 7 Jun 2024 08:48:40 +0000 (10:48 +0200)
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
docs/cmdline-opts/Makefile.inc
docs/cmdline-opts/ip-tos.md [new file with mode: 0644]
docs/options-in-versions
src/tool_cfgable.h
src/tool_getparam.c
src/tool_listhelp.c
src/tool_operate.c

index 87b010ad7beb0fc8a8f94352febebbcfc22c22cf..333b128fcba15cfad629476c06ccc37b1f32727c 100644 (file)
@@ -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
index deb4c7c326dc1c121d1f4bd8374b2611db00d831..2a8ce0c9fdc45c80c61fc6764015fa0264dd5143 100644 (file)
@@ -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 (file)
index 0000000..3fd3c4a
--- /dev/null
@@ -0,0 +1,54 @@
+---
+c: Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+SPDX-License-Identifier: curl
+Long: ip-tos
+Arg: <string>
+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 \<string\> 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
index d3513ff119d7f6497e597306c22a4d2773c8f237..e1375da5c3f1c44642a40c5e7b918fe61b172690 100644 (file)
@@ -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
index 5d99d605f3c7c336c4642daf0011349ea63bf949..27f15055b0a057eed7cd51809ea0e0c4112e8244 100644 (file)
@@ -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 */
index e3eb12fa0b0b8b809d28fb9ea7ec9ce892455a42..d524f092ebbdb5dd0ef25bac5b455f038473e35f 100644 (file)
@@ -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;
index df05de31c257dacd966b44718a5664733b3d840f..250a0c28b380542440f75abfb43bbc9d72adbf0f 100644 (file)
@@ -306,6 +306,9 @@ const struct helptxt helptext[] = {
   {"    --interface <name>",
    "Use network interface",
    CURLHELP_CONNECTION},
+  {"    --ip-tos <string>",
+   "Set IP Type of Service or Traffic Class",
+   CURLHELP_CONNECTION},
   {"    --ipfs-gateway <URL>",
    "Gateway for IPFS",
    CURLHELP_IPFS},
index b5e7d4ccff43a2cfce5d5e12d455431827885817..471f7833517b5e97329bb0c4d426210a1d6324bf 100644 (file)
 #  include <proto/dos.h>
 #endif
 
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif
+
 #define ENABLE_CURLX_PRINTF
 /* use our own printf() functions */
 #include "curlx.h"
 #  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 */