diff --git a/NmapOps.cc b/NmapOps.cc
index fd7a68013..ff3139c83 100644
--- a/NmapOps.cc
+++ b/NmapOps.cc
@@ -195,6 +195,7 @@ void NmapOps::Initialize() {
#endif
debugging = 0;
verbose = 0;
+ min_packet_send_rate = 0.0; /* Unset. */
randomize_hosts = 0;
sendpref = PACKET_SEND_NOPREF;
spoofsource = 0;
diff --git a/NmapOps.h b/NmapOps.h
index 5d3f8db94..f9b0590a7 100644
--- a/NmapOps.h
+++ b/NmapOps.h
@@ -178,6 +178,8 @@ class NmapOps {
bool openOnly() { return open_only; }
void setOpenOnly(bool oo) { open_only = oo; }
int verbose;
+ /* The requested minimum packet sending rate, or 0.0 if unset. */
+ float min_packet_send_rate;
int randomize_hosts;
int spoofsource; /* -S used */
int fastscan;
diff --git a/docs/refguide.xml b/docs/refguide.xml
index dbf2751c8..0f639732b 100644
--- a/docs/refguide.xml
+++ b/docs/refguide.xml
@@ -2300,6 +2300,44 @@ threshold based intrusion detection and prevention systems (IDS/IPS).
+
+
+ --min-rate <number>
+ (Specify a minimum scanning rate)
+ --min-rate
+
+
+
+Nmap's dynamic timing does a good job of finding an appropriate
+speed at which to scan. Sometimes, however, you may happen to know an
+appropriate speed for a network, or you may have to guarantee that a
+scan will be finished by a certain time. When the
+--min-rate is given Nmap will do its best to send
+packets as fast or faster than the given rate. The argument is a
+positive real number representing a packet rate in packets per second.
+For example, using --min-rate 300 will ensure that
+the packet sending rate doesn't fall below 300 packets per second.
+Specifying a minimum rate does not keep Nmap from going faster if
+conditions warrant.
+
+There are two conditions when the actual scanning rate may fall
+below the specified minimum. The first is if the minimum is faster than
+the fastest rate at which Nmap can send, which is dependent on hardware.
+In this case Nmap will send packets as fast a possible, but be aware
+that such high rates are likely to cause a loss of accuracy. The second
+case is when Nmap has nothing to send, for example at the end of a scan
+when the last probes have been sent and Nmap is waiting for them to time
+out or be responded to. It's normal to see the scanning rate drop at the
+end of a scan or in between groups of hosts.
+
+The --min-rate option is global, affecting an
+entire scan, not individual hosts. It only affects port and host
+discovery scans. Other features like OS detection implement their own
+timing.
+
+
+
+
--defeat-rst-ratelimit
--defeat-rst-ratelimit
diff --git a/nmap.cc b/nmap.cc
index c130f9807..5d2e66f5f 100644
--- a/nmap.cc
+++ b/nmap.cc
@@ -278,6 +278,7 @@ printf("%s %s ( %s )\n"
" --max-retries : Caps number of port scan probe retransmissions.\n"
" --host-timeout : Give up on target after this long\n"
" --scan-delay/--max-scan-delay : Adjust delay between probes\n"
+ " --min-rate : Send packets no slower than per second\n"
"FIREWALL/IDS EVASION AND SPOOFING:\n"
" -f; --mtu : fragment packets (optionally w/given MTU)\n"
" -D : Cloak a scan with decoys\n"
@@ -634,6 +635,7 @@ int nmap_main(int argc, char *argv[]) {
#endif
{"ip_options", required_argument, 0, 0},
{"ip-options", required_argument, 0, 0},
+ {"min-rate", required_argument, 0, 0},
{0, 0, 0, 0}
};
@@ -888,6 +890,9 @@ int nmap_main(int argc, char *argv[]) {
o.traceroute = true;
} else if(strcmp(long_options[option_index].name, "reason") == 0) {
o.reason = true;
+ } else if(optcmp(long_options[option_index].name, "min-rate") == 0) {
+ if (sscanf(optarg, "%f", &o.min_packet_send_rate) != 1 || o.min_packet_send_rate <= 0.0)
+ fatal("Argument to --min-rate must be a positive floating-point number");
} else {
fatal("Unknown long option (%s) given@#!$#$", long_options[option_index].name);
}
@@ -1483,6 +1488,7 @@ int nmap_main(int argc, char *argv[]) {
log_write(LOG_PLAIN, " max-scan-delay: TCP %d, UDP %d\n", o.maxTCPScanDelay(), o.maxUDPScanDelay());
log_write(LOG_PLAIN, " parallelism: min %d, max %d\n", o.min_parallelism, o.max_parallelism);
log_write(LOG_PLAIN, " max-retries: %d, host-timeout: %ld\n", o.getMaxRetransmissions(), o.host_timeout);
+ log_write(LOG_PLAIN, " min-rate: %g\n", o.min_packet_send_rate);
log_write(LOG_PLAIN, "---------------------------------------------\n");
}
diff --git a/scan_engine.cc b/scan_engine.cc
index 83b8bdd76..6943c315c 100644
--- a/scan_engine.cc
+++ b/scan_engine.cc
@@ -346,8 +346,9 @@ public:
struct sockaddr_storage latestip;
GroupScanStats(UltraScanInfo *UltraSI);
~GroupScanStats();
+ void probeSent();
/* Returns true if the GLOBAL system says that sending is OK. */
- bool sendOK();
+ bool sendOK(struct timeval *when);
/* Total # of probes outstanding (active) for all Hosts */
int num_probes_active;
UltraScanInfo *USI; /* The USI which contains this GSS. Use for at least
@@ -373,6 +374,9 @@ public:
/* Value of numprobes_sent at lastping_sent time -- to ensure that we don't
send too many pings when probes are going slowly. */
int lastping_sent_numprobes;
+ /* When to send the next probe, to keep the minimum up. Used only when a
+ minimum sending rate (o.min_packet_send_rate) is set. */
+ struct timeval send_no_later_than;
/* The host to which global pings are sent. This is kept updated to be the
most recent host that was found up. */
@@ -653,6 +657,7 @@ public:
list completedHosts;
ScanProgressMeter *SPM;
+ RateMeter send_rate_meter;
struct scan_lists *ports;
int rawsd; /* raw socket descriptor */
pcap_t *pd;
@@ -874,6 +879,7 @@ GroupScanStats::GroupScanStats(UltraScanInfo *UltraSI) {
probes_sent = probes_sent_at_last_wait = 0;
probes_replied_to = 0;
lastping_sent = lastrcvd = USI->now;
+ send_no_later_than = USI->now;
lastping_sent_numprobes = 0;
pinghost = NULL;
gettimeofday(&last_wait, NULL);
@@ -884,10 +890,27 @@ GroupScanStats::~GroupScanStats() {
delete CSI;
}
+void GroupScanStats::probeSent() {
+ /* Find the next scheduled send time for minimum-rate scanning. */
+ if (TIMEVAL_SUBTRACT(send_no_later_than, USI->now) > 0) {
+ /* The next scheduled send is in the future. That means we're ahead of
+ schedule, but it also means there's slack time during which the sending
+ rate could drop. Reschedule the send to keep that from happening. */
+ send_no_later_than = USI->now;
+ }
+ TIMEVAL_ADD(send_no_later_than, send_no_later_than,
+ (time_t) (1000000.0 / o.min_packet_send_rate));
+}
+
/* Returns true if the GLOBAL system says that sending is OK.*/
-bool GroupScanStats::sendOK() {
+bool GroupScanStats::sendOK(struct timeval *when) {
int recentsends;
+ /* In case it's not okay to send, arbitrarily say to check back in one
+ second. */
+ if (when)
+ TIMEVAL_MSEC_ADD(*when, USI->now, 1000);
+
if ((USI->scantype == CONNECT_SCAN || USI->ptech.connecttcpscan)
&& CSI->numSDs >= CSI->maxSocketsAllowed)
return false;
@@ -914,13 +937,34 @@ bool GroupScanStats::sendOK() {
if (recentsends >= 50)
return false;
+ /* Enforce a minimum scanning rate, if necessary. If we're ahead of schedule,
+ record the time of the next scheduled send. If we're behind schedule,
+ return true to indicate that we need to send now, regardless of any
+ congestion control. */
+ if (o.min_packet_send_rate != 0.0) {
+ if (TIMEVAL_SUBTRACT(send_no_later_than, USI->now) > 0) {
+ if (when)
+ *when = send_no_later_than;
+ } else {
+ if (when)
+ *when = USI->now;
+ return true;
+ }
+ }
+
/* When there is only one target left, let the host congestion
stuff deal with it. */
- if (USI->numIncompleteHostsLessThan(2))
+ if (USI->numIncompleteHostsLessThan(2)) {
+ if (when)
+ *when = USI->now;
return true;
+ }
- if (timing.cwnd >= num_probes_active + 0.5)
+ if (timing.cwnd >= num_probes_active + 0.5) {
+ if (when)
+ *when = USI->now;
return true;
+ }
return false;
}
@@ -1053,6 +1097,16 @@ bool HostScanStats::sendOK(struct timeval *when) {
struct timeval probe_to, earliest_to, sendTime;
long tdiff;
+ /* If the group stats say we need to send a probe to enforce a minimum
+ scanning rate, then we need to step up and send a probe. */
+ if (o.min_packet_send_rate != 0.0) {
+ if (TIMEVAL_SUBTRACT(USI->gstats->send_no_later_than, USI->now) <= 0) {
+ if (when)
+ *when = USI->now;
+ return true;
+ }
+ }
+
if (target->timedOut(&USI->now) || completed()) {
if (when) *when = USI->now;
return false;
@@ -1293,6 +1347,7 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp
seqmask = get_random_u32();
scantype = scantp;
SPM = new ScanProgressMeter(scantype2str(scantype));
+ send_rate_meter.start(&now);
tcp_scan = udp_scan = prot_scan = ping_scan = noresp_open_scan = false;
ping_scan_arp = false;
memset((char *) &ptech, 0, sizeof(ptech));
@@ -1447,11 +1502,12 @@ bool UltraScanInfo::sendOK(struct timeval *when) {
bool hgood = false;
bool thisHostGood = false;
bool foundgood = false;
- ggood = gstats->sendOK();
+
+ ggood = gstats->sendOK(when);
if (!ggood) {
if (when) {
- TIMEVAL_MSEC_ADD(lowhtime, now, 1000);
+ lowhtime = *when;
// Can't do anything until global is OK - means packet receipt
// or probe timeout.
for(host = incompleteHosts.begin(); host != incompleteHosts.end();
@@ -1482,6 +1538,13 @@ bool UltraScanInfo::sendOK(struct timeval *when) {
assert(foundgood);
}
+ /* Defer to the group stats if they need a shorter delay to enforce a minimum
+ packet sending rate. */
+ if (o.min_packet_send_rate != 0.0) {
+ if (TIMEVAL_MSEC_SUBTRACT(gstats->send_no_later_than, lowhtime) < 0)
+ lowhtime = gstats->send_no_later_than;
+ }
+
if (TIMEVAL_MSEC_SUBTRACT(lowhtime, now) < 0)
lowhtime = now;
@@ -2483,11 +2546,14 @@ static UltraProbe *sendConnectScanProbe(UltraScanInfo *USI, HostScanStats *hss,
else sin6->sin6_port = htons(probe->pspec()->pd.tcp.dport);
#endif
hss->lastprobe_sent = probe->sent = USI->now;
+ USI->gstats->probeSent();
rc = connect(CP->sd, (struct sockaddr *)&sock, socklen);
gettimeofday(&USI->now, NULL);
if (rc == -1) connect_errno = socket_errno();
PacketTrace::traceConnect(IPPROTO_TCP, (sockaddr *) &sock, socklen, rc,
connect_errno, &USI->now);
+ /* We don't record a byte count for connect probes. */
+ USI->send_rate_meter.record(0, &USI->now);
/* This counts as probe being sent, so update structures */
hss->probes_outstanding.push_back(probe);
probeI = hss->probes_outstanding.end();
@@ -2579,11 +2645,13 @@ static UltraProbe *sendArpScanProbe(UltraScanInfo *USI, HostScanStats *hss,
ETH_ADDR_BROADCAST, *hss->target->v4hostip());
gettimeofday(&USI->now, NULL);
hss->lastprobe_sent = probe->sent = USI->now;
+ USI->gstats->probeSent();
if ((rc = eth_send(USI->ethsd, frame, sizeof(frame))) != sizeof(frame)) {
int err = socket_errno();
error("WARNING: eth_send of ARP packet returned %i rather than expected %d (errno=%i: %s)", rc, (int) sizeof(frame), err, strerror(err));
}
PacketTrace::traceArp(PacketTrace::SENT, (u8 *) frame, sizeof(frame), &USI->now);
+ USI->send_rate_meter.record(sizeof(frame), &USI->now);
probe->tryno = tryno;
probe->pingseq = pingseq;
/* First build the probe */
@@ -2656,6 +2724,7 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
probe->setIP(packet, packetlen, pspec);
hss->lastprobe_sent = probe->sent = USI->now;
}
+ USI->gstats->probeSent();
send_ip_packet(USI->rawsd, ethptr, packet, packetlen);
free(packet);
}
@@ -2671,6 +2740,7 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
probe->setIP(packet, packetlen, pspec);
hss->lastprobe_sent = probe->sent = USI->now;
}
+ USI->gstats->probeSent();
send_ip_packet(USI->rawsd, ethptr, packet, packetlen);
free(packet);
}
@@ -2726,6 +2796,7 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
probe->setIP(packet, packetlen, pspec);
hss->lastprobe_sent = probe->sent = USI->now;
}
+ USI->gstats->probeSent();
send_ip_packet(USI->rawsd, ethptr, packet, packetlen);
free(packet);
}
@@ -2748,10 +2819,12 @@ static UltraProbe *sendIPScanProbe(UltraScanInfo *USI, HostScanStats *hss,
probe->setIP(packet, packetlen, pspec);
hss->lastprobe_sent = probe->sent = USI->now;
}
+ USI->gstats->probeSent();
send_ip_packet(USI->rawsd, ethptr, packet, packetlen);
free(packet);
}
} else assert(0); /* TODO: Maybe RPC scan and the like */
+ USI->send_rate_meter.record(packetlen, &USI->now);
/* Now that the probe has been sent, add it to the Queue for this host */
hss->probes_outstanding.push_back(probe);
USI->gstats->num_probes_active++;
@@ -2811,7 +2884,7 @@ static void doAnyNewProbes(UltraScanInfo *USI) {
sending a probe. */
unableToSend = NULL;
hss = USI->nextIncompleteHost();
- while (hss != NULL && hss != unableToSend && USI->gstats->sendOK()) {
+ while (hss != NULL && hss != unableToSend && USI->gstats->sendOK(NULL)) {
if (hss->freshPortsLeft() && hss->sendOK(NULL)) {
sendNextScanProbe(USI, hss);
unableToSend = NULL;
@@ -2834,7 +2907,7 @@ static void doAnyRetryStackRetransmits(UltraScanInfo *USI) {
sending a probe. */
unableToSend = NULL;
hss = USI->nextIncompleteHost();
- while (hss != NULL && hss != unableToSend && USI->gstats->sendOK()) {
+ while (hss != NULL && hss != unableToSend && USI->gstats->sendOK(NULL)) {
if (!hss->retry_stack.empty() && hss->sendOK(NULL)) {
sendNextRetryStackProbe(USI, hss);
unableToSend = NULL;
@@ -2901,7 +2974,7 @@ static void doAnyPings(UltraScanInfo *USI) {
hss->numprobes_sent >= hss->lastping_sent_numprobes + 10 &&
TIMEVAL_SUBTRACT(USI->now, hss->lastrcvd) > USI->perf.pingtime &&
TIMEVAL_SUBTRACT(USI->now, hss->lastping_sent) > USI->perf.pingtime &&
- USI->gstats->sendOK() && hss->sendOK(NULL)) {
+ USI->gstats->sendOK(NULL) && hss->sendOK(NULL)) {
sendPingProbe(USI, hss);
hss->lastping_sent = USI->now;
hss->lastping_sent_numprobes = hss->numprobes_sent;
@@ -2914,7 +2987,7 @@ static void doAnyPings(UltraScanInfo *USI) {
USI->gstats->probes_sent >= USI->gstats->lastping_sent_numprobes + 20 &&
TIMEVAL_SUBTRACT(USI->now, USI->gstats->lastrcvd) > USI->perf.pingtime &&
TIMEVAL_SUBTRACT(USI->now, USI->gstats->lastping_sent) > USI->perf.pingtime &&
- USI->gstats->sendOK()) {
+ USI->gstats->sendOK(NULL)) {
sendGlobalPingProbe(USI);
USI->gstats->lastping_sent = USI->now;
USI->gstats->lastping_sent_numprobes = USI->gstats->probes_sent;
@@ -2966,6 +3039,12 @@ static void doAnyOutstandingRetransmits(UltraScanInfo *USI) {
int retrans = 0; /* Number of retransmissions during a loop */
unsigned int maxtries;
+ struct timeval tv_start = {0};
+ if (o.debugging) {
+ gettimeofday(&USI->now, NULL);
+ tv_start = USI->now;
+ }
+
gettimeofday(&USI->now, NULL);
/* Loop until we get through all the hosts without a retransmit or we're not
@@ -2973,7 +3052,7 @@ static void doAnyOutstandingRetransmits(UltraScanInfo *USI) {
do {
retrans = 0;
for (hostI = USI->incompleteHosts.begin();
- hostI != USI->incompleteHosts.end() && USI->gstats->sendOK();
+ hostI != USI->incompleteHosts.end() && USI->gstats->sendOK(NULL);
hostI++) {
host = *hostI;
/* Skip this host if it has nothing to send. */
@@ -3006,7 +3085,14 @@ static void doAnyOutstandingRetransmits(UltraScanInfo *USI) {
}
} while (probeI != host->probes_outstanding.begin());
}
- } while (USI->gstats->sendOK() && retrans != 0);
+ } while (USI->gstats->sendOK(NULL) && retrans != 0);
+
+ if (o.debugging) {
+ long tv_diff;
+ gettimeofday(&USI->now, NULL);
+ tv_diff = TIMEVAL_MSEC_SUBTRACT(USI->now, tv_start);
+ if (tv_diff > 30) log_write(LOG_PLAIN, "%s took %lims\n", __func__, tv_diff);
+ }
}
/* Print occasional remaining time estimates, as well as
@@ -3041,6 +3127,13 @@ static void printAnyStats(UltraScanInfo *USI) {
hss->target->to.rttvar);
}
}
+
+ log_write(LOG_PLAIN, "Current sending rates: %.2f packets / s, %.2f bytes / s.\n",
+ USI->send_rate_meter.getCurrentPacketRate(),
+ USI->send_rate_meter.getCurrentByteRate());
+ log_write(LOG_PLAIN, "Overall sending rates: %.2f packets / s, %.2f bytes / s.\n",
+ USI->send_rate_meter.getOverallPacketRate(),
+ USI->send_rate_meter.getOverallByteRate());
}
/* Now time to figure out how close we are to completion ... */
@@ -4694,12 +4787,20 @@ void ultra_scan(vector &Targets, struct scan_lists *ports,
avgdone /= USI->gstats->numtargets;
USI->SPM->printStats(avgdone, NULL); // This prints something like SYN Stealth Scan Timing: About 1.14% done; ETC: 15:01 (0:43:23 remaining);
+ /* Don't update when getting the current rates, otherwise we can get
+ anomalies (rates are too low) from having just done a potentially long
+ waitForResponses without sending any packets. */
+ log_write(LOG_STDOUT, "Current sending rates: %.2f packets / s, %.2f bytes / s.\n",
+ USI->send_rate_meter.getCurrentPacketRate(&USI->now, false),
+ USI->send_rate_meter.getCurrentByteRate(&USI->now, false));
log_flush(LOG_STDOUT);
}
}
+ USI->send_rate_meter.stop(&USI->now);
+
/* Save the computed timeouts. */
if (to != NULL)
*to = USI->gstats->to;
@@ -4718,6 +4819,9 @@ void ultra_scan(vector &Targets, struct scan_lists *ports,
USI->gstats->num_hosts_timedout,
(USI->gstats->num_hosts_timedout == 1)? "host" : "hosts");
USI->SPM->endTask(NULL, additional_info);
+ log_write(LOG_STDOUT, "Overall sending rates: %.2f packets / s, %.2f bytes / s.\n",
+ USI->send_rate_meter.getOverallPacketRate(),
+ USI->send_rate_meter.getOverallByteRate());
}
if (o.debugging > 2 && USI->pd != NULL)
diff --git a/timing.cc b/timing.cc
index e5f27c026..54ddedef9 100644
--- a/timing.cc
+++ b/timing.cc
@@ -238,6 +238,156 @@ void enforce_scan_delay(struct timeval *tv) {
return;
}
+/* Initialize the constant CURRENT_RATE_HISTORY that defines how far back we
+ look when calculating the current rates. */
+RateMeter::RateMeter() : CURRENT_RATE_HISTORY(5.0) {
+ start_tv.tv_sec = 0;
+ start_tv.tv_usec = 0;
+ stop_tv.tv_sec = 0;
+ stop_tv.tv_usec = 0;
+ last_update_tv.tv_sec = 0;
+ last_update_tv.tv_usec = 0;
+ num_packets = 0;
+ num_bytes = 0;
+ current_packet_rate = 0.0;
+ current_byte_rate = 0.0;
+ assert(!isSet(&start_tv));
+ assert(!isSet(&stop_tv));
+}
+
+void RateMeter::start(const struct timeval *now) {
+ assert(!isSet(&start_tv));
+ assert(!isSet(&stop_tv));
+ if (now == NULL)
+ gettimeofday(&start_tv, NULL);
+ else
+ start_tv = *now;
+}
+
+void RateMeter::stop(const struct timeval *now) {
+ assert(isSet(&start_tv));
+ assert(!isSet(&stop_tv));
+ if (now == NULL)
+ gettimeofday(&stop_tv, NULL);
+ else
+ stop_tv = *now;
+}
+
+/* Record a packet of length len. If now is not NULL, use it as the time the
+ packet was received rather than calling gettimeofday. */
+void RateMeter::record(u32 len, const struct timeval *now) {
+ update(1, len, now);
+}
+
+double RateMeter::getOverallPacketRate(const struct timeval *now) const {
+ return num_packets / elapsedTime(now);
+}
+
+/* Get the "current" packet rate (actually a moving average of the last few
+ seconds). If update is true (its default value), lower the rate to account
+ for the time since the last packet was received. */
+double RateMeter::getCurrentPacketRate(const struct timeval *now, bool update) {
+ if (update)
+ this->update(0, 0, now);
+
+ return current_packet_rate;
+}
+
+double RateMeter::getOverallByteRate(const struct timeval *now) const {
+ return num_bytes / elapsedTime(now);
+}
+
+/* Get the "current" byte rate (actually a moving average of the last few
+ seconds). If update is true (its default value), lower the rate to account
+ for the time since the last bytes were received. */
+double RateMeter::getCurrentByteRate(const struct timeval *now, bool update) {
+ if (update)
+ this->update(0, 0, now);
+
+ return current_byte_rate;
+}
+
+/* Update the rates to include packets additional packets and bytes additional
+ bytes. If now is not NULL, use it as the time the packets and bytes were
+ received rather than calling gettimeofday. */
+void RateMeter::update(u32 packets, u32 bytes, const struct timeval *now) {
+ struct timeval tv;
+ double diff;
+ double d;
+ double count;
+
+ assert(isSet(&start_tv));
+ assert(!isSet(&stop_tv));
+
+ /* Update the overall counters. */
+ num_packets += packets;
+ num_bytes += bytes;
+
+ if (now == NULL) {
+ gettimeofday(&tv, NULL);
+ now = &tv;
+ }
+ if (!isSet(&last_update_tv))
+ last_update_tv = start_tv;
+
+ /* Calculate approximate moving averages of how many packets and bytes were
+ recorded in the last CURRENT_RATE_HISTORY seconds. These averages are what
+ are returned as the "current" rates. */
+ /* How long since the last update? */
+ diff = TIMEVAL_SUBTRACT(*now, last_update_tv) / 1000000.0;
+ /* Look back CURRENT_RATE_HISTORY seconds, or to when the last sample was
+ recorded, whichever is longer, but don't look back past the start. */
+ d = MAX(CURRENT_RATE_HISTORY, diff);
+ d = MIN(d, TIMEVAL_SUBTRACT(*now, start_tv) / 1000000.0);
+ assert(d >= 0);
+ /* If we get packets in the very same instant that the timer is started,
+ there's no way to calculate meaningful rates. Ignore it. */
+ if (d == 0.0)
+ return;
+ /* To calculate the approximate average of the packet rate over the last d
+ seconds, we assume that the rate was constant over that interval. We
+ calculate how many packets would have been received in that interval,
+ ignoring the first diff seconds' worth, (d - diff) * current_packet_rate.
+ Then we add how many packets were received in the most recent diff seconds.
+ Divide by the width of the interval to get the average. */
+ count = (d - diff) * current_packet_rate + packets;
+ current_packet_rate = count / d;
+ assert(current_packet_rate >= 0.0);
+ /* Likewise with the byte rate. */
+ count = (d - diff) * current_byte_rate + bytes;
+ current_byte_rate = count / d;
+ assert(current_byte_rate >= 0.0);
+
+ last_update_tv = *now;
+}
+
+/* Get the number of seconds the meter has been running: if it has been stopped,
+ the amount of time between start and stop, or if it is still running, the
+ amount of time between start and now. */
+double RateMeter::elapsedTime(const struct timeval *now) const {
+ struct timeval tv;
+ const struct timeval *end_tv;
+
+ assert(isSet(&start_tv));
+
+ if (isSet(&stop_tv)) {
+ end_tv = &stop_tv;
+ } else if (now == NULL) {
+ gettimeofday(&tv, NULL);
+ end_tv = &tv;
+ } else {
+ end_tv = now;
+ }
+
+ return TIMEVAL_SUBTRACT(*end_tv, start_tv) / 1000000.0;
+}
+
+/* Returns true if tv has been initialized; i.e., its members are not all
+ zero. */
+bool RateMeter::isSet(const struct timeval *tv) {
+ return tv->tv_sec != 0 || tv->tv_usec != 0;
+}
+
ScanProgressMeter::ScanProgressMeter(char *stypestr) {
scantypestr = strdup(stypestr);
gettimeofday(&begin, NULL);
diff --git a/timing.h b/timing.h
index 8879b9c50..d78d1a059 100644
--- a/timing.h
+++ b/timing.h
@@ -129,6 +129,42 @@ void adjust_timeouts(struct timeval sent, struct timeout_info *to);
time is recorded in it */
void enforce_scan_delay(struct timeval *tv);
+/* This class implements current and lifetime average data rates for packets and
+ bytes. */
+class RateMeter {
+ public:
+ RateMeter();
+
+ void start(const struct timeval *now = NULL);
+ void stop(const struct timeval *now = NULL);
+ void record(u32 len, const struct timeval *now = NULL);
+ double getOverallPacketRate(const struct timeval *now = NULL) const;
+ double getCurrentPacketRate(const struct timeval *now = NULL, bool update =true);
+ double getOverallByteRate(const struct timeval *now = NULL) const;
+ double getCurrentByteRate(const struct timeval *now = NULL, bool update =true);
+
+ private:
+ /* How many seconds to look back when calculating the "current" rates. */
+ const double CURRENT_RATE_HISTORY;
+
+ /* When this meter started recording. */
+ struct timeval start_tv;
+ /* When this meter stopped recording. */
+ struct timeval stop_tv;
+ /* The last time the current sample rates were updated. */
+ struct timeval last_update_tv;
+
+ unsigned long long num_packets;
+ unsigned long long num_bytes;
+
+ double current_packet_rate;
+ double current_byte_rate;
+
+ void update(u32 packets, u32 bytes, const struct timeval *now);
+ double elapsedTime(const struct timeval *now = NULL) const;
+ static bool isSet(const struct timeval *tv);
+};
+
class ScanProgressMeter {
public:
/* A COPY of stypestr is made and saved for when stats are printed */
diff --git a/utils.h b/utils.h
index 1c25eb508..9f794528f 100644
--- a/utils.h
+++ b/utils.h
@@ -175,8 +175,8 @@
#define TIMEVAL_SEC_SUBTRACT(a,b) ((a).tv_sec - (b).tv_sec + (((a).tv_usec < (b).tv_usec) ? - 1 : 0))
/* assign one timeval to another timeval plus some msecs: a = b + msecs */
-#define TIMEVAL_MSEC_ADD(a, b, msecs) (a).tv_sec = (b).tv_sec + ((msecs) / 1000); (a).tv_usec = (b).tv_usec + ((msecs) % 1000) * 1000; (a).tv_sec += (a).tv_usec / 1000000; (a).tv_usec %= 1000000
-#define TIMEVAL_ADD(a, b, usecs) (a).tv_sec = (b).tv_sec + ((usecs) / 1000000); (a).tv_usec = (b).tv_usec + ((usecs) % 1000000); (a).tv_sec += (a).tv_usec / 1000000; (a).tv_usec %= 1000000
+#define TIMEVAL_MSEC_ADD(a, b, msecs) { (a).tv_sec = (b).tv_sec + ((msecs) / 1000); (a).tv_usec = (b).tv_usec + ((msecs) % 1000) * 1000; (a).tv_sec += (a).tv_usec / 1000000; (a).tv_usec %= 1000000; }
+#define TIMEVAL_ADD(a, b, usecs) { (a).tv_sec = (b).tv_sec + ((usecs) / 1000000); (a).tv_usec = (b).tv_usec + ((usecs) % 1000000); (a).tv_sec += (a).tv_usec / 1000000; (a).tv_usec %= 1000000; }
/* Return num if it is between min and max. Otherwise return min or
max (whichever is closest to num), */