Print this page
11554 Want TCP_CONGESTION socket option
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>

@@ -19,11 +19,11 @@
  * CDDL HEADER END
  */
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2016 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  * Copyright (c) 2016 by Delphix. All rights reserved.
  */
 
 #include <sys/types.h>
 #include <sys/stream.h>

@@ -32,10 +32,11 @@
 #include <sys/socket.h>
 #include <sys/xti_xtiopt.h>
 #include <sys/xti_inet.h>
 #include <sys/policy.h>
 
+#include <inet/cc.h>
 #include <inet/common.h>
 #include <netinet/ip6.h>
 #include <inet/ip.h>
 
 #include <netinet/in.h>

@@ -139,10 +140,13 @@
 
 { TCP_RTO_MAX, IPPROTO_TCP, OA_RW, OA_RW, OP_NP, 0, sizeof (uint32_t), 0 },
 
 { TCP_LINGER2, IPPROTO_TCP, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
 
+{ TCP_CONGESTION, IPPROTO_TCP, OA_RW, OA_RW, OP_NP,
+        OP_VARLEN, CC_ALGO_NAME_MAX, 0 },
+
 { IP_OPTIONS,   IPPROTO_IP, OA_RW, OA_RW, OP_NP,
         (OP_VARLEN|OP_NODEFAULT),
         IP_MAX_OPT_LENGTH + IP_ADDR_LEN, -1 /* not initialized */ },
 { T_IP_OPTIONS, IPPROTO_IP, OA_RW, OA_RW, OP_NP,
         (OP_VARLEN|OP_NODEFAULT),

@@ -431,10 +435,17 @@
                         *i1 = tcp->tcp_ka_rinterval / 1000;
                         return (sizeof (int));
                 case TCP_KEEPALIVE_ABORT_THRESHOLD:
                         *i1 = tcp->tcp_ka_abort_thres;
                         return (sizeof (int));
+                case TCP_CONGESTION: {
+                        size_t len = strlcpy((char *)ptr, CC_ALGO(tcp)->name,
+                            CC_ALGO_NAME_MAX);
+                        if (len >= CC_ALGO_NAME_MAX)
+                                return (-1);
+                        return (len + 1);
+                }
                 case TCP_CORK:
                         *i1 = tcp->tcp_cork;
                         return (sizeof (int));
                 case TCP_RTO_INITIAL:
                         *i1 = tcp->tcp_rto_initial;

@@ -852,10 +863,45 @@
                                 tcp->tcp_ka_abort_thres = *i1;
                                 tcp->tcp_ka_cnt = 0;
                                 tcp->tcp_ka_rinterval = 0;
                         }
                         break;
+                case TCP_CONGESTION: {
+                        struct cc_algo *algo;
+
+                        if (checkonly) {
+                                break;
+                        }
+
+                        /*
+                         * Make sure the string is NUL-terminated. Some
+                         * consumers pass only the number of characters
+                         * in the string, and don't include the NUL
+                         * terminator, so we set it for them.
+                         */
+                        if (inlen < CC_ALGO_NAME_MAX) {
+                                invalp[inlen] = '\0';
+                        }
+                        invalp[CC_ALGO_NAME_MAX - 1] = '\0';
+
+                        if ((algo = cc_load_algo((char *)invalp)) == NULL) {
+                                return (ENOENT);
+                        }
+
+                        if (CC_ALGO(tcp)->cb_destroy != NULL) {
+                                CC_ALGO(tcp)->cb_destroy(&tcp->tcp_ccv);
+                        }
+
+                        CC_DATA(tcp) = NULL;
+                        CC_ALGO(tcp) = algo;
+
+                        if (CC_ALGO(tcp)->cb_init != NULL) {
+                                VERIFY0(CC_ALGO(tcp)->cb_init(&tcp->tcp_ccv));
+                        }
+
+                        break;
+                }
                 case TCP_CORK:
                         if (!checkonly) {
                                 /*
                                  * if tcp->tcp_cork was set and is now
                                  * being unset, we have to make sure that