monitor.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /*
  2. * ZMap Copyright 2013 Regents of the University of Michigan
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy
  6. * of the License at http://www.apache.org/licenses/LICENSE-2.0
  7. */
  8. // module responsible for printing on-screen updates during the scan process
  9. #include "monitor.h"
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include <unistd.h>
  13. #include <math.h>
  14. #include <time.h>
  15. #include <sys/time.h>
  16. #include "iterator.h"
  17. #include "recv.h"
  18. #include "state.h"
  19. #include "../lib/logger.h"
  20. #include "../lib/util.h"
  21. #include "../lib/xalloc.h"
  22. #include "../lib/lockfd.h"
  23. #define UPDATE_INTERVAL 1 //seconds
  24. #define NUMBER_STR_LEN 20
  25. #define WARMUP_PERIOD 5
  26. #define MIN_HITRATE_TIME_WINDOW 5 //seconds
  27. // internal monitor status that is used to track deltas
  28. typedef struct internal_scan_status {
  29. double last_now;
  30. uint32_t last_sent;
  31. uint32_t last_send_failures;
  32. uint32_t last_recv_net_success;
  33. uint32_t last_recv_app_success;
  34. uint32_t last_recv_total;
  35. uint32_t last_pcap_drop;
  36. double min_hitrate_start;
  37. } int_status_t;
  38. // exportable status information that can be printed to screen
  39. typedef struct export_scan_status {
  40. uint32_t total_sent;
  41. uint32_t recv_success_unique;
  42. uint32_t app_recv_success_unique;
  43. uint32_t total_recv;
  44. uint32_t complete;
  45. uint32_t send_threads;
  46. double percent_complete;
  47. double hitrate; // network, e.g. SYN-ACK vs RST
  48. double app_hitrate; // application level, e.g. DNS response versus correct lookup.
  49. double send_rate;
  50. char send_rate_str[NUMBER_STR_LEN];
  51. double send_rate_avg;
  52. char send_rate_avg_str[NUMBER_STR_LEN];
  53. double recv_rate;
  54. char recv_rate_str[NUMBER_STR_LEN];
  55. double recv_avg;
  56. char recv_avg_str[NUMBER_STR_LEN];
  57. double recv_total_rate;
  58. double recv_total_avg;
  59. double app_success_rate;
  60. char app_success_rate_str[NUMBER_STR_LEN];
  61. double app_success_avg;
  62. char app_success_avg_str[NUMBER_STR_LEN];
  63. uint32_t pcap_drop;
  64. uint32_t pcap_ifdrop;
  65. uint32_t pcap_drop_total;
  66. char pcap_drop_total_str[NUMBER_STR_LEN];
  67. double pcap_drop_last;
  68. char pcap_drop_last_str[NUMBER_STR_LEN];
  69. double pcap_drop_avg;
  70. char pcap_drop_avg_str[NUMBER_STR_LEN];
  71. uint32_t time_remaining;
  72. char time_remaining_str[NUMBER_STR_LEN];
  73. uint32_t time_past;
  74. char time_past_str[NUMBER_STR_LEN];
  75. uint32_t fail_total;
  76. double fail_avg;
  77. double fail_last;
  78. float seconds_under_min_hitrate;
  79. } export_status_t;
  80. // find minimum of an array of doubles
  81. static double min_d(double array[], int n)
  82. {
  83. double value=INFINITY;
  84. for (int i=0; i<n; i++) {
  85. if (array[i] < value) {
  86. value = array[i];
  87. }
  88. }
  89. return value;
  90. }
  91. // estimate time remaining time based on config and state
  92. double compute_remaining_time(double age, uint64_t sent)
  93. {
  94. if (!zsend.complete) {
  95. double remaining[] = {INFINITY, INFINITY, INFINITY};
  96. if (zsend.targets) {
  97. double done = (double) sent/zsend.targets;
  98. remaining[0] = (1. - done)*(age/done) + zconf.cooldown_secs;
  99. }
  100. if (zconf.max_runtime) {
  101. remaining[1] = (zconf.max_runtime - age)+zconf.cooldown_secs;
  102. }
  103. if (zconf.max_results) {
  104. double done = (double)zrecv.success_unique/zconf.max_results;
  105. remaining[2] = (1. - done)*(age/done);
  106. }
  107. return min_d(remaining, sizeof(remaining)/sizeof(double));
  108. } else {
  109. return zconf.cooldown_secs - (now() - zsend.finish);
  110. }
  111. }
  112. static void update_pcap_stats(pthread_mutex_t *recv_ready_mutex)
  113. {
  114. // ask pcap for fresh values
  115. pthread_mutex_lock(recv_ready_mutex);
  116. recv_update_stats();
  117. pthread_mutex_unlock(recv_ready_mutex);
  118. }
  119. static void export_stats(int_status_t *intrnl, export_status_t *exp, iterator_t *it)
  120. {
  121. uint32_t total_sent = iterator_get_sent(it);
  122. uint32_t total_recv = zrecv.pcap_recv;
  123. uint32_t recv_success = zrecv.success_unique;
  124. uint32_t app_success = zrecv.app_success_unique;
  125. double cur_time = now();
  126. double age = cur_time - zsend.start; // time of entire scan
  127. double delta = cur_time - intrnl->last_now; // time since the last time we updated
  128. double remaining_secs = compute_remaining_time(age, total_sent);
  129. // export amount of time the scan has been running
  130. if (age < WARMUP_PERIOD) {
  131. exp->time_remaining_str[0] = '\0';
  132. } else {
  133. char buf[20];
  134. time_string(ceil(remaining_secs), 1, buf, sizeof(buf));
  135. snprintf(exp->time_remaining_str, NUMBER_STR_LEN, " (%s left)", buf);
  136. }
  137. exp->time_past = age;
  138. exp->time_remaining = remaining_secs;
  139. time_string((int)age, 0, exp->time_past_str, NUMBER_STR_LEN);
  140. // export recv statistics
  141. exp->recv_rate = (recv_success - intrnl->last_recv_net_success)/delta;
  142. number_string(exp->recv_rate, exp->recv_rate_str, NUMBER_STR_LEN);
  143. exp->recv_avg = recv_success/age;
  144. number_string(exp->recv_avg, exp->recv_avg_str, NUMBER_STR_LEN);
  145. exp->recv_total_rate = (total_recv - intrnl->last_recv_total)/delta;
  146. exp->recv_total_avg = total_recv/age;
  147. // application level statistics
  148. if (zconf.fsconf.app_success_index >= 0) {
  149. exp->app_success_rate = (app_success - intrnl->last_recv_app_success)/delta;
  150. number_string(exp->app_success_rate, exp->app_success_rate_str, NUMBER_STR_LEN);
  151. exp->app_success_avg = (app_success/age);
  152. number_string(exp->app_success_avg, exp->app_success_avg_str, NUMBER_STR_LEN);
  153. }
  154. if (!total_sent) {
  155. exp->hitrate = 0;
  156. exp->app_hitrate = 0;
  157. } else {
  158. exp->hitrate = recv_success*100.0/total_sent;
  159. exp->app_hitrate = app_success*100.0/total_sent;
  160. }
  161. if (age > WARMUP_PERIOD && exp->hitrate < zconf.min_hitrate) {
  162. if (!intrnl->min_hitrate_start) {
  163. intrnl->min_hitrate_start = cur_time;
  164. }
  165. } else {
  166. intrnl->min_hitrate_start = 0.0;
  167. }
  168. if (intrnl->min_hitrate_start) {
  169. exp->seconds_under_min_hitrate = cur_time - intrnl->min_hitrate_start;
  170. } else {
  171. exp->seconds_under_min_hitrate = 0;
  172. }
  173. if (!zsend.complete) {
  174. exp->send_rate = (total_sent - intrnl->last_sent)/delta;
  175. number_string(exp->send_rate, exp->send_rate_str, NUMBER_STR_LEN);
  176. exp->send_rate_avg = total_sent/age;
  177. number_string(exp->send_rate_avg, exp->send_rate_avg_str, NUMBER_STR_LEN);
  178. } else {
  179. exp->send_rate_avg = total_sent/(zsend.finish - zsend.start);
  180. number_string(exp->send_rate_avg, exp->send_rate_avg_str, NUMBER_STR_LEN);
  181. }
  182. // export other pre-calculated values
  183. exp->total_sent = total_sent;
  184. exp->percent_complete = 100.*age/(age + remaining_secs);
  185. exp->recv_success_unique = recv_success;
  186. exp->app_recv_success_unique = app_success;
  187. exp->total_recv = total_recv;
  188. exp->complete = zsend.complete;
  189. // pcap dropped packets
  190. exp->pcap_drop = zrecv.pcap_drop;
  191. exp->pcap_ifdrop = zrecv.pcap_ifdrop;
  192. exp->pcap_drop_total = exp->pcap_drop + exp->pcap_ifdrop;
  193. exp->pcap_drop_last = (exp->pcap_drop_total - intrnl->last_pcap_drop)/delta;
  194. exp->pcap_drop_avg = exp->pcap_drop_total/age;
  195. number_string(exp->pcap_drop_total, exp->pcap_drop_total_str, NUMBER_STR_LEN);
  196. number_string(exp->pcap_drop_last, exp->pcap_drop_last_str, NUMBER_STR_LEN);
  197. number_string(exp->pcap_drop_avg, exp->pcap_drop_avg_str, NUMBER_STR_LEN);
  198. exp->fail_total = zsend.sendto_failures;
  199. exp->fail_last = (exp->fail_total - intrnl->last_send_failures) / delta;
  200. exp->fail_avg = exp->fail_total/age;
  201. // misc
  202. exp->send_threads = iterator_get_curr_send_threads(it);
  203. // Update internal stats
  204. intrnl->last_now = cur_time;
  205. intrnl->last_sent = exp->total_sent;
  206. intrnl->last_recv_net_success = exp->recv_success_unique;
  207. intrnl->last_recv_app_success = exp->app_recv_success_unique;
  208. intrnl->last_pcap_drop = exp->pcap_drop_total;
  209. intrnl->last_send_failures = exp->fail_total;
  210. intrnl->last_recv_total = exp->total_recv;
  211. }
  212. static void log_drop_warnings(export_status_t *exp)
  213. {
  214. if (exp->pcap_drop_last/exp->recv_rate > 0.05) {
  215. log_warn("monitor", "Dropped %.0f packets in the last second, (%u total dropped (pcap: %u + iface: %u))",
  216. exp->pcap_drop_last, exp->pcap_drop_total, exp->pcap_drop, exp->pcap_ifdrop);
  217. }
  218. if (exp->fail_last/exp->send_rate > 0.01) {
  219. log_warn("monitor", "Failed to send %.0f packets/sec (%u total failures)",
  220. exp->fail_last, exp->fail_total);
  221. }
  222. }
  223. static void onscreen_appsuccess(export_status_t *exp)
  224. {
  225. // this when probe module handles application-level success rates
  226. if (!exp->complete) {
  227. fprintf(stderr,
  228. "%5s %0.0f%%%s; sent: %u %sp/s (%sp/s avg); "
  229. "recv: %u %sp/s (%sp/s avg); "
  230. "app success: %u %sp/s (%sp/s avg); "
  231. "drops: %sp/s (%sp/s avg); "
  232. "hitrate: %0.2f%% "
  233. "app hitrate: %0.2f%%\n",
  234. exp->time_past_str,
  235. exp->percent_complete,
  236. exp->time_remaining_str,
  237. exp->total_sent,
  238. exp->send_rate_str,
  239. exp->send_rate_avg_str,
  240. exp->recv_success_unique,
  241. exp->recv_rate_str,
  242. exp->recv_avg_str,
  243. exp->app_recv_success_unique,
  244. exp->app_success_rate_str,
  245. exp->app_success_avg_str,
  246. exp->pcap_drop_last_str,
  247. exp->pcap_drop_avg_str,
  248. exp->hitrate,
  249. exp->app_hitrate);
  250. } else {
  251. fprintf(stderr,
  252. "%5s %0.0f%%%s; sent: %u done (%sp/s avg); "
  253. "recv: %u %sp/s (%sp/s avg); "
  254. "app success: %u %sp/s (%sp/s avg); "
  255. "drops: %sp/s (%sp/s avg); "
  256. "hitrate: %0.2f%% "
  257. "app hitrate: %0.2f%%\n",
  258. exp->time_past_str,
  259. exp->percent_complete,
  260. exp->time_remaining_str,
  261. exp->total_sent,
  262. exp->send_rate_avg_str,
  263. exp->recv_success_unique,
  264. exp->recv_rate_str,
  265. exp->recv_avg_str,
  266. exp->app_recv_success_unique,
  267. exp->app_success_rate_str,
  268. exp->app_success_avg_str,
  269. exp->pcap_drop_last_str,
  270. exp->pcap_drop_avg_str,
  271. exp->hitrate,
  272. exp->app_hitrate);
  273. }
  274. }
  275. static void onscreen_generic(export_status_t *exp)
  276. {
  277. if (!exp->complete) {
  278. fprintf(stderr,
  279. "%5s %0.0f%%%s; send: %u %sp/s (%sp/s avg); "
  280. "recv: %u %sp/s (%sp/s avg); "
  281. "drops: %sp/s (%sp/s avg); "
  282. "hitrate: %0.2f%%\n",
  283. exp->time_past_str,
  284. exp->percent_complete,
  285. exp->time_remaining_str,
  286. exp->total_sent,
  287. exp->send_rate_str,
  288. exp->send_rate_avg_str,
  289. exp->recv_success_unique,
  290. exp->recv_rate_str,
  291. exp->recv_avg_str,
  292. exp->pcap_drop_last_str,
  293. exp->pcap_drop_avg_str,
  294. exp->hitrate);
  295. } else {
  296. fprintf(stderr,
  297. "%5s %0.0f%%%s; send: %u done (%sp/s avg); "
  298. "recv: %u %sp/s (%sp/s avg); "
  299. "drops: %sp/s (%sp/s avg); "
  300. "hitrate: %0.2f%%\n",
  301. exp->time_past_str,
  302. exp->percent_complete,
  303. exp->time_remaining_str,
  304. exp->total_sent,
  305. exp->send_rate_avg_str,
  306. exp->recv_success_unique,
  307. exp->recv_rate_str,
  308. exp->recv_avg_str,
  309. exp->pcap_drop_last_str,
  310. exp->pcap_drop_avg_str,
  311. exp->hitrate);
  312. }
  313. fflush(stderr);
  314. }
  315. static FILE* init_status_update_file(char *path)
  316. {
  317. FILE *f = fopen(path, "wb");
  318. if (!f) {
  319. log_fatal("csv", "could not open output file (%s)",
  320. zconf.status_updates_file);
  321. }
  322. log_trace("monitor", "status updates CSV will be saved to %s",
  323. zconf.status_updates_file);
  324. fprintf(f,
  325. "real-time,time-elapsed,time-remaining,"
  326. "percent-complete,active-send-threads,"
  327. "sent-total,sent-last-one-sec,sent-avg-per-sec,"
  328. "recv-success-total,recv-success-last-one-sec,recv-success-avg-per-sec,"
  329. "recv-total,recv-total-last-one-sec,recv-total-avg-per-sec,"
  330. "pcap-drop-total,drop-last-one-sec,drop-avg-per-sec,"
  331. "sendto-fail-total,sendto-fail-last-one-sec,sendto-fail-avg-per-sec\n"
  332. );
  333. fflush(f);
  334. return f;
  335. }
  336. static void update_status_updates_file(export_status_t *exp, FILE *f)
  337. {
  338. struct timeval now;
  339. char timestamp[256];
  340. gettimeofday(&now, NULL);
  341. time_t sec = now.tv_sec;
  342. struct tm* ptm = localtime(&sec);
  343. strftime(timestamp, 20, "%Y-%m-%d %H:%M:%S", ptm);
  344. fprintf(f,
  345. "%s,%u,%u,"
  346. "%f,%u,"
  347. "%u,%.0f,%.0f,"
  348. "%u,%.0f,%.0f,"
  349. "%u,%.0f,%.0f,"
  350. "%u,%.0f,%.0f,"
  351. "%u,%.0f,%.0f\n",
  352. timestamp, exp->time_past, exp->time_remaining,
  353. exp->percent_complete, exp->send_threads,
  354. exp->total_sent, exp->send_rate, exp->send_rate_avg,
  355. exp->recv_success_unique, exp->recv_rate, exp->recv_avg,
  356. exp->total_recv, exp->recv_total_rate, exp->recv_total_avg,
  357. exp->pcap_drop_total, exp->pcap_drop_last, exp->pcap_drop_avg,
  358. exp->fail_total, exp->fail_last, exp->fail_avg);
  359. fflush(f);
  360. }
  361. static inline void check_min_hitrate(export_status_t *exp)
  362. {
  363. if (exp->seconds_under_min_hitrate >= MIN_HITRATE_TIME_WINDOW) {
  364. log_fatal("monitor", "hitrate below %.0f for %.0f seconds. aborting scan.",
  365. zconf.min_hitrate, exp->seconds_under_min_hitrate);
  366. }
  367. }
  368. static inline void check_max_sendto_failures(export_status_t *exp)
  369. {
  370. if (zconf.max_sendto_failures >= 0 && exp->fail_total > (uint32_t) zconf.max_sendto_failures) {
  371. log_fatal("monitor", "maxiumum number of sendto failures (%i) exceeded",
  372. zconf.max_sendto_failures);
  373. }
  374. }
  375. void monitor_run(iterator_t *it, pthread_mutex_t *lock)
  376. {
  377. int_status_t *internal_status = xmalloc(sizeof(int_status_t));
  378. export_status_t *export_status = xmalloc(sizeof(export_status_t));
  379. FILE *f = NULL;
  380. if (zconf.status_updates_file) {
  381. f = init_status_update_file(zconf.status_updates_file);
  382. }
  383. while (!(zsend.complete && zrecv.complete)) {
  384. update_pcap_stats(lock);
  385. export_stats(internal_status, export_status, it);
  386. log_drop_warnings(export_status);
  387. check_min_hitrate(export_status);
  388. check_max_sendto_failures(export_status);
  389. if (!zconf.quiet) {
  390. lock_file(stderr);
  391. if (zconf.fsconf.app_success_index >= 0) {
  392. onscreen_appsuccess(export_status);
  393. } else {
  394. onscreen_generic(export_status);
  395. }
  396. unlock_file(stderr);
  397. }
  398. if (f) {
  399. update_status_updates_file(export_status, f);
  400. }
  401. sleep(UPDATE_INTERVAL);
  402. }
  403. if (!zconf.quiet) {
  404. lock_file(stderr);
  405. fflush(stderr);
  406. unlock_file(stderr);
  407. }
  408. if (f) {
  409. fflush(f);
  410. fclose(f);
  411. }
  412. }