Limit the maximum packets per second the server will send to a client.

Change-Id: I3cef24d70ae887301b1e8febd787c7bd7ec3bce1
diff --git a/cmds/isoping.cc b/cmds/isoping.cc
index d7b9d1c..18d73f4 100644
--- a/cmds/isoping.cc
+++ b/cmds/isoping.cc
@@ -307,6 +307,7 @@
           "\n"
           "      -f <lines/sec>  max output lines per second\n"
           "      -r <pps>        packets per second (default=%g)\n"
+          "                      in server mode: the highest accepted rate.\n"
           "      -t <ttl>        packet ttl to use (default=2 for safety)\n"
           "      -q              quiet mode (don't print packets)\n"
           "      -T              print timestamps\n",
@@ -314,6 +315,10 @@
   exit(99);
 }
 
+void set_packets_per_sec(double new_pps) {
+  DLOG("Setting packets_per_sec to %f\n", new_pps);
+  packets_per_sec = new_pps;
+}
 
 bool CompareSockaddr::operator()(const struct sockaddr_storage &lhs,
                                  const struct sockaddr_storage &rhs) {
@@ -425,8 +430,8 @@
   memset(tx, 0, sizeof(*tx));
   tx->magic = htonl(MAGIC);
   tx->id = rx->id;
-  // TODO(pmccurdy): Establish limits on the allowed usec_per_pkt values here
-  tx->usec_per_pkt = rx->usec_per_pkt;
+  tx->usec_per_pkt = htonl(
+      std::max(ntohl(rx->usec_per_pkt), (uint32_t)(1e6 / packets_per_sec)));
   tx->txtime = now;
   tx->clockdiff = htonl(now - ntohl(rx->txtime));
   tx->num_lost = htonl(0);
@@ -646,6 +651,7 @@
     }
     fprintf(stderr, "New client connection: %s\n",
             sockaddr_to_str((struct sockaddr *)remoteaddr));
+    // Use the usec_per_pkt value provided by the server.
     SessionMap::iterator it = s->NewSession(
         now + 10 * 1000, ntohl(rx->usec_per_pkt), remoteaddr, remoteaddr_len);
     Session &session = it->second;
@@ -909,7 +915,7 @@
       }
       break;
     case 'r':
-      packets_per_sec = atof(optarg);
+      set_packets_per_sec(atof(optarg));
       if (packets_per_sec < 0.001 || packets_per_sec > 1e6) {
         fprintf(stderr, "%s: packets per sec (-r) must be 0.001..1000000\n",
                 argv[0]);
diff --git a/cmds/isoping.h b/cmds/isoping.h
index 9c2ee85..33a4438 100644
--- a/cmds/isoping.h
+++ b/cmds/isoping.h
@@ -227,6 +227,9 @@
 // currently readable.
 int read_incoming_packet(Sessions *s, int sock, uint32_t now, int is_server);
 
+// Sets the global packets_per_sec value.  Used for test purposes only.
+void set_packets_per_sec(double new_pps);
+
 // Parses arguments and runs the main loop.  Distinct from main() for unit test
 // purposes.
 int isoping_main(int argc, char **argv);
diff --git a/cmds/isoping_test.cc b/cmds/isoping_test.cc
index ca0c440..9ab5cd9 100644
--- a/cmds/isoping_test.cc
+++ b/cmds/isoping_test.cc
@@ -616,8 +616,10 @@
   WVPASSEQ(read_incoming_packet(&s, ssock, sbase + t, is_server), EINVAL);
 
   // Make a new client, who sends more frequently, getting a new source port.
+  // Also establish an upper limit, to verify that the server enforces it.
   Sessions c2;
-  c2.NewSession(cbase, usec_per_pkt/4, &listenaddr, listenaddr_len);
+  set_packets_per_sec(4e6/usec_per_pkt);
+  c2.NewSession(cbase, usec_per_pkt/10, &listenaddr, listenaddr_len);
   int c2sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
   if (!WVPASS(c2sock > 0)) {
     perror("client socket 2");
@@ -648,6 +650,8 @@
   t += sc_latency;
   WVPASS(!read_incoming_packet(&c2, c2sock, cbase+t, is_client));
 
+  WVPASSEQ(c2Session.usec_per_pkt, usec_per_pkt/4);
+
   // Now we can send a validated packet to the server.
   t = c2Session.next_send - cbase;
   WVPASS(!send_waiting_packets(&c2, c2sock, cbase + t, is_client));