1 /*
2 * Copyright (c) 2001 Mark Fullmer and The Ohio State University
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $Id: flow-dscan.c,v 1.29 2005/05/10 15:53:11 maf Exp $
27 */
28
29 #include "ftconfig.h"
30
31 #if HAVE_INTTYPES_H
32 # include <inttypes.h> /* C99 uint8_t uint16_t uint32_t uint64_t */
33 #elif HAVE_STDINT_H
34 # include <stdint.h> /* or here */
35 #endif /* else commit suicide. later */
36
37 #include <ftlib.h>
38
39 #include <sys/time.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/uio.h>
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <time.h>
47 #include <fcntl.h>
48 #include <signal.h>
49 #include <syslog.h>
50
51 #if HAVE_STRINGS_H
52 #include <strings.h>
53 #endif
54
55 #if HAVE_STRING_H
56 #include <string.h>
57 #endif
58
59 #include "flow-dscan.h"
60
61 /*
62 * Detect network scan's
63 */
64
65 /* XXX TODO
66 *
67 * trigger alarm on # of flows/second
68 *
69 * need to max out on # of records allocated. If hit, then run the ager
70 * more often/more agressive timeout.
71 *
72 * FUTURE:
73 * take advantage of need to only allocate strcuture of the same
74 * size by having malloc allocate chunks at a time and maintaining
75 * a freelist.
76 *
77 * most of the dst ports are 53 and 80. Could save a bunch of memory
78 * by only allocating a bit1024 struct when it's not a list of well
79 * known ports -- tradoff for a few cpu cycles..
80 *
81 * possibly trigger the ager with an alarm instead of by flow traffic
82 *
83 * if the dst ip list gets too long convert to a hash?
84 */
85
86 int debug;
87 int sig_hup, sig_usr1;
88
89 int load_state(struct dscan_state *ds);
90 void flow_dump(struct fts3rec_gen *rec);
91 void clear_suppress(struct dscan_state *ds, int sd);
92 int load_suppress(struct dscan_state *ds, int sd);
93 void ager(struct dscan_state *ds, uint32_t total_flows32);
94 void sig_hup_handler(int sig);
95 void sig_usr1_handler(int sig);
96 void usage(void);
97 int dump_state(struct dscan_state *ds);
98
snprintf_time(char * buffer,struct tm * tm,uint32_t msecs)99 static void snprintf_time(char* buffer, struct tm* tm,
100 uint32_t msecs) {
101 snprintf(buffer, 64, "%-2.2d%-2.2d.%-2.2d:%-2.2d:%-2.2d.%-3" PRIu32 " ",
102 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
103 msecs);
104 }
105
106
main(int argc,char ** argv)107 int main(int argc, char **argv)
108 {
109 struct ftio ftio;
110 struct ftprof ftp;
111 struct fts3rec_gen *rec;
112 struct fttime ftt;
113 int i, match, k, no_detach;
114 uint64_t total_flows;
115 uint32_t total_flows32;
116 struct dscan_state ds;
117 struct dscan_rec *drp;
118 struct dscan_dst *ddp, *ddp2;
119 struct dscan_sup *dsp_src, *dsp_dst;
120 uint32_t hash;
121 char fmt_buf1[64], fmt_buf2[64], fmt_buf3[64], fmt_buf4[64];
122 int do_dump, do_load;
123 int filter_www, filter_mcast, filter_input, filter_output;
124 char in_tbl[65536], out_tbl[65536];
125 uint32_t trigger_time, trigger_packets, trigger_octets;
126 pid_t pid;
127 struct tm *tm;
128
129 total_flows = total_flows32 = 0;
130 bzero(&ds, sizeof ds);
131 ds.ager_timeout = DSCAN_AGER_TIMEOUT;
132 ds.statefile = DSCAN_STATEFILE;
133 ds.supfile = DSCAN_SUP_FILE;
134 ds.dscan_ip_depth = DSCAN_IP_DEPTH;
135 ds.dscan_port_trigger = DSCAN_PORT_TRIGGER;
136 sig_hup = 0;
137 sig_usr1 = 0;
138 do_dump = 0;
139 do_load = 0;
140 filter_www = filter_mcast = filter_input = filter_output = 0;
141 trigger_time = trigger_octets = trigger_packets = 0;
142 no_detach = 0;
143
144 /* init fterr */
145 fterr_setid(argv[0]);
146
147 while ((i = getopt(argc, argv, "d:i:I:D:h?lmps:t:wL:O:P:S:T:bBW")) != -1)
148
149 switch (i) {
150
151 case 'd': /* debug */
152 debug = atoi(optarg);
153 break;
154
155 case 'i': /* input filter interface list */
156
157 if (load_lookup(optarg, 65536, in_tbl))
158 fterr_errx(1, "load_lookup(): failed");
159
160 filter_input = 1;
161 break;
162
163 case 'l': /* load state */
164 do_load = 1;
165 break;
166
167 case 'm': /* multicast filter */
168 filter_mcast = 1;
169 break;
170
171 case 'I': /* output filter interface list */
172
173 if (load_lookup(optarg, 65536, out_tbl))
174 fterr_errx(1, "load_lookup(): failed");
175
176 filter_output = 1;
177 break;
178
179 case 'p': /* dump state on exit */
180 do_dump = 1;
181 break;
182
183 case 's': /* statefile */
184 ds.statefile = optarg;
185 break;
186
187 case 't': /* ager timeout */
188 sscanf(optarg, "%u", &ds.ager_timeout);
189 break;
190
191 case 'w': /* filter www inbound (replies) */
192 filter_www |= 1;
193 break;
194
195 case 'b': /* no detach */
196 no_detach = 1;
197 break;
198
199 case 'B': /* no detach, use syslog */
200 no_detach = 2;
201 break;
202
203 case 'D':
204 sscanf(optarg, "%u", &ds.dscan_ip_depth);
205 break;
206
207 case 'L': /* suppress file */
208 ds.supfile = optarg;
209 break;
210
211 case 'O': /* excessive octets trigger */
212 sscanf(optarg, "%u", (unsigned int*)&trigger_octets);
213 break;
214
215 case 'P': /* excessive packets trigger */
216 sscanf(optarg, "%u", (unsigned int*)&trigger_packets);
217 break;
218
219 case 'S': /* port scan trigger */
220 sscanf(optarg, "%u", &ds.dscan_port_trigger);
221 break;
222
223 case 'T': /* excessive flow time trigger */
224 sscanf(optarg, "%u", (unsigned int*)&trigger_time);
225 break;
226
227 case 'W': /* filter www outbound */
228 filter_www |= 2;
229 break;
230
231 case 'h': /* help */
232 case '?':
233 default:
234 usage();
235 exit (1);
236 break;
237 } /* switch */
238
239 if (argc - optind)
240 fterr_errx(1, "Extra arguments starting with %s.", argv[optind]);
241
242 /* daemonize */
243 if (!no_detach) {
244 if ((pid = fork()) == -1) {
245 fterr_err(1, "fork()");
246 } else if (pid)
247 exit (0); /* parent */
248
249 umask(0022);
250 setsid();
251 for (i = 1; i < 16; ++i) /* XXX dynamically get NOFILE */
252 close (i);
253
254 /* enable syslog */
255 fterr_setsyslog(1, LOG_PID|LOG_NDELAY, LOG_LOCAL6);
256
257 /* disable stderr */
258 fterr_setfile(0, (void*)0L);
259
260 } else {
261 if (no_detach == 2) {
262 /* enable syslog */
263 fterr_setsyslog(1, LOG_PID|LOG_NDELAY, LOG_LOCAL6);
264
265 /* disable stderr */
266 fterr_setfile(0, (void*)0L);
267 }
268 }
269
270 /* profile */
271 ftprof_start (&ftp);
272
273 /* configure handlers */
274 if (mysignal(SIGHUP, sig_hup_handler) == SIG_ERR)
275 fterr_err(1, "signal(SIGHUP)");
276
277 if (mysignal(SIGUSR1, sig_usr1_handler) == SIG_ERR)
278 fterr_err(1, "signal(SIGUSR1)");
279
280 /* initialize scanner hash table */
281 for (i = 0; i < DSCAN_HASHSIZE; ++i) {
282 FT_SLIST_INIT(&ds.hash_scan[i]);
283 }
284
285 /* initialize suppress hash table */
286 for (i = 0; i < DSCAN_HASHSIZE; ++i) {
287 FT_SLIST_INIT(&ds.hash_sup_src[i]);
288 }
289
290 for (i = 0; i < DSCAN_HASHSIZE; ++i) {
291 FT_SLIST_INIT(&ds.hash_sup_dst[i]);
292 }
293
294 /* load state file */
295 if (do_load) {
296
297 if (load_state(&ds))
298 fterr_errx(1, "load_state(): failed");
299
300 #ifdef XXX
301 ds.statefile = "/var/tmp/ds.state2";
302 dump_state(&ds);
303 exit(0);
304 #endif
305
306 }
307
308 if (load_suppress(&ds, 0))
309 fterr_errx(1, "load_suppress(0): failed");
310
311 if (load_suppress(&ds, 1))
312 fterr_errx(1, "load_suppress(1): failed");
313
314 /* read from stdin */
315 if (ftio_init(&ftio, 0, FT_IO_FLAG_READ) < 0)
316 fterr_errx(1, "ftio_init(): failed");
317
318 /* check for records scan can understand */
319 if (ftio_check_generic(&ftio) < 0)
320 fterr_errx(1, "Unsupported export version");
321
322 while ((rec = ftio_read(&ftio))) {
323
324 if (sig_usr1) {
325 if (debug)
326 fterr_info("signal: USR1");
327 dump_state(&ds);
328 sig_usr1 = 0;
329 } /* sig_usr1 */
330
331 if (sig_hup) {
332 if (debug)
333 fterr_info("signal: USR1");
334 clear_suppress(&ds, 0);
335 clear_suppress(&ds, 1);
336 load_suppress(&ds, 0);
337 load_suppress(&ds, 1);
338 sig_hup = 0;
339 } /* sig_hup */
340
341 ++total_flows;
342 ++total_flows32;
343
344 /* filter on input interface */
345 if (filter_input)
346 if (!in_tbl[rec->input])
347 goto skip2;
348
349 /* filter on output interface */
350 if (filter_output)
351 if (!out_tbl[rec->output])
352 goto skip2;
353
354 /* filter multicast? */
355 if (filter_mcast) {
356
357 if ((rec->srcaddr >= 0xe0000000) && (rec->srcaddr <= 0xefffffff))
358 goto skip2;
359
360 if ((rec->dstaddr >= 0xe0000000) && (rec->dstaddr <= 0xefffffff))
361 goto skip2;
362
363 } /* filter_mcast */
364
365 /* calculate hash based on src ip */
366 hash = DSCAN_HASHFUNC(rec->srcaddr);
367
368 if ((filter_www & 1) && (rec->srcport == 80) && (rec->prot == 6) &&
369 (rec->dstport > 1023))
370 goto skip2;
371
372 if ((filter_www & 2) && (rec->dstport == 80) && (rec->prot == 6) &&
373 (rec->srcport > 1023))
374 goto skip2;
375
376 /* evaluate this src ip and the suppress list */
377 match = 0;
378 FT_SLIST_FOREACH(dsp_src, &ds.hash_sup_src[hash], chain) {
379 if (dsp_src->ip == rec->srcaddr) {
380 if (!(dsp_src->flags & DSCAN_SUP_SRCPORT))
381 goto skipsrc;
382 if (dsp_src->srcport != rec->srcport)
383 goto sup1;
384 skipsrc:
385 if (!(dsp_src->flags & DSCAN_SUP_DSTPORT))
386 goto skipdst;
387 if (dsp_src->dstport != rec->dstport)
388 goto sup1;
389 skipdst:
390 if (!(dsp_src->flags & DSCAN_SUP_PROTOCOL))
391 goto skip2;
392 if (dsp_src->protocol != rec->prot)
393 goto sup1;
394 goto skip2;
395
396 }
397 }
398
399 sup1:
400
401 /* evaluate this dst ip and the suppress list */
402 match = 0;
403 FT_SLIST_FOREACH(dsp_dst, &ds.hash_sup_dst[hash], chain) {
404 if (dsp_dst->ip == rec->dstaddr) {
405 if (!(dsp_dst->flags & DSCAN_SUP_SRCPORT))
406 goto skipsrc1;
407 if (dsp_dst->srcport != rec->srcport)
408 goto sup;
409 skipsrc1:
410 if (!(dsp_dst->flags & DSCAN_SUP_DSTPORT))
411 goto skipdst1;
412 if (dsp_dst->dstport != rec->dstport)
413 goto sup;
414 skipdst1:
415 if (!(dsp_dst->flags & DSCAN_SUP_PROTOCOL))
416 goto skip2;
417 if (dsp_dst->protocol != rec->prot)
418 goto sup;
419 goto skip2;
420
421 }
422 }
423
424 sup:
425
426 /* evaluate the triggers */
427 if (trigger_octets && (rec->dOctets > trigger_octets)) {
428 flow_dump(rec);
429 }
430
431 if (trigger_packets && (rec->dPkts > trigger_packets)) {
432 flow_dump(rec);
433 }
434
435 if (trigger_time && ((rec->Last - rec->First) > trigger_time)) {
436 flow_dump(rec);
437 }
438
439 match = 0;
440 FT_SLIST_FOREACH(drp, &ds.hash_scan[hash], chain) {
441 if (drp->ip_src == rec->srcaddr) {
442 match = 1;
443 break;
444 }
445 }
446
447 if (match) { /* src ip match */
448
449
450 /* previous host or scan report then skip */
451 if (drp->flags)
452 goto skip2;
453
454 /* if rec exists with this dest ip */
455 FT_STAILQ_FOREACH(ddp, &drp->dlhead, chain) {
456 if (ddp->ip_dst == rec->dstaddr) {
457 ddp->ip_time = total_flows32;
458 if (rec->dstport < 1024) {
459
460 bit1024_store((int)rec->dstport, &ddp->portmap);
461
462 if ((k = bit1024_count(&ddp->portmap)) >= ds.dscan_port_trigger) {
463
464 fmt_ipv4(fmt_buf1, rec->srcaddr, FMT_JUST_LEFT);
465 fmt_ipv4(fmt_buf2, rec->dstaddr, FMT_JUST_LEFT);
466
467 ftt = ftltime(rec->sysUpTime, rec->unix_secs, rec->unix_nsecs,
468 rec->First);
469 tm = localtime((time_t*)&ftt.secs);
470
471 fmt_uint32(fmt_buf3, ftt.secs, FMT_JUST_LEFT);
472
473 snprintf_time(fmt_buf4, tm, ftt.msecs);
474
475 fterr_info("port scan: src=%s dst=%s ts=%s start=%s",
476 fmt_buf1, fmt_buf2, fmt_buf3, fmt_buf4);
477 bzero(&ddp->portmap, sizeof ddp->portmap);
478 drp->flags |= DSCAN_FLAGS_PORTSCAN;
479 }
480 }
481 goto skip2;
482 }
483 }
484
485 /* no rec exists with this dst ip */
486
487 if (drp->depth >= ds.dscan_ip_depth) {
488
489 fmt_ipv4(fmt_buf1, rec->srcaddr, FMT_JUST_LEFT);
490
491 ftt = ftltime(rec->sysUpTime, rec->unix_secs, rec->unix_nsecs,
492 rec->First);
493 tm = localtime((time_t*)&ftt.secs);
494
495 fmt_uint32(fmt_buf3, ftt.secs, FMT_JUST_LEFT);
496
497 snprintf_time(fmt_buf4, tm, ftt.msecs);
498
499 fterr_info( "host scan: ip=%s ts=%s start=%s",
500 fmt_buf1, fmt_buf3, fmt_buf4);
501
502 drp->depth = 0;
503 drp->flags |= DSCAN_FLAGS_HOSTSCAN;
504
505 ddp = drp->dlhead.stqh_first;
506 while (ddp != NULL) {
507 ddp2 = ddp->chain.stqe_next;
508 free(ddp);
509 ++ds.stat_free;
510 ++ds.stat_free_dst;
511 ddp = ddp2;
512 }
513 FT_STAILQ_INIT(&drp->dlhead);
514 } else {
515
516 if (!(ddp = malloc(sizeof (struct dscan_dst))))
517 fterr_err(1, "malloc(dscan_dst)");
518
519 bzero(ddp, sizeof (struct dscan_dst));
520 ds.stat_malloc++;
521 ds.stat_malloc_dst++;
522 drp->depth ++;
523
524 FT_STAILQ_INSERT_TAIL(&drp->dlhead, ddp, chain);
525
526 ddp->ip_dst = rec->dstaddr;
527 ddp->ip_time = total_flows32;
528 if (rec->dstport < 1024) {
529 bit1024_store((int)rec->dstport, &ddp->portmap);
530 }
531 }
532
533 } else { /* no src ip match */
534
535 if (!(drp = malloc(sizeof (struct dscan_rec))))
536 fterr_err(1, "malloc(dscan_rec)");
537
538 ds.stat_malloc++;
539 ds.stat_malloc_rec++;
540 bzero(drp, sizeof (struct dscan_rec));
541 drp->ip_src = rec->srcaddr;
542 drp->depth = 1;
543 FT_SLIST_INSERT_HEAD(&ds.hash_scan[hash], drp, chain);
544
545 FT_STAILQ_INIT(&drp->dlhead);
546
547 if (!(ddp = malloc(sizeof (struct dscan_dst))))
548 fterr_err(1, "malloc(dscan_dst)");
549
550 bzero(ddp, sizeof (struct dscan_dst));
551 ds.stat_malloc++;
552 ds.stat_malloc_dst++;
553
554 FT_STAILQ_INSERT_TAIL(&drp->dlhead, ddp, chain);
555
556 ddp->ip_dst = rec->dstaddr;
557 ddp->ip_time = total_flows32;
558 if (rec->dstport < 1024) {
559 bit1024_store((int)rec->dstport, &ddp->portmap);
560 }
561
562 }
563
564 /*
565 * ager
566 */
567
568 if (ds.ager_timeout && (!(total_flows % 1000)))
569 ager(&ds, total_flows32);
570
571 skip2:
572 continue;
573
574 } /* while rec */
575
576 if (debug > 0) {
577 ftprof_end(&ftp, ftio_get_rec_total(&ftio));
578 ftprof_print(&ftp, argv[0], stderr);
579 }
580
581 if (do_dump)
582 dump_state(&ds);
583
584 return 0;
585
586
587 } /* main */
588
dump_state(struct dscan_state * ds)589 int dump_state(struct dscan_state *ds)
590 {
591 FILE *FP;
592 int i;
593 char fmt_buf1[64];
594 struct dscan_rec *drp;
595 struct dscan_dst *ddp;
596
597 if (!(FP = fopen(ds->statefile, "w"))) {
598 fterr_warnx("fopen(%s): failed");
599 return -1;
600 }
601
602 /* dump data structures */
603 for (i = 0; i < DSCAN_HASHSIZE; ++i) {
604 FT_SLIST_FOREACH(drp, &ds->hash_scan[i], chain) {
605 fmt_ipv4(fmt_buf1, drp->ip_src, FMT_JUST_LEFT);
606 fprintf(FP, "H %d %d %d %s\n", i, (int)drp->depth, (int)drp->flags,
607 fmt_buf1);
608 FT_STAILQ_FOREACH(ddp, &drp->dlhead, chain) {
609 fmt_ipv4(fmt_buf1, ddp->ip_dst, FMT_JUST_LEFT);
610 fprintf(FP, "I %u %s\n", ddp->ip_time, fmt_buf1);
611 bit1024_print(FP, &ddp->portmap);
612 }
613 }
614 }
615 fprintf(FP, ".\n");
616
617 if (fclose(FP)) {
618 fterr_warnx("fclose(%s): failed", ds->statefile);
619 return -1;
620 }
621
622 return 0;
623
624 } /* dump_state */
625
load_state(struct dscan_state * ds)626 int load_state(struct dscan_state *ds)
627 {
628 FILE *FP;
629 int done;
630 struct dscan_rec *drp;
631 struct dscan_dst *ddp;
632 char type;
633 char buf[1024];
634 uint32_t ip, hash, depth, flags, h, time, nports, i, j;
635
636 if (!(FP = fopen(ds->statefile, "r")))
637 fterr_errx(1, "fopen(%s): failed", ds->statefile);
638
639 done = 0;
640
641 while (!done) {
642
643 if (fscanf(FP, "%c", &type) == EOF) {
644 fterr_warnx("fscanf(): EOF");
645 break;
646 }
647
648 switch (type) {
649
650 case '.':
651 done = 1;
652 break;
653
654 case 'H': /* host - hash depth flags srcIP */
655 fscanf(FP, "%u %u %u %64s", (unsigned*)&hash, (unsigned*)&depth,
656 (unsigned*)&flags, buf);
657 ip = scan_ip(buf);
658 h = DSCAN_HASHFUNC(ip);
659 /* create the record */
660
661 if (!(drp = malloc(sizeof (struct dscan_rec))))
662 fterr_err(1, "malloc(dscan_rec)");
663
664 ds->stat_malloc++;
665 ds->stat_malloc_rec++;
666 bzero(drp, sizeof (struct dscan_rec));
667 drp->ip_src = ip;
668 drp->depth = depth;
669 drp->flags = flags;
670 FT_SLIST_INSERT_HEAD(&ds->hash_scan[hash], drp, chain);
671
672 /* init destination list */
673 FT_STAILQ_INIT(&drp->dlhead);
674 break;
675
676 case 'I': /* include - time dstIP */
677 fscanf(FP, "%u %15s", &time, buf);
678 ip = scan_ip(buf);
679
680 if (!(ddp = malloc(sizeof (struct dscan_dst))))
681 fterr_err(1, "malloc(dscan_dst)");
682
683 bzero(ddp, sizeof (struct dscan_dst));
684 ds->stat_malloc++;
685 ds->stat_malloc_dst++;
686
687 FT_STAILQ_INSERT_TAIL(&drp->dlhead, ddp, chain);
688
689 ddp->ip_dst = ip;
690 ddp->ip_time = time;
691 break;
692
693 case 'P': /* portmap - nports portlist */
694 fscanf(FP, "%u", &nports);
695 for (i = 0; i < nports; ++i) {
696 fscanf(FP, "%u", &j);
697 if (j < 1024) {
698 bit1024_store((int)j, &ddp->portmap);
699 }
700 }
701 break;
702
703 case '\n': /* ignore */
704 break;
705
706 default:
707 fterr_warnx("Unknown record type: %c", type);
708 return -1;
709 break;
710
711 } /* switch */
712 } /* while */
713
714 fclose (FP);
715 return 0;
716 } /* load_state */
717
sig_usr1_handler(int sig)718 void sig_usr1_handler(int sig)
719 {
720 sig_usr1 = 1;
721 } /* sig_usr1_handler */
722
sig_hup_handler(int sig)723 void sig_hup_handler(int sig)
724 {
725 sig_hup = 1;
726 } /* sig_usr1_handler */
727
ager(struct dscan_state * ds,uint32_t total_flows32)728 void ager(struct dscan_state *ds, uint32_t total_flows32)
729 {
730 static int ager_i;
731 int i, work, picky_gcc;
732 struct dscan_rec *drp, *odrp, *drp2;
733 struct dscan_dst *ddp, *oddp, *ddp2;
734 uint32_t i32;
735
736 work = 0;
737 for (i = ager_i; i < DSCAN_HASHSIZE; ++i) {
738 odrp = (void*)0L;
739 drp = ds->hash_scan[i].slh_first;
740 while (drp) {
741 work += 2;
742 oddp = (void*)0L;
743 ddp = drp->dlhead.stqh_first;
744 while (ddp) {
745 ++work;
746 if (ddp->ip_time > total_flows32)
747 i32 = 4294967295U - ddp->ip_time + total_flows32 + 1;
748 else
749 i32 = total_flows32 - ddp->ip_time;
750 if (i32 > ds->ager_timeout) {
751 if (debug > 5)
752 fterr_info( "ager: remove max=%u i32=%u",
753 ds->ager_timeout, i32);
754 --drp->depth;
755 if (oddp) {
756 oddp->chain.stqe_next = ddp->chain.stqe_next;
757 if (!ddp->chain.stqe_next) /* tail update */
758 drp->dlhead.stqh_last = &oddp->chain.stqe_next;
759 }
760 else {
761 drp->dlhead.stqh_first = ddp->chain.stqe_next;
762 if (!ddp->chain.stqe_next) /* tail update */
763 drp->dlhead.stqh_last = &ddp->chain.stqe_next;
764 }
765 ddp2 = ddp;
766 ddp = ddp->chain.stqe_next;
767 free (ddp2);
768 ++ds->stat_free;
769 ++ds->stat_free_dst;
770 ++ds->stat_aged_ip;
771 continue;
772 }
773 oddp = ddp;
774 ddp = ddp->chain.stqe_next;
775 } /* while ddp */
776 /* if they're all gone, delete the record itself */
777 if (!drp->depth) {
778 if (debug > 5)
779 fterr_info("ager: remove record");
780 if (odrp)
781 odrp->chain.sle_next = drp->chain.sle_next;
782 else
783 ds->hash_scan[i].slh_first = drp->chain.sle_next;
784
785 drp2 = drp;
786 drp = drp->chain.sle_next;
787 free (drp2);
788 ++ds->stat_free;
789 ++ds->stat_free_rec;
790 ++ds->stat_aged_dsr;
791 continue;
792 }
793
794 odrp = drp;
795 drp = drp->chain.sle_next;
796
797 } /* while */
798
799 /* simple work queue */
800 if (++work > DSCAN_AGER_WORK) {
801 if (debug > 2) {
802 fterr_info("ager: work=%d malloc=%u free=%u aged_ip=%u aged_dsr=%u",
803 work, ds->stat_malloc, ds->stat_free, ds->stat_aged_ip,
804 ds->stat_aged_dsr);
805 fterr_info("ager: malloc_dst=%u malloc_rec=%u free_dst=%u free_rec=%u",
806 ds->stat_malloc_dst, ds->stat_malloc_rec, ds->stat_free_dst,
807 ds->stat_free_rec);
808 }
809 ager_i = i++;
810 goto skip3;
811 }
812 } /* for hash */
813
814 ager_i = 0;
815 fterr_info("ager: reset hash run");
816
817 skip3:
818 picky_gcc = 1;
819
820 } /* ager */
821
load_suppress(struct dscan_state * ds,int sd)822 int load_suppress(struct dscan_state *ds, int sd)
823 {
824 char buf1[1024], *c1, *c2, *c3, *c4, *c;
825 struct dscan_sup *dsp;
826 int match;
827 FILE *FP;
828 uint32_t prot, srcport, dstport;
829 uint32_t ip, hash;
830 char *fname;
831
832 fterr_info("load_suppress %d", sd);
833
834 if (!(fname = (char*)malloc(strlen(ds->supfile)+5)))
835 fterr_err(1, "malloc()");
836
837 strcpy(fname, ds->supfile);
838
839 if (sd == 0) {
840 strcat(fname, ".src");
841 } else if (sd == 1) {
842 strcat(fname, ".dst");
843 }
844
845 if (!(FP = fopen(fname, "r")))
846 fterr_errx(1, "fopen(%s): failed", fname);
847
848 while (1) {
849
850 if (!fgets(buf1, 1024, FP))
851 break;
852
853 /* skip whitespace */
854 for (c = buf1; *c && ((*c == ' ') || (*c == '\t')); ++c);
855 c1 = c;
856
857 if (*c1 == '#')
858 continue;
859
860 if (*c1 == '\n')
861 continue;
862
863 for (; *c && ((*c != ' ') && (*c != '\t')); ++c);
864 for (; *c && ((*c == ' ') || (*c == '\t')); ++c);
865 c2 = c;
866
867 for (; *c && ((*c != ' ') && (*c != '\t')); ++c);
868 for (; *c && ((*c == ' ') || (*c == '\t')); ++c);
869 c3 = c;
870
871 for (; *c && ((*c != ' ') && (*c != '\t')); ++c);
872 for (; *c && ((*c == ' ') || (*c == '\t')); ++c);
873 c4 = c;
874
875 if ((!*c1) || (!*c2) || (!*c3) || (!*c4)) {
876 fterr_warnx("suppress parser: syntax error: %s", buf1);
877 continue;
878 }
879
880 if (debug > 5)
881 fterr_info( "suppress parser: c1=%s c2=%s c3=%s c4=%s",
882 c1, c2, c3, c4);
883
884 ip = scan_ip(c1);
885 sscanf(c2, "%u", &prot);
886 sscanf(c3, "%u", &srcport);
887 sscanf(c4, "%u", &dstport);
888
889 hash = DSCAN_HASHFUNC(ip);
890
891
892 if (sd == 0) {
893
894 match = 0;
895 FT_SLIST_FOREACH(dsp, &ds->hash_sup_src[hash], chain) {
896 if (dsp->ip == ip) {
897 match = 1;
898 break;
899 }
900 }
901
902 if (match) {
903 fterr_warnx("suppress parser: dup ip: %s", buf1);
904 continue;
905 }
906
907 if (*c1 == '-') {
908 fterr_warnx("suppress parser: no src ip wildcard");
909 continue;
910 }
911
912 if (!(dsp = malloc(sizeof (struct dscan_sup))))
913 fterr_err(1, "malloc(dscan_sup)");
914
915 bzero(dsp, sizeof (struct dscan_sup));
916 dsp->ip = ip;
917 dsp->srcport = srcport;
918 dsp->dstport = dstport;
919 dsp->protocol = prot;
920
921 if (*c2 != '-')
922 dsp->flags &= DSCAN_SUP_PROTOCOL;
923 if (*c3 != '-')
924 dsp->flags &= DSCAN_SUP_SRCPORT;
925 if (*c4 != '-')
926 dsp->flags &= DSCAN_SUP_DSTPORT;
927
928
929 FT_SLIST_INSERT_HEAD(&ds->hash_sup_src[hash], dsp, chain);
930
931 } else if (sd == 1) {
932
933 match = 0;
934 FT_SLIST_FOREACH(dsp, &ds->hash_sup_dst[hash], chain) {
935 if (dsp->ip == ip) {
936 match = 1;
937 break;
938 }
939 }
940
941 if (match) {
942 fterr_warnx("suppress parser: dup ip: %s", buf1);
943 continue;
944 }
945
946 if (*c1 == '-') {
947 fterr_warnx("suppress parser: no src ip wildcard");
948 continue;
949 }
950
951 if (!(dsp = malloc(sizeof (struct dscan_sup))))
952 fterr_err(1, "malloc(dscan_sup)");
953
954 bzero(dsp, sizeof (struct dscan_sup));
955 dsp->ip = ip;
956 dsp->srcport = srcport;
957 dsp->dstport = dstport;
958 dsp->protocol = prot;
959
960 if (*c2 != '-')
961 dsp->flags &= DSCAN_SUP_PROTOCOL;
962 if (*c3 != '-')
963 dsp->flags &= DSCAN_SUP_SRCPORT;
964 if (*c4 != '-')
965 dsp->flags &= DSCAN_SUP_DSTPORT;
966
967
968 FT_SLIST_INSERT_HEAD(&ds->hash_sup_dst[hash], dsp, chain);
969
970 }
971
972 } /* while 1 */
973
974 fclose(FP);
975 free (fname);
976 return 0;
977 } /* load_suppress */
978
clear_suppress(struct dscan_state * ds,int sd)979 void clear_suppress(struct dscan_state *ds, int sd)
980 {
981 int i;
982 struct dscan_sup *dsp_src, *dsp_dst;
983
984 if (sd == 0) {
985 for (i = 0; i < DSCAN_HASHSIZE; ++i) {
986 while (ds->hash_sup_src[i].slh_first != NULL) {
987 dsp_src = ds->hash_sup_src[i].slh_first;
988 FT_SLIST_REMOVE_HEAD(&ds->hash_sup_src[i], chain);
989 free (dsp_src);
990 } /* while */
991 } /* for */
992 } /* if */
993
994 if (sd == 1) {
995 for (i = 0; i < DSCAN_HASHSIZE; ++i) {
996 while (ds->hash_sup_dst[i].slh_first != NULL) {
997 dsp_dst = ds->hash_sup_dst[i].slh_first;
998 FT_SLIST_REMOVE_HEAD(&ds->hash_sup_dst[i], chain);
999 free (dsp_dst);
1000 } /* while */
1001 } /* for */
1002 } /* if */
1003
1004 } /* clear_suppress */
1005
1006
flow_dump(struct fts3rec_gen * rec)1007 void flow_dump(struct fts3rec_gen *rec)
1008 {
1009 struct fttime ftt;
1010 char fmt_buf1[64], fmt_buf2[64];
1011 struct tm *tm;
1012 static int header;
1013
1014 if (!header) {
1015 fterr_info( "Start End Sif SrcIPaddress SrcP DIf DstIPaddress DstP P Fl Pkts Octets");
1016 header = 1;
1017 }
1018
1019 ftt = ftltime(rec->sysUpTime, rec->unix_secs, rec->unix_nsecs, rec->First);
1020 tm = localtime((time_t*)&ftt.secs);
1021
1022 snprintf_time(fmt_buf1, tm, ftt.msecs);
1023 fterr_info(fmt_buf1);
1024
1025 ftt = ftltime(rec->sysUpTime, rec->unix_secs, rec->unix_nsecs, rec->Last);
1026 tm = localtime((time_t*)&ftt.secs);
1027
1028 snprintf_time(fmt_buf1, tm, ftt.msecs);
1029 fterr_info(fmt_buf1);
1030
1031 /* other info */
1032 fmt_ipv4(fmt_buf1, rec->srcaddr, FMT_PAD_RIGHT);
1033 fmt_ipv4(fmt_buf2, rec->dstaddr, FMT_PAD_RIGHT);
1034
1035 fterr_info( "%4d %-15.15s %6d %4d %-15.15s %6d %3d %2d %10" PRIu32 " %10" PRIu32 "",
1036 (int)rec->input, fmt_buf1, (int)rec->srcport,
1037 (int)rec->output, fmt_buf2, (int)rec->dstport,
1038 (int)rec->prot,
1039 (int)rec->tcp_flags & 0x7,
1040 rec->dPkts,
1041 rec->dOctets);
1042
1043 } /* flow_dump */
1044
usage(void)1045 void usage(void) {
1046
1047 fprintf(stderr, "Usage: flow-dscan [-bBhlmpwW] [-d debug_level] [-D iplist_depth]\n");
1048 fprintf(stderr, " [-s state_file] [-i input_filter] [-L suppress_list]\n");
1049 fprintf(stderr, " [-o output_filter] [-O excessive_octets] [-P excessive_flows]\n");
1050 fprintf(stderr, " [-S port_scan_trigger] [-t ager_timeout]\n");
1051 fprintf(stderr, "Signals:\n");
1052 fprintf(stderr, " SIGHUP - reload suppress list\n");
1053 fprintf(stderr, " SIGUSR1 - dump state\n");
1054
1055 } /* usage */
1056
1057