1 /*
2 * Copyright (c) 2003, 2004 Niels Provos <provos@citi.umich.edu>
3 * All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 */
32
33 #include <sys/types.h>
34 #include <sys/param.h>
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <sys/stat.h>
41 #include <sys/queue.h>
42 #include <sys/tree.h>
43 #ifdef HAVE_SYS_TIME_H
44 #include <sys/time.h>
45 #endif
46
47 #include <ctype.h>
48 #include <err.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include <dnet.h>
54 #include <event.h>
55 #include <evdns.h>
56
57 #include "tagging.h"
58 #include "histogram.h"
59 #include "keycount.h"
60 #include "analyze.h"
61 #include "filter.h"
62
63 char *os_report_file = NULL;
64 char *port_report_file = NULL;
65 char *spammer_report_file = NULL;
66 char *country_report_file = NULL;
67
68 static int checkpoint_doreplay; /* externally set by honeydstats */
69 static struct event ev_analyze;
70
71 struct kctree oses;
72 struct kctree ports;
73 struct kctree spammers;
74 struct kctree countries;
75 struct kctree country_cache;
76
77 #define ROL64(x, b) (((x) << b) | ((x) >> (64 - b)))
78 #define ROR64(x, b) (((x) >> b) | ((x) << (64 - b)))
79
80 /*
81 * Thomas Wang's 64-bit hash function from
82 * www.concentric.net/~Ttwang/tech/inthash.htm
83 */
84 static __inline uint64_t
longhash1(uint64_t key)85 longhash1(uint64_t key)
86 {
87 key += ~(key << 32);
88 key ^= ROR64(key, 22);
89 key += ~(key << 13);
90 key ^= ROR64(key, 8);
91 key += (key << 3);
92 key ^= ROR64(key, 15);
93 key += ~(key << 27);
94 key ^= ROR64(key, 31);
95 return key;
96 }
97
98 static __inline uint32_t
port_hash(const struct addr * src,const struct addr * dst)99 port_hash(const struct addr *src, const struct addr *dst)
100 {
101 uint32_t a = src->addr_ip;
102 uint32_t b = dst->addr_ip;
103 return ((uint32_t)(longhash1(((uint64_t)a << 32) | b)));
104 }
105
106 void
port_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)107 port_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
108 {
109 if ((*pkey = calloc(1, sizeof(uint16_t))) == NULL)
110 err(1, "%s: calloc");
111 memcpy(*pkey, keycount->key, sizeof(uint16_t));
112 *pkeylen = sizeof(uint16_t);
113 }
114
115 char *
port_key_print(void * key,size_t keylen)116 port_key_print(void *key, size_t keylen)
117 {
118 static char sport[7];
119 snprintf(sport, sizeof(sport), "%d", *(uint16_t *)key);
120 return (sport);
121 }
122
123 void
spammer_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)124 spammer_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
125 {
126 if ((*pkey = calloc(1, keycount->keylen)) == NULL)
127 err(1, "%s: calloc");
128 memcpy(*pkey, keycount->key, keycount->keylen);
129 *pkeylen = keycount->keylen;
130 }
131
132 char *
spammer_key_print(void * key,size_t keylen)133 spammer_key_print(void *key, size_t keylen)
134 {
135 struct addr addr;
136 addr_pack(&addr, ADDR_TYPE_IP, IP_ADDR_BITS, key, IP_ADDR_LEN);
137 return (addr_ntoa(&addr));
138 }
139
140 void
country_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)141 country_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
142 {
143 if ((*pkey = calloc(1, keycount->keylen)) == NULL)
144 err(1, "%s: calloc");
145 memcpy(*pkey, keycount->key, keycount->keylen);
146 *pkeylen = keycount->keylen;
147 }
148
149 char *
country_key_print(void * key,size_t keylen)150 country_key_print(void *key, size_t keylen)
151 {
152 return (key);
153 }
154
155 struct aux {
156 SPLAY_HEAD(auxtree, auxkey) tree;
157 TAILQ_HEAD(auxq, auxkey) queue;
158 int limit;
159 int entries;
160 };
161
162 static int
aux_compare(struct auxkey * a,struct auxkey * b)163 aux_compare(struct auxkey *a, struct auxkey *b)
164 {
165 if (a->value < b->value)
166 return (-1);
167 if (a->value > b->value)
168 return (1);
169 return (0);
170 }
171
172 SPLAY_PROTOTYPE(auxtree, auxkey, node, aux_compare);
173 SPLAY_GENERATE(auxtree, auxkey, node, aux_compare);
174
175 void *
aux_create(void)176 aux_create(void)
177 {
178 struct aux *aux;
179
180 if ((aux = calloc(1, sizeof(struct aux))) == NULL)
181 err(1, "%s: calloc");
182 SPLAY_INIT(&aux->tree);
183 TAILQ_INIT(&aux->queue);
184 aux->limit = 100000; /* Make better at some point */
185
186 return (aux);
187 }
188
189 void
aux_free(void * arg)190 aux_free(void *arg)
191 {
192 struct aux *aux = arg;
193 struct auxtree *tree = &aux->tree;
194 struct auxkey *key;
195
196 while ((key = SPLAY_ROOT(tree)) != NULL) {
197 SPLAY_REMOVE(auxtree, tree, key);
198 free(key);
199 }
200 free(arg);
201 }
202
203 /* Returns one if the key is new */
204
205 int
aux_enter(struct aux * aux,uint32_t value)206 aux_enter(struct aux *aux, uint32_t value)
207 {
208 struct auxtree *tree = &aux->tree;
209 struct auxq *queue = &aux->queue;
210 struct auxkey tmp, *key;
211
212 tmp.value = value;
213 if ((key = SPLAY_FIND(auxtree, tree, &tmp)) != NULL) {
214 /* Mark this entry as recently used - LRU fashion */
215 TAILQ_REMOVE(queue, key, next);
216 TAILQ_INSERT_HEAD(queue, key, next);
217 return (0);
218 }
219
220 if (aux->entries >= aux->limit) {
221 key = TAILQ_LAST(queue, auxq);
222
223 /* The old entry has to go, bye bye */
224 SPLAY_REMOVE(auxtree, tree, key);
225 TAILQ_REMOVE(queue, key, next);
226 memset(key, 0, sizeof(struct auxkey));
227 aux->entries--;
228 } else {
229 if ((key = calloc(1, sizeof(struct auxkey))) == NULL)
230 err(1, "%s: calloc");
231 }
232 key->value = tmp.value;
233
234 /* Insert the new key */
235 SPLAY_INSERT(auxtree, tree, key);
236 TAILQ_INSERT_TAIL(queue, key, next);
237 aux->entries++;
238
239 return (1);
240 }
241
242 void
os_key_extract(struct keycount * keycount,void ** pkey,size_t * pkeylen)243 os_key_extract(struct keycount *keycount, void **pkey, size_t *pkeylen)
244 {
245 const char *key = keycount->key;
246
247 if ((*pkey = strdup(key)) == NULL)
248 err(1, "%s: strdup");
249 *pkeylen = strlen(key) + 1;
250 }
251
252 char *
os_key_print(void * key,size_t keylen)253 os_key_print(void *key, size_t keylen)
254 {
255 return (key);
256 }
257
258 static int
report_compare(struct report * a,struct report * b)259 report_compare(struct report *a, struct report *b)
260 {
261 return (key_compare(a->key, a->keylen, b->key, b->keylen));
262 }
263
264 SPLAY_HEAD(reporttree, report);
265 SPLAY_PROTOTYPE(reporttree, report, node, report_compare);
266 SPLAY_GENERATE(reporttree, report, node, report_compare);
267
268 void
analyze_init(void)269 analyze_init(void)
270 {
271 struct timeval tv;
272
273 evtimer_set(&ev_analyze, analyze_report_cb, &ev_analyze);
274
275 timerclear(&tv);
276 tv.tv_sec = ANALYZE_REPORT_INTERVAL;
277 evtimer_add(&ev_analyze, &tv);
278
279 SPLAY_INIT(&oses);
280 SPLAY_INIT(&ports);
281 SPLAY_INIT(&spammers);
282 SPLAY_INIT(&countries);
283 SPLAY_INIT(&country_cache);
284
285 evdns_init();
286 }
287
288 void
analyze_set_checkpoint_doreplay(int doit)289 analyze_set_checkpoint_doreplay(int doit)
290 {
291 checkpoint_doreplay = doit;
292 }
293
294 void
analyze_record(const struct record * record)295 analyze_record(const struct record *record)
296 {
297 if (record->dst_port == 25 && record->bytes != 0)
298 analyze_spammer_enter(&record->src, record->bytes);
299
300 /*
301 * Records may be duplicated if they carry extra payload hashes.
302 * In those cases, we need to ignore the connection information.
303 * We also want to ignore connection information for connections
304 * that were generated by the honeypots.
305 */
306 if ((record->state & RECORD_STATE_NEW) == 0 ||
307 (record->flags & REC_FLAG_LOCAL) != 0)
308 return;
309
310 /* Enter OS fingerprint */
311 if (record->os_fp == NULL)
312 analyze_os_enter(&record->src, "unknown");
313 else
314 analyze_os_enter(&record->src, record->os_fp);
315
316 /* Enter Port Analysis */
317 analyze_port_enter(record->dst_port, &record->src, &record->dst);
318
319 /* Entry country analysis */
320 analyze_country_enter(&record->src, &record->dst);
321 }
322
323 void
analyze_spammer_enter(const struct addr * src,uint32_t bytes)324 analyze_spammer_enter(const struct addr *src, uint32_t bytes)
325 {
326 struct keycount tmpkey, *key;
327
328 tmpkey.key = &src->addr_ip;
329 tmpkey.keylen = sizeof(src->addr_ip);
330
331 if ((key = SPLAY_FIND(kctree, &spammers, &tmpkey)) == NULL) {
332 key = keycount_new(&src->addr_ip, sizeof(src->addr_ip),
333 NULL, NULL);
334 SPLAY_INSERT(kctree, &spammers, key);
335 }
336
337 count_increment(key->count, bytes);
338 }
339
340 struct country_state {
341 struct addr src;
342 struct addr dst;
343 int result_from_cache;
344 };
345
346 void
analyze_country_enter_cb(int result,char type,int count,int ttl,void * addresses,void * arg)347 analyze_country_enter_cb(int result, char type, int count, int ttl,
348 void *addresses, void *arg)
349 {
350 struct country_state *state = arg;
351 struct addr *src = &state->src;
352 struct keycount tmpkey, *key;
353 char tld[20];
354
355 if (result != DNS_ERR_NONE || count != 1 || type != DNS_PTR) {
356 /* Enter into our negative cache */
357 if (!state->result_from_cache) {
358 tmpkey.key = &src->addr_ip;
359 tmpkey.keylen = sizeof(src->addr_ip);
360 key = SPLAY_FIND(kctree, &country_cache, &tmpkey);
361 if (!key) {
362 key = keycount_new(
363 &src->addr_ip,
364 sizeof(src->addr_ip),
365 NULL, NULL);
366 SPLAY_INSERT(kctree, &country_cache, key);
367 }
368 count_increment(key->count, 1);
369 }
370
371 strlcpy(tld, "unknown", sizeof(tld));
372 } else {
373 const char *hostname = *(char **)addresses;
374 int i;
375 /* Extract the country key */
376 for (i = strlen(hostname) - 1; i >= 0; --i) {
377 if (hostname[i] == '.') {
378 i += 1;
379 break;
380 }
381 }
382
383 strlcpy(tld, hostname + i, sizeof(tld));
384 for (i = 0; i < strlen(tld); i++) {
385 if (isdigit(tld[i])) {
386 strlcpy(tld, "unknown", sizeof(tld));
387 break;
388 }
389 tld[i] = tolower(tld[i]);
390 }
391 }
392
393 tmpkey.key = tld;
394 tmpkey.keylen = strlen(tmpkey.key) + 1;
395 if ((key = SPLAY_FIND(kctree, &countries, &tmpkey)) == NULL) {
396 key = keycount_new(tld, strlen(tld) + 1, aux_create, aux_free);
397 SPLAY_INSERT(kctree, &countries, key);
398 }
399
400 /* If the address is new, we are going to resolve it */
401 if (aux_enter(key->auxilary, port_hash(&state->src, &state->dst)))
402 count_increment(key->count, 1);
403 free(state);
404 }
405
406 void
analyze_country_enter(const struct addr * addr,const struct addr * dst)407 analyze_country_enter(const struct addr *addr, const struct addr *dst)
408 {
409 struct keycount tmpkey, *key;
410
411 struct country_state *state = calloc(1, sizeof(struct country_state));
412 if (state == NULL)
413 err(1, "%s: calloc", __func__);
414
415 state->src = *addr;
416 state->dst = *dst;
417
418 /*
419 * Check if this IP returned a resolver error in the last hour.
420 */
421 tmpkey.key = &addr->addr_ip;
422 tmpkey.keylen = sizeof(addr->addr_ip);
423 if ((key = SPLAY_FIND(kctree, &country_cache, &tmpkey)) != NULL) {
424 if (count_get_minute(key->count) ||
425 count_get_hour(key->count)) {
426 state->result_from_cache = 1;
427 analyze_country_enter_cb(DNS_ERR_NOTEXIST, DNS_PTR,
428 0, 0, NULL, state);
429 return;
430 }
431 }
432
433 if (!checkpoint_doreplay) {
434 struct in_addr in;
435 in.s_addr = addr->addr_ip;
436 evdns_resolve_reverse(&in, 0, analyze_country_enter_cb, state);
437 } else {
438 /*
439 * If we are replaying a checkpoint, we do not want to do
440 * async calls.
441 */
442 struct hostent *hp = gethostbyaddr(
443 (const char *)&addr->addr_ip, IP_ADDR_LEN, AF_INET);
444 if (hp == NULL) {
445 analyze_country_enter_cb(DNS_ERR_NOTEXIST, DNS_PTR,
446 0, 0, NULL, state);
447 } else {
448 analyze_country_enter_cb(DNS_ERR_NONE, DNS_PTR,
449 1, 1200 /* ttl */, (void *)&hp->h_name, state);
450 }
451 }
452 }
453
454 void
analyze_os_enter(const struct addr * addr,const char * osfp)455 analyze_os_enter(const struct addr *addr, const char *osfp)
456 {
457 struct keycount tmpkey, *key;
458
459 tmpkey.key = osfp;
460 tmpkey.keylen = strlen(osfp) + 1;
461
462 if ((key = SPLAY_FIND(kctree, &oses, &tmpkey)) == NULL) {
463 key = keycount_new(osfp, strlen(osfp) + 1,
464 aux_create, aux_free);
465 SPLAY_INSERT(kctree, &oses, key);
466 }
467
468 /* If the address is new, we are going to increase the counter */
469 if (aux_enter(key->auxilary, addr->addr_ip))
470 count_increment(key->count, 1);
471 }
472
473 void
analyze_port_enter(uint16_t port,const struct addr * src,const struct addr * dst)474 analyze_port_enter(uint16_t port,
475 const struct addr *src, const struct addr *dst)
476 {
477 struct keycount tmpkey, *key;
478
479 tmpkey.key = &port;
480 tmpkey.keylen = sizeof(port);
481
482 if ((key = SPLAY_FIND(kctree, &ports, &tmpkey)) == NULL) {
483 key = keycount_new(&port, sizeof(port),
484 aux_create, aux_free);
485 SPLAY_INSERT(kctree, &ports, key);
486 }
487
488 /* If the address is new, we are going to increase the counter */
489 if (aux_enter(key->auxilary, port_hash(src, dst)))
490 count_increment(key->count, 1);
491 }
492
493 void
report_to_file(struct reporttree * tree,char * filename,char * (* print)(void *,size_t))494 report_to_file(struct reporttree *tree, char *filename,
495 char *(*print)(void *, size_t))
496 {
497 static char line[1024];
498 FILE *fout;
499
500 /* We do not create report files while we are replaying a checkpoint */
501 if (checkpoint_doreplay)
502 return;
503
504 /* create a temporary file */
505 strlcpy(line, filename, sizeof(line));
506 strlcat(line, ".tmp", sizeof(line));
507
508 if ((fout = fopen(line, "w")) != NULL) {
509 report_print(tree, fout, print);
510 fclose(fout);
511 /* This is an atomic operation */
512 rename(line, filename);
513 chmod(filename, S_IRWXU | S_IRGRP | S_IROTH);
514 } else {
515 warn("%s: fopen('%s')", __func__, line);
516 }
517 }
518
519 void
report_print(struct reporttree * tree,FILE * out,char * (* print)(void *,size_t))520 report_print(struct reporttree *tree, FILE *out,
521 char *(*print)(void *, size_t))
522 {
523 struct report *report;
524
525 /* Now print the information in alphabetical order */
526 SPLAY_FOREACH(report, reporttree, tree) {
527 fprintf(out, "%25s: %7d %7d %7d\n",
528 print(report->key, report->keylen),
529 report->minute, report->hour, report->day);
530 }
531 }
532
533 void
report_free(struct reporttree * tree)534 report_free(struct reporttree *tree)
535 {
536 struct report *report;
537
538 /* And then free the whole tree */
539 while ((report = SPLAY_ROOT(tree)) != NULL) {
540 SPLAY_REMOVE(reporttree, tree, report);
541
542 free(report->key);
543 free(report);
544 }
545 free(tree);
546 }
547
548 struct reporttree *
report_create(struct kctree * kctree,void (* extract)(struct keycount *,void **,size_t *))549 report_create(struct kctree *kctree,
550 void (*extract)(struct keycount *, void **, size_t *))
551 {
552 struct reporttree *tree;
553 struct report *report;
554 struct keycount *kc, *next;
555
556 if ((tree = calloc(1, sizeof(struct reporttree))) == NULL)
557 err(1, "%s: calloc", __func__);
558
559 SPLAY_INIT(tree);
560
561 for (kc = SPLAY_MIN(kctree, kctree); kc != NULL; kc = next) {
562 struct report tmp;
563 uint32_t sum = 0;
564
565 next = SPLAY_NEXT(kctree, kctree, kc);
566
567 (*extract)(kc, &tmp.key, &tmp.keylen);
568 if ((report = SPLAY_FIND(reporttree, tree, &tmp)) == NULL) {
569 report = calloc(1, sizeof(struct report));
570 if (report == NULL)
571 err(1, "%s: calloc");
572 report->key = tmp.key;
573 report->keylen = tmp.keylen;
574 SPLAY_INSERT(reporttree, tree, report);
575 }
576
577 /* Now get the data together */
578 if ((sum = count_get_minute(kc->count)) != 0)
579 report->minute += sum;
580 if ((sum += count_get_hour(kc->count)) != 0)
581 report->hour += sum;
582 if ((sum += count_get_day(kc->count)) != 0)
583 report->day += sum;
584
585 if (!sum) {
586 SPLAY_REMOVE(kctree, kctree, kc);
587 keycount_free(kc);
588 }
589 }
590
591 return (tree);
592 }
593
594 void
make_report(struct kctree * kctree,char * filename,void (* extract)(struct keycount *,void **,size_t *),char * (* print)(void *,size_t))595 make_report(struct kctree *kctree, char *filename,
596 void (*extract)(struct keycount *, void **, size_t *),
597 char *(*print)(void *, size_t))
598 {
599 struct reporttree *tree = report_create(kctree, extract);
600
601 report_print(tree, stderr, print);
602
603 if (filename != NULL)
604 report_to_file(tree, filename, print);
605
606 report_free(tree);
607 }
608
609 struct filterarg {
610 struct reporttree *src;
611 struct reporttree *dst;
612 };
613
614 void
analyze_filter_cb(void * reparg,void * treearg)615 analyze_filter_cb(void *reparg, void *treearg)
616 {
617 struct report *report = reparg;
618 struct filterarg *fa = treearg;
619
620 if (SPLAY_FIND(reporttree, fa->dst, report) != NULL)
621 return;
622
623 SPLAY_REMOVE(reporttree, fa->src, report);
624 SPLAY_INSERT(reporttree, fa->dst, report);
625 }
626
627 void
analyze_print_port_report()628 analyze_print_port_report()
629 {
630 struct reporttree *tree, *filtered_tree;
631 struct filtertree *min_filters, *hour_filters, *day_filters;
632 struct report *report;
633 struct filterarg fa;
634
635 tree = report_create(&ports, port_key_extract);
636
637 /* Filter trees for Minutes, Hours and Days */
638 min_filters = filter_create();
639 hour_filters = filter_create();
640 day_filters = filter_create();
641 SPLAY_FOREACH(report, reporttree, tree) {
642 filter_insert(min_filters, report->minute, report);
643 filter_insert(hour_filters, report->hour, report);
644 filter_insert(day_filters, report->day, report);
645 }
646
647 if ((filtered_tree = calloc(1, sizeof(struct reporttree))) == NULL)
648 err(1, "%s: calloc");
649 SPLAY_INIT(filtered_tree);
650
651 /*
652 * Object passed to the call back function to merge the different
653 * filter trees.
654 */
655 fa.src = tree;
656 fa.dst = filtered_tree;
657
658 filter_top(min_filters, 5, analyze_filter_cb, &fa);
659 filter_top(hour_filters,10, analyze_filter_cb, &fa);
660 filter_top(day_filters, 15, analyze_filter_cb, &fa);
661
662 filter_free(min_filters);
663 filter_free(hour_filters);
664 filter_free(day_filters);
665 report_free(tree);
666
667 fprintf(stderr, "Destination Port Statistics\n");
668 report_print(filtered_tree, stderr, port_key_print);
669
670 if (port_report_file != NULL)
671 report_to_file(filtered_tree, port_report_file,
672 port_key_print);
673
674 report_free(filtered_tree);
675 }
676
677 void
analyze_print_spammer_report()678 analyze_print_spammer_report()
679 {
680 struct reporttree *tree, *filtered_tree;
681 struct filtertree *min_filters, *hour_filters, *day_filters;
682 struct report *report;
683 struct filterarg fa;
684
685 tree = report_create(&spammers, spammer_key_extract);
686
687 /* Filter trees for Minutes, Hours and Days */
688 min_filters = filter_create();
689 hour_filters = filter_create();
690 day_filters = filter_create();
691 SPLAY_FOREACH(report, reporttree, tree) {
692 filter_insert(min_filters, report->minute, report);
693 filter_insert(hour_filters, report->hour, report);
694 filter_insert(day_filters, report->day, report);
695 }
696
697 if ((filtered_tree = calloc(1, sizeof(struct reporttree))) == NULL)
698 err(1, "%s: calloc");
699 SPLAY_INIT(filtered_tree);
700
701 /*
702 * Object passed to the call back function to merge the different
703 * filter trees.
704 */
705 fa.src = tree;
706 fa.dst = filtered_tree;
707
708 filter_top(min_filters, 5, analyze_filter_cb, &fa);
709 filter_top(hour_filters,10, analyze_filter_cb, &fa);
710 filter_top(day_filters, 20, analyze_filter_cb, &fa);
711
712 filter_free(min_filters);
713 filter_free(hour_filters);
714 filter_free(day_filters);
715 report_free(tree);
716
717 fprintf(stderr, "Spammer Address Statistics\n");
718 report_print(filtered_tree, stderr, spammer_key_print);
719
720 if (spammer_report_file != NULL)
721 report_to_file(filtered_tree, spammer_report_file,
722 spammer_key_print);
723
724 report_free(filtered_tree);
725 }
726
727 void
analyze_print_country_report()728 analyze_print_country_report()
729 {
730 struct reporttree *tree, *filtered_tree;
731 struct filtertree *min_filters, *hour_filters, *day_filters;
732 struct report *report;
733 struct filterarg fa;
734
735 tree = report_create(&countries, country_key_extract);
736
737 /* Filter trees for Minutes, Hours and Days */
738 min_filters = filter_create();
739 hour_filters = filter_create();
740 day_filters = filter_create();
741 SPLAY_FOREACH(report, reporttree, tree) {
742 filter_insert(min_filters, report->minute, report);
743 filter_insert(hour_filters, report->hour, report);
744 filter_insert(day_filters, report->day, report);
745 }
746
747 if ((filtered_tree = calloc(1, sizeof(struct reporttree))) == NULL)
748 err(1, "%s: calloc");
749 SPLAY_INIT(filtered_tree);
750
751 /*
752 * Object passed to the call back function to merge the different
753 * filter trees.
754 */
755 fa.src = tree;
756 fa.dst = filtered_tree;
757
758 filter_top(min_filters, 5, analyze_filter_cb, &fa);
759 filter_top(hour_filters,10, analyze_filter_cb, &fa);
760 filter_top(day_filters, 20, analyze_filter_cb, &fa);
761
762 filter_free(min_filters);
763 filter_free(hour_filters);
764 filter_free(day_filters);
765 report_free(tree);
766
767 fprintf(stderr, "Country Activity Statistics\n");
768 report_print(filtered_tree, stderr, country_key_print);
769
770 if (country_report_file != NULL)
771 report_to_file(filtered_tree, country_report_file,
772 country_key_print);
773
774 report_free(filtered_tree);
775 }
776
777 void
analyze_print_report()778 analyze_print_report()
779 {
780 fprintf(stderr, "Operating System Statistics\n");
781 make_report(&oses, os_report_file, os_key_extract, os_key_print);
782
783 analyze_print_port_report();
784 analyze_print_spammer_report();
785 analyze_print_country_report();
786 }
787
788 void
analyze_report_cb(int fd,short what,void * arg)789 analyze_report_cb(int fd, short what, void *arg)
790 {
791 struct event *ev = arg;
792 struct timeval tv;
793
794 timerclear(&tv);
795 tv.tv_sec = ANALYZE_REPORT_INTERVAL;
796 evtimer_add(ev, &tv);
797
798 analyze_print_report();
799 }
800
801 #define OS_NUM_OSES 12
802
803 void
os_test()804 os_test()
805 {
806 char *fingerprints[OS_NUM_OSES] = {
807 "Linux",
808 "Windows",
809 "NetBSD",
810 "OpenBSD",
811 "Windows",
812 "Windows",
813 "Linux",
814 "Windows",
815 "Linux",
816 "OpenBSD",
817 "FreeBSD",
818 "unknown"
819 };
820 struct addr src;
821 struct timeval tv;
822 rand_t *rand = rand_open();
823 int i, j;
824
825 gettimeofday(&tv, NULL);
826 count_set_time(&tv);
827
828 addr_pton("127.0.0.1", &src);
829 for (i = 0; i < 24 * 60 * 2 + 60 * 4; i++) {
830 for (j = 0; i < 24 * 60 * 2 &&
831 j < rand_uint8(rand) % 50000 + 5000; j++) {
832 src.addr_ip = rand_uint32(rand);
833 analyze_os_enter(&src,
834 fingerprints[rand_uint8(rand) % OS_NUM_OSES]);
835 }
836 tv.tv_sec += 30;
837
838 if (i % 120 == 0) {
839 fprintf(stderr, "%ld:\n", tv.tv_sec);
840 make_report(&oses, NULL, os_key_extract, os_key_print);
841 }
842 }
843
844 if (SPLAY_ROOT(&oses) != NULL)
845 errx(1, "oses fingerprints should have been purged");
846
847 count_set_time(NULL);
848
849 rand_close(rand);
850 fprintf(stderr, "\t%s: OK\n", __func__);
851 }
852
853 void
analyze_test(void)854 analyze_test(void)
855 {
856 os_test();
857 }
858