Skip to Content
[CAIDA - Cooperative Association for Internet Data Analysis logo]
The Cooperative Association for Internet Data Analysis
corsaro_dos.c
Go to the documentation of this file.
1 /*
2  * corsaro
3  *
4  * Alistair King, CAIDA, UC San Diego
5  * corsaro-info@caida.org
6  *
7  * Copyright (C) 2012 The Regents of the University of California.
8  *
9  * This file is part of corsaro.
10  *
11  * corsaro is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * corsaro is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with corsaro. If not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25 
26 #include "config.h"
27 #include "corsaro_int.h"
28 
29 #include <arpa/inet.h>
30 #include <assert.h>
31 #include <inttypes.h>
32 #include <stdio.h>
33 #include <string.h>
34 #include <time.h>
35 
36 #include "libtrace.h"
37 #ifdef HAVE_LIBPACKETDUMP
38 #include "libpacketdump.h"
39 #endif
40 
41 #include "khash.h"
42 #include "ksort.h"
43 #include "utils.h"
44 
45 #include "corsaro_io.h"
46 #include "corsaro_file.h"
47 #include "corsaro_log.h"
48 #include "corsaro_plugin.h"
49 
50 #include "corsaro_dos.h"
51 
61 #define CORSARO_DOS_MAGIC 0x45444F53
62 
64 #define PLUGIN_NAME "dos"
65 
72 #define PLUGIN_NAME_DEPRECATED "edgar_dos"
73 
76  PLUGIN_NAME, /* name */
77  CORSARO_PLUGIN_ID_DOS, /* id */
78  CORSARO_DOS_MAGIC, /* magic */
79  CORSARO_PLUGIN_GENERATE_PTRS(corsaro_dos), /* func ptrs */
80  NULL, /* next */
81 };
82 
94 #define CORSARO_DOS_INTERVAL 300
95 
97 #define CORSARO_DOS_VECTOR_TIMEOUT CORSARO_DOS_INTERVAL
98 
100 #define CORSARO_DOS_ATTACK_VECTOR_MIN_PACKETS 25
101 
103 #define CORSARO_DOS_ATTACK_VECTOR_MIN_DURATION 60
104 
106 #define CORSARO_DOS_ATTACK_VECTOR_MIN_PPM 30
107 
109 #define CORSARO_DOS_ATTACK_VECTOR_BYTECNT (4+4+4+4+4+8+4+8+4+8+4+4+4+4+4)
110 
112 #define CORSARO_DOS_PPM_WINDOW_SIZE 60
113 
115 #define CORSARO_DOS_PPM_WINDOW_PRECISION 10
116 
118 #define CORSARO_DOS_PPS_BUCKET_CNT (CORSARO_DOS_PPM_WINDOW_SIZE/ \
119  CORSARO_DOS_PPM_WINDOW_PRECISION)
120 
127 KHASH_SET_INIT_INT(32xx)
128 
129 
130 typedef struct ppm_window
131 {
133  uint32_t window_start;
135  uint64_t buckets[CORSARO_DOS_PPS_BUCKET_CNT];
137  uint8_t current_bucket;
139  uint64_t max_ppm;
140 } ppm_window_t;
141 
146 typedef struct attack_vector
147 {
152  uint8_t *initial_packet;
153 
155  uint32_t initial_packet_len;
156 
158  uint32_t attacker_ip;
159 
161  uint32_t responder_ip;
162 
164  uint32_t target_ip;
165 
167  uint64_t packet_cnt;
168 
170  uint32_t interval_packet_cnt;
171 
173  uint64_t byte_cnt;
174 
176  uint32_t interval_byte_cnt;
177 
179  ppm_window_t ppm_window;
180 
182  struct timeval start_time;
183 
185  struct timeval latest_time;
186 
188  kh_32xx_t *attack_ip_hash;
189 
191  kh_32xx_t *attack_port_hash;
192 
194  kh_32xx_t *target_port_hash;
195 
197  uint32_t attack_ip_cnt;
198 
200 
201 /* need to create an attack_vector_in structure when we write the reading
202  stuff */
203 
210 {
211  attack_vector_t *av = NULL;
212  if((av = malloc_zero(sizeof(attack_vector_t))) == NULL)
213  {
214  corsaro_log(__func__, corsaro, "could not malloc memory for attack vector");
215  return NULL;
216  }
217 
218  av->attack_ip_hash = kh_init(32xx);
219  assert(av->attack_ip_hash != NULL);
220 
221  av->attack_port_hash = kh_init(32xx);
222  assert(av->attack_port_hash != NULL);
223 
224  av->target_port_hash = kh_init(32xx);
225  assert(av->target_port_hash != NULL);
226 
227  return av;
228 }
229 
235 {
236  if(av == NULL)
237  {
238  return;
239  }
240 
241  if(av->initial_packet != NULL)
242  {
243  /*trace_destroy_packet(av->initial_packet);*/
244  free(av->initial_packet);
245  }
246 
247  if(av->attack_ip_hash != NULL)
248  {
249  kh_destroy(32xx, av->attack_ip_hash);
250  }
251  if(av->attack_port_hash != NULL)
252  {
253  kh_destroy(32xx, av->attack_port_hash);
254  }
255  if(av->target_port_hash != NULL)
256  {
257  kh_destroy(32xx, av->target_port_hash);
258  }
259 
260  free(av);
261  return;
262 }
263 
269 {
270  assert(av != NULL);
271 
272  av->interval_packet_cnt = 0;
273  av->interval_byte_cnt = 0;
274  av->attack_ip_cnt = kh_size(av->attack_ip_hash);
275 }
276 
278 #define attack_vector_hash_equal(a, b) ( \
279  (a)->target_ip == (b)->target_ip \
280  )
281 
286 static inline khint32_t attack_vector_hash_func(attack_vector_t *av)
287 {
288  return (khint32_t)av->target_ip*59;
289 }
290 
292 KHASH_INIT(av, attack_vector_t*, char, 0,
294 
296 struct corsaro_dos_state_t {
298  uint32_t first_interval;
301  uint16_t number_mismatched_packets;
303  khash_t(av) *attack_hash;
305  corsaro_file_t *outfile;
306 };
307 
309 struct corsaro_dos_in_state_t {
311  corsaro_in_record_type_t expected_type;
313  int vector_total;
315  int vector_cnt;
316 };
317 
319 #define STATE(corsaro) \
320  (CORSARO_PLUGIN_STATE(corsaro, dos,CORSARO_PLUGIN_ID_DOS))
321 
322 #define STATE_IN(corsaro) \
323  (CORSARO_PLUGIN_STATE(corsaro, dos_in, \
324  CORSARO_PLUGIN_ID_DOS))
325 
326 #define PLUGIN(corsaro) \
327  (CORSARO_PLUGIN_PLUGIN(corsaro, CORSARO_PLUGIN_ID_DOS))
328 
335  uint32_t time)
336 {
337  if(vector->latest_time.tv_sec + CORSARO_DOS_VECTOR_TIMEOUT < time)
338  {
339  return 1;
340  }
341  return 0;
342 }
343 
347 static void attack_vector_update_ppm(ppm_window_t *ppm_window)
348 {
349  int i;
350  uint64_t this_ppm;
351 
352  /* calculate the ppm for the current window in the buckets */
353  this_ppm = 0;
354  for(i = 0; i < CORSARO_DOS_PPS_BUCKET_CNT; i++)
355  {
356  this_ppm += ppm_window->buckets[i];
357  }
358  if(this_ppm > ppm_window->max_ppm)
359  {
360  ppm_window->max_ppm = this_ppm;
361  }
362 }
363 
441  struct timeval tv)
442 {
443  int bucket_offset;
444  int i;
445 
446  ppm_window_t *ppm_window = &vector->ppm_window;
447 
448  bucket_offset = (tv.tv_sec-ppm_window->window_start)/
450 
451  /* this packet is outside of the current bucket */
452  if(bucket_offset > 0)
453  {
454  attack_vector_update_ppm(ppm_window);
455 
456  /* zero out the first n buckets in the window */
457  for(i = 0; i < bucket_offset && i < 6; i++)
458  {
459  ppm_window->current_bucket =
460  (ppm_window->current_bucket+1) % CORSARO_DOS_PPS_BUCKET_CNT;
461  ppm_window->buckets[ppm_window->current_bucket] = 0;
462  }
463  /* move the start of the window to the end of the zeroed buckets */
464  ppm_window->window_start += bucket_offset*
466 
467  }
468 
469  /* add this packet to current bucket */
470  ppm_window->buckets[ppm_window->current_bucket]++;
471 
472  return;
473 }
474 
483  attack_vector_t *vector,
484  uint32_t time)
485 {
486  struct timeval duration;
487  uint64_t ppm;
488 
489  if(vector->packet_cnt < CORSARO_DOS_ATTACK_VECTOR_MIN_PACKETS)
490  {
491  /* not enough packets */
492  return 0;
493  }
494 
495  if(timeval_subtract(&duration, &vector->latest_time,
496  &vector->start_time) == 1)
497  {
498  corsaro_log(__func__, corsaro, "last packet seen before first packet!");
499  return -1;
500  }
501  if(duration.tv_sec < CORSARO_DOS_ATTACK_VECTOR_MIN_DURATION)
502  {
503  /* not long enough */
504  return 0;
505  }
506 
507  attack_vector_update_ppm(&vector->ppm_window);
508  ppm = vector->ppm_window.max_ppm;
509 
511  {
512  /* not high enough velocity */
513  return 0;
514  }
515 
516  return 1;
517 }
518 
526 {
527  uint32_t tmp;
528  char t_ip[16];
529 
530  tmp = htonl(vector->target_ip);
531  inet_ntop(AF_INET,&tmp, &t_ip[0], 16);
532 
533  corsaro_file_printf(corsaro, STATE(corsaro)->outfile,
534  "%s"
535  ",%"PRIu32
536  ",%"PRIu32
537  ",%"PRIu32
538  ",%"PRIu32
539  ",%"PRIu64
540  ",%"PRIu32
541  ",%"PRIu64
542  ",%"PRIu32
543  ",%"PRIu64
544  ",%"PRIu32".%06"PRIu32
545  ",%"PRIu32".%06"PRIu32
546  "\n",
547  t_ip,
548  kh_size(vector->attack_ip_hash),
549  kh_size(vector->attack_ip_hash)-vector->attack_ip_cnt,
550  kh_size(vector->attack_port_hash),
551  kh_size(vector->target_port_hash),
552  vector->packet_cnt,
553  vector->interval_packet_cnt,
554  vector->byte_cnt,
555  vector->interval_byte_cnt,
556  vector->ppm_window.max_ppm,
557  (uint32_t)vector->start_time.tv_sec,
558  (uint32_t)vector->start_time.tv_usec,
559  (uint32_t)vector->latest_time.tv_sec,
560  (uint32_t)vector->latest_time.tv_usec);
561  return 0;
562 }
563 
571 {
572  uint8_t av_bytes[CORSARO_DOS_ATTACK_VECTOR_BYTECNT];
573  uint8_t *ptr = &av_bytes[0];
574 
575  /*
576  uint8_t *pkt_buf = NULL;
577  libtrace_linktype_t linktype;
578  uint32_t pkt_length;
579  */
580 
581  /* dump the attack vector details */
582 
583  bytes_htonl(ptr, vector->target_ip);
584  ptr+=4;
585 
586  bytes_htonl(ptr, kh_size(vector->attack_ip_hash));
587  ptr+=4;
588 
589  bytes_htonl(ptr, kh_size(vector->attack_ip_hash)-vector->attack_ip_cnt);
590  ptr+=4;
591 
592  bytes_htonl(ptr, kh_size(vector->attack_port_hash));
593  ptr+=4;
594 
595  bytes_htonl(ptr, kh_size(vector->target_port_hash));
596  ptr+=4;
597 
598  bytes_htonll(ptr, vector->packet_cnt);
599  ptr+=8;
600 
601  bytes_htonl(ptr, vector->interval_packet_cnt);
602  ptr+=4;
603 
604  bytes_htonll(ptr, vector->byte_cnt);
605  ptr+=8;
606 
607  bytes_htonl(ptr, vector->interval_byte_cnt);
608  ptr+=4;
609 
610  bytes_htonll(ptr, vector->ppm_window.max_ppm);
611  ptr+=8;
612 
613  bytes_htonl(ptr, vector->start_time.tv_sec);
614  ptr+=4;
615 
616  bytes_htonl(ptr, vector->start_time.tv_usec);
617  ptr+=4;
618 
619  bytes_htonl(ptr, vector->latest_time.tv_sec);
620  ptr+=4;
621 
622  bytes_htonl(ptr, vector->latest_time.tv_usec);
623  ptr+=4;
624 
625  /* dump the initial packet using trace_get_packet_buffer */
626  /*
627  if((pkt_buf = trace_get_packet_buffer(vector->initial_packet,
628  &linktype, NULL)) == NULL ||
629  (pkt_length = trace_get_capture_length(vector->initial_packet)) == 0)
630  {
631  corsaro_log(__func__, "could not get packet buffer");
632  return -1;
633  }
634  */
635 
636  /* add the size of the packet to the byte array before we dump it */
637  bytes_htonl(ptr, vector->initial_packet_len);
638 
639  if(corsaro_file_write(corsaro, STATE(corsaro)->outfile, &av_bytes[0],
642  {
643  corsaro_log(__func__, corsaro, "could not dump vector byte array to file");
644  return -1;
645  }
646 
647  if(corsaro_file_write(corsaro, STATE(corsaro)->outfile, vector->initial_packet,
648  vector->initial_packet_len) !=
649  vector->initial_packet_len)
650  {
651  corsaro_log(__func__, corsaro, "could not dump packet to file");
652  return -1;
653  }
654 
655  return 0;
656 }
657 
658 static int read_header(corsaro_in_t *corsaro,
659  corsaro_in_record_type_t *record_type,
660  corsaro_in_record_t *record)
661 {
662  off_t bytes_read;
663 
664  if((bytes_read =
665  corsaro_io_read_bytes(corsaro, record,
666  sizeof(corsaro_dos_header_t))) !=
667  sizeof(corsaro_dos_header_t))
668  {
669  corsaro_log_in(__func__, corsaro, "failed to read dos header from file");
670  *record_type = CORSARO_IN_RECORD_TYPE_NULL;
671  return bytes_read;
672  }
673 
674  ((corsaro_dos_header_t *)record->buffer)->attack_vector_cnt =
675  ntohl(((corsaro_dos_header_t *)record->buffer)->attack_vector_cnt);
676 
677  assert(bytes_read == sizeof(corsaro_dos_header_t));
678 
679  *record_type = CORSARO_IN_RECORD_TYPE_DOS_HEADER;
680  STATE_IN(corsaro)->vector_total = ((corsaro_dos_header_t *)
681  record->buffer)->attack_vector_cnt;
682 
683  STATE_IN(corsaro)->expected_type = (STATE_IN(corsaro)->vector_total == 0) ?
686 
687  return bytes_read;
688 }
689 
690 static int validate_attack_vector(corsaro_dos_attack_vector_in_t *av)
691 {
692  /* short-circuit if the packet is empty */
693  if(av->initial_packet_len == 0)
694  {
695  return 0;
696  }
697 
698  /* we need to byte swap */
699  av->target_ip = ntohl(av->target_ip);
700  av->attacker_ip_cnt = ntohl(av->attacker_ip_cnt);
702  av->attack_port_cnt = ntohl(av->attack_port_cnt);
703  av->target_port_cnt = ntohl(av->target_port_cnt);
704  av->packet_cnt = ntohll(av->packet_cnt);
705  av->interval_packet_cnt = ntohl(av->interval_packet_cnt);
706  av->byte_cnt = ntohll(av->byte_cnt);
707  av->interval_byte_cnt = ntohl(av->interval_byte_cnt);
708  av->max_ppm = ntohll(av->max_ppm);
709  av->start_time_sec = ntohl(av->start_time_sec);
710  av->start_time_usec = ntohl(av->start_time_usec);
711  av->latest_time_sec = ntohl(av->latest_time_sec);
712  av->latest_time_usec = ntohl(av->latest_time_usec);
713  av->initial_packet_len = ntohl(av->initial_packet_len);
714 
715  /* NULL it just in case */
716  av->initial_packet = NULL;
717  return 1;
718 }
719 
720 static int read_attack_vector(corsaro_in_t *corsaro,
721  corsaro_in_record_type_t *record_type,
722  corsaro_in_record_t *record)
723 {
724  off_t bytes_read;
725 
726  /* the number of bytes that should be read after the first read */
727  /* this is the size of the attack vector less the size of the pointer */
728  off_t bsbread = sizeof(corsaro_dos_attack_vector_in_t)
729  -sizeof(uint8_t*);
730 
732 
733  /* read the attack vector in record, but not the pointer to the packet
734  we will need to find the length before we read it in */
735 
736  if((bytes_read = corsaro_io_read_bytes(corsaro, record, bsbread)) != bsbread)
737  {
738  corsaro_log_in(__func__, corsaro,
739  "failed to read dos attack vector from file");
740  *record_type = CORSARO_IN_RECORD_TYPE_NULL;
741  return bytes_read;
742  }
743 
744  av = (corsaro_dos_attack_vector_in_t *)record->buffer;
745 
746  if(validate_attack_vector(av) != 1)
747  {
748  corsaro_log_in(__func__, corsaro, "could not validate attack vector");
749  *record_type = CORSARO_IN_RECORD_TYPE_NULL;
750  return -1;
751  }
752 
753  /* now read the packet into the buffer right after the attack vector */
754  if((bytes_read +=
755  corsaro_io_read_bytes_offset(corsaro, record,
757  av->initial_packet_len))
758  != (bsbread += av->initial_packet_len))
759  {
760  corsaro_log_in(__func__, corsaro,
761  "failed to read initial packet from file");
762  *record_type = CORSARO_IN_RECORD_TYPE_NULL;
763  return bytes_read;
764  }
765 
766  /* update the pointer */
768 
770 
771  if(++(STATE_IN(corsaro)->vector_cnt) == STATE_IN(corsaro)->vector_total)
772  {
773  STATE_IN(corsaro)->expected_type = CORSARO_IN_RECORD_TYPE_IO_INTERVAL_END;
774  STATE_IN(corsaro)->vector_total = 0;
775  STATE_IN(corsaro)->vector_cnt = 0;
776  }
777 
778  assert(bytes_read == sizeof(corsaro_dos_attack_vector_in_t)-sizeof(uint8_t*)
779  +av->initial_packet_len);
780 
781  return bytes_read;
782 }
783 
784 static int validate_global_header(corsaro_dos_global_header_t *g)
785 {
786  g->mismatched_pkt_cnt = ntohl(g->mismatched_pkt_cnt);
787  g->attack_vector_cnt = ntohl(g->attack_vector_cnt);
789 
790  return 1;
791 }
792 
793 /* == PUBLIC PLUGIN FUNCS BELOW HERE == */
794 
795 corsaro_plugin_t *corsaro_dos_alloc(corsaro_t *corsaro)
796 {
797  return &corsaro_dos_plugin;
798 }
799 
800 int corsaro_dos_probe_filename(const char *fname)
801 {
802  /* look for 'corsaro_dos' in the name */
803  if(corsaro_plugin_probe_filename(fname, &corsaro_dos_plugin) == 0)
804  {
805  if(strstr(fname, PLUGIN_NAME_DEPRECATED) != NULL)
806  {
807  return 1;
808  }
809  }
810  else
811  {
812  return 1;
813  }
814  return 0;
815 }
816 
821 {
822  /* unfortunately we cant detect this in corsaro 0.6 files.
823  alistair was an idiot and forgot to write an magic number for the
824  DOS plugin. */
825  return -1;
826 }
827 
832 {
833  struct corsaro_dos_state_t *state;
834  /* retrieve a pointer to the plugin struct with our name and id */
835  corsaro_plugin_t *plugin = PLUGIN(corsaro);
836  assert(plugin != NULL);
837 
838  /*
839  * allocate memory for the state structure which will hold a pointer to the
840  * output file and other statistics
841  */
842  if((state = malloc_zero(sizeof(struct corsaro_dos_state_t))) == NULL)
843  {
844  corsaro_log(__func__, corsaro,
845  "could not malloc corsaro_dos_state_t");
846  goto err;
847  }
848  /*
849  * register the state structure with the plugin manager
850  * this associates it with our plugin id so it can be retrieved later
851  */
852  corsaro_plugin_register_state(corsaro->plugin_manager, plugin, state);
853 
854  /* open the output file */
855  if((state->outfile = corsaro_io_prepare_file(corsaro, plugin->name)) == NULL)
856  {
857  corsaro_log(__func__, corsaro, "could not open %s output file",
858  plugin->name);
859  goto err;
860  }
861 
862  /* init the hash table for attack vectors */
863  state->attack_hash = kh_init(av);
864 
865  return 0;
866 
867  err:
868  corsaro_dos_close_output(corsaro);
869  return -1;
870 }
871 
872 int corsaro_dos_init_input(corsaro_in_t *corsaro)
873 {
874  struct corsaro_dos_in_state_t *state;
875  corsaro_plugin_t *plugin = PLUGIN(corsaro);
876  assert(plugin != NULL);
877 
878  if((state = malloc_zero(sizeof(struct corsaro_dos_in_state_t)))
879  == NULL)
880  {
881  corsaro_log_in(__func__, corsaro,
882  "could not malloc corsaro_dos_state_t");
883  goto err;
884  }
885  corsaro_plugin_register_state(corsaro->plugin_manager, plugin, state);
886 
887  /* we initially expect an corsaro interval record */
888  state->expected_type = CORSARO_IN_RECORD_TYPE_IO_INTERVAL_START;
889 
890  /* don't set the vector_cnt until we actually see a header record */
891 
892  return 0;
893 
894  err:
895  corsaro_dos_close_input(corsaro);
896  return -1;
897 }
898 
899 int corsaro_dos_close_input(corsaro_in_t *corsaro)
900 {
901  struct corsaro_dos_in_state_t *state = STATE_IN(corsaro);
902 
903  if(state != NULL)
904  {
905  corsaro_plugin_free_state(corsaro->plugin_manager, PLUGIN(corsaro));
906  }
907  return 0;
908 }
909 
910 int corsaro_dos_close_output(corsaro_t *corsaro)
911 {
912  struct corsaro_dos_state_t *state = STATE(corsaro);
913 
914  if(state != NULL)
915  {
916  if(state->attack_hash != NULL)
917  {
918  kh_free(av, state->attack_hash, &attack_vector_free);
919  kh_destroy(av, state->attack_hash);
920  state->attack_hash = NULL;
921  }
922 
923  if(state->outfile != NULL)
924  {
925  corsaro_file_close(corsaro, state->outfile);
926  state->outfile = NULL;
927  }
928  corsaro_plugin_free_state(corsaro->plugin_manager, PLUGIN(corsaro));
929  }
930  return 0;
931 }
932 
933 off_t corsaro_dos_read_record(struct corsaro_in *corsaro,
934  corsaro_in_record_type_t *record_type,
935  corsaro_in_record_t *record)
936 {
937  struct corsaro_dos_in_state_t *state = STATE_IN(corsaro);
938 
939  off_t bytes_read = -1;
940 
941  /* this code is adapted from corsaro_flowtuple.c */
942  /* we have 5 different types of records that could be in this file */
943  switch(state->expected_type)
944  {
946  /* ask the io subsystem to read it for us */
947  bytes_read = corsaro_io_read_interval_start(corsaro, corsaro->file,
948  record_type, record);
949  if(bytes_read == sizeof(corsaro_interval_t))
950  {
951  state->expected_type = CORSARO_IN_RECORD_TYPE_DOS_HEADER;
952  }
953  break;
954 
956  /* we'll handle this one */
957  bytes_read = read_header(corsaro, record_type, record);
958  break;
959 
961  /* we'll handle this too */
962  bytes_read = read_attack_vector(corsaro, record_type, record);
963  break;
964 
966  /* ask the io subsystem to read it for us */
967  bytes_read = corsaro_io_read_interval_end(corsaro, corsaro->file,
968  record_type, record);
969  if(bytes_read == sizeof(corsaro_interval_t))
970  {
971  state->expected_type = CORSARO_IN_RECORD_TYPE_IO_INTERVAL_START;
972  }
973  break;
974 
975  default:
976  corsaro_log_in(__func__, corsaro, "invalid expected record type");
977  }
978 
979  return bytes_read;
980 }
981 
982 off_t corsaro_dos_read_global_data_record(struct corsaro_in *corsaro,
983  enum corsaro_in_record_type *record_type,
984  struct corsaro_in_record *record)
985 {
986  off_t bytes_read;
987 
988  if((bytes_read = corsaro_io_read_bytes(corsaro, record,
989  sizeof(corsaro_dos_global_header_t))) !=
991  {
992  *record_type = CORSARO_IN_RECORD_TYPE_NULL;
993  return bytes_read;
994  }
995 
996  if(validate_global_header((corsaro_dos_global_header_t *)record->buffer)
997  != 1)
998  {
999  corsaro_log_in(__func__, corsaro, "could not validate global header");
1000  *record_type = CORSARO_IN_RECORD_TYPE_NULL;
1001  return -1;
1002  }
1003 
1005 
1006  assert(bytes_read == sizeof(corsaro_dos_global_header_t));
1007 
1008  return bytes_read;
1009 }
1010 
1011 int corsaro_dos_start_interval(corsaro_t *corsaro, corsaro_interval_t *int_start)
1012 {
1013  if(STATE(corsaro)->first_interval == 0)
1014  {
1015  /* -1 to simulate the end of the 'previous' interval */
1016  STATE(corsaro)->first_interval = int_start->time-1;
1017  }
1018 
1019  return 0;
1020 }
1021 
1022 int corsaro_dos_end_interval(corsaro_t *corsaro, corsaro_interval_t *int_end)
1023 {
1024  int this_interval = int_end->time-STATE(corsaro)->first_interval;
1025 
1026  khiter_t i;
1027  attack_vector_t *vector;
1028  attack_vector_t **attack_arr = NULL;
1029  int attack_arr_cnt = 0;
1030 
1031  uint8_t gbuf[12];
1032  uint8_t cntbuf[4];
1033 
1034  if(this_interval < CORSARO_DOS_INTERVAL)
1035  {
1036  /* we haven't run for long enough to dump */
1037  return 0;
1038  }
1039  else
1040  {
1041  /* we either have hit exactly the right amount of time,
1042  or we have gone for too long, dump now and reset the counter */
1043  STATE(corsaro)->first_interval = int_end->time;
1044  /* fall through and continue to dump */
1045  }
1046 
1047  /* this is an interval we care about */
1048 
1049  /* malloc an array big enough to hold the entire hash even though we wont
1050  need it to be that big */
1051  if((attack_arr =
1052  malloc(sizeof(attack_vector_t *)*
1053  kh_size(STATE(corsaro)->attack_hash))) == NULL)
1054  {
1055  corsaro_log(__func__, corsaro, "could not malloc array for attack vectors");
1056  return -1;
1057  }
1058 
1059  /* classify the flows and dump the attack ones */
1060 
1061  for(i = kh_begin(STATE(corsaro)->attack_hash);
1062  i != kh_end(STATE(corsaro)->attack_hash); ++i)
1063  {
1064  if(kh_exist(STATE(corsaro)->attack_hash, i))
1065  {
1066  vector = kh_key(STATE(corsaro)->attack_hash, i);
1067 
1068  if(attack_vector_is_expired(vector, int_end->time) != 0)
1069  {
1070  kh_del(av, STATE(corsaro)->attack_hash, i);
1071  attack_vector_free(vector);
1072  vector = NULL;
1073  }
1074  else if(attack_vector_is_attack(corsaro, vector, int_end->time) != 0)
1075  {
1076  /* this is an attack */
1077  /* add it to the attack array so we can know how many
1078  before we dump it */
1079  attack_arr[attack_arr_cnt] = vector;
1080  attack_arr_cnt++;
1081  }
1082  else
1083  {
1084  attack_vector_reset(vector);
1085  }
1086  }
1087  }
1088 
1089  corsaro_io_write_interval_start(corsaro, STATE(corsaro)->outfile,
1090  &corsaro->interval_start);
1091  corsaro_io_write_plugin_start(corsaro, corsaro->global_file, PLUGIN(corsaro));
1092  if(CORSARO_FILE_MODE(STATE(corsaro)->outfile) == CORSARO_FILE_MODE_ASCII)
1093  {
1094  /* global stats */
1095  /* dump the number of mismatched packets and vectors */
1096  corsaro_file_printf(corsaro, corsaro->global_file,
1097  "mismatch: %"PRIu32"\n"
1098  "attack_vectors: %"PRIu32"\n"
1099  "non-attack_vectors: %"PRIu32"\n",
1100  STATE(corsaro)->number_mismatched_packets,
1101  attack_arr_cnt,
1102  kh_size(STATE(corsaro)->attack_hash)-attack_arr_cnt);
1103 
1104  /* dump the number of vectors */
1105  corsaro_file_printf(corsaro, STATE(corsaro)->outfile, "%"PRIu32"\n",
1106  attack_arr_cnt);
1107  /* dump the vectors */
1108  for(i = 0; i < attack_arr_cnt; i++)
1109  {
1110  if(ascii_dump(corsaro, attack_arr[i]) != 0)
1111  {
1112  corsaro_log(__func__, corsaro, "could not dump hash");
1113  return -1;
1114  }
1115  /* reset the interval stats */
1116  attack_vector_reset(attack_arr[i]);
1117  }
1118  }
1119  else if(CORSARO_FILE_MODE(STATE(corsaro)->outfile) == CORSARO_FILE_MODE_BINARY)
1120  {
1121  /* global stats */
1122  bytes_htonl(&gbuf[0], STATE(corsaro)->number_mismatched_packets);
1123  bytes_htonl(&gbuf[4], attack_arr_cnt);
1124  bytes_htonl(&gbuf[8],
1125  kh_size(STATE(corsaro)->attack_hash)-attack_arr_cnt);
1126  if(corsaro_file_write(corsaro, corsaro->global_file,
1127  &gbuf[0], 12) != 12)
1128  {
1129  corsaro_log(__func__, corsaro,
1130  "could not dump global stats to file");
1131  return -1;
1132  }
1133 
1134  /* dump the number of vectors */
1135  bytes_htonl(&cntbuf[0], attack_arr_cnt);
1136  if(corsaro_file_write(corsaro, STATE(corsaro)->outfile,
1137  &cntbuf[0], 4) != 4)
1138  {
1139  corsaro_log(__func__, corsaro,
1140  "could not dump vector count to file");
1141  return -1;
1142  }
1143  /* dump the vectors */
1144  for(i = 0; i < attack_arr_cnt; i++)
1145  {
1146  if(binary_dump(corsaro, attack_arr[i]) != 0)
1147  {
1148  corsaro_log(__func__, corsaro, "could not dump hash");
1149  return -1;
1150  }
1151  attack_vector_reset(attack_arr[i]);
1152  }
1153  }
1154  else
1155  {
1156  corsaro_log(__func__, corsaro, "invalid mode");
1157  return -1;
1158  }
1159  corsaro_io_write_plugin_end(corsaro, corsaro->global_file, PLUGIN(corsaro));
1160  corsaro_io_write_interval_end(corsaro, STATE(corsaro)->outfile, int_end);
1161 
1162  STATE(corsaro)->number_mismatched_packets = 0;
1163 
1164  free(attack_arr);
1165 
1166  return 0;
1167 }
1168 
1170  corsaro_packet_t *packet)
1171 {
1172  libtrace_packet_t *ltpacket = LT_PKT(packet);
1173  void *temp = NULL;
1174  uint16_t ethertype;
1175  uint8_t proto;
1176  uint32_t remaining;
1177 
1178  libtrace_ip_t *ip_hdr = NULL;
1179  libtrace_icmp_t *icmp_hdr = NULL;
1180  libtrace_ip_t *inner_ip_hdr = NULL;
1181 
1182  /* borrowed from libtrace's protocols.h (used by trace_get_*_port) */
1183  struct ports_t {
1184  uint16_t src;
1185  uint16_t dst;
1186  };
1187 
1188  uint16_t attacker_port = 0;
1189  uint16_t target_port = 0;
1190 
1191  attack_vector_t findme;
1192 
1193  int khret;
1194  khiter_t khiter;
1195  attack_vector_t *vector = NULL;
1196  uint8_t *pkt_buf = NULL;
1197  libtrace_linktype_t linktype;
1198 
1199  struct timeval tv;
1200 
1201  if((packet->state.flags & CORSARO_PACKET_STATE_FLAG_BACKSCATTER) == 0)
1202  {
1203  /* not a backscatter packet */
1204  return 0;
1205  }
1206 
1207  /* backscatter packet, lets find the flow */
1208  /* check for ipv4 */
1209  if((temp = trace_get_layer3(ltpacket, &ethertype, &remaining)) != NULL &&
1210  ethertype == TRACE_ETHERTYPE_IP)
1211  {
1212  ip_hdr = (libtrace_ip_t *)temp;
1213  }
1214  else
1215  {
1216  /* not an ip packet */
1217  corsaro_log(__func__, corsaro, "non-ip packet found (ethertype: %x)",
1218  ethertype);
1219  return 0;
1220  }
1221 
1222  /* get the transport header */
1223  if((temp = trace_get_transport(ltpacket, &proto, &remaining)) == NULL)
1224  {
1225  /* not enough payload */
1226  return 0;
1227  }
1228 
1229  findme.target_ip = 0;
1230 
1231  if(ip_hdr->ip_p == TRACE_IPPROTO_ICMP && remaining >= 2)
1232  {
1233  icmp_hdr = (libtrace_icmp_t *)temp;
1234 
1235  if((icmp_hdr->type == 3 ||
1236  icmp_hdr->type == 4 ||
1237  icmp_hdr->type == 5 ||
1238  icmp_hdr->type == 11 ||
1239  icmp_hdr->type == 12) &&
1240  ((temp = trace_get_payload_from_icmp(icmp_hdr, &remaining)) != NULL
1241  && remaining >= 20 && (inner_ip_hdr = (libtrace_ip_t *)temp) &&
1242  inner_ip_hdr->ip_v == 4))
1243  {
1244  /* icmp error message */
1245  if(inner_ip_hdr->ip_src.s_addr != ip_hdr->ip_dst.s_addr)
1246  {
1247  STATE(corsaro)->number_mismatched_packets++;
1248  }
1249 
1250  findme.target_ip = ntohl(inner_ip_hdr->ip_dst.s_addr);
1251 
1252  /* just extract the first four bytes of payload as ports */
1253  if((temp = trace_get_payload_from_ip(inner_ip_hdr, NULL,
1254  &remaining)) != NULL
1255  && remaining >= 4)
1256  {
1257  attacker_port = ntohs(((struct ports_t *)temp)->src);
1258  target_port = ntohs(((struct ports_t *)temp)->dst);
1259  }
1260  }
1261  else
1262  {
1263  findme.target_ip = ntohl(ip_hdr->ip_src.s_addr);
1264  attacker_port = ntohs(icmp_hdr->code);
1265  target_port = ntohs(icmp_hdr->type);
1266  }
1267  }
1268  else if((ip_hdr->ip_p == TRACE_IPPROTO_TCP ||
1269  ip_hdr->ip_p == TRACE_IPPROTO_UDP) &&
1270  remaining >= 4)
1271  {
1272  findme.target_ip = ntohl(ip_hdr->ip_src.s_addr);
1273  attacker_port = trace_get_destination_port(ltpacket);
1274  target_port = trace_get_source_port(ltpacket);
1275  }
1276 
1277  if(findme.target_ip == 0)
1278  {
1279  /* the packet is none of ICMP, TCP or UDP */
1280  return 0;
1281  }
1282 
1283  tv = trace_get_timeval(ltpacket);
1284 
1285  /* is this vector in the hash? */
1286  assert(STATE(corsaro)->attack_hash != NULL);
1287  if((khiter = kh_get(av, STATE(corsaro)->attack_hash, &findme))
1288  != kh_end(STATE(corsaro)->attack_hash))
1289  {
1290  /* the vector is in the hash */
1291  vector = kh_key(STATE(corsaro)->attack_hash, khiter);
1292 
1293  if(attack_vector_is_expired(vector, tv.tv_sec) != 0)
1294  {
1295  kh_del(av, STATE(corsaro)->attack_hash, khiter);
1296  attack_vector_free(vector);
1297  vector = NULL;
1298  }
1299  }
1300 
1301  if(vector == NULL)
1302  {
1303  /* create a new vector and fill it */
1304  if((vector = attack_vector_init(corsaro)) == NULL)
1305  {
1306  corsaro_log(__func__, corsaro, "failed to create new attack vector");
1307  return -1;
1308  }
1309 
1310  /* i think this may be buggy. do it the safe way for now
1311  vector->initial_packet = corsaro_mincopy_packet(packet);
1312  */
1313  vector->initial_packet_len = trace_get_capture_length(ltpacket);
1314 
1315  if((vector->initial_packet = malloc(vector->initial_packet_len)) == NULL)
1316  {
1317  corsaro_log(__func__, corsaro, "could not malloc initial packet");
1318  return -1;
1319  }
1320 
1321  if((pkt_buf = trace_get_packet_buffer(ltpacket,
1322  &linktype, NULL)) == NULL)
1323  {
1324  corsaro_log(__func__, corsaro, "could not get packet buffer");
1325  return -1;
1326  }
1327 
1328  memcpy(vector->initial_packet, pkt_buf, vector->initial_packet_len);
1329 
1330  vector->attacker_ip = ntohl(ip_hdr->ip_dst.s_addr);
1331  vector->responder_ip = ntohl(ip_hdr->ip_src.s_addr);
1332  vector->target_ip = findme.target_ip;
1333 
1334  vector->start_time = tv;
1335 
1336  vector->ppm_window.window_start = tv.tv_sec;
1337 
1338  /* add to the hash */
1339  khiter = kh_put(av, STATE(corsaro)->attack_hash, vector, &khret);
1340  }
1341 
1342  assert(vector != NULL);
1343 
1344  vector->packet_cnt++;
1345  vector->interval_packet_cnt++;
1346  vector->byte_cnt += ntohs(ip_hdr->ip_len);
1347  vector->interval_byte_cnt += ntohs(ip_hdr->ip_len);
1348 
1349  vector->latest_time = tv;
1350  /* update the pps window */
1351  attack_vector_update_ppm_window(vector, tv);
1352 
1353  /* add the attacker ip to the hash */
1354  kh_put(32xx, vector->attack_ip_hash, ntohl(ip_hdr->ip_dst.s_addr), &khret);
1355 
1356  /* add the ports to the hashes */
1357  kh_put(32xx, vector->attack_port_hash, attacker_port, &khret);
1358  kh_put(32xx, vector->target_port_hash, target_port, &khret);
1359 
1360  return 0;
1361 }
1362 
1363 /* ==== External Output Convenience Functions ==== */
1364 
1365 void corsaro_dos_attack_vector_get_packet(
1366  corsaro_dos_attack_vector_in_t *attack_vector,
1367  libtrace_packet_t *packet)
1368 {
1369  assert(packet != NULL);
1370 
1371  trace_construct_packet(packet, TRACE_TYPE_ETH,
1372  attack_vector->initial_packet,
1373  attack_vector->initial_packet_len);
1374 }
1375 
1377  corsaro_file_t *file,
1379 {
1380  assert(corsaro != NULL);
1381  assert(file != NULL);
1382  assert(header != NULL);
1383 
1384  return corsaro_file_printf(corsaro, file,
1385  "mismatch: %"PRIu32"\n"
1386  "attack_vectors: %"PRIu32"\n"
1387  "non-attack_vectors: %"PRIu32"\n",
1388  header->mismatched_pkt_cnt,
1389  header->attack_vector_cnt,
1390  header->non_attack_vector_cnt);
1391 
1392 }
1393 
1395 {
1396  assert(header != NULL);
1397 
1398  fprintf(stdout, "mismatch: %"PRIu32"\n"
1399  "attack_vectors: %"PRIu32"\n"
1400  "non-attack_vectors: %"PRIu32"\n",
1401  header->mismatched_pkt_cnt,
1402  header->attack_vector_cnt,
1403  header->non_attack_vector_cnt);
1404 }
1405 
1410  corsaro_file_t *file,
1412 {
1413  uint32_t tmp;
1414  char t_ip[16];
1415 
1416  assert(corsaro != NULL);
1417  assert(file != NULL);
1418  assert(av != NULL);
1419 
1420  tmp = htonl(av->target_ip);
1421  inet_ntop(AF_INET,&tmp, &t_ip[0], 16);
1422 
1423  return corsaro_file_printf(corsaro, file,
1424  "%s"
1425  ",%"PRIu32
1426  ",%"PRIu32
1427  ",%"PRIu32
1428  ",%"PRIu32
1429  ",%"PRIu64
1430  ",%"PRIu32
1431  ",%"PRIu64
1432  ",%"PRIu32
1433  ",%"PRIu64
1434  ",%"PRIu32".%06"PRIu32
1435  ",%"PRIu32".%06"PRIu32
1436  "\n",
1437  t_ip,
1438  av->attacker_ip_cnt,
1440  av->attack_port_cnt,
1441  av->target_port_cnt,
1442  av->packet_cnt,
1443  av->interval_packet_cnt,
1444  av->byte_cnt,
1445  av->interval_byte_cnt,
1446  av->max_ppm,
1447  av->start_time_sec,
1448  av->start_time_usec,
1449  av->latest_time_sec,
1450  av->latest_time_usec);
1451 
1452 }
1453 
1455 {
1456  uint32_t tmp;
1457  char t_ip[16];
1458  libtrace_packet_t *packet;
1459 
1460  assert(av != NULL);
1461 
1462  tmp = htonl(av->target_ip);
1463  inet_ntop(AF_INET,&tmp, &t_ip[0], 16);
1464 
1465  fprintf(stdout,
1466  "%s"
1467  ",%"PRIu32
1468  ",%"PRIu32
1469  ",%"PRIu32
1470  ",%"PRIu32
1471  ",%"PRIu64
1472  ",%"PRIu32
1473  ",%"PRIu64
1474  ",%"PRIu32
1475  ",%"PRIu64
1476  ",%"PRIu32".%06"PRIu32
1477  ",%"PRIu32".%06"PRIu32
1478  "\n",
1479  t_ip,
1480  av->attacker_ip_cnt,
1482  av->attack_port_cnt,
1483  av->target_port_cnt,
1484  av->packet_cnt,
1485  av->interval_packet_cnt,
1486  av->byte_cnt,
1487  av->interval_byte_cnt,
1488  av->max_ppm,
1489  av->start_time_sec,
1490  av->start_time_usec,
1491  av->latest_time_sec,
1492  av->latest_time_usec);
1493 
1494  /* this may get slow if you are dumping *lots* of dos records */
1495  if ((packet = trace_create_packet()) == NULL) {
1496  corsaro_log_file(__func__, NULL, "error creating libtrace packet");
1497  return;
1498  }
1499 
1500  corsaro_dos_attack_vector_get_packet(av, packet);
1501 
1502 #if HAVE_LIBPACKETDUMP
1503  fprintf(stdout, "START PACKET\n");
1504  trace_dump_packet(packet);
1505  fprintf(stdout, "\nEND PACKET\n");
1506 #else
1507  fprintf(stdout, "corsaro not built with libpacketdump support\n"
1508  "not dumping initial packet\n");
1509 #endif
1510 }
1511 
1513  corsaro_file_t *file,
1514  corsaro_dos_header_t *header)
1515 {
1516  assert(corsaro != NULL);
1517  assert(file != NULL);
1518  assert(header != NULL);
1519 
1520  return corsaro_file_printf(corsaro, file,
1521  "%"PRIu32"\n",
1522  header->attack_vector_cnt);
1523 
1524 }
1525 
1527 {
1528  assert(header != NULL);
1529 
1530  fprintf(stdout, "%"PRIu32"\n", header->attack_vector_cnt);
1531 
1532 }
1533 
1535  corsaro_file_t *file,
1536  corsaro_in_record_type_t record_type,
1537  corsaro_in_record_t *record)
1538 {
1539  switch(record_type)
1540  {
1542  return corsaro_dos_global_header_fprint(corsaro, file,
1543  (corsaro_dos_global_header_t *)record->buffer);
1544  break;
1545 
1547  return corsaro_dos_header_fprint(corsaro, file,
1548  (corsaro_dos_header_t *)record->buffer);
1549  break;
1550 
1552  return corsaro_dos_attack_vector_fprint(corsaro, file,
1554  break;
1555 
1556  default:
1557  corsaro_log(__func__, corsaro, "record_type %d not a dos record",
1558  record_type);
1559  return -1;
1560  break;
1561  }
1562 
1563  return -1;
1564 }
1565 
1567  corsaro_in_record_t *record)
1568 {
1569  switch(record_type)
1570  {
1573  (corsaro_dos_global_header_t *)record->buffer);
1574  break;
1575 
1578  (corsaro_dos_header_t *)record->buffer);
1579  break;
1580 
1584  break;
1585 
1586  default:
1587  corsaro_log_file(__func__, NULL,
1588  "record_type %d not a dos record",
1589  record_type);
1590  return -1;
1591  break;
1592  }
1593 
1594  return 0;
1595 }