isostream: add a -C option to set TCP congestion control.

This will let us safely test different TCP congestion algorithms
without changing the system-wide one.

Change-Id: Ic56dee2238268f8793abe63633cb34a600563ecb
diff --git a/cmds/isostream.c b/cmds/isostream.c
index 8225e11..9407319 100644
--- a/cmds/isostream.c
+++ b/cmds/isostream.c
@@ -107,6 +107,7 @@
           "\n"
           "Server specific:\n"
           "      -P <number>     limit to this many parallel connections\n"
+          "      -C <algo>       override TCP congestion control algorithm\n"
           "Client specific:\n"
           "      -b <Mbits/sec>  Mbits per second\n"
           "      -I <interface>  set source interface to specified interface\n"
@@ -148,6 +149,24 @@
 }
 
 
+int set_cong_ctl(int sock, const char *cong_ctl) {
+#ifdef TCP_CONGESTION
+  if (setsockopt(sock, IPPROTO_TCP, TCP_CONGESTION,
+                 cong_ctl, strlen(cong_ctl)) != 0) {
+    char buf[128];
+    int e = errno;
+    snprintf(buf, sizeof(buf), "tcp_congestion('%s')", cong_ctl);
+    errno = e;
+    perror(buf);
+    return -1;
+  } else {
+    fprintf(stderr, "tcp_congestion set to '%s'.\n", cong_ctl);
+  }
+#endif
+  return 0;
+}
+
+
 static int do_select(int sock, long long usec_timeout) {
   fd_set rfds;
   FD_ZERO(&rfds);
@@ -499,10 +518,11 @@
   double sufficient = 0;
   int timeout = 0;
   int max_children = MAX_CHILDREN;
+  const char *cong_ctl = NULL;
 
   int c;
   char *ifr_name = NULL;
-  while ((c = getopt(argc, argv, "b:I:P:s:t:h?")) >= 0) {
+  while ((c = getopt(argc, argv, "b:I:P:C:s:t:h?")) >= 0) {
     switch (c) {
     case 'b':
       megabits_per_sec = atoi(optarg);
@@ -523,6 +543,14 @@
         return 99;
       }
       break;
+    case 'C':
+      cong_ctl = optarg;
+#ifndef TCP_CONGESTION
+      fprintf(stderr, "%s: no support for congestion control overrides.\n",
+              argv[0]);
+      return 99;
+#endif
+      break;
     case 's':
       sufficient = atof(optarg);
       if (sufficient < 1) {
@@ -582,6 +610,9 @@
       perror("getsockname");
       return 1;
     }
+    if (cong_ctl && set_cong_ctl(sock, cong_ctl) != 0) {
+      return 1;
+    }
     if (listen(sock, 1)) {
       perror("listen");
       return 1;
@@ -612,6 +643,9 @@
           perror("accept");
           continue;
         }
+        if (cong_ctl && set_cong_ctl(conn, cong_ctl) != 0) {
+          return 1;
+        }
         pid_t pid = fork();
         if (pid < 0) {
           perror("fork");
@@ -635,6 +669,11 @@
     }
   } else if (argc - optind == 1) {
     fprintf(stderr, "client mode.\n");
+    if (cong_ctl) {
+      fprintf(stderr, "%s: can't set congestion control in client mode.\n",
+              argv[0]);
+      usage_and_die(argv[0]);
+    }
 
     if (!megabits_per_sec) {
       fprintf(stderr, "%s: must specify -b in client mode\n", argv[0]);