summaryrefslogtreecommitdiff
path: root/rdns_scan/zmap4rdns/src/monitor.c
diff options
context:
space:
mode:
Diffstat (limited to 'rdns_scan/zmap4rdns/src/monitor.c')
-rw-r--r--rdns_scan/zmap4rdns/src/monitor.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/rdns_scan/zmap4rdns/src/monitor.c b/rdns_scan/zmap4rdns/src/monitor.c
new file mode 100644
index 0000000..63369a5
--- /dev/null
+++ b/rdns_scan/zmap4rdns/src/monitor.c
@@ -0,0 +1,487 @@
+/*
+ * ZMap Copyright 2013 Regents of the University of Michigan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
+ */
+
+// module responsible for printing on-screen updates during the scan process
+
+#include "monitor.h"
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "iterator.h"
+#include "recv.h"
+#include "state.h"
+
+#include "../lib/lockfd.h"
+#include "../lib/logger.h"
+#include "../lib/util.h"
+#include "../lib/xalloc.h"
+
+#define UPDATE_INTERVAL 1 // seconds
+#define NUMBER_STR_LEN 20
+#define WARMUP_PERIOD 5
+#define MIN_HITRATE_TIME_WINDOW 5 // seconds
+
+// internal monitor status that is used to track deltas
+typedef struct internal_scan_status {
+ double last_now;
+ uint64_t last_sent;
+ uint64_t last_tried_sent;
+ uint32_t last_send_failures;
+ uint32_t last_recv_net_success;
+ uint32_t last_recv_app_success;
+ uint32_t last_recv_total;
+ uint32_t last_pcap_drop;
+ double min_hitrate_start;
+} int_status_t;
+
+// exportable status information that can be printed to screen
+typedef struct export_scan_status {
+ uint64_t total_sent;
+ uint64_t total_tried_sent;
+ uint32_t recv_success_unique;
+ uint32_t app_recv_success_unique;
+ uint64_t total_recv;
+ uint32_t complete;
+ uint32_t send_threads;
+ double percent_complete;
+
+ double hitrate; // network, e.g. SYN-ACK vs RST
+ double
+ app_hitrate; // application level, e.g. DNS response versus correct
+ // lookup.
+
+ double send_rate;
+ char send_rate_str[NUMBER_STR_LEN];
+ double send_rate_avg;
+ char send_rate_avg_str[NUMBER_STR_LEN];
+
+ double recv_rate;
+ char recv_rate_str[NUMBER_STR_LEN];
+ double recv_avg;
+ char recv_avg_str[NUMBER_STR_LEN];
+ double recv_total_rate;
+ double recv_total_avg;
+
+ double app_success_rate;
+ char app_success_rate_str[NUMBER_STR_LEN];
+ double app_success_avg;
+ char app_success_avg_str[NUMBER_STR_LEN];
+
+ uint32_t pcap_drop;
+ uint32_t pcap_ifdrop;
+ uint32_t pcap_drop_total;
+ char pcap_drop_total_str[NUMBER_STR_LEN];
+ double pcap_drop_last;
+ char pcap_drop_last_str[NUMBER_STR_LEN];
+ double pcap_drop_avg;
+ char pcap_drop_avg_str[NUMBER_STR_LEN];
+
+ uint32_t time_remaining;
+ char time_remaining_str[NUMBER_STR_LEN];
+ uint32_t time_past;
+ char time_past_str[NUMBER_STR_LEN];
+
+ uint32_t fail_total;
+ double fail_avg;
+ double fail_last;
+ float seconds_under_min_hitrate;
+
+} export_status_t;
+
+static FILE *status_fd = NULL;
+
+// find minimum of an array of doubles
+static double min_d(double array[], int n)
+{
+ double value = INFINITY;
+ for (int i = 0; i < n; i++) {
+ if (array[i] < value) {
+ value = array[i];
+ }
+ }
+ return value;
+}
+
+// estimate time remaining time based on config and state
+double compute_remaining_time(double age, uint64_t packets_sent, uint64_t iterations)
+{
+ if (!zsend.complete) {
+ double remaining[] = {INFINITY, INFINITY, INFINITY, INFINITY, INFINITY};
+ if (zsend.list_of_ips_pbm) {
+ // Estimate progress using group iterations
+ double done = (double) iterations /
+ ((uint64_t)0xFFFFFFFFU /
+ zconf.total_shards);
+ remaining[0] =
+ (1. - done) * (age / done) + zconf.cooldown_secs;
+ }
+ if (zsend.max_targets) {
+ double done =
+ (double)packets_sent /
+ ((uint64_t)zsend.max_targets * zconf.packet_streams /
+ zconf.total_shards);
+ remaining[1] =
+ (1. - done) * (age / done) + zconf.cooldown_secs;
+ }
+ if (zconf.max_runtime) {
+ remaining[2] =
+ (zconf.max_runtime - age) + zconf.cooldown_secs;
+ }
+ if (zconf.max_results) {
+ double done =
+ (double)zrecv.filter_success / zconf.max_results;
+ remaining[3] = (1. - done) * (age / done);
+ }
+ if (zsend.max_index) {
+ double done = (double)packets_sent /
+ (zsend.max_index * zconf.packet_streams /
+ zconf.total_shards);
+ remaining[4] =
+ (1. - done) * (age / done) + zconf.cooldown_secs;
+ }
+ return min_d(remaining, sizeof(remaining) / sizeof(double));
+ } else {
+ return zconf.cooldown_secs - (now() - zsend.finish);
+ }
+}
+
+static void update_pcap_stats(pthread_mutex_t *recv_ready_mutex)
+{
+ // ask pcap for fresh values
+ pthread_mutex_lock(recv_ready_mutex);
+ recv_update_stats();
+ pthread_mutex_unlock(recv_ready_mutex);
+}
+
+static void export_stats(int_status_t *intrnl, export_status_t *exp,
+ iterator_t *it)
+{
+ uint64_t total_sent = iterator_get_sent(it);
+ uint64_t total_iterations = iterator_get_iterations(it);
+ uint32_t total_fail = iterator_get_fail(it);
+ uint64_t total_recv = zrecv.pcap_recv;
+ uint64_t recv_success = zrecv.filter_success;
+ uint32_t app_success = zrecv.app_success_unique;
+ double cur_time = now();
+ double age = cur_time - zsend.start; // time of entire scan
+ // time since the last time we updated
+ double delta = cur_time - intrnl->last_now;
+ double remaining_secs = compute_remaining_time(age, total_sent, total_iterations);
+
+ // export amount of time the scan has been running
+ if (age < WARMUP_PERIOD) {
+ exp->time_remaining_str[0] = '\0';
+ } else {
+ char buf[20];
+ time_string(ceil(remaining_secs), 1, buf, sizeof(buf));
+ snprintf(exp->time_remaining_str, NUMBER_STR_LEN, " (%s left)",
+ buf);
+ }
+ exp->time_past = age;
+ exp->time_remaining = remaining_secs;
+ time_string((int)age, 0, exp->time_past_str, NUMBER_STR_LEN);
+
+ // export recv statistics
+ exp->recv_rate =
+ ceil((recv_success - intrnl->last_recv_net_success) / delta);
+ number_string(exp->recv_rate, exp->recv_rate_str, NUMBER_STR_LEN);
+ exp->recv_avg = recv_success / age;
+ number_string(exp->recv_avg, exp->recv_avg_str, NUMBER_STR_LEN);
+ exp->recv_total_rate = (total_recv - intrnl->last_recv_total) / delta;
+ exp->recv_total_avg = total_recv / age;
+
+ // application level statistics
+ if (zconf.fsconf.app_success_index >= 0) {
+ exp->app_success_rate =
+ (app_success - intrnl->last_recv_app_success) / delta;
+ number_string(exp->app_success_rate, exp->app_success_rate_str,
+ NUMBER_STR_LEN);
+ exp->app_success_avg = (app_success / age);
+ number_string(exp->app_success_avg, exp->app_success_avg_str,
+ NUMBER_STR_LEN);
+ }
+
+ if (!total_sent) {
+ exp->hitrate = 0;
+ exp->app_hitrate = 0;
+ } else {
+ exp->hitrate = recv_success * 100.0 / total_sent;
+ exp->app_hitrate = app_success * 100.0 / total_sent;
+ }
+
+ if (age > WARMUP_PERIOD && exp->hitrate < zconf.min_hitrate) {
+ if (fabs(intrnl->min_hitrate_start) < .00001) {
+ intrnl->min_hitrate_start = cur_time;
+ }
+ } else {
+ intrnl->min_hitrate_start = 0.0;
+ }
+ if (fabs(intrnl->min_hitrate_start) < .00001) {
+ exp->seconds_under_min_hitrate = 0;
+ } else {
+ exp->seconds_under_min_hitrate =
+ cur_time - intrnl->min_hitrate_start;
+ }
+ if (!zsend.complete) {
+ exp->send_rate = ceil((total_sent - intrnl->last_sent) / delta);
+ number_string(exp->send_rate, exp->send_rate_str,
+ NUMBER_STR_LEN);
+ exp->send_rate_avg = total_sent / age;
+ number_string(exp->send_rate_avg, exp->send_rate_avg_str,
+ NUMBER_STR_LEN);
+ } else {
+ exp->send_rate_avg = total_sent / (zsend.finish - zsend.start);
+ number_string(exp->send_rate_avg, exp->send_rate_avg_str,
+ NUMBER_STR_LEN);
+ }
+ // export other pre-calculated values
+ exp->total_sent = total_sent;
+ exp->total_tried_sent = total_iterations;
+ exp->percent_complete = 100. * age / (age + remaining_secs);
+ exp->recv_success_unique = recv_success;
+ exp->app_recv_success_unique = app_success;
+ exp->total_recv = total_recv;
+ exp->complete = zsend.complete;
+
+ // pcap dropped packets
+ exp->pcap_drop = zrecv.pcap_drop;
+ exp->pcap_ifdrop = zrecv.pcap_ifdrop;
+ exp->pcap_drop_total = exp->pcap_drop + exp->pcap_ifdrop;
+ exp->pcap_drop_last =
+ (exp->pcap_drop_total - intrnl->last_pcap_drop) / delta;
+ exp->pcap_drop_avg = exp->pcap_drop_total / age;
+ number_string(exp->pcap_drop_total, exp->pcap_drop_total_str,
+ NUMBER_STR_LEN);
+ number_string(exp->pcap_drop_last, exp->pcap_drop_last_str,
+ NUMBER_STR_LEN);
+ number_string(exp->pcap_drop_avg, exp->pcap_drop_avg_str,
+ NUMBER_STR_LEN);
+
+ zsend.sendto_failures = total_fail;
+ exp->fail_total = zsend.sendto_failures;
+ exp->fail_last = (exp->fail_total - intrnl->last_send_failures) / delta;
+ exp->fail_avg = exp->fail_total / age;
+
+ // misc
+ exp->send_threads = iterator_get_curr_send_threads(it);
+
+ // Update internal stats
+ intrnl->last_now = cur_time;
+ intrnl->last_sent = exp->total_sent;
+ intrnl->last_recv_net_success = exp->recv_success_unique;
+ intrnl->last_recv_app_success = exp->app_recv_success_unique;
+ intrnl->last_pcap_drop = exp->pcap_drop_total;
+ intrnl->last_send_failures = exp->fail_total;
+ intrnl->last_recv_total = exp->total_recv;
+}
+
+static void log_drop_warnings(export_status_t *exp)
+{
+ if (exp->pcap_drop_last / exp->recv_rate > 0.05) {
+ log_warn("monitor",
+ "Dropped %.0f packets in the last second, (%u total "
+ "dropped (pcap: %u + iface: %u))",
+ exp->pcap_drop_last, exp->pcap_drop_total,
+ exp->pcap_drop, exp->pcap_ifdrop);
+ }
+ if (exp->fail_last / exp->send_rate > 0.01) {
+ log_warn("monitor",
+ "Failed to send %.0f packets/sec (%u total failures)",
+ exp->fail_last, exp->fail_total);
+ }
+}
+
+static void onscreen_appsuccess(export_status_t *exp)
+{
+ // this when probe module handles application-level success rates
+ if (!exp->complete) {
+ fprintf(stderr,
+ "%5s %0.0f%%%s; sent: %"PRIu64" %sp/s (%sp/s avg); "
+ "recv: %u %sp/s (%sp/s avg); "
+ "app success: %u %sp/s (%sp/s avg); "
+ "drops: %sp/s (%sp/s avg); "
+ "hitrate: %0.2f%% "
+ "app hitrate: %0.2f%%\n",
+ exp->time_past_str, exp->percent_complete,
+ exp->time_remaining_str, exp->total_sent,
+ exp->send_rate_str, exp->send_rate_avg_str,
+ exp->recv_success_unique, exp->recv_rate_str,
+ exp->recv_avg_str, exp->app_recv_success_unique,
+ exp->app_success_rate_str, exp->app_success_avg_str,
+ exp->pcap_drop_last_str, exp->pcap_drop_avg_str,
+ exp->hitrate, exp->app_hitrate);
+ } else {
+ fprintf(stderr,
+ "%5s %0.0f%%%s; sent: %"PRIu64" done (%sp/s avg); "
+ "recv: %u %sp/s (%sp/s avg); "
+ "app success: %u %sp/s (%sp/s avg); "
+ "drops: %sp/s (%sp/s avg); "
+ "hitrate: %0.2f%% "
+ "app hitrate: %0.2f%%\n",
+ exp->time_past_str, exp->percent_complete,
+ exp->time_remaining_str, exp->total_sent,
+ exp->send_rate_avg_str, exp->recv_success_unique,
+ exp->recv_rate_str, exp->recv_avg_str,
+ exp->app_recv_success_unique, exp->app_success_rate_str,
+ exp->app_success_avg_str, exp->pcap_drop_last_str,
+ exp->pcap_drop_avg_str, exp->hitrate, exp->app_hitrate);
+ }
+}
+
+static void onscreen_generic(export_status_t *exp)
+{
+ if (!exp->complete) {
+ fprintf(stderr,
+ "%5s %0.0f%%%s; send: %"PRIu64" %sp/s (%sp/s avg); "
+ "recv: %u %sp/s (%sp/s avg); "
+ "drops: %sp/s (%sp/s avg); "
+ "hitrate: %0.2f%%\n",
+ exp->time_past_str, exp->percent_complete,
+ exp->time_remaining_str, exp->total_sent,
+ exp->send_rate_str, exp->send_rate_avg_str,
+ exp->recv_success_unique, exp->recv_rate_str,
+ exp->recv_avg_str, exp->pcap_drop_last_str,
+ exp->pcap_drop_avg_str, exp->hitrate);
+ } else {
+ fprintf(stderr,
+ "%5s %0.0f%%%s; send: %"PRIu64" done (%sp/s avg); "
+ "recv: %u %sp/s (%sp/s avg); "
+ "drops: %sp/s (%sp/s avg); "
+ "hitrate: %0.2f%%\n",
+ exp->time_past_str, exp->percent_complete,
+ exp->time_remaining_str, exp->total_sent,
+ exp->send_rate_avg_str, exp->recv_success_unique,
+ exp->recv_rate_str, exp->recv_avg_str,
+ exp->pcap_drop_last_str, exp->pcap_drop_avg_str,
+ exp->hitrate);
+ }
+ fflush(stderr);
+}
+
+static FILE *init_status_update_file(char *path)
+{
+ FILE *f = fopen(path, "w");
+ if (!f) {
+ log_fatal("csv", "could not open status updates file (%s): %s",
+ zconf.status_updates_file, strerror(errno));
+ }
+ log_debug("monitor", "status updates CSV will be saved to %s",
+ zconf.status_updates_file);
+ fprintf(
+ f,
+ "real-time,time-elapsed,time-remaining,"
+ "percent-complete,hit-rate,active-send-threads,"
+ "sent-total,sent-last-one-sec,sent-avg-per-sec,"
+ "recv-success-total,recv-success-last-one-sec,recv-success-avg-per-sec,"
+ "recv-total,recv-total-last-one-sec,recv-total-avg-per-sec,"
+ "pcap-drop-total,drop-last-one-sec,drop-avg-per-sec,"
+ "sendto-fail-total,sendto-fail-last-one-sec,sendto-fail-avg-per-sec\n");
+ fflush(f);
+ return f;
+}
+
+static void update_status_updates_file(export_status_t *exp, FILE *f)
+{
+ struct timeval now;
+ char timestamp[256];
+ gettimeofday(&now, NULL);
+ time_t sec = now.tv_sec;
+ struct tm *ptm = localtime(&sec);
+ strftime(timestamp, 20, "%Y-%m-%d %H:%M:%S", ptm);
+
+ fprintf(f,
+ "%s,%u,%u,"
+ "%f,%f,%u,"
+ "%"PRIu64",%.0f,%.0f,"
+ "%u,%.0f,%.0f,"
+ "%"PRIu64",%.0f,%.0f,"
+ "%u,%.0f,%.0f,"
+ "%u,%.0f,%.0f\n",
+ timestamp, exp->time_past, exp->time_remaining,
+ exp->percent_complete, exp->hitrate, exp->send_threads,
+ exp->total_sent, exp->send_rate, exp->send_rate_avg,
+ exp->recv_success_unique, exp->recv_rate, exp->recv_avg,
+ exp->total_recv, exp->recv_total_rate, exp->recv_total_avg,
+ exp->pcap_drop_total, exp->pcap_drop_last, exp->pcap_drop_avg,
+ exp->fail_total, exp->fail_last, exp->fail_avg);
+ fflush(f);
+}
+
+static inline void check_min_hitrate(export_status_t *exp)
+{
+ if (exp->seconds_under_min_hitrate >= MIN_HITRATE_TIME_WINDOW) {
+ log_fatal("monitor",
+ "hitrate below %.0f for %.0f seconds. aborting scan.",
+ zconf.min_hitrate, exp->seconds_under_min_hitrate);
+ }
+}
+
+static inline void check_max_sendto_failures(export_status_t *exp)
+{
+ if (zconf.max_sendto_failures >= 0 &&
+ exp->fail_total > (uint32_t)zconf.max_sendto_failures) {
+ log_fatal("monitor",
+ "maximum number of sendto failures (%i) exceeded",
+ zconf.max_sendto_failures);
+ }
+}
+
+void monitor_init(void)
+{
+ if (zconf.status_updates_file) {
+ status_fd = init_status_update_file(zconf.status_updates_file);
+ assert(status_fd);
+ }
+}
+
+void monitor_run(iterator_t *it, pthread_mutex_t *lock)
+{
+ int_status_t *internal_status = xmalloc(sizeof(int_status_t));
+ export_status_t *export_status = xmalloc(sizeof(export_status_t));
+
+ while (!(zsend.complete && zrecv.complete)) {
+ update_pcap_stats(lock);
+ export_stats(internal_status, export_status, it);
+ log_drop_warnings(export_status);
+ check_min_hitrate(export_status);
+ check_max_sendto_failures(export_status);
+ if (!zconf.quiet) {
+ lock_file(stderr);
+ if (zconf.fsconf.app_success_index >= 0) {
+ onscreen_appsuccess(export_status);
+ } else {
+ onscreen_generic(export_status);
+ }
+ unlock_file(stderr);
+ }
+ if (status_fd) {
+ update_status_updates_file(export_status, status_fd);
+ }
+ sleep(UPDATE_INTERVAL);
+ }
+ if (!zconf.quiet) {
+ lock_file(stderr);
+ fflush(stderr);
+ unlock_file(stderr);
+ }
+ if (status_fd) {
+ fflush(status_fd);
+ fclose(status_fd);
+ }
+}