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