Hi,

The following patch for netcat, adds the capability to limit the data transfer bandwidth with the flag '-B'.

For instance, the following command limits ssh traffic to 40KB/s:

  ssh -oProxyCommand='./nc -B 40 %h %p' localhost cat /bsd >/dev/null



? nc
Index: nc.1
===================================================================
RCS file: /cvs/src/usr.bin/nc/nc.1,v
retrieving revision 1.82
diff -u -p -r1.82 nc.1
--- nc.1    9 Feb 2017 20:15:59 -0000    1.82
+++ nc.1    26 May 2017 12:55:27 -0000
@@ -34,6 +34,7 @@
 .Sh SYNOPSIS
 .Nm nc
 .Op Fl 46cDdFhklNnrStUuvz
+.Op Fl B Ar bandwidth
 .Op Fl C Ar certfile
 .Op Fl e Ar name
 .Op Fl H Ar hash
@@ -101,6 +102,9 @@ to use IPv4 addresses only.
 Forces
 .Nm
 to use IPv6 addresses only.
+.It Fl B Ar bandwidth
+Throttles the data transfer so that bandwith usage is limited to
+the amount given in KB per second.
 .It Fl C Ar certfile
 Specifies the filename from which the public key part of the TLS
 certificate is loaded, in PEM format.
Index: netcat.c
===================================================================
RCS file: /cvs/src/usr.bin/nc/netcat.c,v
retrieving revision 1.178
diff -u -p -r1.178 netcat.c
--- netcat.c    9 Mar 2017 13:58:00 -0000    1.178
+++ netcat.c    26 May 2017 12:55:28 -0000
@@ -60,9 +60,9 @@
 #define PORT_MAX    65535
 #define UNIX_DG_TMP_SOCKET_SIZE    19

-#define POLL_STDIN 0
-#define POLL_NETOUT 1
-#define POLL_NETIN 2
+#define POLL_NETOUT 0
+#define POLL_NETIN 1
+#define POLL_STDIN 2
 #define POLL_STDOUT 3
 #define BUFSIZE 16384
 #define DEFAULT_CA_FILE "/etc/ssl/cert.pem"
@@ -96,6 +96,7 @@ int    Oflag;                    /* TCP send buffer size *
 int    Sflag;                    /* TCP MD5 signature option */
 int    Tflag = -1;                /* IP Type of Service */
 int    rtableid = -1;
+long    Bflag;                    /* max bandwidth */

 int    usetls;                    /* use TLS */
 char    *Cflag;                    /* Public cert file */
@@ -113,6 +114,8 @@ char *portlist[PORT_MAX+1];
 char *unix_dg_tmp_socket;
 int ttl = -1;
 int minttl = -1;
+double tokens = 0.0;
+time_t tokens_ts = 0;

 void    atelnet(int, unsigned char *, unsigned int);
 void    build_ports(char *);
@@ -165,7 +168,7 @@ main(int argc, char *argv[])
     signal(SIGPIPE, SIG_IGN);

     while ((ch = getopt(argc, argv,
- "46C:cDde:FH:hI:i:K:klM:m:NnO:o:P:p:R:rSs:T:tUuV:vw:X:x:z")) != -1) {
+ "46B:C:cDde:FH:hI:i:K:klM:m:NnO:o:P:p:R:rSs:T:tUuV:vw:X:x:z")) != -1) {
         switch (ch) {
         case '4':
             family = AF_INET;
@@ -186,6 +189,11 @@ main(int argc, char *argv[])
             else
                 errx(1, "unsupported proxy protocol");
             break;
+                case 'B':
+                        Bflag = strtonum(optarg, 0, LONG_MAX, &errstr);
+                        if (errstr)
+ errx(1, "bandwidth %s: %s", errstr, optarg);
+                        break;
         case 'C':
             Cflag = optarg;
             break;
@@ -1049,22 +1057,38 @@ readwrite(int net_fd, struct tls *tls_ct
             stdinbufpos == 0 && netinbufpos == 0)
             return;

-        /* help says -i is for "wait between lines sent". We read and
-         * write arbitrary amounts of data, and we don't want to start
-         * scanning for newlines, so this is as good as it gets */
-        if (iflag)
-            sleep(iflag);
-
-        /* poll */
-        num_fds = poll(pfd, 4, timeout);
-
-        /* treat poll errors */
-        if (num_fds == -1)
-            err(1, "polling error");
-
-        /* timeout happened */
-        if (num_fds == 0)
-            return;
+                if (Bflag) {
+                        time_t now = time(NULL);
+                        tokens += 1024.0 * Bflag * (now - tokens_ts);
+ if (tokens > 4 * 1024.0 * Bflag) /* burst for 4s or data are allowed */
+                                tokens = 4 * 1024.0 * Bflag;
+                        tokens_ts = now;
+                }
+                if (Bflag && tokens < 0) {
+ /* this depends on POLL_NETIN being equal to POLL_NETOUT+1 */
+                        num_fds = poll(pfd + POLL_NETOUT, 2, 250);
+
+                        pfd[POLL_STDIN].revents = 0;
+                        pfd[POLL_STDOUT].revents = 0;
+                }
+                else {
+ /* help says -i is for "wait between lines sent". We read and + * write arbitrary amounts of data, and we don't want to start + * scanning for newlines, so this is as good as it gets */
+                        if (iflag)
+                                sleep(iflag);
+
+                        /* poll */
+                        num_fds = poll(pfd, 4, timeout);
+
+                        /* timeout happened */
+                        if (num_fds == 0)
+                                return;
+                }
+
+                /* treat poll errors */
+                if (num_fds == -1)
+                        err(1, "polling error");

         /* treat socket error conditions */
         for (n = 0; n < 4; n++) {
@@ -1111,6 +1135,8 @@ readwrite(int net_fd, struct tls *tls_ct
                 pfd[POLL_STDIN].events = POLLOUT;
             else if (ret == 0 || ret == -1)
                 pfd[POLL_STDIN].fd = -1;
+                        else if (Bflag)
+                                tokens -= ret;
             /* read something - poll net out */
             if (stdinbufpos > 0)
                 pfd[POLL_NETOUT].events = POLLOUT;
@@ -1171,6 +1197,8 @@ readwrite(int net_fd, struct tls *tls_ct
                 pfd[POLL_STDOUT].events = POLLOUT;
             else if (ret == -1)
                 pfd[POLL_STDOUT].fd = -1;
+                        else if (Bflag)
+                                tokens -= ret;
             /* buffer empty - remove self from polling */
             if (netinbufpos == 0)
                 pfd[POLL_STDOUT].events = 0;
@@ -1644,6 +1672,7 @@ help(void)
     fprintf(stderr, "\tCommand Summary:\n\
     \t-4        Use IPv4\n\
     \t-6        Use IPv6\n\
+    \t-B bandwidth    Limit network bandwidth usage (KB/s)\n\
     \t-C certfile    Public key file\n\
     \t-c        Use TLS\n\
     \t-D        Enable the debug socket option\n\
@@ -1687,8 +1716,8 @@ void
 usage(int ret)
 {
     fprintf(stderr,
-        "usage: nc [-46cDdFhklNnrStUuvz] [-C certfile] [-e name] "
-        "[-H hash] [-I length]\n"
+        "usage: nc [-46cDdFhklNnrStUuvz] [-B bandwidth] "
+            "[-C certfile] [-e name] [-H hash] [-I length]\n"
"\t [-i interval] [-K keyfile] [-M ttl] [-m minttl] [-O length]\n"
         "\t  [-o staplefile] [-P proxy_username] [-p source_port] "
         "[-R CAfile]\n"




Reply via email to